From 16eff804975ca3e2d27a5d974c63d71685187532 Mon Sep 17 00:00:00 2001 From: Aaron Vaage Date: Tue, 12 Sep 2017 10:24:24 -0700 Subject: [PATCH] Made StreamData Have Const Pointers In prep for changes to Trick Play, we want to make all messages copy on write so that if the same message is sent to multiple handlers, it is not possible for one handler to change the data another handler is using. Change-Id: I554166ca11c532412e4dfced5603972ca24dc2bb --- packager/media/base/audio_stream_info.cc | 4 + packager/media/base/audio_stream_info.h | 1 + packager/media/base/media_handler.h | 126 +++++++++------- .../media/base/media_handler_test_base.cc | 122 +++++++++------- packager/media/base/media_handler_test_base.h | 66 +++++---- packager/media/base/muxer.cc | 11 +- packager/media/base/muxer.h | 14 +- packager/media/base/stream_info.h | 6 + packager/media/base/text_stream_info.cc | 4 + packager/media/base/text_stream_info.h | 2 + packager/media/base/video_stream_info.cc | 4 + packager/media/base/video_stream_info.h | 1 + .../chunking/chunking_handler_unittest.cc | 79 ++++++---- packager/media/crypto/encryption_handler.cc | 135 ++++++++++++------ packager/media/crypto/encryption_handler.h | 14 +- .../crypto/encryption_handler_unittest.cc | 117 +++++++++------ .../formats/mp2t/pes_packet_generator.cc | 16 +-- .../media/formats/mp2t/pes_packet_generator.h | 2 +- .../mp2t/pes_packet_generator_unittest.cc | 12 +- packager/media/formats/mp2t/ts_muxer.cc | 11 +- packager/media/formats/mp2t/ts_muxer.h | 4 +- packager/media/formats/mp2t/ts_segmenter.cc | 6 +- packager/media/formats/mp2t/ts_segmenter.h | 2 +- .../formats/mp2t/ts_segmenter_unittest.cc | 12 +- packager/media/formats/mp4/fragmenter.cc | 33 +++-- packager/media/formats/mp4/fragmenter.h | 6 +- packager/media/formats/mp4/mp4_muxer.cc | 33 +++-- packager/media/formats/mp4/mp4_muxer.h | 5 +- packager/media/formats/mp4/segmenter.cc | 21 ++- packager/media/formats/mp4/segmenter.h | 12 +- .../webm/encrypted_segmenter_unittest.cc | 4 +- .../webm/multi_segment_segmenter_unittest.cc | 8 +- packager/media/formats/webm/segmenter.cc | 89 ++++++------ packager/media/formats/webm/segmenter.h | 19 ++- .../media/formats/webm/segmenter_test_base.h | 2 +- .../webm/single_segment_segmenter_unittest.cc | 12 +- .../webm/two_pass_single_segment_segmenter.cc | 2 +- packager/media/formats/webm/webm_muxer.cc | 15 +- packager/media/formats/webm/webm_muxer.h | 5 +- .../media/trick_play/trick_play_handler.cc | 61 ++++---- .../media/trick_play/trick_play_handler.h | 5 +- .../trick_play/trick_play_handler_unittest.cc | 64 ++++++--- 42 files changed, 691 insertions(+), 476 deletions(-) diff --git a/packager/media/base/audio_stream_info.cc b/packager/media/base/audio_stream_info.cc index ccf5fddaa8..e2135ef87a 100644 --- a/packager/media/base/audio_stream_info.cc +++ b/packager/media/base/audio_stream_info.cc @@ -92,6 +92,10 @@ std::string AudioStreamInfo::ToString() const { return str; } +std::unique_ptr AudioStreamInfo::Clone() const { + return std::unique_ptr(new AudioStreamInfo(*this)); +} + std::string AudioStreamInfo::GetCodecString(Codec codec, uint8_t audio_object_type) { switch (codec) { diff --git a/packager/media/base/audio_stream_info.h b/packager/media/base/audio_stream_info.h index 0d233c172a..2177bbf3b3 100644 --- a/packager/media/base/audio_stream_info.h +++ b/packager/media/base/audio_stream_info.h @@ -33,6 +33,7 @@ class AudioStreamInfo : public StreamInfo { /// @{ bool IsValidConfig() const override; std::string ToString() const override; + std::unique_ptr Clone() const override; /// @} uint8_t sample_bits() const { return sample_bits_; } diff --git a/packager/media/base/media_handler.h b/packager/media/base/media_handler.h index 85fc909047..92997148a6 100644 --- a/packager/media/base/media_handler.h +++ b/packager/media/base/media_handler.h @@ -48,12 +48,66 @@ struct StreamData { size_t stream_index = static_cast(-1); StreamDataType stream_data_type = StreamDataType::kUnknown; - std::shared_ptr period_info; - std::shared_ptr stream_info; - std::shared_ptr media_sample; - std::shared_ptr text_sample; - std::shared_ptr media_event; - std::shared_ptr segment_info; + std::shared_ptr period_info; + std::shared_ptr stream_info; + std::shared_ptr media_sample; + std::shared_ptr text_sample; + std::shared_ptr media_event; + std::shared_ptr segment_info; + + static std::unique_ptr FromPeriodInfo( + size_t stream_index, std::shared_ptr period_info) { + std::unique_ptr stream_data(new StreamData); + stream_data->stream_index = stream_index; + stream_data->stream_data_type = StreamDataType::kPeriodInfo; + stream_data->period_info = std::move(period_info); + return stream_data; + } + + static std::unique_ptr FromStreamInfo( + size_t stream_index, std::shared_ptr stream_info) { + std::unique_ptr stream_data(new StreamData); + stream_data->stream_index = stream_index; + stream_data->stream_data_type = StreamDataType::kStreamInfo; + stream_data->stream_info = std::move(stream_info); + return stream_data; + } + + static std::unique_ptr FromMediaSample( + size_t stream_index, std::shared_ptr media_sample) { + std::unique_ptr stream_data(new StreamData); + stream_data->stream_index = stream_index; + stream_data->stream_data_type = StreamDataType::kMediaSample; + stream_data->media_sample = std::move(media_sample); + return stream_data; + } + + static std::unique_ptr FromTextSample( + size_t stream_index, std::shared_ptr text_sample) { + std::unique_ptr stream_data(new StreamData); + stream_data->stream_index = stream_index; + stream_data->stream_data_type = StreamDataType::kTextSample; + stream_data->text_sample = std::move(text_sample); + return stream_data; + } + + static std::unique_ptr FromMediaEvent( + size_t stream_index, std::shared_ptr media_event) { + std::unique_ptr stream_data(new StreamData); + stream_data->stream_index = stream_index; + stream_data->stream_data_type = StreamDataType::kMediaEvent; + stream_data->media_event = std::move(media_event); + return stream_data; + } + + static std::unique_ptr FromSegmentInfo( + size_t stream_index, std::shared_ptr segment_info) { + std::unique_ptr stream_data(new StreamData); + stream_data->stream_index = stream_index; + stream_data->stream_data_type = StreamDataType::kSegmentInfo; + stream_data->segment_info = std::move(segment_info); + return stream_data; + } }; /// MediaHandler is the base media processing unit. Media handlers transform @@ -114,64 +168,40 @@ class MediaHandler { Status Dispatch(std::unique_ptr stream_data); /// Dispatch the period info to downstream handlers. - Status DispatchPeriodInfo(size_t stream_index, - std::shared_ptr period_info) { - std::unique_ptr stream_data(new StreamData); - stream_data->stream_index = stream_index; - stream_data->stream_data_type = StreamDataType::kPeriodInfo; - stream_data->period_info = std::move(period_info); - return Dispatch(std::move(stream_data)); + Status DispatchPeriodInfo( + size_t stream_index, std::shared_ptr period_info) { + return Dispatch(StreamData::FromPeriodInfo(stream_index, period_info)); } /// Dispatch the stream info to downstream handlers. - Status DispatchStreamInfo(size_t stream_index, - std::shared_ptr stream_info) { - std::unique_ptr stream_data(new StreamData); - stream_data->stream_index = stream_index; - stream_data->stream_data_type = StreamDataType::kStreamInfo; - stream_data->stream_info = std::move(stream_info); - return Dispatch(std::move(stream_data)); + Status DispatchStreamInfo( + size_t stream_index, std::shared_ptr stream_info) { + return Dispatch(StreamData::FromStreamInfo(stream_index, stream_info)); } /// Dispatch the media sample to downstream handlers. - Status DispatchMediaSample(size_t stream_index, - std::shared_ptr media_sample) { - std::unique_ptr stream_data(new StreamData); - stream_data->stream_index = stream_index; - stream_data->stream_data_type = StreamDataType::kMediaSample; - stream_data->media_sample = std::move(media_sample); - return Dispatch(std::move(stream_data)); + Status DispatchMediaSample( + size_t stream_index, std::shared_ptr media_sample) { + return Dispatch(StreamData::FromMediaSample(stream_index, media_sample)); } /// Dispatch the text sample to downsream handlers. // DispatchTextSample should only be override for testing. - Status DispatchTextSample(size_t stream_index, - std::shared_ptr text_sample) { - std::unique_ptr stream_data(new StreamData); - stream_data->stream_index = stream_index; - stream_data->stream_data_type = StreamDataType::kTextSample; - stream_data->text_sample = std::move(text_sample); - return Dispatch(std::move(stream_data)); + Status DispatchTextSample( + size_t stream_index, std::shared_ptr text_sample) { + return Dispatch(StreamData::FromTextSample(stream_index, text_sample)); } /// Dispatch the media event to downstream handlers. - Status DispatchMediaEvent(size_t stream_index, - std::shared_ptr media_event) { - std::unique_ptr stream_data(new StreamData); - stream_data->stream_index = stream_index; - stream_data->stream_data_type = StreamDataType::kMediaEvent; - stream_data->media_event = std::move(media_event); - return Dispatch(std::move(stream_data)); + Status DispatchMediaEvent( + size_t stream_index, std::shared_ptr media_event) { + return Dispatch(StreamData::FromMediaEvent(stream_index, media_event)); } /// Dispatch the segment info to downstream handlers. - Status DispatchSegmentInfo(size_t stream_index, - std::shared_ptr segment_info) { - std::unique_ptr stream_data(new StreamData); - stream_data->stream_index = stream_index; - stream_data->stream_data_type = StreamDataType::kSegmentInfo; - stream_data->segment_info = std::move(segment_info); - return Dispatch(std::move(stream_data)); + Status DispatchSegmentInfo( + size_t stream_index, std::shared_ptr segment_info) { + return Dispatch(StreamData::FromSegmentInfo(stream_index, segment_info)); } /// Flush the downstream connected at the specified output stream index. diff --git a/packager/media/base/media_handler_test_base.cc b/packager/media/base/media_handler_test_base.cc index ca13d1c56f..f6997ec196 100644 --- a/packager/media/base/media_handler_test_base.cc +++ b/packager/media/base/media_handler_test_base.cc @@ -80,45 +80,86 @@ MediaHandlerTestBase::MediaHandlerTestBase() : next_handler_(new FakeMediaHandler), some_handler_(new FakeMediaHandler) {} -std::unique_ptr MediaHandlerTestBase::GetStreamInfoStreamData( - int stream_index, - Codec codec, - uint32_t time_scale) { - std::unique_ptr stream_data(new StreamData); - stream_data->stream_index = stream_index; - stream_data->stream_data_type = StreamDataType::kStreamInfo; - stream_data->stream_info = GetMockStreamInfo(codec, time_scale); - return stream_data; +bool MediaHandlerTestBase::IsVideoCodec(Codec codec) const { + return codec >= kCodecVideo && codec < kCodecVideoMaxPlusOne; } -std::unique_ptr MediaHandlerTestBase::GetMediaSampleStreamData( - int stream_index, +std::unique_ptr MediaHandlerTestBase::GetVideoStreamInfo( + uint32_t time_scale) const { + return GetVideoStreamInfo(time_scale, kCodecVP9, kWidth, kHeight); +} + +std::unique_ptr MediaHandlerTestBase::GetVideoStreamInfo( + uint32_t time_scale, + uint32_t width, + uint64_t height) const { + return GetVideoStreamInfo(time_scale, kCodecVP9, width, height); +} + +std::unique_ptr MediaHandlerTestBase::GetVideoStreamInfo( + uint32_t time_scale, + Codec codec) const { + return GetVideoStreamInfo(time_scale, codec, kWidth, kHeight); +} + +std::unique_ptr MediaHandlerTestBase::GetVideoStreamInfo( + uint32_t time_scale, + Codec codec, + uint32_t width, + uint64_t height) const { + return std::unique_ptr(new VideoStreamInfo( + kTrackId, time_scale, kDuration, codec, H26xStreamFormat::kUnSpecified, + kCodecString, kCodecConfig, sizeof(kCodecConfig), width, height, + kPixelWidth, kPixelHeight, kTrickPlayFactor, kNaluLengthSize, kLanguage, + !kEncrypted)); +} + +std::unique_ptr MediaHandlerTestBase::GetAudioStreamInfo( + uint32_t time_scale) const { + return GetAudioStreamInfo(time_scale, kCodecAAC); +} + +std::unique_ptr MediaHandlerTestBase::GetAudioStreamInfo( + uint32_t time_scale, + Codec codec) const { + return std::unique_ptr(new AudioStreamInfo( + kTrackId, time_scale, kDuration, codec, kCodecString, kCodecConfig, + sizeof(kCodecConfig), kSampleBits, kNumChannels, kSamplingFrequency, + kSeekPrerollNs, kCodecDelayNs, kMaxBitrate, kAvgBitrate, kLanguage, + !kEncrypted)); +} + +std::unique_ptr MediaHandlerTestBase::GetMediaSample( int64_t timestamp, int64_t duration, - bool is_keyframe) { - std::unique_ptr stream_data(new StreamData); - stream_data->stream_index = stream_index; - stream_data->stream_data_type = StreamDataType::kMediaSample; - stream_data->media_sample.reset( - new MediaSample(kData, sizeof(kData), nullptr, 0, is_keyframe)); - stream_data->media_sample->set_dts(timestamp); - stream_data->media_sample->set_duration(duration); - return stream_data; + bool is_keyframe) const { + return GetMediaSample(timestamp, duration, is_keyframe, kData, sizeof(kData)); } -std::unique_ptr MediaHandlerTestBase::GetSegmentInfoStreamData( - int stream_index, +std::unique_ptr MediaHandlerTestBase::GetMediaSample( + int64_t timestamp, + int64_t duration, + bool is_keyframe, + const uint8_t* data, + size_t data_length) const { + std::unique_ptr sample( + new MediaSample(data, data_length, nullptr, 0, is_keyframe)); + sample->set_dts(timestamp); + sample->set_duration(duration); + + return sample; +} + +std::unique_ptr MediaHandlerTestBase::GetSegmentInfo( int64_t start_timestamp, int64_t duration, - bool is_subsegment) { - std::unique_ptr stream_data(new StreamData); - stream_data->stream_index = stream_index; - stream_data->stream_data_type = StreamDataType::kSegmentInfo; - stream_data->segment_info.reset(new SegmentInfo); - stream_data->segment_info->start_timestamp = start_timestamp; - stream_data->segment_info->duration = duration; - stream_data->segment_info->is_subsegment = is_subsegment; - return stream_data; + bool is_subsegment) const { + std::unique_ptr info(new SegmentInfo); + info->start_timestamp = start_timestamp; + info->duration = duration; + info->is_subsegment = is_subsegment; + + return info; } void MediaHandlerTestBase::SetUpGraph(size_t num_inputs, @@ -143,24 +184,5 @@ void MediaHandlerTestBase::ClearOutputStreamDataVector() { next_handler_->clear_stream_data_vector(); } -std::shared_ptr MediaHandlerTestBase::GetMockStreamInfo( - Codec codec, - uint32_t time_scale) { - if (codec >= kCodecAudio && codec < kCodecAudioMaxPlusOne) { - return std::shared_ptr(new AudioStreamInfo( - kTrackId, time_scale, kDuration, codec, kCodecString, kCodecConfig, - sizeof(kCodecConfig), kSampleBits, kNumChannels, kSamplingFrequency, - kSeekPrerollNs, kCodecDelayNs, kMaxBitrate, kAvgBitrate, kLanguage, - !kEncrypted)); - } else if (codec >= kCodecVideo && codec < kCodecVideoMaxPlusOne) { - return std::shared_ptr(new VideoStreamInfo( - kTrackId, time_scale, kDuration, codec, H26xStreamFormat::kUnSpecified, - kCodecString, kCodecConfig, sizeof(kCodecConfig), kWidth, kHeight, - kPixelWidth, kPixelHeight, kTrickPlayFactor, kNaluLengthSize, kLanguage, - !kEncrypted)); - } - return nullptr; -} - } // namespace media } // namespace shaka diff --git a/packager/media/base/media_handler_test_base.h b/packager/media/base/media_handler_test_base.h index 1dd3096818..daedcc5b15 100644 --- a/packager/media/base/media_handler_test_base.h +++ b/packager/media/base/media_handler_test_base.h @@ -95,36 +95,46 @@ class MediaHandlerTestBase : public ::testing::Test { public: MediaHandlerTestBase(); - /// @return a stream data with mock stream info. - std::unique_ptr GetStreamInfoStreamData(int stream_index, - Codec codec, - uint32_t time_scale); + bool IsVideoCodec(Codec codec) const; - /// @return a stream data with mock video stream info. - std::unique_ptr GetVideoStreamInfoStreamData( - int stream_index, - uint32_t time_scale) { - return GetStreamInfoStreamData(stream_index, kCodecVP9, time_scale); - } + std::unique_ptr GetVideoStreamInfo( + uint32_t time_scale) const; - /// @return a stream data with mock audio stream info. - std::unique_ptr GetAudioStreamInfoStreamData( - int stream_index, - uint32_t time_scale) { - return GetStreamInfoStreamData(stream_index, kCodecAAC, time_scale); - } + std::unique_ptr GetVideoStreamInfo( + uint32_t time_scale, uint32_t width, uint64_t height) const; - /// @return a stream data with mock media sample. - std::unique_ptr GetMediaSampleStreamData(int stream_index, - int64_t timestamp, - int64_t duration, - bool is_keyframe); + std::unique_ptr GetVideoStreamInfo( + uint32_t time_scale, Codec codec) const; - /// @return a stream data with mock segment info. - std::unique_ptr GetSegmentInfoStreamData(int stream_index, - int64_t start_timestamp, - int64_t duration, - bool is_subsegment); + std::unique_ptr GetVideoStreamInfo( + uint32_t time_scale, + Codec codec, + uint32_t width, + uint64_t height) const; + + std::unique_ptr GetAudioStreamInfo( + uint32_t time_scale) const; + + std::unique_ptr GetAudioStreamInfo( + uint32_t time_scale, + Codec codec) const; + + std::unique_ptr GetMediaSample( + int64_t timestamp, + int64_t duration, + bool is_keyframe) const; + + std::unique_ptr GetMediaSample( + int64_t timestamp, + int64_t duration, + bool is_keyframe, + const uint8_t* data, + size_t data_length) const; + + std::unique_ptr GetSegmentInfo( + int64_t start_timestamp, + int64_t duration, + bool is_subsegment) const; /// Setup a graph using |handler| with |num_inputs| and |num_outputs|. void SetUpGraph(size_t num_inputs, @@ -148,10 +158,6 @@ class MediaHandlerTestBase : public ::testing::Test { MediaHandlerTestBase(const MediaHandlerTestBase&) = delete; MediaHandlerTestBase& operator=(const MediaHandlerTestBase&) = delete; - // Get a mock stream info for testing. - std::shared_ptr GetMockStreamInfo(Codec codec, - uint32_t time_scale); - // Downstream handler used in testing graph. std::shared_ptr next_handler_; // Some random handler which can be used for testing. diff --git a/packager/media/base/muxer.cc b/packager/media/base/muxer.cc index c5a3331eed..f0ece9cd91 100644 --- a/packager/media/base/muxer.cc +++ b/packager/media/base/muxer.cc @@ -50,10 +50,10 @@ Status Muxer::Process(std::unique_ptr stream_data) { } return InitializeMuxer(); case StreamDataType::kSegmentInfo: { - auto& segment_info = stream_data->segment_info; - if (muxer_listener_ && segment_info->is_encrypted) { + const auto& segment_info = *stream_data->segment_info; + if (muxer_listener_ && segment_info.is_encrypted) { const EncryptionConfig* encryption_config = - segment_info->key_rotation_encryption_config.get(); + segment_info.key_rotation_encryption_config.get(); // Only call OnEncryptionInfoReady again when key updates. if (encryption_config && encryption_config->key_id != current_key_id_) { muxer_listener_->OnEncryptionInfoReady( @@ -67,12 +67,11 @@ Status Muxer::Process(std::unique_ptr stream_data) { muxer_listener_->OnEncryptionStart(); } } - return FinalizeSegment(stream_data->stream_index, - std::move(segment_info)); + return FinalizeSegment(stream_data->stream_index, segment_info); } case StreamDataType::kMediaSample: return AddSample(stream_data->stream_index, - std::move(stream_data->media_sample)); + *stream_data->media_sample); default: VLOG(3) << "Stream data type " << static_cast(stream_data->stream_data_type) << " ignored."; diff --git a/packager/media/base/muxer.h b/packager/media/base/muxer.h index 7839c0b28d..f05ec014e4 100644 --- a/packager/media/base/muxer.h +++ b/packager/media/base/muxer.h @@ -44,7 +44,7 @@ class Muxer : public MediaHandler { /// @param progress_listener should not be NULL. void SetProgressListener(std::unique_ptr progress_listener); - const std::vector>& streams() const { + const std::vector>& streams() const { return streams_; } @@ -79,15 +79,17 @@ class Muxer : public MediaHandler { virtual Status Finalize() = 0; // Add a new sample. - virtual Status AddSample(size_t stream_id, - std::shared_ptr sample) = 0; + virtual Status AddSample( + size_t stream_id, + const MediaSample& sample) = 0; // Finalize the segment or subsegment. - virtual Status FinalizeSegment(size_t stream_id, - std::shared_ptr segment_info) = 0; + virtual Status FinalizeSegment( + size_t stream_id, + const SegmentInfo& segment_info) = 0; MuxerOptions options_; - std::vector> streams_; + std::vector> streams_; std::vector current_key_id_; bool encryption_started_ = false; bool cancelled_; diff --git a/packager/media/base/stream_info.h b/packager/media/base/stream_info.h index dc93b1aac4..bf53746724 100644 --- a/packager/media/base/stream_info.h +++ b/packager/media/base/stream_info.h @@ -7,6 +7,7 @@ #ifndef MEDIA_BASE_STREAM_INFO_H_ #define MEDIA_BASE_STREAM_INFO_H_ +#include #include #include @@ -72,6 +73,11 @@ class StreamInfo { /// @return A human-readable string describing the stream info. virtual std::string ToString() const; + /// @return A new copy of this stream info. The copy will be of the same + /// type as the original. This should be used when a copy is needed + /// without explicitly knowing the stream info type. + virtual std::unique_ptr Clone() const = 0; + StreamType stream_type() const { return stream_type_; } uint32_t track_id() const { return track_id_; } uint32_t time_scale() const { return time_scale_; } diff --git a/packager/media/base/text_stream_info.cc b/packager/media/base/text_stream_info.cc index e3e6515b35..9107397f38 100644 --- a/packager/media/base/text_stream_info.cc +++ b/packager/media/base/text_stream_info.cc @@ -28,5 +28,9 @@ bool TextStreamInfo::IsValidConfig() const { return true; } +std::unique_ptr TextStreamInfo::Clone() const { + return std::unique_ptr(new TextStreamInfo(*this)); +} + } // namespace media } // namespace shaka diff --git a/packager/media/base/text_stream_info.h b/packager/media/base/text_stream_info.h index 0e2270e9de..6e37844db5 100644 --- a/packager/media/base/text_stream_info.h +++ b/packager/media/base/text_stream_info.h @@ -38,6 +38,8 @@ class TextStreamInfo : public StreamInfo { bool IsValidConfig() const override; + std::unique_ptr Clone() const override; + uint16_t width() const { return width_; } uint16_t height() const { return height_; } diff --git a/packager/media/base/video_stream_info.cc b/packager/media/base/video_stream_info.cc index 34dc0ec9ed..dc6f3eb194 100644 --- a/packager/media/base/video_stream_info.cc +++ b/packager/media/base/video_stream_info.cc @@ -88,5 +88,9 @@ std::string VideoStreamInfo::ToString() const { nalu_length_size_); } +std::unique_ptr VideoStreamInfo::Clone() const { + return std::unique_ptr(new VideoStreamInfo(*this)); +} + } // namespace media } // namespace shaka diff --git a/packager/media/base/video_stream_info.h b/packager/media/base/video_stream_info.h index 2b6f564b0c..4981f92c0d 100644 --- a/packager/media/base/video_stream_info.h +++ b/packager/media/base/video_stream_info.h @@ -50,6 +50,7 @@ class VideoStreamInfo : public StreamInfo { /// @{ bool IsValidConfig() const override; std::string ToString() const override; + std::unique_ptr Clone() const override; /// @} H26xStreamFormat h26x_stream_format() const { return h26x_stream_format_; } diff --git a/packager/media/chunking/chunking_handler_unittest.cc b/packager/media/chunking/chunking_handler_unittest.cc index 3e7e086eee..de28c1278b 100644 --- a/packager/media/chunking/chunking_handler_unittest.cc +++ b/packager/media/chunking/chunking_handler_unittest.cc @@ -56,15 +56,17 @@ TEST_F(ChunkingHandlerTest, AudioNoSubsegmentsThenFlush) { chunking_params.segment_duration_in_seconds = 1; SetUpChunkingHandler(1, chunking_params); - ASSERT_OK(Process(GetAudioStreamInfoStreamData(kStreamIndex0, kTimeScale0))); + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex0, GetAudioStreamInfo(kTimeScale0)))); EXPECT_THAT( GetOutputStreamDataVector(), ElementsAre(IsStreamInfo(kStreamIndex0, kTimeScale0, !kEncrypted))); for (int i = 0; i < 5; ++i) { ClearOutputStreamDataVector(); - ASSERT_OK(Process(GetMediaSampleStreamData(kStreamIndex0, i * kDuration1, - kDuration1, kKeyFrame))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample(i * kDuration1, kDuration1, kKeyFrame)))); // One output stream_data except when i == 3, which also has SegmentInfo. if (i == 3) { EXPECT_THAT(GetOutputStreamDataVector(), @@ -93,10 +95,12 @@ TEST_F(ChunkingHandlerTest, AudioWithSubsegments) { chunking_params.subsegment_duration_in_seconds = 0.5; SetUpChunkingHandler(1, chunking_params); - ASSERT_OK(Process(GetAudioStreamInfoStreamData(kStreamIndex0, kTimeScale0))); + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex0, GetAudioStreamInfo(kTimeScale0)))); for (int i = 0; i < 5; ++i) { - ASSERT_OK(Process(GetMediaSampleStreamData(kStreamIndex0, i * kDuration1, - kDuration1, kKeyFrame))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample(i * kDuration1, kDuration1, kKeyFrame)))); } EXPECT_THAT( GetOutputStreamDataVector(), @@ -120,14 +124,18 @@ TEST_F(ChunkingHandlerTest, VideoAndSubsegmentAndNonzeroStart) { chunking_params.subsegment_duration_in_seconds = 0.3; SetUpChunkingHandler(1, chunking_params); - ASSERT_OK(Process(GetVideoStreamInfoStreamData(kStreamIndex0, kTimeScale1))); + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex0, GetVideoStreamInfo(kTimeScale1)))); const int64_t kVideoStartTimestamp = 12345; for (int i = 0; i < 6; ++i) { // Alternate key frame. const bool is_key_frame = (i % 2) == 1; - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex0, kVideoStartTimestamp + i * kDuration1, kDuration1, - is_key_frame))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample( + kVideoStartTimestamp + i * kDuration1, + kDuration1, + is_key_frame)))); } EXPECT_THAT( GetOutputStreamDataVector(), @@ -159,8 +167,10 @@ TEST_F(ChunkingHandlerTest, AudioAndVideo) { chunking_params.subsegment_duration_in_seconds = 0.3; SetUpChunkingHandler(2, chunking_params); - ASSERT_OK(Process(GetAudioStreamInfoStreamData(kStreamIndex0, kTimeScale0))); - ASSERT_OK(Process(GetVideoStreamInfoStreamData(kStreamIndex1, kTimeScale1))); + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex0, GetAudioStreamInfo(kTimeScale0)))); + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex1, GetVideoStreamInfo(kTimeScale1)))); EXPECT_THAT( GetOutputStreamDataVector(), ElementsAre(IsStreamInfo(kStreamIndex0, kTimeScale0, !kEncrypted), @@ -171,14 +181,20 @@ TEST_F(ChunkingHandlerTest, AudioAndVideo) { const int64_t kAudioStartTimestamp = 9876; const int64_t kVideoStartTimestamp = 12345; for (int i = 0; i < 5; ++i) { - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex0, kAudioStartTimestamp + kDuration0 * i, kDuration0, - true))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample( + kAudioStartTimestamp + kDuration0 * i, + kDuration0, + true)))); // Alternate key frame. const bool is_key_frame = (i % 2) == 1; - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex1, kVideoStartTimestamp + kDuration1 * i, kDuration1, - is_key_frame))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex1, + GetMediaSample( + kVideoStartTimestamp + kDuration1 * i, + kDuration1, + is_key_frame)))); } EXPECT_THAT( @@ -211,15 +227,24 @@ TEST_F(ChunkingHandlerTest, AudioAndVideo) { // The side comments below show the equivalent timestamp in video timescale. // The audio and video are made ~aligned. - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex0, kAudioStartTimestamp + kDuration0 * 5, kDuration0, - true))); // 13595 - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex1, kVideoStartTimestamp + kDuration1 * 5, kDuration1, - true))); // 13845 - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex0, kAudioStartTimestamp + kDuration0 * 6, kDuration0, - true))); // 13845 + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample( + kAudioStartTimestamp + kDuration0 * 5, + kDuration0, + true)))); // 13595 + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex1, + GetMediaSample( + kVideoStartTimestamp + kDuration1 * 5, + kDuration1, + true)))); // 13845 + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample( + kAudioStartTimestamp + kDuration0 * 6, + kDuration0, + true)))); // 13845 // This expectation are separated from the expectation above because // ElementsAre supports at most 10 elements. EXPECT_THAT( diff --git a/packager/media/crypto/encryption_handler.cc b/packager/media/crypto/encryption_handler.cc index a56b211ef4..095b37515f 100644 --- a/packager/media/crypto/encryption_handler.cc +++ b/packager/media/crypto/encryption_handler.cc @@ -15,6 +15,7 @@ #include "packager/media/base/aes_pattern_cryptor.h" #include "packager/media/base/key_source.h" #include "packager/media/base/media_sample.h" +#include "packager/media/base/audio_stream_info.h" #include "packager/media/base/video_stream_info.h" #include "packager/media/codecs/video_slice_header_parser.h" #include "packager/media/codecs/vp8_parser.h" @@ -26,6 +27,9 @@ namespace media { namespace { const size_t kCencBlockSize = 16u; +// The encryption handler only supports a single output. +const size_t kStreamIndex = 0; + // The default KID for key rotation is all 0s. const uint8_t kKeyRotationDefaultKeyId[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -98,13 +102,13 @@ Status EncryptionHandler::InitializeInternal() { } Status EncryptionHandler::Process(std::unique_ptr stream_data) { - Status status; switch (stream_data->stream_data_type) { case StreamDataType::kStreamInfo: - status = ProcessStreamInfo(stream_data->stream_info.get()); - break; + return ProcessStreamInfo(*stream_data->stream_info); case StreamDataType::kSegmentInfo: { - SegmentInfo* segment_info = stream_data->segment_info.get(); + std::shared_ptr segment_info(new SegmentInfo( + *stream_data->segment_info)); + segment_info->is_encrypted = remaining_clear_lead_ <= 0; const bool key_rotation_enabled = crypto_period_duration_ != 0; @@ -116,25 +120,28 @@ Status EncryptionHandler::Process(std::unique_ptr stream_data) { if (remaining_clear_lead_ > 0) remaining_clear_lead_ -= segment_info->duration; } - break; + + return DispatchSegmentInfo(kStreamIndex, segment_info); } case StreamDataType::kMediaSample: - status = ProcessMediaSample(stream_data->media_sample.get()); - break; + return ProcessMediaSample(std::move(stream_data->media_sample)); default: VLOG(3) << "Stream data type " << static_cast(stream_data->stream_data_type) << " ignored."; - break; + return Dispatch(std::move(stream_data)); } - return status.ok() ? Dispatch(std::move(stream_data)) : status; } -Status EncryptionHandler::ProcessStreamInfo(StreamInfo* stream_info) { - if (stream_info->is_encrypted()) { +Status EncryptionHandler::ProcessStreamInfo(const StreamInfo& clear_info) { + if (clear_info.is_encrypted()) { return Status(error::INVALID_ARGUMENT, "Input stream is already encrypted."); } + DCHECK_NE(kStreamUnknown, clear_info.stream_type()); + DCHECK_NE(kStreamText, clear_info.stream_type()); + std::shared_ptr stream_info = clear_info.Clone(); + remaining_clear_lead_ = encryption_params_.clear_lead_in_seconds * stream_info->time_scale(); crypto_period_duration_ = @@ -196,16 +203,21 @@ Status EncryptionHandler::ProcessStreamInfo(StreamInfo* stream_info) { stream_info->set_is_encrypted(true); stream_info->set_has_clear_lead(encryption_params_.clear_lead_in_seconds > 0); stream_info->set_encryption_config(*encryption_config_); - return Status::OK; + + return DispatchStreamInfo(kStreamIndex, stream_info); } -Status EncryptionHandler::ProcessMediaSample(MediaSample* sample) { +Status EncryptionHandler::ProcessMediaSample( + std::shared_ptr clear_sample) { + DCHECK(clear_sample); + // We need to parse the frame (which also updates the vpx parser) even if the // frame is not encrypted as the next (encrypted) frame may be dependent on // this clear frame. std::vector vpx_frames; - if (vpx_parser_ && - !vpx_parser_->Parse(sample->data(), sample->data_size(), &vpx_frames)) { + if (vpx_parser_ && !vpx_parser_->Parse(clear_sample->data(), + clear_sample->data_size(), + &vpx_frames)) { return Status(error::ENCRYPTION_FAILURE, "Failed to parse vpx frame."); } @@ -214,7 +226,7 @@ Status EncryptionHandler::ProcessMediaSample(MediaSample* sample) { // allows clients to prefetch the keys. if (check_new_crypto_period_) { const int64_t current_crypto_period_index = - sample->dts() / crypto_period_duration_; + clear_sample->dts() / crypto_period_duration_; if (current_crypto_period_index != prev_crypto_period_index_) { EncryptionKey encryption_key; Status status = key_source_->GetCryptoPeriodKey( @@ -228,38 +240,69 @@ Status EncryptionHandler::ProcessMediaSample(MediaSample* sample) { check_new_crypto_period_ = false; } - if (remaining_clear_lead_ > 0) - return Status::OK; + // Since there is no encryption needed right now, send the clear copy + // downstream so we can save the costs of copying it. + if (remaining_clear_lead_ > 0) { + return DispatchMediaSample(kStreamIndex, std::move(clear_sample)); + } - std::unique_ptr decrypt_config( - new DecryptConfig(encryption_config_->key_id, encryptor_->iv(), - std::vector(), protection_scheme_, - crypt_byte_block_, skip_byte_block_)); - bool result = true; + std::unique_ptr decrypt_config(new DecryptConfig( + encryption_config_->key_id, + encryptor_->iv(), + std::vector(), + protection_scheme_, + crypt_byte_block_, + skip_byte_block_)); + + // Now that we know that this sample must be encrypted, make a copy of + // the sample first so that all the encryption operations can be done + // in-place. + std::shared_ptr cipher_sample = + MediaSample::CopyFrom(*clear_sample); + + Status result; if (vpx_parser_) { - result = EncryptVpxFrame(vpx_frames, sample, decrypt_config.get()); - if (result) { + if (EncryptVpxFrame(vpx_frames, + cipher_sample->writable_data(), + cipher_sample->data_size(), + decrypt_config.get())) { DCHECK_EQ(decrypt_config->GetTotalSizeOfSubsamples(), - sample->data_size()); + cipher_sample->data_size()); + } else { + result = Status( + error::ENCRYPTION_FAILURE, + "Failed to encrypt VPX frame."); } } else if (header_parser_) { - result = EncryptNalFrame(sample, decrypt_config.get()); - if (result) { + if (EncryptNalFrame(cipher_sample->writable_data(), + cipher_sample->data_size(), + decrypt_config.get())) { DCHECK_EQ(decrypt_config->GetTotalSizeOfSubsamples(), - sample->data_size()); - } - } else { - if (sample->data_size() > leading_clear_bytes_size_) { - EncryptBytes(sample->writable_data() + leading_clear_bytes_size_, - sample->data_size() - leading_clear_bytes_size_); + cipher_sample->data_size()); + } else { + result = Status( + error::ENCRYPTION_FAILURE, + "Failed to encrypt NAL frame."); } + } else if (cipher_sample->data_size() > leading_clear_bytes_size_) { + EncryptBytes( + cipher_sample->writable_data() + leading_clear_bytes_size_, + cipher_sample->data_size() - leading_clear_bytes_size_); } - if (!result) - return Status(error::ENCRYPTION_FAILURE, "Failed to encrypt samples."); - sample->set_is_encrypted(true); - sample->set_decrypt_config(std::move(decrypt_config)); + + if (!result.ok()) { + return result; + } + encryptor_->UpdateIv(); - return Status::OK; + + // Finish initializing the sample before sending it downstream. We must + // wait until now to finish the initialization as we will loose access to + // |decrypt_config| once we set it. + cipher_sample->set_is_encrypted(true); + cipher_sample->set_decrypt_config(std::move(decrypt_config)); + + return DispatchMediaSample(kStreamIndex, std::move(cipher_sample)); } Status EncryptionHandler::SetupProtectionPattern(StreamType stream_type) { @@ -390,9 +433,10 @@ bool EncryptionHandler::CreateEncryptor(const EncryptionKey& encryption_key) { bool EncryptionHandler::EncryptVpxFrame( const std::vector& vpx_frames, - MediaSample* sample, + uint8_t* source, + size_t source_size, DecryptConfig* decrypt_config) { - uint8_t* data = sample->writable_data(); + uint8_t* data = source; for (const VPxFrameInfo& frame : vpx_frames) { uint16_t clear_bytes = static_cast(frame.uncompressed_header_size); @@ -419,7 +463,7 @@ bool EncryptionHandler::EncryptVpxFrame( // Add subsample for the superframe index if exists. const bool is_superframe = vpx_frames.size() > 1; if (is_superframe) { - size_t index_size = sample->data() + sample->data_size() - data; + size_t index_size = source + source_size - data; DCHECK_LE(index_size, 2 + vpx_frames.size() * 4); DCHECK_GE(index_size, 2 + vpx_frames.size() * 1); uint16_t clear_bytes = static_cast(index_size); @@ -429,14 +473,14 @@ bool EncryptionHandler::EncryptVpxFrame( return true; } -bool EncryptionHandler::EncryptNalFrame(MediaSample* sample, +bool EncryptionHandler::EncryptNalFrame(uint8_t* data, + size_t data_length, DecryptConfig* decrypt_config) { DCHECK_NE(nalu_length_size_, 0u); DCHECK(header_parser_); const Nalu::CodecType nalu_type = (codec_ == kCodecH265) ? Nalu::kH265 : Nalu::kH264; - NaluReader reader(nalu_type, nalu_length_size_, sample->writable_data(), - sample->data_size()); + NaluReader reader(nalu_type, nalu_length_size_, data, data_length); // Store the current length of clear data. This is used to squash // multiple unencrypted NAL units into fewer subsample entries. @@ -496,6 +540,7 @@ bool EncryptionHandler::EncryptNalFrame(MediaSample* sample, } void EncryptionHandler::EncryptBytes(uint8_t* data, size_t size) { + DCHECK(data); DCHECK(encryptor_); CHECK(encryptor_->Crypt(data, size, data)); } diff --git a/packager/media/crypto/encryption_handler.h b/packager/media/crypto/encryption_handler.h index 2cb8afe8d3..cd93a89869 100644 --- a/packager/media/crypto/encryption_handler.h +++ b/packager/media/crypto/encryption_handler.h @@ -41,17 +41,21 @@ class EncryptionHandler : public MediaHandler { EncryptionHandler& operator=(const EncryptionHandler&) = delete; // Processes |stream_info| and sets up stream specific variables. - Status ProcessStreamInfo(StreamInfo* stream_info); + Status ProcessStreamInfo(const StreamInfo& stream_info); // Processes media sample and encrypts it if needed. - Status ProcessMediaSample(MediaSample* sample); + Status ProcessMediaSample(std::shared_ptr clear_sample); Status SetupProtectionPattern(StreamType stream_type); bool CreateEncryptor(const EncryptionKey& encryption_key); bool EncryptVpxFrame(const std::vector& vpx_frames, - MediaSample* sample, + uint8_t* source, + size_t source_size, DecryptConfig* decrypt_config); - bool EncryptNalFrame(MediaSample* sample, DecryptConfig* decrypt_config); - void EncryptBytes(uint8_t* data, size_t size); + bool EncryptNalFrame(uint8_t* data, + size_t data_length, + DecryptConfig* decrypt_config); + void EncryptBytes(uint8_t* data, + size_t size); // Testing injections. void InjectVpxParserForTesting(std::unique_ptr vpx_parser); diff --git a/packager/media/crypto/encryption_handler_unittest.cc b/packager/media/crypto/encryption_handler_unittest.cc index a547cd247d..16dc3e1d38 100644 --- a/packager/media/crypto/encryption_handler_unittest.cc +++ b/packager/media/crypto/encryption_handler_unittest.cc @@ -271,19 +271,6 @@ class EncryptionHandlerEncryptionTest return subsamples; } - std::unique_ptr GetMediaSampleStreamData(int stream_index, - int64_t timestamp, - int64_t duration) { - std::unique_ptr stream_data(new StreamData); - stream_data->stream_index = stream_index; - stream_data->stream_data_type = StreamDataType::kMediaSample; - stream_data->media_sample.reset( - new MediaSample(kData, sizeof(kData), nullptr, 0, kIsKeyFrame)); - stream_data->media_sample->set_dts(timestamp); - stream_data->media_sample->set_duration(duration); - return stream_data; - } - // Inject vpx parser / video slice header parser if needed. void InjectCodecParser() { switch (codec_) { @@ -480,7 +467,15 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithNoKeyRotation) { EXPECT_CALL(mock_key_source_, GetKey(_, _)) .WillOnce( DoAll(SetArgPointee<1>(mock_encryption_key), Return(Status::OK))); - ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale))); + + if (IsVideoCodec(codec_)) { + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex, GetVideoStreamInfo(kTimeScale, codec_)))); + } else { + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex, GetAudioStreamInfo(kTimeScale, codec_)))); + } + EXPECT_THAT(GetOutputStreamDataVector(), ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale, kEncrypted))); const StreamInfo* stream_info = @@ -500,10 +495,17 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithNoKeyRotation) { // There are three segments. Only the third segment is encrypted. for (int i = 0; i < 3; ++i) { // Use single-frame segment for testing. - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex, i * kSegmentDuration, kSegmentDuration))); - ASSERT_OK(Process(GetSegmentInfoStreamData( - kStreamIndex, i * kSegmentDuration, kSegmentDuration, !kIsSubsegment))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex, + GetMediaSample( + i * kSegmentDuration, + kSegmentDuration, + kIsKeyFrame, + kData, + sizeof(kData))))); + ASSERT_OK(Process(StreamData::FromSegmentInfo( + kStreamIndex, + GetSegmentInfo(i * kSegmentDuration, kSegmentDuration, !kIsSubsegment)))); const bool is_encrypted = i == 2; const auto& output_stream_data = GetOutputStreamDataVector(); EXPECT_THAT(output_stream_data, @@ -531,7 +533,14 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithKeyRotation) { encryption_params.vp9_subsample_encryption = vp9_subsample_encryption_; SetUpEncryptionHandler(encryption_params); - ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale))); + if (IsVideoCodec(codec_)) { + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex, GetVideoStreamInfo(kTimeScale, codec_)))); + } else { + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex, GetAudioStreamInfo(kTimeScale, codec_)))); + } + EXPECT_THAT(GetOutputStreamDataVector(), ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale, kEncrypted))); const StreamInfo* stream_info = @@ -558,10 +567,17 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithKeyRotation) { Return(Status::OK))); } // Use single-frame segment for testing. - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex, i * kSegmentDuration, kSegmentDuration))); - ASSERT_OK(Process(GetSegmentInfoStreamData( - kStreamIndex, i * kSegmentDuration, kSegmentDuration, !kIsSubsegment))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex, + GetMediaSample( + i * kSegmentDuration, + kSegmentDuration, + kIsKeyFrame, + kData, + sizeof(kData))))); + ASSERT_OK(Process(StreamData::FromSegmentInfo( + kStreamIndex, + GetSegmentInfo(i * kSegmentDuration, kSegmentDuration, !kIsSubsegment)))); const bool is_encrypted = i >= 2; const auto& output_stream_data = GetOutputStreamDataVector(); EXPECT_THAT(output_stream_data, @@ -592,7 +608,14 @@ TEST_P(EncryptionHandlerEncryptionTest, Encrypt) { .WillOnce( DoAll(SetArgPointee<1>(mock_encryption_key), Return(Status::OK))); - ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale))); + if (IsVideoCodec(codec_)) { + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex, GetVideoStreamInfo(kTimeScale, codec_)))); + } else { + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex, GetAudioStreamInfo(kTimeScale, codec_)))); + } + EXPECT_THAT(GetOutputStreamDataVector(), ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale, kEncrypted))); const StreamInfo* stream_info = @@ -608,8 +631,14 @@ TEST_P(EncryptionHandlerEncryptionTest, Encrypt) { stream_data->media_sample.reset( new MediaSample(kData, sizeof(kData), nullptr, 0, kIsKeyFrame)); - ASSERT_OK( - Process(GetMediaSampleStreamData(kStreamIndex, 0, kSampleDuration))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex, + GetMediaSample( + 0, + kSampleDuration, + kIsKeyFrame, + kData, + sizeof(kData))))); ASSERT_EQ(2u, GetOutputStreamDataVector().size()); ASSERT_EQ(kStreamIndex, GetOutputStreamDataVector().back()->stream_index); ASSERT_EQ(StreamDataType::kMediaSample, @@ -625,12 +654,14 @@ TEST_P(EncryptionHandlerEncryptionTest, Encrypt) { EXPECT_EQ(GetExpectedCryptByteBlock(), decrypt_config->crypt_byte_block()); EXPECT_EQ(GetExpectedSkipByteBlock(), decrypt_config->skip_byte_block()); - ASSERT_TRUE(Decrypt(*decrypt_config, media_sample->writable_data(), - media_sample->data_size())); - EXPECT_EQ( - std::vector(kData, kData + sizeof(kData)), - std::vector(media_sample->data(), - media_sample->data() + media_sample->data_size())); + std::vector expected( + kData, + kData + sizeof(kData)); + std::vector actual( + media_sample->data(), + media_sample->data() + media_sample->data_size()); + ASSERT_TRUE(Decrypt(*decrypt_config, actual.data(), actual.size())); + EXPECT_EQ(expected, actual); } INSTANTIATE_TEST_CASE_P( @@ -665,12 +696,16 @@ TEST_F(EncryptionHandlerTrackTypeTest, AudioTrackType) { EXPECT_CALL(mock_key_source_, GetKey(kAudioStreamLabel, _)) .WillOnce( DoAll(SetArgPointee<1>(GetMockEncryptionKey()), Return(Status::OK))); - ASSERT_OK(Process(GetAudioStreamInfoStreamData(kStreamIndex, kTimeScale))); + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex, + GetAudioStreamInfo(kTimeScale)))); EXPECT_EQ(EncryptionParams::EncryptedStreamAttributes::kAudio, captured_stream_attributes.stream_type); } TEST_F(EncryptionHandlerTrackTypeTest, VideoTrackType) { + const int32_t kWidth = 12; + const int32_t kHeight = 34; EncryptionParams::EncryptedStreamAttributes captured_stream_attributes; EncryptionParams encryption_params; encryption_params.stream_label_func = @@ -684,19 +719,13 @@ TEST_F(EncryptionHandlerTrackTypeTest, VideoTrackType) { EXPECT_CALL(mock_key_source_, GetKey(kSdVideoStreamLabel, _)) .WillOnce( DoAll(SetArgPointee<1>(GetMockEncryptionKey()), Return(Status::OK))); - std::unique_ptr stream_data = - GetVideoStreamInfoStreamData(kStreamIndex, kTimeScale); - VideoStreamInfo* video_stream_info = - reinterpret_cast(stream_data->stream_info.get()); - video_stream_info->set_width(12); - video_stream_info->set_height(34); - ASSERT_OK(Process(std::move(stream_data))); + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex, + GetVideoStreamInfo(kTimeScale, kWidth, kHeight)))); EXPECT_EQ(EncryptionParams::EncryptedStreamAttributes::kVideo, captured_stream_attributes.stream_type); - EXPECT_EQ(video_stream_info->width(), - captured_stream_attributes.oneof.video.width); - EXPECT_EQ(video_stream_info->height(), - captured_stream_attributes.oneof.video.height); + EXPECT_EQ(captured_stream_attributes.oneof.video.width, kWidth); + EXPECT_EQ(captured_stream_attributes.oneof.video.height, kHeight); } } // namespace media diff --git a/packager/media/formats/mp2t/pes_packet_generator.cc b/packager/media/formats/mp2t/pes_packet_generator.cc index ca74e1417a..f4fb3f4f04 100644 --- a/packager/media/formats/mp2t/pes_packet_generator.cc +++ b/packager/media/formats/mp2t/pes_packet_generator.cc @@ -65,21 +65,21 @@ bool PesPacketGenerator::Initialize(const StreamInfo& stream_info) { return false; } -bool PesPacketGenerator::PushSample(std::shared_ptr sample) { +bool PesPacketGenerator::PushSample(const MediaSample& sample) { if (!current_processing_pes_) current_processing_pes_.reset(new PesPacket()); - current_processing_pes_->set_pts(timescale_scale_ * sample->pts()); - current_processing_pes_->set_dts(timescale_scale_ * sample->dts()); + current_processing_pes_->set_pts(timescale_scale_ * sample.pts()); + current_processing_pes_->set_dts(timescale_scale_ * sample.dts()); if (stream_type_ == kStreamVideo) { DCHECK(converter_); std::vector subsamples; - if (sample->decrypt_config()) - subsamples = sample->decrypt_config()->subsamples(); + if (sample.decrypt_config()) + subsamples = sample.decrypt_config()->subsamples(); const bool kEscapeEncryptedNalu = true; std::vector byte_stream; if (!converter_->ConvertUnitToByteStreamWithSubsamples( - sample->data(), sample->data_size(), sample->is_key_frame(), + sample.data(), sample.data_size(), sample.is_key_frame(), kEscapeEncryptedNalu, &byte_stream, &subsamples)) { LOG(ERROR) << "Failed to convert sample to byte stream."; return false; @@ -93,8 +93,8 @@ bool PesPacketGenerator::PushSample(std::shared_ptr sample) { DCHECK_EQ(stream_type_, kStreamAudio); DCHECK(adts_converter_); - std::vector aac_frame(sample->data(), - sample->data() + sample->data_size()); + std::vector aac_frame(sample.data(), + sample.data() + sample.data_size()); // TODO(rkuroiwa): ConvertToADTS() makes another copy of aac_frame internally. // Optimize copying in this function, possibly by adding a method on diff --git a/packager/media/formats/mp2t/pes_packet_generator.h b/packager/media/formats/mp2t/pes_packet_generator.h index e84b6356eb..84b90f27e9 100644 --- a/packager/media/formats/mp2t/pes_packet_generator.h +++ b/packager/media/formats/mp2t/pes_packet_generator.h @@ -42,7 +42,7 @@ class PesPacketGenerator { /// NumberOfReadyPesPackets(). /// If this returns false, the object may end up in an undefined state. /// @return true on success, false otherwise. - virtual bool PushSample(std::shared_ptr sample); + virtual bool PushSample(const MediaSample& sample); /// @return The number of PES packets that are ready to be consumed. virtual size_t NumberOfReadyPesPackets(); diff --git a/packager/media/formats/mp2t/pes_packet_generator_unittest.cc b/packager/media/formats/mp2t/pes_packet_generator_unittest.cc index db6d79ecdd..00e025dc43 100644 --- a/packager/media/formats/mp2t/pes_packet_generator_unittest.cc +++ b/packager/media/formats/mp2t/pes_packet_generator_unittest.cc @@ -205,7 +205,7 @@ TEST_F(PesPacketGeneratorTest, AddVideoSample) { UseMockNalUnitToByteStreamConverter(std::move(mock)); - EXPECT_TRUE(generator_.PushSample(sample)); + EXPECT_TRUE(generator_.PushSample(*sample)); EXPECT_EQ(1u, generator_.NumberOfReadyPesPackets()); std::unique_ptr pes_packet = generator_.GetNextPesPacket(); ASSERT_TRUE(pes_packet); @@ -252,7 +252,7 @@ TEST_F(PesPacketGeneratorTest, AddEncryptedVideoSample) { UseMockNalUnitToByteStreamConverter(std::move(mock)); - EXPECT_TRUE(generator_.PushSample(sample)); + EXPECT_TRUE(generator_.PushSample(*sample)); EXPECT_EQ(1u, generator_.NumberOfReadyPesPackets()); std::unique_ptr pes_packet = generator_.GetNextPesPacket(); ASSERT_TRUE(pes_packet); @@ -285,7 +285,7 @@ TEST_F(PesPacketGeneratorTest, AddVideoSampleFailedToConvert) { UseMockNalUnitToByteStreamConverter(std::move(mock)); - EXPECT_FALSE(generator_.PushSample(sample)); + EXPECT_FALSE(generator_.PushSample(*sample)); EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets()); EXPECT_TRUE(generator_.Flush()); } @@ -308,7 +308,7 @@ TEST_F(PesPacketGeneratorTest, AddAudioSample) { UseMockAACAudioSpecificConfig(std::move(mock)); - EXPECT_TRUE(generator_.PushSample(sample)); + EXPECT_TRUE(generator_.PushSample(*sample)); EXPECT_EQ(1u, generator_.NumberOfReadyPesPackets()); std::unique_ptr pes_packet = generator_.GetNextPesPacket(); ASSERT_TRUE(pes_packet); @@ -335,7 +335,7 @@ TEST_F(PesPacketGeneratorTest, AddAudioSampleFailedToConvert) { UseMockAACAudioSpecificConfig(std::move(mock)); - EXPECT_FALSE(generator_.PushSample(sample)); + EXPECT_FALSE(generator_.PushSample(*sample)); EXPECT_EQ(0u, generator_.NumberOfReadyPesPackets()); EXPECT_TRUE(generator_.Flush()); } @@ -369,7 +369,7 @@ TEST_F(PesPacketGeneratorTest, TimeStampScaling) { UseMockNalUnitToByteStreamConverter(std::move(mock)); - EXPECT_TRUE(generator_.PushSample(sample)); + EXPECT_TRUE(generator_.PushSample(*sample)); EXPECT_EQ(1u, generator_.NumberOfReadyPesPackets()); std::unique_ptr pes_packet = generator_.GetNextPesPacket(); ASSERT_TRUE(pes_packet); diff --git a/packager/media/formats/mp2t/ts_muxer.cc b/packager/media/formats/mp2t/ts_muxer.cc index 92e3bf99c6..30d5f0ccf8 100644 --- a/packager/media/formats/mp2t/ts_muxer.cc +++ b/packager/media/formats/mp2t/ts_muxer.cc @@ -32,19 +32,18 @@ Status TsMuxer::Finalize() { return segmenter_->Finalize(); } -Status TsMuxer::AddSample(size_t stream_id, - std::shared_ptr sample) { +Status TsMuxer::AddSample(size_t stream_id, const MediaSample& sample) { DCHECK_EQ(stream_id, 0u); return segmenter_->AddSample(sample); } Status TsMuxer::FinalizeSegment(size_t stream_id, - std::shared_ptr segment_info) { + const SegmentInfo& segment_info) { DCHECK_EQ(stream_id, 0u); - return segment_info->is_subsegment + return segment_info.is_subsegment ? Status::OK - : segmenter_->FinalizeSegment(segment_info->start_timestamp, - segment_info->duration); + : segmenter_->FinalizeSegment(segment_info.start_timestamp, + segment_info.duration); } void TsMuxer::FireOnMediaStartEvent() { diff --git a/packager/media/formats/mp2t/ts_muxer.h b/packager/media/formats/mp2t/ts_muxer.h index 51001637fa..e0129dff65 100644 --- a/packager/media/formats/mp2t/ts_muxer.h +++ b/packager/media/formats/mp2t/ts_muxer.h @@ -27,9 +27,9 @@ class TsMuxer : public Muxer { Status InitializeMuxer() override; Status Finalize() override; Status AddSample(size_t stream_id, - std::shared_ptr sample) override; + const MediaSample& sample) override; Status FinalizeSegment(size_t stream_id, - std::shared_ptr sample) override; + const SegmentInfo& sample) override; void FireOnMediaStartEvent(); void FireOnMediaEndEvent(); diff --git a/packager/media/formats/mp2t/ts_segmenter.cc b/packager/media/formats/mp2t/ts_segmenter.cc index f9e2dde3c3..4793084fc8 100644 --- a/packager/media/formats/mp2t/ts_segmenter.cc +++ b/packager/media/formats/mp2t/ts_segmenter.cc @@ -45,11 +45,11 @@ Status TsSegmenter::Finalize() { return Status::OK; } -Status TsSegmenter::AddSample(std::shared_ptr sample) { - if (sample->is_encrypted()) +Status TsSegmenter::AddSample(const MediaSample& sample) { + if (sample.is_encrypted()) ts_writer_->SignalEncrypted(); - if (!ts_writer_file_opened_ && !sample->is_key_frame()) + if (!ts_writer_file_opened_ && !sample.is_key_frame()) LOG(WARNING) << "A segment will start with a non key frame."; if (!pes_packet_generator_->PushSample(sample)) { diff --git a/packager/media/formats/mp2t/ts_segmenter.h b/packager/media/formats/mp2t/ts_segmenter.h index 89d0aa7a26..06f7dc2ee4 100644 --- a/packager/media/formats/mp2t/ts_segmenter.h +++ b/packager/media/formats/mp2t/ts_segmenter.h @@ -46,7 +46,7 @@ class TsSegmenter { /// @param sample gets added to this object. /// @return OK on success. - Status AddSample(std::shared_ptr sample); + Status AddSample(const MediaSample& sample); /// Flush all the samples that are (possibly) buffered and write them to the /// current segment, this will close the file. If a file is not already opened diff --git a/packager/media/formats/mp2t/ts_segmenter_unittest.cc b/packager/media/formats/mp2t/ts_segmenter_unittest.cc index 4073d37c89..4914a1be65 100644 --- a/packager/media/formats/mp2t/ts_segmenter_unittest.cc +++ b/packager/media/formats/mp2t/ts_segmenter_unittest.cc @@ -52,7 +52,7 @@ const uint8_t kAnyData[] = { class MockPesPacketGenerator : public PesPacketGenerator { public: MOCK_METHOD1(Initialize, bool(const StreamInfo& info)); - MOCK_METHOD1(PushSample, bool(std::shared_ptr sample)); + MOCK_METHOD1(PushSample, bool(const MediaSample& sample)); MOCK_METHOD0(NumberOfReadyPesPackets, size_t()); @@ -161,7 +161,7 @@ TEST_F(TsSegmenterTest, AddSample) { std::move(mock_pes_packet_generator_)); EXPECT_OK(segmenter.Initialize(*stream_info)); - EXPECT_OK(segmenter.AddSample(sample)); + EXPECT_OK(segmenter.AddSample(*sample)); } // This will add one sample then finalize segment then add another sample. @@ -257,9 +257,9 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) { segmenter.InjectPesPacketGeneratorForTesting( std::move(mock_pes_packet_generator_)); EXPECT_OK(segmenter.Initialize(*stream_info)); - EXPECT_OK(segmenter.AddSample(sample1)); + EXPECT_OK(segmenter.AddSample(*sample1)); EXPECT_OK(segmenter.FinalizeSegment(kFirstPts, sample1->duration())); - EXPECT_OK(segmenter.AddSample(sample2)); + EXPECT_OK(segmenter.AddSample(*sample2)); } // Finalize right after Initialize(). The writer will not be initialized. @@ -395,13 +395,13 @@ TEST_F(TsSegmenterTest, EncryptedSample) { std::move(mock_pes_packet_generator_)); EXPECT_OK(segmenter.Initialize(*stream_info)); - EXPECT_OK(segmenter.AddSample(sample1)); + EXPECT_OK(segmenter.AddSample(*sample1)); EXPECT_OK(segmenter.FinalizeSegment(1, sample1->duration())); // Signal encrypted if sample is encrypted. EXPECT_CALL(*mock_ts_writer_raw, SignalEncrypted()); sample2->set_is_encrypted(true); - EXPECT_OK(segmenter.AddSample(sample2)); + EXPECT_OK(segmenter.AddSample(*sample2)); } } // namespace mp2t diff --git a/packager/media/formats/mp4/fragmenter.cc b/packager/media/formats/mp4/fragmenter.cc index 85fcf5044f..6d43f8e6e7 100644 --- a/packager/media/formats/mp4/fragmenter.cc +++ b/packager/media/formats/mp4/fragmenter.cc @@ -44,7 +44,7 @@ void NewSampleEncryptionEntry(const DecryptConfig& decrypt_config, } // namespace -Fragmenter::Fragmenter(std::shared_ptr stream_info, +Fragmenter::Fragmenter(std::shared_ptr stream_info, TrackFragment* traf) : stream_info_(std::move(stream_info)), use_decoding_timestamp_in_timeline_(false), @@ -61,40 +61,39 @@ Fragmenter::Fragmenter(std::shared_ptr stream_info, Fragmenter::~Fragmenter() {} -Status Fragmenter::AddSample(std::shared_ptr sample) { - DCHECK(sample); - if (sample->duration() == 0) { +Status Fragmenter::AddSample(const MediaSample& sample) { + if (sample.duration() == 0) { LOG(WARNING) << "Unexpected sample with zero duration @ dts " - << sample->dts(); + << sample.dts(); } if (!fragment_initialized_) { - Status status = InitializeFragment(sample->dts()); + Status status = InitializeFragment(sample.dts()); if (!status.ok()) return status; } - if (sample->side_data_size() > 0) + if (sample.side_data_size() > 0) LOG(WARNING) << "MP4 samples do not support side data. Side data ignored."; // Fill in sample parameters. It will be optimized later. traf_->runs[0].sample_sizes.push_back( - static_cast(sample->data_size())); - traf_->runs[0].sample_durations.push_back(sample->duration()); + static_cast(sample.data_size())); + traf_->runs[0].sample_durations.push_back(sample.duration()); traf_->runs[0].sample_flags.push_back( - sample->is_key_frame() ? 0 : TrackFragmentHeader::kNonKeySampleMask); + sample.is_key_frame() ? 0 : TrackFragmentHeader::kNonKeySampleMask); - if (sample->decrypt_config()) { + if (sample.decrypt_config()) { NewSampleEncryptionEntry( - *sample->decrypt_config(), + *sample.decrypt_config(), !stream_info_->encryption_config().constant_iv.empty(), traf_); } - data_->AppendArray(sample->data(), sample->data_size()); - fragment_duration_ += sample->duration(); + data_->AppendArray(sample.data(), sample.data_size()); + fragment_duration_ += sample.duration(); - const int64_t pts = sample->pts(); - const int64_t dts = sample->dts(); + const int64_t pts = sample.pts(); + const int64_t dts = sample.dts(); const int64_t timestamp = use_decoding_timestamp_in_timeline_ ? dts : pts; // Set |earliest_presentation_time_| to |timestamp| if |timestamp| is smaller @@ -106,7 +105,7 @@ Status Fragmenter::AddSample(std::shared_ptr sample) { if (pts != dts) traf_->runs[0].flags |= TrackFragmentRun::kSampleCompTimeOffsetsPresentMask; - if (sample->is_key_frame()) { + if (sample.is_key_frame()) { if (first_sap_time_ == kInvalidTime) first_sap_time_ = pts; } diff --git a/packager/media/formats/mp4/fragmenter.h b/packager/media/formats/mp4/fragmenter.h index 36856e577a..1d2ca36054 100644 --- a/packager/media/formats/mp4/fragmenter.h +++ b/packager/media/formats/mp4/fragmenter.h @@ -31,14 +31,14 @@ class Fragmenter { public: /// @param info contains stream information. /// @param traf points to a TrackFragment box. - Fragmenter(std::shared_ptr info, TrackFragment* traf); + Fragmenter(std::shared_ptr info, TrackFragment* traf); ~Fragmenter(); /// Add a sample to the fragmenter. /// @param sample points to the sample to be added. /// @return OK on success, an error status otherwise. - Status AddSample(std::shared_ptr sample); + Status AddSample(const MediaSample& sample); /// Initialize the fragment with default data. /// @param first_sample_dts specifies the decoding timestamp for the first @@ -86,7 +86,7 @@ class Fragmenter { // Check if the current fragment starts with SAP. bool StartsWithSAP(); - std::shared_ptr stream_info_; + std::shared_ptr stream_info_; bool use_decoding_timestamp_in_timeline_; TrackFragment* traf_; uint64_t seek_preroll_; diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index df22fef135..b6aa59604b 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -124,7 +124,7 @@ Status MP4Muxer::InitializeMuxer() { if (streams()[0]->stream_type() == kStreamVideo) { codec_fourcc = CodecToFourCC(streams()[0]->codec(), - static_cast(streams()[0].get()) + static_cast(streams()[0].get()) ->h26x_stream_format()); if (codec_fourcc != FOURCC_NULL) ftyp->compatible_brands.push_back(codec_fourcc); @@ -155,16 +155,22 @@ Status MP4Muxer::InitializeMuxer() { switch (streams()[i]->stream_type()) { case kStreamVideo: - GenerateVideoTrak(static_cast(streams()[i].get()), - &trak, i + 1); + GenerateVideoTrak( + static_cast(streams()[i].get()), + &trak, + i + 1); break; case kStreamAudio: - GenerateAudioTrak(static_cast(streams()[i].get()), - &trak, i + 1); + GenerateAudioTrak( + static_cast(streams()[i].get()), + &trak, + i + 1); break; case kStreamText: - GenerateTextTrak(static_cast(streams()[i].get()), - &trak, i + 1); + GenerateTextTrak( + static_cast(streams()[i].get()), + &trak, + i + 1); break; default: NOTIMPLEMENTED() << "Not implemented for stream type: " @@ -210,19 +216,18 @@ Status MP4Muxer::Finalize() { return Status::OK; } -Status MP4Muxer::AddSample(size_t stream_id, - std::shared_ptr sample) { +Status MP4Muxer::AddSample(size_t stream_id, const MediaSample& sample) { DCHECK(segmenter_); return segmenter_->AddSample(stream_id, sample); } Status MP4Muxer::FinalizeSegment(size_t stream_id, - std::shared_ptr segment_info) { + const SegmentInfo& segment_info) { DCHECK(segmenter_); - VLOG(3) << "Finalize " << (segment_info->is_subsegment ? "sub" : "") - << "segment " << segment_info->start_timestamp << " duration " - << segment_info->duration; - return segmenter_->FinalizeSegment(stream_id, std::move(segment_info)); + VLOG(3) << "Finalize " << (segment_info.is_subsegment ? "sub" : "") + << "segment " << segment_info.start_timestamp << " duration " + << segment_info.duration; + return segmenter_->FinalizeSegment(stream_id, segment_info); } void MP4Muxer::InitializeTrak(const StreamInfo* info, Track* trak) { diff --git a/packager/media/formats/mp4/mp4_muxer.h b/packager/media/formats/mp4/mp4_muxer.h index 4f56922ec3..421d65bb21 100644 --- a/packager/media/formats/mp4/mp4_muxer.h +++ b/packager/media/formats/mp4/mp4_muxer.h @@ -39,10 +39,9 @@ class MP4Muxer : public Muxer { // Muxer implementation overrides. Status InitializeMuxer() override; Status Finalize() override; - Status AddSample(size_t stream_id, - std::shared_ptr sample) override; + Status AddSample(size_t stream_id, const MediaSample& sample) override; Status FinalizeSegment(size_t stream_id, - std::shared_ptr segment_info) override; + const SegmentInfo& segment_info) override; // Generate Audio/Video Track box. void InitializeTrak(const StreamInfo* info, Track* trak); diff --git a/packager/media/formats/mp4/segmenter.cc b/packager/media/formats/mp4/segmenter.cc index 8e1bb56b64..51b552b1d3 100644 --- a/packager/media/formats/mp4/segmenter.cc +++ b/packager/media/formats/mp4/segmenter.cc @@ -47,7 +47,7 @@ Segmenter::Segmenter(const MuxerOptions& options, Segmenter::~Segmenter() {} Status Segmenter::Initialize( - const std::vector>& streams, + const std::vector>& streams, MuxerListener* muxer_listener, ProgressListener* progress_listener) { DCHECK_LT(0u, streams.size()); @@ -112,12 +112,11 @@ Status Segmenter::Finalize() { return DoFinalize(); } -Status Segmenter::AddSample(size_t stream_id, - std::shared_ptr sample) { +Status Segmenter::AddSample(size_t stream_id, const MediaSample& sample) { // Set default sample duration if it has not been set yet. if (moov_->extends.tracks[stream_id].default_sample_duration == 0) { moov_->extends.tracks[stream_id].default_sample_duration = - sample->duration(); + sample.duration(); } DCHECK_LT(stream_id, fragmenters_.size()); @@ -132,17 +131,17 @@ Status Segmenter::AddSample(size_t stream_id, return status; if (sample_duration_ == 0) - sample_duration_ = sample->duration(); - stream_durations_[stream_id] += sample->duration(); + sample_duration_ = sample.duration(); + stream_durations_[stream_id] += sample.duration(); return Status::OK; } Status Segmenter::FinalizeSegment(size_t stream_id, - std::shared_ptr segment_info) { - if (segment_info->key_rotation_encryption_config) { + const SegmentInfo& segment_info) { + if (segment_info.key_rotation_encryption_config) { FinalizeFragmentForKeyRotation( - stream_id, segment_info->is_encrypted, - *segment_info->key_rotation_encryption_config); + stream_id, segment_info.is_encrypted, + *segment_info.key_rotation_encryption_config); } DCHECK_LT(stream_id, fragmenters_.size()); @@ -200,7 +199,7 @@ Status Segmenter::FinalizeSegment(size_t stream_id, for (std::unique_ptr& fragmenter : fragmenters_) fragmenter->ClearFragmentFinalized(); - if (!segment_info->is_subsegment) { + if (!segment_info.is_subsegment) { Status status = DoFinalizeSegment(); // Reset segment information to initial state. sidx_->references.clear(); diff --git a/packager/media/formats/mp4/segmenter.h b/packager/media/formats/mp4/segmenter.h index 1423e8e0e3..3487067b05 100644 --- a/packager/media/formats/mp4/segmenter.h +++ b/packager/media/formats/mp4/segmenter.h @@ -54,9 +54,10 @@ class Segmenter { /// @param muxer_listener receives muxer events. Can be NULL. /// @param progress_listener receives progress updates. Can be NULL. /// @return OK on success, an error status otherwise. - Status Initialize(const std::vector>& streams, - MuxerListener* muxer_listener, - ProgressListener* progress_listener); + Status Initialize( + const std::vector>& streams, + MuxerListener* muxer_listener, + ProgressListener* progress_listener); /// Finalize the segmenter. /// @return OK on success, an error status otherwise. @@ -66,14 +67,13 @@ class Segmenter { /// @param stream_id is the zero-based stream index. /// @param sample points to the sample to be added. /// @return OK on success, an error status otherwise. - Status AddSample(size_t stream_id, std::shared_ptr sample); + Status AddSample(size_t stream_id, const MediaSample& sample); /// Finalize the segment / subsegment. /// @param stream_id is the zero-based stream index. /// @param is_subsegment indicates if it is a subsegment (fragment). /// @return OK on success, an error status otherwise. - Status FinalizeSegment(size_t stream_id, - std::shared_ptr segment_info); + Status FinalizeSegment(size_t stream_id, const SegmentInfo& segment_info); // TODO(rkuroiwa): Change these Get*Range() methods to return // base::Optional as well. diff --git a/packager/media/formats/webm/encrypted_segmenter_unittest.cc b/packager/media/formats/webm/encrypted_segmenter_unittest.cc index 02c65df08d..9a8818b4e2 100644 --- a/packager/media/formats/webm/encrypted_segmenter_unittest.cc +++ b/packager/media/formats/webm/encrypted_segmenter_unittest.cc @@ -209,7 +209,7 @@ class EncryptedSegmenterTest : public SegmentTestBase { void InitializeSegmenter(const MuxerOptions& options) { ASSERT_NO_FATAL_FAILURE( CreateAndInitializeSegmenter( - options, info_.get(), &segmenter_)); + options, *info_, &segmenter_)); } std::shared_ptr info_; @@ -236,7 +236,7 @@ TEST_F(EncryptedSegmenterTest, BasicSupport) { std::vector())); sample->set_decrypt_config(std::move(decrypt_config)); } - ASSERT_OK(segmenter_->AddSample(sample)); + ASSERT_OK(segmenter_->AddSample(*sample)); } ASSERT_OK( segmenter_->FinalizeSegment(3 * kDuration, 2 * kDuration, !kSubsegment)); diff --git a/packager/media/formats/webm/multi_segment_segmenter_unittest.cc b/packager/media/formats/webm/multi_segment_segmenter_unittest.cc index 2cde896ca5..50b1924a02 100644 --- a/packager/media/formats/webm/multi_segment_segmenter_unittest.cc +++ b/packager/media/formats/webm/multi_segment_segmenter_unittest.cc @@ -103,7 +103,7 @@ class MultiSegmentSegmenterTest : public SegmentTestBase { void InitializeSegmenter(const MuxerOptions& options) { ASSERT_NO_FATAL_FAILURE( CreateAndInitializeSegmenter( - options, info_.get(), &segmenter_)); + options, *info_, &segmenter_)); } std::string TemplateFileName(int number) const { @@ -124,7 +124,7 @@ TEST_F(MultiSegmentSegmenterTest, BasicSupport) { for (int i = 0; i < 5; i++) { std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, kNoSideData); - ASSERT_OK(segmenter_->AddSample(sample)); + ASSERT_OK(segmenter_->AddSample(*sample)); } ASSERT_OK(segmenter_->FinalizeSegment(0, 8 * kDuration, !kSubsegment)); ASSERT_OK(segmenter_->Finalize()); @@ -148,7 +148,7 @@ TEST_F(MultiSegmentSegmenterTest, SplitsFilesOnSegment) { ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, !kSubsegment)); std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, kNoSideData); - ASSERT_OK(segmenter_->AddSample(sample)); + ASSERT_OK(segmenter_->AddSample(*sample)); } ASSERT_OK( segmenter_->FinalizeSegment(5 * kDuration, 8 * kDuration, !kSubsegment)); @@ -178,7 +178,7 @@ TEST_F(MultiSegmentSegmenterTest, SplitsClustersOnSubsegment) { ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, kSubsegment)); std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, kNoSideData); - ASSERT_OK(segmenter_->AddSample(sample)); + ASSERT_OK(segmenter_->AddSample(*sample)); } ASSERT_OK(segmenter_->FinalizeSegment(0, 8 * kDuration, !kSubsegment)); ASSERT_OK(segmenter_->Finalize()); diff --git a/packager/media/formats/webm/segmenter.cc b/packager/media/formats/webm/segmenter.cc index fe46bdb05f..6715c104f0 100644 --- a/packager/media/formats/webm/segmenter.cc +++ b/packager/media/formats/webm/segmenter.cc @@ -75,14 +75,17 @@ Segmenter::Segmenter(const MuxerOptions& options) : options_(options) {} Segmenter::~Segmenter() {} -Status Segmenter::Initialize(StreamInfo* info, +Status Segmenter::Initialize(const StreamInfo& info, ProgressListener* progress_listener, MuxerListener* muxer_listener) { + is_encrypted_ = info.is_encrypted(); + duration_ = info.duration(); + time_scale_ = info.time_scale(); + muxer_listener_ = muxer_listener; - info_ = info; // Use media duration as progress target. - progress_target_ = info_->duration(); + progress_target_ = info.duration(); progress_listener_ = progress_listener; segment_info_.Init(); @@ -106,39 +109,39 @@ Status Segmenter::Initialize(StreamInfo* info, unsigned int seed = 0; std::unique_ptr track; Status status; - switch (info_->stream_type()) { + switch (info.stream_type()) { case kStreamVideo: { std::unique_ptr video_track(new VideoTrack(&seed)); - status = InitializeVideoTrack(static_cast(info_), + status = InitializeVideoTrack(static_cast(info), video_track.get()); track = std::move(video_track); break; } case kStreamAudio: { std::unique_ptr audio_track(new AudioTrack(&seed)); - status = InitializeAudioTrack(static_cast(info_), + status = InitializeAudioTrack(static_cast(info), audio_track.get()); track = std::move(audio_track); break; } default: NOTIMPLEMENTED() << "Not implemented for stream type: " - << info_->stream_type(); + << info.stream_type(); status = Status(error::UNIMPLEMENTED, "Not implemented for stream type"); } if (!status.ok()) return status; - if (info_->is_encrypted()) { - if (info->encryption_config().per_sample_iv_size != kWebMIvSize) + if (info.is_encrypted()) { + if (info.encryption_config().per_sample_iv_size != kWebMIvSize) return Status(error::MUXER_FAILURE, "Incorrect size WebM encryption IV."); - status = UpdateTrackForEncryption(info_->encryption_config().key_id, + status = UpdateTrackForEncryption(info.encryption_config().key_id, track.get()); if (!status.ok()) return status; } - tracks_.AddTrack(track.get(), info_->track_id()); + tracks_.AddTrack(track.get(), info.track_id()); // number() is only available after the above instruction. track_id_ = track->number(); // |tracks_| owns |track|. @@ -153,7 +156,9 @@ Status Segmenter::Finalize() { return DoFinalize(); } -Status Segmenter::AddSample(std::shared_ptr sample) { +Status Segmenter::AddSample(const MediaSample& source_sample) { + std::shared_ptr sample = MediaSample::CopyFrom(source_sample); + if (sample_duration_ == 0) { first_timestamp_ = sample->pts(); sample_duration_ = sample->duration(); @@ -178,7 +183,7 @@ Status Segmenter::AddSample(std::shared_ptr sample) { if (!status.ok()) return status; - if (info_->is_encrypted()) + if (is_encrypted_) UpdateFrameForEncryption(sample.get()); new_subsegment_ = false; @@ -205,14 +210,14 @@ float Segmenter::GetDurationInSeconds() const { uint64_t Segmenter::FromBmffTimestamp(uint64_t bmff_timestamp) { return NsToWebMTimecode( - BmffTimestampToNs(bmff_timestamp, info_->time_scale()), + BmffTimestampToNs(bmff_timestamp, time_scale_), segment_info_.timecode_scale()); } uint64_t Segmenter::FromWebMTimecode(uint64_t webm_timecode) { return NsToBmffTimestamp( WebMTimecodeToNs(webm_timecode, segment_info_.timecode_scale()), - info_->time_scale()); + time_scale_); } Status Segmenter::WriteSegmentHeader(uint64_t file_size, MkvWriter* writer) { @@ -277,17 +282,17 @@ void Segmenter::UpdateProgress(uint64_t progress) { } } -Status Segmenter::InitializeVideoTrack(const VideoStreamInfo* info, +Status Segmenter::InitializeVideoTrack(const VideoStreamInfo& info, VideoTrack* track) { - if (info->codec() == kCodecVP8) { + if (info.codec() == kCodecVP8) { track->set_codec_id(mkvmuxer::Tracks::kVp8CodecId); - } else if (info->codec() == kCodecVP9) { + } else if (info.codec() == kCodecVP9) { track->set_codec_id(mkvmuxer::Tracks::kVp9CodecId); // The |StreamInfo::codec_config| field is stored using the MP4 format; we // need to convert it to the WebM format. VPCodecConfigurationRecord vp_config; - if (!vp_config.ParseMP4(info->codec_config())) { + if (!vp_config.ParseMP4(info.codec_config())) { return Status(error::INTERNAL_ERROR, "Unable to parse VP9 codec configuration"); } @@ -319,43 +324,43 @@ Status Segmenter::InitializeVideoTrack(const VideoStreamInfo* info, "Only VP8 and VP9 video codecs are supported in WebM."); } - track->set_uid(info->track_id()); - if (!info->language().empty()) - track->set_language(info->language().c_str()); + track->set_uid(info.track_id()); + if (!info.language().empty()) + track->set_language(info.language().c_str()); track->set_type(mkvmuxer::Tracks::kVideo); - track->set_width(info->width()); - track->set_height(info->height()); - track->set_display_height(info->height()); - track->set_display_width(info->width() * info->pixel_width() / - info->pixel_height()); + track->set_width(info.width()); + track->set_height(info.height()); + track->set_display_height(info.height()); + track->set_display_width(info.width() * info.pixel_width() / + info.pixel_height()); return Status::OK; } -Status Segmenter::InitializeAudioTrack(const AudioStreamInfo* info, +Status Segmenter::InitializeAudioTrack(const AudioStreamInfo& info, AudioTrack* track) { - if (info->codec() == kCodecOpus) { + if (info.codec() == kCodecOpus) { track->set_codec_id(mkvmuxer::Tracks::kOpusCodecId); - } else if (info->codec() == kCodecVorbis) { + } else if (info.codec() == kCodecVorbis) { track->set_codec_id(mkvmuxer::Tracks::kVorbisCodecId); } else { LOG(ERROR) << "Only Vorbis and Opus audio codec are supported in WebM."; return Status(error::UNIMPLEMENTED, "Only Vorbis and Opus audio codecs are supported in WebM."); } - if (!track->SetCodecPrivate(info->codec_config().data(), - info->codec_config().size())) { + if (!track->SetCodecPrivate(info.codec_config().data(), + info.codec_config().size())) { return Status(error::INTERNAL_ERROR, "Private codec data required for audio streams"); } - track->set_uid(info->track_id()); - if (!info->language().empty()) - track->set_language(info->language().c_str()); + track->set_uid(info.track_id()); + if (!info.language().empty()) + track->set_language(info.language().c_str()); track->set_type(mkvmuxer::Tracks::kAudio); - track->set_sample_rate(info->sampling_frequency()); - track->set_channels(info->num_channels()); - track->set_seek_pre_roll(info->seek_preroll_ns()); - track->set_codec_delay(info->codec_delay_ns()); + track->set_sample_rate(info.sampling_frequency()); + track->set_channels(info.num_channels()); + track->set_seek_pre_roll(info.seek_preroll_ns()); + track->set_codec_delay(info.codec_delay_ns()); return Status::OK; } @@ -372,11 +377,11 @@ Status Segmenter::WriteFrame(bool write_duration) { if (write_duration) { frame.set_duration( - BmffTimestampToNs(prev_sample_->duration(), info_->time_scale())); + BmffTimestampToNs(prev_sample_->duration(), time_scale_)); } frame.set_is_key(prev_sample_->is_key_frame()); frame.set_timestamp( - BmffTimestampToNs(prev_sample_->pts(), info_->time_scale())); + BmffTimestampToNs(prev_sample_->pts(), time_scale_)); frame.set_track_number(track_id_); if (prev_sample_->side_data_size() > 0) { @@ -397,7 +402,7 @@ Status Segmenter::WriteFrame(bool write_duration) { if (!prev_sample_->is_key_frame() && !frame.CanBeSimpleBlock()) { frame.set_reference_block_timestamp( - BmffTimestampToNs(reference_frame_timestamp_, info_->time_scale())); + BmffTimestampToNs(reference_frame_timestamp_, time_scale_)); } // GetRelativeTimecode will return -1 if the relative timecode is too large diff --git a/packager/media/formats/webm/segmenter.h b/packager/media/formats/webm/segmenter.h index 1b59dba100..c361b077c4 100644 --- a/packager/media/formats/webm/segmenter.h +++ b/packager/media/formats/webm/segmenter.h @@ -41,7 +41,7 @@ class Segmenter { /// @param info The stream info for the stream being segmented. /// @param muxer_listener receives muxer events. Can be NULL. /// @return OK on success, an error status otherwise. - Status Initialize(StreamInfo* info, + Status Initialize(const StreamInfo& info, ProgressListener* progress_listener, MuxerListener* muxer_listener); @@ -52,7 +52,7 @@ class Segmenter { /// Add sample to the indicated stream. /// @param sample points to the sample to be added. /// @return OK on success, an error status otherwise. - Status AddSample(std::shared_ptr sample); + Status AddSample(const MediaSample& sample); /// Finalize the (sub)segment. virtual Status FinalizeSegment(uint64_t start_timestamp, @@ -95,19 +95,20 @@ class Segmenter { mkvmuxer::Cluster* cluster() { return cluster_.get(); } mkvmuxer::Cues* cues() { return &cues_; } MuxerListener* muxer_listener() { return muxer_listener_; } - StreamInfo* info() { return info_; } SeekHead* seek_head() { return &seek_head_; } int track_id() const { return track_id_; } uint64_t segment_payload_pos() const { return segment_payload_pos_; } + uint64_t duration() const { return duration_; } + virtual Status DoInitialize() = 0; virtual Status DoFinalize() = 0; private: - Status InitializeAudioTrack(const AudioStreamInfo* info, + Status InitializeAudioTrack(const AudioStreamInfo& info, mkvmuxer::AudioTrack* track); - Status InitializeVideoTrack(const VideoStreamInfo* info, + Status InitializeVideoTrack(const VideoStreamInfo& info, mkvmuxer::VideoTrack* track); // Writes the previous frame to the file. @@ -120,7 +121,7 @@ class Segmenter { virtual Status NewSegment(uint64_t start_timestamp, bool is_subsegment) = 0; // Store the previous sample so we know which one is the last frame. - std::shared_ptr prev_sample_; + std::shared_ptr prev_sample_; // The reference frame timestamp; used to populate the ReferenceBlock element // when writing non-keyframe BlockGroups. uint64_t reference_frame_timestamp_ = 0; @@ -133,7 +134,6 @@ class Segmenter { mkvmuxer::SegmentInfo segment_info_; mkvmuxer::Tracks tracks_; - StreamInfo* info_ = nullptr; MuxerListener* muxer_listener_ = nullptr; ProgressListener* progress_listener_ = nullptr; uint64_t progress_target_ = 0; @@ -151,6 +151,11 @@ class Segmenter { bool new_subsegment_ = false; int track_id_ = 0; + // The subset of information that we need from StreamInfo + bool is_encrypted_ = false; + uint64_t time_scale_ = 0; + uint64_t duration_ = 0; + DISALLOW_COPY_AND_ASSIGN(Segmenter); }; diff --git a/packager/media/formats/webm/segmenter_test_base.h b/packager/media/formats/webm/segmenter_test_base.h index e7c05c426e..4d190574a4 100644 --- a/packager/media/formats/webm/segmenter_test_base.h +++ b/packager/media/formats/webm/segmenter_test_base.h @@ -46,7 +46,7 @@ class SegmentTestBase : public ::testing::Test { template void CreateAndInitializeSegmenter( const MuxerOptions& options, - StreamInfo* info, + const StreamInfo& info, std::unique_ptr* result) const { std::unique_ptr segmenter(new S(options)); diff --git a/packager/media/formats/webm/single_segment_segmenter_unittest.cc b/packager/media/formats/webm/single_segment_segmenter_unittest.cc index 3ce2a4b19f..c7d458ec51 100644 --- a/packager/media/formats/webm/single_segment_segmenter_unittest.cc +++ b/packager/media/formats/webm/single_segment_segmenter_unittest.cc @@ -144,7 +144,7 @@ class SingleSegmentSegmenterTest : public SegmentTestBase { void InitializeSegmenter(const MuxerOptions& options) { ASSERT_NO_FATAL_FAILURE( CreateAndInitializeSegmenter( - options, info_.get(), &segmenter_)); + options, *info_, &segmenter_)); } std::shared_ptr info_; @@ -161,7 +161,7 @@ TEST_F(SingleSegmentSegmenterTest, BasicSupport) { i == 3 ? kGenerateSideData : kNoSideData; std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, side_data_flag); - ASSERT_OK(segmenter_->AddSample(sample)); + ASSERT_OK(segmenter_->AddSample(*sample)); } ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, !kSubsegment)); ASSERT_OK(segmenter_->Finalize()); @@ -179,7 +179,7 @@ TEST_F(SingleSegmentSegmenterTest, SplitsClustersOnSegment) { ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, !kSubsegment)); std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, kNoSideData); - ASSERT_OK(segmenter_->AddSample(sample)); + ASSERT_OK(segmenter_->AddSample(*sample)); } ASSERT_OK( segmenter_->FinalizeSegment(5 * kDuration, 8 * kDuration, !kSubsegment)); @@ -203,7 +203,7 @@ TEST_F(SingleSegmentSegmenterTest, IgnoresSubsegment) { ASSERT_OK(segmenter_->FinalizeSegment(0, 5 * kDuration, kSubsegment)); std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, kNoSideData); - ASSERT_OK(segmenter_->AddSample(sample)); + ASSERT_OK(segmenter_->AddSample(*sample)); } ASSERT_OK(segmenter_->FinalizeSegment(0, 8 * kDuration, !kSubsegment)); ASSERT_OK(segmenter_->Finalize()); @@ -229,7 +229,7 @@ TEST_F(SingleSegmentSegmenterTest, LargeTimestamp) { i == 3 ? kGenerateSideData : kNoSideData; std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, side_data_flag); - ASSERT_OK(segmenter_->AddSample(sample)); + ASSERT_OK(segmenter_->AddSample(*sample)); } ASSERT_OK(segmenter_->FinalizeSegment(kLargeTimestamp, 5 * kDuration, !kSubsegment)); @@ -265,7 +265,7 @@ TEST_F(SingleSegmentSegmenterTest, ReallyLargeTimestamp) { i == 3 ? kGenerateSideData : kNoSideData; std::shared_ptr sample = CreateSample(kKeyFrame, kDuration, side_data_flag); - ASSERT_OK(segmenter_->AddSample(sample)); + ASSERT_OK(segmenter_->AddSample(*sample)); } ASSERT_OK(segmenter_->FinalizeSegment(kReallyLargeTimestamp, 5 * kDuration, !kSubsegment)); diff --git a/packager/media/formats/webm/two_pass_single_segment_segmenter.cc b/packager/media/formats/webm/two_pass_single_segment_segmenter.cc index 5e1f710157..5d99f41478 100644 --- a/packager/media/formats/webm/two_pass_single_segment_segmenter.cc +++ b/packager/media/formats/webm/two_pass_single_segment_segmenter.cc @@ -71,7 +71,7 @@ TwoPassSingleSegmentSegmenter::~TwoPassSingleSegmentSegmenter() {} Status TwoPassSingleSegmentSegmenter::DoInitialize() { // Assume the amount of time to copy the temp file as the same amount // of time as to make it. - set_progress_target(info()->duration() * 2); + set_progress_target(duration() * 2); if (!TempFilePath(options().temp_dir, &temp_file_name_)) return Status(error::FILE_FAILURE, "Unable to create temporary file."); diff --git a/packager/media/formats/webm/webm_muxer.cc b/packager/media/formats/webm/webm_muxer.cc index 93dda11a5b..793744ed49 100644 --- a/packager/media/formats/webm/webm_muxer.cc +++ b/packager/media/formats/webm/webm_muxer.cc @@ -38,7 +38,7 @@ Status WebMMuxer::InitializeMuxer() { } Status initialized = segmenter_->Initialize( - streams()[0].get(), progress_listener(), muxer_listener()); + *streams()[0], progress_listener(), muxer_listener()); if (!initialized.ok()) return initialized; @@ -58,26 +58,25 @@ Status WebMMuxer::Finalize() { return Status::OK; } -Status WebMMuxer::AddSample(size_t stream_id, - std::shared_ptr sample) { +Status WebMMuxer::AddSample(size_t stream_id, const MediaSample& sample) { DCHECK(segmenter_); DCHECK_EQ(stream_id, 0u); return segmenter_->AddSample(sample); } Status WebMMuxer::FinalizeSegment(size_t stream_id, - std::shared_ptr segment_info) { + const SegmentInfo& segment_info) { DCHECK(segmenter_); DCHECK_EQ(stream_id, 0u); - if (segment_info->key_rotation_encryption_config) { + if (segment_info.key_rotation_encryption_config) { NOTIMPLEMENTED() << "Key rotation is not implemented for WebM."; return Status(error::UNIMPLEMENTED, "Key rotation is not implemented for WebM"); } - return segmenter_->FinalizeSegment(segment_info->start_timestamp, - segment_info->duration, - segment_info->is_subsegment); + return segmenter_->FinalizeSegment(segment_info.start_timestamp, + segment_info.duration, + segment_info.is_subsegment); } void WebMMuxer::FireOnMediaStartEvent() { diff --git a/packager/media/formats/webm/webm_muxer.h b/packager/media/formats/webm/webm_muxer.h index e9d016dcd9..0ae2043667 100644 --- a/packager/media/formats/webm/webm_muxer.h +++ b/packager/media/formats/webm/webm_muxer.h @@ -26,10 +26,9 @@ class WebMMuxer : public Muxer { // Muxer implementation overrides. Status InitializeMuxer() override; Status Finalize() override; - Status AddSample(size_t stream_id, - std::shared_ptr sample) override; + Status AddSample(size_t stream_id, const MediaSample& sample) override; Status FinalizeSegment(size_t stream_id, - std::shared_ptr segment_info) override; + const SegmentInfo& segment_info) override; void FireOnMediaStartEvent(); void FireOnMediaEndEvent(); diff --git a/packager/media/trick_play/trick_play_handler.cc b/packager/media/trick_play/trick_play_handler.cc index 49d858c76e..106131188b 100644 --- a/packager/media/trick_play/trick_play_handler.cc +++ b/packager/media/trick_play/trick_play_handler.cc @@ -49,27 +49,29 @@ Status TrickPlayHandler::InitializeInternal() { return Status::OK; } -Status TrickPlayHandler::Process( - std::unique_ptr input_stream_data) { +Status TrickPlayHandler::Process(std::unique_ptr stream_data) { // The non-trick play stream is dispatched at index 0. // The trick-play streams are dispatched to index 1, index 2 and so on. - DCHECK_EQ(input_stream_data->stream_index, 0u); - std::unique_ptr output_stream_data(new StreamData()); - *output_stream_data = *input_stream_data; - Status status = Dispatch(std::move(output_stream_data)); + DCHECK(stream_data); + DCHECK_EQ(stream_data->stream_index, 0u); + + std::unique_ptr copy(new StreamData); + *copy = *stream_data; + Status status = Dispatch(std::move(copy)); if (!status.ok()) { return status; } - std::shared_ptr stream_data(std::move(input_stream_data)); - if (stream_data->stream_data_type == StreamDataType::kStreamInfo) { - if (stream_data->stream_info->stream_type() != kStreamVideo) { + std::shared_ptr shared_stream_data(std::move(stream_data)); + + if (shared_stream_data->stream_data_type == StreamDataType::kStreamInfo) { + if (shared_stream_data->stream_info->stream_type() != kStreamVideo) { status.SetError(error::TRICK_PLAY_ERROR, "Trick play does not support non-video stream"); return status; } const VideoStreamInfo& video_stream_info = - static_cast(*stream_data->stream_info); + static_cast(*shared_stream_data->stream_info); if (video_stream_info.trick_play_factor() > 0) { status.SetError(error::TRICK_PLAY_ERROR, "This stream is alreay a trick play stream."); @@ -77,28 +79,28 @@ Status TrickPlayHandler::Process( } } - if (stream_data->stream_data_type == StreamDataType::kSegmentInfo) { + if (shared_stream_data->stream_data_type == StreamDataType::kSegmentInfo) { for (auto& cached_data : cached_stream_data_) { // It is possible that trick play stream has large frame duration that // some segments in the main stream are skipped. To avoid empty segments, // only cache SegementInfo with MediaSample before it. if (!cached_data.empty() && cached_data.back()->stream_data_type == StreamDataType::kMediaSample) - cached_data.push_back(stream_data); + cached_data.push_back(shared_stream_data); } return Status::OK; } - if (stream_data->stream_data_type != StreamDataType::kMediaSample) { + if (shared_stream_data->stream_data_type != StreamDataType::kMediaSample) { // Non media sample stream data needs to be dispatched to every output // stream. It is just cached in every queue until a new key frame comes or // the stream is flushed. for (size_t i = 0; i < cached_stream_data_.size(); ++i) - cached_stream_data_[i].push_back(stream_data); + cached_stream_data_[i].push_back(shared_stream_data); return Status::OK; } - if (stream_data->media_sample->is_key_frame()) { + if (shared_stream_data->media_sample->is_key_frame()) { // For a new key frame, some of the trick play streams may include it. // The cached data in those trick play streams will be processed. DCHECK_EQ(trick_play_factors_.size(), cached_stream_data_.size()); @@ -118,7 +120,7 @@ Status TrickPlayHandler::Process( if (!status.ok()) return status; } - cached_stream_data_[i].push_back(stream_data); + cached_stream_data_[i].push_back(shared_stream_data); } } @@ -126,8 +128,8 @@ Status TrickPlayHandler::Process( } total_frames_++; - prev_sample_end_timestamp_ = - stream_data->media_sample->dts() + stream_data->media_sample->duration(); + prev_sample_end_timestamp_ = shared_stream_data->media_sample->dts() + + shared_stream_data->media_sample->duration(); return Status::OK; } @@ -166,7 +168,7 @@ Status TrickPlayHandler::ProcessCachedStreamData( std::deque>* cached_stream_data) { while (!cached_stream_data->empty()) { Status status = - ProcessOneStreamData(output_stream_index, cached_stream_data->front()); + ProcessOneStreamData(output_stream_index, *cached_stream_data->front()); if (!status.ok()) { return status; } @@ -175,17 +177,16 @@ Status TrickPlayHandler::ProcessCachedStreamData( return Status::OK; } -Status TrickPlayHandler::ProcessOneStreamData( - size_t output_stream_index, - const std::shared_ptr& stream_data) { +Status TrickPlayHandler::ProcessOneStreamData(size_t output_stream_index, + const StreamData& stream_data) { size_t trick_play_index = output_stream_index - 1; uint32_t trick_play_factor = trick_play_factors_[trick_play_index]; Status status; - switch (stream_data->stream_data_type) { + switch (stream_data.stream_data_type) { // trick_play_factor in StreamInfo should be modified. case StreamDataType::kStreamInfo: { const VideoStreamInfo& video_stream_info = - static_cast(*stream_data->stream_info); + static_cast(*stream_data.stream_info); std::shared_ptr trick_play_video_stream_info( new VideoStreamInfo(video_stream_info)); trick_play_video_stream_info->set_trick_play_factor(trick_play_factor); @@ -197,20 +198,20 @@ Status TrickPlayHandler::ProcessOneStreamData( break; } case StreamDataType::kMediaSample: { - if (stream_data->media_sample->is_key_frame()) { + if (stream_data.media_sample->is_key_frame()) { std::shared_ptr trick_play_media_sample = - MediaSample::CopyFrom(*(stream_data->media_sample)); + MediaSample::CopyFrom(*(stream_data.media_sample)); trick_play_media_sample->set_duration(prev_sample_end_timestamp_ - - stream_data->media_sample->dts()); + stream_data.media_sample->dts()); status = DispatchMediaSample(output_stream_index, trick_play_media_sample); } break; } default: - std::unique_ptr new_stream_data(new StreamData(*stream_data)); - new_stream_data->stream_index = output_stream_index; - status = Dispatch(std::move(new_stream_data)); + std::unique_ptr copy(new StreamData(stream_data)); + copy->stream_index = output_stream_index; + status = Dispatch(std::move(copy)); break; } return status; diff --git a/packager/media/trick_play/trick_play_handler.h b/packager/media/trick_play/trick_play_handler.h index f2486f1a3c..c9042cef6c 100644 --- a/packager/media/trick_play/trick_play_handler.h +++ b/packager/media/trick_play/trick_play_handler.h @@ -56,8 +56,9 @@ class TrickPlayHandler : public MediaHandler { // Decoding timestamp for current key media sample. It is used for calculating // the duration of previous key media sample, to make sure there is no gap // between two key media samples. - Status ProcessOneStreamData(size_t output_stream_index, - const std::shared_ptr& stream_data); + Status ProcessOneStreamData( + size_t output_stream_index, + const StreamData& stream_data); // Trick play factors. Note that there can be multiple trick play factors, // e.g., 2, 4 and 8. That means, one input video stream will generate 3 diff --git a/packager/media/trick_play/trick_play_handler_unittest.cc b/packager/media/trick_play/trick_play_handler_unittest.cc index 560072f791..f237b4a19e 100644 --- a/packager/media/trick_play/trick_play_handler_unittest.cc +++ b/packager/media/trick_play/trick_play_handler_unittest.cc @@ -88,8 +88,8 @@ TEST_F(TrickPlayHandlerTest, AudioStream) { std::end(kTrickPlayFactors)); SetUpTrickPlayHandler(trick_play_factors); - Status status = - Process(GetAudioStreamInfoStreamData(kStreamIndex0, kTimeScale)); + Status status = Process(StreamData::FromStreamInfo( + kStreamIndex0, GetAudioStreamInfo(kTimeScale))); Status kExpectStatus(error::TRICK_PLAY_ERROR, "Some Messages"); EXPECT_TRUE(status.Matches(kExpectStatus)); } @@ -101,7 +101,8 @@ TEST_F(TrickPlayHandlerTest, VideoStreamWithTrickPlay) { std::end(kTrickPlayFactors)); SetUpTrickPlayHandler(trick_play_factors); - ASSERT_OK(Process(GetVideoStreamInfoStreamData(kStreamIndex0, kTimeScale))); + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex0, GetVideoStreamInfo(kTimeScale)))); // The stream info is cached, so the output is empty. EXPECT_THAT( GetOutputStreamDataVector(), @@ -113,9 +114,12 @@ TEST_F(TrickPlayHandlerTest, VideoStreamWithTrickPlay) { const int kGOPSize = 3; for (int i = 0; i < 3; ++i) { const bool is_key_frame = (i % kGOPSize == 0); - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex0, kVideoStartTimestamp + kDuration * i, kDuration, - is_key_frame))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample( + kVideoStartTimestamp + kDuration * i, + kDuration, + is_key_frame)))); } EXPECT_THAT( @@ -136,9 +140,12 @@ TEST_F(TrickPlayHandlerTest, VideoStreamWithTrickPlay) { // ElementsAre supports at most 10 elements. for (int i = 3; i < 6; ++i) { const bool is_key_frame = (i % kGOPSize == 0); - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex0, kVideoStartTimestamp + kDuration * i, kDuration, - is_key_frame))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample( + kVideoStartTimestamp + kDuration * i, + kDuration, + is_key_frame)))); } EXPECT_THAT( @@ -166,9 +173,12 @@ TEST_F(TrickPlayHandlerTest, VideoStreamWithTrickPlay) { // ElementsAre supports at most 10 elements. for (int i = 6; i < 8; ++i) { const bool is_key_frame = (i % kGOPSize == 0); - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex0, kVideoStartTimestamp + kDuration * i, kDuration, - is_key_frame))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample( + kVideoStartTimestamp + kDuration * i, + kDuration, + is_key_frame)))); } EXPECT_THAT( @@ -219,7 +229,8 @@ TEST_F(TrickPlayHandlerTest, VideoStreamWithDecreasingTrickPlayFactors) { std::end(kTrickPlayFactorsDecreasing)); SetUpTrickPlayHandler(trick_play_factors); - ASSERT_OK(Process(GetVideoStreamInfoStreamData(kStreamIndex0, kTimeScale))); + ASSERT_OK(Process(StreamData::FromStreamInfo( + kStreamIndex0, GetVideoStreamInfo(kTimeScale)))); // The stream info is cached, so the output is empty. EXPECT_THAT( GetOutputStreamDataVector(), @@ -231,9 +242,12 @@ TEST_F(TrickPlayHandlerTest, VideoStreamWithDecreasingTrickPlayFactors) { const int kGOPSize = 3; for (int i = 0; i < 3; ++i) { const bool is_key_frame = (i % kGOPSize == 0); - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex0, kVideoStartTimestamp + kDuration * i, kDuration, - is_key_frame))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample( + kVideoStartTimestamp + kDuration * i, + kDuration, + is_key_frame)))); } EXPECT_THAT( @@ -254,9 +268,12 @@ TEST_F(TrickPlayHandlerTest, VideoStreamWithDecreasingTrickPlayFactors) { // ElementsAre supports at most 10 elements. for (int i = 3; i < 6; ++i) { const bool is_key_frame = (i % kGOPSize == 0); - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex0, kVideoStartTimestamp + kDuration * i, kDuration, - is_key_frame))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample( + kVideoStartTimestamp + kDuration * i, + kDuration, + is_key_frame)))); } EXPECT_THAT( @@ -285,9 +302,12 @@ TEST_F(TrickPlayHandlerTest, VideoStreamWithDecreasingTrickPlayFactors) { // ElementsAre supports at most 10 elements. for (int i = 6; i < 8; ++i) { const bool is_key_frame = (i % kGOPSize == 0); - ASSERT_OK(Process(GetMediaSampleStreamData( - kStreamIndex0, kVideoStartTimestamp + kDuration * i, kDuration, - is_key_frame))); + ASSERT_OK(Process(StreamData::FromMediaSample( + kStreamIndex0, + GetMediaSample( + kVideoStartTimestamp + kDuration * i, + kDuration, + is_key_frame)))); } EXPECT_THAT(