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/crypto_flags.h"
11 #include "packager/app/fixed_key_encryption_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/stream_descriptor.h"
18 #include "packager/app/vlog_flags.h"
19 #include "packager/app/widevine_encryption_flags.h"
20 #include "packager/base/command_line.h"
21 #include "packager/base/logging.h"
22 #include "packager/base/optional.h"
23 #include "packager/base/strings/string_number_conversions.h"
24 #include "packager/base/strings/string_split.h"
25 #include "packager/base/strings/string_util.h"
26 #include "packager/base/strings/stringprintf.h"
27 #include "packager/file/file.h"
28 #include "packager/packager.h"
29 
30 #if defined(OS_WIN)
31 #include <codecvt>
32 #include <functional>
33 #include <locale>
34 #endif // defined(OS_WIN)
35 
36 DEFINE_bool(dump_stream_info, false, "Dump demuxed stream info.");
37 DEFINE_bool(use_fake_clock_for_muxer,
38  false,
39  "Set to true to use a fake clock for muxer. With this flag set, "
40  "creation time and modification time in outputs are set to 0. "
41  "Should only be used for testing.");
42 DEFINE_bool(override_version,
43  false,
44  "Override packager version in the generated outputs with "
45  "--test_version if it is set to true. Should be used for "
46  "testing only.");
47 DEFINE_string(test_version,
48  "",
49  "Packager version for testing. Ignored if --override_version is "
50  "false. Should be used for testing only.");
51 
52 namespace shaka {
53 namespace {
54 
55 const char kUsage[] =
56  "%s [flags] <stream_descriptor> ...\n\n"
57  " stream_descriptor consists of comma separated field_name/value pairs:\n"
58  " field_name=value,[field_name=value,]...\n"
59  " Supported field names are as follows (names in parenthesis are alias):\n"
60  " - input (in): Required input/source media file path or network stream\n"
61  " URL.\n"
62  " - stream_selector (stream): Required field with value 'audio',\n"
63  " 'video', 'text', or stream number (zero based).\n"
64  " - output (out,init_segment): Required output file (single file) or\n"
65  " initialization file path (multiple file).\n"
66  " - segment_template (segment): Optional value which specifies the\n"
67  " naming pattern for the segment files, and that the stream should be\n"
68  " split into multiple files. Its presence should be consistent across\n"
69  " streams.\n"
70  " - bandwidth (bw): Optional value which contains a user-specified\n"
71  " content bit rate for the stream, in bits/sec. If specified, this\n"
72  " value is propagated to (HLS) EXT-X-STREAM-INF:BANDWIDTH or (DASH)\n"
73  " Representation@bandwidth and the $Bandwidth$ template parameter for\n"
74  " segment names. If not specified, the bandwidth value is estimated\n"
75  " from content bitrate. Note that it only affects the generated\n"
76  " manifests/playlists; it has no effect on the media content itself.\n"
77  " - language (lang): Optional value which contains a user-specified\n"
78  " language tag. If specified, this value overrides any language\n"
79  " metadata in the input stream.\n"
80  " - output_format (format): Optional value which specifies the format\n"
81  " of the output files (MP4 or WebM). If not specified, it will be\n"
82  " derived from the file extension of the output file.\n"
83  " - skip_encryption=0|1: Optional. Defaults to 0 if not specified. If\n"
84  " it is set to 1, no encryption of the stream will be made.\n"
85  " - trick_play_factor (tpf): Optional value which specifies the trick\n"
86  " play, a.k.a. trick mode, stream sampling rate among key frames.\n"
87  " If specified, the output is a trick play stream.\n"
88  " - hls_name: Required for audio when outputting HLS.\n"
89  " name of the output stream. This is not (necessarily) the same as\n"
90  " output. This is used as the NAME attribute for EXT-X-MEDIA.\n"
91  " - hls_group_id: Required for audio when outputting HLS.\n"
92  " The group ID for the output stream. This is used as the GROUP-ID\n"
93  " attribute for EXT-X-MEDIA.\n"
94  " - playlist_name: Required for HLS output.\n"
95  " Name of the playlist for the stream. Usually ends with '.m3u8'.\n";
96 
97 enum ExitStatus {
98  kSuccess = 0,
99  kArgumentValidationFailed,
100  kPackagingFailed,
101  kInternalError,
102 };
103 
104 bool GetWidevineSigner(WidevineSigner* signer) {
105  signer->signer_name = FLAGS_signer;
106  if (!FLAGS_aes_signing_key_bytes.empty()) {
107  signer->signing_key_type = WidevineSigner::SigningKeyType::kAes;
108  signer->aes.key = FLAGS_aes_signing_key_bytes;
109  signer->aes.iv = FLAGS_aes_signing_iv_bytes;
110  } else if (!FLAGS_rsa_signing_key_path.empty()) {
111  signer->signing_key_type = WidevineSigner::SigningKeyType::kRsa;
112  if (!File::ReadFileToString(FLAGS_rsa_signing_key_path.c_str(),
113  &signer->rsa.key)) {
114  LOG(ERROR) << "Failed to read from '" << FLAGS_rsa_signing_key_path
115  << "'.";
116  return false;
117  }
118  }
119  return true;
120 }
121 
122 bool GetHlsPlaylistType(const std::string& playlist_type,
123  HlsPlaylistType* playlist_type_enum) {
124  if (base::ToUpperASCII(playlist_type) == "VOD") {
125  *playlist_type_enum = HlsPlaylistType::kVod;
126  } else if (base::ToUpperASCII(playlist_type) == "LIVE") {
127  *playlist_type_enum = HlsPlaylistType::kLive;
128  } else if (base::ToUpperASCII(playlist_type) == "EVENT") {
129  *playlist_type_enum = HlsPlaylistType::kEvent;
130  } else {
131  LOG(ERROR) << "Unrecognized playlist type " << playlist_type;
132  return false;
133  }
134  return true;
135 }
136 
137 bool GetProtectionScheme(uint32_t* protection_scheme) {
138  if (FLAGS_protection_scheme == "cenc") {
139  *protection_scheme = EncryptionParams::kProtectionSchemeCenc;
140  return true;
141  }
142  if (FLAGS_protection_scheme == "cbc1") {
143  *protection_scheme = EncryptionParams::kProtectionSchemeCbc1;
144  return true;
145  }
146  if (FLAGS_protection_scheme == "cbcs") {
147  *protection_scheme = EncryptionParams::kProtectionSchemeCbcs;
148  return true;
149  }
150  if (FLAGS_protection_scheme == "cens") {
151  *protection_scheme = EncryptionParams::kProtectionSchemeCens;
152  return true;
153  }
154  LOG(ERROR) << "Unrecognized protection_scheme " << FLAGS_protection_scheme;
155  return false;
156 }
157 
158 base::Optional<PackagingParams> GetPackagingParams() {
159  PackagingParams packaging_params;
160 
161  ChunkingParams& chunking_params = packaging_params.chunking_params;
162  chunking_params.segment_duration_in_seconds = FLAGS_segment_duration;
163  chunking_params.subsegment_duration_in_seconds = FLAGS_fragment_duration;
164  chunking_params.segment_sap_aligned = FLAGS_segment_sap_aligned;
165  chunking_params.subsegment_sap_aligned = FLAGS_fragment_sap_aligned;
166 
167  int num_key_providers = 0;
168  EncryptionParams& encryption_params = packaging_params.encryption_params;
169  if (FLAGS_enable_widevine_encryption) {
170  encryption_params.key_provider = KeyProvider::kWidevine;
171  ++num_key_providers;
172  }
173  if (FLAGS_enable_playready_encryption) {
174  encryption_params.key_provider = KeyProvider::kPlayready;
175  ++num_key_providers;
176  }
177  if (FLAGS_enable_fixed_key_encryption) {
178  encryption_params.key_provider = KeyProvider::kRawKey;
179  ++num_key_providers;
180  }
181  if (num_key_providers > 1) {
182  LOG(ERROR) << "Only one of --enable_widevine_encryption, "
183  "--enable_playready_encryption, "
184  "--enable_fixed_key_encryption can be enabled.";
185  return base::nullopt;
186  }
187 
188  if (encryption_params.key_provider != KeyProvider::kNone) {
189  encryption_params.clear_lead_in_seconds = FLAGS_clear_lead;
190  if (!GetProtectionScheme(&encryption_params.protection_scheme))
191  return base::nullopt;
192  encryption_params.crypto_period_duration_in_seconds =
193  FLAGS_crypto_period_duration;
194  encryption_params.vp9_subsample_encryption = FLAGS_vp9_subsample_encryption;
195  encryption_params.stream_label_func = std::bind(
196  &Packager::DefaultStreamLabelFunction, FLAGS_max_sd_pixels,
197  FLAGS_max_hd_pixels, FLAGS_max_uhd1_pixels, std::placeholders::_1);
198  }
199  switch (encryption_params.key_provider) {
200  case KeyProvider::kWidevine: {
201  WidevineEncryptionParams& widevine = encryption_params.widevine;
202  widevine.key_server_url = FLAGS_key_server_url;
203  widevine.include_common_pssh = FLAGS_include_common_pssh;
204 
205  widevine.content_id = FLAGS_content_id_bytes;
206  widevine.policy = FLAGS_policy;
207  widevine.group_id = FLAGS_group_id_bytes;
208  if (!GetWidevineSigner(&widevine.signer))
209  return base::nullopt;
210  break;
211  }
212  case KeyProvider::kPlayready: {
213  PlayreadyEncryptionParams& playready = encryption_params.playready;
214  playready.key_server_url = FLAGS_playready_server_url;
215  playready.program_identifier = FLAGS_program_identifier;
216  playready.ca_file = FLAGS_ca_file;
217  playready.client_cert_file = FLAGS_client_cert_file;
218  playready.client_cert_private_key_file =
219  FLAGS_client_cert_private_key_file;
220  playready.client_cert_private_key_password =
221  FLAGS_client_cert_private_key_password;
222  playready.key_id = FLAGS_playready_key_id_bytes;
223  playready.key = FLAGS_playready_key_bytes;
224  break;
225  }
226  case KeyProvider::kRawKey: {
227  RawKeyEncryptionParams& raw_key = encryption_params.raw_key;
228  raw_key.iv = FLAGS_iv_bytes;
229  raw_key.pssh = FLAGS_pssh_bytes;
230  // An empty StreamLabel specifies the default KeyPair.
231  RawKeyEncryptionParams::KeyPair& key_pair = raw_key.key_map[""];
232  key_pair.key_id = FLAGS_key_id_bytes;
233  key_pair.key = FLAGS_key_bytes;
234  break;
235  }
236  case KeyProvider::kNone:
237  break;
238  }
239 
240  num_key_providers = 0;
241  DecryptionParams& decryption_params = packaging_params.decryption_params;
242  if (FLAGS_enable_widevine_decryption) {
243  decryption_params.key_provider = KeyProvider::kWidevine;
244  ++num_key_providers;
245  }
246  if (FLAGS_enable_fixed_key_decryption) {
247  decryption_params.key_provider = KeyProvider::kRawKey;
248  ++num_key_providers;
249  }
250  if (num_key_providers > 1) {
251  LOG(ERROR) << "Only one of --enable_widevine_decryption, "
252  "--enable_fixed_key_decryption can be enabled.";
253  return base::nullopt;
254  }
255  switch (decryption_params.key_provider) {
256  case KeyProvider::kWidevine: {
257  WidevineDecryptionParams& widevine = decryption_params.widevine;
258  widevine.key_server_url = FLAGS_key_server_url;
259  if (!GetWidevineSigner(&widevine.signer))
260  return base::nullopt;
261  break;
262  }
263  case KeyProvider::kRawKey: {
264  RawKeyDecryptionParams& raw_key = decryption_params.raw_key;
265  // An empty StreamLabel specifies the default KeyPair.
266  RawKeyDecryptionParams::KeyPair& key_pair = raw_key.key_map[""];
267  key_pair.key_id = FLAGS_key_id_bytes;
268  key_pair.key = FLAGS_key_bytes;
269  break;
270  }
271  case KeyProvider::kPlayready:
272  case KeyProvider::kNone:
273  break;
274  }
275 
276  Mp4OutputParams& mp4_params = packaging_params.mp4_output_params;
277  mp4_params.num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx;
278  if (FLAGS_mp4_use_decoding_timestamp_in_timeline) {
279  LOG(WARNING) << "Flag --mp4_use_decoding_timestamp_in_timeline is set. "
280  "Note that it is a temporary hack to workaround Chromium "
281  "bug https://crbug.com/398130. The flag may be removed "
282  "when the Chromium bug is fixed.";
283  }
284  mp4_params.use_decoding_timestamp_in_timeline =
285  FLAGS_mp4_use_decoding_timestamp_in_timeline;
286  mp4_params.include_pssh_in_stream = FLAGS_mp4_include_pssh_in_stream;
287 
288  packaging_params.output_media_info = FLAGS_output_media_info;
289 
290  MpdParams& mpd_params = packaging_params.mpd_params;
291  mpd_params.generate_static_live_mpd = FLAGS_generate_static_mpd;
292  mpd_params.mpd_output = FLAGS_mpd_output;
293  mpd_params.base_urls = base::SplitString(
294  FLAGS_base_urls, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
295  mpd_params.generate_dash_if_iop_compliant_mpd =
296  FLAGS_generate_dash_if_iop_compliant_mpd;
297  mpd_params.minimum_update_period = FLAGS_minimum_update_period;
298  mpd_params.min_buffer_time = FLAGS_min_buffer_time;
299  mpd_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
300  mpd_params.suggested_presentation_delay = FLAGS_suggested_presentation_delay;
301  mpd_params.default_language = FLAGS_default_language;
302 
303  HlsParams& hls_params = packaging_params.hls_params;
304  if (!GetHlsPlaylistType(FLAGS_hls_playlist_type, &hls_params.playlist_type)) {
305  return base::nullopt;
306  }
307  hls_params.master_playlist_output = FLAGS_hls_master_playlist_output;
308  hls_params.base_url = FLAGS_hls_base_url;
309  hls_params.time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
310 
311  TestParams& test_params = packaging_params.test_params;
312  test_params.dump_stream_info = FLAGS_dump_stream_info;
313  test_params.inject_fake_clock = FLAGS_use_fake_clock_for_muxer;
314  if (FLAGS_override_version)
315  test_params.injected_library_version = FLAGS_test_version;
316 
317  return packaging_params;
318 }
319 
320 int PackagerMain(int argc, char** argv) {
321  // Needed to enable VLOG/DVLOG through --vmodule or --v.
322  base::CommandLine::Init(argc, argv);
323 
324  // Set up logging.
325  logging::LoggingSettings log_settings;
326  log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
327  CHECK(logging::InitLogging(log_settings));
328 
329  google::SetVersionString(shaka::Packager::GetLibraryVersion());
330  google::SetUsageMessage(base::StringPrintf(kUsage, argv[0]));
331  google::ParseCommandLineFlags(&argc, &argv, true);
332  if (argc < 2) {
333  google::ShowUsageWithFlags("Usage");
334  return kSuccess;
335  }
336 
339  return kArgumentValidationFailed;
340  }
341 
342  base::Optional<PackagingParams> packaging_params = GetPackagingParams();
343  if (!packaging_params)
344  return kArgumentValidationFailed;
345 
346  std::vector<StreamDescriptor> stream_descriptors;
347  for (int i = 1; i < argc; ++i) {
348  base::Optional<StreamDescriptor> stream_descriptor =
349  ParseStreamDescriptor(argv[i]);
350  if (!stream_descriptor)
351  return kArgumentValidationFailed;
352  stream_descriptors.push_back(stream_descriptor.value());
353  }
354  Packager packager;
355  Status status =
356  packager.Initialize(packaging_params.value(), stream_descriptors);
357  if (!status.ok()) {
358  LOG(ERROR) << "Failed to initialize packager: " << status.ToString();
359  return kArgumentValidationFailed;
360  }
361  status = packager.Run();
362  if (!status.ok()) {
363  LOG(ERROR) << "Packaging Error: " << status.ToString();
364  return kPackagingFailed;
365  }
366  printf("Packaging completed successfully.\n");
367  return kSuccess;
368 }
369 
370 } // namespace
371 } // namespace shaka
372 
373 #if defined(OS_WIN)
374 // Windows wmain, which converts wide character arguments to UTF-8.
375 int wmain(int argc, wchar_t* argv[], wchar_t* envp[]) {
376  std::unique_ptr<char* [], std::function<void(char**)>> utf8_argv(
377  new char*[argc], [argc](char** utf8_args) {
378  // TODO(tinskip): This leaks, but if this code is enabled, it crashes.
379  // Figure out why. I suspect gflags does something funny with the
380  // argument array.
381  // for (int idx = 0; idx < argc; ++idx)
382  // delete[] utf8_args[idx];
383  delete[] utf8_args;
384  });
385  std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
386  for (int idx = 0; idx < argc; ++idx) {
387  std::string utf8_arg(converter.to_bytes(argv[idx]));
388  utf8_arg += '\0';
389  utf8_argv[idx] = new char[utf8_arg.size()];
390  memcpy(utf8_argv[idx], &utf8_arg[0], utf8_arg.size());
391  }
392  return shaka::PackagerMain(argc, utf8_argv.get());
393 }
394 #else
395 int main(int argc, char** argv) {
396  return shaka::PackagerMain(argc, argv);
397 }
398 #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:808
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 ValidateWidevineCryptoFlags()
static std::string GetLibraryVersion()
Definition: packager.cc:804
static constexpr uint32_t kProtectionSchemeCenc
The protection scheme: "cenc", "cens", "cbc1", "cbcs".
bool ValidateFixedCryptoFlags()