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" 38 #endif // defined(OS_WIN) 40 DEFINE_bool(dump_stream_info,
false,
"Dump demuxed stream info.");
41 DEFINE_bool(licenses,
false,
"Dump licenses.");
42 DEFINE_bool(use_fake_clock_for_muxer,
44 "Set to true to use a fake clock for muxer. With this flag set, " 45 "creation time and modification time in outputs are set to 0. " 46 "Should only be used for testing.");
47 DEFINE_bool(override_version,
49 "Override packager version in the generated outputs with " 50 "--test_version if it is set to true. Should be used for " 52 DEFINE_string(test_version,
54 "Packager version for testing. Ignored if --override_version is " 55 "false. Should be used for testing only.");
61 "%s [flags] <stream_descriptor> ...\n\n" 62 " stream_descriptor consists of comma separated field_name/value pairs:\n" 63 " field_name=value,[field_name=value,]...\n" 64 " Supported field names are as follows (names in parenthesis are alias):\n" 65 " - input (in): Required input/source media file path or network stream\n" 67 " - stream_selector (stream): Required field with value 'audio',\n" 68 " 'video', 'text', or stream number (zero based).\n" 69 " - output (out,init_segment): Required output file (single file) or\n" 70 " initialization file path (multiple file).\n" 71 " - segment_template (segment): Optional value which specifies the\n" 72 " naming pattern for the segment files, and that the stream should be\n" 73 " split into multiple files. Its presence should be consistent across\n" 75 " - bandwidth (bw): Optional value which contains a user-specified\n" 76 " content bit rate for the stream, in bits/sec. If specified, this\n" 77 " value is propagated to (HLS) EXT-X-STREAM-INF:BANDWIDTH or (DASH)\n" 78 " Representation@bandwidth and the $Bandwidth$ template parameter for\n" 79 " segment names. If not specified, the bandwidth value is estimated\n" 80 " from content bitrate. Note that it only affects the generated\n" 81 " manifests/playlists; it has no effect on the media content itself.\n" 82 " - language (lang): Optional value which contains a user-specified\n" 83 " language tag. If specified, this value overrides any language\n" 84 " metadata in the input stream.\n" 85 " - output_format (format): Optional value which specifies the format\n" 86 " of the output files (MP4 or WebM). If not specified, it will be\n" 87 " derived from the file extension of the output file.\n" 88 " - skip_encryption=0|1: Optional. Defaults to 0 if not specified. If\n" 89 " it is set to 1, no encryption of the stream will be made.\n" 90 " - drm_label: Optional value for custom DRM label, which defines the\n" 91 " encryption key applied to the stream. Typical values include AUDIO,\n" 92 " SD, HD, UHD1, UHD2. For raw key, it should be a label defined in\n" 93 " --keys. If not provided, the DRM label is derived from stream type\n" 94 " (video, audio), resolution, etc.\n" 95 " Note that it is case sensitive.\n" 96 " - trick_play_factor (tpf): Optional value which specifies the trick\n" 97 " play, a.k.a. trick mode, stream sampling rate among key frames.\n" 98 " If specified, the output is a trick play stream.\n" 99 " - hls_name: Used for HLS audio to set the NAME attribute for\n" 100 " EXT-X-MEDIA. Defaults to the base of the playlist name.\n" 101 " - hls_group_id: Used for HLS audio to set the GROUP-ID attribute for\n" 102 " EXT-X-MEDIA. Defaults to 'audio' if not specified.\n" 103 " - playlist_name: The HLS playlist file to create. Usually ends with\n" 104 " '.m3u8', and is relative to --hls_master_playlist_output. If\n" 105 " unspecified, defaults to something of the form 'stream_0.m3u8',\n" 106 " 'stream_1.m3u8', 'stream_2.m3u8', etc.\n" 107 " - iframe_playlist_name: The optional HLS I-Frames only playlist file\n" 108 " to create. Usually ends with '.m3u8', and is relative to\n" 109 " hls_master_playlist_output. Should only be set for video streams. If\n" 110 " unspecified, no I-Frames only playlist is created.\n";
113 const char kDrmLabelLabel[] =
"label";
114 const char kKeyIdLabel[] =
"key_id";
115 const char kKeyLabel[] =
"key";
119 kArgumentValidationFailed,
124 bool GetWidevineSigner(WidevineSigner* signer) {
125 signer->signer_name = FLAGS_signer;
126 if (!FLAGS_aes_signing_key_bytes.empty()) {
127 signer->signing_key_type = WidevineSigner::SigningKeyType::kAes;
128 signer->aes.key = FLAGS_aes_signing_key_bytes;
129 signer->aes.iv = FLAGS_aes_signing_iv_bytes;
130 }
else if (!FLAGS_rsa_signing_key_path.empty()) {
131 signer->signing_key_type = WidevineSigner::SigningKeyType::kRsa;
134 LOG(ERROR) <<
"Failed to read from '" << FLAGS_rsa_signing_key_path
142 bool GetHlsPlaylistType(
const std::string& playlist_type,
144 if (base::ToUpperASCII(playlist_type) ==
"VOD") {
145 *playlist_type_enum = HlsPlaylistType::kVod;
146 }
else if (base::ToUpperASCII(playlist_type) ==
"LIVE") {
147 *playlist_type_enum = HlsPlaylistType::kLive;
148 }
else if (base::ToUpperASCII(playlist_type) ==
"EVENT") {
149 *playlist_type_enum = HlsPlaylistType::kEvent;
151 LOG(ERROR) <<
"Unrecognized playlist type " << playlist_type;
157 bool GetProtectionScheme(uint32_t* protection_scheme) {
158 if (FLAGS_protection_scheme ==
"cenc") {
162 if (FLAGS_protection_scheme ==
"cbc1") {
163 *protection_scheme = EncryptionParams::kProtectionSchemeCbc1;
166 if (FLAGS_protection_scheme ==
"cbcs") {
167 *protection_scheme = EncryptionParams::kProtectionSchemeCbcs;
170 if (FLAGS_protection_scheme ==
"cens") {
171 *protection_scheme = EncryptionParams::kProtectionSchemeCens;
174 LOG(ERROR) <<
"Unrecognized protection_scheme " << FLAGS_protection_scheme;
178 bool ParseKeys(
const std::string& keys, RawKeyParams* raw_key) {
179 for (
const std::string& key_data : base::SplitString(
180 keys,
",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
181 base::StringPairs string_pairs;
182 base::SplitStringIntoKeyValuePairs(key_data,
'=',
':', &string_pairs);
184 std::map<std::string, std::string> value_map;
185 for (
const auto& string_pair : string_pairs)
186 value_map[string_pair.first] = string_pair.second;
187 const std::string drm_label = value_map[kDrmLabelLabel];
188 if (raw_key->key_map.find(drm_label) != raw_key->key_map.end()) {
189 LOG(ERROR) <<
"Seeing duplicated DRM label '" << drm_label <<
"'.";
192 auto& key_info = raw_key->key_map[drm_label];
193 if (value_map[kKeyIdLabel].empty() ||
194 !base::HexStringToBytes(value_map[kKeyIdLabel], &key_info.key_id)) {
195 LOG(ERROR) <<
"Empty key id or invalid hex string for key id: " 196 << value_map[kKeyIdLabel];
199 if (value_map[kKeyLabel].empty() ||
200 !base::HexStringToBytes(value_map[kKeyLabel], &key_info.key)) {
201 LOG(ERROR) <<
"Empty key or invalid hex string for key: " 202 << value_map[kKeyLabel];
209 bool GetRawKeyParams(RawKeyParams* raw_key) {
210 raw_key->iv = FLAGS_iv_bytes;
211 raw_key->pssh = FLAGS_pssh_bytes;
212 if (!FLAGS_keys.empty()) {
213 if (!ParseKeys(FLAGS_keys, raw_key)) {
214 LOG(ERROR) <<
"Failed to parse --keys " << FLAGS_keys;
219 RawKeyParams::KeyInfo& key_info = raw_key->key_map[
""];
220 key_info.key_id = FLAGS_key_id_bytes;
221 key_info.key = FLAGS_key_bytes;
226 bool ParseAdCues(
const std::string& ad_cues, std::vector<Cuepoint>* cuepoints) {
228 size_t duration_count = 0;
230 for (
const std::string& ad_cue : base::SplitString(
231 ad_cues,
";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
233 auto split_ad_cue = base::SplitString(ad_cue,
",", base::TRIM_WHITESPACE,
234 base::SPLIT_WANT_NONEMPTY);
235 if (split_ad_cue.size() > 2) {
236 LOG(ERROR) <<
"Failed to parse --ad_cues " << ad_cues
237 <<
" Each ad cue must contain no more than 2 components.";
239 if (!base::StringToDouble(split_ad_cue.front(),
240 &cuepoint.start_time_in_seconds)) {
241 LOG(ERROR) <<
"Failed to parse --ad_cues " << ad_cues
242 <<
" Start time component must be of type double.";
245 if (split_ad_cue.size() > 1) {
247 if (!base::StringToDouble(split_ad_cue[1],
248 &cuepoint.duration_in_seconds)) {
249 LOG(ERROR) <<
"Failed to parse --ad_cues " << ad_cues
250 <<
" Duration component must be of type double.";
254 cuepoints->push_back(cuepoint);
257 if (duration_count > 0 && duration_count != cuepoints->size()) {
258 LOG(ERROR) <<
"Failed to parse --ad_cues " << ad_cues
259 <<
" Duration component is optional. However if it is supplied," 260 <<
" it must be supplied consistently across all cuepoints.";
266 base::Optional<PackagingParams> GetPackagingParams() {
267 PackagingParams packaging_params;
269 packaging_params.temp_dir = FLAGS_temp_dir;
271 AdCueGeneratorParams& ad_cue_generator_params =
272 packaging_params.ad_cue_generator_params;
273 if (!ParseAdCues(FLAGS_ad_cues, &ad_cue_generator_params.cue_points)) {
274 return base::nullopt;
277 ChunkingParams& chunking_params = packaging_params.chunking_params;
278 chunking_params.segment_duration_in_seconds = FLAGS_segment_duration;
279 chunking_params.subsegment_duration_in_seconds = FLAGS_fragment_duration;
280 chunking_params.segment_sap_aligned = FLAGS_segment_sap_aligned;
281 chunking_params.subsegment_sap_aligned = FLAGS_fragment_sap_aligned;
283 int num_key_providers = 0;
284 EncryptionParams& encryption_params = packaging_params.encryption_params;
285 encryption_params.generate_common_pssh = FLAGS_generate_common_pssh;
286 encryption_params.generate_playready_pssh = FLAGS_generate_playready_pssh;
287 encryption_params.generate_widevine_pssh = FLAGS_generate_widevine_pssh;
288 if (FLAGS_enable_widevine_encryption) {
289 encryption_params.key_provider = KeyProvider::kWidevine;
292 if (FLAGS_enable_playready_encryption) {
293 encryption_params.key_provider = KeyProvider::kPlayReady;
296 if (FLAGS_enable_raw_key_encryption) {
297 encryption_params.key_provider = KeyProvider::kRawKey;
300 if (num_key_providers > 1) {
301 LOG(ERROR) <<
"Only one of --enable_widevine_encryption, " 302 "--enable_playready_encryption, " 303 "--enable_raw_key_encryption can be enabled.";
304 return base::nullopt;
307 if (encryption_params.key_provider != KeyProvider::kNone) {
308 encryption_params.clear_lead_in_seconds = FLAGS_clear_lead;
309 if (!GetProtectionScheme(&encryption_params.protection_scheme))
310 return base::nullopt;
311 encryption_params.crypto_period_duration_in_seconds =
312 FLAGS_crypto_period_duration;
313 encryption_params.vp9_subsample_encryption = FLAGS_vp9_subsample_encryption;
314 encryption_params.stream_label_func = std::bind(
316 FLAGS_max_hd_pixels, FLAGS_max_uhd1_pixels, std::placeholders::_1);
318 switch (encryption_params.key_provider) {
319 case KeyProvider::kWidevine: {
320 WidevineEncryptionParams& widevine = encryption_params.widevine;
321 widevine.key_server_url = FLAGS_key_server_url;
323 widevine.content_id = FLAGS_content_id_bytes;
324 widevine.policy = FLAGS_policy;
325 widevine.group_id = FLAGS_group_id_bytes;
326 widevine.enable_entitlement_license = FLAGS_enable_entitlement_license;
327 if (!GetWidevineSigner(&widevine.signer))
328 return base::nullopt;
331 case KeyProvider::kPlayReady: {
332 PlayReadyEncryptionParams& playready = encryption_params.playready;
333 playready.key_server_url = FLAGS_playready_server_url;
334 playready.program_identifier = FLAGS_program_identifier;
335 playready.ca_file = FLAGS_ca_file;
336 playready.client_cert_file = FLAGS_client_cert_file;
337 playready.client_cert_private_key_file =
338 FLAGS_client_cert_private_key_file;
339 playready.client_cert_private_key_password =
340 FLAGS_client_cert_private_key_password;
343 case KeyProvider::kRawKey: {
344 if (!GetRawKeyParams(&encryption_params.raw_key))
345 return base::nullopt;
348 case KeyProvider::kNone:
352 num_key_providers = 0;
353 DecryptionParams& decryption_params = packaging_params.decryption_params;
354 if (FLAGS_enable_widevine_decryption) {
355 decryption_params.key_provider = KeyProvider::kWidevine;
358 if (FLAGS_enable_raw_key_decryption) {
359 decryption_params.key_provider = KeyProvider::kRawKey;
362 if (num_key_providers > 1) {
363 LOG(ERROR) <<
"Only one of --enable_widevine_decryption, " 364 "--enable_raw_key_decryption can be enabled.";
365 return base::nullopt;
367 switch (decryption_params.key_provider) {
368 case KeyProvider::kWidevine: {
369 WidevineDecryptionParams& widevine = decryption_params.widevine;
370 widevine.key_server_url = FLAGS_key_server_url;
371 if (!GetWidevineSigner(&widevine.signer))
372 return base::nullopt;
375 case KeyProvider::kRawKey: {
376 if (!GetRawKeyParams(&decryption_params.raw_key))
377 return base::nullopt;
380 case KeyProvider::kPlayReady:
381 case KeyProvider::kNone:
385 Mp4OutputParams& mp4_params = packaging_params.mp4_output_params;
386 mp4_params.num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx;
387 if (FLAGS_mp4_use_decoding_timestamp_in_timeline) {
388 LOG(WARNING) <<
"Flag --mp4_use_decoding_timestamp_in_timeline is set. " 389 "Note that it is a temporary hack to workaround Chromium " 390 "bug https://crbug.com/398130. The flag may be removed " 391 "when the Chromium bug is fixed.";
393 mp4_params.use_decoding_timestamp_in_timeline =
394 FLAGS_mp4_use_decoding_timestamp_in_timeline;
395 mp4_params.include_pssh_in_stream = FLAGS_mp4_include_pssh_in_stream;
397 packaging_params.output_media_info = FLAGS_output_media_info;
399 MpdParams& mpd_params = packaging_params.mpd_params;
400 mpd_params.mpd_output = FLAGS_mpd_output;
401 mpd_params.base_urls = base::SplitString(
402 FLAGS_base_urls,
",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
403 mpd_params.min_buffer_time = FLAGS_min_buffer_time;
404 mpd_params.minimum_update_period = FLAGS_minimum_update_period;
405 mpd_params.suggested_presentation_delay = FLAGS_suggested_presentation_delay;
406 mpd_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
407 mpd_params.preserved_segments_outside_live_window =
408 FLAGS_preserved_segments_outside_live_window;
410 if (!FLAGS_utc_timings.empty()) {
411 base::StringPairs pairs;
412 if (!base::SplitStringIntoKeyValuePairs(FLAGS_utc_timings,
'=',
',',
414 LOG(ERROR) <<
"Invalid --utc_timings scheme_id_uri/value pairs.";
415 return base::nullopt;
417 for (
const auto& string_pair : pairs) {
418 mpd_params.utc_timings.push_back({string_pair.first, string_pair.second});
422 mpd_params.default_language = FLAGS_default_language;
423 mpd_params.generate_static_live_mpd = FLAGS_generate_static_mpd;
424 mpd_params.generate_dash_if_iop_compliant_mpd =
425 FLAGS_generate_dash_if_iop_compliant_mpd;
426 mpd_params.allow_approximate_segment_timeline =
427 FLAGS_allow_approximate_segment_timeline;
429 HlsParams& hls_params = packaging_params.hls_params;
430 if (!GetHlsPlaylistType(FLAGS_hls_playlist_type, &hls_params.playlist_type)) {
431 return base::nullopt;
433 hls_params.master_playlist_output = FLAGS_hls_master_playlist_output;
434 hls_params.base_url = FLAGS_hls_base_url;
435 hls_params.key_uri = FLAGS_hls_key_uri;
436 hls_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
437 hls_params.preserved_segments_outside_live_window =
438 FLAGS_preserved_segments_outside_live_window;
439 hls_params.default_language = FLAGS_default_language;
441 TestParams& test_params = packaging_params.test_params;
442 test_params.dump_stream_info = FLAGS_dump_stream_info;
443 test_params.inject_fake_clock = FLAGS_use_fake_clock_for_muxer;
444 if (FLAGS_override_version)
445 test_params.injected_library_version = FLAGS_test_version;
447 return packaging_params;
450 int PackagerMain(
int argc,
char** argv) {
452 base::CommandLine::Init(argc, argv);
455 logging::LoggingSettings log_settings;
456 log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
457 CHECK(logging::InitLogging(log_settings));
460 google::SetUsageMessage(base::StringPrintf(kUsage, argv[0]));
461 google::ParseCommandLineFlags(&argc, &argv,
true);
462 if (FLAGS_licenses) {
463 for (
const char* line : kLicenseNotice)
464 std::cout << line << std::endl;
468 google::ShowUsageWithFlags(
"Usage");
474 return kArgumentValidationFailed;
477 base::Optional<PackagingParams> packaging_params = GetPackagingParams();
478 if (!packaging_params)
479 return kArgumentValidationFailed;
481 std::vector<StreamDescriptor> stream_descriptors;
482 for (
int i = 1; i < argc; ++i) {
483 base::Optional<StreamDescriptor> stream_descriptor =
485 if (!stream_descriptor)
486 return kArgumentValidationFailed;
487 stream_descriptors.push_back(stream_descriptor.value());
491 packager.Initialize(packaging_params.value(), stream_descriptors);
493 LOG(ERROR) <<
"Failed to initialize packager: " << status.ToString();
494 return kArgumentValidationFailed;
496 status = packager.Run();
498 LOG(ERROR) <<
"Packaging Error: " << status.ToString();
499 return kPackagingFailed;
501 printf(
"Packaging completed successfully.\n");
510 int wmain(
int argc,
wchar_t* argv[],
wchar_t* envp[]) {
511 std::unique_ptr<char* [], std::function<void(char**)>> utf8_argv(
512 new char*[argc], [argc](
char** utf8_args) {
520 std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
521 for (
int idx = 0; idx < argc; ++idx) {
522 std::string utf8_arg(converter.to_bytes(argv[idx]));
524 utf8_argv[idx] =
new char[utf8_arg.size()];
525 memcpy(utf8_argv[idx], &utf8_arg[0], utf8_arg.size());
527 return shaka::PackagerMain(argc, utf8_argv.get());
530 int main(
int argc,
char** argv) {
531 return shaka::PackagerMain(argc, argv);
533 #endif // defined(OS_WIN) static std::string DefaultStreamLabelFunction(int max_sd_pixels, int max_hd_pixels, int max_uhd1_pixels, const EncryptionParams::EncryptedStreamAttributes &stream_attributes)
base::Optional< StreamDescriptor > ParseStreamDescriptor(const std::string &descriptor_string)
static bool ReadFileToString(const char *file_name, std::string *contents)
bool ValidateRawKeyCryptoFlags()
bool ValidateWidevineCryptoFlags()
All the methods that are virtual are virtual for mocking.
static std::string GetLibraryVersion()
static constexpr uint32_t kProtectionSchemeCenc
The protection scheme: "cenc", "cens", "cbc1", "cbcs".
bool ValidatePRCryptoFlags()