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);