DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
packager_util.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 "packager/app/packager_util.h"
8 
9 #include <gflags/gflags.h>
10 #include <iostream>
11 
12 #include "packager/app/fixed_key_encryption_flags.h"
13 #include "packager/app/mpd_flags.h"
14 #include "packager/app/muxer_flags.h"
15 #include "packager/app/widevine_encryption_flags.h"
16 #include "packager/base/logging.h"
17 #include "packager/base/strings/string_number_conversions.h"
18 #include "packager/media/base/fixed_key_source.h"
19 #include "packager/media/base/media_stream.h"
20 #include "packager/media/base/muxer.h"
21 #include "packager/media/base/muxer_options.h"
22 #include "packager/media/base/request_signer.h"
23 #include "packager/media/base/stream_info.h"
24 #include "packager/media/base/widevine_key_source.h"
25 #include "packager/media/file/file.h"
26 #include "packager/mpd/base/mpd_builder.h"
27 
28 DEFINE_bool(mp4_use_decoding_timestamp_in_timeline,
29  false,
30  "If set, decoding timestamp instead of presentation timestamp will "
31  "be used when generating media timeline, e.g. timestamps in sidx "
32  "and mpd. This is to workaround a Chromium bug that decoding "
33  "timestamp is used in buffered range, https://crbug.com/398130.");
34 DEFINE_bool(dump_stream_info, false, "Dump demuxed stream info.");
35 
36 namespace shaka {
37 namespace media {
38 
39 void DumpStreamInfo(const std::vector<MediaStream*>& streams) {
40  printf("Found %zu stream(s).\n", streams.size());
41  for (size_t i = 0; i < streams.size(); ++i)
42  printf("Stream [%zu] %s\n", i, streams[i]->info()->ToString().c_str());
43 }
44 
45 scoped_ptr<RequestSigner> CreateSigner() {
46  scoped_ptr<RequestSigner> signer;
47 
48  if (!FLAGS_aes_signing_key.empty()) {
49  signer.reset(AesRequestSigner::CreateSigner(
50  FLAGS_signer, FLAGS_aes_signing_key, FLAGS_aes_signing_iv));
51  if (!signer) {
52  LOG(ERROR) << "Cannot create an AES signer object from '"
53  << FLAGS_aes_signing_key << "':'" << FLAGS_aes_signing_iv
54  << "'.";
55  return scoped_ptr<RequestSigner>();
56  }
57  } else if (!FLAGS_rsa_signing_key_path.empty()) {
58  std::string rsa_private_key;
59  if (!File::ReadFileToString(FLAGS_rsa_signing_key_path.c_str(),
60  &rsa_private_key)) {
61  LOG(ERROR) << "Failed to read from '" << FLAGS_rsa_signing_key_path
62  << "'.";
63  return scoped_ptr<RequestSigner>();
64  }
65  signer.reset(RsaRequestSigner::CreateSigner(FLAGS_signer, rsa_private_key));
66  if (!signer) {
67  LOG(ERROR) << "Cannot create a RSA signer object from '"
68  << FLAGS_rsa_signing_key_path << "'.";
69  return scoped_ptr<RequestSigner>();
70  }
71  }
72  return signer.Pass();
73 }
74 
75 scoped_ptr<KeySource> CreateEncryptionKeySource() {
76  scoped_ptr<KeySource> encryption_key_source;
77  if (FLAGS_enable_widevine_encryption) {
78  scoped_ptr<WidevineKeySource> widevine_key_source(
79  new WidevineKeySource(FLAGS_key_server_url, FLAGS_include_common_pssh));
80  if (!FLAGS_signer.empty()) {
81  scoped_ptr<RequestSigner> request_signer(CreateSigner());
82  if (!request_signer)
83  return scoped_ptr<KeySource>();
84  widevine_key_source->set_signer(request_signer.Pass());
85  }
86 
87  std::vector<uint8_t> content_id;
88  if (!base::HexStringToBytes(FLAGS_content_id, &content_id)) {
89  LOG(ERROR) << "Invalid content_id hex string specified.";
90  return scoped_ptr<KeySource>();
91  }
92  Status status = widevine_key_source->FetchKeys(content_id, FLAGS_policy);
93  if (!status.ok()) {
94  LOG(ERROR) << "Widevine encryption key source failed to fetch keys: "
95  << status.ToString();
96  return scoped_ptr<KeySource>();
97  }
98  encryption_key_source = widevine_key_source.Pass();
99  } else if (FLAGS_enable_fixed_key_encryption) {
100  encryption_key_source = FixedKeySource::CreateFromHexStrings(
101  FLAGS_key_id, FLAGS_key, FLAGS_pssh, FLAGS_iv);
102  }
103  return encryption_key_source.Pass();
104 }
105 
106 scoped_ptr<KeySource> CreateDecryptionKeySource() {
107  scoped_ptr<KeySource> decryption_key_source;
108  if (FLAGS_enable_widevine_decryption) {
109  scoped_ptr<WidevineKeySource> widevine_key_source(
110  new WidevineKeySource(FLAGS_key_server_url, FLAGS_include_common_pssh));
111  if (!FLAGS_signer.empty()) {
112  scoped_ptr<RequestSigner> request_signer(CreateSigner());
113  if (!request_signer)
114  return scoped_ptr<KeySource>();
115  widevine_key_source->set_signer(request_signer.Pass());
116  }
117 
118  decryption_key_source = widevine_key_source.Pass();
119  } else if (FLAGS_enable_fixed_key_decryption) {
120  const char kNoPssh[] = "";
121  const char kNoIv[] = "";
122  decryption_key_source = FixedKeySource::CreateFromHexStrings(
123  FLAGS_key_id, FLAGS_key, kNoPssh, kNoIv);
124  }
125  return decryption_key_source.Pass();
126 }
127 
128 bool AssignFlagsFromProfile() {
129  bool single_segment = FLAGS_single_segment;
130  if (FLAGS_profile == "on-demand") {
131  single_segment = true;
132  } else if (FLAGS_profile == "live") {
133  single_segment = false;
134  } else if (FLAGS_profile != "") {
135  fprintf(stderr, "ERROR: --profile '%s' is not supported.\n",
136  FLAGS_profile.c_str());
137  return false;
138  }
139 
140  if (FLAGS_single_segment != single_segment) {
141  FLAGS_single_segment = single_segment;
142  fprintf(stdout, "Profile %s: set --single_segment to %s.\n",
143  FLAGS_profile.c_str(), single_segment ? "true" : "false");
144  }
145  return true;
146 }
147 
148 bool GetMuxerOptions(MuxerOptions* muxer_options) {
149  DCHECK(muxer_options);
150 
151  muxer_options->single_segment = FLAGS_single_segment;
152  muxer_options->segment_duration = FLAGS_segment_duration;
153  muxer_options->fragment_duration = FLAGS_fragment_duration;
154  muxer_options->segment_sap_aligned = FLAGS_segment_sap_aligned;
155  muxer_options->fragment_sap_aligned = FLAGS_fragment_sap_aligned;
156  muxer_options->num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx;
157 
158  if (FLAGS_mp4_use_decoding_timestamp_in_timeline) {
159  LOG(WARNING) << "Flag --mp4_use_decoding_timestamp_in_timeline is set. "
160  "Note that it is a temporary hack to workaround Chromium "
161  "bug https://crbug.com/398130. The flag may be removed "
162  "when the Chromium bug is fixed.";
163  }
164  muxer_options->mp4_use_decoding_timestamp_in_timeline =
165  FLAGS_mp4_use_decoding_timestamp_in_timeline;
166 
167  muxer_options->temp_dir = FLAGS_temp_dir;
168  return true;
169 }
170 
171 bool GetMpdOptions(MpdOptions* mpd_options) {
172  DCHECK(mpd_options);
173 
174  mpd_options->availability_time_offset = FLAGS_availability_time_offset;
175  mpd_options->minimum_update_period = FLAGS_minimum_update_period;
176  mpd_options->min_buffer_time = FLAGS_min_buffer_time;
177  mpd_options->time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
178  mpd_options->suggested_presentation_delay =
179  FLAGS_suggested_presentation_delay;
180  return true;
181 }
182 
183 MediaStream* FindFirstStreamOfType(const std::vector<MediaStream*>& streams,
184  StreamType stream_type) {
185  typedef std::vector<MediaStream*>::const_iterator StreamIterator;
186  for (StreamIterator it = streams.begin(); it != streams.end(); ++it) {
187  if ((*it)->info()->stream_type() == stream_type)
188  return *it;
189  }
190  return NULL;
191 }
192 MediaStream* FindFirstVideoStream(const std::vector<MediaStream*>& streams) {
193  return FindFirstStreamOfType(streams, kStreamVideo);
194 }
195 MediaStream* FindFirstAudioStream(const std::vector<MediaStream*>& streams) {
196  return FindFirstStreamOfType(streams, kStreamAudio);
197 }
198 
199 bool AddStreamToMuxer(const std::vector<MediaStream*>& streams,
200  const std::string& stream_selector,
201  const std::string& language_override,
202  Muxer* muxer) {
203  DCHECK(muxer);
204 
205  MediaStream* stream = NULL;
206  if (stream_selector == "video") {
207  stream = FindFirstVideoStream(streams);
208  } else if (stream_selector == "audio") {
209  stream = FindFirstAudioStream(streams);
210  } else {
211  // Expect stream_selector to be a zero based stream id.
212  size_t stream_id;
213  if (!base::StringToSizeT(stream_selector, &stream_id) ||
214  stream_id >= streams.size()) {
215  LOG(ERROR) << "Invalid argument --stream=" << stream_selector << "; "
216  << "should be 'audio', 'video', or a number within [0, "
217  << streams.size() - 1 << "].";
218  return false;
219  }
220  stream = streams[stream_id];
221  DCHECK(stream);
222  }
223 
224  // This could occur only if stream_selector=audio|video and the corresponding
225  // stream does not exist in the input.
226  if (!stream) {
227  LOG(ERROR) << "No " << stream_selector << " stream found in the input.";
228  return false;
229  }
230 
231  if (!language_override.empty()) {
232  stream->info()->set_language(language_override);
233  }
234 
235  muxer->AddStream(stream);
236  return true;
237 }
238 
239 } // namespace media
240 } // namespace shaka
static RsaRequestSigner * CreateSigner(const std::string &signer_name, const std::string &pkcs1_rsa_key)
static scoped_ptr< FixedKeySource > CreateFromHexStrings(const std::string &key_id_hex, const std::string &key_hex, const std::string &pssh_boxes_hex, const std::string &iv_hex)
static AesRequestSigner * CreateSigner(const std::string &signer_name, const std::string &aes_key_hex, const std::string &iv_hex)
static bool ReadFileToString(const char *file_name, std::string *contents)
Definition: file.cc:189