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:
parent
107145c693
commit
f059d926d7
|
@ -15,7 +15,6 @@
|
|||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "media/base/demuxer.h"
|
||||
#include "media/base/fixed_encryptor_source.h"
|
||||
#include "media/base/media_stream.h"
|
||||
#include "media/base/muxer_options.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(
|
||||
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) {
|
||||
encryptor_source.reset(
|
||||
new FixedEncryptorSource(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>();
|
||||
}
|
||||
encryptor_source = EncryptorSource::CreateFromHexStrings(
|
||||
FLAGS_key_id, FLAGS_key, FLAGS_pssh, "");
|
||||
}
|
||||
return encryptor_source.Pass();
|
||||
}
|
||||
|
@ -229,7 +212,15 @@ bool RunPackager(const std::string& input) {
|
|||
if (!encryptor_source)
|
||||
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.
|
||||
status = demuxer.Run();
|
||||
|
|
|
@ -20,7 +20,7 @@ DEFINE_bool(enable_widevine_encryption,
|
|||
"--aes_signing_iv) or RSA signing key (--rsa_signing_key_path).");
|
||||
DEFINE_string(server_url, "", "License server url.");
|
||||
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(aes_signing_key,
|
||||
"",
|
||||
|
|
|
@ -16,6 +16,7 @@ Muxer::Muxer(const MuxerOptions& options)
|
|||
: options_(options),
|
||||
encryptor_source_(NULL),
|
||||
initialized_(false),
|
||||
track_type_(EncryptorSource::TRACK_TYPE_SD),
|
||||
clear_lead_in_seconds_(0),
|
||||
muxer_listener_(NULL),
|
||||
clock_(NULL) {}
|
||||
|
@ -23,8 +24,10 @@ Muxer::Muxer(const MuxerOptions& options)
|
|||
Muxer::~Muxer() {}
|
||||
|
||||
void Muxer::SetEncryptorSource(EncryptorSource* encryptor_source,
|
||||
EncryptorSource::TrackType track_type,
|
||||
double clear_lead_in_seconds) {
|
||||
encryptor_source_ = encryptor_source;
|
||||
track_type_ = track_type;
|
||||
clear_lead_in_seconds_ = clear_lead_in_seconds;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "media/base/encryptor_source.h"
|
||||
#include "media/base/muxer_options.h"
|
||||
#include "media/base/status.h"
|
||||
|
||||
|
@ -41,8 +42,11 @@ class Muxer {
|
|||
/// Set encryptor source.
|
||||
/// @param encryptor_source points to the encryptor source to be injected.
|
||||
/// 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.
|
||||
void SetEncryptorSource(EncryptorSource* encryptor_source,
|
||||
EncryptorSource::TrackType track_type,
|
||||
double clear_lead_in_seconds);
|
||||
|
||||
/// Add video/audio stream.
|
||||
|
@ -70,6 +74,7 @@ class Muxer {
|
|||
protected:
|
||||
const MuxerOptions& options() const { return options_; }
|
||||
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_; }
|
||||
event::MuxerListener* muxer_listener() { return muxer_listener_; }
|
||||
base::Clock* clock() { return clock_; }
|
||||
|
@ -95,6 +100,7 @@ class Muxer {
|
|||
std::vector<MediaStream*> streams_;
|
||||
EncryptorSource* encryptor_source_;
|
||||
bool initialized_;
|
||||
EncryptorSource::TrackType track_type_;
|
||||
double clear_lead_in_seconds_;
|
||||
|
||||
event::MuxerListener* muxer_listener_;
|
||||
|
|
|
@ -90,6 +90,12 @@ ProtectionSystemSpecificHeader::~ProtectionSystemSpecificHeader() {}
|
|||
FourCC ProtectionSystemSpecificHeader::BoxType() const { return FOURCC_PSSH; }
|
||||
|
||||
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();
|
||||
RCHECK(FullBox::ReadWrite(buffer) &&
|
||||
buffer->ReadWriteVector(&system_id, 16) &&
|
||||
|
@ -108,6 +114,9 @@ bool ProtectionSystemSpecificHeader::ReadWrite(BoxBuffer* buffer) {
|
|||
}
|
||||
|
||||
uint32 ProtectionSystemSpecificHeader::ComputeSize() {
|
||||
if (!raw_box.empty())
|
||||
atom_size = raw_box.size();
|
||||
else
|
||||
atom_size = kFullBoxSize + system_id.size() + sizeof(uint32) + data.size();
|
||||
return atom_size;
|
||||
}
|
||||
|
|
|
@ -733,7 +733,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
typedef testing::Types<
|
||||
FileType,
|
||||
SegmentType,
|
||||
// ProtectionSystemSpecificHeader, // Not fully parsed.
|
||||
ProtectionSystemSpecificHeader,
|
||||
SampleAuxiliaryInformationOffset,
|
||||
SampleAuxiliaryInformationSize,
|
||||
OriginalFormat,
|
||||
|
@ -854,8 +854,17 @@ TEST_F(BoxDefinitionsTest, ProtectionSystemSpecificHeader) {
|
|||
|
||||
ProtectionSystemSpecificHeader pssh_readback;
|
||||
ASSERT_TRUE(ReadBack(&pssh_readback));
|
||||
// PSSH does not parse data.
|
||||
ASSERT_EQ(pssh.system_id, pssh_readback.system_id);
|
||||
ASSERT_EQ(pssh, pssh_readback);
|
||||
|
||||
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) {
|
||||
|
|
|
@ -23,9 +23,6 @@
|
|||
#include "media/formats/mp4/single_segment_segmenter.h"
|
||||
|
||||
namespace {
|
||||
// The version of cenc implemented here. CENC 4.
|
||||
const int kCencSchemeVersion = 0x00010000;
|
||||
|
||||
// Sets the range start and end value from offset and size.
|
||||
// |start| and |end| are for byte-range-spec specified in RFC2616.
|
||||
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) {
|
||||
segmenter_.reset(
|
||||
new SingleSegmentSegmenter(options(), ftyp.Pass(), moov.Pass()));
|
||||
|
@ -107,7 +99,7 @@ Status MP4Muxer::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())
|
||||
return segmenter_initialized;
|
||||
|
@ -169,17 +161,6 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
|
|||
trak->media.information.sample_table.description;
|
||||
sample_description.type = kVideo;
|
||||
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,
|
||||
|
@ -209,33 +190,6 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
|
|||
trak->media.information.sample_table.description;
|
||||
sample_description.type = kAudio;
|
||||
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) {
|
||||
|
@ -320,7 +274,7 @@ void MP4Muxer::FireOnMediaEndEvent() {
|
|||
index_range_end,
|
||||
duration_seconds,
|
||||
file_size,
|
||||
IsEncryptionRequired());
|
||||
encryptor_source());
|
||||
}
|
||||
|
||||
uint64 MP4Muxer::IsoTimeNow() {
|
||||
|
|
|
@ -50,15 +50,6 @@ class MP4Muxer : public Muxer {
|
|||
Track* trak,
|
||||
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.
|
||||
void GetStreamInfo(std::vector<StreamInfo*>* stream_infos);
|
||||
|
||||
|
|
|
@ -30,34 +30,6 @@ MultiSegmentSegmenter::MultiSegmentSegmenter(const MuxerOptions& options,
|
|||
|
||||
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) {
|
||||
DLOG(INFO) << "MultiSegmentSegmenter outputs init segment: "
|
||||
<< options().output_file_name;
|
||||
|
@ -69,11 +41,31 @@ bool MultiSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
|
|||
return false;
|
||||
}
|
||||
|
||||
Status MultiSegmentSegmenter::FinalizeSegment() {
|
||||
Status status = Segmenter::FinalizeSegment();
|
||||
if (!status.ok())
|
||||
Status MultiSegmentSegmenter::DoInitialize() {
|
||||
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 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());
|
||||
// earliest_presentation_time is the earliest presentation time of any
|
||||
// access unit in the reference stream in the first subsegment.
|
||||
|
|
|
@ -36,18 +36,16 @@ class MultiSegmentSegmenter : public Segmenter {
|
|||
|
||||
/// @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 GetIndexRange(size_t* offset, size_t* size) OVERRIDE;
|
||||
/// @}
|
||||
|
||||
protected:
|
||||
virtual Status FinalizeSegment() OVERRIDE;
|
||||
|
||||
private:
|
||||
// Segmenter implementation overrides.
|
||||
virtual Status DoInitialize() OVERRIDE;
|
||||
virtual Status DoFinalize() OVERRIDE;
|
||||
virtual Status DoFinalizeSegment() OVERRIDE;
|
||||
|
||||
// Write segment to file.
|
||||
Status WriteSegment();
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "base/stl_util.h"
|
||||
#include "media/base/aes_encryptor.h"
|
||||
#include "media/base/buffer_writer.h"
|
||||
#include "media/base/encryptor_source.h"
|
||||
#include "media/base/media_sample.h"
|
||||
|
@ -18,14 +19,79 @@
|
|||
#include "media/formats/mp4/box_definitions.h"
|
||||
#include "media/formats/mp4/fragmenter.h"
|
||||
|
||||
namespace media {
|
||||
namespace mp4 {
|
||||
|
||||
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) {
|
||||
return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace media {
|
||||
namespace mp4 {
|
||||
scoped_ptr<AesCtrEncryptor> CreateEncryptor(
|
||||
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,
|
||||
scoped_ptr<FileType> ftyp,
|
||||
|
@ -41,9 +107,10 @@ Segmenter::Segmenter(const MuxerOptions& options,
|
|||
|
||||
Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
|
||||
|
||||
Status Segmenter::Initialize(EncryptorSource* encryptor_source,
|
||||
double clear_lead_in_seconds,
|
||||
const std::vector<MediaStream*>& streams) {
|
||||
Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||
EncryptorSource* encryptor_source,
|
||||
EncryptorSource::TrackType track_type,
|
||||
double clear_lead_in_seconds) {
|
||||
DCHECK_LT(0u, streams.size());
|
||||
moof_->header.sequence_number = 0;
|
||||
|
||||
|
@ -64,7 +131,30 @@ Status Segmenter::Initialize(EncryptorSource* encryptor_source,
|
|||
}
|
||||
scoped_ptr<AesCtrEncryptor> encryptor;
|
||||
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)
|
||||
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.
|
||||
moov_->header.timescale = sidx_->timescale;
|
||||
InitializeFragments();
|
||||
return Status::OK;
|
||||
return DoInitialize();
|
||||
}
|
||||
|
||||
Status Segmenter::Finalize() {
|
||||
|
@ -110,7 +200,7 @@ Status Segmenter::Finalize() {
|
|||
moov_->header.duration = track->header.duration;
|
||||
}
|
||||
|
||||
return Status::OK;
|
||||
return DoFinalize();
|
||||
}
|
||||
|
||||
Status Segmenter::AddSample(const MediaStream* stream,
|
||||
|
@ -191,7 +281,7 @@ void Segmenter::InitializeSegment() {
|
|||
|
||||
Status Segmenter::FinalizeSegment() {
|
||||
segment_initialized_ = false;
|
||||
return Status::OK;
|
||||
return DoFinalizeSegment();
|
||||
}
|
||||
|
||||
uint32 Segmenter::GetReferenceStreamId() {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "media/base/encryptor_source.h"
|
||||
#include "media/base/status.h"
|
||||
|
||||
namespace media {
|
||||
|
@ -47,16 +48,29 @@ class Segmenter {
|
|||
|
||||
/// Initialize the segmenter.
|
||||
/// Calling other public methods of this class without this method returning
|
||||
/// Status::OK, results in an undefined behavior.
|
||||
/// @param encryptor_source can be NULL.
|
||||
/// @return Status::OK on success.
|
||||
virtual Status Initialize(EncryptorSource* encryptor_source,
|
||||
double clear_lead_in_seconds,
|
||||
const std::vector<MediaStream*>& streams);
|
||||
/// Status::OK results in an undefined behavior.
|
||||
/// @param encryptor_source points to the key source which contains
|
||||
/// the encryption keys. It can be NULL to indicate that no encryption
|
||||
/// is required.
|
||||
/// @param track_type indicates whether SD key or HD key should be used to
|
||||
/// 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.
|
||||
/// @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
|
||||
|
@ -73,11 +87,6 @@ class Segmenter {
|
|||
double GetDuration() const;
|
||||
|
||||
protected:
|
||||
void InitializeSegment();
|
||||
virtual Status FinalizeSegment();
|
||||
|
||||
uint32 GetReferenceStreamId();
|
||||
|
||||
const MuxerOptions& options() const { return options_; }
|
||||
FileType* ftyp() { return ftyp_.get(); }
|
||||
Movie* moov() { return moov_.get(); }
|
||||
|
@ -85,6 +94,14 @@ class Segmenter {
|
|||
SegmentIndex* sidx() { return sidx_.get(); }
|
||||
|
||||
private:
|
||||
virtual Status DoInitialize() = 0;
|
||||
virtual Status DoFinalize() = 0;
|
||||
virtual Status DoFinalizeSegment() = 0;
|
||||
|
||||
void InitializeSegment();
|
||||
Status FinalizeSegment();
|
||||
uint32 GetReferenceStreamId();
|
||||
|
||||
void InitializeFragments();
|
||||
Status FinalizeFragment(Fragmenter* fragment);
|
||||
|
||||
|
|
|
@ -21,27 +21,30 @@ SingleSegmentSegmenter::SingleSegmentSegmenter(const MuxerOptions& options,
|
|||
: Segmenter(options, ftyp.Pass(), moov.Pass()) {}
|
||||
SingleSegmentSegmenter::~SingleSegmentSegmenter() {}
|
||||
|
||||
Status SingleSegmentSegmenter::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;
|
||||
bool SingleSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) {
|
||||
// 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::DoInitialize() {
|
||||
temp_file_.reset(File::Open(options().temp_file_name.c_str(), "w"));
|
||||
if (temp_file_ == NULL) {
|
||||
return Status(error::FILE_FAILURE,
|
||||
return temp_file_
|
||||
? Status::OK
|
||||
: Status(error::FILE_FAILURE,
|
||||
"Cannot open file to write " + options().temp_file_name);
|
||||
}
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
Status SingleSegmentSegmenter::Finalize() {
|
||||
Status status = Segmenter::Finalize();
|
||||
if (!status.ok())
|
||||
return status;
|
||||
|
||||
Status SingleSegmentSegmenter::DoFinalize() {
|
||||
DCHECK(temp_file_);
|
||||
DCHECK(ftyp());
|
||||
DCHECK(moov());
|
||||
|
@ -65,7 +68,7 @@ Status SingleSegmentSegmenter::Finalize() {
|
|||
ftyp()->Write(buffer.get());
|
||||
moov()->Write(buffer.get());
|
||||
vod_sidx_->Write(buffer.get());
|
||||
status = buffer->WriteToFile(file.get());
|
||||
Status status = buffer->WriteToFile(file.get());
|
||||
if (!status.ok())
|
||||
return status;
|
||||
|
||||
|
@ -94,26 +97,7 @@ Status SingleSegmentSegmenter::Finalize() {
|
|||
return Status::OK;
|
||||
}
|
||||
|
||||
bool SingleSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) {
|
||||
// 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;
|
||||
|
||||
Status SingleSegmentSegmenter::DoFinalizeSegment() {
|
||||
DCHECK(sidx());
|
||||
DCHECK(fragment_buffer());
|
||||
// sidx() contains pre-generated segment references with one reference per
|
||||
|
|
|
@ -33,19 +33,16 @@ class SingleSegmentSegmenter : public Segmenter {
|
|||
|
||||
/// @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 GetIndexRange(size_t* offset, size_t* size) OVERRIDE;
|
||||
/// @}
|
||||
|
||||
protected:
|
||||
virtual Status FinalizeSegment() OVERRIDE;
|
||||
|
||||
private:
|
||||
// Segmenter implementation overrides.
|
||||
virtual Status DoInitialize() OVERRIDE;
|
||||
virtual Status DoFinalize() OVERRIDE;
|
||||
virtual Status DoFinalizeSegment() OVERRIDE;
|
||||
|
||||
scoped_ptr<SegmentIndex> vod_sidx_;
|
||||
scoped_ptr<File, FileCloser> temp_file_;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "base/strings/stringprintf.h"
|
||||
#include "base/time/clock.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/muxer.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_);
|
||||
ASSERT_OK(demuxer.Initialize());
|
||||
|
||||
FixedEncryptorSource encryptor_source(kKeyIdHex, kKeyHex, kPsshHex);
|
||||
ASSERT_OK(encryptor_source.Initialize());
|
||||
scoped_ptr<EncryptorSource> encryptor_source(
|
||||
EncryptorSource::CreateFromHexStrings(kKeyIdHex, kKeyHex, kPsshHex, ""));
|
||||
DCHECK(encryptor_source);
|
||||
|
||||
scoped_ptr<Muxer> muxer_video;
|
||||
if (!video_output.empty()) {
|
||||
|
@ -159,8 +160,11 @@ void PackagerTestBasic::Remux(const std::string& input,
|
|||
|
||||
muxer_video->AddStream(FindFirstVideoStream(demuxer.streams()));
|
||||
|
||||
if (enable_encryption)
|
||||
muxer_video->SetEncryptorSource(&encryptor_source, kClearLeadInSeconds);
|
||||
if (enable_encryption) {
|
||||
muxer_video->SetEncryptorSource(encryptor_source.get(),
|
||||
EncryptorSource::TRACK_TYPE_SD,
|
||||
kClearLeadInSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
scoped_ptr<Muxer> muxer_audio;
|
||||
|
@ -171,8 +175,11 @@ void PackagerTestBasic::Remux(const std::string& input,
|
|||
|
||||
muxer_audio->AddStream(FindFirstAudioStream(demuxer.streams()));
|
||||
|
||||
if (enable_encryption)
|
||||
muxer_video->SetEncryptorSource(&encryptor_source, kClearLeadInSeconds);
|
||||
if (enable_encryption) {
|
||||
muxer_audio->SetEncryptorSource(encryptor_source.get(),
|
||||
EncryptorSource::TRACK_TYPE_SD,
|
||||
kClearLeadInSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
// Start remuxing process.
|
||||
|
|
Loading…
Reference in New Issue