Shaka Packager SDK
packager_main.cc
1 // Copyright 2014 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include <gflags/gflags.h>
8 #include <iostream>
9 
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"
33 
34 #if defined(OS_WIN)
35 #include <codecvt>
36 #include <functional>
37 #include <locale>
38 #endif // defined(OS_WIN)
39 
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,
44  false,
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,
49  "",
50  "Packager version for testing. Should be used for testing only.");
51 DEFINE_bool(single_threaded,
52  false,
53  "If enabled, only use one thread when generating content.");
54 
55 namespace shaka {
56 namespace {
57 
58 const char kUsage[] =
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"
64  " URL.\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"
72  " streams.\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";
119 
120 // Labels for parameters in RawKey key info.
121 const char kDrmLabelLabel[] = "label";
122 const char kKeyIdLabel[] = "key_id";
123 const char kKeyLabel[] = "key";
124 const char kKeyIvLabel[] = "iv";
125 
126 enum ExitStatus {
127  kSuccess = 0,
128  kArgumentValidationFailed,
129  kPackagingFailed,
130  kInternalError,
131 };
132 
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;
141  if (!File::ReadFileToString(FLAGS_rsa_signing_key_path.c_str(),
142  &signer->rsa.key)) {
143  LOG(ERROR) << "Failed to read from '" << FLAGS_rsa_signing_key_path
144  << "'.";
145  return false;
146  }
147  }
148  return true;
149 }
150 
151 bool GetHlsPlaylistType(const std::string& playlist_type,
152  HlsPlaylistType* playlist_type_enum) {
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;
159  } else {
160  LOG(ERROR) << "Unrecognized playlist type " << playlist_type;
161  return false;
162  }
163  return true;
164 }
165 
166 bool GetProtectionScheme(uint32_t* protection_scheme) {
167  if (FLAGS_protection_scheme == "cenc") {
168  *protection_scheme = EncryptionParams::kProtectionSchemeCenc;
169  return true;
170  }
171  if (FLAGS_protection_scheme == "cbc1") {
172  *protection_scheme = EncryptionParams::kProtectionSchemeCbc1;
173  return true;
174  }
175  if (FLAGS_protection_scheme == "cbcs") {
176  *protection_scheme = EncryptionParams::kProtectionSchemeCbcs;
177  return true;
178  }
179  if (FLAGS_protection_scheme == "cens") {
180  *protection_scheme = EncryptionParams::kProtectionSchemeCens;
181  return true;
182  }
183  LOG(ERROR) << "Unrecognized protection_scheme " << FLAGS_protection_scheme;
184  return false;
185 }
186 
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);
192 
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 << "'.";
199  return false;
200  }
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];
206  return false;
207  }
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];
212  return false;
213  }
214  if (!value_map[kKeyIvLabel].empty()) {
215  if (!raw_key->iv.empty()) {
216  LOG(ERROR) << "IV already specified with --iv";
217  return false;
218  }
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];
222  return false;
223  }
224  }
225  }
226  return true;
227 }
228 
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;
235  return false;
236  }
237  } else {
238  // An empty StreamLabel specifies the default key info.
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;
242  }
243  return true;
244 }
245 
246 bool ParseAdCues(const std::string& ad_cues, std::vector<Cuepoint>* cuepoints) {
247  // Track if optional field is supplied consistently across all cue points.
248  size_t duration_count = 0;
249 
250  for (const std::string& ad_cue : base::SplitString(
251  ad_cues, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
252  Cuepoint cuepoint;
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.";
258  }
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.";
263  return false;
264  }
265  if (split_ad_cue.size() > 1) {
266  duration_count++;
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.";
271  return false;
272  }
273  }
274  cuepoints->push_back(cuepoint);
275  }
276 
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.";
281  return false;
282  }
283  return true;
284 }
285 
286 bool ParseProtectionSystems(const std::string& protection_systems_str,
287  ProtectionSystem* protection_systems) {
288  *protection_systems = ProtectionSystem::kNone;
289 
290  std::map<std::string, ProtectionSystem> mapping = {
291  {"common", ProtectionSystem::kCommon},
292  {"commonsystem", ProtectionSystem::kCommon},
293  {"fairplay", ProtectionSystem::kFairPlay},
294  {"marlin", ProtectionSystem::kMarlin},
295  {"playready", ProtectionSystem::kPlayReady},
296  {"widevine", ProtectionSystem::kWidevine},
297  };
298 
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;
306  return false;
307  }
308  *protection_systems |= iter->second;
309  }
310  return true;
311 }
312 
313 base::Optional<PackagingParams> GetPackagingParams() {
314  PackagingParams packaging_params;
315 
316  packaging_params.temp_dir = FLAGS_temp_dir;
317  packaging_params.single_threaded = FLAGS_single_threaded;
318 
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;
323  }
324 
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;
330 
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;
335  ++num_key_providers;
336  }
337  if (FLAGS_enable_playready_encryption) {
338  encryption_params.key_provider = KeyProvider::kPlayReady;
339  ++num_key_providers;
340  }
341  if (FLAGS_enable_raw_key_encryption) {
342  encryption_params.key_provider = KeyProvider::kRawKey;
343  ++num_key_providers;
344  }
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;
350  }
351 
352  if (!ParseProtectionSystems(FLAGS_protection_systems,
353  &encryption_params.protection_systems)) {
354  return base::nullopt;
355  }
356 
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;
363 
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(
368  &Packager::DefaultStreamLabelFunction, FLAGS_max_sd_pixels,
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;
372  }
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;
377 
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;
384  break;
385  }
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;
390  break;
391  }
392  case KeyProvider::kRawKey: {
393  if (!GetRawKeyParams(&encryption_params.raw_key))
394  return base::nullopt;
395  break;
396  }
397  case KeyProvider::kNone:
398  break;
399  }
400 
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;
405  ++num_key_providers;
406  }
407  if (FLAGS_enable_raw_key_decryption) {
408  decryption_params.key_provider = KeyProvider::kRawKey;
409  ++num_key_providers;
410  }
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;
415  }
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;
422  break;
423  }
424  case KeyProvider::kRawKey: {
425  if (!GetRawKeyParams(&decryption_params.raw_key))
426  return base::nullopt;
427  break;
428  }
429  case KeyProvider::kPlayReady:
430  case KeyProvider::kNone:
431  break;
432  }
433 
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;
438 
439  packaging_params.transport_stream_timestamp_offset_ms =
440  FLAGS_transport_stream_timestamp_offset_ms;
441 
442  packaging_params.output_media_info = FLAGS_output_media_info;
443 
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;
455 
456  if (!FLAGS_utc_timings.empty()) {
457  base::StringPairs pairs;
458  if (!base::SplitStringIntoKeyValuePairs(FLAGS_utc_timings, '=', ',',
459  &pairs)) {
460  LOG(ERROR) << "Invalid --utc_timings scheme_id_uri/value pairs.";
461  return base::nullopt;
462  }
463  for (const auto& string_pair : pairs) {
464  mpd_params.utc_timings.push_back({string_pair.first, string_pair.second});
465  }
466  }
467 
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;
477 
478  HlsParams& hls_params = packaging_params.hls_params;
479  if (!GetHlsPlaylistType(FLAGS_hls_playlist_type, &hls_params.playlist_type)) {
480  return base::nullopt;
481  }
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;
491 
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;
497 
498  return packaging_params;
499 }
500 
501 int PackagerMain(int argc, char** argv) {
502  // Needed to enable VLOG/DVLOG through --vmodule or --v.
503  base::CommandLine::Init(argc, argv);
504 
505  // Set up logging.
506  logging::LoggingSettings log_settings;
507  log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
508  CHECK(logging::InitLogging(log_settings));
509 
510  google::SetVersionString(shaka::Packager::GetLibraryVersion());
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;
516  return kSuccess;
517  }
518  if (argc < 2) {
519  google::ShowUsageWithFlags("Usage");
520  return kSuccess;
521  }
522  if (FLAGS_quiet)
523  logging::SetMinLogLevel(logging::LOG_WARNING);
524 
527  return kArgumentValidationFailed;
528  }
529 
530  base::Optional<PackagingParams> packaging_params = GetPackagingParams();
531  if (!packaging_params)
532  return kArgumentValidationFailed;
533 
534  std::vector<StreamDescriptor> stream_descriptors;
535  for (int i = 1; i < argc; ++i) {
536  base::Optional<StreamDescriptor> stream_descriptor =
537  ParseStreamDescriptor(argv[i]);
538  if (!stream_descriptor)
539  return kArgumentValidationFailed;
540  stream_descriptors.push_back(stream_descriptor.value());
541  }
542  Packager packager;
543  Status status =
544  packager.Initialize(packaging_params.value(), stream_descriptors);
545  if (!status.ok()) {
546  LOG(ERROR) << "Failed to initialize packager: " << status.ToString();
547  return kArgumentValidationFailed;
548  }
549  status = packager.Run();
550  if (!status.ok()) {
551  LOG(ERROR) << "Packaging Error: " << status.ToString();
552  return kPackagingFailed;
553  }
554  if (!FLAGS_quiet)
555  printf("Packaging completed successfully.\n");
556  return kSuccess;
557 }
558 
559 } // namespace
560 } // namespace shaka
561 
562 #if defined(OS_WIN)
563 // Windows wmain, which converts wide character arguments to UTF-8.
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) {
567  // TODO(tinskip): This leaks, but if this code is enabled, it crashes.
568  // Figure out why. I suspect gflags does something funny with the
569  // argument array.
570  // for (int idx = 0; idx < argc; ++idx)
571  // delete[] utf8_args[idx];
572  delete[] utf8_args;
573  });
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]));
577  utf8_arg += '\0';
578  utf8_argv[idx] = new char[utf8_arg.size()];
579  memcpy(utf8_argv[idx], &utf8_arg[0], utf8_arg.size());
580  }
581  return shaka::PackagerMain(argc, utf8_argv.get());
582 }
583 #else
584 int main(int argc, char** argv) {
585  return shaka::PackagerMain(argc, argv);
586 }
587 #endif // defined(OS_WIN)
static bool ReadFileToString(const char *file_name, std::string *contents)
Definition: file.cc:230
static std::string DefaultStreamLabelFunction(int max_sd_pixels, int max_hd_pixels, int max_uhd1_pixels, const EncryptionParams::EncryptedStreamAttributes &stream_attributes)
Definition: packager.cc:984
static std::string GetLibraryVersion()
Definition: packager.cc:980
All the methods that are virtual are virtual for mocking.
base::Optional< StreamDescriptor > ParseStreamDescriptor(const std::string &descriptor_string)
ProtectionSystem
Definition: crypto_params.h:31
@ kCommon
The common key system from EME: https://goo.gl/s8RIhr.
bool ValidateRawKeyCryptoFlags()
HlsPlaylistType
Definition: hls_params.h:16
bool ValidateWidevineCryptoFlags()
static constexpr uint32_t kProtectionSchemeCenc
The protection scheme: "cenc", "cens", "cbc1", "cbcs".