7 #include <gflags/gflags.h>
10 #include "packager/app/ad_cue_generator_flags.h"
11 #include "packager/app/crypto_flags.h"
12 #include "packager/app/hls_flags.h"
13 #include "packager/app/manifest_flags.h"
14 #include "packager/app/mpd_flags.h"
15 #include "packager/app/muxer_flags.h"
16 #include "packager/app/packager_util.h"
17 #include "packager/app/playready_key_encryption_flags.h"
18 #include "packager/app/protection_system_flags.h"
19 #include "packager/app/raw_key_encryption_flags.h"
20 #include "packager/app/stream_descriptor.h"
21 #include "packager/app/vlog_flags.h"
22 #include "packager/app/widevine_encryption_flags.h"
23 #include "packager/base/command_line.h"
24 #include "packager/base/logging.h"
25 #include "packager/base/optional.h"
26 #include "packager/base/strings/string_number_conversions.h"
27 #include "packager/base/strings/string_split.h"
28 #include "packager/base/strings/string_util.h"
29 #include "packager/base/strings/stringprintf.h"
30 #include "packager/file/file.h"
31 #include "packager/packager.h"
32 #include "packager/tools/license_notice.h"
40 DEFINE_bool(dump_stream_info,
false,
"Dump demuxed stream info.");
41 DEFINE_bool(licenses,
false,
"Dump licenses.");
42 DEFINE_bool(quiet,
false,
"When enabled, LOG(INFO) output is suppressed.");
43 DEFINE_bool(use_fake_clock_for_muxer,
45 "Set to true to use a fake clock for muxer. With this flag set, "
46 "creation time and modification time in outputs are set to 0. "
47 "Should only be used for testing.");
48 DEFINE_string(test_packager_version,
50 "Packager version for testing. Should be used for testing only.");
51 DEFINE_bool(single_threaded,
53 "If enabled, only use one thread when generating content.");
59 "%s [flags] <stream_descriptor> ...\n\n"
60 " stream_descriptor consists of comma separated field_name/value pairs:\n"
61 " field_name=value,[field_name=value,]...\n"
62 " Supported field names are as follows (names in parenthesis are alias):\n"
63 " - input (in): Required input/source media file path or network stream\n"
65 " - stream_selector (stream): Required field with value 'audio',\n"
66 " 'video', 'text', or stream number (zero based).\n"
67 " - output (out,init_segment): Required output file (single file) or\n"
68 " initialization file path (multiple file).\n"
69 " - segment_template (segment): Optional value which specifies the\n"
70 " naming pattern for the segment files, and that the stream should be\n"
71 " split into multiple files. Its presence should be consistent across\n"
73 " - bandwidth (bw): Optional value which contains a user-specified\n"
74 " maximum bit rate for the stream, in bits/sec. If specified, this\n"
75 " value is propagated to (HLS) EXT-X-STREAM-INF:BANDWIDTH or (DASH)\n"
76 " Representation@bandwidth and the $Bandwidth$ template parameter for\n"
77 " segment names. If not specified, the bandwidth value is estimated\n"
78 " from content bitrate. Note that it only affects the generated\n"
79 " manifests/playlists; it has no effect on the media content itself.\n"
80 " - language (lang): Optional value which contains a user-specified\n"
81 " language tag. If specified, this value overrides any language\n"
82 " metadata in the input stream.\n"
83 " - output_format (format): Optional value which specifies the format\n"
84 " of the output files (MP4 or WebM). If not specified, it will be\n"
85 " derived from the file extension of the output file.\n"
86 " - skip_encryption=0|1: Optional. Defaults to 0 if not specified. If\n"
87 " it is set to 1, no encryption of the stream will be made.\n"
88 " - drm_label: Optional value for custom DRM label, which defines the\n"
89 " encryption key applied to the stream. Typical values include AUDIO,\n"
90 " SD, HD, UHD1, UHD2. For raw key, it should be a label defined in\n"
91 " --keys. If not provided, the DRM label is derived from stream type\n"
92 " (video, audio), resolution, etc.\n"
93 " Note that it is case sensitive.\n"
94 " - trick_play_factor (tpf): Optional value which specifies the trick\n"
95 " play, a.k.a. trick mode, stream sampling rate among key frames.\n"
96 " If specified, the output is a trick play stream.\n"
97 " - hls_name: Used for HLS audio to set the NAME attribute for\n"
98 " EXT-X-MEDIA. Defaults to the base of the playlist name.\n"
99 " - hls_group_id: Used for HLS audio to set the GROUP-ID attribute for\n"
100 " EXT-X-MEDIA. Defaults to 'audio' if not specified.\n"
101 " - playlist_name: The HLS playlist file to create. Usually ends with\n"
102 " '.m3u8', and is relative to --hls_master_playlist_output. If\n"
103 " unspecified, defaults to something of the form 'stream_0.m3u8',\n"
104 " 'stream_1.m3u8', 'stream_2.m3u8', etc.\n"
105 " - iframe_playlist_name: The optional HLS I-Frames only playlist file\n"
106 " to create. Usually ends with '.m3u8', and is relative to\n"
107 " hls_master_playlist_output. Should only be set for video streams. If\n"
108 " unspecified, no I-Frames only playlist is created.\n"
109 " - hls_characteristics (charcs): Optional colon/semicolon separated\n"
110 " list of values for the CHARACTERISTICS attribute for EXT-X-MEDIA.\n"
111 " See CHARACTERISTICS attribute in http://bit.ly/2OOUkdB for details.\n"
112 " - dash_accessibilities (accessibilities): Optional semicolon separated\n"
113 " list of values for DASH Accessibility elements. The value should be\n"
114 " in the format: scheme_id_uri=value.\n"
115 " - dash_roles (roles): Optional semicolon separated list of values for\n"
116 " DASH Role elements. The value should be one of: caption, subtitle,\n"
117 " main, alternate, supplementary, commentary and dub. See DASH\n"
118 " (ISO/IEC 23009-1) specification for details.\n";
121 const char kDrmLabelLabel[] =
"label";
122 const char kKeyIdLabel[] =
"key_id";
123 const char kKeyLabel[] =
"key";
124 const char kKeyIvLabel[] =
"iv";
128 kArgumentValidationFailed,
133 bool GetWidevineSigner(WidevineSigner* signer) {
134 signer->signer_name = FLAGS_signer;
135 if (!FLAGS_aes_signing_key_bytes.empty()) {
136 signer->signing_key_type = WidevineSigner::SigningKeyType::kAes;
137 signer->aes.key = FLAGS_aes_signing_key_bytes;
138 signer->aes.iv = FLAGS_aes_signing_iv_bytes;
139 }
else if (!FLAGS_rsa_signing_key_path.empty()) {
140 signer->signing_key_type = WidevineSigner::SigningKeyType::kRsa;
143 LOG(ERROR) <<
"Failed to read from '" << FLAGS_rsa_signing_key_path
151 bool GetHlsPlaylistType(
const std::string& playlist_type,
153 if (base::ToUpperASCII(playlist_type) ==
"VOD") {
154 *playlist_type_enum = HlsPlaylistType::kVod;
155 }
else if (base::ToUpperASCII(playlist_type) ==
"LIVE") {
156 *playlist_type_enum = HlsPlaylistType::kLive;
157 }
else if (base::ToUpperASCII(playlist_type) ==
"EVENT") {
158 *playlist_type_enum = HlsPlaylistType::kEvent;
160 LOG(ERROR) <<
"Unrecognized playlist type " << playlist_type;
166 bool GetProtectionScheme(uint32_t* protection_scheme) {
167 if (FLAGS_protection_scheme ==
"cenc") {
171 if (FLAGS_protection_scheme ==
"cbc1") {
172 *protection_scheme = EncryptionParams::kProtectionSchemeCbc1;
175 if (FLAGS_protection_scheme ==
"cbcs") {
176 *protection_scheme = EncryptionParams::kProtectionSchemeCbcs;
179 if (FLAGS_protection_scheme ==
"cens") {
180 *protection_scheme = EncryptionParams::kProtectionSchemeCens;
183 LOG(ERROR) <<
"Unrecognized protection_scheme " << FLAGS_protection_scheme;
187 bool ParseKeys(
const std::string& keys, RawKeyParams* raw_key) {
188 for (
const std::string& key_data : base::SplitString(
189 keys,
",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
190 base::StringPairs string_pairs;
191 base::SplitStringIntoKeyValuePairs(key_data,
'=',
':', &string_pairs);
193 std::map<std::string, std::string> value_map;
194 for (
const auto& string_pair : string_pairs)
195 value_map[string_pair.first] = string_pair.second;
196 const std::string drm_label = value_map[kDrmLabelLabel];
197 if (raw_key->key_map.find(drm_label) != raw_key->key_map.end()) {
198 LOG(ERROR) <<
"Seeing duplicated DRM label '" << drm_label <<
"'.";
201 auto& key_info = raw_key->key_map[drm_label];
202 if (value_map[kKeyIdLabel].empty() ||
203 !base::HexStringToBytes(value_map[kKeyIdLabel], &key_info.key_id)) {
204 LOG(ERROR) <<
"Empty key id or invalid hex string for key id: "
205 << value_map[kKeyIdLabel];
208 if (value_map[kKeyLabel].empty() ||
209 !base::HexStringToBytes(value_map[kKeyLabel], &key_info.key)) {
210 LOG(ERROR) <<
"Empty key or invalid hex string for key: "
211 << value_map[kKeyLabel];
214 if (!value_map[kKeyIvLabel].empty()) {
215 if (!raw_key->iv.empty()) {
216 LOG(ERROR) <<
"IV already specified with --iv";
219 if (!base::HexStringToBytes(value_map[kKeyIvLabel], &key_info.iv)) {
220 LOG(ERROR) <<
"Empty IV or invalid hex string for IV: "
221 << value_map[kKeyIvLabel];
229 bool GetRawKeyParams(RawKeyParams* raw_key) {
230 raw_key->iv = FLAGS_iv_bytes;
231 raw_key->pssh = FLAGS_pssh_bytes;
232 if (!FLAGS_keys.empty()) {
233 if (!ParseKeys(FLAGS_keys, raw_key)) {
234 LOG(ERROR) <<
"Failed to parse --keys " << FLAGS_keys;
239 RawKeyParams::KeyInfo& key_info = raw_key->key_map[
""];
240 key_info.key_id = FLAGS_key_id_bytes;
241 key_info.key = FLAGS_key_bytes;
246 bool ParseAdCues(
const std::string& ad_cues, std::vector<Cuepoint>* cuepoints) {
248 size_t duration_count = 0;
250 for (
const std::string& ad_cue : base::SplitString(
251 ad_cues,
";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
253 auto split_ad_cue = base::SplitString(ad_cue,
",", base::TRIM_WHITESPACE,
254 base::SPLIT_WANT_NONEMPTY);
255 if (split_ad_cue.size() > 2) {
256 LOG(ERROR) <<
"Failed to parse --ad_cues " << ad_cues
257 <<
" Each ad cue must contain no more than 2 components.";
259 if (!base::StringToDouble(split_ad_cue.front(),
260 &cuepoint.start_time_in_seconds)) {
261 LOG(ERROR) <<
"Failed to parse --ad_cues " << ad_cues
262 <<
" Start time component must be of type double.";
265 if (split_ad_cue.size() > 1) {
267 if (!base::StringToDouble(split_ad_cue[1],
268 &cuepoint.duration_in_seconds)) {
269 LOG(ERROR) <<
"Failed to parse --ad_cues " << ad_cues
270 <<
" Duration component must be of type double.";
274 cuepoints->push_back(cuepoint);
277 if (duration_count > 0 && duration_count != cuepoints->size()) {
278 LOG(ERROR) <<
"Failed to parse --ad_cues " << ad_cues
279 <<
" Duration component is optional. However if it is supplied,"
280 <<
" it must be supplied consistently across all cuepoints.";
286 bool ParseProtectionSystems(
const std::string& protection_systems_str,
288 *protection_systems = ProtectionSystem::kNone;
290 std::map<std::string, ProtectionSystem> mapping = {
293 {
"fairplay", ProtectionSystem::kFairPlay},
294 {
"marlin", ProtectionSystem::kMarlin},
295 {
"playready", ProtectionSystem::kPlayReady},
296 {
"widevine", ProtectionSystem::kWidevine},
299 for (
const std::string& protection_system :
300 base::SplitString(base::ToLowerASCII(protection_systems_str),
",",
301 base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
302 auto iter = mapping.find(protection_system);
303 if (iter == mapping.end()) {
304 LOG(ERROR) <<
"Seeing unrecognized protection system: "
305 << protection_system;
308 *protection_systems |= iter->second;
313 base::Optional<PackagingParams> GetPackagingParams() {
314 PackagingParams packaging_params;
316 packaging_params.temp_dir = FLAGS_temp_dir;
317 packaging_params.single_threaded = FLAGS_single_threaded;
319 AdCueGeneratorParams& ad_cue_generator_params =
320 packaging_params.ad_cue_generator_params;
321 if (!ParseAdCues(FLAGS_ad_cues, &ad_cue_generator_params.cue_points)) {
322 return base::nullopt;
325 ChunkingParams& chunking_params = packaging_params.chunking_params;
326 chunking_params.segment_duration_in_seconds = FLAGS_segment_duration;
327 chunking_params.subsegment_duration_in_seconds = FLAGS_fragment_duration;
328 chunking_params.segment_sap_aligned = FLAGS_segment_sap_aligned;
329 chunking_params.subsegment_sap_aligned = FLAGS_fragment_sap_aligned;
331 int num_key_providers = 0;
332 EncryptionParams& encryption_params = packaging_params.encryption_params;
333 if (FLAGS_enable_widevine_encryption) {
334 encryption_params.key_provider = KeyProvider::kWidevine;
337 if (FLAGS_enable_playready_encryption) {
338 encryption_params.key_provider = KeyProvider::kPlayReady;
341 if (FLAGS_enable_raw_key_encryption) {
342 encryption_params.key_provider = KeyProvider::kRawKey;
345 if (num_key_providers > 1) {
346 LOG(ERROR) <<
"Only one of --enable_widevine_encryption, "
347 "--enable_playready_encryption, "
348 "--enable_raw_key_encryption can be enabled.";
349 return base::nullopt;
352 if (!ParseProtectionSystems(FLAGS_protection_systems,
353 &encryption_params.protection_systems)) {
354 return base::nullopt;
357 if (encryption_params.key_provider != KeyProvider::kNone) {
358 encryption_params.clear_lead_in_seconds = FLAGS_clear_lead;
359 if (!GetProtectionScheme(&encryption_params.protection_scheme))
360 return base::nullopt;
361 encryption_params.crypt_byte_block = FLAGS_crypt_byte_block;
362 encryption_params.skip_byte_block = FLAGS_skip_byte_block;
364 encryption_params.crypto_period_duration_in_seconds =
365 FLAGS_crypto_period_duration;
366 encryption_params.vp9_subsample_encryption = FLAGS_vp9_subsample_encryption;
367 encryption_params.stream_label_func = std::bind(
369 FLAGS_max_hd_pixels, FLAGS_max_uhd1_pixels, std::placeholders::_1);
370 encryption_params.playready_extra_header_data =
371 FLAGS_playready_extra_header_data;
373 switch (encryption_params.key_provider) {
374 case KeyProvider::kWidevine: {
375 WidevineEncryptionParams& widevine = encryption_params.widevine;
376 widevine.key_server_url = FLAGS_key_server_url;
378 widevine.content_id = FLAGS_content_id_bytes;
379 widevine.policy = FLAGS_policy;
380 widevine.group_id = FLAGS_group_id_bytes;
381 widevine.enable_entitlement_license = FLAGS_enable_entitlement_license;
382 if (!GetWidevineSigner(&widevine.signer))
383 return base::nullopt;
386 case KeyProvider::kPlayReady: {
387 PlayReadyEncryptionParams& playready = encryption_params.playready;
388 playready.key_server_url = FLAGS_playready_server_url;
389 playready.program_identifier = FLAGS_program_identifier;
392 case KeyProvider::kRawKey: {
393 if (!GetRawKeyParams(&encryption_params.raw_key))
394 return base::nullopt;
397 case KeyProvider::kNone:
401 num_key_providers = 0;
402 DecryptionParams& decryption_params = packaging_params.decryption_params;
403 if (FLAGS_enable_widevine_decryption) {
404 decryption_params.key_provider = KeyProvider::kWidevine;
407 if (FLAGS_enable_raw_key_decryption) {
408 decryption_params.key_provider = KeyProvider::kRawKey;
411 if (num_key_providers > 1) {
412 LOG(ERROR) <<
"Only one of --enable_widevine_decryption, "
413 "--enable_raw_key_decryption can be enabled.";
414 return base::nullopt;
416 switch (decryption_params.key_provider) {
417 case KeyProvider::kWidevine: {
418 WidevineDecryptionParams& widevine = decryption_params.widevine;
419 widevine.key_server_url = FLAGS_key_server_url;
420 if (!GetWidevineSigner(&widevine.signer))
421 return base::nullopt;
424 case KeyProvider::kRawKey: {
425 if (!GetRawKeyParams(&decryption_params.raw_key))
426 return base::nullopt;
429 case KeyProvider::kPlayReady:
430 case KeyProvider::kNone:
434 Mp4OutputParams& mp4_params = packaging_params.mp4_output_params;
435 mp4_params.generate_sidx_in_media_segments =
436 FLAGS_generate_sidx_in_media_segments;
437 mp4_params.include_pssh_in_stream = FLAGS_mp4_include_pssh_in_stream;
439 packaging_params.transport_stream_timestamp_offset_ms =
440 FLAGS_transport_stream_timestamp_offset_ms;
442 packaging_params.output_media_info = FLAGS_output_media_info;
444 MpdParams& mpd_params = packaging_params.mpd_params;
445 mpd_params.mpd_output = FLAGS_mpd_output;
446 mpd_params.base_urls = base::SplitString(
447 FLAGS_base_urls,
",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
448 mpd_params.min_buffer_time = FLAGS_min_buffer_time;
449 mpd_params.minimum_update_period = FLAGS_minimum_update_period;
450 mpd_params.suggested_presentation_delay = FLAGS_suggested_presentation_delay;
451 mpd_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
452 mpd_params.preserved_segments_outside_live_window =
453 FLAGS_preserved_segments_outside_live_window;
454 mpd_params.use_segment_list = FLAGS_dash_force_segment_list;
456 if (!FLAGS_utc_timings.empty()) {
457 base::StringPairs pairs;
458 if (!base::SplitStringIntoKeyValuePairs(FLAGS_utc_timings,
'=',
',',
460 LOG(ERROR) <<
"Invalid --utc_timings scheme_id_uri/value pairs.";
461 return base::nullopt;
463 for (
const auto& string_pair : pairs) {
464 mpd_params.utc_timings.push_back({string_pair.first, string_pair.second});
468 mpd_params.default_language = FLAGS_default_language;
469 mpd_params.default_text_language = FLAGS_default_text_language;
470 mpd_params.generate_static_live_mpd = FLAGS_generate_static_live_mpd;
471 mpd_params.generate_dash_if_iop_compliant_mpd =
472 FLAGS_generate_dash_if_iop_compliant_mpd;
473 mpd_params.allow_approximate_segment_timeline =
474 FLAGS_allow_approximate_segment_timeline;
475 mpd_params.allow_codec_switching = FLAGS_allow_codec_switching;
476 mpd_params.include_mspr_pro = FLAGS_include_mspr_pro_for_playready;
478 HlsParams& hls_params = packaging_params.hls_params;
479 if (!GetHlsPlaylistType(FLAGS_hls_playlist_type, &hls_params.playlist_type)) {
480 return base::nullopt;
482 hls_params.master_playlist_output = FLAGS_hls_master_playlist_output;
483 hls_params.base_url = FLAGS_hls_base_url;
484 hls_params.key_uri = FLAGS_hls_key_uri;
485 hls_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
486 hls_params.preserved_segments_outside_live_window =
487 FLAGS_preserved_segments_outside_live_window;
488 hls_params.default_language = FLAGS_default_language;
489 hls_params.default_text_language = FLAGS_default_text_language;
490 hls_params.media_sequence_number = FLAGS_hls_media_sequence_number;
492 TestParams& test_params = packaging_params.test_params;
493 test_params.dump_stream_info = FLAGS_dump_stream_info;
494 test_params.inject_fake_clock = FLAGS_use_fake_clock_for_muxer;
495 if (!FLAGS_test_packager_version.empty())
496 test_params.injected_library_version = FLAGS_test_packager_version;
498 return packaging_params;
501 int PackagerMain(
int argc,
char** argv) {
503 base::CommandLine::Init(argc, argv);
506 logging::LoggingSettings log_settings;
507 log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
508 CHECK(logging::InitLogging(log_settings));
511 google::SetUsageMessage(base::StringPrintf(kUsage, argv[0]));
512 google::ParseCommandLineFlags(&argc, &argv,
true);
513 if (FLAGS_licenses) {
514 for (
const char* line : kLicenseNotice)
515 std::cout << line << std::endl;
519 google::ShowUsageWithFlags(
"Usage");
523 logging::SetMinLogLevel(logging::LOG_WARNING);
527 return kArgumentValidationFailed;
530 base::Optional<PackagingParams> packaging_params = GetPackagingParams();
531 if (!packaging_params)
532 return kArgumentValidationFailed;
534 std::vector<StreamDescriptor> stream_descriptors;
535 for (
int i = 1; i < argc; ++i) {
536 base::Optional<StreamDescriptor> stream_descriptor =
538 if (!stream_descriptor)
539 return kArgumentValidationFailed;
540 stream_descriptors.push_back(stream_descriptor.value());
544 packager.Initialize(packaging_params.value(), stream_descriptors);
546 LOG(ERROR) <<
"Failed to initialize packager: " << status.ToString();
547 return kArgumentValidationFailed;
549 status = packager.Run();
551 LOG(ERROR) <<
"Packaging Error: " << status.ToString();
552 return kPackagingFailed;
555 printf(
"Packaging completed successfully.\n");
564 int wmain(
int argc,
wchar_t* argv[],
wchar_t* envp[]) {
565 std::unique_ptr<
char* [], std::function<void(
char**)>> utf8_argv(
566 new char*[argc], [argc](
char** utf8_args) {
574 std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
575 for (
int idx = 0; idx < argc; ++idx) {
576 std::string utf8_arg(converter.to_bytes(argv[idx]));
578 utf8_argv[idx] =
new char[utf8_arg.size()];
579 memcpy(utf8_argv[idx], &utf8_arg[0], utf8_arg.size());
581 return shaka::PackagerMain(argc, utf8_argv.get());
584 int main(
int argc,
char** argv) {
585 return shaka::PackagerMain(argc, argv);
static bool ReadFileToString(const char *file_name, std::string *contents)
static std::string DefaultStreamLabelFunction(int max_sd_pixels, int max_hd_pixels, int max_uhd1_pixels, const EncryptionParams::EncryptedStreamAttributes &stream_attributes)
static std::string GetLibraryVersion()
All the methods that are virtual are virtual for mocking.
bool ValidatePRCryptoFlags()
base::Optional< StreamDescriptor > ParseStreamDescriptor(const std::string &descriptor_string)
@ kCommon
The common key system from EME: https://goo.gl/s8RIhr.
bool ValidateRawKeyCryptoFlags()
bool ValidateWidevineCryptoFlags()
static constexpr uint32_t kProtectionSchemeCenc
The protection scheme: "cenc", "cens", "cbc1", "cbcs".