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(