diff --git a/packager/hls/base/simple_hls_notifier.cc b/packager/hls/base/simple_hls_notifier.cc index c9b36ecb2e..03e63b0cd3 100644 --- a/packager/hls/base/simple_hls_notifier.cc +++ b/packager/hls/base/simple_hls_notifier.cc @@ -146,7 +146,6 @@ bool SimpleHlsNotifier::NotifyNewStream(const MediaInfo& media_info, const std::string& group_id, uint32_t* stream_id) { DCHECK(stream_id); - *stream_id = sequence_number_.GetNext(); MediaPlaylist::MediaPlaylistType type; switch (profile()) { @@ -171,6 +170,7 @@ bool SimpleHlsNotifier::NotifyNewStream(const MediaInfo& media_info, return false; } + *stream_id = sequence_number_.GetNext(); base::AutoLock auto_lock(lock_); master_playlist_->AddMediaPlaylist(media_playlist.get()); media_playlist_map_.insert( diff --git a/packager/media/event/hls_notify_muxer_listener.cc b/packager/media/event/hls_notify_muxer_listener.cc index 96007b5d4d..fcd17bb0ff 100644 --- a/packager/media/event/hls_notify_muxer_listener.cc +++ b/packager/media/event/hls_notify_muxer_listener.cc @@ -36,6 +36,12 @@ void HlsNotifyMuxerListener::OnEncryptionInfoReady( const std::vector& key_id, const std::vector& iv, const std::vector& key_system_infos) { + if (!media_started_) { + next_key_id_ = key_id; + next_iv_ = iv; + next_key_system_infos_ = key_system_infos; + return; + } for (const ProtectionSystemSpecificInfo& info : key_system_infos) { const bool result = hls_notifier_->NotifyEncryptionUpdate( stream_id_, key_id, info.system_id(), iv, info.pssh_data()); @@ -43,6 +49,28 @@ void HlsNotifyMuxerListener::OnEncryptionInfoReady( } } +void HlsNotifyMuxerListener::OnEncryptionStart() { + if (!media_started_) { + DLOG(WARNING) << "Media not started, cannot notify encryption start."; + return; + } + if (next_key_id_.empty()) { + DCHECK(next_iv_.empty()); + DCHECK(next_key_system_infos_.empty()); + return; + } + + for (const ProtectionSystemSpecificInfo& info : next_key_system_infos_) { + const bool result = hls_notifier_->NotifyEncryptionUpdate( + stream_id_, next_key_id_, info.system_id(), next_iv_, + info.pssh_data()); + LOG_IF(WARNING, !result) << "Failed to add encryption info"; + } + next_key_id_.clear(); + next_iv_.clear(); + next_key_system_infos_.clear(); +} + void HlsNotifyMuxerListener::OnMediaStart(const MuxerOptions& muxer_options, const StreamInfo& stream_info, uint32_t time_scale, @@ -56,7 +84,12 @@ void HlsNotifyMuxerListener::OnMediaStart(const MuxerOptions& muxer_options, const bool result = hls_notifier_->NotifyNewStream( media_info, playlist_name_, ext_x_media_name_, ext_x_media_group_id_, &stream_id_); - LOG_IF(WARNING, !result) << "Failed to notify new stream."; + if (!result) { + LOG(WARNING) << "Failed to notify new stream."; + return; + } + + media_started_ = true; } void HlsNotifyMuxerListener::OnSampleDurationReady(uint32_t sample_duration) {} diff --git a/packager/media/event/hls_notify_muxer_listener.h b/packager/media/event/hls_notify_muxer_listener.h index 5390295f14..b402ea144f 100644 --- a/packager/media/event/hls_notify_muxer_listener.h +++ b/packager/media/event/hls_notify_muxer_listener.h @@ -45,6 +45,7 @@ class HlsNotifyMuxerListener : public MuxerListener { const std::vector& iv, const std::vector& key_system_info) override; + void OnEncryptionStart() override; void OnMediaStart(const MuxerOptions& muxer_options, const StreamInfo& stream_info, uint32_t time_scale, @@ -71,6 +72,12 @@ class HlsNotifyMuxerListener : public MuxerListener { hls::HlsNotifier* const hls_notifier_; uint32_t stream_id_ = 0; + bool media_started_ = false; + // Cached encryption info before OnMediaStart() is called. + std::vector next_key_id_; + std::vector next_iv_; + std::vector next_key_system_infos_; + DISALLOW_COPY_AND_ASSIGN(HlsNotifyMuxerListener); }; diff --git a/packager/media/event/hls_notify_muxer_listener_unittest.cc b/packager/media/event/hls_notify_muxer_listener_unittest.cc index 9b09543e32..be8739db87 100644 --- a/packager/media/event/hls_notify_muxer_listener_unittest.cc +++ b/packager/media/event/hls_notify_muxer_listener_unittest.cc @@ -83,7 +83,126 @@ class HlsNotifyMuxerListenerTest : public ::testing::Test { HlsNotifyMuxerListener listener_; }; +// Verify that NotifyEncryptionUpdate() is not called before OnMediaStart() is +// called. +TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReadyBeforeMediaStart) { + ProtectionSystemSpecificInfo info; + std::vector system_id(kAnySystemId, + kAnySystemId + arraysize(kAnySystemId)); + info.set_system_id(system_id.data(), system_id.size()); + std::vector pssh_data(kAnyData, kAnyData + arraysize(kAnyData)); + info.set_pssh_data(pssh_data); + + std::vector key_id(16, 0x05); + std::vector key_system_infos; + key_system_infos.push_back(info); + + std::vector iv(16, 0x54); + + EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); + listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, + iv, key_system_infos); +} + +TEST_F(HlsNotifyMuxerListenerTest, OnMediaStart) { + VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams(); + scoped_refptr video_stream_info = + CreateVideoStreamInfo(video_params); + + EXPECT_CALL(mock_notifier_, + NotifyNewStream(_, StrEq(kDefaultPlaylistName), + StrEq("DEFAULTNAME"), StrEq("DEFAULTGROUPID"), _)) + .WillOnce(Return(true)); + + MuxerOptions muxer_options; + listener_.OnMediaStart(muxer_options, *video_stream_info, 90000, + MuxerListener::kContainerMpeg2ts); +} + +// OnEncryptionStart() should NotifyEncryptionUpdate() after +// OnEncryptionInfoReady() and OnMediaStart(). +TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionStart) { + ProtectionSystemSpecificInfo info; + std::vector system_id(kAnySystemId, + kAnySystemId + arraysize(kAnySystemId)); + info.set_system_id(system_id.data(), system_id.size()); + std::vector pssh_data(kAnyData, kAnyData + arraysize(kAnyData)); + info.set_pssh_data(pssh_data); + + std::vector key_id(16, 0x05); + std::vector key_system_infos; + key_system_infos.push_back(info); + + std::vector iv(16, 0x54); + + EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); + listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, + iv, key_system_infos); + ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); + + ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _)) + .WillByDefault(Return(true)); + VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams(); + scoped_refptr video_stream_info = + CreateVideoStreamInfo(video_params); + MuxerOptions muxer_options; + + EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); + listener_.OnMediaStart(muxer_options, *video_stream_info, 90000, + MuxerListener::kContainerMpeg2ts); + ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); + + EXPECT_CALL(mock_notifier_, + NotifyEncryptionUpdate(_, key_id, system_id, iv, pssh_data)) + .WillOnce(Return(true)); + listener_.OnEncryptionStart(); +} + +// NotifyEncryptionUpdate() should not be called if NotifyNewStream() fails in +// OnMediaStart(). +TEST_F(HlsNotifyMuxerListenerTest, NoEncryptionUpdateIfNotifyNewStreamFails) { + ProtectionSystemSpecificInfo info; + std::vector system_id(kAnySystemId, + kAnySystemId + arraysize(kAnySystemId)); + info.set_system_id(system_id.data(), system_id.size()); + std::vector pssh_data(kAnyData, kAnyData + arraysize(kAnyData)); + info.set_pssh_data(pssh_data); + + std::vector key_id(16, 0x05); + std::vector key_system_infos; + key_system_infos.push_back(info); + + std::vector iv(16, 0x54); + + EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); + listener_.OnEncryptionInfoReady(kInitialEncryptionInfo, FOURCC_cbcs, key_id, + iv, key_system_infos); + ::testing::Mock::VerifyAndClearExpectations(&mock_notifier_); + + EXPECT_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _)) + .WillOnce(Return(false)); + VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams(); + scoped_refptr video_stream_info = + CreateVideoStreamInfo(video_params); + MuxerOptions muxer_options; + + EXPECT_CALL(mock_notifier_, NotifyEncryptionUpdate(_, _, _, _, _)).Times(0); + listener_.OnMediaStart(muxer_options, *video_stream_info, 90000, + MuxerListener::kContainerMpeg2ts); +} + +// Verify that after OnMediaStart(), OnEncryptionInfoReady() calls +// NotifyEncryptionUpdate(). TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReady) { + ON_CALL(mock_notifier_, NotifyNewStream(_, _, _, _, _)) + .WillByDefault(Return(true)); + VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams(); + scoped_refptr video_stream_info = + CreateVideoStreamInfo(video_params); + MuxerOptions muxer_options; + listener_.OnMediaStart(muxer_options, *video_stream_info, 90000, + MuxerListener::kContainerMpeg2ts); + ProtectionSystemSpecificInfo info; std::vector system_id(kAnySystemId, kAnySystemId + arraysize(kAnySystemId)); @@ -104,21 +223,6 @@ TEST_F(HlsNotifyMuxerListenerTest, OnEncryptionInfoReady) { iv, key_system_infos); } -TEST_F(HlsNotifyMuxerListenerTest, OnMediaStart) { - VideoStreamInfoParameters video_params = GetDefaultVideoStreamInfoParams(); - scoped_refptr video_stream_info = - CreateVideoStreamInfo(video_params); - - EXPECT_CALL(mock_notifier_, - NotifyNewStream(_, StrEq(kDefaultPlaylistName), - StrEq("DEFAULTNAME"), StrEq("DEFAULTGROUPID"), _)) - .WillOnce(Return(true)); - - MuxerOptions muxer_options; - listener_.OnMediaStart(muxer_options, *video_stream_info, 90000, - MuxerListener::kContainerMpeg2ts); -} - // Make sure it doesn't crash. TEST_F(HlsNotifyMuxerListenerTest, OnSampleDurationReady) { listener_.OnSampleDurationReady(2340); diff --git a/packager/media/event/mock_muxer_listener.h b/packager/media/event/mock_muxer_listener.h index c89f5f2a13..d30dab8b9d 100644 --- a/packager/media/event/mock_muxer_listener.h +++ b/packager/media/event/mock_muxer_listener.h @@ -30,6 +30,8 @@ class MockMuxerListener : public MuxerListener { const std::vector& iv, const std::vector& key_system_info)); + MOCK_METHOD0(OnEncryptionStart, void()); + MOCK_METHOD4(OnMediaStart, void(const MuxerOptions& muxer_options, const StreamInfo& stream_info, diff --git a/packager/media/event/mpd_notify_muxer_listener.cc b/packager/media/event/mpd_notify_muxer_listener.cc index dd14843308..fd017f8ca6 100644 --- a/packager/media/event/mpd_notify_muxer_listener.cc +++ b/packager/media/event/mpd_notify_muxer_listener.cc @@ -54,6 +54,8 @@ void MpdNotifyMuxerListener::OnEncryptionInfoReady( } } +void MpdNotifyMuxerListener::OnEncryptionStart() {} + void MpdNotifyMuxerListener::OnMediaStart( const MuxerOptions& muxer_options, const StreamInfo& stream_info, diff --git a/packager/media/event/mpd_notify_muxer_listener.h b/packager/media/event/mpd_notify_muxer_listener.h index fa2e81cde2..cfed22ce14 100644 --- a/packager/media/event/mpd_notify_muxer_listener.h +++ b/packager/media/event/mpd_notify_muxer_listener.h @@ -39,6 +39,7 @@ class MpdNotifyMuxerListener : public MuxerListener { const std::vector& iv, const std::vector& key_system_info) override; + void OnEncryptionStart() override; void OnMediaStart(const MuxerOptions& muxer_options, const StreamInfo& stream_info, uint32_t time_scale, diff --git a/packager/media/event/muxer_listener.h b/packager/media/event/muxer_listener.h index 87fc929912..393a9ab5f2 100644 --- a/packager/media/event/muxer_listener.h +++ b/packager/media/event/muxer_listener.h @@ -64,6 +64,12 @@ class MuxerListener { const std::vector& iv, const std::vector& key_system_info) = 0; + /// Called when the muxer starts encrypting the segments. + /// Further segments notified via OnNewSegment() are encrypted. + /// This may be called more than once e.g. per segment, but the semantics does + /// not change. + virtual void OnEncryptionStart() = 0; + /// Called when muxing starts. /// For MPEG DASH Live profile, the initialization segment information is /// available from StreamInfo. diff --git a/packager/media/event/vod_media_info_dump_muxer_listener.cc b/packager/media/event/vod_media_info_dump_muxer_listener.cc index 11a6f9401b..b598df62ba 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener.cc +++ b/packager/media/event/vod_media_info_dump_muxer_listener.cc @@ -62,6 +62,8 @@ void VodMediaInfoDumpMuxerListener::OnMediaStart( } } +void VodMediaInfoDumpMuxerListener::OnEncryptionStart() {} + void VodMediaInfoDumpMuxerListener::OnSampleDurationReady( uint32_t sample_duration) { // Assume one VideoInfo. diff --git a/packager/media/event/vod_media_info_dump_muxer_listener.h b/packager/media/event/vod_media_info_dump_muxer_listener.h index fba8a5d027..70daa66da8 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener.h +++ b/packager/media/event/vod_media_info_dump_muxer_listener.h @@ -38,6 +38,7 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener { const std::vector& iv, const std::vector& key_system_info) override; + void OnEncryptionStart() override; void OnMediaStart(const MuxerOptions& muxer_options, const StreamInfo& stream_info, uint32_t time_scale, diff --git a/packager/media/formats/mp2t/ts_segmenter.cc b/packager/media/formats/mp2t/ts_segmenter.cc index 1eb80636ec..458c2c3989 100644 --- a/packager/media/formats/mp2t/ts_segmenter.cc +++ b/packager/media/formats/mp2t/ts_segmenter.cc @@ -57,8 +57,18 @@ Status TsSegmenter::Initialize(const StreamInfo& stream_info, } if (!status.ok()) return status; + encryption_key_ = encryption_key.Pass(); clear_lead_in_seconds_ = clear_lead_in_seconds; + + if (listener_) { + // For now this only happens once, so send true. + const bool kIsInitialEncryptionInfo = true; + listener_->OnEncryptionInfoReady( + kIsInitialEncryptionInfo, FOURCC_cbcs, encryption_key_->key_id, + encryption_key_->iv, encryption_key_->key_system_info); + } + status = NotifyEncrypted(); if (!status.ok()) return status; @@ -174,13 +184,8 @@ Status TsSegmenter::Flush() { Status TsSegmenter::NotifyEncrypted() { if (encryption_key_ && total_duration_in_seconds_ >= clear_lead_in_seconds_) { - if (listener_) { - // For now this only happens once, so send true. - const bool kIsInitialEncryptionInfo = true; - listener_->OnEncryptionInfoReady( - kIsInitialEncryptionInfo, FOURCC_cbcs, encryption_key_->key_id, - encryption_key_->iv, encryption_key_->key_system_info); - } + if (listener_) + listener_->OnEncryptionStart(); if (!pes_packet_generator_->SetEncryptionKey(encryption_key_.Pass())) return Status(error::INTERNAL_ERROR, "Failed to set encryption key."); diff --git a/packager/media/formats/mp2t/ts_segmenter_unittest.cc b/packager/media/formats/mp2t/ts_segmenter_unittest.cc index 77ea659560..07acf20cb9 100644 --- a/packager/media/formats/mp2t/ts_segmenter_unittest.cc +++ b/packager/media/formats/mp2t/ts_segmenter_unittest.cc @@ -481,6 +481,44 @@ TEST_F(TsSegmenterTest, WithEncryptionNoClearLead) { kClearLeadSeconds)); } +// Verify that the muxer listener pointer is not used without checking that it's +// not null. +TEST_F(TsSegmenterTest, WithEncryptionNoClearLeadNoMuxerListener) { + scoped_refptr stream_info(new VideoStreamInfo( + kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage, + kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate, + kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted)); + MuxerOptions options; + options.segment_duration = 10.0; + options.segment_template = "file$Number$.ts"; + + TsSegmenter segmenter(options, nullptr); + + EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true)); + EXPECT_CALL(*mock_ts_writer_, SignalEncypted()); + EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_)) + .WillOnce(Return(true)); + + EXPECT_CALL(*mock_pes_packet_generator_, SetEncryptionKeyMock(_)) + .WillOnce(Return(true)); + + segmenter.InjectTsWriterForTesting(mock_ts_writer_.Pass()); + segmenter.InjectPesPacketGeneratorForTesting( + mock_pes_packet_generator_.Pass()); + + MockKeySource mock_key_source; + EXPECT_CALL(mock_key_source, GetKey(KeySource::TRACK_TYPE_HD, _)) + .WillOnce(Return(Status::OK)); + + const uint32_t k480pPixels = 640 * 480; + // Set this to 0 so that Finalize will call + // PesPacketGenerator::SetEncryptionKey(). + // Even tho no samples have been added. + const double kClearLeadSeconds = 0; + EXPECT_OK(segmenter.Initialize(*stream_info, &mock_key_source, k480pPixels, + kClearLeadSeconds)); +} + // Verify that encryption notification is sent to objects after clear lead. TEST_F(TsSegmenterTest, WithEncryptionWithClearLead) { scoped_refptr stream_info(new VideoStreamInfo( @@ -568,13 +606,14 @@ TEST_F(TsSegmenterTest, WithEncryptionWithClearLead) { EXPECT_CALL(mock_key_source, GetKey(KeySource::TRACK_TYPE_HD, _)) .WillOnce(Return(Status::OK)); + EXPECT_CALL(mock_listener, OnEncryptionInfoReady(_, _, _, _, _)); EXPECT_OK(segmenter.Initialize(*stream_info, &mock_key_source, 0, kClearLeadSeconds)); EXPECT_OK(segmenter.AddSample(sample1)); // These should be called AFTER the first AddSample(), before the second // segment. - EXPECT_CALL(mock_listener, OnEncryptionInfoReady(_, _, _, _, _)); + EXPECT_CALL(mock_listener, OnEncryptionStart()); EXPECT_CALL(*mock_pes_packet_generator_raw, SetEncryptionKeyMock(_)) .WillOnce(Return(true)); EXPECT_CALL(*mock_ts_writer_raw, SignalEncypted()); diff --git a/packager/media/formats/mp4/encrypting_fragmenter.cc b/packager/media/formats/mp4/encrypting_fragmenter.cc index 46fd66fd30..610d511316 100644 --- a/packager/media/formats/mp4/encrypting_fragmenter.cc +++ b/packager/media/formats/mp4/encrypting_fragmenter.cc @@ -66,7 +66,8 @@ EncryptingFragmenter::EncryptingFragmenter( int64_t clear_time, FourCC protection_scheme, uint8_t crypt_byte_block, - uint8_t skip_byte_block) + uint8_t skip_byte_block, + MuxerListener* listener) : Fragmenter(info, traf), info_(info), encryption_key_(encryption_key.Pass()), @@ -75,7 +76,8 @@ EncryptingFragmenter::EncryptingFragmenter( clear_time_(clear_time), protection_scheme_(protection_scheme), crypt_byte_block_(crypt_byte_block), - skip_byte_block_(skip_byte_block) { + skip_byte_block_(skip_byte_block), + listener_(listener) { DCHECK(encryption_key_); switch (video_codec_) { case kCodecVP8: @@ -143,6 +145,9 @@ Status EncryptingFragmenter::InitializeFragment(int64_t first_sample_dts) { traf()->header.flags |= TrackFragmentHeader::kSampleDescriptionIndexPresentMask; traf()->header.sample_description_index = kClearSampleDescriptionIndex; + } else { + if (listener_) + listener_->OnEncryptionStart(); } return PrepareFragmentForEncryption(enable_encryption); } diff --git a/packager/media/formats/mp4/encrypting_fragmenter.h b/packager/media/formats/mp4/encrypting_fragmenter.h index 01d64da76f..bd666779f3 100644 --- a/packager/media/formats/mp4/encrypting_fragmenter.h +++ b/packager/media/formats/mp4/encrypting_fragmenter.h @@ -12,6 +12,7 @@ #include "packager/media/base/fourccs.h" #include "packager/media/codecs/video_slice_header_parser.h" #include "packager/media/codecs/vpx_parser.h" +#include "packager/media/event/muxer_listener.h" #include "packager/media/formats/mp4/fragmenter.h" namespace shaka { @@ -43,7 +44,8 @@ class EncryptingFragmenter : public Fragmenter { int64_t clear_time, FourCC protection_scheme, uint8_t crypt_byte_block, - uint8_t skip_byte_block); + uint8_t skip_byte_block, + MuxerListener* listener); ~EncryptingFragmenter() override; @@ -95,6 +97,7 @@ class EncryptingFragmenter : public Fragmenter { const FourCC protection_scheme_; const uint8_t crypt_byte_block_; const uint8_t skip_byte_block_; + MuxerListener* listener_; scoped_ptr vpx_parser_; scoped_ptr header_parser_; diff --git a/packager/media/formats/mp4/key_rotation_fragmenter.cc b/packager/media/formats/mp4/key_rotation_fragmenter.cc index f8eea8be8a..c5be9d778a 100644 --- a/packager/media/formats/mp4/key_rotation_fragmenter.cc +++ b/packager/media/formats/mp4/key_rotation_fragmenter.cc @@ -34,7 +34,8 @@ KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof, clear_time, protection_scheme, crypt_byte_block, - skip_byte_block), + skip_byte_block, + muxer_listener), moof_(moof), encryption_key_source_(encryption_key_source), track_type_(track_type), diff --git a/packager/media/formats/mp4/segmenter.cc b/packager/media/formats/mp4/segmenter.cc index 5b35794e00..a487be4919 100644 --- a/packager/media/formats/mp4/segmenter.cc +++ b/packager/media/formats/mp4/segmenter.cc @@ -259,7 +259,8 @@ Status Segmenter::Initialize(const std::vector& streams, fragmenters_[i] = new EncryptingFragmenter( streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(), clear_lead_in_seconds * streams[i]->info()->time_scale(), - protection_scheme, pattern.crypt_byte_block, pattern.skip_byte_block); + protection_scheme, pattern.crypt_byte_block, pattern.skip_byte_block, + muxer_listener_); } // Choose the first stream if there is no VIDEO. diff --git a/packager/media/formats/webm/segmenter.cc b/packager/media/formats/webm/segmenter.cc index d4c40114b9..f40771953a 100644 --- a/packager/media/formats/webm/segmenter.cc +++ b/packager/media/formats/webm/segmenter.cc @@ -166,6 +166,8 @@ Status Segmenter::AddSample(scoped_refptr sample) { LOG(ERROR) << "Error encrypting frame."; return status; } + if (encrypt_frame && muxer_listener_) + muxer_listener_->OnEncryptionStart(); }