Shaka Packager SDK
demuxer.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/media/demuxer/demuxer.h"
8 
9 #include <algorithm>
10 
11 #include "packager/base/bind.h"
12 #include "packager/base/logging.h"
13 #include "packager/base/strings/string_number_conversions.h"
14 #include "packager/file/file.h"
15 #include "packager/media/base/decryptor_source.h"
16 #include "packager/media/base/key_source.h"
17 #include "packager/media/base/macros.h"
18 #include "packager/media/base/media_sample.h"
19 #include "packager/media/base/stream_info.h"
20 #include "packager/media/formats/mp2t/mp2t_media_parser.h"
21 #include "packager/media/formats/mp4/mp4_media_parser.h"
22 #include "packager/media/formats/webm/webm_media_parser.h"
23 #include "packager/media/formats/webvtt/webvtt_parser.h"
24 #include "packager/media/formats/wvm/wvm_media_parser.h"
25 
26 namespace {
27 // 65KB, sufficient to determine the container and likely all init data.
28 const size_t kInitBufSize = 0x10000;
29 const size_t kBufSize = 0x200000; // 2MB
30 // Maximum number of allowed queued samples. If we are receiving a lot of
31 // samples before seeing init_event, something is not right. The number
32 // set here is arbitrary though.
33 const size_t kQueuedSamplesLimit = 10000;
34 const size_t kInvalidStreamIndex = static_cast<size_t>(-1);
35 const size_t kBaseVideoOutputStreamIndex = 0x100;
36 const size_t kBaseAudioOutputStreamIndex = 0x200;
37 const size_t kBaseTextOutputStreamIndex = 0x300;
38 
39 std::string GetStreamLabel(size_t stream_index) {
40  switch (stream_index) {
41  case kBaseVideoOutputStreamIndex:
42  return "video";
43  case kBaseAudioOutputStreamIndex:
44  return "audio";
45  case kBaseTextOutputStreamIndex:
46  return "text";
47  default:
48  return base::SizeTToString(stream_index);
49  }
50 }
51 
52 bool GetStreamIndex(const std::string& stream_label, size_t* stream_index) {
53  DCHECK(stream_index);
54  if (stream_label == "video") {
55  *stream_index = kBaseVideoOutputStreamIndex;
56  } else if (stream_label == "audio") {
57  *stream_index = kBaseAudioOutputStreamIndex;
58  } else if (stream_label == "text") {
59  *stream_index = kBaseTextOutputStreamIndex;
60  } else {
61  // Expect stream_label to be a zero based stream id.
62  if (!base::StringToSizeT(stream_label, stream_index)) {
63  LOG(ERROR) << "Invalid argument --stream=" << stream_label << "; "
64  << "should be 'audio', 'video', 'text', or a number";
65  return false;
66  }
67  }
68  return true;
69 }
70 
71 }
72 
73 namespace shaka {
74 namespace media {
75 
76 Demuxer::Demuxer(const std::string& file_name)
77  : file_name_(file_name), buffer_(new uint8_t[kBufSize]) {}
78 
79 Demuxer::~Demuxer() {
80  if (media_file_)
81  media_file_->Close();
82 }
83 
84 void Demuxer::SetKeySource(std::unique_ptr<KeySource> key_source) {
85  key_source_ = std::move(key_source);
86 }
87 
89  LOG(INFO) << "Demuxer::Run() on file '" << file_name_ << "'.";
90  Status status = InitializeParser();
91  // ParserInitEvent callback is called after a few calls to Parse(), which sets
92  // up the streams. Only after that, we can verify the outputs below.
93  while (!all_streams_ready_ && status.ok())
94  status.Update(Parse());
95  // If no output is defined, then return success after receiving all stream
96  // info.
97  if (all_streams_ready_ && output_handlers().empty())
98  return Status::OK;
99  if (!init_event_status_.ok())
100  return init_event_status_;
101  if (!status.ok())
102  return status;
103  // Check if all specified outputs exists.
104  for (const auto& pair : output_handlers()) {
105  if (std::find(stream_indexes_.begin(), stream_indexes_.end(), pair.first) ==
106  stream_indexes_.end()) {
107  LOG(ERROR) << "Invalid argument, stream=" << GetStreamLabel(pair.first)
108  << " not available.";
109  return Status(error::INVALID_ARGUMENT, "Stream not available");
110  }
111  }
112 
113  while (!cancelled_ && status.ok())
114  status.Update(Parse());
115  if (cancelled_ && status.ok())
116  return Status(error::CANCELLED, "Demuxer run cancelled");
117 
118  if (status.error_code() == error::END_OF_STREAM) {
119  for (size_t stream_index : stream_indexes_) {
120  status = FlushDownstream(stream_index);
121  if (!status.ok())
122  return status;
123  }
124  return Status::OK;
125  }
126  return status;
127 }
128 
130  cancelled_ = true;
131 }
132 
133 Status Demuxer::SetHandler(const std::string& stream_label,
134  std::shared_ptr<MediaHandler> handler) {
135  size_t stream_index = kInvalidStreamIndex;
136  if (!GetStreamIndex(stream_label, &stream_index)) {
137  return Status(error::INVALID_ARGUMENT,
138  "Invalid stream: " + stream_label);
139  }
140  return MediaHandler::SetHandler(stream_index, std::move(handler));
141 }
142 
143 void Demuxer::SetLanguageOverride(const std::string& stream_label,
144  const std::string& language_override) {
145  size_t stream_index = kInvalidStreamIndex;
146  if (!GetStreamIndex(stream_label, &stream_index))
147  LOG(WARNING) << "Invalid stream for language override " << stream_label;
148  language_overrides_[stream_index] = language_override;
149 }
150 
151 Status Demuxer::InitializeParser() {
152  DCHECK(!media_file_);
153  DCHECK(!all_streams_ready_);
154 
155  LOG(INFO) << "Initialize Demuxer for file '" << file_name_ << "'.";
156 
157  media_file_ = File::Open(file_name_.c_str(), "r");
158  if (!media_file_) {
159  return Status(error::FILE_FAILURE,
160  "Cannot open file for reading " + file_name_);
161  }
162 
163  // Read enough bytes before detecting the container.
164  int64_t bytes_read = 0;
165  while (static_cast<size_t>(bytes_read) < kInitBufSize) {
166  int64_t read_result =
167  media_file_->Read(buffer_.get() + bytes_read, kInitBufSize);
168  if (read_result < 0)
169  return Status(error::FILE_FAILURE, "Cannot read file " + file_name_);
170  if (read_result == 0)
171  break;
172  bytes_read += read_result;
173  }
174  container_name_ = DetermineContainer(buffer_.get(), bytes_read);
175 
176  // Initialize media parser.
177  switch (container_name_) {
178  case CONTAINER_MOV:
179  parser_.reset(new mp4::MP4MediaParser());
180  break;
181  case CONTAINER_MPEG2TS:
182  parser_.reset(new mp2t::Mp2tMediaParser());
183  break;
184  // Widevine classic (WVM) is derived from MPEG2PS. We do not support
185  // non-WVM MPEG2PS file, thus we do not differentiate between the two.
186  // Every MPEG2PS file is assumed to be WVM file. If it turns out not the
187  // case, an error will be reported when trying to parse the file as WVM
188  // file.
189  case CONTAINER_MPEG2PS:
190  FALLTHROUGH_INTENDED;
191  case CONTAINER_WVM:
192  parser_.reset(new wvm::WvmMediaParser());
193  break;
194  case CONTAINER_WEBM:
195  parser_.reset(new WebMMediaParser());
196  break;
197  case CONTAINER_WEBVTT:
198  parser_.reset(new WebVttParser());
199  break;
200  case CONTAINER_UNKNOWN: {
201  const int64_t kDumpSizeLimit = 512;
202  LOG(ERROR) << "Failed to detect the container type from the buffer: "
203  << base::HexEncode(buffer_.get(),
204  std::min(bytes_read, kDumpSizeLimit));
205  return Status(error::INVALID_ARGUMENT,
206  "Failed to detect the container type.");
207  }
208  default:
209  NOTIMPLEMENTED() << "Container " << container_name_
210  << " is not supported.";
211  return Status(error::UNIMPLEMENTED, "Container not supported.");
212  }
213 
214  parser_->Init(
215  base::Bind(&Demuxer::ParserInitEvent, base::Unretained(this)),
216  base::Bind(&Demuxer::NewMediaSampleEvent, base::Unretained(this)),
217  base::Bind(&Demuxer::NewTextSampleEvent, base::Unretained(this)),
218  key_source_.get());
219 
220  // Handle trailing 'moov'.
221  if (container_name_ == CONTAINER_MOV &&
222  File::IsLocalRegularFile(file_name_.c_str())) {
223  // TODO(kqyang): Investigate whether we can reuse the existing file
224  // descriptor |media_file_| instead of opening the same file again.
225  static_cast<mp4::MP4MediaParser*>(parser_.get())->LoadMoov(file_name_);
226  }
227  if (!parser_->Parse(buffer_.get(), bytes_read)) {
228  return Status(error::PARSER_FAILURE,
229  "Cannot parse media file " + file_name_);
230  }
231  return Status::OK;
232 }
233 
234 void Demuxer::ParserInitEvent(
235  const std::vector<std::shared_ptr<StreamInfo>>& stream_infos) {
236  if (dump_stream_info_) {
237  printf("\nFile \"%s\":\n", file_name_.c_str());
238  printf("Found %zu stream(s).\n", stream_infos.size());
239  for (size_t i = 0; i < stream_infos.size(); ++i)
240  printf("Stream [%zu] %s\n", i, stream_infos[i]->ToString().c_str());
241  }
242 
243  int base_stream_index = 0;
244  bool video_handler_set =
245  output_handlers().find(kBaseVideoOutputStreamIndex) !=
246  output_handlers().end();
247  bool audio_handler_set =
248  output_handlers().find(kBaseAudioOutputStreamIndex) !=
249  output_handlers().end();
250  bool text_handler_set =
251  output_handlers().find(kBaseTextOutputStreamIndex) !=
252  output_handlers().end();
253  for (const std::shared_ptr<StreamInfo>& stream_info : stream_infos) {
254  size_t stream_index = base_stream_index;
255  if (video_handler_set && stream_info->stream_type() == kStreamVideo) {
256  stream_index = kBaseVideoOutputStreamIndex;
257  // Only for the first video stream.
258  video_handler_set = false;
259  }
260  if (audio_handler_set && stream_info->stream_type() == kStreamAudio) {
261  stream_index = kBaseAudioOutputStreamIndex;
262  // Only for the first audio stream.
263  audio_handler_set = false;
264  }
265  if (text_handler_set && stream_info->stream_type() == kStreamText) {
266  stream_index = kBaseTextOutputStreamIndex;
267  text_handler_set = false;
268  }
269 
270  const bool handler_set =
271  output_handlers().find(stream_index) != output_handlers().end();
272  if (handler_set) {
273  track_id_to_stream_index_map_[stream_info->track_id()] = stream_index;
274  stream_indexes_.push_back(stream_index);
275  auto iter = language_overrides_.find(stream_index);
276  if (iter != language_overrides_.end() &&
277  stream_info->stream_type() != kStreamVideo) {
278  stream_info->set_language(iter->second);
279  }
280  if (stream_info->is_encrypted()) {
281  init_event_status_.Update(Status(error::INVALID_ARGUMENT,
282  "A decryption key source is not "
283  "provided for an encrypted stream."));
284  } else {
285  init_event_status_.Update(
286  DispatchStreamInfo(stream_index, stream_info));
287  }
288  } else {
289  track_id_to_stream_index_map_[stream_info->track_id()] =
290  kInvalidStreamIndex;
291  }
292  ++base_stream_index;
293  }
294  all_streams_ready_ = true;
295 }
296 
297 bool Demuxer::NewMediaSampleEvent(uint32_t track_id,
298  std::shared_ptr<MediaSample> sample) {
299  if (!all_streams_ready_) {
300  if (queued_media_samples_.size() >= kQueuedSamplesLimit) {
301  LOG(ERROR) << "Queued samples limit reached: " << kQueuedSamplesLimit;
302  return false;
303  }
304  queued_media_samples_.emplace_back(track_id, sample);
305  return true;
306  }
307  if (!init_event_status_.ok()) {
308  return false;
309  }
310 
311  while (!queued_media_samples_.empty()) {
312  if (!PushMediaSample(queued_media_samples_.front().track_id,
313  queued_media_samples_.front().sample)) {
314  return false;
315  }
316  queued_media_samples_.pop_front();
317  }
318  return PushMediaSample(track_id, sample);
319 }
320 
321 bool Demuxer::NewTextSampleEvent(uint32_t track_id,
322  std::shared_ptr<TextSample> sample) {
323  if (!all_streams_ready_) {
324  if (queued_text_samples_.size() >= kQueuedSamplesLimit) {
325  LOG(ERROR) << "Queued samples limit reached: " << kQueuedSamplesLimit;
326  return false;
327  }
328  queued_text_samples_.emplace_back(track_id, sample);
329  return true;
330  }
331  if (!init_event_status_.ok()) {
332  return false;
333  }
334 
335  while (!queued_text_samples_.empty()) {
336  if (!PushTextSample(queued_text_samples_.front().track_id,
337  queued_text_samples_.front().sample)) {
338  return false;
339  }
340  queued_text_samples_.pop_front();
341  }
342  return PushTextSample(track_id, sample);
343 }
344 
345 bool Demuxer::PushMediaSample(uint32_t track_id,
346  std::shared_ptr<MediaSample> sample) {
347  auto stream_index_iter = track_id_to_stream_index_map_.find(track_id);
348  if (stream_index_iter == track_id_to_stream_index_map_.end()) {
349  LOG(ERROR) << "Track " << track_id << " not found.";
350  return false;
351  }
352  if (stream_index_iter->second == kInvalidStreamIndex)
353  return true;
354  Status status = DispatchMediaSample(stream_index_iter->second, sample);
355  if (!status.ok()) {
356  LOG(ERROR) << "Failed to process sample " << stream_index_iter->second
357  << " " << status;
358  return false;
359  }
360  return true;
361 }
362 
363 bool Demuxer::PushTextSample(uint32_t track_id,
364  std::shared_ptr<TextSample> sample) {
365  auto stream_index_iter = track_id_to_stream_index_map_.find(track_id);
366  if (stream_index_iter == track_id_to_stream_index_map_.end()) {
367  LOG(ERROR) << "Track " << track_id << " not found.";
368  return false;
369  }
370  if (stream_index_iter->second == kInvalidStreamIndex)
371  return true;
372  Status status = DispatchTextSample(stream_index_iter->second, sample);
373  if (!status.ok()) {
374  LOG(ERROR) << "Failed to process sample " << stream_index_iter->second
375  << " " << status;
376  return false;
377  }
378  return true;
379 }
380 
381 Status Demuxer::Parse() {
382  DCHECK(media_file_);
383  DCHECK(parser_);
384  DCHECK(buffer_);
385 
386  int64_t bytes_read = media_file_->Read(buffer_.get(), kBufSize);
387  if (bytes_read == 0) {
388  if (!parser_->Flush())
389  return Status(error::PARSER_FAILURE, "Failed to flush.");
390  return Status(error::END_OF_STREAM, "");
391  } else if (bytes_read < 0) {
392  return Status(error::FILE_FAILURE, "Cannot read file " + file_name_);
393  }
394 
395  return parser_->Parse(buffer_.get(), bytes_read)
396  ? Status::OK
397  : Status(error::PARSER_FAILURE,
398  "Cannot parse media file " + file_name_);
399 }
400 
401 } // namespace media
402 } // namespace shaka
shaka::Status::Update
void Update(Status new_status)
Definition: status.cc:78
shaka::File::IsLocalRegularFile
static bool IsLocalRegularFile(const char *file_name)
Definition: file.cc:377
shaka::media::Demuxer::SetHandler
Status SetHandler(const std::string &stream_label, std::shared_ptr< MediaHandler > handler)
Definition: demuxer.cc:133
shaka::media::MediaHandler::DispatchMediaSample
Status DispatchMediaSample(size_t stream_index, std::shared_ptr< const MediaSample > media_sample) const
Dispatch the media sample to downstream handlers.
Definition: media_handler.h:207
shaka::media::MediaHandler::SetHandler
Status SetHandler(size_t output_stream_index, std::shared_ptr< MediaHandler > handler)
Connect downstream handler at the specified output stream index.
Definition: media_handler.cc:34
shaka::File::Read
virtual int64_t Read(void *buffer, uint64_t length)=0
shaka
All the methods that are virtual are virtual for mocking.
Definition: gflags_hex_bytes.cc:11
shaka::media::Demuxer::Demuxer
Demuxer(const std::string &file_name)
Definition: demuxer.cc:76
shaka::media::Demuxer::Run
Status Run() override
Definition: demuxer.cc:88
shaka::media::MediaHandler::DispatchStreamInfo
Status DispatchStreamInfo(size_t stream_index, std::shared_ptr< const StreamInfo > stream_info) const
Dispatch the stream info to downstream handlers.
Definition: media_handler.h:199
shaka::File::Close
virtual bool Close()=0
shaka::media::Demuxer::SetKeySource
void SetKeySource(std::unique_ptr< KeySource > key_source)
Definition: demuxer.cc:84
shaka::media::Demuxer::Cancel
void Cancel() override
Definition: demuxer.cc:129
shaka::Status
Definition: status.h:110
shaka::media::MediaHandler::FlushDownstream
Status FlushDownstream(size_t output_stream_index)
Flush the downstream connected at the specified output stream index.
Definition: media_handler.cc:105
shaka::media::MediaHandler::DispatchTextSample
Status DispatchTextSample(size_t stream_index, std::shared_ptr< const TextSample > text_sample) const
Dispatch the text sample to downstream handlers.
Definition: media_handler.h:216
shaka::media::Demuxer::SetLanguageOverride
void SetLanguageOverride(const std::string &stream_label, const std::string &language_override)
Definition: demuxer.cc:143
shaka::File::Open
virtual bool Open()=0
Internal open. Should not be used directly.