Add support for 'cbcs' and 'cens' protection schemes
Issue #78 Change-Id: I9f71b9a92067e2f6b388092494a7d6a84986cdc0
This commit is contained in:
parent
c8819cb257
commit
e253747453
|
@ -57,8 +57,12 @@ DEFINE_int32(crypto_period_duration,
|
|||
"rotation is enabled.");
|
||||
DEFINE_string(protection_scheme,
|
||||
"cenc",
|
||||
"Choose protection scheme. Currently support cenc and cbc1. "
|
||||
"Default is cenc.");
|
||||
"Choose protection scheme, 'cenc' or 'cbc1' or pattern-based "
|
||||
"protection schemes 'cens' or 'cbcs'. Note that if a "
|
||||
"pattern-based protection scheme only applies to video stream; "
|
||||
"audio stream will be encrypted using the corresponding "
|
||||
"non-pattern-based encryption schemes, i.e. 'cenc' for 'cens', "
|
||||
"'cbc1' for 'cbcs'.");
|
||||
|
||||
namespace edash_packager {
|
||||
|
||||
|
|
|
@ -12,16 +12,20 @@ namespace media {
|
|||
DecryptConfig::DecryptConfig(const std::vector<uint8_t>& key_id,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const std::vector<SubsampleEntry>& subsamples)
|
||||
: DecryptConfig(key_id, iv, subsamples, FOURCC_cenc) {}
|
||||
: DecryptConfig(key_id, iv, subsamples, FOURCC_cenc, 0, 0) {}
|
||||
|
||||
DecryptConfig::DecryptConfig(const std::vector<uint8_t>& key_id,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const std::vector<SubsampleEntry>& subsamples,
|
||||
FourCC protection_scheme)
|
||||
FourCC protection_scheme,
|
||||
uint8_t crypt_byte_block,
|
||||
uint8_t skip_byte_block)
|
||||
: key_id_(key_id),
|
||||
iv_(iv),
|
||||
subsamples_(subsamples),
|
||||
protection_scheme_(protection_scheme) {
|
||||
protection_scheme_(protection_scheme),
|
||||
crypt_byte_block_(crypt_byte_block),
|
||||
skip_byte_block_(skip_byte_block) {
|
||||
CHECK_GT(key_id.size(), 0u);
|
||||
}
|
||||
|
||||
|
|
|
@ -60,10 +60,16 @@ class DecryptConfig {
|
|||
/// in size to the sum of the subsample sizes.
|
||||
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
||||
/// 'cbc1', 'cbcs'.
|
||||
/// @param crypt_byte_block indicates number of encrypted blocks (16-byte) in
|
||||
/// pattern based encryption, 'cens' and 'cbcs'. Ignored otherwise.
|
||||
/// @param skip_byte_block indicates number of unencrypted blocks (16-byte)
|
||||
/// in pattern based encryption, 'cens' and 'cbcs'. Ignored otherwise.
|
||||
DecryptConfig(const std::vector<uint8_t>& key_id,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const std::vector<SubsampleEntry>& subsamples,
|
||||
FourCC protection_scheme);
|
||||
FourCC protection_scheme,
|
||||
uint8_t crypt_byte_block,
|
||||
uint8_t skip_byte_block);
|
||||
|
||||
~DecryptConfig();
|
||||
|
||||
|
@ -71,6 +77,8 @@ class DecryptConfig {
|
|||
const std::vector<uint8_t>& iv() const { return iv_; }
|
||||
const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; }
|
||||
FourCC protection_scheme() const { return protection_scheme_; }
|
||||
uint8_t crypt_byte_block() const { return crypt_byte_block_; }
|
||||
uint8_t skip_byte_block() const { return skip_byte_block_; }
|
||||
|
||||
private:
|
||||
const std::vector<uint8_t> key_id_;
|
||||
|
@ -83,6 +91,9 @@ class DecryptConfig {
|
|||
const std::vector<SubsampleEntry> subsamples_;
|
||||
|
||||
const FourCC protection_scheme_;
|
||||
// For pattern-based protection schemes, like CENS and CBCS.
|
||||
const uint8_t crypt_byte_block_;
|
||||
const uint8_t skip_byte_block_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DecryptConfig);
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "packager/base/logging.h"
|
||||
#include "packager/base/stl_util.h"
|
||||
#include "packager/media/base/aes_decryptor.h"
|
||||
#include "packager/media/base/aes_pattern_cryptor.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
@ -47,6 +48,21 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
|
|||
case FOURCC_cbc1:
|
||||
aes_decryptor.reset(new AesCbcDecryptor(kNoPadding, kChainAcrossCalls));
|
||||
break;
|
||||
case FOURCC_cens:
|
||||
aes_decryptor.reset(
|
||||
new AesPatternCryptor(decrypt_config->crypt_byte_block(),
|
||||
decrypt_config->skip_byte_block(),
|
||||
AesPatternCryptor::kDontUseConstantIv,
|
||||
scoped_ptr<AesCryptor>(new AesCtrDecryptor)));
|
||||
break;
|
||||
case FOURCC_cbcs:
|
||||
aes_decryptor.reset(
|
||||
new AesPatternCryptor(decrypt_config->crypt_byte_block(),
|
||||
decrypt_config->skip_byte_block(),
|
||||
AesPatternCryptor::kUseConstantIv,
|
||||
scoped_ptr<AesCryptor>(new AesCbcDecryptor(
|
||||
kNoPadding, kChainAcrossCalls))));
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "Unsupported protection scheme: "
|
||||
<< decrypt_config->protection_scheme();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <limits>
|
||||
|
||||
#include "packager/media/base/aes_encryptor.h"
|
||||
#include "packager/media/base/aes_pattern_cryptor.h"
|
||||
#include "packager/media/base/buffer_reader.h"
|
||||
#include "packager/media/base/key_source.h"
|
||||
#include "packager/media/base/media_sample.h"
|
||||
|
@ -63,26 +64,37 @@ EncryptingFragmenter::EncryptingFragmenter(
|
|||
TrackFragment* traf,
|
||||
scoped_ptr<EncryptionKey> encryption_key,
|
||||
int64_t clear_time,
|
||||
FourCC protection_scheme)
|
||||
FourCC protection_scheme,
|
||||
uint8_t crypt_byte_block,
|
||||
uint8_t skip_byte_block)
|
||||
: Fragmenter(traf),
|
||||
info_(info),
|
||||
encryption_key_(encryption_key.Pass()),
|
||||
nalu_length_size_(GetNaluLengthSize(*info)),
|
||||
video_codec_(GetVideoCodec(*info)),
|
||||
clear_time_(clear_time),
|
||||
protection_scheme_(protection_scheme) {
|
||||
protection_scheme_(protection_scheme),
|
||||
crypt_byte_block_(crypt_byte_block),
|
||||
skip_byte_block_(skip_byte_block) {
|
||||
DCHECK(encryption_key_);
|
||||
if (video_codec_ == kCodecVP8) {
|
||||
vpx_parser_.reset(new VP8Parser);
|
||||
} else if (video_codec_ == kCodecVP9) {
|
||||
vpx_parser_.reset(new VP9Parser);
|
||||
} else if (video_codec_ == kCodecH264) {
|
||||
header_parser_.reset(new H264VideoSliceHeaderParser);
|
||||
} else if (video_codec_ == kCodecHVC1 || video_codec_ == kCodecHEV1) {
|
||||
header_parser_.reset(new H265VideoSliceHeaderParser);
|
||||
} else if (nalu_length_size_ > 0) {
|
||||
LOG(WARNING) << "Unknown video codec '" << video_codec_
|
||||
<< "', whole subsamples will be encrypted.";
|
||||
switch (video_codec_) {
|
||||
case kCodecVP8:
|
||||
vpx_parser_.reset(new VP8Parser);
|
||||
break;
|
||||
case kCodecVP9:
|
||||
vpx_parser_.reset(new VP9Parser);
|
||||
break;
|
||||
case kCodecH264:
|
||||
header_parser_.reset(new H264VideoSliceHeaderParser);
|
||||
break;
|
||||
case kCodecHVC1:
|
||||
FALLTHROUGH_INTENDED;
|
||||
case kCodecHEV1:
|
||||
header_parser_.reset(new H265VideoSliceHeaderParser);
|
||||
break;
|
||||
default:
|
||||
LOG(WARNING) << "Unknown video codec '" << video_codec_
|
||||
<< "', whole subsamples will be encrypted.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,6 +165,11 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() {
|
|||
// The offset will be adjusted in Segmenter after knowing moof size.
|
||||
traf()->auxiliary_offset.offsets.push_back(0);
|
||||
|
||||
// For 'cbcs' scheme, Constant IVs SHALL be used.
|
||||
const size_t per_sample_iv_size =
|
||||
(protection_scheme_ == FOURCC_cbcs) ? 0 : encryptor_->iv().size();
|
||||
traf()->sample_encryption.iv_size = per_sample_iv_size;
|
||||
|
||||
// Optimize saiz box.
|
||||
SampleAuxiliaryInformationSize& saiz = traf()->auxiliary_size;
|
||||
saiz.sample_count = traf()->runs[0].sample_sizes.size();
|
||||
|
@ -165,9 +182,11 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() {
|
|||
// |sample_info_sizes| table is filled in only for subsample encryption,
|
||||
// otherwise |sample_info_size| is just the IV size.
|
||||
DCHECK(!IsSubsampleEncryptionRequired());
|
||||
saiz.default_sample_info_size = encryptor_->iv().size();
|
||||
saiz.default_sample_info_size = per_sample_iv_size;
|
||||
}
|
||||
traf()->sample_encryption.iv_size = encryptor_->iv().size();
|
||||
// It should only happen with full sample encryption + constant iv, which is
|
||||
// not a legal combination.
|
||||
CHECK(!saiz.sample_info_sizes.empty() || saiz.default_sample_info_size != 0);
|
||||
}
|
||||
|
||||
Status EncryptingFragmenter::CreateEncryptor() {
|
||||
|
@ -180,6 +199,19 @@ Status EncryptingFragmenter::CreateEncryptor() {
|
|||
case FOURCC_cbc1:
|
||||
encryptor.reset(new AesCbcEncryptor(kNoPadding, kChainAcrossCalls));
|
||||
break;
|
||||
case FOURCC_cens:
|
||||
encryptor.reset(
|
||||
new AesPatternCryptor(crypt_byte_block(), skip_byte_block(),
|
||||
AesPatternCryptor::kDontUseConstantIv,
|
||||
scoped_ptr<AesCryptor>(new AesCtrEncryptor)));
|
||||
break;
|
||||
case FOURCC_cbcs:
|
||||
encryptor.reset(
|
||||
new AesPatternCryptor(crypt_byte_block(), skip_byte_block(),
|
||||
AesPatternCryptor::kUseConstantIv,
|
||||
scoped_ptr<AesCryptor>(new AesCbcEncryptor(
|
||||
kNoPadding, kChainAcrossCalls))));
|
||||
break;
|
||||
default:
|
||||
return Status(error::MUXER_FAILURE, "Unsupported protection scheme.");
|
||||
}
|
||||
|
@ -202,7 +234,9 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
|||
DCHECK(encryptor_);
|
||||
|
||||
SampleEncryptionEntry sample_encryption_entry;
|
||||
sample_encryption_entry.initialization_vector = encryptor_->iv();
|
||||
// For 'cbcs' scheme, Constant IVs SHALL be used.
|
||||
if (protection_scheme_ != FOURCC_cbcs)
|
||||
sample_encryption_entry.initialization_vector = encryptor_->iv();
|
||||
uint8_t* data = sample->writable_data();
|
||||
if (IsSubsampleEncryptionRequired()) {
|
||||
if (vpx_parser_) {
|
||||
|
|
|
@ -32,11 +32,17 @@ class EncryptingFragmenter : public Fragmenter {
|
|||
/// track's timescale.
|
||||
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
||||
/// 'cbc1', 'cbcs'.
|
||||
/// @param crypt_byte_block indicates number of encrypted blocks (16-byte) in
|
||||
/// pattern based encryption.
|
||||
/// @param skip_byte_block indicates number of unencrypted blocks (16-byte)
|
||||
/// in pattern based encryption.
|
||||
EncryptingFragmenter(scoped_refptr<StreamInfo> info,
|
||||
TrackFragment* traf,
|
||||
scoped_ptr<EncryptionKey> encryption_key,
|
||||
int64_t clear_time,
|
||||
FourCC protection_scheme);
|
||||
FourCC protection_scheme,
|
||||
uint8_t crypt_byte_block,
|
||||
uint8_t skip_byte_block);
|
||||
|
||||
~EncryptingFragmenter() override;
|
||||
|
||||
|
@ -62,6 +68,8 @@ class EncryptingFragmenter : public Fragmenter {
|
|||
const EncryptionKey* encryption_key() const { return encryption_key_.get(); }
|
||||
AesCryptor* encryptor() { return encryptor_.get(); }
|
||||
FourCC protection_scheme() const { return protection_scheme_; }
|
||||
uint8_t crypt_byte_block() const { return crypt_byte_block_; }
|
||||
uint8_t skip_byte_block() const { return skip_byte_block_; }
|
||||
|
||||
void set_encryption_key(scoped_ptr<EncryptionKey> encryption_key) {
|
||||
encryption_key_ = encryption_key.Pass();
|
||||
|
@ -83,7 +91,9 @@ class EncryptingFragmenter : public Fragmenter {
|
|||
const uint8_t nalu_length_size_;
|
||||
const VideoCodec video_codec_;
|
||||
int64_t clear_time_;
|
||||
FourCC protection_scheme_;
|
||||
const FourCC protection_scheme_;
|
||||
const uint8_t crypt_byte_block_;
|
||||
const uint8_t skip_byte_block_;
|
||||
|
||||
scoped_ptr<VPxParser> vpx_parser_;
|
||||
scoped_ptr<VideoSliceHeaderParser> header_parser_;
|
||||
|
|
|
@ -25,12 +25,16 @@ KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof,
|
|||
int64_t crypto_period_duration,
|
||||
int64_t clear_time,
|
||||
FourCC protection_scheme,
|
||||
uint8_t crypt_byte_block,
|
||||
uint8_t skip_byte_block,
|
||||
MuxerListener* muxer_listener)
|
||||
: EncryptingFragmenter(info,
|
||||
traf,
|
||||
scoped_ptr<EncryptionKey>(new EncryptionKey()),
|
||||
clear_time,
|
||||
protection_scheme),
|
||||
protection_scheme,
|
||||
crypt_byte_block,
|
||||
skip_byte_block),
|
||||
moof_(moof),
|
||||
encryption_key_source_(encryption_key_source),
|
||||
track_type_(track_type),
|
||||
|
@ -105,10 +109,18 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption(
|
|||
// Fill in SampleGroupDescription box information.
|
||||
traf()->sample_group_description.grouping_type = FOURCC_seig;
|
||||
traf()->sample_group_description.entries.resize(1);
|
||||
traf()->sample_group_description.entries[0].is_protected = 1;
|
||||
traf()->sample_group_description.entries[0].per_sample_iv_size =
|
||||
encryptor()->iv().size();
|
||||
traf()->sample_group_description.entries[0].key_id = encryption_key()->key_id;
|
||||
auto& sample_group_entry = traf()->sample_group_description.entries[0];
|
||||
sample_group_entry.is_protected = 1;
|
||||
if (protection_scheme() == FOURCC_cbcs) {
|
||||
// For 'cbcs' scheme, Constant IVs SHALL be used.
|
||||
sample_group_entry.per_sample_iv_size = 0;
|
||||
sample_group_entry.constant_iv = encryptor()->iv();
|
||||
} else {
|
||||
sample_group_entry.per_sample_iv_size = encryptor()->iv().size();
|
||||
}
|
||||
sample_group_entry.crypt_byte_block = crypt_byte_block();
|
||||
sample_group_entry.skip_byte_block = skip_byte_block();
|
||||
sample_group_entry.key_id = encryption_key()->key_id;
|
||||
|
||||
// Fill in SampleToGroup box information.
|
||||
traf()->sample_to_group.grouping_type = FOURCC_seig;
|
||||
|
|
|
@ -34,6 +34,10 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
|
|||
/// track's timescale.
|
||||
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
||||
/// 'cbc1', 'cbcs'.
|
||||
/// @param crypt_byte_block indicates number of encrypted blocks (16-byte) in
|
||||
/// pattern based encryption.
|
||||
/// @param skip_byte_block indicates number of unencrypted blocks (16-byte)
|
||||
/// in pattern based encryption.
|
||||
/// @param muxer_listener is a pointer to MuxerListener for notifying
|
||||
/// muxer related events. This may be null.
|
||||
KeyRotationFragmenter(MovieFragment* moof,
|
||||
|
@ -44,6 +48,8 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
|
|||
int64_t crypto_period_duration,
|
||||
int64_t clear_time,
|
||||
FourCC protection_scheme,
|
||||
uint8_t crypt_byte_block,
|
||||
uint8_t skip_byte_block,
|
||||
MuxerListener* muxer_listener);
|
||||
~KeyRotationFragmenter() override;
|
||||
|
||||
|
|
|
@ -26,6 +26,9 @@ namespace media {
|
|||
namespace mp4 {
|
||||
|
||||
namespace {
|
||||
// For pattern-based encryption.
|
||||
const uint8_t kCryptByteBlock = 1u;
|
||||
const uint8_t kSkipByteBlock = 9u;
|
||||
|
||||
const size_t kCencKeyIdSize = 16u;
|
||||
|
||||
|
@ -47,6 +50,18 @@ uint64_t Rescale(uint64_t time_in_old_scale,
|
|||
return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
|
||||
}
|
||||
|
||||
uint8_t GetCryptByteBlock(FourCC protection_scheme) {
|
||||
return (protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens)
|
||||
? kCryptByteBlock
|
||||
: 0;
|
||||
}
|
||||
|
||||
uint8_t GetSkipByteBlock(FourCC protection_scheme) {
|
||||
return (protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens)
|
||||
? kSkipByteBlock
|
||||
: 0;
|
||||
}
|
||||
|
||||
void GenerateSinf(const EncryptionKey& encryption_key,
|
||||
FourCC old_type,
|
||||
FourCC protection_scheme,
|
||||
|
@ -60,7 +75,18 @@ void GenerateSinf(const EncryptionKey& encryption_key,
|
|||
auto& track_encryption = sinf->info.track_encryption;
|
||||
track_encryption.default_is_protected = 1;
|
||||
DCHECK(!encryption_key.iv.empty());
|
||||
track_encryption.default_per_sample_iv_size = encryption_key.iv.size();
|
||||
if (protection_scheme == FOURCC_cbcs) {
|
||||
// ISO/IEC 23001-7:2016 10.4.1
|
||||
// For 'cbcs' scheme, Constant IVs SHALL be used.
|
||||
track_encryption.default_per_sample_iv_size = 0;
|
||||
track_encryption.default_constant_iv = encryption_key.iv;
|
||||
} else {
|
||||
track_encryption.default_per_sample_iv_size = encryption_key.iv.size();
|
||||
}
|
||||
track_encryption.default_crypt_byte_block =
|
||||
GetCryptByteBlock(protection_scheme);
|
||||
track_encryption.default_skip_byte_block =
|
||||
GetSkipByteBlock(protection_scheme);
|
||||
track_encryption.default_kid = encryption_key.key_id;
|
||||
}
|
||||
|
||||
|
@ -159,6 +185,16 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
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 =
|
||||
GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels);
|
||||
SampleDescription& description =
|
||||
|
@ -170,10 +206,12 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
encryption_key.key_id.assign(
|
||||
kKeyRotationDefaultKeyId,
|
||||
kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId));
|
||||
if (!AesCryptor::GenerateRandomIv(protection_scheme, &encryption_key.iv))
|
||||
if (!AesCryptor::GenerateRandomIv(local_protection_scheme,
|
||||
&encryption_key.iv)) {
|
||||
return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
|
||||
}
|
||||
GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
|
||||
protection_scheme, &description);
|
||||
local_protection_scheme, &description);
|
||||
if (muxer_listener_) {
|
||||
muxer_listener_->OnEncryptionInfoReady(
|
||||
kInitialEncryptionInfo, encryption_key.key_id,
|
||||
|
@ -185,7 +223,8 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
encryption_key_source, track_type,
|
||||
crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
|
||||
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
||||
protection_scheme, muxer_listener_);
|
||||
local_protection_scheme, GetCryptByteBlock(local_protection_scheme),
|
||||
GetSkipByteBlock(local_protection_scheme), muxer_listener_);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -195,12 +234,14 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
if (!status.ok())
|
||||
return status;
|
||||
if (encryption_key->iv.empty()) {
|
||||
if (!AesCryptor::GenerateRandomIv(protection_scheme, &encryption_key->iv))
|
||||
if (!AesCryptor::GenerateRandomIv(local_protection_scheme,
|
||||
&encryption_key->iv)) {
|
||||
return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
|
||||
}
|
||||
}
|
||||
|
||||
GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
|
||||
protection_scheme, &description);
|
||||
local_protection_scheme, &description);
|
||||
|
||||
if (moov_->pssh.empty()) {
|
||||
moov_->pssh.resize(encryption_key->key_system_info.size());
|
||||
|
@ -218,7 +259,8 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
fragmenters_[i] = new EncryptingFragmenter(
|
||||
streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(),
|
||||
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
||||
protection_scheme);
|
||||
local_protection_scheme, GetCryptByteBlock(local_protection_scheme),
|
||||
GetSkipByteBlock(local_protection_scheme));
|
||||
}
|
||||
|
||||
// Choose the first stream if there is no VIDEO.
|
||||
|
|
|
@ -606,10 +606,22 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
|
|||
|
||||
FourCC protection_scheme = is_audio() ? audio_description().sinf.type.type
|
||||
: video_description().sinf.type.type;
|
||||
return scoped_ptr<DecryptConfig>(
|
||||
new DecryptConfig(track_encryption().default_kid,
|
||||
sample_encryption_entry.initialization_vector,
|
||||
sample_encryption_entry.subsamples, protection_scheme));
|
||||
std::vector<uint8_t> iv = sample_encryption_entry.initialization_vector;
|
||||
if (iv.empty()) {
|
||||
if (protection_scheme != FOURCC_cbcs) {
|
||||
LOG(WARNING)
|
||||
<< "Constant IV should only be used with 'cbcs' protection scheme.";
|
||||
}
|
||||
iv = track_encryption().default_constant_iv;
|
||||
if (iv.empty()) {
|
||||
LOG(ERROR) << "IV cannot be empty.";
|
||||
return scoped_ptr<DecryptConfig>();
|
||||
}
|
||||
}
|
||||
return scoped_ptr<DecryptConfig>(new DecryptConfig(
|
||||
track_encryption().default_kid, iv, sample_encryption_entry.subsamples,
|
||||
protection_scheme, track_encryption().default_crypt_byte_block,
|
||||
track_encryption().default_skip_byte_block));
|
||||
}
|
||||
|
||||
} // namespace mp4
|
||||
|
|
|
@ -19,6 +19,9 @@ const int kSumAscending1 = 45;
|
|||
const int kAudioScale = 48000;
|
||||
const int kVideoScale = 25;
|
||||
|
||||
const uint8_t kDefaultCryptByteBlock = 2;
|
||||
const uint8_t kDefaultSkipByteBlock = 8;
|
||||
|
||||
const uint8_t kAuxInfo[] = {
|
||||
// Sample 1: IV (no subsumples).
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
||||
|
@ -29,7 +32,8 @@ const uint8_t kAuxInfo[] = {
|
|||
// Sample 2: Subsample 1.
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
||||
// Sample 2: Subsample 2.
|
||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x04};
|
||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
|
||||
};
|
||||
|
||||
const uint8_t kSampleEncryptionDataWithSubsamples[] = {
|
||||
// Sample count.
|
||||
|
@ -47,7 +51,8 @@ const uint8_t kSampleEncryptionDataWithSubsamples[] = {
|
|||
// Sample 2: Subsample 1.
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
||||
// Sample 2: Subsample 2.
|
||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x04};
|
||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
|
||||
};
|
||||
|
||||
const uint8_t kSampleEncryptionDataWithoutSubsamples[] = {
|
||||
// Sample count.
|
||||
|
@ -55,13 +60,37 @@ const uint8_t kSampleEncryptionDataWithoutSubsamples[] = {
|
|||
// Sample 1: IV.
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
||||
// Sample 2: IV.
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32};
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32,
|
||||
};
|
||||
|
||||
const uint8_t kSampleEncryptionDataWithConstantIvAndSubsamples[] = {
|
||||
// Sample count.
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
// Sample 1: Subsample count.
|
||||
0x00, 0x01,
|
||||
// Sample 1: Subsample 1.
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
||||
// Sample 2: Subsample count.
|
||||
0x00, 0x02,
|
||||
// Sample 2: Subsample 1.
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
||||
// Sample 2: Subsample 2.
|
||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
|
||||
};
|
||||
|
||||
const uint8_t kSampleEncryptionDataWithConstantIvWithoutSubsamples[] = {
|
||||
// Sample count.
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
};
|
||||
|
||||
const char kIv1[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31};
|
||||
const char kIv2[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32};
|
||||
const char kConstantIv[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x33};
|
||||
|
||||
const uint8_t kKeyId[] = {0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
|
||||
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44};
|
||||
const uint8_t kKeyId[] = {
|
||||
0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
|
||||
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -142,7 +171,7 @@ class TrackRunIteratorTest : public testing::Test {
|
|||
}
|
||||
|
||||
// Update the first sample description of a Track to indicate encryption
|
||||
void AddEncryption(Track* track) {
|
||||
void AddEncryption(FourCC protection_scheme, Track* track) {
|
||||
SampleDescription* stsd =
|
||||
&track->media.information.sample_table.description;
|
||||
ProtectionSchemeInfo* sinf;
|
||||
|
@ -152,9 +181,20 @@ class TrackRunIteratorTest : public testing::Test {
|
|||
sinf = &stsd->audio_entries[0].sinf;
|
||||
}
|
||||
|
||||
sinf->type.type = FOURCC_cenc;
|
||||
sinf->type.type = protection_scheme;
|
||||
sinf->info.track_encryption.default_is_protected = 1;
|
||||
sinf->info.track_encryption.default_per_sample_iv_size = 8;
|
||||
// Use constant IV for CBCS protection scheme.
|
||||
if (protection_scheme == FOURCC_cbcs) {
|
||||
sinf->info.track_encryption.default_per_sample_iv_size = 0;
|
||||
sinf->info.track_encryption.default_constant_iv.assign(
|
||||
kConstantIv, kConstantIv + arraysize(kConstantIv));
|
||||
sinf->info.track_encryption.default_crypt_byte_block =
|
||||
kDefaultCryptByteBlock;
|
||||
sinf->info.track_encryption.default_skip_byte_block =
|
||||
kDefaultSkipByteBlock;
|
||||
} else {
|
||||
sinf->info.track_encryption.default_per_sample_iv_size = 8;
|
||||
}
|
||||
sinf->info.track_encryption.default_kid.assign(kKeyId,
|
||||
kKeyId + arraysize(kKeyId));
|
||||
}
|
||||
|
@ -202,6 +242,39 @@ class TrackRunIteratorTest : public testing::Test {
|
|||
}
|
||||
}
|
||||
|
||||
void AddSampleEncryptionWithConstantIv(uint8_t use_subsample_flag,
|
||||
TrackFragment* frag) {
|
||||
frag->sample_encryption.iv_size = 0;
|
||||
frag->sample_encryption.flags = use_subsample_flag;
|
||||
if (use_subsample_flag) {
|
||||
frag->sample_encryption.sample_encryption_data.assign(
|
||||
kSampleEncryptionDataWithConstantIvAndSubsamples,
|
||||
kSampleEncryptionDataWithConstantIvAndSubsamples +
|
||||
arraysize(kSampleEncryptionDataWithConstantIvAndSubsamples));
|
||||
} else {
|
||||
frag->sample_encryption.sample_encryption_data.assign(
|
||||
kSampleEncryptionDataWithConstantIvWithoutSubsamples,
|
||||
kSampleEncryptionDataWithConstantIvWithoutSubsamples +
|
||||
arraysize(kSampleEncryptionDataWithConstantIvWithoutSubsamples));
|
||||
}
|
||||
|
||||
// Update sample sizes and aux info header.
|
||||
frag->runs.resize(1);
|
||||
frag->runs[0].sample_count = 2;
|
||||
if (use_subsample_flag) {
|
||||
// Update sample sizes to match with subsample entries above.
|
||||
frag->runs[0].sample_sizes[0] = 3;
|
||||
frag->runs[0].sample_sizes[1] = 10;
|
||||
// Set aux info header.
|
||||
frag->auxiliary_offset.offsets.push_back(0);
|
||||
frag->auxiliary_size.sample_count = 2;
|
||||
frag->auxiliary_size.sample_info_sizes.push_back(16);
|
||||
frag->auxiliary_size.sample_info_sizes.push_back(22);
|
||||
} else {
|
||||
// No aux info needed for constant iv and full sample encryption.
|
||||
}
|
||||
}
|
||||
|
||||
void SetAscending(std::vector<uint32_t>* vec) {
|
||||
vec->resize(10);
|
||||
for (size_t i = 0; i < vec->size(); i++)
|
||||
|
@ -364,7 +437,7 @@ TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) {
|
|||
|
||||
TEST_F(TrackRunIteratorTest,
|
||||
DecryptConfigTestWithSampleEncryptionAndSubsample) {
|
||||
AddEncryption(&moov_.tracks[1]);
|
||||
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
|
||||
MovieFragment moof = CreateFragment();
|
||||
|
@ -402,7 +475,7 @@ TEST_F(TrackRunIteratorTest,
|
|||
|
||||
TEST_F(TrackRunIteratorTest,
|
||||
DecryptConfigTestWithSampleEncryptionAndNoSubsample) {
|
||||
AddEncryption(&moov_.tracks[1]);
|
||||
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
|
||||
MovieFragment moof = CreateFragment();
|
||||
|
@ -432,8 +505,84 @@ TEST_F(TrackRunIteratorTest,
|
|||
EXPECT_EQ(config->subsamples().size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(TrackRunIteratorTest,
|
||||
DecryptConfigTestWithSampleEncryptionAndConstantIvAndSubsample) {
|
||||
AddEncryption(FOURCC_cbcs, &moov_.tracks[1]);
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
|
||||
MovieFragment moof = CreateFragment();
|
||||
AddSampleEncryptionWithConstantIv(SampleEncryption::kUseSubsampleEncryption,
|
||||
&moof.tracks[1]);
|
||||
|
||||
ASSERT_TRUE(iter_->Init(moof));
|
||||
// The run for track 2 will be the second, which is parsed according to
|
||||
// data_offset.
|
||||
iter_->AdvanceRun();
|
||||
EXPECT_EQ(iter_->track_id(), 2u);
|
||||
|
||||
EXPECT_TRUE(iter_->is_encrypted());
|
||||
EXPECT_EQ(iter_->sample_offset(), 200);
|
||||
EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[1].runs[0].data_offset);
|
||||
scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
|
||||
EXPECT_EQ(FOURCC_cbcs, config->protection_scheme());
|
||||
EXPECT_EQ(kDefaultCryptByteBlock, config->crypt_byte_block());
|
||||
EXPECT_EQ(kDefaultSkipByteBlock, config->skip_byte_block());
|
||||
EXPECT_EQ(std::vector<uint8_t>(kKeyId, kKeyId + arraysize(kKeyId)),
|
||||
config->key_id());
|
||||
EXPECT_EQ(
|
||||
std::vector<uint8_t>(kConstantIv, kConstantIv + arraysize(kConstantIv)),
|
||||
config->iv());
|
||||
EXPECT_EQ(config->subsamples().size(), 1u);
|
||||
EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u);
|
||||
EXPECT_EQ(config->subsamples()[0].cipher_bytes, 2u);
|
||||
iter_->AdvanceSample();
|
||||
config = iter_->GetDecryptConfig();
|
||||
EXPECT_EQ(
|
||||
std::vector<uint8_t>(kConstantIv, kConstantIv + arraysize(kConstantIv)),
|
||||
config->iv());
|
||||
EXPECT_EQ(config->subsamples().size(), 2u);
|
||||
EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u);
|
||||
EXPECT_EQ(config->subsamples()[0].cipher_bytes, 2u);
|
||||
EXPECT_EQ(config->subsamples()[1].clear_bytes, 3u);
|
||||
EXPECT_EQ(config->subsamples()[1].cipher_bytes, 4u);
|
||||
}
|
||||
|
||||
TEST_F(TrackRunIteratorTest,
|
||||
DecryptConfigTestWithSampleEncryptionAndConstantIvAndNoSubsample) {
|
||||
AddEncryption(FOURCC_cbcs, &moov_.tracks[1]);
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
|
||||
MovieFragment moof = CreateFragment();
|
||||
AddSampleEncryptionWithConstantIv(!SampleEncryption::kUseSubsampleEncryption,
|
||||
&moof.tracks[1]);
|
||||
|
||||
ASSERT_TRUE(iter_->Init(moof));
|
||||
// The run for track 2 will be the second, which is parsed according to
|
||||
// data_offset.
|
||||
iter_->AdvanceRun();
|
||||
EXPECT_EQ(iter_->track_id(), 2u);
|
||||
|
||||
EXPECT_TRUE(iter_->is_encrypted());
|
||||
EXPECT_EQ(iter_->sample_offset(), 200);
|
||||
EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[1].runs[0].data_offset);
|
||||
scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
|
||||
EXPECT_EQ(FOURCC_cbcs, config->protection_scheme());
|
||||
EXPECT_EQ(std::vector<uint8_t>(kKeyId, kKeyId + arraysize(kKeyId)),
|
||||
config->key_id());
|
||||
EXPECT_EQ(
|
||||
std::vector<uint8_t>(kConstantIv, kConstantIv + arraysize(kConstantIv)),
|
||||
config->iv());
|
||||
EXPECT_EQ(config->subsamples().size(), 0u);
|
||||
iter_->AdvanceSample();
|
||||
config = iter_->GetDecryptConfig();
|
||||
EXPECT_EQ(
|
||||
std::vector<uint8_t>(kConstantIv, kConstantIv + arraysize(kConstantIv)),
|
||||
config->iv());
|
||||
EXPECT_EQ(config->subsamples().size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(TrackRunIteratorTest, DecryptConfigTestWithAuxInfo) {
|
||||
AddEncryption(&moov_.tracks[1]);
|
||||
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
|
||||
MovieFragment moof = CreateFragment();
|
||||
|
@ -470,8 +619,8 @@ TEST_F(TrackRunIteratorTest, DecryptConfigTestWithAuxInfo) {
|
|||
|
||||
// It is legal for aux info blocks to be shared among multiple formats.
|
||||
TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
|
||||
AddEncryption(&moov_.tracks[0]);
|
||||
AddEncryption(&moov_.tracks[1]);
|
||||
AddEncryption(FOURCC_cenc, &moov_.tracks[0]);
|
||||
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
|
||||
MovieFragment moof = CreateFragment();
|
||||
|
@ -512,8 +661,8 @@ TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
|
|||
// byte 10000: track 1, run 2 data
|
||||
// byte 20000: track 1, run 1 aux info
|
||||
TEST_F(TrackRunIteratorTest, UnexpectedOrderingTest) {
|
||||
AddEncryption(&moov_.tracks[0]);
|
||||
AddEncryption(&moov_.tracks[1]);
|
||||
AddEncryption(FOURCC_cenc, &moov_.tracks[0]);
|
||||
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
|
||||
MovieFragment moof = CreateFragment();
|
||||
|
|
Loading…
Reference in New Issue