From b25834a910f98f78be1dbc7be26db1e9c203901e Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Thu, 8 May 2014 13:53:08 -0700 Subject: [PATCH] Implement EncryptingFragmenter from Fragmenter Extract encryption related handlings from Fragmenter and move into new class EncrpytingFragmenter. Change-Id: Id42493cd321fd7e306f8ce522c7ff04990965256 --- media/formats/mp4/encrypting_fragmenter.cc | 164 ++++++++++++++++++ media/formats/mp4/encrypting_fragmenter.h | 85 ++++++++++ media/formats/mp4/fragmenter.cc | 170 +------------------ media/formats/mp4/fragmenter.h | 89 ++++------ media/formats/mp4/key_rotation_fragmenter.cc | 30 ++-- media/formats/mp4/key_rotation_fragmenter.h | 8 +- media/formats/mp4/mp4.gyp | 2 + media/formats/mp4/segmenter.cc | 14 +- 8 files changed, 316 insertions(+), 246 deletions(-) create mode 100644 media/formats/mp4/encrypting_fragmenter.cc create mode 100644 media/formats/mp4/encrypting_fragmenter.h diff --git a/media/formats/mp4/encrypting_fragmenter.cc b/media/formats/mp4/encrypting_fragmenter.cc new file mode 100644 index 0000000000..8d1a5e5dfb --- /dev/null +++ b/media/formats/mp4/encrypting_fragmenter.cc @@ -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 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 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 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 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 diff --git a/media/formats/mp4/encrypting_fragmenter.h b/media/formats/mp4/encrypting_fragmenter.h new file mode 100644 index 0000000000..6bbc5bfa2b --- /dev/null +++ b/media/formats/mp4/encrypting_fragmenter.h @@ -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 encryption_key, + int64 clear_time, + uint8 nalu_length_size); + + virtual ~EncryptingFragmenter(); + + /// @name Fragmenter implementation overrides. + /// @{ + virtual Status AddSample(scoped_refptr 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 encryption_key) { + encryption_key_ = encryption_key.Pass(); + } + + private: + void EncryptBytes(uint8* data, uint32 size); + Status EncryptSample(scoped_refptr sample); + + // Should we enable subsample encryption? + bool IsSubsampleEncryptionRequired() { return nalu_length_size_ != 0; } + + scoped_ptr encryption_key_; + scoped_ptr 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_ diff --git a/media/formats/mp4/fragmenter.cc b/media/formats/mp4/fragmenter.cc index c659d9686b..880f741682 100644 --- a/media/formats/mp4/fragmenter.cc +++ b/media/formats/mp4/fragmenter.cc @@ -6,50 +6,20 @@ #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/encryption_key_source.h" #include "media/base/media_sample.h" #include "media/formats/mp4/box_definitions.h" -#include "media/formats/mp4/cenc.h" namespace media { namespace mp4 { namespace { -// Generate 64bit IV by default. -const size_t kDefaultIvSize = 8u; 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 -bool OptimizeSampleEntries(std::vector* entries, T* default_value) { - DCHECK(entries); - DCHECK(default_value); - DCHECK(!entries->empty()); - - typename std::vector::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 Fragmenter::Fragmenter(TrackFragment* traf, bool normalize_presentation_timestamp) : traf_(traf), - nalu_length_size_(0), - clear_time_(0), fragment_finalized_(false), fragment_duration_(0), normalize_presentation_timestamp_(normalize_presentation_timestamp), @@ -59,47 +29,21 @@ Fragmenter::Fragmenter(TrackFragment* traf, DCHECK(traf); } -Fragmenter::Fragmenter(TrackFragment* traf, - bool normalize_presentation_timestamp, - scoped_ptr 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() {} Status Fragmenter::AddSample(scoped_refptr sample) { + DCHECK(sample); 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. traf_->runs[0].sample_sizes.push_back(sample->data_size()); traf_->runs[0].sample_durations.push_back(sample->duration()); traf_->runs[0].sample_flags.push_back( sample->is_key_frame() ? 0 : TrackFragmentHeader::kNonKeySampleMask); - traf_->runs[0] - .sample_composition_time_offsets.push_back(sample->pts() - sample->dts()); - if (sample->pts() != sample->dts()) { - traf_->runs[0].flags |= - TrackFragmentRun::kSampleCompTimeOffsetsPresentMask; - } + traf_->runs[0].sample_composition_time_offsets.push_back(sample->pts() - + sample->dts()); + if (sample->pts() != sample->dts()) + traf_->runs[0].flags |= TrackFragmentRun::kSampleCompTimeOffsetsPresentMask; data_->AppendArray(sample->data(), sample->data_size()); fragment_duration_ += sample->duration(); @@ -114,7 +58,7 @@ Status Fragmenter::AddSample(scoped_refptr sample) { presentation_start_time_ = pts; pts = 0; } 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? DCHECK_GE(pts, presentation_start_time_); pts -= presentation_start_time_; @@ -145,30 +89,10 @@ Status Fragmenter::InitializeFragment() { first_sap_time_ = kInvalidTime; 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(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() { - if (encryptor_) - FinalizeFragmentForEncryption(); - // Optimize trun box. traf_->runs[0].sample_count = traf_->runs[0].sample_sizes.size(); if (OptimizeSampleEntries(&traf_->runs[0].sample_durations, @@ -209,88 +133,6 @@ void Fragmenter::GenerateSegmentReference(SegmentReference* reference) { 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 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 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() { DCHECK(!traf_->runs.empty()); uint32 start_sample_flag; diff --git a/media/formats/mp4/fragmenter.h b/media/formats/mp4/fragmenter.h index 417a99e88d..c1de2341c0 100644 --- a/media/formats/mp4/fragmenter.h +++ b/media/formats/mp4/fragmenter.h @@ -15,54 +15,36 @@ namespace media { -class AesCtrEncryptor; class BufferWriter; class MediaSample; -struct EncryptionKey; - namespace mp4 { -struct MovieFragment; struct SegmentReference; struct TrackFragment; -/// Fragmenter is responsible for the generation of MP4 fragments, i.e. traf -/// box and corresponding mdat box. The samples are also encrypted if encryption -/// is requested. +/// Fragmenter is responsible for the generation of MP4 fragments, i.e. 'traf' +/// box and corresponding 'mdat' box. class Fragmenter { public: /// @param traf points to a TrackFragment box. /// @param normalize_presentation_timestamp defines whether PTS should be /// 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 - /// 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 encryption_key, - int64 clear_time, - uint8 nalu_length_size); + Fragmenter(TrackFragment* traf, bool normalize_presentation_timestamp); virtual ~Fragmenter(); /// Add a sample to the fragmenter. - Status AddSample(scoped_refptr sample); + /// @param sample points to the sample to be added. + /// @return OK on success, an error status otherwise. + virtual Status AddSample(scoped_refptr sample); /// Initialize the fragment with default data. /// @return OK on success, an error status otherwise. - Status InitializeFragment(); + virtual Status InitializeFragment(); /// Finalize and optimize the fragment. - void FinalizeFragment(); + virtual void FinalizeFragment(); /// Fill @a reference with current fragment information. void GenerateSegmentReference(SegmentReference* reference); @@ -77,45 +59,19 @@ class Fragmenter { 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 encryption_key) { - encryption_key_ = encryption_key.Pass(); - } + /// Optimize sample entries table. If all values in @a entries are identical, + /// 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 + bool OptimizeSampleEntries(std::vector* entries, T* default_value); private: - void EncryptBytes(uint8* data, uint32 size); - Status EncryptSample(scoped_refptr sample); - - // Should we enable subsample encryption? - bool IsSubsampleEncryptionRequired() { return nalu_length_size_ != 0; } - // Check if the current fragment starts with SAP. bool StartsWithSAP(); TrackFragment* traf_; - - scoped_ptr encryption_key_; - scoped_ptr 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_; uint64 fragment_duration_; bool normalize_presentation_timestamp_; @@ -128,6 +84,25 @@ class Fragmenter { DISALLOW_COPY_AND_ASSIGN(Fragmenter); }; +template +bool Fragmenter::OptimizeSampleEntries(std::vector* entries, + T* default_value) { + DCHECK(entries); + DCHECK(default_value); + DCHECK(!entries->empty()); + + typename std::vector::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 media diff --git a/media/formats/mp4/key_rotation_fragmenter.cc b/media/formats/mp4/key_rotation_fragmenter.cc index 9a4a0fd784..5e8e3a2286 100644 --- a/media/formats/mp4/key_rotation_fragmenter.cc +++ b/media/formats/mp4/key_rotation_fragmenter.cc @@ -21,11 +21,11 @@ KeyRotationFragmenter::KeyRotationFragmenter( int64 crypto_period_duration, int64 clear_time, uint8 nalu_length_size) - : Fragmenter(traf, - normalize_presentation_timestamp, - scoped_ptr(new EncryptionKey()), - clear_time, - nalu_length_size), + : EncryptingFragmenter(traf, + normalize_presentation_timestamp, + scoped_ptr(new EncryptionKey()), + clear_time, + nalu_length_size), moof_(moof), encryption_key_source_(encryption_key_source), track_type_(track_type), @@ -57,29 +57,29 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption() { prev_crypto_period_index_ = current_crypto_period_index; } - EncryptionKey* encryption_key = Fragmenter::encryption_key(); + EncryptionKey* encryption_key = EncryptingFragmenter::encryption_key(); DCHECK(encryption_key); - AesCtrEncryptor* encryptor = Fragmenter::encryptor(); + AesCtrEncryptor* encryptor = EncryptingFragmenter::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. + // Key rotation happens in fragment boundary only in this implementation, + // i.e. there is at most one key for the fragment. So there should be only + // one entry in SampleGroupDescription box and one entry in SampleToGroup box. + // Fill in SampleGroupDescription box information. traf()->sample_group_description.grouping_type = FOURCC_SEIG; traf()->sample_group_description.entries.resize(1); traf()->sample_group_description.entries[0].is_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. + // Fill in SampleToGroup 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. + // One and only one 'pssh' box is needed. if (moof_->pssh.empty()) moof_->pssh.resize(1); moof_->pssh[0].raw_box = encryption_key->pssh; @@ -88,7 +88,7 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption() { } void KeyRotationFragmenter::FinalizeFragmentForEncryption() { - Fragmenter::FinalizeFragmentForEncryption(); + EncryptingFragmenter::FinalizeFragmentForEncryption(); DCHECK_EQ(1u, traf()->sample_to_group.entries.size()); traf()->sample_to_group.entries[0].sample_count = traf()->auxiliary_size.sample_count; @@ -96,5 +96,3 @@ void KeyRotationFragmenter::FinalizeFragmentForEncryption() { } // namespace media } // namespace mp4 - - diff --git a/media/formats/mp4/key_rotation_fragmenter.h b/media/formats/mp4/key_rotation_fragmenter.h index 379751f18b..0d969d4fdf 100644 --- a/media/formats/mp4/key_rotation_fragmenter.h +++ b/media/formats/mp4/key_rotation_fragmenter.h @@ -8,12 +8,16 @@ #define MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_ #include "media/base/encryption_key_source.h" -#include "media/formats/mp4/fragmenter.h" +#include "media/formats/mp4/encrypting_fragmenter.h" namespace media { namespace mp4 { -class KeyRotationFragmenter : public Fragmenter { +struct MovieFragment; + +/// KeyRotationFragmenter generates MP4 fragments with sample encrypted by +/// rotation keys. +class KeyRotationFragmenter : public EncryptingFragmenter { public: /// @param moof points to a MovieFragment box. /// @param traf points to a TrackFragment box. diff --git a/media/formats/mp4/mp4.gyp b/media/formats/mp4/mp4.gyp index 13e3ab5c47..f1495bce66 100644 --- a/media/formats/mp4/mp4.gyp +++ b/media/formats/mp4/mp4.gyp @@ -37,6 +37,8 @@ 'composition_offset_iterator.h', 'decoding_time_iterator.cc', 'decoding_time_iterator.h', + 'encrypting_fragmenter.cc', + 'encrypting_fragmenter.h', 'es_descriptor.cc', 'es_descriptor.h', 'fourccs.h', diff --git a/media/formats/mp4/segmenter.cc b/media/formats/mp4/segmenter.cc index ae851b3e71..ac4b0eb02f 100644 --- a/media/formats/mp4/segmenter.cc +++ b/media/formats/mp4/segmenter.cc @@ -180,18 +180,18 @@ Status Segmenter::Initialize(const std::vector& streams, GenerateEncryptedSampleEntry( *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()) { 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); + fragmenters_[i] = new EncryptingFragmenter( + &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.