Support key rotation when generating MP4 fragments
Change-Id: I472e03a2d41ee450c12c0fe3012904628d6893e7
This commit is contained in:
parent
9076411044
commit
50ed026751
|
@ -219,8 +219,10 @@ bool RunPackager(const std::string& input) {
|
||||||
LOG(ERROR) << "FLAGS_track_type should be either 'SD' or 'HD'";
|
LOG(ERROR) << "FLAGS_track_type should be either 'SD' or 'HD'";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
muxer->SetEncryptionKeySource(
|
muxer->SetEncryptionKeySource(encryption_key_source.get(),
|
||||||
encryption_key_source.get(), track_type, FLAGS_clear_lead);
|
track_type,
|
||||||
|
FLAGS_clear_lead,
|
||||||
|
FLAGS_crypto_period_duration);
|
||||||
|
|
||||||
// Start remuxing process.
|
// Start remuxing process.
|
||||||
status = demuxer.Run();
|
status = demuxer.Run();
|
||||||
|
|
|
@ -33,6 +33,10 @@ DEFINE_string(rsa_signing_key_path,
|
||||||
"",
|
"",
|
||||||
"Stores PKCS#1 RSA private key for request signing. Exclusive "
|
"Stores PKCS#1 RSA private key for request signing. Exclusive "
|
||||||
"with --aes_signing_key.");
|
"with --aes_signing_key.");
|
||||||
|
DEFINE_int32(crypto_period_duration,
|
||||||
|
0,
|
||||||
|
"Crypto period duration in seconds. If it is non-zero, key "
|
||||||
|
"rotation is enabled.");
|
||||||
|
|
||||||
static bool IsNotEmptyWithWidevineEncryption(const char* flag_name,
|
static bool IsNotEmptyWithWidevineEncryption(const char* flag_name,
|
||||||
const std::string& flag_value) {
|
const std::string& flag_value) {
|
||||||
|
|
|
@ -30,6 +30,13 @@ Status EncryptionKeySource::GetKey(TrackType track_type, EncryptionKey* key) {
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status EncryptionKeySource::GetCryptoPeriodKey(size_t crypto_period_index,
|
||||||
|
TrackType track_type,
|
||||||
|
EncryptionKey* key) {
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
return Status(error::UNIMPLEMENTED, "");
|
||||||
|
}
|
||||||
|
|
||||||
scoped_ptr<EncryptionKeySource> EncryptionKeySource::CreateFromHexStrings(
|
scoped_ptr<EncryptionKeySource> EncryptionKeySource::CreateFromHexStrings(
|
||||||
const std::string& key_id_hex,
|
const std::string& key_id_hex,
|
||||||
const std::string& key_hex,
|
const std::string& key_hex,
|
||||||
|
|
|
@ -41,6 +41,12 @@ class EncryptionKeySource {
|
||||||
/// @return OK on success, an error status otherwise.
|
/// @return OK on success, an error status otherwise.
|
||||||
virtual Status GetKey(TrackType track_type, EncryptionKey* key);
|
virtual Status GetKey(TrackType track_type, EncryptionKey* key);
|
||||||
|
|
||||||
|
/// Get encryption key of the specified track type at the specified index.
|
||||||
|
/// @return OK on success, an error status otherwise.
|
||||||
|
virtual Status GetCryptoPeriodKey(size_t crypto_period_index,
|
||||||
|
TrackType track_type,
|
||||||
|
EncryptionKey* key);
|
||||||
|
|
||||||
/// Create EncryptionKeySource object from hex strings.
|
/// Create EncryptionKeySource object from hex strings.
|
||||||
/// @param key_id_hex is the key id in hex string.
|
/// @param key_id_hex is the key id in hex string.
|
||||||
/// @param key_hex is the key in hex string.
|
/// @param key_hex is the key in hex string.
|
||||||
|
|
|
@ -14,10 +14,11 @@ namespace media {
|
||||||
|
|
||||||
Muxer::Muxer(const MuxerOptions& options)
|
Muxer::Muxer(const MuxerOptions& options)
|
||||||
: options_(options),
|
: options_(options),
|
||||||
encryption_key_source_(NULL),
|
|
||||||
initialized_(false),
|
initialized_(false),
|
||||||
|
encryption_key_source_(NULL),
|
||||||
track_type_(EncryptionKeySource::TRACK_TYPE_SD),
|
track_type_(EncryptionKeySource::TRACK_TYPE_SD),
|
||||||
clear_lead_in_seconds_(0),
|
clear_lead_in_seconds_(0),
|
||||||
|
crypto_period_duration_in_seconds_(0),
|
||||||
muxer_listener_(NULL),
|
muxer_listener_(NULL),
|
||||||
clock_(NULL) {}
|
clock_(NULL) {}
|
||||||
|
|
||||||
|
@ -25,10 +26,13 @@ Muxer::~Muxer() {}
|
||||||
|
|
||||||
void Muxer::SetEncryptionKeySource(EncryptionKeySource* encryption_key_source,
|
void Muxer::SetEncryptionKeySource(EncryptionKeySource* encryption_key_source,
|
||||||
EncryptionKeySource::TrackType track_type,
|
EncryptionKeySource::TrackType track_type,
|
||||||
double clear_lead_in_seconds) {
|
double clear_lead_in_seconds,
|
||||||
|
double crypto_period_duration_in_seconds) {
|
||||||
|
DCHECK(encryption_key_source);
|
||||||
encryption_key_source_ = encryption_key_source;
|
encryption_key_source_ = encryption_key_source;
|
||||||
track_type_ = track_type;
|
track_type_ = track_type;
|
||||||
clear_lead_in_seconds_ = clear_lead_in_seconds;
|
clear_lead_in_seconds_ = clear_lead_in_seconds;
|
||||||
|
crypto_period_duration_in_seconds_ = crypto_period_duration_in_seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Muxer::AddStream(MediaStream* stream) {
|
void Muxer::AddStream(MediaStream* stream) {
|
||||||
|
|
|
@ -45,9 +45,13 @@ class Muxer {
|
||||||
/// @param track_type should be either SD or HD. It affects whether SD key or
|
/// @param track_type should be either SD or HD. It affects whether SD key or
|
||||||
/// HD key is used to encrypt the video content.
|
/// HD key is used to encrypt the video content.
|
||||||
/// @param clear_lead_in_seconds specifies clear lead duration in seconds.
|
/// @param clear_lead_in_seconds specifies clear lead duration in seconds.
|
||||||
|
/// @param crypto_period_duration_in_seconds specifies crypto period duration
|
||||||
|
/// in seconds. A positive value means key rotation is enabled, the
|
||||||
|
/// key source must support key rotation in this case.
|
||||||
void SetEncryptionKeySource(EncryptionKeySource* encryption_key_source,
|
void SetEncryptionKeySource(EncryptionKeySource* encryption_key_source,
|
||||||
EncryptionKeySource::TrackType track_type,
|
EncryptionKeySource::TrackType track_type,
|
||||||
double clear_lead_in_seconds);
|
double clear_lead_in_seconds,
|
||||||
|
double crypto_period_duration_in_seconds);
|
||||||
|
|
||||||
/// Add video/audio stream.
|
/// Add video/audio stream.
|
||||||
void AddStream(MediaStream* stream);
|
void AddStream(MediaStream* stream);
|
||||||
|
@ -78,6 +82,9 @@ class Muxer {
|
||||||
}
|
}
|
||||||
EncryptionKeySource::TrackType track_type() const { return track_type_; }
|
EncryptionKeySource::TrackType track_type() const { return track_type_; }
|
||||||
double clear_lead_in_seconds() const { return clear_lead_in_seconds_; }
|
double clear_lead_in_seconds() const { return clear_lead_in_seconds_; }
|
||||||
|
double crypto_period_duration_in_seconds() const {
|
||||||
|
return crypto_period_duration_in_seconds_;
|
||||||
|
}
|
||||||
event::MuxerListener* muxer_listener() { return muxer_listener_; }
|
event::MuxerListener* muxer_listener() { return muxer_listener_; }
|
||||||
base::Clock* clock() { return clock_; }
|
base::Clock* clock() { return clock_; }
|
||||||
|
|
||||||
|
@ -99,11 +106,12 @@ class Muxer {
|
||||||
scoped_refptr<MediaSample> sample) = 0;
|
scoped_refptr<MediaSample> sample) = 0;
|
||||||
|
|
||||||
MuxerOptions options_;
|
MuxerOptions options_;
|
||||||
|
bool initialized_;
|
||||||
std::vector<MediaStream*> streams_;
|
std::vector<MediaStream*> streams_;
|
||||||
EncryptionKeySource* encryption_key_source_;
|
EncryptionKeySource* encryption_key_source_;
|
||||||
bool initialized_;
|
|
||||||
EncryptionKeySource::TrackType track_type_;
|
EncryptionKeySource::TrackType track_type_;
|
||||||
double clear_lead_in_seconds_;
|
double clear_lead_in_seconds_;
|
||||||
|
double crypto_period_duration_in_seconds_;
|
||||||
|
|
||||||
event::MuxerListener* muxer_listener_;
|
event::MuxerListener* muxer_listener_;
|
||||||
// An external injected clock, can be NULL.
|
// An external injected clock, can be NULL.
|
||||||
|
|
|
@ -9,11 +9,17 @@
|
||||||
#include "media/base/aes_encryptor.h"
|
#include "media/base/aes_encryptor.h"
|
||||||
#include "media/base/buffer_reader.h"
|
#include "media/base/buffer_reader.h"
|
||||||
#include "media/base/buffer_writer.h"
|
#include "media/base/buffer_writer.h"
|
||||||
|
#include "media/base/encryption_key_source.h"
|
||||||
#include "media/base/media_sample.h"
|
#include "media/base/media_sample.h"
|
||||||
#include "media/formats/mp4/box_definitions.h"
|
#include "media/formats/mp4/box_definitions.h"
|
||||||
#include "media/formats/mp4/cenc.h"
|
#include "media/formats/mp4/cenc.h"
|
||||||
|
|
||||||
|
namespace media {
|
||||||
|
namespace mp4 {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
// Generate 64bit IV by default.
|
||||||
|
const size_t kDefaultIvSize = 8u;
|
||||||
const int64 kInvalidTime = kint64max;
|
const int64 kInvalidTime = kint64max;
|
||||||
|
|
||||||
// Optimize sample entries table. If all values in |entries| are identical,
|
// Optimize sample entries table. If all values in |entries| are identical,
|
||||||
|
@ -36,33 +42,48 @@ bool OptimizeSampleEntries(std::vector<T>* entries, T* default_value) {
|
||||||
*default_value = value;
|
*default_value = value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace media {
|
|
||||||
namespace mp4 {
|
|
||||||
|
|
||||||
Fragmenter::Fragmenter(TrackFragment* traf,
|
Fragmenter::Fragmenter(TrackFragment* traf,
|
||||||
scoped_ptr<AesCtrEncryptor> encryptor,
|
|
||||||
int64 clear_time,
|
|
||||||
uint8 nalu_length_size,
|
|
||||||
bool normalize_presentation_timestamp)
|
bool normalize_presentation_timestamp)
|
||||||
: encryptor_(encryptor.Pass()),
|
: traf_(traf),
|
||||||
nalu_length_size_(nalu_length_size),
|
nalu_length_size_(0),
|
||||||
traf_(traf),
|
clear_time_(0),
|
||||||
fragment_finalized_(false),
|
fragment_finalized_(false),
|
||||||
fragment_duration_(0),
|
fragment_duration_(0),
|
||||||
normalize_presentation_timestamp_(normalize_presentation_timestamp),
|
normalize_presentation_timestamp_(normalize_presentation_timestamp),
|
||||||
presentation_start_time_(kInvalidTime),
|
presentation_start_time_(kInvalidTime),
|
||||||
earliest_presentation_time_(kInvalidTime),
|
earliest_presentation_time_(kInvalidTime),
|
||||||
first_sap_time_(kInvalidTime),
|
first_sap_time_(kInvalidTime) {
|
||||||
clear_time_(clear_time) {}
|
DCHECK(traf);
|
||||||
|
}
|
||||||
|
|
||||||
|
Fragmenter::Fragmenter(TrackFragment* traf,
|
||||||
|
bool normalize_presentation_timestamp,
|
||||||
|
scoped_ptr<EncryptionKey> encryption_key,
|
||||||
|
int64 clear_time,
|
||||||
|
uint8 nalu_length_size)
|
||||||
|
: traf_(traf),
|
||||||
|
encryption_key_(encryption_key.Pass()),
|
||||||
|
nalu_length_size_(nalu_length_size),
|
||||||
|
clear_time_(clear_time),
|
||||||
|
fragment_finalized_(false),
|
||||||
|
fragment_duration_(0),
|
||||||
|
normalize_presentation_timestamp_(normalize_presentation_timestamp),
|
||||||
|
presentation_start_time_(kInvalidTime),
|
||||||
|
earliest_presentation_time_(kInvalidTime),
|
||||||
|
first_sap_time_(kInvalidTime) {
|
||||||
|
DCHECK(traf);
|
||||||
|
DCHECK(encryption_key_);
|
||||||
|
}
|
||||||
|
|
||||||
Fragmenter::~Fragmenter() {}
|
Fragmenter::~Fragmenter() {}
|
||||||
|
|
||||||
Status Fragmenter::AddSample(scoped_refptr<MediaSample> sample) {
|
Status Fragmenter::AddSample(scoped_refptr<MediaSample> sample) {
|
||||||
CHECK_GT(sample->duration(), 0);
|
CHECK_GT(sample->duration(), 0);
|
||||||
|
|
||||||
if (ShouldEncryptFragment()) {
|
if (encryptor_) {
|
||||||
Status status = EncryptSample(sample);
|
Status status = EncryptSample(sample);
|
||||||
if (!status.ok())
|
if (!status.ok())
|
||||||
return status;
|
return status;
|
||||||
|
@ -112,11 +133,9 @@ Status Fragmenter::AddSample(scoped_refptr<MediaSample> sample) {
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fragmenter::InitializeFragment() {
|
Status Fragmenter::InitializeFragment() {
|
||||||
fragment_finalized_ = false;
|
fragment_finalized_ = false;
|
||||||
traf_->decode_time.decode_time += fragment_duration_;
|
traf_->decode_time.decode_time += fragment_duration_;
|
||||||
traf_->auxiliary_size.sample_info_sizes.clear();
|
|
||||||
traf_->auxiliary_offset.offsets.clear();
|
|
||||||
traf_->runs.clear();
|
traf_->runs.clear();
|
||||||
traf_->runs.resize(1);
|
traf_->runs.resize(1);
|
||||||
traf_->runs[0].flags = TrackFragmentRun::kDataOffsetPresentMask;
|
traf_->runs[0].flags = TrackFragmentRun::kDataOffsetPresentMask;
|
||||||
|
@ -127,41 +146,28 @@ void Fragmenter::InitializeFragment() {
|
||||||
data_.reset(new BufferWriter());
|
data_.reset(new BufferWriter());
|
||||||
aux_data_.reset(new BufferWriter());
|
aux_data_.reset(new BufferWriter());
|
||||||
|
|
||||||
if (ShouldEncryptFragment()) {
|
if (!encryption_key_)
|
||||||
if (!IsSubsampleEncryptionRequired()) {
|
return Status::OK;
|
||||||
DCHECK(encryptor_);
|
|
||||||
traf_->auxiliary_size.default_sample_info_size = encryptor_->iv().size();
|
// Enable encryption for this fragment if decode time passes clear lead.
|
||||||
}
|
if (static_cast<int64>(traf_->decode_time.decode_time) >= clear_time_)
|
||||||
}
|
return PrepareFragmentForEncryption();
|
||||||
|
|
||||||
|
// Otherwise, this fragment should be in clear text.
|
||||||
|
// We generate at most two sample description entries, encrypted entry and
|
||||||
|
// clear entry. The 1-based clear entry index is always 2.
|
||||||
|
const uint32 kClearSampleDescriptionIndex = 2;
|
||||||
|
|
||||||
|
traf_->header.flags |=
|
||||||
|
TrackFragmentHeader::kSampleDescriptionIndexPresentMask;
|
||||||
|
traf_->header.sample_description_index = kClearSampleDescriptionIndex;
|
||||||
|
|
||||||
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fragmenter::FinalizeFragment() {
|
void Fragmenter::FinalizeFragment() {
|
||||||
if (ShouldEncryptFragment()) {
|
if (encryptor_)
|
||||||
DCHECK(encryptor_);
|
FinalizeFragmentForEncryption();
|
||||||
|
|
||||||
// The offset will be adjusted in Segmenter when we know moof size.
|
|
||||||
traf_->auxiliary_offset.offsets.push_back(0);
|
|
||||||
|
|
||||||
// Optimize saiz box.
|
|
||||||
SampleAuxiliaryInformationSize& saiz = traf_->auxiliary_size;
|
|
||||||
saiz.sample_count = traf_->runs[0].sample_sizes.size();
|
|
||||||
if (!saiz.sample_info_sizes.empty()) {
|
|
||||||
if (!OptimizeSampleEntries(&saiz.sample_info_sizes,
|
|
||||||
&saiz.default_sample_info_size)) {
|
|
||||||
saiz.default_sample_info_size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (encryptor_ && clear_time_ > 0) {
|
|
||||||
// This fragment should be in clear.
|
|
||||||
// We generate at most two sample description entries, encrypted entry and
|
|
||||||
// clear entry. The 1-based clear entry index is always 2.
|
|
||||||
const uint32 kClearSampleDescriptionIndex = 2;
|
|
||||||
|
|
||||||
traf_->header.flags |=
|
|
||||||
TrackFragmentHeader::kSampleDescriptionIndexPresentMask;
|
|
||||||
traf_->header.sample_description_index = kClearSampleDescriptionIndex;
|
|
||||||
clear_time_ -= fragment_duration_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optimize trun box.
|
// Optimize trun box.
|
||||||
traf_->runs[0].sample_count = traf_->runs[0].sample_sizes.size();
|
traf_->runs[0].sample_count = traf_->runs[0].sample_sizes.size();
|
||||||
|
@ -203,6 +209,47 @@ void Fragmenter::GenerateSegmentReference(SegmentReference* reference) {
|
||||||
reference->earliest_presentation_time = earliest_presentation_time_;
|
reference->earliest_presentation_time = earliest_presentation_time_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status Fragmenter::PrepareFragmentForEncryption() {
|
||||||
|
traf_->auxiliary_size.sample_info_sizes.clear();
|
||||||
|
traf_->auxiliary_offset.offsets.clear();
|
||||||
|
return encryptor_ ? Status::OK : CreateEncryptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fragmenter::FinalizeFragmentForEncryption() {
|
||||||
|
// The offset will be adjusted in Segmenter when we know moof size.
|
||||||
|
traf_->auxiliary_offset.offsets.push_back(0);
|
||||||
|
|
||||||
|
// Optimize saiz box.
|
||||||
|
SampleAuxiliaryInformationSize& saiz = traf_->auxiliary_size;
|
||||||
|
saiz.sample_count = traf_->runs[0].sample_sizes.size();
|
||||||
|
if (!saiz.sample_info_sizes.empty()) {
|
||||||
|
if (!OptimizeSampleEntries(&saiz.sample_info_sizes,
|
||||||
|
&saiz.default_sample_info_size)) {
|
||||||
|
saiz.default_sample_info_size = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// |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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Status Fragmenter::CreateEncryptor() {
|
||||||
|
DCHECK(encryption_key_);
|
||||||
|
|
||||||
|
scoped_ptr<AesCtrEncryptor> encryptor(new AesCtrEncryptor());
|
||||||
|
const bool initialized = encryption_key_->iv.empty()
|
||||||
|
? encryptor->InitializeWithRandomIv(
|
||||||
|
encryption_key_->key, kDefaultIvSize)
|
||||||
|
: encryptor->InitializeWithIv(
|
||||||
|
encryption_key_->key, encryption_key_->iv);
|
||||||
|
if (!initialized)
|
||||||
|
return Status(error::MUXER_FAILURE, "Failed to create the encryptor.");
|
||||||
|
encryptor_ = encryptor.Pass();
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
void Fragmenter::EncryptBytes(uint8* data, uint32 size) {
|
void Fragmenter::EncryptBytes(uint8* data, uint32 size) {
|
||||||
DCHECK(encryptor_);
|
DCHECK(encryptor_);
|
||||||
CHECK(encryptor_->Encrypt(data, size, data));
|
CHECK(encryptor_->Encrypt(data, size, data));
|
||||||
|
|
|
@ -19,8 +19,11 @@ class AesCtrEncryptor;
|
||||||
class BufferWriter;
|
class BufferWriter;
|
||||||
class MediaSample;
|
class MediaSample;
|
||||||
|
|
||||||
|
struct EncryptionKey;
|
||||||
|
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
|
struct MovieFragment;
|
||||||
struct SegmentReference;
|
struct SegmentReference;
|
||||||
struct TrackFragment;
|
struct TrackFragment;
|
||||||
|
|
||||||
|
@ -30,26 +33,33 @@ struct TrackFragment;
|
||||||
class Fragmenter {
|
class Fragmenter {
|
||||||
public:
|
public:
|
||||||
/// @param traf points to a TrackFragment box.
|
/// @param traf points to a TrackFragment box.
|
||||||
/// @param encryptor handles encryption of the samples. It can be NULL, which
|
/// @param normalize_presentation_timestamp defines whether PTS should be
|
||||||
/// indicates no encryption is required.
|
/// normalized to start from zero.
|
||||||
|
Fragmenter(TrackFragment* traf,
|
||||||
|
bool normalize_presentation_timestamp);
|
||||||
|
|
||||||
|
/// @param traf points to a TrackFragment box.
|
||||||
|
/// @param normalize_presentation_timestamp defines whether PTS should be
|
||||||
|
/// normalized to start from zero.
|
||||||
|
/// @param encryption_key contains the encryption parameters.
|
||||||
/// @param clear_time specifies clear lead duration in units of the current
|
/// @param clear_time specifies clear lead duration in units of the current
|
||||||
/// track's timescale.
|
/// track's timescale.
|
||||||
/// @param nalu_length_size NAL unit length size, in bytes, for subsample
|
/// @param nalu_length_size NAL unit length size, in bytes, for subsample
|
||||||
/// encryption.
|
/// encryption.
|
||||||
/// @param normalize_presentation_timestamp defines whether PTS should be
|
|
||||||
/// normalized to start from zero.
|
|
||||||
Fragmenter(TrackFragment* traf,
|
Fragmenter(TrackFragment* traf,
|
||||||
scoped_ptr<AesCtrEncryptor> encryptor,
|
bool normalize_presentation_timestamp,
|
||||||
|
scoped_ptr<EncryptionKey> encryption_key,
|
||||||
int64 clear_time,
|
int64 clear_time,
|
||||||
uint8 nalu_length_size,
|
uint8 nalu_length_size);
|
||||||
bool normalize_presentation_timestamp);
|
|
||||||
~Fragmenter();
|
virtual ~Fragmenter();
|
||||||
|
|
||||||
/// Add a sample to the fragmenter.
|
/// Add a sample to the fragmenter.
|
||||||
Status AddSample(scoped_refptr<MediaSample> sample);
|
Status AddSample(scoped_refptr<MediaSample> sample);
|
||||||
|
|
||||||
/// Initialize the fragment with default data.
|
/// Initialize the fragment with default data.
|
||||||
void InitializeFragment();
|
/// @return OK on success, an error status otherwise.
|
||||||
|
Status InitializeFragment();
|
||||||
|
|
||||||
/// Finalize and optimize the fragment.
|
/// Finalize and optimize the fragment.
|
||||||
void FinalizeFragment();
|
void FinalizeFragment();
|
||||||
|
@ -66,34 +76,52 @@ class Fragmenter {
|
||||||
BufferWriter* data() { return data_.get(); }
|
BufferWriter* data() { return data_.get(); }
|
||||||
BufferWriter* aux_data() { return aux_data_.get(); }
|
BufferWriter* aux_data() { return aux_data_.get(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Prepare current fragment for encryption.
|
||||||
|
/// @return OK on success, an error status otherwise.
|
||||||
|
virtual Status PrepareFragmentForEncryption();
|
||||||
|
/// Finalize current fragment for encryption.
|
||||||
|
virtual void FinalizeFragmentForEncryption();
|
||||||
|
|
||||||
|
/// Create the encryptor for the internal encryption key. The existing
|
||||||
|
/// encryptor will be reset if it is not NULL.
|
||||||
|
/// @return OK on success, an error status otherwise.
|
||||||
|
Status CreateEncryptor();
|
||||||
|
|
||||||
|
TrackFragment* traf() { return traf_; }
|
||||||
|
EncryptionKey* encryption_key() { return encryption_key_.get(); }
|
||||||
|
AesCtrEncryptor* encryptor() { return encryptor_.get(); }
|
||||||
|
|
||||||
|
void set_encryption_key(scoped_ptr<EncryptionKey> encryption_key) {
|
||||||
|
encryption_key_ = encryption_key.Pass();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void EncryptBytes(uint8* data, uint32 size);
|
void EncryptBytes(uint8* data, uint32 size);
|
||||||
Status EncryptSample(scoped_refptr<MediaSample> sample);
|
Status EncryptSample(scoped_refptr<MediaSample> sample);
|
||||||
|
|
||||||
// Should we enable encrytion for the current fragment?
|
|
||||||
bool ShouldEncryptFragment() {
|
|
||||||
return (encryptor_ != NULL && clear_time_ <= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should we enable subsample encryption?
|
// Should we enable subsample encryption?
|
||||||
bool IsSubsampleEncryptionRequired() { return nalu_length_size_ != 0; }
|
bool IsSubsampleEncryptionRequired() { return nalu_length_size_ != 0; }
|
||||||
|
|
||||||
// Check if the current fragment starts with SAP.
|
// Check if the current fragment starts with SAP.
|
||||||
bool StartsWithSAP();
|
bool StartsWithSAP();
|
||||||
|
|
||||||
|
TrackFragment* traf_;
|
||||||
|
|
||||||
|
scoped_ptr<EncryptionKey> encryption_key_;
|
||||||
scoped_ptr<AesCtrEncryptor> encryptor_;
|
scoped_ptr<AesCtrEncryptor> encryptor_;
|
||||||
// If this stream contains AVC, subsample encryption specifies that the size
|
// If this stream contains AVC, subsample encryption specifies that the size
|
||||||
// and type of NAL units remain unencrypted. This field specifies the size of
|
// and type of NAL units remain unencrypted. This field specifies the size of
|
||||||
// the size field. Can be 1, 2 or 4 bytes.
|
// the size field. Can be 1, 2 or 4 bytes.
|
||||||
uint8 nalu_length_size_;
|
const uint8 nalu_length_size_;
|
||||||
TrackFragment* traf_;
|
const int64 clear_time_;
|
||||||
|
|
||||||
bool fragment_finalized_;
|
bool fragment_finalized_;
|
||||||
uint64 fragment_duration_;
|
uint64 fragment_duration_;
|
||||||
bool normalize_presentation_timestamp_;
|
bool normalize_presentation_timestamp_;
|
||||||
int64 presentation_start_time_;
|
int64 presentation_start_time_;
|
||||||
int64 earliest_presentation_time_;
|
int64 earliest_presentation_time_;
|
||||||
int64 first_sap_time_;
|
int64 first_sap_time_;
|
||||||
int64 clear_time_;
|
|
||||||
scoped_ptr<BufferWriter> data_;
|
scoped_ptr<BufferWriter> data_;
|
||||||
scoped_ptr<BufferWriter> aux_data_;
|
scoped_ptr<BufferWriter> aux_data_;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
// Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#include "media/formats/mp4/key_rotation_fragmenter.h"
|
||||||
|
|
||||||
|
#include "media/base/aes_encryptor.h"
|
||||||
|
#include "media/formats/mp4/box_definitions.h"
|
||||||
|
|
||||||
|
namespace media {
|
||||||
|
namespace mp4 {
|
||||||
|
|
||||||
|
KeyRotationFragmenter::KeyRotationFragmenter(
|
||||||
|
MovieFragment* moof,
|
||||||
|
TrackFragment* traf,
|
||||||
|
bool normalize_presentation_timestamp,
|
||||||
|
EncryptionKeySource* encryption_key_source,
|
||||||
|
EncryptionKeySource::TrackType track_type,
|
||||||
|
int64 crypto_period_duration,
|
||||||
|
int64 clear_time,
|
||||||
|
uint8 nalu_length_size)
|
||||||
|
: Fragmenter(traf,
|
||||||
|
normalize_presentation_timestamp,
|
||||||
|
scoped_ptr<EncryptionKey>(new EncryptionKey()),
|
||||||
|
clear_time,
|
||||||
|
nalu_length_size),
|
||||||
|
moof_(moof),
|
||||||
|
encryption_key_source_(encryption_key_source),
|
||||||
|
track_type_(track_type),
|
||||||
|
crypto_period_duration_(crypto_period_duration),
|
||||||
|
prev_crypto_period_index_(-1) {
|
||||||
|
DCHECK(moof);
|
||||||
|
DCHECK(encryption_key_source);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyRotationFragmenter::~KeyRotationFragmenter() {}
|
||||||
|
|
||||||
|
Status KeyRotationFragmenter::PrepareFragmentForEncryption() {
|
||||||
|
traf()->auxiliary_size.sample_info_sizes.clear();
|
||||||
|
traf()->auxiliary_offset.offsets.clear();
|
||||||
|
|
||||||
|
size_t current_crypto_period_index =
|
||||||
|
traf()->decode_time.decode_time / crypto_period_duration_;
|
||||||
|
if (current_crypto_period_index != prev_crypto_period_index_) {
|
||||||
|
scoped_ptr<EncryptionKey> encryption_key(new EncryptionKey());
|
||||||
|
Status status = encryption_key_source_->GetCryptoPeriodKey(
|
||||||
|
current_crypto_period_index, track_type_, encryption_key.get());
|
||||||
|
if (!status.ok())
|
||||||
|
return status;
|
||||||
|
set_encryption_key(encryption_key.Pass());
|
||||||
|
|
||||||
|
status = CreateEncryptor();
|
||||||
|
if (!status.ok())
|
||||||
|
return status;
|
||||||
|
prev_crypto_period_index_ = current_crypto_period_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncryptionKey* encryption_key = Fragmenter::encryption_key();
|
||||||
|
DCHECK(encryption_key);
|
||||||
|
AesCtrEncryptor* encryptor = Fragmenter::encryptor();
|
||||||
|
DCHECK(encryptor);
|
||||||
|
|
||||||
|
// We support key rotation in fragment boundary only, i.e. there is at most
|
||||||
|
// one key for a single fragment. So we should have only one entry in
|
||||||
|
// Sample Group Description box and one entry in Sample to Group box.
|
||||||
|
// Fill in Sample Group Description box information.
|
||||||
|
traf()->sample_group_description.grouping_type = FOURCC_SEIG;
|
||||||
|
traf()->sample_group_description.entries.resize(1);
|
||||||
|
traf()->sample_group_description.entries[0].is_encrypted = true;
|
||||||
|
traf()->sample_group_description.entries[0].iv_size = encryptor->iv().size();
|
||||||
|
traf()->sample_group_description.entries[0].key_id = encryption_key->key_id;
|
||||||
|
|
||||||
|
// Fill in Sample to Group box information.
|
||||||
|
traf()->sample_to_group.grouping_type = FOURCC_SEIG;
|
||||||
|
traf()->sample_to_group.entries.resize(1);
|
||||||
|
// sample_count is adjusted in |FinalizeFragment| later.
|
||||||
|
traf()->sample_to_group.entries[0].group_description_index =
|
||||||
|
SampleToGroupEntry::kTrackFragmentGroupDescriptionIndexBase + 1;
|
||||||
|
|
||||||
|
// We need one and only one pssh box.
|
||||||
|
if (moof_->pssh.empty())
|
||||||
|
moof_->pssh.resize(1);
|
||||||
|
moof_->pssh[0].raw_box = encryption_key->pssh;
|
||||||
|
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyRotationFragmenter::FinalizeFragmentForEncryption() {
|
||||||
|
Fragmenter::FinalizeFragmentForEncryption();
|
||||||
|
DCHECK_EQ(1u, traf()->sample_to_group.entries.size());
|
||||||
|
traf()->sample_to_group.entries[0].sample_count =
|
||||||
|
traf()->auxiliary_size.sample_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace mp4
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#ifndef MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_
|
||||||
|
#define MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_
|
||||||
|
|
||||||
|
#include "media/base/encryption_key_source.h"
|
||||||
|
#include "media/formats/mp4/fragmenter.h"
|
||||||
|
|
||||||
|
namespace media {
|
||||||
|
namespace mp4 {
|
||||||
|
|
||||||
|
class KeyRotationFragmenter : public Fragmenter {
|
||||||
|
public:
|
||||||
|
/// @param moof points to a MovieFragment box.
|
||||||
|
/// @param traf points to a TrackFragment box.
|
||||||
|
/// @param normalize_presentation_timestamp defines whether PTS should be
|
||||||
|
/// normalized to start from zero.
|
||||||
|
/// @param encryption_key_source points to the source which generates
|
||||||
|
/// encryption keys.
|
||||||
|
/// @param track_type indicates whether SD key or HD key should be used to
|
||||||
|
/// encrypt the video content.
|
||||||
|
/// @param crypto_period_duration specifies crypto period duration in units
|
||||||
|
/// of the current track's timescale.
|
||||||
|
/// @param clear_time specifies clear lead duration in units of the current
|
||||||
|
/// track's timescale.
|
||||||
|
/// @param nalu_length_size NAL unit length size, in bytes, for subsample
|
||||||
|
/// encryption.
|
||||||
|
KeyRotationFragmenter(MovieFragment* moof,
|
||||||
|
TrackFragment* traf,
|
||||||
|
bool normalize_presentation_timestamp,
|
||||||
|
EncryptionKeySource* encryption_key_source,
|
||||||
|
EncryptionKeySource::TrackType track_type,
|
||||||
|
int64 crypto_period_duration,
|
||||||
|
int64 clear_time,
|
||||||
|
uint8 nalu_length_size);
|
||||||
|
virtual ~KeyRotationFragmenter();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @name Fragmenter implementation overrides.
|
||||||
|
/// @{
|
||||||
|
virtual Status PrepareFragmentForEncryption() OVERRIDE;
|
||||||
|
virtual void FinalizeFragmentForEncryption() OVERRIDE;
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MovieFragment* moof_;
|
||||||
|
|
||||||
|
EncryptionKeySource* encryption_key_source_;
|
||||||
|
EncryptionKeySource::TrackType track_type_;
|
||||||
|
const int64 crypto_period_duration_;
|
||||||
|
size_t prev_crypto_period_index_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(KeyRotationFragmenter);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mp4
|
||||||
|
} // namespace media
|
||||||
|
|
||||||
|
#endif // MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_
|
|
@ -42,6 +42,8 @@
|
||||||
'fourccs.h',
|
'fourccs.h',
|
||||||
'fragmenter.cc',
|
'fragmenter.cc',
|
||||||
'fragmenter.h',
|
'fragmenter.h',
|
||||||
|
'key_rotation_fragmenter.cc',
|
||||||
|
'key_rotation_fragmenter.h',
|
||||||
'mp4_media_parser.cc',
|
'mp4_media_parser.cc',
|
||||||
'mp4_media_parser.h',
|
'mp4_media_parser.h',
|
||||||
'mp4_muxer.cc',
|
'mp4_muxer.cc',
|
||||||
|
|
|
@ -102,7 +102,8 @@ Status MP4Muxer::Initialize() {
|
||||||
segmenter_->Initialize(streams(),
|
segmenter_->Initialize(streams(),
|
||||||
encryption_key_source(),
|
encryption_key_source(),
|
||||||
track_type(),
|
track_type(),
|
||||||
clear_lead_in_seconds());
|
clear_lead_in_seconds(),
|
||||||
|
crypto_period_duration_in_seconds());
|
||||||
|
|
||||||
if (!segmenter_initialized.ok())
|
if (!segmenter_initialized.ok())
|
||||||
return segmenter_initialized;
|
return segmenter_initialized;
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "base/stl_util.h"
|
#include "base/stl_util.h"
|
||||||
#include "media/base/aes_encryptor.h"
|
|
||||||
#include "media/base/buffer_writer.h"
|
#include "media/base/buffer_writer.h"
|
||||||
#include "media/base/encryption_key_source.h"
|
#include "media/base/encryption_key_source.h"
|
||||||
#include "media/base/media_sample.h"
|
#include "media/base/media_sample.h"
|
||||||
|
@ -17,7 +16,7 @@
|
||||||
#include "media/base/muxer_options.h"
|
#include "media/base/muxer_options.h"
|
||||||
#include "media/base/video_stream_info.h"
|
#include "media/base/video_stream_info.h"
|
||||||
#include "media/formats/mp4/box_definitions.h"
|
#include "media/formats/mp4/box_definitions.h"
|
||||||
#include "media/formats/mp4/fragmenter.h"
|
#include "media/formats/mp4/key_rotation_fragmenter.h"
|
||||||
|
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
@ -26,6 +25,7 @@ namespace {
|
||||||
|
|
||||||
// Generate 64bit IV by default.
|
// Generate 64bit IV by default.
|
||||||
const size_t kDefaultIvSize = 8u;
|
const size_t kDefaultIvSize = 8u;
|
||||||
|
const size_t kCencKeyIdSize = 16u;
|
||||||
|
|
||||||
// The version of cenc implemented here. CENC 4.
|
// The version of cenc implemented here. CENC 4.
|
||||||
const int kCencSchemeVersion = 0x00010000;
|
const int kCencSchemeVersion = 0x00010000;
|
||||||
|
@ -34,21 +34,6 @@ uint64 Rescale(uint64 time_in_old_scale, uint32 old_scale, uint32 new_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;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoped_ptr<AesCtrEncryptor> CreateEncryptor(
|
|
||||||
const EncryptionKey& encryption_key) {
|
|
||||||
scoped_ptr<AesCtrEncryptor> encryptor(new AesCtrEncryptor());
|
|
||||||
const bool initialized =
|
|
||||||
encryption_key.iv.empty()
|
|
||||||
? encryptor->InitializeWithRandomIv(encryption_key.key,
|
|
||||||
kDefaultIvSize)
|
|
||||||
: encryptor->InitializeWithIv(encryption_key.key, encryption_key.iv);
|
|
||||||
if (!initialized) {
|
|
||||||
LOG(ERROR) << "Failed to the initialize encryptor.";
|
|
||||||
return scoped_ptr<AesCtrEncryptor>();
|
|
||||||
}
|
|
||||||
return encryptor.Pass();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GenerateSinf(const EncryptionKey& encryption_key,
|
void GenerateSinf(const EncryptionKey& encryption_key,
|
||||||
FourCC old_type,
|
FourCC old_type,
|
||||||
ProtectionSchemeInfo* sinf) {
|
ProtectionSchemeInfo* sinf) {
|
||||||
|
@ -91,6 +76,16 @@ void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GenerateEncryptedSampleEntryForKeyRotation(
|
||||||
|
double clear_lead_in_seconds,
|
||||||
|
SampleDescription* description) {
|
||||||
|
// Fill encrypted sample entry with default key.
|
||||||
|
EncryptionKey encryption_key;
|
||||||
|
encryption_key.key_id.assign(kCencKeyIdSize, 0);
|
||||||
|
GenerateEncryptedSampleEntry(
|
||||||
|
encryption_key, clear_lead_in_seconds, description);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Segmenter::Segmenter(const MuxerOptions& options,
|
Segmenter::Segmenter(const MuxerOptions& options,
|
||||||
|
@ -110,7 +105,8 @@ Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
|
||||||
Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
EncryptionKeySource* encryption_key_source,
|
EncryptionKeySource* encryption_key_source,
|
||||||
EncryptionKeySource::TrackType track_type,
|
EncryptionKeySource::TrackType track_type,
|
||||||
double clear_lead_in_seconds) {
|
double clear_lead_in_seconds,
|
||||||
|
double crypto_period_duration_in_seconds) {
|
||||||
DCHECK_LT(0u, streams.size());
|
DCHECK_LT(0u, streams.size());
|
||||||
moof_->header.sequence_number = 0;
|
moof_->header.sequence_number = 0;
|
||||||
|
|
||||||
|
@ -129,41 +125,59 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
if (sidx_->reference_id == 0)
|
if (sidx_->reference_id == 0)
|
||||||
sidx_->reference_id = i + 1;
|
sidx_->reference_id = i + 1;
|
||||||
}
|
}
|
||||||
scoped_ptr<AesCtrEncryptor> encryptor;
|
|
||||||
if (encryption_key_source) {
|
|
||||||
SampleDescription& description =
|
|
||||||
moov_->tracks[i].media.information.sample_table.description;
|
|
||||||
|
|
||||||
DCHECK(track_type == EncryptionKeySource::TRACK_TYPE_SD ||
|
if (!encryption_key_source) {
|
||||||
track_type == EncryptionKeySource::TRACK_TYPE_HD);
|
fragmenters_[i] = new Fragmenter(
|
||||||
|
&moof_->tracks[i], options_.normalize_presentation_timestamp);
|
||||||
EncryptionKey encryption_key;
|
continue;
|
||||||
Status status = encryption_key_source->GetKey(
|
|
||||||
description.type == kAudio ? EncryptionKeySource::TRACK_TYPE_AUDIO
|
|
||||||
: track_type,
|
|
||||||
&encryption_key);
|
|
||||||
if (!status.ok())
|
|
||||||
return status;
|
|
||||||
|
|
||||||
GenerateEncryptedSampleEntry(
|
|
||||||
encryption_key, clear_lead_in_seconds, &description);
|
|
||||||
|
|
||||||
// We need one and only one pssh box.
|
|
||||||
if (moov_->pssh.empty()) {
|
|
||||||
moov_->pssh.resize(1);
|
|
||||||
moov_->pssh[0].raw_box = encryption_key.pssh;
|
|
||||||
}
|
|
||||||
|
|
||||||
encryptor = CreateEncryptor(encryption_key);
|
|
||||||
if (!encryptor)
|
|
||||||
return Status(error::MUXER_FAILURE, "Failed to create the encryptor.");
|
|
||||||
}
|
}
|
||||||
fragmenters_[i] = new Fragmenter(
|
|
||||||
&moof_->tracks[i],
|
DCHECK(track_type == EncryptionKeySource::TRACK_TYPE_SD ||
|
||||||
encryptor.Pass(),
|
track_type == EncryptionKeySource::TRACK_TYPE_HD);
|
||||||
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
SampleDescription& description =
|
||||||
nalu_length_size,
|
moov_->tracks[i].media.information.sample_table.description;
|
||||||
options_.normalize_presentation_timestamp);
|
EncryptionKeySource::TrackType cur_track_type =
|
||||||
|
description.type == kAudio ? EncryptionKeySource::TRACK_TYPE_AUDIO
|
||||||
|
: track_type;
|
||||||
|
|
||||||
|
const bool key_rotation_enabled = crypto_period_duration_in_seconds != 0;
|
||||||
|
if (key_rotation_enabled) {
|
||||||
|
GenerateEncryptedSampleEntryForKeyRotation(clear_lead_in_seconds,
|
||||||
|
&description);
|
||||||
|
|
||||||
|
fragmenters_[i] = new KeyRotationFragmenter(
|
||||||
|
moof_.get(),
|
||||||
|
&moof_->tracks[i],
|
||||||
|
options_.normalize_presentation_timestamp,
|
||||||
|
encryption_key_source,
|
||||||
|
cur_track_type,
|
||||||
|
crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
|
||||||
|
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
||||||
|
nalu_length_size);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
scoped_ptr<EncryptionKey> encryption_key(new EncryptionKey());
|
||||||
|
Status status =
|
||||||
|
encryption_key_source->GetKey(cur_track_type, encryption_key.get());
|
||||||
|
if (!status.ok())
|
||||||
|
return status;
|
||||||
|
|
||||||
|
GenerateEncryptedSampleEntry(
|
||||||
|
*encryption_key, clear_lead_in_seconds, &description);
|
||||||
|
|
||||||
|
// We need one and only one pssh box.
|
||||||
|
if (moov_->pssh.empty()) {
|
||||||
|
moov_->pssh.resize(1);
|
||||||
|
moov_->pssh[0].raw_box = encryption_key->pssh;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragmenters_[i] =
|
||||||
|
new Fragmenter(&moof_->tracks[i],
|
||||||
|
options_.normalize_presentation_timestamp,
|
||||||
|
encryption_key.Pass(),
|
||||||
|
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
||||||
|
nalu_length_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose the first stream if there is no VIDEO.
|
// Choose the first stream if there is no VIDEO.
|
||||||
|
@ -289,13 +303,17 @@ uint32 Segmenter::GetReferenceStreamId() {
|
||||||
return sidx_->reference_id - 1;
|
return sidx_->reference_id - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Segmenter::InitializeFragments() {
|
Status Segmenter::InitializeFragments() {
|
||||||
++moof_->header.sequence_number;
|
++moof_->header.sequence_number;
|
||||||
|
Status status;
|
||||||
for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
|
for (std::vector<Fragmenter*>::iterator it = fragmenters_.begin();
|
||||||
it != fragmenters_.end();
|
it != fragmenters_.end();
|
||||||
++it) {
|
++it) {
|
||||||
(*it)->InitializeFragment();
|
status = (*it)->InitializeFragment();
|
||||||
|
if (!status.ok())
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Segmenter::FinalizeFragment(Fragmenter* fragmenter) {
|
Status Segmenter::FinalizeFragment(Fragmenter* fragmenter) {
|
||||||
|
|
|
@ -49,17 +49,20 @@ class Segmenter {
|
||||||
/// Initialize the segmenter.
|
/// Initialize the segmenter.
|
||||||
/// Calling other public methods of this class without this method returning
|
/// Calling other public methods of this class without this method returning
|
||||||
/// Status::OK results in an undefined behavior.
|
/// Status::OK results in an undefined behavior.
|
||||||
|
/// @param streams contains the vector of MediaStreams to be segmented.
|
||||||
/// @param encryption_key_source points to the key source which contains
|
/// @param encryption_key_source points to the key source which contains
|
||||||
/// the encryption keys. It can be NULL to indicate that no encryption
|
/// the encryption keys. It can be NULL to indicate that no encryption
|
||||||
/// is required.
|
/// is required.
|
||||||
/// @param track_type indicates whether SD key or HD key should be used to
|
/// @param track_type indicates whether SD key or HD key should be used to
|
||||||
/// encrypt the video content.
|
/// encrypt the video content.
|
||||||
/// @param clear_time specifies clear lead duration in seconds.
|
/// @param clear_time specifies clear lead duration in seconds.
|
||||||
|
/// @param crypto_period_duration specifies crypto period duration in seconds.
|
||||||
/// @return OK on success, an error status otherwise.
|
/// @return OK on success, an error status otherwise.
|
||||||
Status Initialize(const std::vector<MediaStream*>& streams,
|
Status Initialize(const std::vector<MediaStream*>& streams,
|
||||||
EncryptionKeySource* encryption_key_source,
|
EncryptionKeySource* encryption_key_source,
|
||||||
EncryptionKeySource::TrackType track_type,
|
EncryptionKeySource::TrackType track_type,
|
||||||
double clear_lead_in_seconds);
|
double clear_lead_in_seconds,
|
||||||
|
double crypto_period_duration_in_seconds);
|
||||||
|
|
||||||
/// Finalize the segmenter.
|
/// Finalize the segmenter.
|
||||||
/// @return OK on success, an error status otherwise.
|
/// @return OK on success, an error status otherwise.
|
||||||
|
@ -102,7 +105,7 @@ class Segmenter {
|
||||||
Status FinalizeSegment();
|
Status FinalizeSegment();
|
||||||
uint32 GetReferenceStreamId();
|
uint32 GetReferenceStreamId();
|
||||||
|
|
||||||
void InitializeFragments();
|
Status InitializeFragments();
|
||||||
Status FinalizeFragment(Fragmenter* fragment);
|
Status FinalizeFragment(Fragmenter* fragment);
|
||||||
|
|
||||||
const MuxerOptions& options_;
|
const MuxerOptions& options_;
|
||||||
|
|
|
@ -54,6 +54,7 @@ const char kPsshHex[] =
|
||||||
"758382cd1a0d7769646576696e655f746573742211544553545f"
|
"758382cd1a0d7769646576696e655f746573742211544553545f"
|
||||||
"434f4e54454e545f49445f312a025344";
|
"434f4e54454e545f49445f312a025344";
|
||||||
const double kClearLeadInSeconds = 1.5;
|
const double kClearLeadInSeconds = 1.5;
|
||||||
|
const double kCryptoDurationInSeconds = 0; // Key rotation is disabled.
|
||||||
|
|
||||||
MediaStream* FindFirstStreamOfType(const std::vector<MediaStream*>& streams,
|
MediaStream* FindFirstStreamOfType(const std::vector<MediaStream*>& streams,
|
||||||
StreamType stream_type) {
|
StreamType stream_type) {
|
||||||
|
@ -164,7 +165,8 @@ void PackagerTestBasic::Remux(const std::string& input,
|
||||||
if (enable_encryption) {
|
if (enable_encryption) {
|
||||||
muxer_video->SetEncryptionKeySource(encryption_key_source.get(),
|
muxer_video->SetEncryptionKeySource(encryption_key_source.get(),
|
||||||
EncryptionKeySource::TRACK_TYPE_SD,
|
EncryptionKeySource::TRACK_TYPE_SD,
|
||||||
kClearLeadInSeconds);
|
kClearLeadInSeconds,
|
||||||
|
kCryptoDurationInSeconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +181,8 @@ void PackagerTestBasic::Remux(const std::string& input,
|
||||||
if (enable_encryption) {
|
if (enable_encryption) {
|
||||||
muxer_audio->SetEncryptionKeySource(encryption_key_source.get(),
|
muxer_audio->SetEncryptionKeySource(encryption_key_source.get(),
|
||||||
EncryptionKeySource::TRACK_TYPE_SD,
|
EncryptionKeySource::TRACK_TYPE_SD,
|
||||||
kClearLeadInSeconds);
|
kClearLeadInSeconds,
|
||||||
|
kCryptoDurationInSeconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue