Renamed packager_main to single_packager.
Did some re-factoring to share code with upcoming (multi stream) packager. Change-Id: I2b3845e48ba6aa63a95ecc276abcb52c8355d8d5
This commit is contained in:
parent
beaea71946
commit
7fe5b5171a
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2014 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 or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
//
|
||||
// Defines command line flags for fixed key encryption.
|
||||
|
||||
#include "app/fixed_key_encryption_flags.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);
|
|
@ -11,26 +11,9 @@
|
|||
|
||||
#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);
|
||||
DECLARE_bool(enable_fixed_key_encryption);
|
||||
DECLARE_string(key_id);
|
||||
DECLARE_string(key);
|
||||
DECLARE_string(pssh);
|
||||
|
||||
#endif // APP_FIXED_KEY_ENCRYPTION_FLAGS_H_
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2014 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 or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
//
|
||||
// Defines Muxer flags.
|
||||
|
||||
#include "app/muxer_flags.h"
|
||||
|
||||
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_bool(normalize_presentation_timestamp,
|
||||
true,
|
||||
"Set to true to normalize the presentation timestamps to start"
|
||||
"from zero.");
|
||||
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(temp_dir,
|
||||
"",
|
||||
"Specify a directory in which to store temporary (intermediate) "
|
||||
" files. Used only if single_segment=true.");
|
||||
|
||||
// Flags for MuxerListener.
|
||||
DEFINE_bool(output_media_info,
|
||||
true,
|
||||
"Create a human readable format of MediaInfo. The output file name "
|
||||
"will be the name specified by output flag, suffixed with "
|
||||
"'.media_info'.");
|
||||
DEFINE_string(scheme_id_uri,
|
||||
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed",
|
||||
"This flag only applies if output_media_info is true. This value "
|
||||
"will be set in MediaInfo if the stream is encrypted. "
|
||||
"If the stream is encrypted, MPD requires a <ContentProtection> "
|
||||
"element which requires the schemeIdUri attribute. "
|
||||
"Default value is Widevine PSSH system ID, and it is valid only "
|
||||
"for ISO BMFF.");
|
|
@ -11,80 +11,19 @@
|
|||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DEFINE_string(stream,
|
||||
"video",
|
||||
"Add the specified stream to muxer. Allowed values, 'video' - "
|
||||
"the first video stream; or 'audio' - the first audio stream; or "
|
||||
"zero based stream id.");
|
||||
DEFINE_double(clear_lead,
|
||||
10.0,
|
||||
"Clear lead in seconds if encryption is enabled.");
|
||||
DECLARE_double(clear_lead);
|
||||
|
||||
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_bool(normalize_presentation_timestamp,
|
||||
true,
|
||||
"Set to true to normalize the presentation timestamps to start"
|
||||
"from zero.");
|
||||
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.");
|
||||
DECLARE_bool(single_segment);
|
||||
DECLARE_double(segment_duration);
|
||||
DECLARE_bool(segment_sap_aligned);
|
||||
DECLARE_double(fragment_duration);
|
||||
DECLARE_bool(fragment_sap_aligned);
|
||||
DECLARE_bool(normalize_presentation_timestamp);
|
||||
DECLARE_int32(num_subsegments_per_sidx);
|
||||
DECLARE_string(temp_dir);
|
||||
|
||||
// Flags for MuxerListener.
|
||||
DEFINE_bool(output_media_info,
|
||||
true,
|
||||
"Create a human readable format of MediaInfo. The output file name "
|
||||
"will be the name specified by output flag, suffixed with "
|
||||
"'.media_info'.");
|
||||
DEFINE_string(scheme_id_uri,
|
||||
"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed",
|
||||
"This flag only applies if output_media_info is true. This value "
|
||||
"will be set in MediaInfo if the stream is encrypted. "
|
||||
"If the stream is encrypted, MPD requires a <ContentProtection> "
|
||||
"element which requires the schemeIdUri attribute. "
|
||||
"Default value is Widevine PSSH system ID, and it is valid only "
|
||||
"for ISO BMFF.");
|
||||
DECLARE_bool(output_media_info);
|
||||
DECLARE_string(scheme_id_uri);
|
||||
|
||||
#endif // APP_MUXER_FLAGS_H_
|
||||
|
|
|
@ -10,27 +10,18 @@
|
|||
#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/string_number_conversions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "media/base/demuxer.h"
|
||||
#include "media/base/media_stream.h"
|
||||
#include "media/base/muxer.h"
|
||||
#include "media/base/muxer_options.h"
|
||||
#include "media/base/request_signer.h"
|
||||
#include "media/base/stream_info.h"
|
||||
#include "media/base/widevine_encryption_key_source.h"
|
||||
#include "media/event/vod_media_info_dump_muxer_listener.h"
|
||||
#include "media/file/file.h"
|
||||
#include "media/file/file_closer.h"
|
||||
#include "media/formats/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 {
|
||||
|
||||
|
@ -76,7 +67,7 @@ scoped_ptr<EncryptionKeySource> CreateEncryptionKeySource() {
|
|||
}
|
||||
|
||||
encryption_key_source.reset(new WidevineEncryptionKeySource(
|
||||
FLAGS_server_url,
|
||||
FLAGS_key_server_url,
|
||||
FLAGS_content_id,
|
||||
signer.Pass(),
|
||||
FLAGS_crypto_period_duration == 0 ? kDisableKeyRotation : 0));
|
||||
|
@ -98,19 +89,7 @@ bool GetMuxerOptions(MuxerOptions* muxer_options) {
|
|||
muxer_options->normalize_presentation_timestamp =
|
||||
FLAGS_normalize_presentation_timestamp;
|
||||
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 (!base::CreateTemporaryFile(&path)) {
|
||||
LOG(ERROR) << "Failed to create a temporary file.";
|
||||
return false;
|
||||
}
|
||||
muxer_options->temp_file_name = path.value();
|
||||
}
|
||||
muxer_options->temp_dir = FLAGS_temp_dir;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -130,20 +109,22 @@ MediaStream* FindFirstAudioStream(const std::vector<MediaStream*>& streams) {
|
|||
return FindFirstStreamOfType(streams, kStreamAudio);
|
||||
}
|
||||
|
||||
bool AddStreamToMuxer(const std::vector<MediaStream*>& streams, Muxer* muxer) {
|
||||
bool AddStreamToMuxer(const std::vector<MediaStream*>& streams,
|
||||
const std::string& stream_selector,
|
||||
Muxer* muxer) {
|
||||
DCHECK(muxer);
|
||||
|
||||
MediaStream* stream = NULL;
|
||||
if (FLAGS_stream == "video") {
|
||||
if (stream_selector == "video") {
|
||||
stream = FindFirstVideoStream(streams);
|
||||
} else if (FLAGS_stream == "audio") {
|
||||
} else if (stream_selector == "audio") {
|
||||
stream = FindFirstAudioStream(streams);
|
||||
} else {
|
||||
// Expect FLAGS_stream to be a zero based stream id.
|
||||
// Expect stream_selector to be a zero based stream id.
|
||||
size_t stream_id;
|
||||
if (!base::StringToSizeT(FLAGS_stream, &stream_id) ||
|
||||
if (!base::StringToSizeT(stream_selector, &stream_id) ||
|
||||
stream_id >= streams.size()) {
|
||||
LOG(ERROR) << "Invalid argument --stream=" << FLAGS_stream << "; "
|
||||
LOG(ERROR) << "Invalid argument --stream=" << stream_selector << "; "
|
||||
<< "should be 'audio', 'video', or a number within [0, "
|
||||
<< streams.size() - 1 << "].";
|
||||
return false;
|
||||
|
@ -152,93 +133,14 @@ bool AddStreamToMuxer(const std::vector<MediaStream*>& streams, Muxer* muxer) {
|
|||
DCHECK(stream);
|
||||
}
|
||||
|
||||
// This could occur only if FLAGS_stream=audio|video and the corresponding
|
||||
// This could occur only if stream_selector=audio|video and the corresponding
|
||||
// stream does not exist in the input.
|
||||
if (!stream) {
|
||||
LOG(ERROR) << "No " << FLAGS_stream << " stream found in the input.";
|
||||
LOG(ERROR) << "No " << stream_selector << " stream found in the input.";
|
||||
return false;
|
||||
}
|
||||
muxer->AddStream(stream);
|
||||
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()) {
|
||||
if (!FLAGS_dump_stream_info)
|
||||
LOG(WARNING) << "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));
|
||||
scoped_ptr<event::MuxerListener> muxer_listener;
|
||||
scoped_ptr<File, FileCloser> mpd_file;
|
||||
if (FLAGS_output_media_info) {
|
||||
std::string output_mpd_file_name = FLAGS_output + ".media_info";
|
||||
mpd_file.reset(File::Open(output_mpd_file_name.c_str(), "w"));
|
||||
if (!mpd_file) {
|
||||
LOG(ERROR) << "Failed to open " << output_mpd_file_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
scoped_ptr<event::VodMediaInfoDumpMuxerListener> media_info_muxer_listener(
|
||||
new event::VodMediaInfoDumpMuxerListener(mpd_file.get()));
|
||||
media_info_muxer_listener->SetContentProtectionSchemeIdUri(
|
||||
FLAGS_scheme_id_uri);
|
||||
muxer_listener = media_info_muxer_listener.Pass();
|
||||
muxer->SetMuxerListener(muxer_listener.get());
|
||||
}
|
||||
|
||||
if (!AddStreamToMuxer(demuxer.streams(), muxer.get()))
|
||||
return false;
|
||||
|
||||
scoped_ptr<EncryptionKeySource> encryption_key_source;
|
||||
if (FLAGS_enable_widevine_encryption || FLAGS_enable_fixed_key_encryption) {
|
||||
encryption_key_source = CreateEncryptionKeySource();
|
||||
if (!encryption_key_source)
|
||||
return false;
|
||||
muxer->SetEncryptionKeySource(encryption_key_source.get(),
|
||||
FLAGS_max_sd_pixels,
|
||||
FLAGS_clear_lead,
|
||||
FLAGS_crypto_period_duration);
|
||||
}
|
||||
|
||||
// Start remuxing process.
|
||||
status = demuxer.Run();
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Remuxing failed: " << status.ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Packaging completed successfully.\n");
|
||||
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,52 @@
|
|||
// Copyright 2014 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 or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
//
|
||||
// Functionality common to single and multiple file packager.
|
||||
|
||||
#ifndef APP_PACKAGER_COMMON_H_
|
||||
#define APP_PACKAGER_COMMON_H_
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
|
||||
DECLARE_bool(dump_stream_info);
|
||||
|
||||
namespace media {
|
||||
|
||||
class EncryptionKeySource;
|
||||
class MediaInfo;
|
||||
class MediaStream;
|
||||
class Muxer;
|
||||
class MuxerOptions;
|
||||
|
||||
/// Print all the stream info for the provided strings to standard output.
|
||||
void DumpStreamInfo(const std::vector<MediaStream*>& streams);
|
||||
|
||||
/// Create EncryptionKeySource based on provided command line options.
|
||||
/// @return A scoped_ptr containig a new EncryptionKeySource, or NULL if
|
||||
/// encryption is not required.
|
||||
scoped_ptr<EncryptionKeySource> CreateEncryptionKeySource();
|
||||
|
||||
/// Fill MuxerOptions members using provided command line options.
|
||||
bool GetMuxerOptions(MuxerOptions* muxer_options);
|
||||
|
||||
/// Select and add a stream from a provided set to a muxer.
|
||||
/// @param streams contains the set of MediaStreams from which to select.
|
||||
/// @param stream_selector is a string containing one of the following values:
|
||||
/// "audio" to select the first audio track, "video" to select the first
|
||||
/// video track, or a decimal number indicating which track number to
|
||||
/// select (start at "1").
|
||||
/// @return true if successful, false otherwise.
|
||||
bool AddStreamToMuxer(const std::vector<MediaStream*>& streams,
|
||||
const std::string& stream_selector,
|
||||
Muxer* muxer);
|
||||
|
||||
} // namespace media
|
||||
|
||||
#endif // APP_PACKAGER_COMMON_H_
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2014 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 or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
//
|
||||
// Defines Muxer flags.
|
||||
|
||||
#include "app/muxer_flags.h"
|
||||
|
||||
DEFINE_string(stream,
|
||||
"video",
|
||||
"Add the specified stream to muxer. Allowed values, 'video' - "
|
||||
"the first video stream; or 'audio' - the first audio stream; or "
|
||||
"zero based stream id.");
|
||||
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$.");
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2014 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 or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
//
|
||||
// Defines Single muxer flags.
|
||||
|
||||
#ifndef APP_SINGLE_MUXER_FLAGS_H_
|
||||
#define APP_SINGLE_MUXER_FLAGS_H_
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
DECLARE_string(stream);
|
||||
DECLARE_string(output);
|
||||
DECLARE_string(segment_template);
|
||||
|
||||
#endif // APP_SINGLE_MUXER_FLAGS_H_
|
|
@ -0,0 +1,123 @@
|
|||
// Copyright 2014 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 or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "app/fixed_key_encryption_flags.h"
|
||||
#include "app/packager_common.h"
|
||||
#include "app/muxer_flags.h"
|
||||
#include "app/single_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/encryption_key_source.h"
|
||||
#include "media/base/muxer_options.h"
|
||||
#include "media/event/vod_media_info_dump_muxer_listener.h"
|
||||
#include "media/file/file.h"
|
||||
#include "media/file/file_closer.h"
|
||||
#include "media/formats/mp4/mp4_muxer.h"
|
||||
|
||||
namespace {
|
||||
const char kUsage[] =
|
||||
"Packager driver program. Sample Usage:\n%s <input> [flags]";
|
||||
} // namespace
|
||||
|
||||
namespace media {
|
||||
|
||||
bool GetSingleMuxerOptions(MuxerOptions* muxer_options) {
|
||||
DCHECK(muxer_options);
|
||||
|
||||
if (!GetMuxerOptions(muxer_options))
|
||||
return false;
|
||||
|
||||
muxer_options->output_file_name = FLAGS_output;
|
||||
muxer_options->segment_template = FLAGS_segment_template;
|
||||
|
||||
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()) {
|
||||
if (!FLAGS_dump_stream_info)
|
||||
LOG(WARNING) << "No output specified. Exiting.";
|
||||
return true;
|
||||
}
|
||||
|
||||
// Setup muxer.
|
||||
MuxerOptions muxer_options;
|
||||
if (!GetSingleMuxerOptions(&muxer_options))
|
||||
return false;
|
||||
|
||||
scoped_ptr<Muxer> muxer(new mp4::MP4Muxer(muxer_options));
|
||||
scoped_ptr<event::MuxerListener> muxer_listener;
|
||||
scoped_ptr<File, FileCloser> mpd_file;
|
||||
if (FLAGS_output_media_info) {
|
||||
std::string output_mpd_file_name = FLAGS_output + ".media_info";
|
||||
mpd_file.reset(File::Open(output_mpd_file_name.c_str(), "w"));
|
||||
if (!mpd_file) {
|
||||
LOG(ERROR) << "Failed to open " << output_mpd_file_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
scoped_ptr<event::VodMediaInfoDumpMuxerListener> media_info_muxer_listener(
|
||||
new event::VodMediaInfoDumpMuxerListener(mpd_file.get()));
|
||||
media_info_muxer_listener->SetContentProtectionSchemeIdUri(
|
||||
FLAGS_scheme_id_uri);
|
||||
muxer_listener = media_info_muxer_listener.Pass();
|
||||
muxer->SetMuxerListener(muxer_listener.get());
|
||||
}
|
||||
|
||||
if (!AddStreamToMuxer(demuxer.streams(), FLAGS_stream, muxer.get()))
|
||||
return false;
|
||||
|
||||
scoped_ptr<EncryptionKeySource> encryption_key_source;
|
||||
if (FLAGS_enable_widevine_encryption || FLAGS_enable_fixed_key_encryption) {
|
||||
encryption_key_source = CreateEncryptionKeySource();
|
||||
if (!encryption_key_source)
|
||||
return false;
|
||||
muxer->SetEncryptionKeySource(encryption_key_source.get(),
|
||||
FLAGS_max_sd_pixels,
|
||||
FLAGS_clear_lead,
|
||||
FLAGS_crypto_period_duration);
|
||||
}
|
||||
|
||||
// Start remuxing process.
|
||||
status = demuxer.Run();
|
||||
if (!status.ok()) {
|
||||
LOG(ERROR) << "Remuxing failed: " << status.ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Packaging completed successfully.\n");
|
||||
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,98 @@
|
|||
// Copyright 2014 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 or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
//
|
||||
// Defines command line flags for widevine_encryption.
|
||||
|
||||
#include "app/widevine_encryption_flags.h"
|
||||
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
|
||||
DEFINE_bool(enable_widevine_encryption,
|
||||
false,
|
||||
"Enable encryption with Widevine license server/proxy. User should "
|
||||
"provide either AES signing key (--aes_signing_key, "
|
||||
"--aes_signing_iv) or RSA signing key (--rsa_signing_key_path).");
|
||||
DEFINE_string(key_server_url, "", "Key server url.");
|
||||
DEFINE_string(content_id, "", "Content Id.");
|
||||
DEFINE_int32(max_sd_pixels,
|
||||
768 * 576,
|
||||
"If the video track has more pixels per frame than max_sd_pixels, "
|
||||
"it is considered as HD, SD otherwise. Default: 768 * 576.");
|
||||
DEFINE_string(signer, "", "The name of the signer.");
|
||||
DEFINE_string(aes_signing_key,
|
||||
"",
|
||||
"AES signing key in hex string. --aes_signing_iv is required. "
|
||||
"Exclusive with --rsa_signing_key_path.");
|
||||
DEFINE_string(aes_signing_iv,
|
||||
"",
|
||||
"AES signing iv in hex string.");
|
||||
DEFINE_string(rsa_signing_key_path,
|
||||
"",
|
||||
"Stores PKCS#1 RSA private key for request signing. Exclusive "
|
||||
"with --aes_signing_key.");
|
||||
DEFINE_int32(crypto_period_duration,
|
||||
0,
|
||||
"Crypto period duration in seconds. If it is non-zero, key "
|
||||
"rotation is enabled.");
|
||||
|
||||
namespace {
|
||||
|
||||
static bool IsNotEmptyWithWidevineEncryption(const char* flag_name,
|
||||
const std::string& flag_value) {
|
||||
return FLAGS_enable_widevine_encryption ? !flag_value.empty() : true;
|
||||
}
|
||||
|
||||
static bool IsPositive(const char* flag_name, int flag_value) {
|
||||
return flag_value > 0;
|
||||
}
|
||||
|
||||
static bool VerifyAesRsaKey(const char* flag_name,
|
||||
const std::string& flag_value) {
|
||||
if (!FLAGS_enable_widevine_encryption)
|
||||
return true;
|
||||
const std::string flag_name_str = flag_name;
|
||||
if (flag_name_str == "aes_signing_iv") {
|
||||
if (!FLAGS_aes_signing_key.empty() && flag_value.empty()) {
|
||||
fprintf(stderr,
|
||||
"ERROR: --aes_signing_iv is required for --aes_signing_key.\n");
|
||||
return false;
|
||||
}
|
||||
} else if (flag_name_str == "rsa_signing_key_path") {
|
||||
if (FLAGS_aes_signing_key.empty() && flag_value.empty()) {
|
||||
fprintf(stderr,
|
||||
"ERROR: --aes_signing_key or --rsa_signing_key_path is "
|
||||
"required.\n");
|
||||
return false;
|
||||
}
|
||||
if (!FLAGS_aes_signing_key.empty() && !flag_value.empty()) {
|
||||
fprintf(stderr,
|
||||
"ERROR: --aes_signing_key and --rsa_signing_key_path are "
|
||||
"exclusive.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dummy_key_server_url_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_key_server_url,
|
||||
&IsNotEmptyWithWidevineEncryption);
|
||||
bool dummy_content_id_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_content_id,
|
||||
&IsNotEmptyWithWidevineEncryption);
|
||||
bool dummy_track_type_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_max_sd_pixels, &IsPositive);
|
||||
bool dummy_signer_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_signer,
|
||||
&IsNotEmptyWithWidevineEncryption);
|
||||
bool dummy_aes_iv_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_aes_signing_iv,
|
||||
&VerifyAesRsaKey);
|
||||
bool dummy_rsa_key_file_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_rsa_signing_key_path,
|
||||
&VerifyAesRsaKey);
|
||||
|
||||
} // anonymous namespace
|
|
@ -11,88 +11,14 @@
|
|||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
DECLARE_bool(enable_widevine_encryption);
|
||||
DECLARE_string(key_server_url);
|
||||
DECLARE_string(content_id);
|
||||
DECLARE_int32(max_sd_pixels);
|
||||
DECLARE_string(signer);
|
||||
DECLARE_string(aes_signing_key);
|
||||
DECLARE_string(aes_signing_iv);
|
||||
DECLARE_string(rsa_signing_key_path);
|
||||
DECLARE_int32(crypto_period_duration);
|
||||
|
||||
DEFINE_bool(enable_widevine_encryption,
|
||||
false,
|
||||
"Enable encryption with Widevine license server/proxy. User should "
|
||||
"provide either AES signing key (--aes_signing_key, "
|
||||
"--aes_signing_iv) or RSA signing key (--rsa_signing_key_path).");
|
||||
DEFINE_string(server_url, "", "License server url.");
|
||||
DEFINE_string(content_id, "", "Content Id.");
|
||||
DEFINE_int32(max_sd_pixels,
|
||||
768 * 576,
|
||||
"If the video track has more pixels per frame than max_sd_pixels, "
|
||||
"it is considered as HD, SD otherwise. Default: 768 * 576.");
|
||||
DEFINE_string(signer, "", "The name of the signer.");
|
||||
DEFINE_string(aes_signing_key,
|
||||
"",
|
||||
"AES signing key in hex string. --aes_signing_iv is required. "
|
||||
"Exclusive with --rsa_signing_key_path.");
|
||||
DEFINE_string(aes_signing_iv,
|
||||
"",
|
||||
"AES signing iv in hex string.");
|
||||
DEFINE_string(rsa_signing_key_path,
|
||||
"",
|
||||
"Stores PKCS#1 RSA private key for request signing. Exclusive "
|
||||
"with --aes_signing_key.");
|
||||
DEFINE_int32(crypto_period_duration,
|
||||
0,
|
||||
"Crypto period duration in seconds. If it is non-zero, key "
|
||||
"rotation is enabled.");
|
||||
|
||||
static bool IsNotEmptyWithWidevineEncryption(const char* flag_name,
|
||||
const std::string& flag_value) {
|
||||
return FLAGS_enable_widevine_encryption ? !flag_value.empty() : true;
|
||||
}
|
||||
|
||||
static bool IsPositive(const char* flag_name, int flag_value) {
|
||||
return flag_value > 0;
|
||||
}
|
||||
|
||||
static bool VerifyAesRsaKey(const char* flag_name,
|
||||
const std::string& flag_value) {
|
||||
if (!FLAGS_enable_widevine_encryption)
|
||||
return true;
|
||||
const std::string flag_name_str = flag_name;
|
||||
if (flag_name_str == "aes_signing_iv") {
|
||||
if (!FLAGS_aes_signing_key.empty() && flag_value.empty()) {
|
||||
fprintf(stderr,
|
||||
"ERROR: --aes_signing_iv is required for --aes_signing_key.\n");
|
||||
return false;
|
||||
}
|
||||
} else if (flag_name_str == "rsa_signing_key_path") {
|
||||
if (FLAGS_aes_signing_key.empty() && flag_value.empty()) {
|
||||
fprintf(stderr,
|
||||
"ERROR: --aes_signing_key or --rsa_signing_key_path is "
|
||||
"required.\n");
|
||||
return false;
|
||||
}
|
||||
if (!FLAGS_aes_signing_key.empty() && !flag_value.empty()) {
|
||||
fprintf(stderr,
|
||||
"ERROR: --aes_signing_key and --rsa_signing_key_path are "
|
||||
"exclusive.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return 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_max_sd_pixels, &IsPositive);
|
||||
static bool dummy_signer_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_signer,
|
||||
&IsNotEmptyWithWidevineEncryption);
|
||||
static bool dummy_aes_iv_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_aes_signing_iv,
|
||||
&VerifyAesRsaKey);
|
||||
static bool dummy_rsa_key_file_validator =
|
||||
google::RegisterFlagValidator(&FLAGS_rsa_signing_key_path,
|
||||
&VerifyAesRsaKey);
|
||||
#endif // APP_WIDEVINE_ENCRYPTION_FLAGS_H_
|
||||
|
|
|
@ -60,8 +60,8 @@ struct MuxerOptions {
|
|||
/// Optional.
|
||||
std::string segment_template;
|
||||
|
||||
/// Specify the temporary file for on demand media file creation.
|
||||
std::string temp_file_name;
|
||||
/// Specify temporary directory for intermediate files.
|
||||
std::string temp_dir;
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
|
|
|
@ -117,7 +117,7 @@ void SetDefaultMuxerOptionsValues(MuxerOptions* muxer_options) {
|
|||
muxer_options->num_subsegments_per_sidx = 0;
|
||||
muxer_options->output_file_name = "test_output_file_name.mp4";
|
||||
muxer_options->segment_template.clear();
|
||||
muxer_options->temp_file_name.clear();
|
||||
muxer_options->temp_dir.clear();
|
||||
}
|
||||
|
||||
void ExpectMediaInfoEqual(const MediaInfo& expect, const MediaInfo& actual) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "media/formats/mp4/single_segment_segmenter.h"
|
||||
|
||||
#include "base/file_util.h"
|
||||
#include "media/base/buffer_writer.h"
|
||||
#include "media/base/media_stream.h"
|
||||
#include "media/base/muxer_options.h"
|
||||
|
@ -37,11 +38,20 @@ bool SingleSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
|
|||
}
|
||||
|
||||
Status SingleSegmentSegmenter::DoInitialize() {
|
||||
temp_file_.reset(File::Open(options().temp_file_name.c_str(), "w"));
|
||||
base::FilePath temp_file_path;
|
||||
if (options().temp_dir.empty() ?
|
||||
!base::CreateTemporaryFile(&temp_file_path) :
|
||||
!base::CreateTemporaryFileInDir(base::FilePath(options().temp_dir),
|
||||
&temp_file_path)) {
|
||||
return Status(error::FILE_FAILURE, "Unable to create temporary file.");
|
||||
}
|
||||
temp_file_name_ = temp_file_path.value();
|
||||
|
||||
temp_file_.reset(File::Open(temp_file_name_.c_str(), "w"));
|
||||
return temp_file_
|
||||
? Status::OK
|
||||
: Status(error::FILE_FAILURE,
|
||||
"Cannot open file to write " + options().temp_file_name);
|
||||
"Cannot open file to write " + temp_file_name_);
|
||||
}
|
||||
|
||||
Status SingleSegmentSegmenter::DoFinalize() {
|
||||
|
@ -53,7 +63,7 @@ Status SingleSegmentSegmenter::DoFinalize() {
|
|||
// Close the temp file to prepare for reading later.
|
||||
if (!temp_file_.release()->Close()) {
|
||||
return Status(error::FILE_FAILURE,
|
||||
"Cannot close the temp file " + options().temp_file_name);
|
||||
"Cannot close the temp file " + temp_file_name_);
|
||||
}
|
||||
|
||||
scoped_ptr<File, FileCloser> file(
|
||||
|
@ -74,10 +84,10 @@ Status SingleSegmentSegmenter::DoFinalize() {
|
|||
|
||||
// Load the temp file and write to output file.
|
||||
scoped_ptr<File, FileCloser> temp_file(
|
||||
File::Open(options().temp_file_name.c_str(), "r"));
|
||||
File::Open(temp_file_name_.c_str(), "r"));
|
||||
if (temp_file == NULL) {
|
||||
return Status(error::FILE_FAILURE,
|
||||
"Cannot open file to read " + options().temp_file_name);
|
||||
"Cannot open file to read " + temp_file_name_);
|
||||
}
|
||||
|
||||
const int kBufSize = 0x40000; // 256KB.
|
||||
|
@ -86,7 +96,7 @@ Status SingleSegmentSegmenter::DoFinalize() {
|
|||
int64 size = temp_file->Read(buf.get(), kBufSize);
|
||||
if (size <= 0) {
|
||||
return Status(error::FILE_FAILURE,
|
||||
"Failed to read file " + options().temp_file_name);
|
||||
"Failed to read file " + temp_file_name_);
|
||||
}
|
||||
int64 size_written = file->Write(buf.get(), size);
|
||||
if (size_written != size) {
|
||||
|
|
|
@ -44,6 +44,7 @@ class SingleSegmentSegmenter : public Segmenter {
|
|||
virtual Status DoFinalizeSegment() OVERRIDE;
|
||||
|
||||
scoped_ptr<SegmentIndex> vod_sidx_;
|
||||
std::string temp_file_name_;
|
||||
scoped_ptr<File, FileCloser> temp_file_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SingleSegmentSegmenter);
|
||||
|
|
|
@ -140,7 +140,7 @@ MuxerOptions PackagerTestBasic::SetupOptions(const std::string& output,
|
|||
|
||||
options.output_file_name = GetFullPath(output);
|
||||
options.segment_template = GetFullPath(kSegmentTemplate);
|
||||
options.temp_file_name = GetFullPath(output + ".temp");
|
||||
options.temp_dir = test_directory_.value();
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
11
packager.gyp
11
packager.gyp
|
@ -16,12 +16,19 @@
|
|||
},
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'packager_main',
|
||||
'target_name': 'single_packager',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'app/fixed_key_encryption_flags.cc',
|
||||
'app/fixed_key_encryption_flags.h',
|
||||
'app/packager_common.cc',
|
||||
'app/packager_common.h',
|
||||
'app/muxer_flags.cc',
|
||||
'app/muxer_flags.h',
|
||||
'app/packager_main.cc',
|
||||
'app/single_muxer_flags.cc',
|
||||
'app/single_muxer_flags.h',
|
||||
'app/single_packager_main.cc',
|
||||
'app/widevine_encryption_flags.cc',
|
||||
'app/widevine_encryption_flags.h',
|
||||
],
|
||||
'dependencies': [
|
||||
|
|
Loading…
Reference in New Issue