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.");
|
"rotation is enabled.");
|
||||||
DEFINE_string(protection_scheme,
|
DEFINE_string(protection_scheme,
|
||||||
"cenc",
|
"cenc",
|
||||||
"Choose protection scheme. Currently support cenc and cbc1. "
|
"Choose protection scheme, 'cenc' or 'cbc1' or pattern-based "
|
||||||
"Default is cenc.");
|
"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 {
|
namespace edash_packager {
|
||||||
|
|
||||||
|
|
|
@ -12,16 +12,20 @@ namespace media {
|
||||||
DecryptConfig::DecryptConfig(const std::vector<uint8_t>& key_id,
|
DecryptConfig::DecryptConfig(const std::vector<uint8_t>& key_id,
|
||||||
const std::vector<uint8_t>& iv,
|
const std::vector<uint8_t>& iv,
|
||||||
const std::vector<SubsampleEntry>& subsamples)
|
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,
|
DecryptConfig::DecryptConfig(const std::vector<uint8_t>& key_id,
|
||||||
const std::vector<uint8_t>& iv,
|
const std::vector<uint8_t>& iv,
|
||||||
const std::vector<SubsampleEntry>& subsamples,
|
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),
|
: key_id_(key_id),
|
||||||
iv_(iv),
|
iv_(iv),
|
||||||
subsamples_(subsamples),
|
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);
|
CHECK_GT(key_id.size(), 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,10 +60,16 @@ class DecryptConfig {
|
||||||
/// in size to the sum of the subsample sizes.
|
/// in size to the sum of the subsample sizes.
|
||||||
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
||||||
/// 'cbc1', 'cbcs'.
|
/// '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,
|
DecryptConfig(const std::vector<uint8_t>& key_id,
|
||||||
const std::vector<uint8_t>& iv,
|
const std::vector<uint8_t>& iv,
|
||||||
const std::vector<SubsampleEntry>& subsamples,
|
const std::vector<SubsampleEntry>& subsamples,
|
||||||
FourCC protection_scheme);
|
FourCC protection_scheme,
|
||||||
|
uint8_t crypt_byte_block,
|
||||||
|
uint8_t skip_byte_block);
|
||||||
|
|
||||||
~DecryptConfig();
|
~DecryptConfig();
|
||||||
|
|
||||||
|
@ -71,6 +77,8 @@ class DecryptConfig {
|
||||||
const std::vector<uint8_t>& iv() const { return iv_; }
|
const std::vector<uint8_t>& iv() const { return iv_; }
|
||||||
const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; }
|
const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; }
|
||||||
FourCC protection_scheme() const { return protection_scheme_; }
|
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:
|
private:
|
||||||
const std::vector<uint8_t> key_id_;
|
const std::vector<uint8_t> key_id_;
|
||||||
|
@ -83,6 +91,9 @@ class DecryptConfig {
|
||||||
const std::vector<SubsampleEntry> subsamples_;
|
const std::vector<SubsampleEntry> subsamples_;
|
||||||
|
|
||||||
const FourCC protection_scheme_;
|
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);
|
DISALLOW_COPY_AND_ASSIGN(DecryptConfig);
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/base/stl_util.h"
|
#include "packager/base/stl_util.h"
|
||||||
#include "packager/media/base/aes_decryptor.h"
|
#include "packager/media/base/aes_decryptor.h"
|
||||||
|
#include "packager/media/base/aes_pattern_cryptor.h"
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
@ -47,6 +48,21 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
|
||||||
case FOURCC_cbc1:
|
case FOURCC_cbc1:
|
||||||
aes_decryptor.reset(new AesCbcDecryptor(kNoPadding, kChainAcrossCalls));
|
aes_decryptor.reset(new AesCbcDecryptor(kNoPadding, kChainAcrossCalls));
|
||||||
break;
|
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:
|
default:
|
||||||
LOG(ERROR) << "Unsupported protection scheme: "
|
LOG(ERROR) << "Unsupported protection scheme: "
|
||||||
<< decrypt_config->protection_scheme();
|
<< decrypt_config->protection_scheme();
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include "packager/media/base/aes_encryptor.h"
|
#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/buffer_reader.h"
|
||||||
#include "packager/media/base/key_source.h"
|
#include "packager/media/base/key_source.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
|
@ -63,24 +64,35 @@ EncryptingFragmenter::EncryptingFragmenter(
|
||||||
TrackFragment* traf,
|
TrackFragment* traf,
|
||||||
scoped_ptr<EncryptionKey> encryption_key,
|
scoped_ptr<EncryptionKey> encryption_key,
|
||||||
int64_t clear_time,
|
int64_t clear_time,
|
||||||
FourCC protection_scheme)
|
FourCC protection_scheme,
|
||||||
|
uint8_t crypt_byte_block,
|
||||||
|
uint8_t skip_byte_block)
|
||||||
: Fragmenter(traf),
|
: Fragmenter(traf),
|
||||||
info_(info),
|
info_(info),
|
||||||
encryption_key_(encryption_key.Pass()),
|
encryption_key_(encryption_key.Pass()),
|
||||||
nalu_length_size_(GetNaluLengthSize(*info)),
|
nalu_length_size_(GetNaluLengthSize(*info)),
|
||||||
video_codec_(GetVideoCodec(*info)),
|
video_codec_(GetVideoCodec(*info)),
|
||||||
clear_time_(clear_time),
|
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_);
|
DCHECK(encryption_key_);
|
||||||
if (video_codec_ == kCodecVP8) {
|
switch (video_codec_) {
|
||||||
|
case kCodecVP8:
|
||||||
vpx_parser_.reset(new VP8Parser);
|
vpx_parser_.reset(new VP8Parser);
|
||||||
} else if (video_codec_ == kCodecVP9) {
|
break;
|
||||||
|
case kCodecVP9:
|
||||||
vpx_parser_.reset(new VP9Parser);
|
vpx_parser_.reset(new VP9Parser);
|
||||||
} else if (video_codec_ == kCodecH264) {
|
break;
|
||||||
|
case kCodecH264:
|
||||||
header_parser_.reset(new H264VideoSliceHeaderParser);
|
header_parser_.reset(new H264VideoSliceHeaderParser);
|
||||||
} else if (video_codec_ == kCodecHVC1 || video_codec_ == kCodecHEV1) {
|
break;
|
||||||
|
case kCodecHVC1:
|
||||||
|
FALLTHROUGH_INTENDED;
|
||||||
|
case kCodecHEV1:
|
||||||
header_parser_.reset(new H265VideoSliceHeaderParser);
|
header_parser_.reset(new H265VideoSliceHeaderParser);
|
||||||
} else if (nalu_length_size_ > 0) {
|
break;
|
||||||
|
default:
|
||||||
LOG(WARNING) << "Unknown video codec '" << video_codec_
|
LOG(WARNING) << "Unknown video codec '" << video_codec_
|
||||||
<< "', whole subsamples will be encrypted.";
|
<< "', whole subsamples will be encrypted.";
|
||||||
}
|
}
|
||||||
|
@ -153,6 +165,11 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() {
|
||||||
// The offset will be adjusted in Segmenter after knowing moof size.
|
// The offset will be adjusted in Segmenter after knowing moof size.
|
||||||
traf()->auxiliary_offset.offsets.push_back(0);
|
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.
|
// Optimize saiz box.
|
||||||
SampleAuxiliaryInformationSize& saiz = traf()->auxiliary_size;
|
SampleAuxiliaryInformationSize& saiz = traf()->auxiliary_size;
|
||||||
saiz.sample_count = traf()->runs[0].sample_sizes.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,
|
// |sample_info_sizes| table is filled in only for subsample encryption,
|
||||||
// otherwise |sample_info_size| is just the IV size.
|
// otherwise |sample_info_size| is just the IV size.
|
||||||
DCHECK(!IsSubsampleEncryptionRequired());
|
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() {
|
Status EncryptingFragmenter::CreateEncryptor() {
|
||||||
|
@ -180,6 +199,19 @@ Status EncryptingFragmenter::CreateEncryptor() {
|
||||||
case FOURCC_cbc1:
|
case FOURCC_cbc1:
|
||||||
encryptor.reset(new AesCbcEncryptor(kNoPadding, kChainAcrossCalls));
|
encryptor.reset(new AesCbcEncryptor(kNoPadding, kChainAcrossCalls));
|
||||||
break;
|
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:
|
default:
|
||||||
return Status(error::MUXER_FAILURE, "Unsupported protection scheme.");
|
return Status(error::MUXER_FAILURE, "Unsupported protection scheme.");
|
||||||
}
|
}
|
||||||
|
@ -202,6 +234,8 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
||||||
DCHECK(encryptor_);
|
DCHECK(encryptor_);
|
||||||
|
|
||||||
SampleEncryptionEntry sample_encryption_entry;
|
SampleEncryptionEntry sample_encryption_entry;
|
||||||
|
// For 'cbcs' scheme, Constant IVs SHALL be used.
|
||||||
|
if (protection_scheme_ != FOURCC_cbcs)
|
||||||
sample_encryption_entry.initialization_vector = encryptor_->iv();
|
sample_encryption_entry.initialization_vector = encryptor_->iv();
|
||||||
uint8_t* data = sample->writable_data();
|
uint8_t* data = sample->writable_data();
|
||||||
if (IsSubsampleEncryptionRequired()) {
|
if (IsSubsampleEncryptionRequired()) {
|
||||||
|
|
|
@ -32,11 +32,17 @@ class EncryptingFragmenter : public Fragmenter {
|
||||||
/// track's timescale.
|
/// track's timescale.
|
||||||
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
||||||
/// 'cbc1', 'cbcs'.
|
/// '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,
|
EncryptingFragmenter(scoped_refptr<StreamInfo> info,
|
||||||
TrackFragment* traf,
|
TrackFragment* traf,
|
||||||
scoped_ptr<EncryptionKey> encryption_key,
|
scoped_ptr<EncryptionKey> encryption_key,
|
||||||
int64_t clear_time,
|
int64_t clear_time,
|
||||||
FourCC protection_scheme);
|
FourCC protection_scheme,
|
||||||
|
uint8_t crypt_byte_block,
|
||||||
|
uint8_t skip_byte_block);
|
||||||
|
|
||||||
~EncryptingFragmenter() override;
|
~EncryptingFragmenter() override;
|
||||||
|
|
||||||
|
@ -62,6 +68,8 @@ class EncryptingFragmenter : public Fragmenter {
|
||||||
const EncryptionKey* encryption_key() const { return encryption_key_.get(); }
|
const EncryptionKey* encryption_key() const { return encryption_key_.get(); }
|
||||||
AesCryptor* encryptor() { return encryptor_.get(); }
|
AesCryptor* encryptor() { return encryptor_.get(); }
|
||||||
FourCC protection_scheme() const { return protection_scheme_; }
|
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) {
|
void set_encryption_key(scoped_ptr<EncryptionKey> encryption_key) {
|
||||||
encryption_key_ = encryption_key.Pass();
|
encryption_key_ = encryption_key.Pass();
|
||||||
|
@ -83,7 +91,9 @@ class EncryptingFragmenter : public Fragmenter {
|
||||||
const uint8_t nalu_length_size_;
|
const uint8_t nalu_length_size_;
|
||||||
const VideoCodec video_codec_;
|
const VideoCodec video_codec_;
|
||||||
int64_t clear_time_;
|
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<VPxParser> vpx_parser_;
|
||||||
scoped_ptr<VideoSliceHeaderParser> header_parser_;
|
scoped_ptr<VideoSliceHeaderParser> header_parser_;
|
||||||
|
|
|
@ -25,12 +25,16 @@ KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof,
|
||||||
int64_t crypto_period_duration,
|
int64_t crypto_period_duration,
|
||||||
int64_t clear_time,
|
int64_t clear_time,
|
||||||
FourCC protection_scheme,
|
FourCC protection_scheme,
|
||||||
|
uint8_t crypt_byte_block,
|
||||||
|
uint8_t skip_byte_block,
|
||||||
MuxerListener* muxer_listener)
|
MuxerListener* muxer_listener)
|
||||||
: EncryptingFragmenter(info,
|
: EncryptingFragmenter(info,
|
||||||
traf,
|
traf,
|
||||||
scoped_ptr<EncryptionKey>(new EncryptionKey()),
|
scoped_ptr<EncryptionKey>(new EncryptionKey()),
|
||||||
clear_time,
|
clear_time,
|
||||||
protection_scheme),
|
protection_scheme,
|
||||||
|
crypt_byte_block,
|
||||||
|
skip_byte_block),
|
||||||
moof_(moof),
|
moof_(moof),
|
||||||
encryption_key_source_(encryption_key_source),
|
encryption_key_source_(encryption_key_source),
|
||||||
track_type_(track_type),
|
track_type_(track_type),
|
||||||
|
@ -105,10 +109,18 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption(
|
||||||
// Fill in SampleGroupDescription box information.
|
// Fill in SampleGroupDescription box information.
|
||||||
traf()->sample_group_description.grouping_type = FOURCC_seig;
|
traf()->sample_group_description.grouping_type = FOURCC_seig;
|
||||||
traf()->sample_group_description.entries.resize(1);
|
traf()->sample_group_description.entries.resize(1);
|
||||||
traf()->sample_group_description.entries[0].is_protected = 1;
|
auto& sample_group_entry = traf()->sample_group_description.entries[0];
|
||||||
traf()->sample_group_description.entries[0].per_sample_iv_size =
|
sample_group_entry.is_protected = 1;
|
||||||
encryptor()->iv().size();
|
if (protection_scheme() == FOURCC_cbcs) {
|
||||||
traf()->sample_group_description.entries[0].key_id = encryption_key()->key_id;
|
// 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.
|
// Fill in SampleToGroup box information.
|
||||||
traf()->sample_to_group.grouping_type = FOURCC_seig;
|
traf()->sample_to_group.grouping_type = FOURCC_seig;
|
||||||
|
|
|
@ -34,6 +34,10 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
|
||||||
/// track's timescale.
|
/// track's timescale.
|
||||||
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
||||||
/// 'cbc1', 'cbcs'.
|
/// '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
|
/// @param muxer_listener is a pointer to MuxerListener for notifying
|
||||||
/// muxer related events. This may be null.
|
/// muxer related events. This may be null.
|
||||||
KeyRotationFragmenter(MovieFragment* moof,
|
KeyRotationFragmenter(MovieFragment* moof,
|
||||||
|
@ -44,6 +48,8 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
|
||||||
int64_t crypto_period_duration,
|
int64_t crypto_period_duration,
|
||||||
int64_t clear_time,
|
int64_t clear_time,
|
||||||
FourCC protection_scheme,
|
FourCC protection_scheme,
|
||||||
|
uint8_t crypt_byte_block,
|
||||||
|
uint8_t skip_byte_block,
|
||||||
MuxerListener* muxer_listener);
|
MuxerListener* muxer_listener);
|
||||||
~KeyRotationFragmenter() override;
|
~KeyRotationFragmenter() override;
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,9 @@ 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;
|
||||||
|
|
||||||
|
@ -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;
|
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,
|
void GenerateSinf(const EncryptionKey& encryption_key,
|
||||||
FourCC old_type,
|
FourCC old_type,
|
||||||
FourCC protection_scheme,
|
FourCC protection_scheme,
|
||||||
|
@ -60,7 +75,18 @@ void GenerateSinf(const EncryptionKey& encryption_key,
|
||||||
auto& track_encryption = sinf->info.track_encryption;
|
auto& track_encryption = sinf->info.track_encryption;
|
||||||
track_encryption.default_is_protected = 1;
|
track_encryption.default_is_protected = 1;
|
||||||
DCHECK(!encryption_key.iv.empty());
|
DCHECK(!encryption_key.iv.empty());
|
||||||
|
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_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;
|
track_encryption.default_kid = encryption_key.key_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +185,16 @@ 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 =
|
||||||
|
@ -170,10 +206,12 @@ 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(protection_scheme, &encryption_key.iv))
|
if (!AesCryptor::GenerateRandomIv(local_protection_scheme,
|
||||||
|
&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,
|
||||||
protection_scheme, &description);
|
local_protection_scheme, &description);
|
||||||
if (muxer_listener_) {
|
if (muxer_listener_) {
|
||||||
muxer_listener_->OnEncryptionInfoReady(
|
muxer_listener_->OnEncryptionInfoReady(
|
||||||
kInitialEncryptionInfo, encryption_key.key_id,
|
kInitialEncryptionInfo, encryption_key.key_id,
|
||||||
|
@ -185,7 +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(),
|
||||||
protection_scheme, muxer_listener_);
|
local_protection_scheme, GetCryptByteBlock(local_protection_scheme),
|
||||||
|
GetSkipByteBlock(local_protection_scheme), muxer_listener_);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,12 +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(protection_scheme, &encryption_key->iv))
|
if (!AesCryptor::GenerateRandomIv(local_protection_scheme,
|
||||||
|
&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,
|
||||||
protection_scheme, &description);
|
local_protection_scheme, &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());
|
||||||
|
@ -218,7 +259,8 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
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(),
|
||||||
protection_scheme);
|
local_protection_scheme, GetCryptByteBlock(local_protection_scheme),
|
||||||
|
GetSkipByteBlock(local_protection_scheme));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose the first stream if there is no VIDEO.
|
// 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
|
FourCC protection_scheme = is_audio() ? audio_description().sinf.type.type
|
||||||
: video_description().sinf.type.type;
|
: video_description().sinf.type.type;
|
||||||
return scoped_ptr<DecryptConfig>(
|
std::vector<uint8_t> iv = sample_encryption_entry.initialization_vector;
|
||||||
new DecryptConfig(track_encryption().default_kid,
|
if (iv.empty()) {
|
||||||
sample_encryption_entry.initialization_vector,
|
if (protection_scheme != FOURCC_cbcs) {
|
||||||
sample_encryption_entry.subsamples, protection_scheme));
|
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
|
} // namespace mp4
|
||||||
|
|
|
@ -19,6 +19,9 @@ const int kSumAscending1 = 45;
|
||||||
const int kAudioScale = 48000;
|
const int kAudioScale = 48000;
|
||||||
const int kVideoScale = 25;
|
const int kVideoScale = 25;
|
||||||
|
|
||||||
|
const uint8_t kDefaultCryptByteBlock = 2;
|
||||||
|
const uint8_t kDefaultSkipByteBlock = 8;
|
||||||
|
|
||||||
const uint8_t kAuxInfo[] = {
|
const uint8_t kAuxInfo[] = {
|
||||||
// Sample 1: IV (no subsumples).
|
// Sample 1: IV (no subsumples).
|
||||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
||||||
|
@ -29,7 +32,8 @@ const uint8_t kAuxInfo[] = {
|
||||||
// Sample 2: Subsample 1.
|
// Sample 2: Subsample 1.
|
||||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
||||||
// Sample 2: Subsample 2.
|
// Sample 2: Subsample 2.
|
||||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x04};
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
const uint8_t kSampleEncryptionDataWithSubsamples[] = {
|
const uint8_t kSampleEncryptionDataWithSubsamples[] = {
|
||||||
// Sample count.
|
// Sample count.
|
||||||
|
@ -47,7 +51,8 @@ const uint8_t kSampleEncryptionDataWithSubsamples[] = {
|
||||||
// Sample 2: Subsample 1.
|
// Sample 2: Subsample 1.
|
||||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
||||||
// Sample 2: Subsample 2.
|
// Sample 2: Subsample 2.
|
||||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x04};
|
0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
const uint8_t kSampleEncryptionDataWithoutSubsamples[] = {
|
const uint8_t kSampleEncryptionDataWithoutSubsamples[] = {
|
||||||
// Sample count.
|
// Sample count.
|
||||||
|
@ -55,13 +60,37 @@ const uint8_t kSampleEncryptionDataWithoutSubsamples[] = {
|
||||||
// Sample 1: IV.
|
// Sample 1: IV.
|
||||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
||||||
// Sample 2: IV.
|
// 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 kIv1[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31};
|
||||||
const char kIv2[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32};
|
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,
|
const uint8_t kKeyId[] = {
|
||||||
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44};
|
0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
|
||||||
|
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44,
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -142,7 +171,7 @@ class TrackRunIteratorTest : public testing::Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the first sample description of a Track to indicate encryption
|
// Update the first sample description of a Track to indicate encryption
|
||||||
void AddEncryption(Track* track) {
|
void AddEncryption(FourCC protection_scheme, Track* track) {
|
||||||
SampleDescription* stsd =
|
SampleDescription* stsd =
|
||||||
&track->media.information.sample_table.description;
|
&track->media.information.sample_table.description;
|
||||||
ProtectionSchemeInfo* sinf;
|
ProtectionSchemeInfo* sinf;
|
||||||
|
@ -152,9 +181,20 @@ class TrackRunIteratorTest : public testing::Test {
|
||||||
sinf = &stsd->audio_entries[0].sinf;
|
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_is_protected = 1;
|
||||||
|
// 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_per_sample_iv_size = 8;
|
||||||
|
}
|
||||||
sinf->info.track_encryption.default_kid.assign(kKeyId,
|
sinf->info.track_encryption.default_kid.assign(kKeyId,
|
||||||
kKeyId + arraysize(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) {
|
void SetAscending(std::vector<uint32_t>* vec) {
|
||||||
vec->resize(10);
|
vec->resize(10);
|
||||||
for (size_t i = 0; i < vec->size(); i++)
|
for (size_t i = 0; i < vec->size(); i++)
|
||||||
|
@ -364,7 +437,7 @@ TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) {
|
||||||
|
|
||||||
TEST_F(TrackRunIteratorTest,
|
TEST_F(TrackRunIteratorTest,
|
||||||
DecryptConfigTestWithSampleEncryptionAndSubsample) {
|
DecryptConfigTestWithSampleEncryptionAndSubsample) {
|
||||||
AddEncryption(&moov_.tracks[1]);
|
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
|
||||||
iter_.reset(new TrackRunIterator(&moov_));
|
iter_.reset(new TrackRunIterator(&moov_));
|
||||||
|
|
||||||
MovieFragment moof = CreateFragment();
|
MovieFragment moof = CreateFragment();
|
||||||
|
@ -402,7 +475,7 @@ TEST_F(TrackRunIteratorTest,
|
||||||
|
|
||||||
TEST_F(TrackRunIteratorTest,
|
TEST_F(TrackRunIteratorTest,
|
||||||
DecryptConfigTestWithSampleEncryptionAndNoSubsample) {
|
DecryptConfigTestWithSampleEncryptionAndNoSubsample) {
|
||||||
AddEncryption(&moov_.tracks[1]);
|
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
|
||||||
iter_.reset(new TrackRunIterator(&moov_));
|
iter_.reset(new TrackRunIterator(&moov_));
|
||||||
|
|
||||||
MovieFragment moof = CreateFragment();
|
MovieFragment moof = CreateFragment();
|
||||||
|
@ -432,8 +505,84 @@ TEST_F(TrackRunIteratorTest,
|
||||||
EXPECT_EQ(config->subsamples().size(), 0u);
|
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) {
|
TEST_F(TrackRunIteratorTest, DecryptConfigTestWithAuxInfo) {
|
||||||
AddEncryption(&moov_.tracks[1]);
|
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
|
||||||
iter_.reset(new TrackRunIterator(&moov_));
|
iter_.reset(new TrackRunIterator(&moov_));
|
||||||
|
|
||||||
MovieFragment moof = CreateFragment();
|
MovieFragment moof = CreateFragment();
|
||||||
|
@ -470,8 +619,8 @@ TEST_F(TrackRunIteratorTest, DecryptConfigTestWithAuxInfo) {
|
||||||
|
|
||||||
// It is legal for aux info blocks to be shared among multiple formats.
|
// It is legal for aux info blocks to be shared among multiple formats.
|
||||||
TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
|
TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
|
||||||
AddEncryption(&moov_.tracks[0]);
|
AddEncryption(FOURCC_cenc, &moov_.tracks[0]);
|
||||||
AddEncryption(&moov_.tracks[1]);
|
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
|
||||||
iter_.reset(new TrackRunIterator(&moov_));
|
iter_.reset(new TrackRunIterator(&moov_));
|
||||||
|
|
||||||
MovieFragment moof = CreateFragment();
|
MovieFragment moof = CreateFragment();
|
||||||
|
@ -512,8 +661,8 @@ TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
|
||||||
// byte 10000: track 1, run 2 data
|
// byte 10000: track 1, run 2 data
|
||||||
// byte 20000: track 1, run 1 aux info
|
// byte 20000: track 1, run 1 aux info
|
||||||
TEST_F(TrackRunIteratorTest, UnexpectedOrderingTest) {
|
TEST_F(TrackRunIteratorTest, UnexpectedOrderingTest) {
|
||||||
AddEncryption(&moov_.tracks[0]);
|
AddEncryption(FOURCC_cenc, &moov_.tracks[0]);
|
||||||
AddEncryption(&moov_.tracks[1]);
|
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
|
||||||
iter_.reset(new TrackRunIterator(&moov_));
|
iter_.reset(new TrackRunIterator(&moov_));
|
||||||
|
|
||||||
MovieFragment moof = CreateFragment();
|
MovieFragment moof = CreateFragment();
|
||||||
|
|
Loading…
Reference in New Issue