2014-04-18 18:49:49 +00:00
|
|
|
// 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
|
|
|
|
|
2014-10-01 22:10:21 +00:00
|
|
|
#include "packager/media/formats/mp4/key_rotation_fragmenter.h"
|
2014-04-18 18:49:49 +00:00
|
|
|
|
2014-10-01 22:10:21 +00:00
|
|
|
#include "packager/media/base/aes_encryptor.h"
|
|
|
|
#include "packager/media/formats/mp4/box_definitions.h"
|
2014-04-18 18:49:49 +00:00
|
|
|
|
2014-09-19 20:41:13 +00:00
|
|
|
namespace edash_packager {
|
2014-04-18 18:49:49 +00:00
|
|
|
namespace media {
|
|
|
|
namespace mp4 {
|
|
|
|
|
2014-09-30 21:52:21 +00:00
|
|
|
KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof,
|
|
|
|
TrackFragment* traf,
|
|
|
|
KeySource* encryption_key_source,
|
|
|
|
KeySource::TrackType track_type,
|
|
|
|
int64_t crypto_period_duration,
|
|
|
|
int64_t clear_time,
|
2015-08-26 20:25:29 +00:00
|
|
|
uint8_t nalu_length_size,
|
|
|
|
MuxerListener* muxer_listener)
|
2014-05-08 20:53:08 +00:00
|
|
|
: EncryptingFragmenter(traf,
|
|
|
|
scoped_ptr<EncryptionKey>(new EncryptionKey()),
|
|
|
|
clear_time,
|
|
|
|
nalu_length_size),
|
2014-04-18 18:49:49 +00:00
|
|
|
moof_(moof),
|
|
|
|
encryption_key_source_(encryption_key_source),
|
|
|
|
track_type_(track_type),
|
|
|
|
crypto_period_duration_(crypto_period_duration),
|
2015-08-26 20:25:29 +00:00
|
|
|
prev_crypto_period_index_(-1),
|
|
|
|
muxer_listener_(muxer_listener) {
|
2014-04-18 18:49:49 +00:00
|
|
|
DCHECK(moof);
|
|
|
|
DCHECK(encryption_key_source);
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyRotationFragmenter::~KeyRotationFragmenter() {}
|
|
|
|
|
2014-05-08 21:50:38 +00:00
|
|
|
Status KeyRotationFragmenter::PrepareFragmentForEncryption(
|
|
|
|
bool enable_encryption) {
|
|
|
|
bool need_to_refresh_encryptor = !encryptor();
|
2014-04-18 18:49:49 +00:00
|
|
|
|
|
|
|
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());
|
2014-05-08 21:50:38 +00:00
|
|
|
prev_crypto_period_index_ = current_crypto_period_index;
|
|
|
|
need_to_refresh_encryptor = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2015-08-26 20:25:29 +00:00
|
|
|
if (muxer_listener_) {
|
|
|
|
muxer_listener_->OnEncryptionInfoReady(
|
|
|
|
encryption_key_source_->UUID(), encryption_key_source_->SystemName(),
|
|
|
|
encryption_key()->key_id, encryption_key()->pssh);
|
|
|
|
}
|
|
|
|
|
2014-05-08 21:50:38 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2014-04-18 18:49:49 +00:00
|
|
|
|
2014-05-08 21:50:38 +00:00
|
|
|
if (need_to_refresh_encryptor) {
|
|
|
|
Status status = CreateEncryptor();
|
2014-04-18 18:49:49 +00:00
|
|
|
if (!status.ok())
|
|
|
|
return status;
|
|
|
|
}
|
2014-05-08 21:50:38 +00:00
|
|
|
DCHECK(encryptor());
|
2014-04-18 18:49:49 +00:00
|
|
|
|
2014-05-08 20:53:08 +00:00
|
|
|
// 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.
|
2014-04-18 18:49:49 +00:00
|
|
|
traf()->sample_group_description.grouping_type = FOURCC_SEIG;
|
|
|
|
traf()->sample_group_description.entries.resize(1);
|
|
|
|
traf()->sample_group_description.entries[0].is_encrypted = true;
|
2014-05-08 21:50:38 +00:00
|
|
|
traf()->sample_group_description.entries[0].iv_size =
|
|
|
|
encryptor()->iv().size();
|
|
|
|
traf()->sample_group_description.entries[0].key_id = encryption_key()->key_id;
|
2014-04-18 18:49:49 +00:00
|
|
|
|
2014-05-08 20:53:08 +00:00
|
|
|
// Fill in SampleToGroup box information.
|
2014-04-18 18:49:49 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
return Status::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KeyRotationFragmenter::FinalizeFragmentForEncryption() {
|
2014-05-08 20:53:08 +00:00
|
|
|
EncryptingFragmenter::FinalizeFragmentForEncryption();
|
2014-04-18 18:49:49 +00:00
|
|
|
DCHECK_EQ(1u, traf()->sample_to_group.entries.size());
|
|
|
|
traf()->sample_to_group.entries[0].sample_count =
|
|
|
|
traf()->auxiliary_size.sample_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mp4
|
2014-09-19 20:41:13 +00:00
|
|
|
} // namespace media
|
|
|
|
} // namespace edash_packager
|