From e7fe62763d0887d180414d5a3bb085042db3de6f Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Thu, 8 May 2014 14:50:38 -0700 Subject: [PATCH] Add 'pssh' box for clear lead fragments as well This is to workaround a Chrome limitation that CDM must be initialized before starting the playback. CDM can only be initialized with a valid 'pssh'. Change-Id: Ia34e90dac42abbcdf0193fe4e3c971c87debdd42 --- media/formats/mp4/encrypting_fragmenter.cc | 32 ++++++------- media/formats/mp4/encrypting_fragmenter.h | 2 +- media/formats/mp4/key_rotation_fragmenter.cc | 49 +++++++++++++------- media/formats/mp4/key_rotation_fragmenter.h | 2 +- 4 files changed, 49 insertions(+), 36 deletions(-) diff --git a/media/formats/mp4/encrypting_fragmenter.cc b/media/formats/mp4/encrypting_fragmenter.cc index 8d1a5e5dfb..dd9bb677f5 100644 --- a/media/formats/mp4/encrypting_fragmenter.cc +++ b/media/formats/mp4/encrypting_fragmenter.cc @@ -51,20 +51,21 @@ Status EncryptingFragmenter::InitializeFragment() { if (!status.ok()) return status; - // Enable encryption for this fragment if |clear_time_| becomes non-positive. - if (clear_time_ <= 0) - return PrepareFragmentForEncryption(); + traf()->auxiliary_size.sample_info_sizes.clear(); + traf()->auxiliary_offset.offsets.clear(); - // 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; + const bool enable_encryption = clear_time_ <= 0; + if (!enable_encryption) { + // 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; + traf()->header.flags |= + TrackFragmentHeader::kSampleDescriptionIndexPresentMask; + traf()->header.sample_description_index = kClearSampleDescriptionIndex; + } + return PrepareFragmentForEncryption(enable_encryption); } void EncryptingFragmenter::FinalizeFragment() { @@ -78,10 +79,9 @@ void EncryptingFragmenter::FinalizeFragment() { Fragmenter::FinalizeFragment(); } -Status EncryptingFragmenter::PrepareFragmentForEncryption() { - traf()->auxiliary_size.sample_info_sizes.clear(); - traf()->auxiliary_offset.offsets.clear(); - return encryptor_ ? Status::OK : CreateEncryptor(); +Status EncryptingFragmenter::PrepareFragmentForEncryption( + bool enable_encryption) { + return (!enable_encryption || encryptor_) ? Status::OK : CreateEncryptor(); } void EncryptingFragmenter::FinalizeFragmentForEncryption() { diff --git a/media/formats/mp4/encrypting_fragmenter.h b/media/formats/mp4/encrypting_fragmenter.h index 6bbc5bfa2b..be4c411222 100644 --- a/media/formats/mp4/encrypting_fragmenter.h +++ b/media/formats/mp4/encrypting_fragmenter.h @@ -45,7 +45,7 @@ class EncryptingFragmenter : public Fragmenter { protected: /// Prepare current fragment for encryption. /// @return OK on success, an error status otherwise. - virtual Status PrepareFragmentForEncryption(); + virtual Status PrepareFragmentForEncryption(bool enable_encryption); /// Finalize current fragment for encryption. virtual void FinalizeFragmentForEncryption(); diff --git a/media/formats/mp4/key_rotation_fragmenter.cc b/media/formats/mp4/key_rotation_fragmenter.cc index 5e8e3a2286..6d4944aaa2 100644 --- a/media/formats/mp4/key_rotation_fragmenter.cc +++ b/media/formats/mp4/key_rotation_fragmenter.cc @@ -37,9 +37,9 @@ KeyRotationFragmenter::KeyRotationFragmenter( KeyRotationFragmenter::~KeyRotationFragmenter() {} -Status KeyRotationFragmenter::PrepareFragmentForEncryption() { - traf()->auxiliary_size.sample_info_sizes.clear(); - traf()->auxiliary_offset.offsets.clear(); +Status KeyRotationFragmenter::PrepareFragmentForEncryption( + bool enable_encryption) { + bool need_to_refresh_encryptor = !encryptor(); size_t current_crypto_period_index = traf()->decode_time.decode_time / crypto_period_duration_; @@ -50,17 +50,34 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption() { 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; + need_to_refresh_encryptor = true; } - EncryptionKey* encryption_key = EncryptingFragmenter::encryption_key(); - DCHECK(encryption_key); - AesCtrEncryptor* encryptor = EncryptingFragmenter::encryptor(); - DCHECK(encryptor); + // One and only one 'pssh' box is needed. + if (moof_->pssh.empty()) + moof_->pssh.resize(1); + DCHECK(encryption_key()); + moof_->pssh[0].raw_box = encryption_key()->pssh; + + // Skip the following steps if the current fragment is not going to be + // encrypted. 'pssh' box needs to be included in the fragment, which is + // performed above, regardless of whether the fragment is encrypted. This is + // necessary for two reasons: 1) Requesting keys before reaching encrypted + // content avoids playback delay due to license requests; 2) In Chrome, CDM + // must be initialized before starting the playback and CDM can only be + // initialized with a valid 'pssh'. + if (!enable_encryption) { + DCHECK(!encryptor()); + return Status::OK; + } + + if (need_to_refresh_encryptor) { + Status status = CreateEncryptor(); + if (!status.ok()) + return status; + } + DCHECK(encryptor()); // 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 @@ -69,8 +86,9 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption() { 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; + 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 SampleToGroup box information. traf()->sample_to_group.grouping_type = FOURCC_SEIG; @@ -79,11 +97,6 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption() { traf()->sample_to_group.entries[0].group_description_index = SampleToGroupEntry::kTrackFragmentGroupDescriptionIndexBase + 1; - // One and only one 'pssh' box is needed. - if (moof_->pssh.empty()) - moof_->pssh.resize(1); - moof_->pssh[0].raw_box = encryption_key->pssh; - return Status::OK; } diff --git a/media/formats/mp4/key_rotation_fragmenter.h b/media/formats/mp4/key_rotation_fragmenter.h index 0d969d4fdf..9c5a8d7718 100644 --- a/media/formats/mp4/key_rotation_fragmenter.h +++ b/media/formats/mp4/key_rotation_fragmenter.h @@ -46,7 +46,7 @@ class KeyRotationFragmenter : public EncryptingFragmenter { protected: /// @name Fragmenter implementation overrides. /// @{ - virtual Status PrepareFragmentForEncryption() OVERRIDE; + virtual Status PrepareFragmentForEncryption(bool enable_encryption) OVERRIDE; virtual void FinalizeFragmentForEncryption() OVERRIDE; /// @}