Fix two bugs in 'cbcs' and 'cens' handling and add tests

- BytesOfProtectedData in 'cens' SHALL also be 16-byte aligned
- Fix an incorrect box definition bug w.r.t to 'cbcs' and 'cens'
- Also add various protection scheme tests

Close #77
Close #78

Change-Id: I63c8d8b01ce16ed60affa97ec95fc62bc2da06df
This commit is contained in:
Kongqun Yang 2016-04-21 16:28:21 -07:00 committed by KongQun Yang
parent 976bf69294
commit 3ff74398df
11 changed files with 149 additions and 33 deletions

View File

@ -137,6 +137,39 @@ class PackagerAppTest(unittest.TestCase):
self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4')
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
def testPackageWithEncryptionCbc1(self):
self.packager.Package(
self._GetStreams(['audio', 'video']),
self._GetFlags(encryption=True,
protection_scheme='cbc1'))
self._DiffGold(self.output[0], 'bear-640x360-a-cbc1-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-cbc1-golden.mp4')
self._DiffGold(self.mpd_output, 'bear-640x360-av-cbc1-golden.mpd')
self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4')
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
def testPackageWithEncryptionCens(self):
self.packager.Package(
self._GetStreams(['audio', 'video']),
self._GetFlags(encryption=True,
protection_scheme='cens'))
self._DiffGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-cens-golden.mp4')
self._DiffGold(self.mpd_output, 'bear-640x360-av-cens-golden.mpd')
self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4')
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
def testPackageWithEncryptionCbcs(self):
self.packager.Package(
self._GetStreams(['audio', 'video']),
self._GetFlags(encryption=True,
protection_scheme='cbcs'))
self._DiffGold(self.output[0], 'bear-640x360-a-cbc1-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-cbcs-golden.mp4')
self._DiffGold(self.mpd_output, 'bear-640x360-av-cbcs-golden.mpd')
self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4')
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
def testPackageWebmWithEncryption(self): def testPackageWebmWithEncryption(self):
self.packager.Package( self.packager.Package(
self._GetStreams(['video'], self._GetStreams(['video'],
@ -368,6 +401,7 @@ class PackagerAppTest(unittest.TestCase):
def _GetFlags(self, def _GetFlags(self,
encryption=False, encryption=False,
protection_scheme=None,
decryption=False, decryption=False,
random_iv=False, random_iv=False,
widevine_encryption=False, widevine_encryption=False,
@ -394,6 +428,8 @@ class PackagerAppTest(unittest.TestCase):
'--clear_lead=1'] '--clear_lead=1']
if not random_iv: if not random_iv:
flags.append('--iv=3334353637383930') flags.append('--iv=3334353637383930')
if protection_scheme:
flags += ['--protection_scheme', protection_scheme]
if decryption: if decryption:
flags += ['--enable_fixed_key_decryption', flags += ['--enable_fixed_key_decryption',

Binary file not shown.

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-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" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<Representation id="0" bandwidth="885555" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
<ContentProtection value="cbc1" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection>
<BaseURL>output_video.mp4</BaseURL>
<SegmentBase indexRange="1079-1146" timescale="30000">
<Initialization range="0-1078"/>
</SegmentBase>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<Representation id="1" bandwidth="129127" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<ContentProtection value="cbc1" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection>
<BaseURL>output_audio.mp4</BaseURL>
<SegmentBase indexRange="955-1022" timescale="44100">
<Initialization range="0-954"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-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" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<Representation id="0" bandwidth="884365" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
<ContentProtection value="cbcs" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection>
<BaseURL>output_video.mp4</BaseURL>
<SegmentBase indexRange="1088-1155" timescale="30000">
<Initialization range="0-1087"/>
</SegmentBase>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<Representation id="1" bandwidth="129127" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<ContentProtection value="cbc1" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection>
<BaseURL>output_audio.mp4</BaseURL>
<SegmentBase indexRange="955-1022" timescale="44100">
<Initialization range="0-954"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-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" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S">
<Period id="0">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<Representation id="0" bandwidth="885555" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
<ContentProtection value="cens" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection>
<BaseURL>output_video.mp4</BaseURL>
<SegmentBase indexRange="1079-1146" timescale="30000">
<Initialization range="0-1078"/>
</SegmentBase>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<Representation id="1" bandwidth="129127" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection>
<BaseURL>output_audio.mp4</BaseURL>
<SegmentBase indexRange="955-1022" timescale="44100">
<Initialization range="0-954"/>
</SegmentBase>
</Representation>
</AdaptationSet>
</Period>
</MPD>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -31,7 +31,8 @@ void Box::Write(BufferWriter* writer) {
size_t buffer_size_before_write = writer->Size(); size_t buffer_size_before_write = writer->Size();
BoxBuffer buffer(writer); BoxBuffer buffer(writer);
CHECK(ReadWriteInternal(&buffer)); CHECK(ReadWriteInternal(&buffer));
DCHECK_EQ(box_size_, writer->Size() - buffer_size_before_write); DCHECK_EQ(box_size_, writer->Size() - buffer_size_before_write)
<< FourCCToString(BoxType());
} }
void Box::WriteHeader(BufferWriter* writer) { void Box::WriteHeader(BufferWriter* writer) {

View File

@ -449,6 +449,7 @@ bool TrackEncryption::ReadWriteInternal(BoxBuffer* buffer) {
// of |default_is_protected| is not supported. // of |default_is_protected| is not supported.
RCHECK(default_is_protected == 0); RCHECK(default_is_protected == 0);
RCHECK(default_per_sample_iv_size == 0); RCHECK(default_per_sample_iv_size == 0);
RCHECK(default_constant_iv.empty());
} }
return true; return true;
} }
@ -482,7 +483,8 @@ bool ProtectionSchemeInfo::ReadWriteInternal(BoxBuffer* buffer) {
buffer->PrepareChildren() && buffer->PrepareChildren() &&
buffer->ReadWriteChild(&format) && buffer->ReadWriteChild(&format) &&
buffer->ReadWriteChild(&type)); buffer->ReadWriteChild(&type));
if (type.type == FOURCC_cenc || type.type == FOURCC_cbc1) RCHECK(type.type == FOURCC_cenc || type.type == FOURCC_cbc1 ||
type.type == FOURCC_cens || type.type == FOURCC_cbcs);
RCHECK(buffer->ReadWriteChild(&info)); RCHECK(buffer->ReadWriteChild(&info));
// Other protection schemes are silently ignored. Since the protection scheme // Other protection schemes are silently ignored. Since the protection scheme
// type can't be determined until this box is opened, we return 'true' for // type can't be determined until this box is opened, we return 'true' for
@ -1328,18 +1330,8 @@ bool VideoSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(buffer->PrepareChildren()); RCHECK(buffer->PrepareChildren());
if (format == FOURCC_encv) { if (format == FOURCC_encv)
if (buffer->Reading()) {
// Continue scanning until a recognized protection scheme is found,
// or until we run out of protection schemes.
while (sinf.type.type != FOURCC_cenc && sinf.type.type != FOURCC_cbc1) {
if (!buffer->ReadWriteChild(&sinf))
return false;
}
} else {
RCHECK(buffer->ReadWriteChild(&sinf)); RCHECK(buffer->ReadWriteChild(&sinf));
}
}
const FourCC actual_format = GetActualFormat(); const FourCC actual_format = GetActualFormat();
switch (actual_format) { switch (actual_format) {
@ -1513,18 +1505,8 @@ bool AudioSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
samplerate >>= 16; samplerate >>= 16;
RCHECK(buffer->PrepareChildren()); RCHECK(buffer->PrepareChildren());
if (format == FOURCC_enca) { if (format == FOURCC_enca)
if (buffer->Reading()) {
// Continue scanning until a recognized protection scheme is found,
// or until we run out of protection schemes.
while (sinf.type.type != FOURCC_cenc && sinf.type.type != FOURCC_cbc1) {
if (!buffer->ReadWriteChild(&sinf))
return false;
}
} else {
RCHECK(buffer->ReadWriteChild(&sinf)); RCHECK(buffer->ReadWriteChild(&sinf));
}
}
RCHECK(buffer->TryReadWriteChild(&esds)); RCHECK(buffer->TryReadWriteChild(&esds));
RCHECK(buffer->TryReadWriteChild(&ddts)); RCHECK(buffer->TryReadWriteChild(&ddts));

View File

@ -256,9 +256,11 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
// encrypted bytes of each frame within the superframe must be block // encrypted bytes of each frame within the superframe must be block
// aligned so that the counter state can be computed for each frame // aligned so that the counter state can be computed for each frame
// within the superframe. // within the superframe.
// For AES-CBC mode 'cbc1' scheme, clear data is sized appropriately so // ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
// that the cipher data is block aligned. // The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
if (is_superframe || protection_scheme_ == FOURCC_cbc1) { // avoid partial blocks in Subsamples.
if (is_superframe || protection_scheme_ == FOURCC_cbc1 ||
protection_scheme_ == FOURCC_cens) {
const uint16_t misalign_bytes = const uint16_t misalign_bytes =
subsample.cipher_bytes % kCencBlockSize; subsample.cipher_bytes % kCencBlockSize;
subsample.clear_bytes += misalign_bytes; subsample.clear_bytes += misalign_bytes;
@ -298,9 +300,11 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
nalu.header_size() + video_slice_header_size; nalu.header_size() + video_slice_header_size;
uint64_t cipher_bytes = nalu.payload_size() - video_slice_header_size; uint64_t cipher_bytes = nalu.payload_size() - video_slice_header_size;
// For AES-CBC mode 'cbc1' scheme, clear data is sized appropriately // ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
// so that the cipher data is block aligned. // The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
if (protection_scheme_ == FOURCC_cbc1) { // avoid partial blocks in Subsamples.
if (protection_scheme_ == FOURCC_cbc1 ||
protection_scheme_ == FOURCC_cens) {
const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize; const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize;
current_clear_bytes += misalign_bytes; current_clear_bytes += misalign_bytes;
cipher_bytes -= misalign_bytes; cipher_bytes -= misalign_bytes;