Implement EncryptingFragmenter from Fragmenter

Extract encryption related handlings from Fragmenter and move into new
class EncrpytingFragmenter.

Change-Id: Id42493cd321fd7e306f8ce522c7ff04990965256
This commit is contained in:
KongQun Yang 2014-05-08 13:53:08 -07:00
parent 3f0454eced
commit b25834a910
8 changed files with 316 additions and 246 deletions

View File

@ -0,0 +1,164 @@
// 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/encrypting_fragmenter.h"
#include "media/base/aes_encryptor.h"
#include "media/base/buffer_reader.h"
#include "media/base/encryption_key_source.h"
#include "media/base/media_sample.h"
#include "media/formats/mp4/box_definitions.h"
#include "media/formats/mp4/cenc.h"
namespace {
// Generate 64bit IV by default.
const size_t kDefaultIvSize = 8u;
} // namespace
namespace media {
namespace mp4 {
EncryptingFragmenter::EncryptingFragmenter(
TrackFragment* traf,
bool normalize_presentation_timestamp,
scoped_ptr<EncryptionKey> encryption_key,
int64 clear_time,
uint8 nalu_length_size)
: Fragmenter(traf, normalize_presentation_timestamp),
encryption_key_(encryption_key.Pass()),
nalu_length_size_(nalu_length_size),
clear_time_(clear_time) {
DCHECK(encryption_key_);
}
EncryptingFragmenter::~EncryptingFragmenter() {}
Status EncryptingFragmenter::AddSample(scoped_refptr<MediaSample> sample) {
DCHECK(sample);
if (encryptor_) {
Status status = EncryptSample(sample);
if (!status.ok())
return status;
}
return Fragmenter::AddSample(sample);
}
Status EncryptingFragmenter::InitializeFragment() {
Status status = Fragmenter::InitializeFragment();
if (!status.ok())
return status;
// Enable encryption for this fragment if |clear_time_| becomes non-positive.
if (clear_time_ <= 0)
return PrepareFragmentForEncryption();
// Otherwise, this fragment should be in clear text.
// At most two sample description entries, an encrypted entry and a clear
// entry, are generated. 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 EncryptingFragmenter::FinalizeFragment() {
if (encryptor_) {
DCHECK_LE(clear_time_, 0);
FinalizeFragmentForEncryption();
} else {
DCHECK_GT(clear_time_, 0);
clear_time_ -= fragment_duration();
}
Fragmenter::FinalizeFragment();
}
Status EncryptingFragmenter::PrepareFragmentForEncryption() {
traf()->auxiliary_size.sample_info_sizes.clear();
traf()->auxiliary_offset.offsets.clear();
return encryptor_ ? Status::OK : CreateEncryptor();
}
void EncryptingFragmenter::FinalizeFragmentForEncryption() {
// The offset will be adjusted in Segmenter after knowing 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 EncryptingFragmenter::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 EncryptingFragmenter::EncryptBytes(uint8* data, uint32 size) {
DCHECK(encryptor_);
CHECK(encryptor_->Encrypt(data, size, data));
}
Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
DCHECK(encryptor_);
FrameCENCInfo cenc_info(encryptor_->iv());
uint8* data = sample->writable_data();
if (!IsSubsampleEncryptionRequired()) {
EncryptBytes(data, sample->data_size());
} else {
BufferReader reader(data, sample->data_size());
while (reader.HasBytes(1)) {
uint64 nalu_length;
if (!reader.ReadNBytesInto8(&nalu_length, nalu_length_size_))
return Status(error::MUXER_FAILURE, "Fail to read nalu_length.");
SubsampleEntry subsample;
subsample.clear_bytes = nalu_length_size_ + 1;
subsample.cypher_bytes = nalu_length - 1;
if (!reader.SkipBytes(nalu_length)) {
return Status(error::MUXER_FAILURE,
"Sample size does not match nalu_length.");
}
EncryptBytes(data + subsample.clear_bytes, subsample.cypher_bytes);
cenc_info.AddSubsample(subsample);
data += nalu_length_size_ + nalu_length;
}
// The length of per-sample auxiliary datum, defined in CENC ch. 7.
traf()->auxiliary_size.sample_info_sizes.push_back(cenc_info.ComputeSize());
}
cenc_info.Write(aux_data());
encryptor_->UpdateIv();
return Status::OK;
}
} // namespace mp4
} // namespace media

View File

@ -0,0 +1,85 @@
// 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_ENCRYPTING_FRAGMENTER_H_
#define MEDIA_FORMATS_MP4_ENCRYPTING_FRAGMENTER_H_
#include "media/formats/mp4/fragmenter.h"
namespace media {
class AesCtrEncryptor;
struct EncryptionKey;
namespace mp4 {
/// EncryptingFragmenter generates MP4 fragments with sample encrypted.
class EncryptingFragmenter : public Fragmenter {
public:
/// @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
/// track's timescale.
/// @param nalu_length_size specifies the size of NAL unit length, in bytes,
/// for subsample encryption.
EncryptingFragmenter(TrackFragment* traf,
bool normalize_presentation_timestamp,
scoped_ptr<EncryptionKey> encryption_key,
int64 clear_time,
uint8 nalu_length_size);
virtual ~EncryptingFragmenter();
/// @name Fragmenter implementation overrides.
/// @{
virtual Status AddSample(scoped_refptr<MediaSample> sample) OVERRIDE;
virtual Status InitializeFragment() OVERRIDE;
virtual void FinalizeFragment() OVERRIDE;
/// @}
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();
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:
void EncryptBytes(uint8* data, uint32 size);
Status EncryptSample(scoped_refptr<MediaSample> sample);
// Should we enable subsample encryption?
bool IsSubsampleEncryptionRequired() { return nalu_length_size_ != 0; }
scoped_ptr<EncryptionKey> encryption_key_;
scoped_ptr<AesCtrEncryptor> encryptor_;
// If this stream contains AVC, subsample encryption specifies that the size
// and type of NAL units remain unencrypted. This field specifies the size of
// the size field. Can be 1, 2 or 4 bytes.
const uint8 nalu_length_size_;
int64 clear_time_;
DISALLOW_COPY_AND_ASSIGN(EncryptingFragmenter);
};
} // namespace mp4
} // namespace media
#endif // MEDIA_FORMATS_MP4_ENCRYPTING_FRAGMENTER_H_

View File

@ -6,50 +6,20 @@
#include "media/formats/mp4/fragmenter.h" #include "media/formats/mp4/fragmenter.h"
#include "media/base/aes_encryptor.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"
namespace media { namespace media {
namespace mp4 { 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,
// then |entries| is cleared and the value is assigned to |default_value|;
// otherwise it is a NOP. Return true if the table is optimized.
template <typename T>
bool OptimizeSampleEntries(std::vector<T>* entries, T* default_value) {
DCHECK(entries);
DCHECK(default_value);
DCHECK(!entries->empty());
typename std::vector<T>::const_iterator it = entries->begin();
T value = *it;
for (; it < entries->end(); ++it)
if (value != *it)
return false;
// Clear |entries| if it contains only one value.
entries->clear();
*default_value = value;
return true;
}
} // namespace } // namespace
Fragmenter::Fragmenter(TrackFragment* traf, Fragmenter::Fragmenter(TrackFragment* traf,
bool normalize_presentation_timestamp) bool normalize_presentation_timestamp)
: traf_(traf), : traf_(traf),
nalu_length_size_(0),
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),
@ -59,47 +29,21 @@ Fragmenter::Fragmenter(TrackFragment* traf,
DCHECK(traf); 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) {
DCHECK(sample);
CHECK_GT(sample->duration(), 0); CHECK_GT(sample->duration(), 0);
if (encryptor_) {
Status status = EncryptSample(sample);
if (!status.ok())
return status;
}
// Fill in sample parameters. It will be optimized later. // Fill in sample parameters. It will be optimized later.
traf_->runs[0].sample_sizes.push_back(sample->data_size()); traf_->runs[0].sample_sizes.push_back(sample->data_size());
traf_->runs[0].sample_durations.push_back(sample->duration()); traf_->runs[0].sample_durations.push_back(sample->duration());
traf_->runs[0].sample_flags.push_back( traf_->runs[0].sample_flags.push_back(
sample->is_key_frame() ? 0 : TrackFragmentHeader::kNonKeySampleMask); sample->is_key_frame() ? 0 : TrackFragmentHeader::kNonKeySampleMask);
traf_->runs[0] traf_->runs[0].sample_composition_time_offsets.push_back(sample->pts() -
.sample_composition_time_offsets.push_back(sample->pts() - sample->dts()); sample->dts());
if (sample->pts() != sample->dts()) { if (sample->pts() != sample->dts())
traf_->runs[0].flags |= traf_->runs[0].flags |= TrackFragmentRun::kSampleCompTimeOffsetsPresentMask;
TrackFragmentRun::kSampleCompTimeOffsetsPresentMask;
}
data_->AppendArray(sample->data(), sample->data_size()); data_->AppendArray(sample->data(), sample->data_size());
fragment_duration_ += sample->duration(); fragment_duration_ += sample->duration();
@ -114,7 +58,7 @@ Status Fragmenter::AddSample(scoped_refptr<MediaSample> sample) {
presentation_start_time_ = pts; presentation_start_time_ = pts;
pts = 0; pts = 0;
} else { } else {
// Can we safely assume the first sample in the media has the earliest // Is it safe to assume the first sample in the media has the earliest
// presentation timestamp? // presentation timestamp?
DCHECK_GE(pts, presentation_start_time_); DCHECK_GE(pts, presentation_start_time_);
pts -= presentation_start_time_; pts -= presentation_start_time_;
@ -145,30 +89,10 @@ Status Fragmenter::InitializeFragment() {
first_sap_time_ = kInvalidTime; first_sap_time_ = kInvalidTime;
data_.reset(new BufferWriter()); data_.reset(new BufferWriter());
aux_data_.reset(new BufferWriter()); aux_data_.reset(new BufferWriter());
if (!encryption_key_)
return Status::OK;
// 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; return Status::OK;
} }
void Fragmenter::FinalizeFragment() { void Fragmenter::FinalizeFragment() {
if (encryptor_)
FinalizeFragmentForEncryption();
// 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();
if (OptimizeSampleEntries(&traf_->runs[0].sample_durations, if (OptimizeSampleEntries(&traf_->runs[0].sample_durations,
@ -209,88 +133,6 @@ 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) {
DCHECK(encryptor_);
CHECK(encryptor_->Encrypt(data, size, data));
}
Status Fragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
DCHECK(encryptor_);
FrameCENCInfo cenc_info(encryptor_->iv());
uint8* data = sample->writable_data();
if (!IsSubsampleEncryptionRequired()) {
EncryptBytes(data, sample->data_size());
} else {
BufferReader reader(data, sample->data_size());
while (reader.HasBytes(1)) {
uint64 nalu_length;
if (!reader.ReadNBytesInto8(&nalu_length, nalu_length_size_))
return Status(error::MUXER_FAILURE, "Fail to read nalu_length.");
SubsampleEntry subsample;
subsample.clear_bytes = nalu_length_size_ + 1;
subsample.cypher_bytes = nalu_length - 1;
if (!reader.SkipBytes(nalu_length)) {
return Status(error::MUXER_FAILURE,
"Sample size does not match nalu_length.");
}
EncryptBytes(data + subsample.clear_bytes, subsample.cypher_bytes);
cenc_info.AddSubsample(subsample);
data += nalu_length_size_ + nalu_length;
}
// The length of per-sample auxiliary datum, defined in CENC ch. 7.
traf_->auxiliary_size.sample_info_sizes.push_back(cenc_info.ComputeSize());
}
cenc_info.Write(aux_data_.get());
encryptor_->UpdateIv();
return Status::OK;
}
bool Fragmenter::StartsWithSAP() { bool Fragmenter::StartsWithSAP() {
DCHECK(!traf_->runs.empty()); DCHECK(!traf_->runs.empty());
uint32 start_sample_flag; uint32 start_sample_flag;

View File

@ -15,54 +15,36 @@
namespace media { namespace media {
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;
/// Fragmenter is responsible for the generation of MP4 fragments, i.e. traf /// Fragmenter is responsible for the generation of MP4 fragments, i.e. 'traf'
/// box and corresponding mdat box. The samples are also encrypted if encryption /// box and corresponding 'mdat' box.
/// is requested.
class Fragmenter { class Fragmenter {
public: public:
/// @param traf points to a TrackFragment box. /// @param traf points to a TrackFragment box.
/// @param normalize_presentation_timestamp defines whether PTS should be /// @param normalize_presentation_timestamp defines whether PTS should be
/// normalized to start from zero. /// normalized to start from zero.
Fragmenter(TrackFragment* traf, Fragmenter(TrackFragment* traf, bool normalize_presentation_timestamp);
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
/// track's timescale.
/// @param nalu_length_size NAL unit length size, in bytes, for subsample
/// encryption.
Fragmenter(TrackFragment* traf,
bool normalize_presentation_timestamp,
scoped_ptr<EncryptionKey> encryption_key,
int64 clear_time,
uint8 nalu_length_size);
virtual ~Fragmenter(); virtual ~Fragmenter();
/// Add a sample to the fragmenter. /// Add a sample to the fragmenter.
Status AddSample(scoped_refptr<MediaSample> sample); /// @param sample points to the sample to be added.
/// @return OK on success, an error status otherwise.
virtual Status AddSample(scoped_refptr<MediaSample> sample);
/// Initialize the fragment with default data. /// Initialize the fragment with default data.
/// @return OK on success, an error status otherwise. /// @return OK on success, an error status otherwise.
Status InitializeFragment(); virtual Status InitializeFragment();
/// Finalize and optimize the fragment. /// Finalize and optimize the fragment.
void FinalizeFragment(); virtual void FinalizeFragment();
/// Fill @a reference with current fragment information. /// Fill @a reference with current fragment information.
void GenerateSegmentReference(SegmentReference* reference); void GenerateSegmentReference(SegmentReference* reference);
@ -77,45 +59,19 @@ class Fragmenter {
BufferWriter* aux_data() { return aux_data_.get(); } BufferWriter* aux_data() { return aux_data_.get(); }
protected: 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_; } 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) { /// Optimize sample entries table. If all values in @a entries are identical,
encryption_key_ = encryption_key.Pass(); /// then @a entries is cleared and the value is assigned to @a default_value;
} /// otherwise it is a NOP. Return true if the table is optimized.
template <typename T>
bool OptimizeSampleEntries(std::vector<T>* entries, T* default_value);
private: private:
void EncryptBytes(uint8* data, uint32 size);
Status EncryptSample(scoped_refptr<MediaSample> sample);
// Should we enable subsample encryption?
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_; TrackFragment* traf_;
scoped_ptr<EncryptionKey> encryption_key_;
scoped_ptr<AesCtrEncryptor> encryptor_;
// If this stream contains AVC, subsample encryption specifies that the size
// and type of NAL units remain unencrypted. This field specifies the size of
// the size field. Can be 1, 2 or 4 bytes.
const uint8 nalu_length_size_;
const int64 clear_time_;
bool fragment_finalized_; bool fragment_finalized_;
uint64 fragment_duration_; uint64 fragment_duration_;
bool normalize_presentation_timestamp_; bool normalize_presentation_timestamp_;
@ -128,6 +84,25 @@ class Fragmenter {
DISALLOW_COPY_AND_ASSIGN(Fragmenter); DISALLOW_COPY_AND_ASSIGN(Fragmenter);
}; };
template <typename T>
bool Fragmenter::OptimizeSampleEntries(std::vector<T>* entries,
T* default_value) {
DCHECK(entries);
DCHECK(default_value);
DCHECK(!entries->empty());
typename std::vector<T>::const_iterator it = entries->begin();
T value = *it;
for (; it < entries->end(); ++it)
if (value != *it)
return false;
// Clear |entries| if it contains only one value.
entries->clear();
*default_value = value;
return true;
}
} // namespace mp4 } // namespace mp4
} // namespace media } // namespace media

View File

@ -21,11 +21,11 @@ KeyRotationFragmenter::KeyRotationFragmenter(
int64 crypto_period_duration, int64 crypto_period_duration,
int64 clear_time, int64 clear_time,
uint8 nalu_length_size) uint8 nalu_length_size)
: Fragmenter(traf, : EncryptingFragmenter(traf,
normalize_presentation_timestamp, normalize_presentation_timestamp,
scoped_ptr<EncryptionKey>(new EncryptionKey()), scoped_ptr<EncryptionKey>(new EncryptionKey()),
clear_time, clear_time,
nalu_length_size), nalu_length_size),
moof_(moof), moof_(moof),
encryption_key_source_(encryption_key_source), encryption_key_source_(encryption_key_source),
track_type_(track_type), track_type_(track_type),
@ -57,29 +57,29 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption() {
prev_crypto_period_index_ = current_crypto_period_index; prev_crypto_period_index_ = current_crypto_period_index;
} }
EncryptionKey* encryption_key = Fragmenter::encryption_key(); EncryptionKey* encryption_key = EncryptingFragmenter::encryption_key();
DCHECK(encryption_key); DCHECK(encryption_key);
AesCtrEncryptor* encryptor = Fragmenter::encryptor(); AesCtrEncryptor* encryptor = EncryptingFragmenter::encryptor();
DCHECK(encryptor); DCHECK(encryptor);
// We support key rotation in fragment boundary only, i.e. there is at most // Key rotation happens in fragment boundary only in this implementation,
// one key for a single fragment. So we should have only one entry in // i.e. there is at most one key for the fragment. So there should be only
// Sample Group Description box and one entry in Sample to Group box. // one entry in SampleGroupDescription box and one entry in SampleToGroup box.
// Fill in Sample Group Description 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_encrypted = true; 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].iv_size = encryptor->iv().size();
traf()->sample_group_description.entries[0].key_id = encryption_key->key_id; traf()->sample_group_description.entries[0].key_id = encryption_key->key_id;
// Fill in Sample to Group box information. // Fill in SampleToGroup box information.
traf()->sample_to_group.grouping_type = FOURCC_SEIG; traf()->sample_to_group.grouping_type = FOURCC_SEIG;
traf()->sample_to_group.entries.resize(1); traf()->sample_to_group.entries.resize(1);
// sample_count is adjusted in |FinalizeFragment| later. // sample_count is adjusted in |FinalizeFragment| later.
traf()->sample_to_group.entries[0].group_description_index = traf()->sample_to_group.entries[0].group_description_index =
SampleToGroupEntry::kTrackFragmentGroupDescriptionIndexBase + 1; SampleToGroupEntry::kTrackFragmentGroupDescriptionIndexBase + 1;
// We need one and only one pssh box. // One and only one 'pssh' box is needed.
if (moof_->pssh.empty()) if (moof_->pssh.empty())
moof_->pssh.resize(1); moof_->pssh.resize(1);
moof_->pssh[0].raw_box = encryption_key->pssh; moof_->pssh[0].raw_box = encryption_key->pssh;
@ -88,7 +88,7 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption() {
} }
void KeyRotationFragmenter::FinalizeFragmentForEncryption() { void KeyRotationFragmenter::FinalizeFragmentForEncryption() {
Fragmenter::FinalizeFragmentForEncryption(); EncryptingFragmenter::FinalizeFragmentForEncryption();
DCHECK_EQ(1u, traf()->sample_to_group.entries.size()); DCHECK_EQ(1u, traf()->sample_to_group.entries.size());
traf()->sample_to_group.entries[0].sample_count = traf()->sample_to_group.entries[0].sample_count =
traf()->auxiliary_size.sample_count; traf()->auxiliary_size.sample_count;
@ -96,5 +96,3 @@ void KeyRotationFragmenter::FinalizeFragmentForEncryption() {
} // namespace media } // namespace media
} // namespace mp4 } // namespace mp4

View File

@ -8,12 +8,16 @@
#define MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_ #define MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_
#include "media/base/encryption_key_source.h" #include "media/base/encryption_key_source.h"
#include "media/formats/mp4/fragmenter.h" #include "media/formats/mp4/encrypting_fragmenter.h"
namespace media { namespace media {
namespace mp4 { namespace mp4 {
class KeyRotationFragmenter : public Fragmenter { struct MovieFragment;
/// KeyRotationFragmenter generates MP4 fragments with sample encrypted by
/// rotation keys.
class KeyRotationFragmenter : public EncryptingFragmenter {
public: public:
/// @param moof points to a MovieFragment box. /// @param moof points to a MovieFragment box.
/// @param traf points to a TrackFragment box. /// @param traf points to a TrackFragment box.

View File

@ -37,6 +37,8 @@
'composition_offset_iterator.h', 'composition_offset_iterator.h',
'decoding_time_iterator.cc', 'decoding_time_iterator.cc',
'decoding_time_iterator.h', 'decoding_time_iterator.h',
'encrypting_fragmenter.cc',
'encrypting_fragmenter.h',
'es_descriptor.cc', 'es_descriptor.cc',
'es_descriptor.h', 'es_descriptor.h',
'fourccs.h', 'fourccs.h',

View File

@ -180,18 +180,18 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
GenerateEncryptedSampleEntry( GenerateEncryptedSampleEntry(
*encryption_key, clear_lead_in_seconds, &description); *encryption_key, clear_lead_in_seconds, &description);
// We need one and only one pssh box. // One and only one pssh box is needed.
if (moov_->pssh.empty()) { if (moov_->pssh.empty()) {
moov_->pssh.resize(1); moov_->pssh.resize(1);
moov_->pssh[0].raw_box = encryption_key->pssh; moov_->pssh[0].raw_box = encryption_key->pssh;
} }
fragmenters_[i] = fragmenters_[i] = new EncryptingFragmenter(
new Fragmenter(&moof_->tracks[i], &moof_->tracks[i],
options_.normalize_presentation_timestamp, options_.normalize_presentation_timestamp,
encryption_key.Pass(), encryption_key.Pass(),
clear_lead_in_seconds * streams[i]->info()->time_scale(), clear_lead_in_seconds * streams[i]->info()->time_scale(),
nalu_length_size); nalu_length_size);
} }
// Choose the first stream if there is no VIDEO. // Choose the first stream if there is no VIDEO.