Generate two sample entries only if there is clear lead

This addresses playready broken with clear lead = 0.

clear lead != 0 is still broken with playready on Edge. We think it is
likely an Edge bug, which does not support multiple sample entry boxes,
thus does not support clear lead.

b/37913785

Change-Id: I7adb77a913dccf669153b03b31b4a1e1c98d1cb0
This commit is contained in:
KongQun Yang 2017-05-09 15:49:01 -07:00
parent 4ba5bec660
commit d9e7e2f1d0
8 changed files with 76 additions and 10 deletions

View File

@ -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

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/shaka-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.76317S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
</ContentProtection>
<Representation id="0" bandwidth="886751" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
<BaseURL>output_video.mp4</BaseURL>
<SegmentBase indexRange="955-1022" timescale="30000">
<Initialization range="0-954"/>
</SegmentBase>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b">
<cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAExMjM0NTY3ODkwMTIzNDU2AAAAAA==</cenc:pssh>
</ContentProtection>
<Representation id="1" bandwidth="130109" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<BaseURL>output_audio.mp4</BaseURL>
<SegmentBase indexRange="889-956" timescale="44100">
<Initialization range="0-888"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -81,6 +81,7 @@ class StreamInfo {
const std::vector<uint8_t>& 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.

View File

@ -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;
}

View File

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

View File

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