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:
parent
4ba5bec660
commit
d9e7e2f1d0
|
@ -270,6 +270,19 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
self._VerifyDecryption(self.output[2], 'bear-640x360-v-trick-1-golden.mp4')
|
self._VerifyDecryption(self.output[2], 'bear-640x360-v-trick-1-golden.mp4')
|
||||||
self._VerifyDecryption(self.output[3], 'bear-640x360-v-trick-2-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):
|
def testPackageWithEncryptionAndNoPsshInStream(self):
|
||||||
self.packager.Package(
|
self.packager.Package(
|
||||||
self._GetStreams(['audio', 'video']),
|
self._GetStreams(['audio', 'video']),
|
||||||
|
@ -682,6 +695,7 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
def _GetFlags(self,
|
def _GetFlags(self,
|
||||||
strip_parameter_set_nalus=True,
|
strip_parameter_set_nalus=True,
|
||||||
encryption=False,
|
encryption=False,
|
||||||
|
clear_lead=1,
|
||||||
protection_scheme=None,
|
protection_scheme=None,
|
||||||
vp9_subsample_encryption=True,
|
vp9_subsample_encryption=True,
|
||||||
decryption=False,
|
decryption=False,
|
||||||
|
@ -710,7 +724,7 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
elif encryption:
|
elif encryption:
|
||||||
flags += ['--enable_fixed_key_encryption',
|
flags += ['--enable_fixed_key_encryption',
|
||||||
'--key_id=31323334353637383930313233343536',
|
'--key_id=31323334353637383930313233343536',
|
||||||
'--clear_lead=1']
|
'--clear_lead={0}'.format(clear_lead)]
|
||||||
|
|
||||||
if test_env.options.encryption_key:
|
if test_env.options.encryption_key:
|
||||||
encryption_key = test_env.options.encryption_key
|
encryption_key = test_env.options.encryption_key
|
||||||
|
|
Binary file not shown.
|
@ -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>
|
Binary file not shown.
|
@ -81,6 +81,7 @@ class StreamInfo {
|
||||||
const std::vector<uint8_t>& codec_config() const { return codec_config_; }
|
const std::vector<uint8_t>& codec_config() const { return codec_config_; }
|
||||||
const std::string& language() const { return language_; }
|
const std::string& language() const { return language_; }
|
||||||
bool is_encrypted() const { return is_encrypted_; }
|
bool is_encrypted() const { return is_encrypted_; }
|
||||||
|
bool has_clear_lead() const { return has_clear_lead_; }
|
||||||
const EncryptionConfig& encryption_config() const {
|
const EncryptionConfig& encryption_config() const {
|
||||||
return encryption_config_;
|
return encryption_config_;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +94,9 @@ class StreamInfo {
|
||||||
}
|
}
|
||||||
void set_language(const std::string& language) { language_ = language; }
|
void set_language(const std::string& language) { language_ = language; }
|
||||||
void set_is_encrypted(bool is_encrypted) { is_encrypted_ = is_encrypted; }
|
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) {
|
void set_encryption_config(const EncryptionConfig& encryption_config) {
|
||||||
encryption_config_ = encryption_config;
|
encryption_config_ = encryption_config;
|
||||||
}
|
}
|
||||||
|
@ -112,6 +116,8 @@ class StreamInfo {
|
||||||
// Note that in a potentially encrypted stream, individual buffers
|
// Note that in a potentially encrypted stream, individual buffers
|
||||||
// can be encrypted or not encrypted.
|
// can be encrypted or not encrypted.
|
||||||
bool is_encrypted_;
|
bool is_encrypted_;
|
||||||
|
// Whether the stream has clear lead.
|
||||||
|
bool has_clear_lead_ = false;
|
||||||
EncryptionConfig encryption_config_;
|
EncryptionConfig encryption_config_;
|
||||||
// Optional byte data required for some audio/video decoders such as Vorbis
|
// Optional byte data required for some audio/video decoders such as Vorbis
|
||||||
// codebooks.
|
// codebooks.
|
||||||
|
|
|
@ -194,6 +194,8 @@ Status EncryptionHandler::ProcessStreamInfo(StreamInfo* stream_info) {
|
||||||
return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
|
return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
|
||||||
|
|
||||||
stream_info->set_is_encrypted(true);
|
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_);
|
stream_info->set_encryption_config(*encryption_config_);
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -465,9 +465,11 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithNoKeyRotation) {
|
||||||
ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale)));
|
ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale)));
|
||||||
EXPECT_THAT(GetOutputStreamDataVector(),
|
EXPECT_THAT(GetOutputStreamDataVector(),
|
||||||
ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale, kEncrypted)));
|
ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale, kEncrypted)));
|
||||||
const EncryptionConfig& encryption_config =
|
const StreamInfo* stream_info =
|
||||||
GetOutputStreamDataVector().back()->stream_info->encryption_config();
|
GetOutputStreamDataVector().back()->stream_info.get();
|
||||||
EXPECT_THAT(encryption_config,
|
ASSERT_TRUE(stream_info);
|
||||||
|
EXPECT_TRUE(stream_info->has_clear_lead());
|
||||||
|
EXPECT_THAT(stream_info->encryption_config(),
|
||||||
MatchEncryptionConfig(
|
MatchEncryptionConfig(
|
||||||
protection_scheme_, GetExpectedCryptByteBlock(),
|
protection_scheme_, GetExpectedCryptByteBlock(),
|
||||||
GetExpectedSkipByteBlock(), GetExpectedPerSampleIvSize(),
|
GetExpectedSkipByteBlock(), GetExpectedPerSampleIvSize(),
|
||||||
|
@ -512,8 +514,11 @@ TEST_P(EncryptionHandlerEncryptionTest, ClearLeadWithKeyRotation) {
|
||||||
ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale)));
|
ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale)));
|
||||||
EXPECT_THAT(GetOutputStreamDataVector(),
|
EXPECT_THAT(GetOutputStreamDataVector(),
|
||||||
ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale, kEncrypted)));
|
ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale, kEncrypted)));
|
||||||
const EncryptionConfig& encryption_config =
|
const StreamInfo* stream_info =
|
||||||
GetOutputStreamDataVector().back()->stream_info->encryption_config();
|
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(protection_scheme_, encryption_config.protection_scheme);
|
||||||
EXPECT_EQ(GetExpectedCryptByteBlock(), encryption_config.crypt_byte_block);
|
EXPECT_EQ(GetExpectedCryptByteBlock(), encryption_config.crypt_byte_block);
|
||||||
EXPECT_EQ(GetExpectedSkipByteBlock(), encryption_config.skip_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)));
|
ASSERT_OK(Process(GetStreamInfoStreamData(kStreamIndex, codec_, kTimeScale)));
|
||||||
EXPECT_THAT(GetOutputStreamDataVector(),
|
EXPECT_THAT(GetOutputStreamDataVector(),
|
||||||
ElementsAre(IsStreamInfo(kStreamIndex, kTimeScale, kEncrypted)));
|
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();
|
InjectCodecParser();
|
||||||
|
|
||||||
|
|
|
@ -288,8 +288,10 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
|
||||||
sample_description.video_entries.push_back(video);
|
sample_description.video_entries.push_back(video);
|
||||||
|
|
||||||
if (video_info->is_encrypted()) {
|
if (video_info->is_encrypted()) {
|
||||||
// Add a second entry for clear content.
|
if (video_info->has_clear_lead()) {
|
||||||
sample_description.video_entries.push_back(video);
|
// Add a second entry for clear content.
|
||||||
|
sample_description.video_entries.push_back(video);
|
||||||
|
}
|
||||||
// Convert the first entry to an encrypted entry.
|
// Convert the first entry to an encrypted entry.
|
||||||
VideoSampleEntry& entry = sample_description.video_entries[0];
|
VideoSampleEntry& entry = sample_description.video_entries[0];
|
||||||
GenerateSinf(entry.format, video_info->encryption_config(), &entry.sinf);
|
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);
|
sample_description.audio_entries.push_back(audio);
|
||||||
|
|
||||||
if (audio_info->is_encrypted()) {
|
if (audio_info->is_encrypted()) {
|
||||||
// Add a second entry for clear content.
|
if (audio_info->has_clear_lead()) {
|
||||||
sample_description.audio_entries.push_back(audio);
|
// Add a second entry for clear content.
|
||||||
|
sample_description.audio_entries.push_back(audio);
|
||||||
|
}
|
||||||
// Convert the first entry to an encrypted entry.
|
// Convert the first entry to an encrypted entry.
|
||||||
AudioSampleEntry& entry = sample_description.audio_entries[0];
|
AudioSampleEntry& entry = sample_description.audio_entries[0];
|
||||||
GenerateSinf(entry.format, audio_info->encryption_config(), &entry.sinf);
|
GenerateSinf(entry.format, audio_info->encryption_config(), &entry.sinf);
|
||||||
|
|
Loading…
Reference in New Issue