diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 4cb94db2a9..0bd7f0d566 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -270,6 +270,19 @@ class PackagerAppTest(unittest.TestCase): self._VerifyDecryption(self.output[2], 'bear-640x360-v-trick-1-golden.mp4') self._VerifyDecryption(self.output[3], 'bear-640x360-v-trick-2-golden.mp4') + def testPackageWithEncryptionAndNoClearLead(self): + self.packager.Package( + self._GetStreams(['audio', 'video']), + self._GetFlags(encryption=True, clear_lead=0)) + self._DiffGold(self.output[0], + 'bear-640x360-a-cenc-no-clear-lead-golden.mp4') + self._DiffGold(self.output[1], + 'bear-640x360-v-cenc-no-clear-lead-golden.mp4') + self._DiffGold(self.mpd_output, + 'bear-640x360-av-cenc-no-clear-lead-golden.mpd') + self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') + self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') + def testPackageWithEncryptionAndNoPsshInStream(self): self.packager.Package( self._GetStreams(['audio', 'video']), @@ -682,6 +695,7 @@ class PackagerAppTest(unittest.TestCase): def _GetFlags(self, strip_parameter_set_nalus=True, encryption=False, + clear_lead=1, protection_scheme=None, vp9_subsample_encryption=True, decryption=False, @@ -710,7 +724,7 @@ class PackagerAppTest(unittest.TestCase): elif encryption: flags += ['--enable_fixed_key_encryption', '--key_id=31323334353637383930313233343536', - '--clear_lead=1'] + '--clear_lead={0}'.format(clear_lead)] if test_env.options.encryption_key: encryption_key = test_env.options.encryption_key diff --git a/packager/app/test/testdata/bear-640x360-a-cenc-no-clear-lead-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-cenc-no-clear-lead-golden.mp4 new file mode 100644 index 0000000000..870e1cb3b3 Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-a-cenc-no-clear-lead-golden.mp4 differ diff --git a/packager/app/test/testdata/bear-640x360-av-cenc-no-clear-lead-golden.mpd b/packager/app/test/testdata/bear-640x360-av-cenc-no-clear-lead-golden.mpd new file mode 100644 index 0000000000..9c9d34c31b --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-av-cenc-no-clear-lead-golden.mpd @@ -0,0 +1,31 @@ + + + + + + + + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== + + + output_video.mp4 + + + + + + + + + AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA== + + + + output_audio.mp4 + + + + + + + diff --git a/packager/app/test/testdata/bear-640x360-v-cenc-no-clear-lead-golden.mp4 b/packager/app/test/testdata/bear-640x360-v-cenc-no-clear-lead-golden.mp4 new file mode 100644 index 0000000000..01bcc6be51 Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-v-cenc-no-clear-lead-golden.mp4 differ diff --git a/packager/media/base/stream_info.h b/packager/media/base/stream_info.h index 742d05d396..dc93b1aac4 100644 --- a/packager/media/base/stream_info.h +++ b/packager/media/base/stream_info.h @@ -81,6 +81,7 @@ class StreamInfo { const std::vector& codec_config() const { return codec_config_; } const std::string& language() const { return language_; } bool is_encrypted() const { return is_encrypted_; } + bool has_clear_lead() const { return has_clear_lead_; } const EncryptionConfig& encryption_config() const { return encryption_config_; } @@ -93,6 +94,9 @@ class StreamInfo { } void set_language(const std::string& language) { language_ = language; } void set_is_encrypted(bool is_encrypted) { is_encrypted_ = is_encrypted; } + void set_has_clear_lead(bool has_clear_lead) { + has_clear_lead_ = has_clear_lead; + } void set_encryption_config(const EncryptionConfig& encryption_config) { encryption_config_ = encryption_config; } @@ -112,6 +116,8 @@ class StreamInfo { // Note that in a potentially encrypted stream, individual buffers // can be encrypted or not encrypted. bool is_encrypted_; + // Whether the stream has clear lead. + bool has_clear_lead_ = false; EncryptionConfig encryption_config_; // Optional byte data required for some audio/video decoders such as Vorbis // codebooks. diff --git a/packager/media/crypto/encryption_handler.cc b/packager/media/crypto/encryption_handler.cc index 8dbc43c579..0e45bd9fa4 100644 --- a/packager/media/crypto/encryption_handler.cc +++ b/packager/media/crypto/encryption_handler.cc @@ -194,6 +194,8 @@ Status EncryptionHandler::ProcessStreamInfo(StreamInfo* stream_info) { return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor"); stream_info->set_is_encrypted(true); + stream_info->set_has_clear_lead(encryption_options_.clear_lead_in_seconds > + 0); stream_info->set_encryption_config(*encryption_config_); return Status::OK; } diff --git a/packager/media/crypto/encryption_handler_unittest.cc b/packager/media/crypto/encryption_handler_unittest.cc index f5c07e6bea..a7ff9cb681 100644 --- a/packager/media/crypto/encryption_handler_unittest.cc +++ b/packager/media/crypto/encryption_handler_unittest.cc @@ -465,9 +465,11 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithNoKeyRotation) { ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale))); EXPECT_THAT(GetOutputStreamDataVector(), ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale, kEncrypted))); - const EncryptionConfig& encryption_config = - GetOutputStreamDataVector().back()->stream_info->encryption_config(); - EXPECT_THAT(encryption_config, + const StreamInfo* stream_info = + GetOutputStreamDataVector().back()->stream_info.get(); + ASSERT_TRUE(stream_info); + EXPECT_TRUE(stream_info->has_clear_lead()); + EXPECT_THAT(stream_info->encryption_config(), MatchEncryptionConfig( protection_scheme_, GetExpectedCryptByteBlock(), GetExpectedSkipByteBlock(), GetExpectedPerSampleIvSize(), @@ -512,8 +514,11 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithKeyRotation) { ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale))); EXPECT_THAT(GetOutputStreamDataVector(), ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale, kEncrypted))); - const EncryptionConfig& encryption_config = - GetOutputStreamDataVector().back()->stream_info->encryption_config(); + const StreamInfo* stream_info = + GetOutputStreamDataVector().back()->stream_info.get(); + ASSERT_TRUE(stream_info); + EXPECT_TRUE(stream_info->has_clear_lead()); + const EncryptionConfig& encryption_config = stream_info->encryption_config(); EXPECT_EQ(protection_scheme_, encryption_config.protection_scheme); EXPECT_EQ(GetExpectedCryptByteBlock(), encryption_config.crypt_byte_block); EXPECT_EQ(GetExpectedSkipByteBlock(), encryption_config.skip_byte_block); @@ -569,6 +574,10 @@ TEST_P(EncryptionHandlerEncryptionTest, Encrypt) { ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale))); EXPECT_THAT(GetOutputStreamDataVector(), ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale, kEncrypted))); + const StreamInfo* stream_info = + GetOutputStreamDataVector().back()->stream_info.get(); + ASSERT_TRUE(stream_info); + EXPECT_FALSE(stream_info->has_clear_lead()); InjectCodecParser(); diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index 6f03533f9a..4070d1b367 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -288,8 +288,10 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info, sample_description.video_entries.push_back(video); if (video_info->is_encrypted()) { - // Add a second entry for clear content. - sample_description.video_entries.push_back(video); + if (video_info->has_clear_lead()) { + // Add a second entry for clear content. + sample_description.video_entries.push_back(video); + } // Convert the first entry to an encrypted entry. VideoSampleEntry& entry = sample_description.video_entries[0]; GenerateSinf(entry.format, video_info->encryption_config(), &entry.sinf); @@ -350,8 +352,10 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info, sample_description.audio_entries.push_back(audio); if (audio_info->is_encrypted()) { - // Add a second entry for clear content. - sample_description.audio_entries.push_back(audio); + if (audio_info->has_clear_lead()) { + // Add a second entry for clear content. + sample_description.audio_entries.push_back(audio); + } // Convert the first entry to an encrypted entry. AudioSampleEntry& entry = sample_description.audio_entries[0]; GenerateSinf(entry.format, audio_info->encryption_config(), &entry.sinf);