Make edash-packager outputs predictable for testing

- Added --iv to inject iv for fixed key encryption.
- Added support for key rotation in fixed key encryption.
- Added --use_fake_clock_for_muxer to set timestamp to 0 in the output.

Also updates stream info dump message:
- Modified pixel_width: %d\n pixel_height: %d to pixel aspect ratio: %d:%d
- Removed language from video stream info.

These flags should only be used for testing.

Change-Id: Iedf8d6d6492226219f49fe44d932645f557010e4
This commit is contained in:
KongQun Yang 2015-09-25 15:48:18 -07:00 committed by Gerrit Code Review
parent 7ac31f2111
commit 8c202047fb
9 changed files with 64 additions and 29 deletions

View File

@ -18,6 +18,10 @@ DEFINE_bool(enable_fixed_key_decryption,
"Enable decryption with fixed key."); "Enable decryption with fixed key.");
DEFINE_string(key_id, "", "Key id in hex string format."); DEFINE_string(key_id, "", "Key id in hex string format.");
DEFINE_string(key, "", "Key in hex string format."); DEFINE_string(key, "", "Key in hex string format.");
DEFINE_string(iv,
"",
"Iv in hex string format. If not specified, a random iv will be "
"generated. This flag should only be used for testing.");
DEFINE_string(pssh, "", "PSSH in hex string format."); DEFINE_string(pssh, "", "PSSH in hex string format.");
namespace edash_packager { namespace edash_packager {
@ -38,6 +42,10 @@ bool ValidateFixedCryptoFlags() {
"key", FLAGS_key, fixed_crypto, false, fixed_crypto_label)) { "key", FLAGS_key, fixed_crypto, false, fixed_crypto_label)) {
success = false; success = false;
} }
if (!ValidateFlag("iv", FLAGS_iv, FLAGS_enable_fixed_key_encryption, true,
"--enable_fixed_key_encryption")) {
success = false;
}
// --pssh is associated with --enable_fix_key_encryption. // --pssh is associated with --enable_fix_key_encryption.
if (!ValidateFlag("pssh", if (!ValidateFlag("pssh",

View File

@ -15,6 +15,7 @@ DECLARE_bool(enable_fixed_key_encryption);
DECLARE_bool(enable_fixed_key_decryption); DECLARE_bool(enable_fixed_key_decryption);
DECLARE_string(key_id); DECLARE_string(key_id);
DECLARE_string(key); DECLARE_string(key);
DECLARE_string(iv);
DECLARE_string(pssh); DECLARE_string(pssh);
namespace edash_packager { namespace edash_packager {

View File

@ -4,6 +4,7 @@
// license that can be found in the LICENSE file or at // license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd // https://developers.google.com/open-source/licenses/bsd
#include <gflags/gflags.h>
#include <iostream> #include <iostream>
#include "packager/app/fixed_key_encryption_flags.h" #include "packager/app/fixed_key_encryption_flags.h"
@ -18,6 +19,7 @@
#include "packager/base/strings/string_split.h" #include "packager/base/strings/string_split.h"
#include "packager/base/strings/stringprintf.h" #include "packager/base/strings/stringprintf.h"
#include "packager/base/threading/simple_thread.h" #include "packager/base/threading/simple_thread.h"
#include "packager/base/time/clock.h"
#include "packager/media/base/demuxer.h" #include "packager/media/base/demuxer.h"
#include "packager/media/base/key_source.h" #include "packager/media/base/key_source.h"
#include "packager/media/base/muxer_options.h" #include "packager/media/base/muxer_options.h"
@ -29,6 +31,12 @@
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"
#include "packager/mpd/base/simple_mpd_notifier.h" #include "packager/mpd/base/simple_mpd_notifier.h"
DEFINE_bool(use_fake_clock_for_muxer,
false,
"Set to true to use a fake clock for muxer. With this flag set, "
"creation time and modification time in outputs are set to 0. "
"Should only be used for testing.");
namespace { namespace {
const char kUsage[] = const char kUsage[] =
"Packager driver program. Sample Usage:\n" "Packager driver program. Sample Usage:\n"
@ -66,6 +74,13 @@ enum ExitStatus {
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
// A fake clock that always return time 0 (epoch). Should only be used for
// testing.
class FakeClock : public base::Clock {
public:
virtual base::Time Now() OVERRIDE { return base::Time(); }
};
// Demux, Mux(es) and worker thread used to remux a source file/stream. // Demux, Mux(es) and worker thread used to remux a source file/stream.
class RemuxJob : public base::SimpleThread { class RemuxJob : public base::SimpleThread {
public: public:
@ -99,6 +114,7 @@ class RemuxJob : public base::SimpleThread {
bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
const MuxerOptions& muxer_options, const MuxerOptions& muxer_options,
FakeClock* fake_clock,
KeySource* key_source, KeySource* key_source,
MpdNotifier* mpd_notifier, MpdNotifier* mpd_notifier,
std::vector<RemuxJob*>* remux_jobs) { std::vector<RemuxJob*>* remux_jobs) {
@ -149,6 +165,8 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
DCHECK(!remux_jobs->empty()); DCHECK(!remux_jobs->empty());
scoped_ptr<Muxer> muxer(new mp4::MP4Muxer(stream_muxer_options)); scoped_ptr<Muxer> muxer(new mp4::MP4Muxer(stream_muxer_options));
if (FLAGS_use_fake_clock_for_muxer) muxer->set_clock(fake_clock);
if (key_source) { if (key_source) {
muxer->SetKeySource(key_source, muxer->SetKeySource(key_source,
FLAGS_max_sd_pixels, FLAGS_max_sd_pixels,
@ -274,10 +292,9 @@ bool RunPackager(const StreamDescriptorList& stream_descriptors) {
std::vector<RemuxJob*> remux_jobs; std::vector<RemuxJob*> remux_jobs;
STLElementDeleter<std::vector<RemuxJob*> > scoped_jobs_deleter(&remux_jobs); STLElementDeleter<std::vector<RemuxJob*> > scoped_jobs_deleter(&remux_jobs);
if (!CreateRemuxJobs(stream_descriptors, FakeClock fake_clock;
muxer_options, if (!CreateRemuxJobs(stream_descriptors, muxer_options, &fake_clock,
encryption_key_source.get(), encryption_key_source.get(), mpd_notifier.get(),
mpd_notifier.get(),
&remux_jobs)) { &remux_jobs)) {
return false; return false;
} }

View File

@ -91,7 +91,7 @@ scoped_ptr<KeySource> CreateEncryptionKeySource() {
encryption_key_source = widevine_key_source.Pass(); encryption_key_source = widevine_key_source.Pass();
} else if (FLAGS_enable_fixed_key_encryption) { } else if (FLAGS_enable_fixed_key_encryption) {
encryption_key_source = KeySource::CreateFromHexStrings( encryption_key_source = KeySource::CreateFromHexStrings(
FLAGS_key_id, FLAGS_key, FLAGS_pssh, ""); FLAGS_key_id, FLAGS_key, FLAGS_pssh, FLAGS_iv);
} }
return encryption_key_source.Pass(); return encryption_key_source.Pass();
} }

View File

@ -142,12 +142,6 @@ bool ValidateWidevineCryptoFlags() {
if (FLAGS_crypto_period_duration < 0) { if (FLAGS_crypto_period_duration < 0) {
PrintError("--crypto_period_duration should not be negative."); PrintError("--crypto_period_duration should not be negative.");
success = false; success = false;
} else if (FLAGS_crypto_period_duration > 0 &&
!FLAGS_enable_widevine_encryption) {
PrintError(
"--crypto_period_duration should be specified only if "
"--enable_widevine_encryption.");
success = false;
} }
return success; return success;
} }

View File

@ -90,12 +90,9 @@ bool AudioStreamInfo::IsValidConfig() const {
std::string AudioStreamInfo::ToString() const { std::string AudioStreamInfo::ToString() const {
return base::StringPrintf( return base::StringPrintf(
"%s codec: %s\n sample_bits: %d\n num_channels: %d\n " "%s codec: %s\n sample_bits: %d\n num_channels: %d\n "
"sampling_frequency: %d\n", "sampling_frequency: %d\n language: %s\n",
StreamInfo::ToString().c_str(), StreamInfo::ToString().c_str(), AudioCodecToString(codec_).c_str(),
AudioCodecToString(codec_).c_str(), sample_bits_, num_channels_, sampling_frequency_, language().c_str());
sample_bits_,
num_channels_,
sampling_frequency_);
} }
std::string AudioStreamInfo::GetCodecString(AudioCodec codec, std::string AudioStreamInfo::GetCodecString(AudioCodec codec,

View File

@ -14,7 +14,8 @@ namespace {
const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, const uint8_t kWidevineSystemId[] = {0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6,
0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc,
0xd5, 0x1d, 0x21, 0xed}; 0xd5, 0x1d, 0x21, 0xed};
const char kDefaultUUID[] = ""; // TODO(kqyang): Consider making it configurable.
const char kDefaultUUID[] = "edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";
const char kDefaultSystemName[] = ""; const char kDefaultSystemName[] = "";
} // namespace } // namespace
@ -65,8 +66,27 @@ Status KeySource::GetKey(const std::vector<uint8_t>& key_id,
Status KeySource::GetCryptoPeriodKey(uint32_t crypto_period_index, Status KeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
TrackType track_type, TrackType track_type,
EncryptionKey* key) { EncryptionKey* key) {
NOTIMPLEMENTED(); *key = *encryption_key_;
return Status(error::UNIMPLEMENTED, ""); // A naive key rotation algorithm is implemented here by left rotating the
// key, key_id and pssh. Note that this implementation is only intended for
// testing purpose. The actual key rotation algorithm can be much more
// complicated.
LOG(WARNING)
<< "This naive key rotation algorithm should not be used in production.";
std::rotate(key->key_id.begin(),
key->key_id.begin() + (crypto_period_index % key->key_id.size()),
key->key_id.end());
std::rotate(key->key.begin(),
key->key.begin() + (crypto_period_index % key->key.size()),
key->key.end());
const size_t kPsshHeaderSize = 32u;
std::vector<uint8_t> pssh_data(key->pssh.begin() + kPsshHeaderSize,
key->pssh.end());
std::rotate(pssh_data.begin(),
pssh_data.begin() + (crypto_period_index % pssh_data.size()),
pssh_data.end());
key->pssh = PsshBoxFromPsshData(pssh_data);
return Status::OK;
} }
std::string KeySource::UUID() { std::string KeySource::UUID() {

View File

@ -40,11 +40,11 @@ StreamInfo::~StreamInfo() {}
std::string StreamInfo::ToString() const { std::string StreamInfo::ToString() const {
return base::StringPrintf( return base::StringPrintf(
"type: %s\n codec_string: %s\n time_scale: %d\n duration: " "type: %s\n codec_string: %s\n time_scale: %d\n duration: "
"%" PRIu64 " (%.1f seconds)\n language: %s\n is_encrypted: %s\n", "%" PRIu64 " (%.1f seconds)\n is_encrypted: %s\n",
(stream_type_ == kStreamAudio ? "Audio" : "Video"), (stream_type_ == kStreamAudio ? "Audio" : "Video"),
codec_string_.c_str(), codec_string_.c_str(),
time_scale_, duration_, time_scale_, duration_,
static_cast<double>(duration_) / time_scale_, language_.c_str(), static_cast<double>(duration_) / time_scale_,
is_encrypted_ ? "true" : "false"); is_encrypted_ ? "true" : "false");
} }

View File

@ -85,13 +85,11 @@ bool VideoStreamInfo::IsValidConfig() const {
std::string VideoStreamInfo::ToString() const { std::string VideoStreamInfo::ToString() const {
return base::StringPrintf( return base::StringPrintf(
"%s codec: %s\n width: %d\n height: %d\n pixel_width: %d\n pixel_height: " "%s codec: %s\n width: %d\n height: %d\n pixel aspect ratio: %d:%d\n "
"%d\n trick_play_rate: %d\n nalu_length_size: %d\n", "trick_play_rate: %d\n nalu_length_size: %d\n",
StreamInfo::ToString().c_str(), StreamInfo::ToString().c_str(), VideoCodecToString(codec_).c_str(),
VideoCodecToString(codec_).c_str(), width_, height_, pixel_width_, pixel_height_, trick_play_rate_,
width_, height_, nalu_length_size_);
pixel_width_, pixel_height_,
trick_play_rate_, nalu_length_size_);
} }
std::string VideoStreamInfo::GetCodecString(VideoCodec codec, std::string VideoStreamInfo::GetCodecString(VideoCodec codec,