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_media_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 Demuxer::QueuedSample::QueuedSample(uint32_t local_track_id,
152  std::shared_ptr<MediaSample> local_sample)
153  : track_id(local_track_id), sample(local_sample) {}
154 
155 Demuxer::QueuedSample::~QueuedSample() {}
156 
157 Status Demuxer::InitializeParser() {
158  DCHECK(!media_file_);
159  DCHECK(!all_streams_ready_);
160 
161  LOG(INFO) << "Initialize Demuxer for file '" << file_name_ << "'.";
162 
163  media_file_ = File::Open(file_name_.c_str(), "r");
164  if (!media_file_) {
165  return Status(error::FILE_FAILURE,
166  "Cannot open file for reading " + file_name_);
167  }
168 
169  // Read enough bytes before detecting the container.
170  int64_t bytes_read = 0;
171  while (static_cast<size_t>(bytes_read) < kInitBufSize) {
172  int64_t read_result =
173  media_file_->Read(buffer_.get() + bytes_read, kInitBufSize);
174  if (read_result < 0)
175  return Status(error::FILE_FAILURE, "Cannot read file " + file_name_);
176  if (read_result == 0)
177  break;
178  bytes_read += read_result;
179  }
180  container_name_ = DetermineContainer(buffer_.get(), bytes_read);
181 
182  // Initialize media parser.
183  switch (container_name_) {
184  case CONTAINER_MOV:
185  parser_.reset(new mp4::MP4MediaParser());
186  break;
187  case CONTAINER_MPEG2TS:
188  parser_.reset(new mp2t::Mp2tMediaParser());
189  break;
190  // Widevine classic (WVM) is derived from MPEG2PS. We do not support
191  // non-WVM MPEG2PS file, thus we do not differentiate between the two.
192  // Every MPEG2PS file is assumed to be WVM file. If it turns out not the
193  // case, an error will be reported when trying to parse the file as WVM
194  // file.
195  case CONTAINER_MPEG2PS:
196  FALLTHROUGH_INTENDED;
197  case CONTAINER_WVM:
198  parser_.reset(new wvm::WvmMediaParser());
199  break;
200  case CONTAINER_WEBM:
201  parser_.reset(new WebMMediaParser());
202  break;
203  case CONTAINER_WEBVTT:
204  parser_.reset(new WebVttMediaParser());
205  break;
206  default:
207  NOTIMPLEMENTED();
208  return Status(error::UNIMPLEMENTED, "Container not supported.");
209  }
210 
211  parser_->Init(base::Bind(&Demuxer::ParserInitEvent, base::Unretained(this)),
212  base::Bind(&Demuxer::NewSampleEvent, base::Unretained(this)),
213  key_source_.get());
214 
215  // Handle trailing 'moov'.
216  if (container_name_ == CONTAINER_MOV)
217  static_cast<mp4::MP4MediaParser*>(parser_.get())->LoadMoov(file_name_);
218  if (!parser_->Parse(buffer_.get(), bytes_read)) {
219  return Status(error::PARSER_FAILURE,
220  "Cannot parse media file " + file_name_);
221  }
222  return Status::OK;
223 }
224 
225 void Demuxer::ParserInitEvent(
226  const std::vector<std::shared_ptr<StreamInfo>>& stream_infos) {
227  if (dump_stream_info_) {
228  printf("\nFile \"%s\":\n", file_name_.c_str());
229  printf("Found %zu stream(s).\n", stream_infos.size());
230  for (size_t i = 0; i < stream_infos.size(); ++i)
231  printf("Stream [%zu] %s\n", i, stream_infos[i]->ToString().c_str());
232  }
233 
234  int base_stream_index = 0;
235  bool video_handler_set =
236  output_handlers().find(kBaseVideoOutputStreamIndex) !=
237  output_handlers().end();
238  bool audio_handler_set =
239  output_handlers().find(kBaseAudioOutputStreamIndex) !=
240  output_handlers().end();
241  bool text_handler_set =
242  output_handlers().find(kBaseTextOutputStreamIndex) !=
243  output_handlers().end();
244  for (const std::shared_ptr<StreamInfo>& stream_info : stream_infos) {
245  size_t stream_index = base_stream_index;
246  if (video_handler_set && stream_info->stream_type() == kStreamVideo) {
247  stream_index = kBaseVideoOutputStreamIndex;
248  // Only for the first video stream.
249  video_handler_set = false;
250  }
251  if (audio_handler_set && stream_info->stream_type() == kStreamAudio) {
252  stream_index = kBaseAudioOutputStreamIndex;
253  // Only for the first audio stream.
254  audio_handler_set = false;
255  }
256  if (text_handler_set && stream_info->stream_type() == kStreamText) {
257  stream_index = kBaseTextOutputStreamIndex;
258  text_handler_set = false;
259  }
260 
261  const bool handler_set =
262  output_handlers().find(stream_index) != output_handlers().end();
263  if (handler_set) {
264  track_id_to_stream_index_map_[stream_info->track_id()] = stream_index;
265  stream_indexes_.push_back(stream_index);
266  auto iter = language_overrides_.find(stream_index);
267  if (iter != language_overrides_.end() &&
268  stream_info->stream_type() != kStreamVideo) {
269  stream_info->set_language(iter->second);
270  }
271  if (stream_info->is_encrypted()) {
272  init_event_status_.Update(Status(error::INVALID_ARGUMENT,
273  "A decryption key source is not "
274  "provided for an encrypted stream."));
275  } else {
276  init_event_status_.Update(
277  DispatchStreamInfo(stream_index, stream_info));
278  }
279  } else {
280  track_id_to_stream_index_map_[stream_info->track_id()] =
281  kInvalidStreamIndex;
282  }
283  ++base_stream_index;
284  }
285  all_streams_ready_ = true;
286 }
287 
288 bool Demuxer::NewSampleEvent(uint32_t track_id,
289  const std::shared_ptr<MediaSample>& sample) {
290  if (!all_streams_ready_) {
291  if (queued_samples_.size() >= kQueuedSamplesLimit) {
292  LOG(ERROR) << "Queued samples limit reached: " << kQueuedSamplesLimit;
293  return false;
294  }
295  queued_samples_.push_back(QueuedSample(track_id, sample));
296  return true;
297  }
298  if (!init_event_status_.ok()) {
299  return false;
300  }
301  while (!queued_samples_.empty()) {
302  if (!PushSample(queued_samples_.front().track_id,
303  queued_samples_.front().sample)) {
304  return false;
305  }
306  queued_samples_.pop_front();
307  }
308  return PushSample(track_id, sample);
309 }
310 
311 bool Demuxer::PushSample(uint32_t track_id,
312  const std::shared_ptr<MediaSample>& sample) {
313  auto stream_index_iter = track_id_to_stream_index_map_.find(track_id);
314  if (stream_index_iter == track_id_to_stream_index_map_.end()) {
315  LOG(ERROR) << "Track " << track_id << " not found.";
316  return false;
317  }
318  if (stream_index_iter->second == kInvalidStreamIndex)
319  return true;
320  Status status = DispatchMediaSample(stream_index_iter->second, sample);
321  if (!status.ok()) {
322  LOG(ERROR) << "Failed to process sample " << stream_index_iter->second
323  << " " << status;
324  }
325  return status.ok();
326 }
327 
328 Status Demuxer::Parse() {
329  DCHECK(media_file_);
330  DCHECK(parser_);
331  DCHECK(buffer_);
332 
333  int64_t bytes_read = media_file_->Read(buffer_.get(), kBufSize);
334  if (bytes_read == 0) {
335  if (!parser_->Flush())
336  return Status(error::PARSER_FAILURE, "Failed to flush.");
337  return Status(error::END_OF_STREAM, "");
338  } else if (bytes_read < 0) {
339  return Status(error::FILE_FAILURE, "Cannot read file " + file_name_);
340  }
341 
342  return parser_->Parse(buffer_.get(), bytes_read)
343  ? Status::OK
344  : Status(error::PARSER_FAILURE,
345  "Cannot parse media file " + file_name_);
346 }
347 
348 } // namespace media
349 } // namespace shaka
Status SetHandler(const std::string &stream_label, std::shared_ptr< MediaHandler > handler)
Definition: demuxer.cc:133
Status Run() override
Definition: demuxer.cc:88
void SetLanguageOverride(const std::string &stream_label, const std::string &language_override)
Definition: demuxer.cc:143
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:84
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:76
Status DispatchStreamInfo(size_t stream_index, std::shared_ptr< const StreamInfo > stream_info)
Dispatch the stream info to downstream handlers.
Status DispatchMediaSample(size_t stream_index, std::shared_ptr< const MediaSample > media_sample)
Dispatch the media sample to downstream handlers.
Demuxer(const std::string &file_name)
Definition: demuxer.cc:76
virtual bool Open()=0
Internal open. Should not be used directly.
void Cancel() override
Definition: demuxer.cc:129
Status SetHandler(size_t output_stream_index, std::shared_ptr< MediaHandler > handler)
Connect downstream handler at the specified output stream index.