Update client of EncryptorSource due to refactoring

This CL also includes two related changes:
  1. Support serialization of raw PSSH box;
  2. Refactor segmenter interface a bit.

Change-Id: I668aa01a5a5ff524ad6fe74976339a3fd499120a
This commit is contained in:
Kongqun Yang 2014-04-18 15:00:30 -07:00
parent 107145c693
commit f059d926d7
15 changed files with 248 additions and 200 deletions

View File

@ -15,7 +15,6 @@
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "media/base/demuxer.h" #include "media/base/demuxer.h"
#include "media/base/fixed_encryptor_source.h"
#include "media/base/media_stream.h" #include "media/base/media_stream.h"
#include "media/base/muxer_options.h" #include "media/base/muxer_options.h"
#include "media/base/request_signer.h" #include "media/base/request_signer.h"
@ -76,27 +75,11 @@ scoped_ptr<EncryptorSource> CreateEncryptorSource() {
} }
} }
WidevineEncryptorSource::TrackType track_type =
WidevineEncryptorSource::GetTrackTypeFromString(FLAGS_track_type);
if (track_type == WidevineEncryptorSource::TRACK_TYPE_UNKNOWN) {
LOG(ERROR) << "Unknown track_type specified.";
return scoped_ptr<EncryptorSource>();
}
encryptor_source.reset(new WidevineEncryptorSource( encryptor_source.reset(new WidevineEncryptorSource(
FLAGS_server_url, FLAGS_content_id, track_type, signer.Pass())); FLAGS_server_url, FLAGS_content_id, signer.Pass()));
} else if (FLAGS_enable_fixed_key_encryption) { } else if (FLAGS_enable_fixed_key_encryption) {
encryptor_source.reset( encryptor_source = EncryptorSource::CreateFromHexStrings(
new FixedEncryptorSource(FLAGS_key_id, FLAGS_key, FLAGS_pssh)); FLAGS_key_id, FLAGS_key, FLAGS_pssh, "");
}
if (encryptor_source) {
Status status = encryptor_source->Initialize();
if (!status.ok()) {
LOG(ERROR) << "Encryptor source failed to initialize: "
<< status.ToString();
return scoped_ptr<EncryptorSource>();
}
} }
return encryptor_source.Pass(); return encryptor_source.Pass();
} }
@ -229,7 +212,15 @@ bool RunPackager(const std::string& input) {
if (!encryptor_source) if (!encryptor_source)
return false; return false;
} }
muxer->SetEncryptorSource(encryptor_source.get(), FLAGS_clear_lead); EncryptorSource::TrackType track_type =
EncryptorSource::GetTrackTypeFromString(FLAGS_track_type);
if (track_type != EncryptorSource::TRACK_TYPE_SD &&
track_type != EncryptorSource::TRACK_TYPE_HD) {
LOG(ERROR) << "FLAGS_track_type should be either 'SD' or 'HD'";
return false;
}
muxer->SetEncryptorSource(
encryptor_source.get(), track_type, FLAGS_clear_lead);
// Start remuxing process. // Start remuxing process.
status = demuxer.Run(); status = demuxer.Run();

View File

@ -20,7 +20,7 @@ DEFINE_bool(enable_widevine_encryption,
"--aes_signing_iv) or RSA signing key (--rsa_signing_key_path)."); "--aes_signing_iv) or RSA signing key (--rsa_signing_key_path).");
DEFINE_string(server_url, "", "License server url."); DEFINE_string(server_url, "", "License server url.");
DEFINE_string(content_id, "", "Content Id."); DEFINE_string(content_id, "", "Content Id.");
DEFINE_string(track_type, "SD", "Track type: HD, SD or AUDIO."); DEFINE_string(track_type, "SD", "Track type: SD or HD.");
DEFINE_string(signer, "", "The name of the signer."); DEFINE_string(signer, "", "The name of the signer.");
DEFINE_string(aes_signing_key, DEFINE_string(aes_signing_key,
"", "",

View File

@ -16,6 +16,7 @@ Muxer::Muxer(const MuxerOptions& options)
: options_(options), : options_(options),
encryptor_source_(NULL), encryptor_source_(NULL),
initialized_(false), initialized_(false),
track_type_(EncryptorSource::TRACK_TYPE_SD),
clear_lead_in_seconds_(0), clear_lead_in_seconds_(0),
muxer_listener_(NULL), muxer_listener_(NULL),
clock_(NULL) {} clock_(NULL) {}
@ -23,8 +24,10 @@ Muxer::Muxer(const MuxerOptions& options)
Muxer::~Muxer() {} Muxer::~Muxer() {}
void Muxer::SetEncryptorSource(EncryptorSource* encryptor_source, void Muxer::SetEncryptorSource(EncryptorSource* encryptor_source,
EncryptorSource::TrackType track_type,
double clear_lead_in_seconds) { double clear_lead_in_seconds) {
encryptor_source_ = encryptor_source; encryptor_source_ = encryptor_source;
track_type_ = track_type;
clear_lead_in_seconds_ = clear_lead_in_seconds; clear_lead_in_seconds_ = clear_lead_in_seconds;
} }

View File

@ -13,6 +13,7 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "media/base/encryptor_source.h"
#include "media/base/muxer_options.h" #include "media/base/muxer_options.h"
#include "media/base/status.h" #include "media/base/status.h"
@ -41,8 +42,11 @@ class Muxer {
/// Set encryptor source. /// Set encryptor source.
/// @param encryptor_source points to the encryptor source to be injected. /// @param encryptor_source points to the encryptor source to be injected.
/// Should not be NULL. /// Should not be NULL.
/// @param track_type should be either SD or HD. It affects whether SD key or
/// HD key is used to encrypt the video content.
/// @param clear_lead_in_seconds specifies clear lead duration in seconds. /// @param clear_lead_in_seconds specifies clear lead duration in seconds.
void SetEncryptorSource(EncryptorSource* encryptor_source, void SetEncryptorSource(EncryptorSource* encryptor_source,
EncryptorSource::TrackType track_type,
double clear_lead_in_seconds); double clear_lead_in_seconds);
/// Add video/audio stream. /// Add video/audio stream.
@ -70,6 +74,7 @@ class Muxer {
protected: protected:
const MuxerOptions& options() const { return options_; } const MuxerOptions& options() const { return options_; }
EncryptorSource* encryptor_source() { return encryptor_source_; } EncryptorSource* encryptor_source() { return encryptor_source_; }
EncryptorSource::TrackType track_type() const { return track_type_; }
double clear_lead_in_seconds() const { return clear_lead_in_seconds_; } double clear_lead_in_seconds() const { return clear_lead_in_seconds_; }
event::MuxerListener* muxer_listener() { return muxer_listener_; } event::MuxerListener* muxer_listener() { return muxer_listener_; }
base::Clock* clock() { return clock_; } base::Clock* clock() { return clock_; }
@ -95,6 +100,7 @@ class Muxer {
std::vector<MediaStream*> streams_; std::vector<MediaStream*> streams_;
EncryptorSource* encryptor_source_; EncryptorSource* encryptor_source_;
bool initialized_; bool initialized_;
EncryptorSource::TrackType track_type_;
double clear_lead_in_seconds_; double clear_lead_in_seconds_;
event::MuxerListener* muxer_listener_; event::MuxerListener* muxer_listener_;

View File

@ -90,6 +90,12 @@ ProtectionSystemSpecificHeader::~ProtectionSystemSpecificHeader() {}
FourCC ProtectionSystemSpecificHeader::BoxType() const { return FOURCC_PSSH; } FourCC ProtectionSystemSpecificHeader::BoxType() const { return FOURCC_PSSH; }
bool ProtectionSystemSpecificHeader::ReadWrite(BoxBuffer* buffer) { bool ProtectionSystemSpecificHeader::ReadWrite(BoxBuffer* buffer) {
if (!buffer->Reading() && !raw_box.empty()) {
// Write the raw box directly.
buffer->writer()->AppendVector(raw_box);
return true;
}
uint32 size = data.size(); uint32 size = data.size();
RCHECK(FullBox::ReadWrite(buffer) && RCHECK(FullBox::ReadWrite(buffer) &&
buffer->ReadWriteVector(&system_id, 16) && buffer->ReadWriteVector(&system_id, 16) &&
@ -108,7 +114,10 @@ bool ProtectionSystemSpecificHeader::ReadWrite(BoxBuffer* buffer) {
} }
uint32 ProtectionSystemSpecificHeader::ComputeSize() { uint32 ProtectionSystemSpecificHeader::ComputeSize() {
atom_size = kFullBoxSize + system_id.size() + sizeof(uint32) + data.size(); if (!raw_box.empty())
atom_size = raw_box.size();
else
atom_size = kFullBoxSize + system_id.size() + sizeof(uint32) + data.size();
return atom_size; return atom_size;
} }

View File

@ -733,7 +733,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
typedef testing::Types< typedef testing::Types<
FileType, FileType,
SegmentType, SegmentType,
// ProtectionSystemSpecificHeader, // Not fully parsed. ProtectionSystemSpecificHeader,
SampleAuxiliaryInformationOffset, SampleAuxiliaryInformationOffset,
SampleAuxiliaryInformationSize, SampleAuxiliaryInformationSize,
OriginalFormat, OriginalFormat,
@ -854,8 +854,17 @@ TEST_F(BoxDefinitionsTest, ProtectionSystemSpecificHeader) {
ProtectionSystemSpecificHeader pssh_readback; ProtectionSystemSpecificHeader pssh_readback;
ASSERT_TRUE(ReadBack(&pssh_readback)); ASSERT_TRUE(ReadBack(&pssh_readback));
// PSSH does not parse data. ASSERT_EQ(pssh, pssh_readback);
ASSERT_EQ(pssh.system_id, pssh_readback.system_id);
pssh_readback.raw_box[15] += 1;
pssh_readback.Write(this->buffer_.get());
ProtectionSystemSpecificHeader pssh_readback2;
ASSERT_TRUE(ReadBack(&pssh_readback2));
// If raw_box is set, raw_box will be written instead.
ASSERT_FALSE(pssh_readback == pssh_readback2);
ASSERT_EQ(pssh_readback.raw_box, pssh_readback2.raw_box);
} }
TEST_F(BoxDefinitionsTest, CompactSampleSize_FieldSize16) { TEST_F(BoxDefinitionsTest, CompactSampleSize_FieldSize16) {

View File

@ -23,9 +23,6 @@
#include "media/formats/mp4/single_segment_segmenter.h" #include "media/formats/mp4/single_segment_segmenter.h"
namespace { namespace {
// The version of cenc implemented here. CENC 4.
const int kCencSchemeVersion = 0x00010000;
// Sets the range start and end value from offset and size. // Sets the range start and end value from offset and size.
// |start| and |end| are for byte-range-spec specified in RFC2616. // |start| and |end| are for byte-range-spec specified in RFC2616.
void SetStartAndEndFromOffsetAndSize(size_t offset, void SetStartAndEndFromOffsetAndSize(size_t offset,
@ -93,11 +90,6 @@ Status MP4Muxer::Initialize() {
} }
} }
if (IsEncryptionRequired()) {
moov->pssh.resize(1);
GeneratePssh(&moov->pssh[0]);
}
if (options().single_segment) { if (options().single_segment) {
segmenter_.reset( segmenter_.reset(
new SingleSegmentSegmenter(options(), ftyp.Pass(), moov.Pass())); new SingleSegmentSegmenter(options(), ftyp.Pass(), moov.Pass()));
@ -107,7 +99,7 @@ Status MP4Muxer::Initialize() {
} }
Status segmenter_initialized = segmenter_->Initialize( Status segmenter_initialized = segmenter_->Initialize(
encryptor_source(), clear_lead_in_seconds(), streams()); streams(), encryptor_source(), track_type(), clear_lead_in_seconds());
if (!segmenter_initialized.ok()) if (!segmenter_initialized.ok())
return segmenter_initialized; return segmenter_initialized;
@ -169,17 +161,6 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
trak->media.information.sample_table.description; trak->media.information.sample_table.description;
sample_description.type = kVideo; sample_description.type = kVideo;
sample_description.video_entries.push_back(video); sample_description.video_entries.push_back(video);
if (IsEncryptionRequired()) {
DCHECK(encryptor_source());
// Add a second entry for clear content if needed.
if (clear_lead_in_seconds() > 0)
sample_description.video_entries.push_back(video);
VideoSampleEntry& encrypted_video = sample_description.video_entries[0];
GenerateSinf(&encrypted_video.sinf, encrypted_video.format);
encrypted_video.format = FOURCC_ENCV;
}
} }
void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info, void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
@ -209,33 +190,6 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
trak->media.information.sample_table.description; trak->media.information.sample_table.description;
sample_description.type = kAudio; sample_description.type = kAudio;
sample_description.audio_entries.push_back(audio); sample_description.audio_entries.push_back(audio);
if (IsEncryptionRequired()) {
DCHECK(encryptor_source());
// Add a second entry for clear content if needed.
if (clear_lead_in_seconds() > 0)
sample_description.audio_entries.push_back(audio);
AudioSampleEntry& encrypted_audio = sample_description.audio_entries[0];
GenerateSinf(&encrypted_audio.sinf, encrypted_audio.format);
encrypted_audio.format = FOURCC_ENCA;
}
}
void MP4Muxer::GeneratePssh(ProtectionSystemSpecificHeader* pssh) {
DCHECK(encryptor_source());
pssh->system_id = encryptor_source()->key_system_id();
pssh->data = encryptor_source()->pssh();
}
void MP4Muxer::GenerateSinf(ProtectionSchemeInfo* sinf, FourCC old_type) {
DCHECK(encryptor_source());
sinf->format.format = old_type;
sinf->type.type = FOURCC_CENC;
sinf->type.version = kCencSchemeVersion;
sinf->info.track_encryption.is_encrypted = true;
sinf->info.track_encryption.default_iv_size = encryptor_source()->iv_size();
sinf->info.track_encryption.default_kid = encryptor_source()->key_id();
} }
void MP4Muxer::GetStreamInfo(std::vector<StreamInfo*>* stream_infos) { void MP4Muxer::GetStreamInfo(std::vector<StreamInfo*>* stream_infos) {
@ -320,7 +274,7 @@ void MP4Muxer::FireOnMediaEndEvent() {
index_range_end, index_range_end,
duration_seconds, duration_seconds,
file_size, file_size,
IsEncryptionRequired()); encryptor_source());
} }
uint64 MP4Muxer::IsoTimeNow() { uint64 MP4Muxer::IsoTimeNow() {

View File

@ -50,15 +50,6 @@ class MP4Muxer : public Muxer {
Track* trak, Track* trak,
uint32 track_id); uint32 track_id);
// Generate Pssh atom.
void GeneratePssh(ProtectionSystemSpecificHeader* pssh);
// Generates a sinf atom with CENC encryption parameters.
void GenerateSinf(ProtectionSchemeInfo* sinf, FourCC old_type);
// Should we enable encrytion?
bool IsEncryptionRequired() { return (encryptor_source() != NULL); }
// Helper functions for events. // Helper functions for events.
void GetStreamInfo(std::vector<StreamInfo*>* stream_infos); void GetStreamInfo(std::vector<StreamInfo*>* stream_infos);

View File

@ -30,34 +30,6 @@ MultiSegmentSegmenter::MultiSegmentSegmenter(const MuxerOptions& options,
MultiSegmentSegmenter::~MultiSegmentSegmenter() {} MultiSegmentSegmenter::~MultiSegmentSegmenter() {}
Status MultiSegmentSegmenter::Initialize(
EncryptorSource* encryptor_source,
double clear_lead_in_seconds,
const std::vector<MediaStream*>& streams) {
Status status =
Segmenter::Initialize(encryptor_source, clear_lead_in_seconds, streams);
if (!status.ok())
return status;
DCHECK(ftyp());
DCHECK(moov());
// Generate the output file with init segment.
File* file = File::Open(options().output_file_name.c_str(), "w");
if (file == NULL) {
return Status(error::FILE_FAILURE,
"Cannot open file for write " + options().output_file_name);
}
scoped_ptr<BufferWriter> buffer(new BufferWriter);
ftyp()->Write(buffer.get());
moov()->Write(buffer.get());
status = buffer->WriteToFile(file);
if (!file->Close()) {
LOG(WARNING) << "Failed to close the file properly: "
<< options().output_file_name;
}
return status;
}
bool MultiSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) { bool MultiSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) {
DLOG(INFO) << "MultiSegmentSegmenter outputs init segment: " DLOG(INFO) << "MultiSegmentSegmenter outputs init segment: "
<< options().output_file_name; << options().output_file_name;
@ -69,11 +41,31 @@ bool MultiSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
return false; return false;
} }
Status MultiSegmentSegmenter::FinalizeSegment() { Status MultiSegmentSegmenter::DoInitialize() {
Status status = Segmenter::FinalizeSegment(); DCHECK(ftyp());
if (!status.ok()) DCHECK(moov());
return status; // Generate the output file with init segment.
File* file = File::Open(options().output_file_name.c_str(), "w");
if (file == NULL) {
return Status(error::FILE_FAILURE,
"Cannot open file for write " + options().output_file_name);
}
scoped_ptr<BufferWriter> buffer(new BufferWriter);
ftyp()->Write(buffer.get());
moov()->Write(buffer.get());
Status status = buffer->WriteToFile(file);
if (!file->Close()) {
LOG(WARNING) << "Failed to close the file properly: "
<< options().output_file_name;
}
return status;
}
Status MultiSegmentSegmenter::DoFinalize() {
return Status::OK;
}
Status MultiSegmentSegmenter::DoFinalizeSegment() {
DCHECK(sidx()); DCHECK(sidx());
// earliest_presentation_time is the earliest presentation time of any // earliest_presentation_time is the earliest presentation time of any
// access unit in the reference stream in the first subsegment. // access unit in the reference stream in the first subsegment.

View File

@ -36,18 +36,16 @@ class MultiSegmentSegmenter : public Segmenter {
/// @name Segmenter implementation overrides. /// @name Segmenter implementation overrides.
/// @{ /// @{
virtual Status Initialize(EncryptorSource* encryptor_source,
double clear_lead_in_seconds,
const std::vector<MediaStream*>& streams) OVERRIDE;
virtual bool GetInitRange(size_t* offset, size_t* size) OVERRIDE; virtual bool GetInitRange(size_t* offset, size_t* size) OVERRIDE;
virtual bool GetIndexRange(size_t* offset, size_t* size) OVERRIDE; virtual bool GetIndexRange(size_t* offset, size_t* size) OVERRIDE;
/// @} /// @}
protected:
virtual Status FinalizeSegment() OVERRIDE;
private: private:
// Segmenter implementation overrides.
virtual Status DoInitialize() OVERRIDE;
virtual Status DoFinalize() OVERRIDE;
virtual Status DoFinalizeSegment() OVERRIDE;
// Write segment to file. // Write segment to file.
Status WriteSegment(); Status WriteSegment();

View File

@ -9,6 +9,7 @@
#include <algorithm> #include <algorithm>
#include "base/stl_util.h" #include "base/stl_util.h"
#include "media/base/aes_encryptor.h"
#include "media/base/buffer_writer.h" #include "media/base/buffer_writer.h"
#include "media/base/encryptor_source.h" #include "media/base/encryptor_source.h"
#include "media/base/media_sample.h" #include "media/base/media_sample.h"
@ -18,14 +19,79 @@
#include "media/formats/mp4/box_definitions.h" #include "media/formats/mp4/box_definitions.h"
#include "media/formats/mp4/fragmenter.h" #include "media/formats/mp4/fragmenter.h"
namespace media {
namespace mp4 {
namespace { namespace {
// Generate 64bit IV by default.
const size_t kDefaultIvSize = 8u;
// The version of cenc implemented here. CENC 4.
const int kCencSchemeVersion = 0x00010000;
uint64 Rescale(uint64 time_in_old_scale, uint32 old_scale, uint32 new_scale) { uint64 Rescale(uint64 time_in_old_scale, uint32 old_scale, uint32 new_scale) {
return static_cast<double>(time_in_old_scale) / old_scale * new_scale; return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
} }
} // namespace
namespace media { scoped_ptr<AesCtrEncryptor> CreateEncryptor(
namespace mp4 { const EncryptionKey& 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) {
LOG(ERROR) << "Failed to the initialize encryptor.";
return scoped_ptr<AesCtrEncryptor>();
}
return encryptor.Pass();
}
void GenerateSinf(const EncryptionKey& encryption_key,
FourCC old_type,
ProtectionSchemeInfo* sinf) {
sinf->format.format = old_type;
sinf->type.type = FOURCC_CENC;
sinf->type.version = kCencSchemeVersion;
sinf->info.track_encryption.is_encrypted = true;
sinf->info.track_encryption.default_iv_size =
encryption_key.iv.empty() ? kDefaultIvSize : encryption_key.iv.size();
sinf->info.track_encryption.default_kid = encryption_key.key_id;
}
void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
double clear_lead_in_seconds,
SampleDescription* description) {
DCHECK(description);
if (description->type == kVideo) {
DCHECK_EQ(1u, description->video_entries.size());
// Add a second entry for clear content if needed.
if (clear_lead_in_seconds > 0)
description->video_entries.push_back(description->video_entries[0]);
// Convert the first entry to an encrypted entry.
VideoSampleEntry& entry = description->video_entries[0];
GenerateSinf(encryption_key, entry.format, &entry.sinf);
entry.format = FOURCC_ENCV;
} else {
DCHECK_EQ(kAudio, description->type);
DCHECK_EQ(1u, description->audio_entries.size());
// Add a second entry for clear content if needed.
if (clear_lead_in_seconds > 0)
description->audio_entries.push_back(description->audio_entries[0]);
// Convert the first entry to an encrypted entry.
AudioSampleEntry& entry = description->audio_entries[0];
GenerateSinf(encryption_key, entry.format, &entry.sinf);
entry.format = FOURCC_ENCA;
}
}
} // namespace
Segmenter::Segmenter(const MuxerOptions& options, Segmenter::Segmenter(const MuxerOptions& options,
scoped_ptr<FileType> ftyp, scoped_ptr<FileType> ftyp,
@ -41,9 +107,10 @@ Segmenter::Segmenter(const MuxerOptions& options,
Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); } Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
Status Segmenter::Initialize(EncryptorSource* encryptor_source, Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
double clear_lead_in_seconds, EncryptorSource* encryptor_source,
const std::vector<MediaStream*>& streams) { EncryptorSource::TrackType track_type,
double clear_lead_in_seconds) {
DCHECK_LT(0u, streams.size()); DCHECK_LT(0u, streams.size());
moof_->header.sequence_number = 0; moof_->header.sequence_number = 0;
@ -64,7 +131,30 @@ Status Segmenter::Initialize(EncryptorSource* encryptor_source,
} }
scoped_ptr<AesCtrEncryptor> encryptor; scoped_ptr<AesCtrEncryptor> encryptor;
if (encryptor_source) { if (encryptor_source) {
encryptor = encryptor_source->CreateEncryptor(); SampleDescription& description =
moov_->tracks[i].media.information.sample_table.description;
DCHECK(track_type == EncryptorSource::TRACK_TYPE_SD ||
track_type == EncryptorSource::TRACK_TYPE_HD);
EncryptionKey encryption_key;
Status status = encryptor_source->GetKey(
description.type == kAudio ? EncryptorSource::TRACK_TYPE_AUDIO
: track_type,
&encryption_key);
if (!status.ok())
return status;
GenerateEncryptedSampleEntry(
encryption_key, clear_lead_in_seconds, &description);
// We need one and only one pssh box.
if (moov_->pssh.empty()) {
moov_->pssh.resize(1);
moov_->pssh[0].raw_box = encryption_key.pssh;
}
encryptor = CreateEncryptor(encryption_key);
if (!encryptor) if (!encryptor)
return Status(error::MUXER_FAILURE, "Failed to create the encryptor."); return Status(error::MUXER_FAILURE, "Failed to create the encryptor.");
} }
@ -84,7 +174,7 @@ Status Segmenter::Initialize(EncryptorSource* encryptor_source,
// Use the reference stream's time scale as movie time scale. // Use the reference stream's time scale as movie time scale.
moov_->header.timescale = sidx_->timescale; moov_->header.timescale = sidx_->timescale;
InitializeFragments(); InitializeFragments();
return Status::OK; return DoInitialize();
} }
Status Segmenter::Finalize() { Status Segmenter::Finalize() {
@ -110,7 +200,7 @@ Status Segmenter::Finalize() {
moov_->header.duration = track->header.duration; moov_->header.duration = track->header.duration;
} }
return Status::OK; return DoFinalize();
} }
Status Segmenter::AddSample(const MediaStream* stream, Status Segmenter::AddSample(const MediaStream* stream,
@ -191,7 +281,7 @@ void Segmenter::InitializeSegment() {
Status Segmenter::FinalizeSegment() { Status Segmenter::FinalizeSegment() {
segment_initialized_ = false; segment_initialized_ = false;
return Status::OK; return DoFinalizeSegment();
} }
uint32 Segmenter::GetReferenceStreamId() { uint32 Segmenter::GetReferenceStreamId() {

View File

@ -12,6 +12,7 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "media/base/encryptor_source.h"
#include "media/base/status.h" #include "media/base/status.h"
namespace media { namespace media {
@ -47,17 +48,30 @@ class Segmenter {
/// Initialize the segmenter. /// Initialize the segmenter.
/// Calling other public methods of this class without this method returning /// Calling other public methods of this class without this method returning
/// Status::OK, results in an undefined behavior. /// Status::OK results in an undefined behavior.
/// @param encryptor_source can be NULL. /// @param encryptor_source points to the key source which contains
/// @return Status::OK on success. /// the encryption keys. It can be NULL to indicate that no encryption
virtual Status Initialize(EncryptorSource* encryptor_source, /// is required.
double clear_lead_in_seconds, /// @param track_type indicates whether SD key or HD key should be used to
const std::vector<MediaStream*>& streams); /// encrypt the video content.
/// @param clear_time specifies clear lead duration in seconds.
/// @return OK on success, an error status otherwise.
Status Initialize(const std::vector<MediaStream*>& streams,
EncryptorSource* encryptor_source,
EncryptorSource::TrackType track_type,
double clear_lead_in_seconds);
virtual Status Finalize(); /// Finalize the segmenter.
/// @return OK on success, an error status otherwise.
Status Finalize();
virtual Status AddSample(const MediaStream* stream, /// Add sample to the indicated stream.
scoped_refptr<MediaSample> sample); /// @param stream points to the stream to which the sample belongs. It cannot
/// be NULL.
/// @param sample points to the sample to be added.
/// @return OK on success, an error status otherwise.
Status AddSample(const MediaStream* stream,
scoped_refptr<MediaSample> sample);
/// @return true if there is an initialization range, while setting @a offset /// @return true if there is an initialization range, while setting @a offset
/// and @a size; or false if initialization range does not apply. /// and @a size; or false if initialization range does not apply.
@ -73,11 +87,6 @@ class Segmenter {
double GetDuration() const; double GetDuration() const;
protected: protected:
void InitializeSegment();
virtual Status FinalizeSegment();
uint32 GetReferenceStreamId();
const MuxerOptions& options() const { return options_; } const MuxerOptions& options() const { return options_; }
FileType* ftyp() { return ftyp_.get(); } FileType* ftyp() { return ftyp_.get(); }
Movie* moov() { return moov_.get(); } Movie* moov() { return moov_.get(); }
@ -85,6 +94,14 @@ class Segmenter {
SegmentIndex* sidx() { return sidx_.get(); } SegmentIndex* sidx() { return sidx_.get(); }
private: private:
virtual Status DoInitialize() = 0;
virtual Status DoFinalize() = 0;
virtual Status DoFinalizeSegment() = 0;
void InitializeSegment();
Status FinalizeSegment();
uint32 GetReferenceStreamId();
void InitializeFragments(); void InitializeFragments();
Status FinalizeFragment(Fragmenter* fragment); Status FinalizeFragment(Fragmenter* fragment);

View File

@ -21,27 +21,30 @@ SingleSegmentSegmenter::SingleSegmentSegmenter(const MuxerOptions& options,
: Segmenter(options, ftyp.Pass(), moov.Pass()) {} : Segmenter(options, ftyp.Pass(), moov.Pass()) {}
SingleSegmentSegmenter::~SingleSegmentSegmenter() {} SingleSegmentSegmenter::~SingleSegmentSegmenter() {}
Status SingleSegmentSegmenter::Initialize( bool SingleSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) {
EncryptorSource* encryptor_source, // In Finalize, ftyp and moov gets written first so offset must be 0.
double clear_lead_in_seconds, *offset = 0;
const std::vector<MediaStream*>& streams) { *size = ftyp()->ComputeSize() + moov()->ComputeSize();
Status status = return true;
Segmenter::Initialize(encryptor_source, clear_lead_in_seconds, streams);
if (!status.ok())
return status;
temp_file_.reset(File::Open(options().temp_file_name.c_str(), "w"));
if (temp_file_ == NULL) {
return Status(error::FILE_FAILURE,
"Cannot open file to write " + options().temp_file_name);
}
return Status::OK;
} }
Status SingleSegmentSegmenter::Finalize() { bool SingleSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
Status status = Segmenter::Finalize(); // Index range is right after init range so the offset must be the size of
if (!status.ok()) // ftyp and moov.
return status; *offset = ftyp()->ComputeSize() + moov()->ComputeSize();
*size = vod_sidx_->ComputeSize();
return true;
}
Status SingleSegmentSegmenter::DoInitialize() {
temp_file_.reset(File::Open(options().temp_file_name.c_str(), "w"));
return temp_file_
? Status::OK
: Status(error::FILE_FAILURE,
"Cannot open file to write " + options().temp_file_name);
}
Status SingleSegmentSegmenter::DoFinalize() {
DCHECK(temp_file_); DCHECK(temp_file_);
DCHECK(ftyp()); DCHECK(ftyp());
DCHECK(moov()); DCHECK(moov());
@ -65,7 +68,7 @@ Status SingleSegmentSegmenter::Finalize() {
ftyp()->Write(buffer.get()); ftyp()->Write(buffer.get());
moov()->Write(buffer.get()); moov()->Write(buffer.get());
vod_sidx_->Write(buffer.get()); vod_sidx_->Write(buffer.get());
status = buffer->WriteToFile(file.get()); Status status = buffer->WriteToFile(file.get());
if (!status.ok()) if (!status.ok())
return status; return status;
@ -94,26 +97,7 @@ Status SingleSegmentSegmenter::Finalize() {
return Status::OK; return Status::OK;
} }
bool SingleSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) { Status SingleSegmentSegmenter::DoFinalizeSegment() {
// In Finalize, ftyp and moov gets written first so offset must be 0.
*offset = 0;
*size = ftyp()->ComputeSize() + moov()->ComputeSize();
return true;
}
bool SingleSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
// Index range is right after init range so the offset must be the size of
// ftyp and moov.
*offset = ftyp()->ComputeSize() + moov()->ComputeSize();
*size = vod_sidx_->ComputeSize();
return true;
}
Status SingleSegmentSegmenter::FinalizeSegment() {
Status status = Segmenter::FinalizeSegment();
if (!status.ok())
return status;
DCHECK(sidx()); DCHECK(sidx());
DCHECK(fragment_buffer()); DCHECK(fragment_buffer());
// sidx() contains pre-generated segment references with one reference per // sidx() contains pre-generated segment references with one reference per

View File

@ -33,19 +33,16 @@ class SingleSegmentSegmenter : public Segmenter {
/// @name Segmenter implementation overrides. /// @name Segmenter implementation overrides.
/// @{ /// @{
virtual Status Initialize(EncryptorSource* encryptor_source,
double clear_lead_in_seconds,
const std::vector<MediaStream*>& streams) OVERRIDE;
virtual Status Finalize() OVERRIDE;
virtual bool GetInitRange(size_t* offset, size_t* size) OVERRIDE; virtual bool GetInitRange(size_t* offset, size_t* size) OVERRIDE;
virtual bool GetIndexRange(size_t* offset, size_t* size) OVERRIDE; virtual bool GetIndexRange(size_t* offset, size_t* size) OVERRIDE;
/// @} /// @}
protected:
virtual Status FinalizeSegment() OVERRIDE;
private: private:
// Segmenter implementation overrides.
virtual Status DoInitialize() OVERRIDE;
virtual Status DoFinalize() OVERRIDE;
virtual Status DoFinalizeSegment() OVERRIDE;
scoped_ptr<SegmentIndex> vod_sidx_; scoped_ptr<SegmentIndex> vod_sidx_;
scoped_ptr<File, FileCloser> temp_file_; scoped_ptr<File, FileCloser> temp_file_;

View File

@ -9,7 +9,7 @@
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/time/clock.h" #include "base/time/clock.h"
#include "media/base/demuxer.h" #include "media/base/demuxer.h"
#include "media/base/fixed_encryptor_source.h" #include "media/base/encryptor_source.h"
#include "media/base/media_stream.h" #include "media/base/media_stream.h"
#include "media/base/muxer.h" #include "media/base/muxer.h"
#include "media/base/status_test_util.h" #include "media/base/status_test_util.h"
@ -148,8 +148,9 @@ void PackagerTestBasic::Remux(const std::string& input,
Demuxer demuxer(GetFullPath(input), decryptor_source_); Demuxer demuxer(GetFullPath(input), decryptor_source_);
ASSERT_OK(demuxer.Initialize()); ASSERT_OK(demuxer.Initialize());
FixedEncryptorSource encryptor_source(kKeyIdHex, kKeyHex, kPsshHex); scoped_ptr<EncryptorSource> encryptor_source(
ASSERT_OK(encryptor_source.Initialize()); EncryptorSource::CreateFromHexStrings(kKeyIdHex, kKeyHex, kPsshHex, ""));
DCHECK(encryptor_source);
scoped_ptr<Muxer> muxer_video; scoped_ptr<Muxer> muxer_video;
if (!video_output.empty()) { if (!video_output.empty()) {
@ -159,8 +160,11 @@ void PackagerTestBasic::Remux(const std::string& input,
muxer_video->AddStream(FindFirstVideoStream(demuxer.streams())); muxer_video->AddStream(FindFirstVideoStream(demuxer.streams()));
if (enable_encryption) if (enable_encryption) {
muxer_video->SetEncryptorSource(&encryptor_source, kClearLeadInSeconds); muxer_video->SetEncryptorSource(encryptor_source.get(),
EncryptorSource::TRACK_TYPE_SD,
kClearLeadInSeconds);
}
} }
scoped_ptr<Muxer> muxer_audio; scoped_ptr<Muxer> muxer_audio;
@ -171,8 +175,11 @@ void PackagerTestBasic::Remux(const std::string& input,
muxer_audio->AddStream(FindFirstAudioStream(demuxer.streams())); muxer_audio->AddStream(FindFirstAudioStream(demuxer.streams()));
if (enable_encryption) if (enable_encryption) {
muxer_video->SetEncryptorSource(&encryptor_source, kClearLeadInSeconds); muxer_audio->SetEncryptorSource(encryptor_source.get(),
EncryptorSource::TRACK_TYPE_SD,
kClearLeadInSeconds);
}
} }
// Start remuxing process. // Start remuxing process.