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/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();
|
||||||
|
|
|
@ -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,
|
||||||
"",
|
"",
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue