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
This commit is contained in:
KongQun Yang 2014-05-08 14:50:38 -07:00
parent b25834a910
commit e7fe62763d
4 changed files with 49 additions and 36 deletions

View File

@ -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() {

View File

@ -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();

View File

@ -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;
}

View File

@ -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;
/// @}