Implement packager driving program packager_main
I am not sure whether it is a good idea to define command line flags in the actual source. I created several flags definition .h to host these flags for now. Change-Id: Ib6ca60d8656e8015a64dafff8e0a98a47676bbd2
This commit is contained in:
parent
8f495e6615
commit
6957a4ac07
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2013 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// Defines command line flags for fixed key encryption.
|
||||
|
||||
#ifndef APP_FIXED_KEY_ENCRYPTION_FLAGS_H_
|
||||
#define APP_FIXED_KEY_ENCRYPTION_FLAGS_H_
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DEFINE_bool(enable_fixed_key_encryption,
|
||||
false,
|
||||
"Enable encryption with fixed key.");
|
||||
DEFINE_string(key_id, "", "Key id in hex string format.");
|
||||
DEFINE_string(key, "", "Key in hex string format.");
|
||||
DEFINE_string(pssh, "", "PSSH in hex string format.");
|
||||
|
||||
static bool IsNotEmptyWithFixedKeyEncryption(const char* flag_name,
|
||||
const std::string& flag_value) {
|
||||
return FLAGS_enable_fixed_key_encryption ? !flag_value.empty() : true;
|
||||
}
|
||||
|
||||
static bool dummy_key_id_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_key_id,
|
||||
&IsNotEmptyWithFixedKeyEncryption);
|
||||
static bool dummy_key_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_key,
|
||||
&IsNotEmptyWithFixedKeyEncryption);
|
||||
static bool dummy_pssh_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_pssh,
|
||||
&IsNotEmptyWithFixedKeyEncryption);
|
||||
|
||||
#endif // APP_FIXED_KEY_ENCRYPTION_FLAGS_H_
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2013 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// Defines Muxer flags.
|
||||
|
||||
#ifndef APP_MUXER_FLAGS_H_
|
||||
#define APP_MUXER_FLAGS_H_
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DEFINE_bool(audio, false, "Add the first audio stream to muxer.");
|
||||
DEFINE_bool(video, false, "Add the first video stream to muxer.");
|
||||
DEFINE_double(clear_lead,
|
||||
10.0,
|
||||
"Clear lead in seconds if encryption is enabled.");
|
||||
|
||||
DEFINE_bool(single_segment,
|
||||
true,
|
||||
"Generate a single segment for the media presentation. This option "
|
||||
"should be set for on demand profile.");
|
||||
DEFINE_double(segment_duration,
|
||||
10.0f,
|
||||
"Segment duration in seconds. If single_segment is specified, "
|
||||
"this parameter sets the duration of a subsegment; otherwise, "
|
||||
"this parameter sets the duration of a segment. Actual segment "
|
||||
"durations may not be exactly as requested.");
|
||||
DEFINE_bool(segment_sap_aligned,
|
||||
true,
|
||||
"Force segments to begin with stream access points.");
|
||||
DEFINE_double(fragment_duration,
|
||||
2.0f,
|
||||
"Fragment duration in seconds. Should not be larger than "
|
||||
"the segment duration. Actual fragment durations may not be "
|
||||
"exactly as requested.");
|
||||
DEFINE_bool(fragment_sap_aligned,
|
||||
true,
|
||||
"Force fragments to begin with stream access points. This flag "
|
||||
"implies segment_sap_aligned.");
|
||||
DEFINE_int32(num_subsegments_per_sidx,
|
||||
1,
|
||||
"For ISO BMFF only. Set the number of subsegments in each "
|
||||
"SIDX box. If 0, a single SIDX box is used per segment; if "
|
||||
"-1, no SIDX box is used; Otherwise, the muxer packs N "
|
||||
"subsegments in the root SIDX of the segment, with "
|
||||
"segment_duration/N/fragment_duration fragments per "
|
||||
"subsegment.");
|
||||
DEFINE_string(output,
|
||||
"",
|
||||
"Output file path. If segment_template is not specified, "
|
||||
"the muxer generates this single output file with all "
|
||||
"segments concatenated; Otherwise, it specifies the "
|
||||
"initialization segment name.");
|
||||
DEFINE_string(segment_template,
|
||||
"",
|
||||
"Output segment name pattern for generated segments. It "
|
||||
"can furthermore be configured using a subset of "
|
||||
"SegmentTemplate identifiers: $Number$, $Bandwidth$ and "
|
||||
"$Time$.");
|
||||
DEFINE_string(temp_file,
|
||||
"",
|
||||
"Specify a temporary file for on demand media file creation. If "
|
||||
"not specified, a new file will be created in an OS-dependent "
|
||||
"temporary directory.");
|
||||
|
||||
#endif // APP_MUXER_FLAGS_H_
|
|
@ -0,0 +1,215 @@
|
|||
// Copyright (c) 2013 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "app/fixed_key_encryption_flags.h"
|
||||
#include "app/muxer_flags.h"
|
||||
#include "app/widevine_encryption_flags.h"
|
||||
#include "base/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "media/base/demuxer.h"
|
||||
#include "media/base/fixed_encryptor_source.h"
|
||||
#include "media/base/widevine_encryptor_source.h"
|
||||
#include "media/base/media_stream.h"
|
||||
#include "media/base/muxer_options.h"
|
||||
#include "media/base/request_signer.h"
|
||||
#include "media/base/stream_info.h"
|
||||
#include "media/file/file.h"
|
||||
#include "media/mp4/mp4_muxer.h"
|
||||
|
||||
DEFINE_bool(dump_stream_info, false, "Dump demuxed stream info.");
|
||||
|
||||
namespace {
|
||||
const char kUsage[] =
|
||||
"Packager driver program. Sample Usage:\n%s <input> [flags]";
|
||||
} // namespace
|
||||
|
||||
namespace media {
|
||||
|
||||
void DumpStreamInfo(const std::vector<MediaStream*>& streams) {
|
||||
std::cout << "Found " << streams.size() << " stream(s)." << std::endl;
|
||||
for (size_t i = 0; i < streams.size(); ++i)
|
||||
std::cout << "Stream [" << i << "] " << streams[i]->ToString() << std::endl;
|
||||
}
|
||||
|
||||
// Create and initialize encryptor source.
|
||||
scoped_ptr<EncryptorSource> CreateEncryptorSource() {
|
||||
scoped_ptr<EncryptorSource> encryptor_source;
|
||||
if (FLAGS_enable_widevine_encryption) {
|
||||
std::string rsa_private_key;
|
||||
if (!File::ReadFileToString(FLAGS_signing_key_path.c_str(),
|
||||
&rsa_private_key)) {
|
||||
LOG(ERROR) << "Failed to read from rsa_key_file.";
|
||||
return scoped_ptr<EncryptorSource>();
|
||||
}
|
||||
|
||||
scoped_ptr<RequestSigner> signer(
|
||||
RsaRequestSigner::CreateSigner(FLAGS_signer, rsa_private_key));
|
||||
if (!signer) {
|
||||
LOG(ERROR) << "Cannot create signer object from "
|
||||
<< FLAGS_signing_key_path;
|
||||
return scoped_ptr<EncryptorSource>();
|
||||
}
|
||||
|
||||
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()));
|
||||
} 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>();
|
||||
}
|
||||
}
|
||||
return encryptor_source.Pass();
|
||||
}
|
||||
|
||||
bool GetMuxerOptions(MuxerOptions* muxer_options) {
|
||||
DCHECK(muxer_options);
|
||||
|
||||
muxer_options->single_segment = FLAGS_single_segment;
|
||||
muxer_options->segment_duration = FLAGS_segment_duration;
|
||||
muxer_options->fragment_duration = FLAGS_fragment_duration;
|
||||
muxer_options->segment_sap_aligned = FLAGS_segment_sap_aligned;
|
||||
muxer_options->fragment_sap_aligned = FLAGS_fragment_sap_aligned;
|
||||
muxer_options->num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx;
|
||||
muxer_options->output_file_name = FLAGS_output;
|
||||
muxer_options->segment_template = FLAGS_segment_template;
|
||||
muxer_options->temp_file_name = FLAGS_temp_file;
|
||||
|
||||
// Create a temp file if needed.
|
||||
if (muxer_options->single_segment && muxer_options->temp_file_name.empty()) {
|
||||
base::FilePath path;
|
||||
if (!file_util::CreateTemporaryFile(&path)) {
|
||||
LOG(ERROR) << "Failed to create a temporary file.";
|
||||
return false;
|
||||
}
|
||||
muxer_options->temp_file_name = path.value();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MediaStream* FindFirstStreamOfType(const std::vector<MediaStream*>& streams,
|
||||
StreamType stream_type) {
|
||||
typedef std::vector<MediaStream*>::const_iterator StreamIterator;
|
||||
for (StreamIterator it = streams.begin(); it != streams.end(); ++it) {
|
||||
if ((*it)->info()->stream_type() == stream_type)
|
||||
return *it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
MediaStream* FindFirstVideoStream(const std::vector<MediaStream*>& streams) {
|
||||
return FindFirstStreamOfType(streams, kStreamVideo);
|
||||
}
|
||||
MediaStream* FindFirstAudioStream(const std::vector<MediaStream*>& streams) {
|
||||
return FindFirstStreamOfType(streams, kStreamAudio);
|
||||
}
|
||||
|
||||
bool AddStreamToMuxer(const std::vector<MediaStream*>& streams, Muxer* muxer) {
|
||||
DCHECK(muxer);
|
||||
|
||||
if (!FLAGS_video && !FLAGS_audio) {
|
||||
LOG(ERROR) << "Required: add_video_stream or add_audio_stream.";
|
||||
return false;
|
||||
}
|
||||
|
||||
MediaStream* stream = FLAGS_video ? FindFirstVideoStream(streams)
|
||||
: FindFirstAudioStream(streams);
|
||||
if (!stream) {
|
||||
LOG(ERROR) << "Cannot find a " << (FLAGS_video ? "video" : "audio")
|
||||
<< " stream to mux.";
|
||||
return false;
|
||||
}
|
||||
Status status = muxer->AddStream(stream);
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Muxer failed to add stream: " << status.ToString();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RunPackager(const std::string& input) {
|
||||
Status status;
|
||||
|
||||
// Setup and initialize Demuxer.
|
||||
Demuxer demuxer(input, NULL);
|
||||
status = demuxer.Initialize();
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Demuxer failed to initialize: " << status.ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FLAGS_dump_stream_info)
|
||||
DumpStreamInfo(demuxer.streams());
|
||||
|
||||
if (FLAGS_output.empty()) {
|
||||
LOG(INFO) << "No output specified. Exiting.";
|
||||
return true;
|
||||
}
|
||||
|
||||
// Setup muxer.
|
||||
MuxerOptions muxer_options;
|
||||
if (!GetMuxerOptions(&muxer_options))
|
||||
return false;
|
||||
|
||||
scoped_ptr<Muxer> muxer(new mp4::MP4Muxer(muxer_options));
|
||||
if (!AddStreamToMuxer(demuxer.streams(), muxer.get()))
|
||||
return false;
|
||||
|
||||
scoped_ptr<EncryptorSource> encryptor_source;
|
||||
if (FLAGS_enable_widevine_encryption || FLAGS_enable_fixed_key_encryption) {
|
||||
encryptor_source = CreateEncryptorSource();
|
||||
if (!encryptor_source)
|
||||
return false;
|
||||
}
|
||||
muxer->SetEncryptorSource(encryptor_source.get(), FLAGS_clear_lead);
|
||||
|
||||
status = muxer->Initialize();
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Muxer failed to initialize: " << status.ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start remuxing process.
|
||||
status = demuxer.Run();
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Remuxing failed: " << status.ToString();
|
||||
return false;
|
||||
}
|
||||
status = muxer->Finalize();
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Muxer failed to finalize: " << status.ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Packaging completed successfully." << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace media
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
google::SetUsageMessage(base::StringPrintf(kUsage, argv[0]));
|
||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (argc < 2) {
|
||||
google::ShowUsageWithFlags(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
return media::RunPackager(argv[1]) ? 0 : 1;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2013 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// Defines command line flags for widevine_encryption.
|
||||
|
||||
#ifndef APP_WIDEVINE_ENCRYPTION_FLAGS_H_
|
||||
#define APP_WIDEVINE_ENCRYPTION_FLAGS_H_
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DEFINE_bool(enable_widevine_encryption,
|
||||
false,
|
||||
"Enable encryption with Widevine license server/proxy.");
|
||||
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(signer, "", "The name of the signer.");
|
||||
DEFINE_string(signing_key_path,
|
||||
"",
|
||||
"Stores PKCS#1 RSA private key for request signing.");
|
||||
|
||||
static bool IsNotEmptyWithWidevineEncryption(const char* flag_name,
|
||||
const std::string& flag_value) {
|
||||
return FLAGS_enable_widevine_encryption ? !flag_value.empty() : true;
|
||||
}
|
||||
|
||||
static bool dummy_server_url_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_server_url,
|
||||
&IsNotEmptyWithWidevineEncryption);
|
||||
static bool dummy_content_id_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_content_id,
|
||||
&IsNotEmptyWithWidevineEncryption);
|
||||
static bool dummy_track_type_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_track_type,
|
||||
&IsNotEmptyWithWidevineEncryption);
|
||||
static bool dummy_signer_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_signer,
|
||||
&IsNotEmptyWithWidevineEncryption);
|
||||
static bool dummy_rsa_key_file_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_signing_key_path,
|
||||
&IsNotEmptyWithWidevineEncryption);
|
||||
|
||||
#endif // APP_WIDEVINE_ENCRYPTION_FLAGS_H_
|
12
packager.gyp
12
packager.gyp
|
@ -272,5 +272,17 @@
|
|||
'testing/gtest.gyp:gtest',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'packager_main',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'app/packager_main.cc',
|
||||
],
|
||||
'dependencies': [
|
||||
'file',
|
||||
'mp4',
|
||||
'third_party/gflags/gflags.gyp:gflags',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue