DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
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/base/demuxer.h"
8 
9 #include "packager/base/bind.h"
10 #include "packager/base/logging.h"
11 #include "packager/media/base/decryptor_source.h"
12 #include "packager/media/base/key_source.h"
13 #include "packager/media/base/media_sample.h"
14 #include "packager/media/base/media_stream.h"
15 #include "packager/media/base/stream_info.h"
16 #include "packager/media/file/file.h"
17 #include "packager/media/formats/mp2t/mp2t_media_parser.h"
18 #include "packager/media/formats/mp4/mp4_media_parser.h"
19 #include "packager/media/formats/webm/webm_media_parser.h"
20 #include "packager/media/formats/webvtt/webvtt_media_parser.h"
21 #include "packager/media/formats/wvm/wvm_media_parser.h"
22 
23 namespace {
24 // 65KB, sufficient to determine the container and likely all init data.
25 const size_t kInitBufSize = 0x10000;
26 const size_t kBufSize = 0x200000; // 2MB
27 // Maximum number of allowed queued samples. If we are receiving a lot of
28 // samples before seeing init_event, something is not right. The number
29 // set here is arbitrary though.
30 const size_t kQueuedSamplesLimit = 10000;
31 }
32 
33 namespace shaka {
34 namespace media {
35 
36 Demuxer::Demuxer(const std::string& file_name)
37  : file_name_(file_name),
38  media_file_(NULL),
39  init_event_received_(false),
40  container_name_(CONTAINER_UNKNOWN),
41  buffer_(new uint8_t[kBufSize]),
42  cancelled_(false) {
43 }
44 
45 Demuxer::~Demuxer() {
46  if (media_file_)
47  media_file_->Close();
48 }
49 
50 void Demuxer::SetKeySource(std::unique_ptr<KeySource> key_source) {
51  key_source_ = std::move(key_source);
52 }
53 
55  DCHECK(!media_file_);
56  DCHECK(!init_event_received_);
57 
58  LOG(INFO) << "Initialize Demuxer for file '" << file_name_ << "'.";
59 
60  media_file_ = File::Open(file_name_.c_str(), "r");
61  if (!media_file_) {
62  return Status(error::FILE_FAILURE,
63  "Cannot open file for reading " + file_name_);
64  }
65 
66  // Read enough bytes before detecting the container.
67  size_t bytes_read = 0;
68  while (bytes_read < kInitBufSize) {
69  int64_t read_result =
70  media_file_->Read(buffer_.get() + bytes_read, kInitBufSize);
71  if (read_result < 0)
72  return Status(error::FILE_FAILURE, "Cannot read file " + file_name_);
73  if (read_result == 0)
74  break;
75  bytes_read += read_result;
76  }
77  container_name_ = DetermineContainer(buffer_.get(), bytes_read);
78 
79  // Initialize media parser.
80  switch (container_name_) {
81  case CONTAINER_MOV:
82  parser_.reset(new mp4::MP4MediaParser());
83  break;
84  case CONTAINER_MPEG2TS:
85  parser_.reset(new mp2t::Mp2tMediaParser());
86  break;
87  case CONTAINER_MPEG2PS:
88  parser_.reset(new wvm::WvmMediaParser());
89  break;
90  case CONTAINER_WEBM:
91  parser_.reset(new WebMMediaParser());
92  break;
93  case CONTAINER_WEBVTT:
94  parser_.reset(new WebVttMediaParser());
95  break;
96  default:
97  NOTIMPLEMENTED();
98  return Status(error::UNIMPLEMENTED, "Container not supported.");
99  }
100 
101  parser_->Init(base::Bind(&Demuxer::ParserInitEvent, base::Unretained(this)),
102  base::Bind(&Demuxer::NewSampleEvent, base::Unretained(this)),
103  key_source_.get());
104 
105  // Handle trailing 'moov'.
106  if (container_name_ == CONTAINER_MOV)
107  static_cast<mp4::MP4MediaParser*>(parser_.get())->LoadMoov(file_name_);
108 
109  if (!parser_->Parse(buffer_.get(), bytes_read)) {
110  init_parsing_status_ =
111  Status(error::PARSER_FAILURE, "Cannot parse media file " + file_name_);
112  }
113 
114  // Parse until init event received or on error.
115  while (!init_event_received_ && init_parsing_status_.ok())
116  init_parsing_status_ = Parse();
117  // Defer error reporting if init completed successfully.
118  return init_event_received_ ? Status::OK : init_parsing_status_;
119 }
120 
121 void Demuxer::ParserInitEvent(
122  const std::vector<scoped_refptr<StreamInfo>>& stream_infos) {
123  init_event_received_ = true;
124  for (const scoped_refptr<StreamInfo>& stream_info : stream_infos)
125  streams_.emplace_back(new MediaStream(stream_info, this));
126 }
127 
128 Demuxer::QueuedSample::QueuedSample(uint32_t local_track_id,
129  scoped_refptr<MediaSample> local_sample)
130  : track_id(local_track_id), sample(local_sample) {}
131 Demuxer::QueuedSample::~QueuedSample() {}
132 
133 bool Demuxer::NewSampleEvent(uint32_t track_id,
134  const scoped_refptr<MediaSample>& sample) {
135  if (!init_event_received_) {
136  if (queued_samples_.size() >= kQueuedSamplesLimit) {
137  LOG(ERROR) << "Queued samples limit reached: " << kQueuedSamplesLimit;
138  return false;
139  }
140  queued_samples_.push_back(QueuedSample(track_id, sample));
141  return true;
142  }
143  while (!queued_samples_.empty()) {
144  if (!PushSample(queued_samples_.front().track_id,
145  queued_samples_.front().sample)) {
146  return false;
147  }
148  queued_samples_.pop_front();
149  }
150  return PushSample(track_id, sample);
151 }
152 
153 bool Demuxer::PushSample(uint32_t track_id,
154  const scoped_refptr<MediaSample>& sample) {
155  for (const std::unique_ptr<MediaStream>& stream : streams_) {
156  if (track_id == stream->info()->track_id()) {
157  Status status = stream->PushSample(sample);
158  if (!status.ok())
159  LOG(ERROR) << "Demuxer::PushSample failed with " << status;
160  return status.ok();
161  }
162  }
163  LOG(ERROR) << "Track " << track_id << " not found.";
164  return false;
165 }
166 
168  Status status;
169 
170  LOG(INFO) << "Demuxer::Run() on file '" << file_name_ << "'.";
171 
172  // Start the streams.
173  for (const std::unique_ptr<MediaStream>& stream : streams_) {
174  status = stream->Start(MediaStream::kPush);
175  if (!status.ok())
176  return status;
177  }
178 
179  while (!cancelled_ && (status = Parse()).ok())
180  continue;
181 
182  if (cancelled_ && status.ok())
183  return Status(error::CANCELLED, "Demuxer run cancelled");
184 
185  if (status.error_code() == error::END_OF_STREAM) {
186  // Push EOS sample to muxer to indicate end of stream.
187  const scoped_refptr<MediaSample>& sample = MediaSample::CreateEOSBuffer();
188  for (const std::unique_ptr<MediaStream>& stream : streams_) {
189  status = stream->PushSample(sample);
190  if (!status.ok())
191  return status;
192  }
193  }
194  return status;
195 }
196 
198  DCHECK(media_file_);
199  DCHECK(parser_);
200  DCHECK(buffer_);
201 
202  // Return early and avoid call Parse(...) again if it has already failed at
203  // the initialization.
204  if (!init_parsing_status_.ok())
205  return init_parsing_status_;
206 
207  int64_t bytes_read = media_file_->Read(buffer_.get(), kBufSize);
208  if (bytes_read == 0) {
209  if (!parser_->Flush())
210  return Status(error::PARSER_FAILURE, "Failed to flush.");
211  return Status(error::END_OF_STREAM, "");
212  } else if (bytes_read < 0) {
213  return Status(error::FILE_FAILURE, "Cannot read file " + file_name_);
214  }
215 
216  return parser_->Parse(buffer_.get(), bytes_read)
217  ? Status::OK
218  : Status(error::PARSER_FAILURE,
219  "Cannot parse media file " + file_name_);
220 }
221 
223  cancelled_ = true;
224 }
225 
226 } // namespace media
227 } // namespace shaka
virtual bool Open()=0
Internal open. Should not be used directly.
virtual bool Close()=0
void SetKeySource(std::unique_ptr< KeySource > key_source)
Definition: demuxer.cc:50
virtual int64_t Read(void *buffer, uint64_t length)=0
static scoped_refptr< MediaSample > CreateEOSBuffer()
Definition: media_sample.cc:80
Status Parse()
Read from the source and send it to the parser.
Definition: demuxer.cc:197
Status Initialize()
Definition: demuxer.cc:54
Demuxer(const std::string &file_name)
Definition: demuxer.cc:36