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