Fix cbcs/cens behavior when applied to non video tracks

- Tracks other than video are protected using whole-block full-sample
  encryption as specified in ISO/IEC 23001-7:2016(E) 9.7, which is
  equivalent to a pattern of 1:0. This is different to the non pattern
  encryption counterparts.
- Also updated the code to allow the existence of other protection
  schemes in the original content, which will simply be ignored.
- The internal cryptor used by AesPatternCryptor should not use
  constant iv, add a DCHECK for that.
- Optimize AesPatternCryptor handling on the special pattern 1:0.

Change-Id: Idc704e7bc6b347741336f38c6d3620fc19392960
This commit is contained in:
Kongqun Yang 2016-05-26 15:44:37 -07:00 committed by KongQun Yang
parent 84f3911985
commit d08f6ae0cc
13 changed files with 163 additions and 72 deletions

View File

@ -171,7 +171,7 @@ class PackagerAppTest(unittest.TestCase):
self._GetStreams(['audio', 'video']), self._GetStreams(['audio', 'video']),
self._GetFlags(encryption=True, self._GetFlags(encryption=True,
protection_scheme='cens')) protection_scheme='cens'))
self._DiffGold(self.output[0], 'bear-640x360-a-cenc-golden.mp4') self._DiffGold(self.output[0], 'bear-640x360-a-cens-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-cens-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._DiffGold(self.mpd_output, 'bear-640x360-av-cens-golden.mpd')
self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4')
@ -182,7 +182,7 @@ class PackagerAppTest(unittest.TestCase):
self._GetStreams(['audio', 'video']), self._GetStreams(['audio', 'video']),
self._GetFlags(encryption=True, self._GetFlags(encryption=True,
protection_scheme='cbcs')) protection_scheme='cbcs'))
self._DiffGold(self.output[0], 'bear-640x360-a-cbc1-golden.mp4') self._DiffGold(self.output[0], 'bear-640x360-a-cbcs-golden.mp4')
self._DiffGold(self.output[1], 'bear-640x360-v-cbcs-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._DiffGold(self.mpd_output, 'bear-640x360-av-cbcs-golden.mpd')
self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4')

Binary file not shown.

Binary file not shown.

View File

@ -15,15 +15,15 @@
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true"> <AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<Representation id="1" bandwidth="129127" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100"> <Representation id="1" bandwidth="127202" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/> <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 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"> <ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh> <cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection> </ContentProtection>
<BaseURL>output_audio.mp4</BaseURL> <BaseURL>output_audio.mp4</BaseURL>
<SegmentBase indexRange="955-1022" timescale="44100"> <SegmentBase indexRange="964-1031" timescale="44100">
<Initialization range="0-954"/> <Initialization range="0-963"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>

View File

@ -17,7 +17,7 @@
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true"> <AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<Representation id="1" bandwidth="129127" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100"> <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"/> <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 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"> <ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh> <cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection> </ContentProtection>

View File

@ -58,11 +58,7 @@ DEFINE_int32(crypto_period_duration,
DEFINE_string(protection_scheme, DEFINE_string(protection_scheme,
"cenc", "cenc",
"Choose protection scheme, 'cenc' or 'cbc1' or pattern-based " "Choose protection scheme, 'cenc' or 'cbc1' or pattern-based "
"protection schemes 'cens' or 'cbcs'. Note that if a " "protection schemes 'cens' or 'cbcs'.");
"pattern-based protection scheme only applies to video stream; "
"audio stream will be encrypted using the corresponding "
"non-pattern-based protection schemes, i.e. 'cenc' for 'cens', "
"'cbc1' for 'cbcs'.");
namespace shaka { namespace shaka {

View File

@ -80,6 +80,9 @@ class AesCryptor {
/// @return The current iv. /// @return The current iv.
const std::vector<uint8_t>& iv() const { return iv_; } const std::vector<uint8_t>& iv() const { return iv_; }
/// @return true if constant iv is used, false otherwise.
bool use_constant_iv() const { return constant_iv_flag_ == kUseConstantIv; }
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens', /// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
/// 'cbc1', 'cbcs', which is useful to determine the random iv size. /// 'cbc1', 'cbcs', which is useful to determine the random iv size.
/// @param iv points to generated initialization vector. /// @param iv points to generated initialization vector.

View File

@ -23,7 +23,13 @@ AesPatternCryptor::AesPatternCryptor(uint8_t crypt_byte_block,
skip_byte_block_(skip_byte_block), skip_byte_block_(skip_byte_block),
encryption_mode_(encryption_mode), encryption_mode_(encryption_mode),
cryptor_(cryptor.Pass()) { cryptor_(cryptor.Pass()) {
// |crypt_byte_block_| should never be 0. |skip_byte_block_| can be 0 to allow
// a special pattern of 1:0, which is the pattern for the case of pattern
// encryption when applied to non video tracks.
DCHECK_NE(crypt_byte_block_, 0u);
DCHECK(skip_byte_block_ != 0 || crypt_byte_block_ == 1);
DCHECK(cryptor_); DCHECK(cryptor_);
DCHECK(!cryptor_->use_constant_iv());
} }
AesPatternCryptor::~AesPatternCryptor() {} AesPatternCryptor::~AesPatternCryptor() {}
@ -45,6 +51,17 @@ bool AesPatternCryptor::CryptInternal(const uint8_t* text,
} }
*crypt_text_size = text_size; *crypt_text_size = text_size;
// Handle the special pattern 1:0.
if (skip_byte_block_ == 0) {
DCHECK_EQ(crypt_byte_block_, 1u);
const size_t crypt_byte_size = text_size / AES_BLOCK_SIZE * AES_BLOCK_SIZE;
if (!cryptor_->Crypt(text, crypt_byte_size, crypt_text))
return false;
memcpy(crypt_text + crypt_byte_size, text + crypt_byte_size,
text_size - crypt_byte_size);
return true;
}
while (text_size > 0) { while (text_size > 0) {
const size_t crypt_byte_size = crypt_byte_block_ * AES_BLOCK_SIZE; const size_t crypt_byte_size = crypt_byte_block_ * AES_BLOCK_SIZE;
if (NeedEncrypt(text_size, crypt_byte_size)) { if (NeedEncrypt(text_size, crypt_byte_size)) {

View File

@ -47,7 +47,8 @@ class AesPatternCryptor : public AesCryptor {
/// internally inside Crypt call, i.e. iv will be updated across Crypt /// internally inside Crypt call, i.e. iv will be updated across Crypt
/// calls. /// calls.
/// @param cryptor points to an AesCryptor instance which performs the actual /// @param cryptor points to an AesCryptor instance which performs the actual
/// encryption/decryption. /// encryption/decryption. Note that @a cryptor shall not use constant
/// iv.
AesPatternCryptor(uint8_t crypt_byte_block, AesPatternCryptor(uint8_t crypt_byte_block,
uint8_t skip_byte_block, uint8_t skip_byte_block,
PatternEncryptionMode encryption_mode, PatternEncryptionMode encryption_mode,

View File

@ -219,5 +219,35 @@ TEST(SampleAesPatternCryptor, MoreThan16Bytes) {
ASSERT_TRUE(pattern_cryptor.Crypt("0123456789abcdef012", &crypt_text)); ASSERT_TRUE(pattern_cryptor.Crypt("0123456789abcdef012", &crypt_text));
} }
TEST(FullSampleSpecialPatternTest, Test) {
MockAesCryptor* mock_cryptor = new MockAesCryptor();
EXPECT_CALL(*mock_cryptor, CryptInternal(_, 96u, _, _))
.WillOnce(Invoke([](const uint8_t* text, size_t text_size,
uint8_t* crypt_text, size_t* crypt_text_size) {
*crypt_text_size = text_size;
for (size_t i = 0; i < text_size; ++i)
*crypt_text++ = 'e';
return true;
}));
const uint8_t kFulLSampleCryptBlock = 1;
const uint8_t kFullSampleSkipBlock = 0;
AesPatternCryptor pattern_cryptor(
kFulLSampleCryptBlock, kFullSampleSkipBlock,
AesPatternCryptor::kSkipIfCryptByteBlockRemaining,
AesPatternCryptor::kUseConstantIv,
scoped_ptr<MockAesCryptor>(mock_cryptor));
std::vector<uint8_t> iv(8, 'i');
// SetIv will be called only once by AesPatternCryptor::SetIv.
EXPECT_TRUE(pattern_cryptor.SetIv(iv));
std::string input_text(100, 'c');
std::string crypt_text;
// More than 16 bytes so mock's CryptInternal should be called.
ASSERT_TRUE(pattern_cryptor.Crypt(input_text, &crypt_text));
EXPECT_EQ(std::string(96, 'e') + std::string(4, 'c'), crypt_text);
}
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka

View File

@ -126,6 +126,11 @@ FourCC TrackTypeToFourCC(TrackType track_type) {
} }
} }
bool IsProtectionSchemeSupported(FourCC scheme) {
return scheme == FOURCC_cenc || scheme == FOURCC_cens ||
scheme == FOURCC_cbc1 || scheme == FOURCC_cbcs;
}
} // namespace } // namespace
FileType::FileType() : major_brand(FOURCC_NULL), minor_version(0) {} FileType::FileType() : major_brand(FOURCC_NULL), minor_version(0) {}
@ -483,9 +488,12 @@ bool ProtectionSchemeInfo::ReadWriteInternal(BoxBuffer* buffer) {
buffer->PrepareChildren() && buffer->PrepareChildren() &&
buffer->ReadWriteChild(&format) && buffer->ReadWriteChild(&format) &&
buffer->ReadWriteChild(&type)); buffer->ReadWriteChild(&type));
RCHECK(type.type == FOURCC_cenc || type.type == FOURCC_cbc1 || if (IsProtectionSchemeSupported(type.type)) {
type.type == FOURCC_cens || type.type == FOURCC_cbcs);
RCHECK(buffer->ReadWriteChild(&info)); RCHECK(buffer->ReadWriteChild(&info));
} else {
DLOG(WARNING) << "Ignore unsupported protection scheme: "
<< FourCCToString(type.type);
}
// 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
// non-CENC protection scheme types. It is the parent box's responsibility to // non-CENC protection scheme types. It is the parent box's responsibility to
@ -1001,8 +1009,8 @@ bool SampleGroupDescription::ReadWriteInternal(BoxBuffer* buffer) {
return ReadWriteEntries(buffer, &audio_roll_recovery_entries); return ReadWriteEntries(buffer, &audio_roll_recovery_entries);
default: default:
DCHECK(buffer->Reading()); DCHECK(buffer->Reading());
DLOG(WARNING) << "Sample group '" << grouping_type DLOG(WARNING) << "Ignore unsupported sample group: "
<< "' is not supported."; << FourCCToString(static_cast<FourCC>(grouping_type));
return true; return true;
} }
} }
@ -1077,9 +1085,8 @@ bool SampleToGroup::ReadWriteInternal(BoxBuffer* buffer) {
if (grouping_type != FOURCC_seig && grouping_type != FOURCC_roll) { if (grouping_type != FOURCC_seig && grouping_type != FOURCC_roll) {
DCHECK(buffer->Reading()); DCHECK(buffer->Reading());
DLOG(WARNING) << "Sample group " DLOG(WARNING) << "Ignore unsupported sample group: "
<< FourCCToString(static_cast<FourCC>(grouping_type)) << FourCCToString(static_cast<FourCC>(grouping_type));
<< " is not supported.";
return true; return true;
} }
@ -1538,8 +1545,17 @@ 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 supported protection scheme is found, or
// until we run out of protection schemes.
while (!IsProtectionSchemeSupported(sinf.type.type))
RCHECK(buffer->ReadWriteChild(&sinf)); RCHECK(buffer->ReadWriteChild(&sinf));
} else {
DCHECK(IsProtectionSchemeSupported(sinf.type.type));
RCHECK(buffer->ReadWriteChild(&sinf));
}
}
const FourCC actual_format = GetActualFormat(); const FourCC actual_format = GetActualFormat();
if (buffer->Reading()) { if (buffer->Reading()) {
@ -1777,8 +1793,17 @@ 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 supported protection scheme is found, or
// until we run out of protection schemes.
while (!IsProtectionSchemeSupported(sinf.type.type))
RCHECK(buffer->ReadWriteChild(&sinf)); RCHECK(buffer->ReadWriteChild(&sinf));
} else {
DCHECK(IsProtectionSchemeSupported(sinf.type.type));
RCHECK(buffer->ReadWriteChild(&sinf));
}
}
RCHECK(buffer->TryReadWriteChild(&esds)); RCHECK(buffer->TryReadWriteChild(&esds));
RCHECK(buffer->TryReadWriteChild(&ddts)); RCHECK(buffer->TryReadWriteChild(&ddts));

View File

@ -186,9 +186,18 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() {
DCHECK(!IsSubsampleEncryptionRequired()); DCHECK(!IsSubsampleEncryptionRequired());
saiz.default_sample_info_size = per_sample_iv_size; saiz.default_sample_info_size = per_sample_iv_size;
} }
// It should only happen with full sample encryption + constant iv, which is
// not a legal combination. // It should only happen with full sample encryption + constant iv, i.e.
CHECK(!saiz.sample_info_sizes.empty() || saiz.default_sample_info_size != 0); // 'cbcs' applying to audio.
if (saiz.default_sample_info_size == 0 && saiz.sample_info_sizes.empty()) {
DCHECK_EQ(protection_scheme_, FOURCC_cbcs);
DCHECK(!IsSubsampleEncryptionRequired());
// ISO/IEC 23001-7:2016(E) The sample auxiliary information would then be
// empty and should be emitted. Clear saiz and saio boxes so they are not
// written.
saiz.sample_count = 0;
traf()->auxiliary_offset.offsets.clear();
}
} }
Status EncryptingFragmenter::CreateEncryptor() { Status EncryptingFragmenter::CreateEncryptor() {
@ -337,6 +346,8 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
traf()->auxiliary_size.sample_info_sizes.push_back( traf()->auxiliary_size.sample_info_sizes.push_back(
sample_encryption_entry.ComputeSize()); sample_encryption_entry.ComputeSize());
} else { } else {
DCHECK_LE(crypt_byte_block(), 1u);
DCHECK_EQ(skip_byte_block(), 0u);
EncryptBytes(data, sample->data_size()); EncryptBytes(data, sample->data_size());
} }

View File

@ -28,10 +28,6 @@ namespace media {
namespace mp4 { namespace mp4 {
namespace { namespace {
// For pattern-based encryption.
const uint8_t kCryptByteBlock = 1u;
const uint8_t kSkipByteBlock = 9u;
const size_t kCencKeyIdSize = 16u; const size_t kCencKeyIdSize = 16u;
// The version of cenc implemented here. CENC 4. // The version of cenc implemented here. CENC 4.
@ -43,6 +39,12 @@ const uint8_t kKeyRotationDefaultKeyId[] = {
0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0
}; };
// Defines protection pattern for pattern-based encryption.
struct ProtectionPattern {
uint8_t crypt_byte_block;
uint8_t skip_byte_block;
};
COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize, COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize,
cenc_key_id_must_be_size_16); cenc_key_id_must_be_size_16);
@ -52,21 +54,37 @@ uint64_t Rescale(uint64_t time_in_old_scale,
return static_cast<double>(time_in_old_scale) / old_scale * new_scale; return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
} }
uint8_t GetCryptByteBlock(FourCC protection_scheme) { ProtectionPattern GetProtectionPattern(FourCC protection_scheme,
return (protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens) TrackType track_type) {
? kCryptByteBlock ProtectionPattern pattern;
: 0; if (protection_scheme != FOURCC_cbcs && protection_scheme != FOURCC_cens) {
} // Not using pattern encryption.
pattern.crypt_byte_block = 0u;
uint8_t GetSkipByteBlock(FourCC protection_scheme) { pattern.skip_byte_block = 0u;
return (protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens) } else if (track_type != kVideo) {
? kSkipByteBlock // Tracks other than video are protected using whole-block full-sample
: 0; // encryption, which is essentially a pattern of 1:0. Note that this may not
// be the same as the non-pattern based encryption counterparts, e.g. in
// 'cens' for full sample encryption, the whole sample is encrypted up to
// the last 16-byte boundary, see 23001-7:2016(E) 9.7; while in 'cenc' for
// full sample encryption, the last partial 16-byte block is also encrypted,
// see 23001-7:2016(E) 9.4.2. Another difference is the use of constant iv.
pattern.crypt_byte_block = 1u;
pattern.skip_byte_block = 0u;
} else {
// Use 1:9 pattern for video.
const uint8_t kCryptByteBlock = 1u;
const uint8_t kSkipByteBlock = 9u;
pattern.crypt_byte_block = kCryptByteBlock;
pattern.skip_byte_block = kSkipByteBlock;
}
return pattern;
} }
void GenerateSinf(const EncryptionKey& encryption_key, void GenerateSinf(const EncryptionKey& encryption_key,
FourCC old_type, FourCC old_type,
FourCC protection_scheme, FourCC protection_scheme,
ProtectionPattern pattern,
ProtectionSchemeInfo* sinf) { ProtectionSchemeInfo* sinf) {
sinf->format.format = old_type; sinf->format.format = old_type;
@ -85,16 +103,15 @@ void GenerateSinf(const EncryptionKey& encryption_key,
} else { } else {
track_encryption.default_per_sample_iv_size = encryption_key.iv.size(); track_encryption.default_per_sample_iv_size = encryption_key.iv.size();
} }
track_encryption.default_crypt_byte_block = track_encryption.default_crypt_byte_block = pattern.crypt_byte_block;
GetCryptByteBlock(protection_scheme); track_encryption.default_skip_byte_block = pattern.skip_byte_block;
track_encryption.default_skip_byte_block =
GetSkipByteBlock(protection_scheme);
track_encryption.default_kid = encryption_key.key_id; track_encryption.default_kid = encryption_key.key_id;
} }
void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key, void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
double clear_lead_in_seconds, double clear_lead_in_seconds,
FourCC protection_scheme, FourCC protection_scheme,
ProtectionPattern pattern,
SampleDescription* description) { SampleDescription* description) {
DCHECK(description); DCHECK(description);
if (description->type == kVideo) { if (description->type == kVideo) {
@ -106,7 +123,8 @@ void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
// Convert the first entry to an encrypted entry. // Convert the first entry to an encrypted entry.
VideoSampleEntry& entry = description->video_entries[0]; VideoSampleEntry& entry = description->video_entries[0];
GenerateSinf(encryption_key, entry.format, protection_scheme, &entry.sinf); GenerateSinf(encryption_key, entry.format, protection_scheme, pattern,
&entry.sinf);
entry.format = FOURCC_encv; entry.format = FOURCC_encv;
} else { } else {
DCHECK_EQ(kAudio, description->type); DCHECK_EQ(kAudio, description->type);
@ -118,7 +136,8 @@ void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
// Convert the first entry to an encrypted entry. // Convert the first entry to an encrypted entry.
AudioSampleEntry& entry = description->audio_entries[0]; AudioSampleEntry& entry = description->audio_entries[0];
GenerateSinf(encryption_key, entry.format, protection_scheme, &entry.sinf); GenerateSinf(encryption_key, entry.format, protection_scheme, pattern,
&entry.sinf);
entry.format = FOURCC_enca; entry.format = FOURCC_enca;
} }
} }
@ -174,20 +193,12 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
continue; continue;
} }
FourCC local_protection_scheme = protection_scheme;
if (streams[i]->info()->stream_type() != kStreamVideo) {
// Pattern encryption should only be used with video. Replaces with the
// corresponding non-pattern encryption scheme.
if (protection_scheme == FOURCC_cbcs)
local_protection_scheme = FOURCC_cbc1;
else if (protection_scheme == FOURCC_cens)
local_protection_scheme = FOURCC_cenc;
}
KeySource::TrackType track_type = KeySource::TrackType track_type =
GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels); GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels);
SampleDescription& description = SampleDescription& description =
moov_->tracks[i].media.information.sample_table.description; moov_->tracks[i].media.information.sample_table.description;
ProtectionPattern pattern =
GetProtectionPattern(protection_scheme, description.type);
if (key_rotation_enabled) { if (key_rotation_enabled) {
// Fill encrypted sample entry with default key. // Fill encrypted sample entry with default key.
@ -195,17 +206,16 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
encryption_key.key_id.assign( encryption_key.key_id.assign(
kKeyRotationDefaultKeyId, kKeyRotationDefaultKeyId,
kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId)); kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId));
if (!AesCryptor::GenerateRandomIv(local_protection_scheme, if (!AesCryptor::GenerateRandomIv(protection_scheme,
&encryption_key.iv)) { &encryption_key.iv)) {
return Status(error::INTERNAL_ERROR, "Failed to generate random iv."); return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
} }
GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds, GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
local_protection_scheme, &description); protection_scheme, pattern, &description);
if (muxer_listener_) { if (muxer_listener_) {
muxer_listener_->OnEncryptionInfoReady( muxer_listener_->OnEncryptionInfoReady(
kInitialEncryptionInfo, local_protection_scheme, kInitialEncryptionInfo, protection_scheme, encryption_key.key_id,
encryption_key.key_id, encryption_key.iv, encryption_key.iv, encryption_key.key_system_info);
encryption_key.key_system_info);
} }
fragmenters_[i] = new KeyRotationFragmenter( fragmenters_[i] = new KeyRotationFragmenter(
@ -213,8 +223,8 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
encryption_key_source, track_type, encryption_key_source, track_type,
crypto_period_duration_in_seconds * streams[i]->info()->time_scale(), crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
clear_lead_in_seconds * streams[i]->info()->time_scale(), clear_lead_in_seconds * streams[i]->info()->time_scale(),
local_protection_scheme, GetCryptByteBlock(local_protection_scheme), protection_scheme, pattern.crypt_byte_block, pattern.skip_byte_block,
GetSkipByteBlock(local_protection_scheme), muxer_listener_); muxer_listener_);
continue; continue;
} }
@ -224,14 +234,14 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
if (!status.ok()) if (!status.ok())
return status; return status;
if (encryption_key->iv.empty()) { if (encryption_key->iv.empty()) {
if (!AesCryptor::GenerateRandomIv(local_protection_scheme, if (!AesCryptor::GenerateRandomIv(protection_scheme,
&encryption_key->iv)) { &encryption_key->iv)) {
return Status(error::INTERNAL_ERROR, "Failed to generate random iv."); return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
} }
} }
GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds, GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
local_protection_scheme, &description); protection_scheme, pattern, &description);
if (moov_->pssh.empty()) { if (moov_->pssh.empty()) {
moov_->pssh.resize(encryption_key->key_system_info.size()); moov_->pssh.resize(encryption_key->key_system_info.size());
@ -241,17 +251,15 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
if (muxer_listener_) { if (muxer_listener_) {
muxer_listener_->OnEncryptionInfoReady( muxer_listener_->OnEncryptionInfoReady(
kInitialEncryptionInfo, local_protection_scheme, kInitialEncryptionInfo, protection_scheme, encryption_key->key_id,
encryption_key->key_id, encryption_key->iv, encryption_key->iv, encryption_key->key_system_info);
encryption_key->key_system_info);
} }
} }
fragmenters_[i] = new EncryptingFragmenter( fragmenters_[i] = new EncryptingFragmenter(
streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(), streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(),
clear_lead_in_seconds * streams[i]->info()->time_scale(), clear_lead_in_seconds * streams[i]->info()->time_scale(),
local_protection_scheme, GetCryptByteBlock(local_protection_scheme), protection_scheme, pattern.crypt_byte_block, pattern.skip_byte_block);
GetSkipByteBlock(local_protection_scheme));
} }
// Choose the first stream if there is no VIDEO. // Choose the first stream if there is no VIDEO.