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