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