Remove EncryptorSource argument from Muxer constructor.
Add a new function Muxer::SetEncryptorSource. Also clean up packager test. Change-Id: I5fee46e3d15e0c7a0f138c1d90f980b724887768
This commit is contained in:
parent
b6af6ca976
commit
57ca7d2144
|
@ -10,11 +10,17 @@
|
||||||
|
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
Muxer::Muxer(const MuxerOptions& options, EncryptorSource* encrytor_source)
|
Muxer::Muxer(const MuxerOptions& options)
|
||||||
: options_(options), encryptor_source_(encrytor_source) {}
|
: options_(options), encryptor_source_(NULL), clear_lead_in_seconds_(0) {}
|
||||||
|
|
||||||
Muxer::~Muxer() {}
|
Muxer::~Muxer() {}
|
||||||
|
|
||||||
|
void Muxer::SetEncryptorSource(EncryptorSource* encryptor_source,
|
||||||
|
double clear_lead_in_seconds) {
|
||||||
|
encryptor_source_ = encryptor_source;
|
||||||
|
clear_lead_in_seconds_ = clear_lead_in_seconds;
|
||||||
|
}
|
||||||
|
|
||||||
Status Muxer::AddStream(MediaStream* stream) {
|
Status Muxer::AddStream(MediaStream* stream) {
|
||||||
stream->Connect(this);
|
stream->Connect(this);
|
||||||
streams_.push_back(stream);
|
streams_.push_back(stream);
|
||||||
|
|
|
@ -22,9 +22,14 @@ class MediaStream;
|
||||||
|
|
||||||
class Muxer {
|
class Muxer {
|
||||||
public:
|
public:
|
||||||
Muxer(const MuxerOptions& options, EncryptorSource* encryptor_source);
|
explicit Muxer(const MuxerOptions& options);
|
||||||
virtual ~Muxer();
|
virtual ~Muxer();
|
||||||
|
|
||||||
|
// Set encryptor source. Caller retains ownership of |encryptor_source|.
|
||||||
|
// Should be called before calling Initialize().
|
||||||
|
void SetEncryptorSource(EncryptorSource* encryptor_source,
|
||||||
|
double clear_lead_in_seconds);
|
||||||
|
|
||||||
// Initialize the muxer. Must be called after connecting all the streams.
|
// Initialize the muxer. Must be called after connecting all the streams.
|
||||||
virtual Status Initialize() = 0;
|
virtual Status Initialize() = 0;
|
||||||
|
|
||||||
|
@ -46,11 +51,13 @@ 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_; }
|
||||||
|
double clear_lead_in_seconds() const { return clear_lead_in_seconds_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MuxerOptions options_;
|
MuxerOptions options_;
|
||||||
std::vector<MediaStream*> streams_;
|
std::vector<MediaStream*> streams_;
|
||||||
EncryptorSource* const encryptor_source_;
|
EncryptorSource* encryptor_source_;
|
||||||
|
double clear_lead_in_seconds_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Muxer);
|
DISALLOW_COPY_AND_ASSIGN(Muxer);
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include "media/base/aes_encryptor.h"
|
#include "media/base/aes_encryptor.h"
|
||||||
#include "media/base/buffer_reader.h"
|
#include "media/base/buffer_reader.h"
|
||||||
#include "media/base/buffer_writer.h"
|
#include "media/base/buffer_writer.h"
|
||||||
#include "media/base/encryptor_source.h"
|
|
||||||
#include "media/base/media_sample.h"
|
#include "media/base/media_sample.h"
|
||||||
#include "media/mp4/box_definitions.h"
|
#include "media/mp4/box_definitions.h"
|
||||||
#include "media/mp4/cenc.h"
|
#include "media/mp4/cenc.h"
|
||||||
|
@ -40,10 +39,10 @@ namespace media {
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
MP4Fragmenter::MP4Fragmenter(TrackFragment* traf,
|
MP4Fragmenter::MP4Fragmenter(TrackFragment* traf,
|
||||||
EncryptorSource* encryptor_source,
|
scoped_ptr<AesCtrEncryptor> encryptor,
|
||||||
int64 clear_time,
|
int64 clear_time,
|
||||||
uint8 nalu_length_size)
|
uint8 nalu_length_size)
|
||||||
: encryptor_source_(encryptor_source),
|
: encryptor_(encryptor.Pass()),
|
||||||
nalu_length_size_(nalu_length_size),
|
nalu_length_size_(nalu_length_size),
|
||||||
traf_(traf),
|
traf_(traf),
|
||||||
fragment_finalized_(false),
|
fragment_finalized_(false),
|
||||||
|
@ -106,16 +105,15 @@ void MP4Fragmenter::InitializeFragment() {
|
||||||
|
|
||||||
if (ShouldEncryptFragment()) {
|
if (ShouldEncryptFragment()) {
|
||||||
if (!IsSubsampleEncryptionRequired()) {
|
if (!IsSubsampleEncryptionRequired()) {
|
||||||
DCHECK(encryptor_source_ != NULL);
|
DCHECK(encryptor_);
|
||||||
traf_->auxiliary_size.default_sample_info_size =
|
traf_->auxiliary_size.default_sample_info_size = encryptor_->iv().size();
|
||||||
encryptor_source_->encryptor()->iv().size();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MP4Fragmenter::FinalizeFragment() {
|
void MP4Fragmenter::FinalizeFragment() {
|
||||||
if (ShouldEncryptFragment()) {
|
if (ShouldEncryptFragment()) {
|
||||||
DCHECK(encryptor_source_ != NULL);
|
DCHECK(encryptor_);
|
||||||
|
|
||||||
// The offset will be adjusted in Segmenter when we know moof size.
|
// The offset will be adjusted in Segmenter when we know moof size.
|
||||||
traf_->auxiliary_offset.offsets.push_back(0);
|
traf_->auxiliary_offset.offsets.push_back(0);
|
||||||
|
@ -129,7 +127,7 @@ void MP4Fragmenter::FinalizeFragment() {
|
||||||
saiz.default_sample_info_size = 0;
|
saiz.default_sample_info_size = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (encryptor_source_ && clear_time_ > 0) {
|
} else if (encryptor_ && clear_time_ > 0) {
|
||||||
// This fragment should be in clear.
|
// This fragment should be in clear.
|
||||||
// We generate at most two sample description entries, encrypted entry and
|
// We generate at most two sample description entries, encrypted entry and
|
||||||
// clear entry. The 1-based clear entry index is always 2.
|
// clear entry. The 1-based clear entry index is always 2.
|
||||||
|
@ -182,13 +180,14 @@ void MP4Fragmenter::GenerateSegmentReference(SegmentReference* reference) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MP4Fragmenter::EncryptBytes(uint8* data, uint32 size) {
|
void MP4Fragmenter::EncryptBytes(uint8* data, uint32 size) {
|
||||||
CHECK(encryptor_source_->encryptor()->Encrypt(data, size, data));
|
DCHECK(encryptor_);
|
||||||
|
CHECK(encryptor_->Encrypt(data, size, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
Status MP4Fragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
Status MP4Fragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
||||||
DCHECK(encryptor_source_ != NULL && encryptor_source_->encryptor() != NULL);
|
DCHECK(encryptor_);
|
||||||
|
|
||||||
FrameCENCInfo cenc_info(encryptor_source_->encryptor()->iv());
|
FrameCENCInfo cenc_info(encryptor_->iv());
|
||||||
uint8* data = sample->writable_data();
|
uint8* data = sample->writable_data();
|
||||||
if (!IsSubsampleEncryptionRequired()) {
|
if (!IsSubsampleEncryptionRequired()) {
|
||||||
EncryptBytes(data, sample->data_size());
|
EncryptBytes(data, sample->data_size());
|
||||||
|
@ -217,7 +216,7 @@ Status MP4Fragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cenc_info.Write(aux_data_.get());
|
cenc_info.Write(aux_data_.get());
|
||||||
encryptor_source_->encryptor()->UpdateIv();
|
encryptor_->UpdateIv();
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
|
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
class AesCtrEncryptor;
|
||||||
class BufferWriter;
|
class BufferWriter;
|
||||||
class EncryptorSource;
|
|
||||||
class MediaSample;
|
class MediaSample;
|
||||||
|
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
@ -28,12 +28,12 @@ class TrackFragment;
|
||||||
|
|
||||||
class MP4Fragmenter {
|
class MP4Fragmenter {
|
||||||
public:
|
public:
|
||||||
// Caller retains the ownership of |traf| and |encryptor_source|.
|
// Caller retains the ownership of |traf| and transfers ownership of
|
||||||
// |clear_time| specifies clear time in the current track timescale.
|
// |encryptor|. |clear_time| specifies clear time in the current track
|
||||||
// |nalu_length_size| specifies NAL unit length size, for subsample
|
// timescale. |nalu_length_size| specifies NAL unit length size, for
|
||||||
// encryption.
|
// subsample encryption.
|
||||||
MP4Fragmenter(TrackFragment* traf,
|
MP4Fragmenter(TrackFragment* traf,
|
||||||
EncryptorSource* encryptor_source,
|
scoped_ptr<AesCtrEncryptor> encryptor,
|
||||||
int64 clear_time,
|
int64 clear_time,
|
||||||
uint8 nalu_length_size);
|
uint8 nalu_length_size);
|
||||||
~MP4Fragmenter();
|
~MP4Fragmenter();
|
||||||
|
@ -64,7 +64,7 @@ class MP4Fragmenter {
|
||||||
|
|
||||||
// Should we enable encrytion for the current fragment?
|
// Should we enable encrytion for the current fragment?
|
||||||
bool ShouldEncryptFragment() {
|
bool ShouldEncryptFragment() {
|
||||||
return (encryptor_source_ != NULL && clear_time_ <= 0);
|
return (encryptor_ != NULL && clear_time_ <= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should we enable subsample encryption?
|
// Should we enable subsample encryption?
|
||||||
|
@ -73,7 +73,7 @@ class MP4Fragmenter {
|
||||||
// Check if the current fragment starts with SAP.
|
// Check if the current fragment starts with SAP.
|
||||||
bool StartsWithSAP();
|
bool StartsWithSAP();
|
||||||
|
|
||||||
EncryptorSource* encryptor_source_;
|
scoped_ptr<AesCtrEncryptor> encryptor_;
|
||||||
// If this stream contains AVC, subsample encryption specifies that the size
|
// If this stream contains AVC, subsample encryption specifies that the size
|
||||||
// and type of NAL units remain unencrypted. This field specifies the size of
|
// and type of NAL units remain unencrypted. This field specifies the size of
|
||||||
// the size field. Can be 1, 2 or 4 bytes.
|
// the size field. Can be 1, 2 or 4 bytes.
|
||||||
|
|
|
@ -30,8 +30,10 @@ MP4GeneralSegmenter::~MP4GeneralSegmenter() {}
|
||||||
|
|
||||||
Status MP4GeneralSegmenter::Initialize(
|
Status MP4GeneralSegmenter::Initialize(
|
||||||
EncryptorSource* encryptor_source,
|
EncryptorSource* encryptor_source,
|
||||||
|
double clear_lead_in_seconds,
|
||||||
const std::vector<MediaStream*>& streams) {
|
const std::vector<MediaStream*>& streams) {
|
||||||
Status status = MP4Segmenter::Initialize(encryptor_source, streams);
|
Status status = MP4Segmenter::Initialize(
|
||||||
|
encryptor_source, clear_lead_in_seconds, streams);
|
||||||
if (!status.ok())
|
if (!status.ok())
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ class MP4GeneralSegmenter : public MP4Segmenter {
|
||||||
|
|
||||||
// MP4Segmenter implementations.
|
// MP4Segmenter implementations.
|
||||||
virtual Status Initialize(EncryptorSource* encryptor_source,
|
virtual Status Initialize(EncryptorSource* encryptor_source,
|
||||||
|
double clear_lead_in_seconds,
|
||||||
const std::vector<MediaStream*>& streams) OVERRIDE;
|
const std::vector<MediaStream*>& streams) OVERRIDE;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -33,9 +33,7 @@ uint64 IsoTimeNow() {
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
MP4Muxer::MP4Muxer(const MuxerOptions& options,
|
MP4Muxer::MP4Muxer(const MuxerOptions& options) : Muxer(options) {}
|
||||||
EncryptorSource* encryptor_source)
|
|
||||||
: Muxer(options, encryptor_source) {}
|
|
||||||
MP4Muxer::~MP4Muxer() {}
|
MP4Muxer::~MP4Muxer() {}
|
||||||
|
|
||||||
Status MP4Muxer::Initialize() {
|
Status MP4Muxer::Initialize() {
|
||||||
|
@ -98,7 +96,8 @@ Status MP4Muxer::Initialize() {
|
||||||
segmenter_.reset(
|
segmenter_.reset(
|
||||||
new MP4GeneralSegmenter(options(), ftyp.Pass(), moov.Pass()));
|
new MP4GeneralSegmenter(options(), ftyp.Pass(), moov.Pass()));
|
||||||
}
|
}
|
||||||
return segmenter_->Initialize(encryptor_source(), streams());
|
return segmenter_->Initialize(
|
||||||
|
encryptor_source(), clear_lead_in_seconds(), streams());
|
||||||
}
|
}
|
||||||
|
|
||||||
Status MP4Muxer::Finalize() {
|
Status MP4Muxer::Finalize() {
|
||||||
|
@ -152,7 +151,7 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
|
||||||
if (IsEncryptionRequired()) {
|
if (IsEncryptionRequired()) {
|
||||||
DCHECK(encryptor_source() != NULL);
|
DCHECK(encryptor_source() != NULL);
|
||||||
// Add a second entry for clear content if needed.
|
// Add a second entry for clear content if needed.
|
||||||
if (encryptor_source()->clear_milliseconds() > 0)
|
if (clear_lead_in_seconds() > 0)
|
||||||
sample_description.video_entries.push_back(video);
|
sample_description.video_entries.push_back(video);
|
||||||
|
|
||||||
VideoSampleEntry& encrypted_video = sample_description.video_entries[0];
|
VideoSampleEntry& encrypted_video = sample_description.video_entries[0];
|
||||||
|
@ -192,7 +191,7 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
|
||||||
if (IsEncryptionRequired()) {
|
if (IsEncryptionRequired()) {
|
||||||
DCHECK(encryptor_source() != NULL);
|
DCHECK(encryptor_source() != NULL);
|
||||||
// Add a second entry for clear content if needed.
|
// Add a second entry for clear content if needed.
|
||||||
if (encryptor_source()->clear_milliseconds() > 0)
|
if (clear_lead_in_seconds() > 0)
|
||||||
sample_description.audio_entries.push_back(audio);
|
sample_description.audio_entries.push_back(audio);
|
||||||
|
|
||||||
AudioSampleEntry& encrypted_audio = sample_description.audio_entries[0];
|
AudioSampleEntry& encrypted_audio = sample_description.audio_entries[0];
|
||||||
|
@ -209,13 +208,11 @@ void MP4Muxer::GeneratePssh(ProtectionSystemSpecificHeader* pssh) {
|
||||||
|
|
||||||
void MP4Muxer::GenerateSinf(ProtectionSchemeInfo* sinf, FourCC old_type) {
|
void MP4Muxer::GenerateSinf(ProtectionSchemeInfo* sinf, FourCC old_type) {
|
||||||
DCHECK(encryptor_source() != NULL);
|
DCHECK(encryptor_source() != NULL);
|
||||||
DCHECK(encryptor_source()->encryptor() != NULL);
|
|
||||||
sinf->format.format = old_type;
|
sinf->format.format = old_type;
|
||||||
sinf->type.type = FOURCC_CENC;
|
sinf->type.type = FOURCC_CENC;
|
||||||
sinf->type.version = kCencSchemeVersion;
|
sinf->type.version = kCencSchemeVersion;
|
||||||
sinf->info.track_encryption.is_encrypted = true;
|
sinf->info.track_encryption.is_encrypted = true;
|
||||||
sinf->info.track_encryption.default_iv_size =
|
sinf->info.track_encryption.default_iv_size = encryptor_source()->iv_size();
|
||||||
encryptor_source()->encryptor()->iv().size();
|
|
||||||
sinf->info.track_encryption.default_kid = encryptor_source()->key_id();
|
sinf->info.track_encryption.default_kid = encryptor_source()->key_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ struct Track;
|
||||||
|
|
||||||
class MP4Muxer : public Muxer {
|
class MP4Muxer : public Muxer {
|
||||||
public:
|
public:
|
||||||
MP4Muxer(const MuxerOptions& options, EncryptorSource* encryptor_source);
|
explicit MP4Muxer(const MuxerOptions& options);
|
||||||
virtual ~MP4Muxer();
|
virtual ~MP4Muxer();
|
||||||
|
|
||||||
// Muxer implementations.
|
// Muxer implementations.
|
||||||
|
|
|
@ -40,6 +40,7 @@ MP4Segmenter::MP4Segmenter(const MuxerOptions& options,
|
||||||
MP4Segmenter::~MP4Segmenter() { STLDeleteElements(&fragmenters_); }
|
MP4Segmenter::~MP4Segmenter() { STLDeleteElements(&fragmenters_); }
|
||||||
|
|
||||||
Status MP4Segmenter::Initialize(EncryptorSource* encryptor_source,
|
Status MP4Segmenter::Initialize(EncryptorSource* encryptor_source,
|
||||||
|
double clear_lead_in_seconds,
|
||||||
const std::vector<MediaStream*>& streams) {
|
const std::vector<MediaStream*>& streams) {
|
||||||
DCHECK_LT(0, streams.size());
|
DCHECK_LT(0, streams.size());
|
||||||
moof_->header.sequence_number = 0;
|
moof_->header.sequence_number = 0;
|
||||||
|
@ -59,13 +60,17 @@ Status MP4Segmenter::Initialize(EncryptorSource* encryptor_source,
|
||||||
if (sidx_->reference_id == 0)
|
if (sidx_->reference_id == 0)
|
||||||
sidx_->reference_id = i + 1;
|
sidx_->reference_id = i + 1;
|
||||||
}
|
}
|
||||||
int64 clear_time = 0;
|
scoped_ptr<AesCtrEncryptor> encryptor;
|
||||||
if (encryptor_source) {
|
if (encryptor_source) {
|
||||||
clear_time = encryptor_source->clear_milliseconds() / 1000.0 *
|
encryptor = encryptor_source->CreateEncryptor();
|
||||||
streams[i]->info()->time_scale();
|
if (!encryptor)
|
||||||
|
return Status(error::MUXER_FAILURE, "Failed to create the encryptor.");
|
||||||
}
|
}
|
||||||
fragmenters_[i] = new MP4Fragmenter(
|
fragmenters_[i] = new MP4Fragmenter(
|
||||||
&moof_->tracks[i], encryptor_source, clear_time, nalu_length_size);
|
&moof_->tracks[i],
|
||||||
|
encryptor.Pass(),
|
||||||
|
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
||||||
|
nalu_length_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose the first stream if there is no VIDEO.
|
// Choose the first stream if there is no VIDEO.
|
||||||
|
|
|
@ -48,6 +48,7 @@ class MP4Segmenter {
|
||||||
// Initialize the segmenter. Caller retains the ownership of
|
// Initialize the segmenter. Caller retains the ownership of
|
||||||
// |encryptor_source|. |encryptor_source| can be NULL.
|
// |encryptor_source|. |encryptor_source| can be NULL.
|
||||||
virtual Status Initialize(EncryptorSource* encryptor_source,
|
virtual Status Initialize(EncryptorSource* encryptor_source,
|
||||||
|
double clear_lead_in_seconds,
|
||||||
const std::vector<MediaStream*>& streams);
|
const std::vector<MediaStream*>& streams);
|
||||||
|
|
||||||
virtual Status Finalize();
|
virtual Status Finalize();
|
||||||
|
|
|
@ -20,8 +20,10 @@ MP4VODSegmenter::MP4VODSegmenter(const MuxerOptions& options,
|
||||||
MP4VODSegmenter::~MP4VODSegmenter() {}
|
MP4VODSegmenter::~MP4VODSegmenter() {}
|
||||||
|
|
||||||
Status MP4VODSegmenter::Initialize(EncryptorSource* encryptor_source,
|
Status MP4VODSegmenter::Initialize(EncryptorSource* encryptor_source,
|
||||||
|
double clear_lead_in_seconds,
|
||||||
const std::vector<MediaStream*>& streams) {
|
const std::vector<MediaStream*>& streams) {
|
||||||
Status status = MP4Segmenter::Initialize(encryptor_source, streams);
|
Status status = MP4Segmenter::Initialize(
|
||||||
|
encryptor_source, clear_lead_in_seconds, streams);
|
||||||
if (!status.ok())
|
if (!status.ok())
|
||||||
return status;
|
return status;
|
||||||
temp_file_.reset(File::Open(options().temp_file_name.c_str(), "w"));
|
temp_file_.reset(File::Open(options().temp_file_name.c_str(), "w"));
|
||||||
|
|
|
@ -33,6 +33,7 @@ class MP4VODSegmenter : public MP4Segmenter {
|
||||||
|
|
||||||
// MP4Segmenter implementations.
|
// MP4Segmenter implementations.
|
||||||
virtual Status Initialize(EncryptorSource* encryptor_source,
|
virtual Status Initialize(EncryptorSource* encryptor_source,
|
||||||
|
double clear_lead_in_seconds,
|
||||||
const std::vector<MediaStream*>& streams) OVERRIDE;
|
const std::vector<MediaStream*>& streams) OVERRIDE;
|
||||||
virtual Status Finalize() OVERRIDE;
|
virtual Status Finalize() OVERRIDE;
|
||||||
|
|
||||||
|
|
|
@ -2,26 +2,35 @@
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "base/file_util.h"
|
||||||
#include "base/strings/string_number_conversions.h"
|
#include "base/strings/string_number_conversions.h"
|
||||||
#include "media/base/demuxer.h"
|
#include "media/base/demuxer.h"
|
||||||
#include "media/base/fixed_encryptor_source.h"
|
#include "media/base/fixed_encryptor_source.h"
|
||||||
#include "media/base/media_sample.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/muxer_options.h"
|
|
||||||
#include "media/base/status_test_util.h"
|
#include "media/base/status_test_util.h"
|
||||||
#include "media/base/stream_info.h"
|
#include "media/base/stream_info.h"
|
||||||
#include "media/mp4/mp4_muxer.h"
|
#include "media/mp4/mp4_muxer.h"
|
||||||
#include "media/test/test_data_util.h"
|
#include "media/test/test_data_util.h"
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
using ::testing::Combine;
|
|
||||||
using ::testing::Values;
|
|
||||||
using ::testing::ValuesIn;
|
using ::testing::ValuesIn;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const char* kMediaFiles[] = {"bear-1280x720.mp4", "bear-1280x720-av_frag.mp4"};
|
const char* kMediaFiles[] = {"bear-1280x720.mp4", "bear-1280x720-av_frag.mp4"};
|
||||||
|
|
||||||
|
// Muxer options.
|
||||||
|
const double kSegmentDurationInSeconds = 1.0;
|
||||||
|
const double kFragmentDurationInSecodns = 0.1;
|
||||||
|
const bool kSegmentSapAligned = true;
|
||||||
|
const bool kFragmentSapAligned = true;
|
||||||
|
const int kNumSubsegmentsPerSidx = 2;
|
||||||
|
const char kOutputFileName[] = "output_file";
|
||||||
|
const char kOutputFileName2[] = "output_file2";
|
||||||
|
const char kSegmentTemplate[] = "template$Number$.m4s";
|
||||||
|
const char kSegmentTemplateOutputFile[] = "template1.m4s";
|
||||||
|
const char kTempFileName[] = "temp_file";
|
||||||
|
|
||||||
// Encryption constants.
|
// Encryption constants.
|
||||||
const char kKeyIdHex[] = "e5007e6e9dcd5ac095202ed3758382cd";
|
const char kKeyIdHex[] = "e5007e6e9dcd5ac095202ed3758382cd";
|
||||||
const char kKeyHex[] = "6fc96fe628a265b13aeddec0bc421f4d";
|
const char kKeyHex[] = "6fc96fe628a265b13aeddec0bc421f4d";
|
||||||
|
@ -29,112 +38,108 @@ const char kPsshHex[] =
|
||||||
"08011210e5007e6e9dcd5ac095202ed3"
|
"08011210e5007e6e9dcd5ac095202ed3"
|
||||||
"758382cd1a0d7769646576696e655f746573742211544553545f"
|
"758382cd1a0d7769646576696e655f746573742211544553545f"
|
||||||
"434f4e54454e545f49445f312a025344";
|
"434f4e54454e545f49445f312a025344";
|
||||||
const uint32 kClearMilliseconds = 1500;
|
const double kClearLeadInSeconds = 1.5;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
class TestingMuxer : public Muxer {
|
class PackagerTest : public ::testing::TestWithParam<const char*> {
|
||||||
public:
|
public:
|
||||||
TestingMuxer(const MuxerOptions& options, EncryptorSource* encryptor_source)
|
virtual void SetUp() OVERRIDE {
|
||||||
: Muxer(options, encryptor_source) {}
|
// Create a test directory for testing, will be deleted after test.
|
||||||
|
ASSERT_TRUE(
|
||||||
|
file_util::CreateNewTempDirectory("packager_", &test_directory_));
|
||||||
|
|
||||||
virtual Status Initialize() OVERRIDE {
|
options_.segment_duration = kSegmentDurationInSeconds;
|
||||||
DVLOG(1) << "Initialize is called.";
|
options_.fragment_duration = kFragmentDurationInSecodns;
|
||||||
return Status::OK;
|
options_.segment_sap_aligned = kSegmentSapAligned;
|
||||||
|
options_.fragment_sap_aligned = kFragmentSapAligned;
|
||||||
|
options_.num_subsegments_per_sidx = kNumSubsegmentsPerSidx;
|
||||||
|
|
||||||
|
options_.output_file_name =
|
||||||
|
test_directory_.AppendASCII(kOutputFileName).value();
|
||||||
|
options_.segment_template =
|
||||||
|
test_directory_.AppendASCII(kSegmentTemplate).value();
|
||||||
|
options_.temp_file_name =
|
||||||
|
test_directory_.AppendASCII(kTempFileName).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Status AddSample(const MediaStream* stream,
|
virtual void TearDown() OVERRIDE { base::DeleteFile(test_directory_, true); }
|
||||||
scoped_refptr<MediaSample> sample) OVERRIDE {
|
|
||||||
DVLOG(1) << "Add Sample: " << sample->ToString();
|
void Remux(const std::string& input_file, Muxer* muxer) {
|
||||||
DVLOG(2) << "To Stream: " << stream->ToString();
|
DCHECK(muxer);
|
||||||
return Status::OK;
|
|
||||||
|
Demuxer demuxer(input_file, NULL);
|
||||||
|
ASSERT_OK(demuxer.Initialize());
|
||||||
|
ASSERT_LE(1, demuxer.streams().size());
|
||||||
|
|
||||||
|
VLOG(1) << "Num Streams: " << demuxer.streams().size();
|
||||||
|
for (size_t i = 0; i < demuxer.streams().size(); ++i) {
|
||||||
|
VLOG(1) << "Streams " << i << ": " << demuxer.streams()[i]->ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_OK(muxer->AddStream(demuxer.streams()[0]));
|
||||||
|
ASSERT_OK(muxer->Initialize());
|
||||||
|
|
||||||
|
// Starts remuxing process.
|
||||||
|
ASSERT_OK(demuxer.Run());
|
||||||
|
ASSERT_OK(muxer->Finalize());
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Status Finalize() OVERRIDE {
|
protected:
|
||||||
DVLOG(1) << "Finalize is called.";
|
base::FilePath test_directory_;
|
||||||
return Status::OK;
|
MuxerOptions options_;
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(TestingMuxer);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Muxer* CreateMuxerFunc(const std::string& input_file_name,
|
TEST_P(PackagerTest, MP4MuxerSingleSegmentUnencrypted) {
|
||||||
EncryptorSource* encryptor_source);
|
options_.single_segment = true;
|
||||||
|
|
||||||
Muxer* CreateTestingMuxer(const std::string& input_file_name,
|
const std::string input_media_file = GetTestDataFilePath(GetParam()).value();
|
||||||
EncryptorSource* encryptor_source) {
|
scoped_ptr<Muxer> muxer(new mp4::MP4Muxer(options_));
|
||||||
MuxerOptions options;
|
ASSERT_NO_FATAL_FAILURE(Remux(input_media_file, muxer.get()));
|
||||||
return new TestingMuxer(options, NULL);
|
|
||||||
|
// Take the muxer output and feed into muxer again. The new muxer output
|
||||||
|
// should contain the same contents as the previous muxer output.
|
||||||
|
const std::string new_input_media_file = options_.output_file_name;
|
||||||
|
options_.output_file_name =
|
||||||
|
test_directory_.AppendASCII(kOutputFileName2).value();
|
||||||
|
muxer.reset(new mp4::MP4Muxer(options_));
|
||||||
|
ASSERT_NO_FATAL_FAILURE(Remux(new_input_media_file, muxer.get()));
|
||||||
|
|
||||||
|
EXPECT_TRUE(base::ContentsEqual(base::FilePath(new_input_media_file),
|
||||||
|
base::FilePath(options_.output_file_name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Muxer* CreateNormalMP4Muxer(const std::string& input_file_name,
|
TEST_P(PackagerTest, MP4MuxerSingleSegmentEncrypted) {
|
||||||
EncryptorSource* encryptor_source) {
|
options_.single_segment = true;
|
||||||
MuxerOptions options;
|
|
||||||
options.single_segment = true;
|
|
||||||
options.segment_duration = 0.005;
|
|
||||||
options.fragment_duration = 0.002;
|
|
||||||
options.segment_sap_aligned = true;
|
|
||||||
options.fragment_sap_aligned = true;
|
|
||||||
options.num_subsegments_per_sidx = 1;
|
|
||||||
options.output_file_name = "/tmp/clear_" + input_file_name;
|
|
||||||
options.segment_template = "/tmp/template$Number$.m4s";
|
|
||||||
options.temp_file_name = "/tmp/tmp.mp4";
|
|
||||||
return new mp4::MP4Muxer(options, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
Muxer* CreateEncryptionMP4Muxer(const std::string& input_file_name,
|
FixedEncryptorSource encryptor_source(kKeyIdHex, kKeyHex, kPsshHex);
|
||||||
EncryptorSource* encryptor_source) {
|
ASSERT_OK(encryptor_source.Initialize());
|
||||||
MuxerOptions options;
|
|
||||||
options.single_segment = true;
|
|
||||||
options.segment_duration = 0.005;
|
|
||||||
options.fragment_duration = 0.002;
|
|
||||||
options.segment_sap_aligned = true;
|
|
||||||
options.fragment_sap_aligned = true;
|
|
||||||
options.num_subsegments_per_sidx = 1;
|
|
||||||
options.output_file_name = "/tmp/enc_" + input_file_name;
|
|
||||||
options.segment_template = "/tmp/template$Number$.m4s";
|
|
||||||
options.temp_file_name = "/tmp/tmp.mp4";
|
|
||||||
return new mp4::MP4Muxer(options, encryptor_source);
|
|
||||||
}
|
|
||||||
|
|
||||||
class PackagerTest : public ::testing::TestWithParam<
|
const std::string input_media_file = GetTestDataFilePath(GetParam()).value();
|
||||||
::std::tr1::tuple<const char*, CreateMuxerFunc*> > {};
|
scoped_ptr<Muxer> muxer(new mp4::MP4Muxer(options_));
|
||||||
|
muxer->SetEncryptorSource(&encryptor_source, kClearLeadInSeconds);
|
||||||
|
ASSERT_NO_FATAL_FAILURE(Remux(input_media_file, muxer.get()));
|
||||||
|
|
||||||
TEST_P(PackagerTest, Remux) {
|
// Expect the output to be encrypted.
|
||||||
std::string file_name = ::std::tr1::get<0>(GetParam());
|
Demuxer demuxer(options_.output_file_name, NULL);
|
||||||
CreateMuxerFunc* CreateMuxer = ::std::tr1::get<1>(GetParam());
|
|
||||||
|
|
||||||
Demuxer demuxer(GetTestDataFilePath(file_name).value(), NULL);
|
|
||||||
ASSERT_OK(demuxer.Initialize());
|
ASSERT_OK(demuxer.Initialize());
|
||||||
|
ASSERT_EQ(1, demuxer.streams().size());
|
||||||
LOG(INFO) << "Num Streams: " << demuxer.streams().size();
|
EXPECT_TRUE(demuxer.streams()[0]->info()->is_encrypted());
|
||||||
for (int i = 0; i < demuxer.streams().size(); ++i) {
|
|
||||||
LOG(INFO) << "Streams " << i << " " << demuxer.streams()[i]->ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
FixedEncryptorSource encryptor_source(
|
|
||||||
kKeyIdHex, kKeyHex, kPsshHex, kClearMilliseconds);
|
|
||||||
EXPECT_OK(encryptor_source.Initialize());
|
|
||||||
|
|
||||||
scoped_ptr<Muxer> muxer(CreateMuxer(file_name, &encryptor_source));
|
|
||||||
|
|
||||||
ASSERT_OK(muxer->AddStream(demuxer.streams()[0]));
|
|
||||||
ASSERT_OK(muxer->Initialize());
|
|
||||||
|
|
||||||
// Starts remuxing process.
|
|
||||||
ASSERT_OK(demuxer.Run());
|
|
||||||
|
|
||||||
ASSERT_OK(muxer->Finalize());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(PackagerE2ETest,
|
TEST_P(PackagerTest, MP4MuxerMultipleSegmentsUnencrypted) {
|
||||||
PackagerTest,
|
options_.single_segment = false;
|
||||||
Combine(ValuesIn(kMediaFiles),
|
|
||||||
Values(&CreateTestingMuxer,
|
const std::string input_media_file = GetTestDataFilePath(GetParam()).value();
|
||||||
&CreateNormalMP4Muxer,
|
scoped_ptr<Muxer> muxer(new mp4::MP4Muxer(options_));
|
||||||
&CreateEncryptionMP4Muxer)));
|
ASSERT_NO_FATAL_FAILURE(Remux(input_media_file, muxer.get()));
|
||||||
|
|
||||||
|
EXPECT_TRUE(base::PathExists(
|
||||||
|
test_directory_.AppendASCII(kSegmentTemplateOutputFile)));
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(PackagerE2ETest, PackagerTest, ValuesIn(kMediaFiles));
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
Loading…
Reference in New Issue