Shaka Packager SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
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/mpd_flags.h"
14 #include "packager/app/muxer_flags.h"
15 #include "packager/app/packager_util.h"
16 #include "packager/app/playready_key_encryption_flags.h"
17 #include "packager/app/raw_key_encryption_flags.h"
18 #include "packager/app/stream_descriptor.h"
19 #include "packager/app/vlog_flags.h"
20 #include "packager/app/widevine_encryption_flags.h"
21 #include "packager/base/command_line.h"
22 #include "packager/base/logging.h"
23 #include "packager/base/optional.h"
24 #include "packager/base/strings/string_number_conversions.h"
25 #include "packager/base/strings/string_split.h"
26 #include "packager/base/strings/string_util.h"
27 #include "packager/base/strings/stringprintf.h"
28 #include "packager/file/file.h"
29 #include "packager/packager.h"
30 
31 #if defined(OS_WIN)
32 #include <codecvt>
33 #include <functional>
34 #include <locale>
35 #endif // defined(OS_WIN)
36 
37 DEFINE_bool(dump_stream_info, false, "Dump demuxed stream info.");
38 DEFINE_bool(use_fake_clock_for_muxer,
39  false,
40  "Set to true to use a fake clock for muxer. With this flag set, "
41  "creation time and modification time in outputs are set to 0. "
42  "Should only be used for testing.");
43 DEFINE_bool(override_version,
44  false,
45  "Override packager version in the generated outputs with "
46  "--test_version if it is set to true. Should be used for "
47  "testing only.");
48 DEFINE_string(test_version,
49  "",
50  "Packager version for testing. Ignored if --override_version is "
51  "false. Should be used for testing only.");
52 
53 namespace shaka {
54 namespace {
55 
56 const char kUsage[] =
57  "%s [flags] <stream_descriptor> ...\n\n"
58  " stream_descriptor consists of comma separated field_name/value pairs:\n"
59  " field_name=value,[field_name=value,]...\n"
60  " Supported field names are as follows (names in parenthesis are alias):\n"
61  " - input (in): Required input/source media file path or network stream\n"
62  " URL.\n"
63  " - stream_selector (stream): Required field with value 'audio',\n"
64  " 'video', 'text', or stream number (zero based).\n"
65  " - output (out,init_segment): Required output file (single file) or\n"
66  " initialization file path (multiple file).\n"
67  " - segment_template (segment): Optional value which specifies the\n"
68  " naming pattern for the segment files, and that the stream should be\n"
69  " split into multiple files. Its presence should be consistent across\n"
70  " streams.\n"
71  " - bandwidth (bw): Optional value which contains a user-specified\n"
72  " content bit rate for the stream, in bits/sec. If specified, this\n"
73  " value is propagated to (HLS) EXT-X-STREAM-INF:BANDWIDTH or (DASH)\n"
74  " Representation@bandwidth and the $Bandwidth$ template parameter for\n"
75  " segment names. If not specified, the bandwidth value is estimated\n"
76  " from content bitrate. Note that it only affects the generated\n"
77  " manifests/playlists; it has no effect on the media content itself.\n"
78  " - language (lang): Optional value which contains a user-specified\n"
79  " language tag. If specified, this value overrides any language\n"
80  " metadata in the input stream.\n"
81  " - output_format (format): Optional value which specifies the format\n"
82  " of the output files (MP4 or WebM). If not specified, it will be\n"
83  " derived from the file extension of the output file.\n"
84  " - skip_encryption=0|1: Optional. Defaults to 0 if not specified. If\n"
85  " it is set to 1, no encryption of the stream will be made.\n"
86  " - drm_label: Optional value for custom DRM label, which defines the\n"
87  " encryption key applied to the stream. Typical values include AUDIO,\n"
88  " SD, HD, UHD1, UHD2. For raw key, it should be a label defined in\n"
89  " --keys. If not provided, the DRM label is derived from stream type\n"
90  " (video, audio), resolution, etc.\n"
91  " Note that it is case sensitive.\n"
92  " - trick_play_factor (tpf): Optional value which specifies the trick\n"
93  " play, a.k.a. trick mode, stream sampling rate among key frames.\n"
94  " If specified, the output is a trick play stream.\n"
95  " - hls_name: Used for HLS audio to set the NAME attribute for\n"
96  " EXT-X-MEDIA. Defaults to the base of the playlist name.\n"
97  " - hls_group_id: Used for HLS audio to set the GROUP-ID attribute for\n"
98  " EXT-X-MEDIA. Defaults to 'audio' if not specified.\n"
99  " - playlist_name: Used for HLS to name the playlist for the stream.\n"
100  " Usually ends with '.m3u8'. If unspecified, defaults to something of\n"
101  " the form 'stream_0.m3u8', 'stream_1.m3u8', 'stream_2.m3u8', etc.\n";
102 
103 // Labels for parameters in RawKey key info.
104 const char kDrmLabelLabel[] = "label";
105 const char kKeyIdLabel[] = "key_id";
106 const char kKeyLabel[] = "key";
107 
108 enum ExitStatus {
109  kSuccess = 0,
110  kArgumentValidationFailed,
111  kPackagingFailed,
112  kInternalError,
113 };
114 
115 bool GetWidevineSigner(WidevineSigner* signer) {
116  signer->signer_name = FLAGS_signer;
117  if (!FLAGS_aes_signing_key_bytes.empty()) {
118  signer->signing_key_type = WidevineSigner::SigningKeyType::kAes;
119  signer->aes.key = FLAGS_aes_signing_key_bytes;
120  signer->aes.iv = FLAGS_aes_signing_iv_bytes;
121  } else if (!FLAGS_rsa_signing_key_path.empty()) {
122  signer->signing_key_type = WidevineSigner::SigningKeyType::kRsa;
123  if (!File::ReadFileToString(FLAGS_rsa_signing_key_path.c_str(),
124  &signer->rsa.key)) {
125  LOG(ERROR) << "Failed to read from '" << FLAGS_rsa_signing_key_path
126  << "'.";
127  return false;
128  }
129  }
130  return true;
131 }
132 
133 bool GetHlsPlaylistType(const std::string& playlist_type,
134  HlsPlaylistType* playlist_type_enum) {
135  if (base::ToUpperASCII(playlist_type) == "VOD") {
136  *playlist_type_enum = HlsPlaylistType::kVod;
137  } else if (base::ToUpperASCII(playlist_type) == "LIVE") {
138  *playlist_type_enum = HlsPlaylistType::kLive;
139  } else if (base::ToUpperASCII(playlist_type) == "EVENT") {
140  *playlist_type_enum = HlsPlaylistType::kEvent;
141  } else {
142  LOG(ERROR) << "Unrecognized playlist type " << playlist_type;
143  return false;
144  }
145  return true;
146 }
147 
148 bool GetProtectionScheme(uint32_t* protection_scheme) {
149  if (FLAGS_protection_scheme == "cenc") {
150  *protection_scheme = EncryptionParams::kProtectionSchemeCenc;
151  return true;
152  }
153  if (FLAGS_protection_scheme == "cbc1") {
154  *protection_scheme = EncryptionParams::kProtectionSchemeCbc1;
155  return true;
156  }
157  if (FLAGS_protection_scheme == "cbcs") {
158  *protection_scheme = EncryptionParams::kProtectionSchemeCbcs;
159  return true;
160  }
161  if (FLAGS_protection_scheme == "cens") {
162  *protection_scheme = EncryptionParams::kProtectionSchemeCens;
163  return true;
164  }
165  LOG(ERROR) << "Unrecognized protection_scheme " << FLAGS_protection_scheme;
166  return false;
167 }
168 
169 bool ParseKeys(const std::string& keys, RawKeyParams* raw_key) {
170  for (const std::string& key_data : base::SplitString(
171  keys, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
172  base::StringPairs string_pairs;
173  base::SplitStringIntoKeyValuePairs(key_data, '=', ':', &string_pairs);
174 
175  std::map<std::string, std::string> value_map;
176  for (const auto& string_pair : string_pairs)
177  value_map[string_pair.first] = string_pair.second;
178  const std::string drm_label = value_map[kDrmLabelLabel];
179  if (raw_key->key_map.find(drm_label) != raw_key->key_map.end()) {
180  LOG(ERROR) << "Seeing duplicated DRM label '" << drm_label << "'.";
181  return false;
182  }
183  auto& key_info = raw_key->key_map[drm_label];
184  if (value_map[kKeyIdLabel].empty() ||
185  !base::HexStringToBytes(value_map[kKeyIdLabel], &key_info.key_id)) {
186  LOG(ERROR) << "Empty key id or invalid hex string for key id: "
187  << value_map[kKeyIdLabel];
188  return false;
189  }
190  if (value_map[kKeyLabel].empty() ||
191  !base::HexStringToBytes(value_map[kKeyLabel], &key_info.key)) {
192  LOG(ERROR) << "Empty key or invalid hex string for key: "
193  << value_map[kKeyLabel];
194  return false;
195  }
196  }
197  return true;
198 }
199 
200 bool GetRawKeyParams(RawKeyParams* raw_key) {
201  raw_key->iv = FLAGS_iv_bytes;
202  raw_key->pssh = FLAGS_pssh_bytes;
203  if (!FLAGS_keys.empty()) {
204  if (!ParseKeys(FLAGS_keys, raw_key)) {
205  LOG(ERROR) << "Failed to parse --keys " << FLAGS_keys;
206  return false;
207  }
208  } else {
209  // An empty StreamLabel specifies the default key info.
210  RawKeyParams::KeyInfo& key_info = raw_key->key_map[""];
211  key_info.key_id = FLAGS_key_id_bytes;
212  key_info.key = FLAGS_key_bytes;
213  }
214  return true;
215 }
216 
217 bool ParseAdCues(const std::string& ad_cues, std::vector<Cuepoint>* cuepoints) {
218  // Track if optional field is supplied consistently across all cue points.
219  size_t duration_count = 0;
220 
221  for (const std::string& ad_cue : base::SplitString(
222  ad_cues, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
223  Cuepoint cuepoint;
224  auto split_ad_cue = base::SplitString(ad_cue, ",", base::TRIM_WHITESPACE,
225  base::SPLIT_WANT_NONEMPTY);
226  if (split_ad_cue.size() > 2) {
227  LOG(ERROR) << "Failed to parse --ad_cues " << ad_cues
228  << " Each ad cue must contain no more than 2 components.";
229  }
230  if (!base::StringToDouble(split_ad_cue.front(),
231  &cuepoint.start_time_in_seconds)) {
232  LOG(ERROR) << "Failed to parse --ad_cues " << ad_cues
233  << " Start time component must be of type double.";
234  return false;
235  }
236  if (split_ad_cue.size() > 1) {
237  duration_count++;
238  if (!base::StringToDouble(split_ad_cue[1],
239  &cuepoint.duration_in_seconds)) {
240  LOG(ERROR) << "Failed to parse --ad_cues " << ad_cues
241  << " Duration component must be of type double.";
242  return false;
243  }
244  }
245  cuepoints->push_back(cuepoint);
246  }
247 
248  if (duration_count > 0 && duration_count != cuepoints->size()) {
249  LOG(ERROR) << "Failed to parse --ad_cues " << ad_cues
250  << " Duration component is optional. However if it is supplied,"
251  << " it must be supplied consistently across all cuepoints.";
252  return false;
253  }
254  return true;
255 }
256 
257 base::Optional<PackagingParams> GetPackagingParams() {
258  PackagingParams packaging_params;
259 
260  AdCueGeneratorParams& ad_cue_generator_params =
261  packaging_params.ad_cue_generator_params;
262  if (!ParseAdCues(FLAGS_ad_cues, &ad_cue_generator_params.cue_points)) {
263  return base::nullopt;
264  }
265 
266  ChunkingParams& chunking_params = packaging_params.chunking_params;
267  chunking_params.segment_duration_in_seconds = FLAGS_segment_duration;
268  chunking_params.subsegment_duration_in_seconds = FLAGS_fragment_duration;
269  chunking_params.segment_sap_aligned = FLAGS_segment_sap_aligned;
270  chunking_params.subsegment_sap_aligned = FLAGS_fragment_sap_aligned;
271 
272  int num_key_providers = 0;
273  EncryptionParams& encryption_params = packaging_params.encryption_params;
274  if (FLAGS_enable_widevine_encryption) {
275  encryption_params.key_provider = KeyProvider::kWidevine;
276  ++num_key_providers;
277  }
278  if (FLAGS_enable_playready_encryption) {
279  encryption_params.key_provider = KeyProvider::kPlayready;
280  ++num_key_providers;
281  }
282  if (FLAGS_enable_raw_key_encryption) {
283  encryption_params.key_provider = KeyProvider::kRawKey;
284  ++num_key_providers;
285  }
286  if (num_key_providers > 1) {
287  LOG(ERROR) << "Only one of --enable_widevine_encryption, "
288  "--enable_playready_encryption, "
289  "--enable_raw_key_encryption can be enabled.";
290  return base::nullopt;
291  }
292 
293  if (encryption_params.key_provider != KeyProvider::kNone) {
294  encryption_params.clear_lead_in_seconds = FLAGS_clear_lead;
295  if (!GetProtectionScheme(&encryption_params.protection_scheme))
296  return base::nullopt;
297  encryption_params.crypto_period_duration_in_seconds =
298  FLAGS_crypto_period_duration;
299  encryption_params.vp9_subsample_encryption = FLAGS_vp9_subsample_encryption;
300  encryption_params.stream_label_func = std::bind(
301  &Packager::DefaultStreamLabelFunction, FLAGS_max_sd_pixels,
302  FLAGS_max_hd_pixels, FLAGS_max_uhd1_pixels, std::placeholders::_1);
303  }
304  switch (encryption_params.key_provider) {
305  case KeyProvider::kWidevine: {
306  WidevineEncryptionParams& widevine = encryption_params.widevine;
307  widevine.key_server_url = FLAGS_key_server_url;
308  widevine.include_common_pssh = FLAGS_include_common_pssh;
309 
310  widevine.content_id = FLAGS_content_id_bytes;
311  widevine.policy = FLAGS_policy;
312  widevine.group_id = FLAGS_group_id_bytes;
313  if (!GetWidevineSigner(&widevine.signer))
314  return base::nullopt;
315  break;
316  }
317  case KeyProvider::kPlayready: {
318  PlayreadyEncryptionParams& playready = encryption_params.playready;
319  playready.key_server_url = FLAGS_playready_server_url;
320  playready.program_identifier = FLAGS_program_identifier;
321  playready.ca_file = FLAGS_ca_file;
322  playready.client_cert_file = FLAGS_client_cert_file;
323  playready.client_cert_private_key_file =
324  FLAGS_client_cert_private_key_file;
325  playready.client_cert_private_key_password =
326  FLAGS_client_cert_private_key_password;
327  playready.key_id = FLAGS_playready_key_id_bytes;
328  playready.key = FLAGS_playready_key_bytes;
329  break;
330  }
331  case KeyProvider::kRawKey: {
332  if (!GetRawKeyParams(&encryption_params.raw_key))
333  return base::nullopt;
334  break;
335  }
336  case KeyProvider::kNone:
337  break;
338  }
339 
340  num_key_providers = 0;
341  DecryptionParams& decryption_params = packaging_params.decryption_params;
342  if (FLAGS_enable_widevine_decryption) {
343  decryption_params.key_provider = KeyProvider::kWidevine;
344  ++num_key_providers;
345  }
346  if (FLAGS_enable_raw_key_decryption) {
347  decryption_params.key_provider = KeyProvider::kRawKey;
348  ++num_key_providers;
349  }
350  if (num_key_providers > 1) {
351  LOG(ERROR) << "Only one of --enable_widevine_decryption, "
352  "--enable_raw_key_decryption can be enabled.";
353  return base::nullopt;
354  }
355  switch (decryption_params.key_provider) {
356  case KeyProvider::kWidevine: {
357  WidevineDecryptionParams& widevine = decryption_params.widevine;
358  widevine.key_server_url = FLAGS_key_server_url;
359  if (!GetWidevineSigner(&widevine.signer))
360  return base::nullopt;
361  break;
362  }
363  case KeyProvider::kRawKey: {
364  if (!GetRawKeyParams(&decryption_params.raw_key))
365  return base::nullopt;
366  break;
367  }
368  case KeyProvider::kPlayready:
369  case KeyProvider::kNone:
370  break;
371  }
372 
373  Mp4OutputParams& mp4_params = packaging_params.mp4_output_params;
374  mp4_params.num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx;
375  if (FLAGS_mp4_use_decoding_timestamp_in_timeline) {
376  LOG(WARNING) << "Flag --mp4_use_decoding_timestamp_in_timeline is set. "
377  "Note that it is a temporary hack to workaround Chromium "
378  "bug https://crbug.com/398130. The flag may be removed "
379  "when the Chromium bug is fixed.";
380  }
381  mp4_params.use_decoding_timestamp_in_timeline =
382  FLAGS_mp4_use_decoding_timestamp_in_timeline;
383  mp4_params.include_pssh_in_stream = FLAGS_mp4_include_pssh_in_stream;
384 
385  packaging_params.output_media_info = FLAGS_output_media_info;
386 
387  MpdParams& mpd_params = packaging_params.mpd_params;
388  mpd_params.generate_static_live_mpd = FLAGS_generate_static_mpd;
389  mpd_params.mpd_output = FLAGS_mpd_output;
390  mpd_params.base_urls = base::SplitString(
391  FLAGS_base_urls, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
392  mpd_params.generate_dash_if_iop_compliant_mpd =
393  FLAGS_generate_dash_if_iop_compliant_mpd;
394  mpd_params.minimum_update_period = FLAGS_minimum_update_period;
395  mpd_params.min_buffer_time = FLAGS_min_buffer_time;
396  mpd_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
397  mpd_params.suggested_presentation_delay = FLAGS_suggested_presentation_delay;
398  mpd_params.default_language = FLAGS_default_language;
399 
400  HlsParams& hls_params = packaging_params.hls_params;
401  if (!GetHlsPlaylistType(FLAGS_hls_playlist_type, &hls_params.playlist_type)) {
402  return base::nullopt;
403  }
404  hls_params.master_playlist_output = FLAGS_hls_master_playlist_output;
405  hls_params.base_url = FLAGS_hls_base_url;
406  hls_params.key_uri = FLAGS_hls_key_uri;
407  hls_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
408 
409  TestParams& test_params = packaging_params.test_params;
410  test_params.dump_stream_info = FLAGS_dump_stream_info;
411  test_params.inject_fake_clock = FLAGS_use_fake_clock_for_muxer;
412  if (FLAGS_override_version)
413  test_params.injected_library_version = FLAGS_test_version;
414 
415  return packaging_params;
416 }
417 
418 int PackagerMain(int argc, char** argv) {
419  // Needed to enable VLOG/DVLOG through --vmodule or --v.
420  base::CommandLine::Init(argc, argv);
421 
422  // Set up logging.
423  logging::LoggingSettings log_settings;
424  log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
425  CHECK(logging::InitLogging(log_settings));
426 
427  google::SetVersionString(shaka::Packager::GetLibraryVersion());
428  google::SetUsageMessage(base::StringPrintf(kUsage, argv[0]));
429  google::ParseCommandLineFlags(&argc, &argv, true);
430  if (argc < 2) {
431  google::ShowUsageWithFlags("Usage");
432  return kSuccess;
433  }
434 
437  return kArgumentValidationFailed;
438  }
439 
440  base::Optional<PackagingParams> packaging_params = GetPackagingParams();
441  if (!packaging_params)
442  return kArgumentValidationFailed;
443 
444  std::vector<StreamDescriptor> stream_descriptors;
445  for (int i = 1; i < argc; ++i) {
446  base::Optional<StreamDescriptor> stream_descriptor =
447  ParseStreamDescriptor(argv[i]);
448  if (!stream_descriptor)
449  return kArgumentValidationFailed;
450  stream_descriptors.push_back(stream_descriptor.value());
451  }
452  Packager packager;
453  Status status =
454  packager.Initialize(packaging_params.value(), stream_descriptors);
455  if (!status.ok()) {
456  LOG(ERROR) << "Failed to initialize packager: " << status.ToString();
457  return kArgumentValidationFailed;
458  }
459  status = packager.Run();
460  if (!status.ok()) {
461  LOG(ERROR) << "Packaging Error: " << status.ToString();
462  return kPackagingFailed;
463  }
464  printf("Packaging completed successfully.\n");
465  return kSuccess;
466 }
467 
468 } // namespace
469 } // namespace shaka
470 
471 #if defined(OS_WIN)
472 // Windows wmain, which converts wide character arguments to UTF-8.
473 int wmain(int argc, wchar_t* argv[], wchar_t* envp[]) {
474  std::unique_ptr<char* [], std::function<void(char**)>> utf8_argv(
475  new char*[argc], [argc](char** utf8_args) {
476  // TODO(tinskip): This leaks, but if this code is enabled, it crashes.
477  // Figure out why. I suspect gflags does something funny with the
478  // argument array.
479  // for (int idx = 0; idx < argc; ++idx)
480  // delete[] utf8_args[idx];
481  delete[] utf8_args;
482  });
483  std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
484  for (int idx = 0; idx < argc; ++idx) {
485  std::string utf8_arg(converter.to_bytes(argv[idx]));
486  utf8_arg += '\0';
487  utf8_argv[idx] = new char[utf8_arg.size()];
488  memcpy(utf8_argv[idx], &utf8_arg[0], utf8_arg.size());
489  }
490  return shaka::PackagerMain(argc, utf8_argv.get());
491 }
492 #else
493 int main(int argc, char** argv) {
494  return shaka::PackagerMain(argc, argv);
495 }
496 #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)
Definition: packager.cc:892
HlsPlaylistType
Definition: hls_params.h:16
base::Optional< StreamDescriptor > ParseStreamDescriptor(const std::string &descriptor_string)
static bool ReadFileToString(const char *file_name, std::string *contents)
Definition: file.cc:216
bool ValidateRawKeyCryptoFlags()
bool ValidateWidevineCryptoFlags()
static std::string GetLibraryVersion()
Definition: packager.cc:888
static constexpr uint32_t kProtectionSchemeCenc
The protection scheme: "cenc", "cens", "cbc1", "cbcs".