DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerator
mp4_media_parser.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "packager/media/formats/mp4/mp4_media_parser.h"
6 
7 #include <limits>
8 
9 #include "packager/base/callback.h"
10 #include "packager/base/callback_helpers.h"
11 #include "packager/base/logging.h"
12 #include "packager/base/memory/ref_counted.h"
13 #include "packager/base/strings/string_number_conversions.h"
14 #include "packager/media/base/aes_encryptor.h"
15 #include "packager/media/base/audio_stream_info.h"
16 #include "packager/media/base/buffer_reader.h"
17 #include "packager/media/base/decrypt_config.h"
18 #include "packager/media/base/key_source.h"
19 #include "packager/media/base/media_sample.h"
20 #include "packager/media/base/video_stream_info.h"
21 #include "packager/media/file/file.h"
22 #include "packager/media/file/file_closer.h"
23 #include "packager/media/filters/avc_decoder_configuration.h"
24 #include "packager/media/formats/mp4/box_definitions.h"
25 #include "packager/media/formats/mp4/box_reader.h"
26 #include "packager/media/formats/mp4/es_descriptor.h"
27 #include "packager/media/formats/mp4/rcheck.h"
28 #include "packager/media/formats/mp4/track_run_iterator.h"
29 
30 namespace {
31 
32 uint64_t Rescale(uint64_t time_in_old_scale,
33  uint32_t old_scale,
34  uint32_t new_scale) {
35  return (static_cast<double>(time_in_old_scale) / old_scale) * new_scale;
36 }
37 
38 
39 const char kWidevineKeySystemId[] = "edef8ba979d64acea3c827dcd51d21ed";
40 
41 } // namespace
42 
43 namespace edash_packager {
44 namespace media {
45 namespace mp4 {
46 
47 MP4MediaParser::MP4MediaParser()
48  : state_(kWaitingForInit), moof_head_(0), mdat_tail_(0) {}
49 
50 MP4MediaParser::~MP4MediaParser() {
51  STLDeleteValues(&decryptor_map_);
52 }
53 
54 void MP4MediaParser::Init(const InitCB& init_cb,
55  const NewSampleCB& new_sample_cb,
56  KeySource* decryption_key_source) {
57  DCHECK_EQ(state_, kWaitingForInit);
58  DCHECK(init_cb_.is_null());
59  DCHECK(!init_cb.is_null());
60  DCHECK(!new_sample_cb.is_null());
61 
62  ChangeState(kParsingBoxes);
63  init_cb_ = init_cb;
64  new_sample_cb_ = new_sample_cb;
65  decryption_key_source_ = decryption_key_source;
66 }
67 
68 void MP4MediaParser::Reset() {
69  queue_.Reset();
70  runs_.reset();
71  moof_head_ = 0;
72  mdat_tail_ = 0;
73 }
74 
76  DCHECK_NE(state_, kWaitingForInit);
77  Reset();
78  ChangeState(kParsingBoxes);
79 }
80 
81 bool MP4MediaParser::Parse(const uint8_t* buf, int size) {
82  DCHECK_NE(state_, kWaitingForInit);
83 
84  if (state_ == kError)
85  return false;
86 
87  queue_.Push(buf, size);
88 
89  bool result, err = false;
90 
91  do {
92  if (state_ == kParsingBoxes) {
93  result = ParseBox(&err);
94  } else {
95  DCHECK_EQ(kEmittingSamples, state_);
96  result = EnqueueSample(&err);
97  if (result) {
98  int64_t max_clear = runs_->GetMaxClearOffset() + moof_head_;
99  err = !ReadAndDiscardMDATsUntil(max_clear);
100  }
101  }
102  } while (result && !err);
103 
104  if (err) {
105  DLOG(ERROR) << "Error while parsing MP4";
106  moov_.reset();
107  Reset();
108  ChangeState(kError);
109  return false;
110  }
111 
112  return true;
113 }
114 
115 bool MP4MediaParser::LoadMoov(const std::string& file_path) {
116  scoped_ptr<File, FileCloser> file(
117  File::OpenWithNoBuffering(file_path.c_str(), "r"));
118  if (!file) {
119  LOG(ERROR) << "Unable to open media file '" << file_path << "'";
120  return false;
121  }
122  if (!file->Seek(0)) {
123  LOG(WARNING) << "Filesystem does not support seeking on file '" << file_path
124  << "'";
125  return false;
126  }
127 
128  uint64_t file_position(0);
129  bool mdat_seen(false);
130  while (true) {
131  const uint32_t kBoxHeaderReadSize(16);
132  std::vector<uint8_t> buffer(kBoxHeaderReadSize);
133  int64_t bytes_read = file->Read(&buffer[0], kBoxHeaderReadSize);
134  if (bytes_read == 0) {
135  LOG(ERROR) << "Could not find 'moov' box in file '" << file_path << "'";
136  return false;
137  }
138  if (bytes_read < kBoxHeaderReadSize) {
139  LOG(ERROR) << "Error reading media file '" << file_path << "'";
140  return false;
141  }
142  uint64_t box_size;
143  FourCC box_type;
144  bool err;
145  if (!BoxReader::StartTopLevelBox(&buffer[0], kBoxHeaderReadSize, &box_type,
146  &box_size, &err)) {
147  LOG(ERROR) << "Could not start top level box from file '" << file_path
148  << "'";
149  return false;
150  }
151  if (box_type == FOURCC_MDAT) {
152  mdat_seen = true;
153  } else if (box_type == FOURCC_MOOV) {
154  if (!mdat_seen) {
155  // 'moov' is before 'mdat'. Nothing to do.
156  break;
157  }
158  // 'mdat' before 'moov'. Read and parse 'moov'.
159  if (!Parse(&buffer[0], bytes_read)) {
160  LOG(ERROR) << "Error parsing mp4 file '" << file_path << "'";
161  return false;
162  }
163  uint64_t bytes_to_read = box_size - bytes_read;
164  buffer.resize(bytes_to_read);
165  while (bytes_to_read > 0) {
166  bytes_read = file->Read(&buffer[0], bytes_to_read);
167  if (bytes_read <= 0) {
168  LOG(ERROR) << "Error reading 'moov' contents from file '" << file_path
169  << "'";
170  return false;
171  }
172  if (!Parse(&buffer[0], bytes_read)) {
173  LOG(ERROR) << "Error parsing mp4 file '" << file_path << "'";
174  return false;
175  }
176  bytes_to_read -= bytes_read;
177  }
178  queue_.Reset(); // So that we don't need to adjust data offsets.
179  mdat_tail_ = 0; // So it will skip boxes until mdat.
180  break; // Done.
181  }
182  file_position += box_size;
183  if (!file->Seek(file_position)) {
184  LOG(ERROR) << "Error skipping box in mp4 file '" << file_path << "'";
185  return false;
186  }
187  }
188  return true;
189 }
190 
191 bool MP4MediaParser::ParseBox(bool* err) {
192  const uint8_t* buf;
193  int size;
194  queue_.Peek(&buf, &size);
195  if (!size)
196  return false;
197 
198  scoped_ptr<BoxReader> reader(BoxReader::ReadTopLevelBox(buf, size, err));
199  if (reader.get() == NULL)
200  return false;
201 
202  if (reader->type() == FOURCC_MDAT) {
203  // The code ends up here only if a MOOV box is not yet seen.
204  DCHECK(!moov_);
205 
206  NOTIMPLEMENTED() << " Files with MDAT before MOOV is not supported yet.";
207  *err = true;
208  return false;
209  }
210 
211  // Set up mdat offset for ReadMDATsUntil().
212  mdat_tail_ = queue_.head() + reader->size();
213 
214  if (reader->type() == FOURCC_MOOV) {
215  *err = !ParseMoov(reader.get());
216  } else if (reader->type() == FOURCC_MOOF) {
217  moof_head_ = queue_.head();
218  *err = !ParseMoof(reader.get());
219 
220  // Return early to avoid evicting 'moof' data from queue. Auxiliary info may
221  // be located anywhere in the file, including inside the 'moof' itself.
222  // (Since 'default-base-is-moof' is mandated, no data references can come
223  // before the head of the 'moof', so keeping this box around is sufficient.)
224  return !(*err);
225  } else {
226  VLOG(2) << "Skipping top-level box: " << FourCCToString(reader->type());
227  }
228 
229  queue_.Pop(reader->size());
230  return !(*err);
231 }
232 
233 bool MP4MediaParser::ParseMoov(BoxReader* reader) {
234  if (moov_)
235  return true; // Already parsed the 'moov' box.
236 
237  moov_.reset(new Movie);
238  RCHECK(moov_->Parse(reader));
239  runs_.reset();
240 
241  std::vector<scoped_refptr<StreamInfo> > streams;
242 
243  for (std::vector<Track>::const_iterator track = moov_->tracks.begin();
244  track != moov_->tracks.end(); ++track) {
245  const uint32_t timescale = track->media.header.timescale;
246 
247  // Calculate duration (based on timescale).
248  uint64_t duration = 0;
249  if (track->media.header.duration > 0) {
250  duration = track->media.header.duration;
251  } else if (moov_->extends.header.fragment_duration > 0) {
252  DCHECK(moov_->header.timescale != 0);
253  duration = Rescale(moov_->extends.header.fragment_duration,
254  moov_->header.timescale,
255  timescale);
256  } else if (moov_->header.duration > 0 &&
257  moov_->header.duration != std::numeric_limits<uint64_t>::max()) {
258  DCHECK(moov_->header.timescale != 0);
259  duration =
260  Rescale(moov_->header.duration, moov_->header.timescale, timescale);
261  }
262 
263  const SampleDescription& samp_descr =
264  track->media.information.sample_table.description;
265 
266  size_t desc_idx = 0;
267 
268  // Read sample description index from mvex if it exists otherwise read
269  // from the first entry in Sample To Chunk box.
270  if (moov_->extends.tracks.size() > 0) {
271  for (size_t t = 0; t < moov_->extends.tracks.size(); t++) {
272  const TrackExtends& trex = moov_->extends.tracks[t];
273  if (trex.track_id == track->header.track_id) {
274  desc_idx = trex.default_sample_description_index;
275  break;
276  }
277  }
278  } else {
279  const std::vector<ChunkInfo>& chunk_info =
280  track->media.information.sample_table.sample_to_chunk.chunk_info;
281  RCHECK(chunk_info.size() > 0);
282  desc_idx = chunk_info[0].sample_description_index;
283  }
284  RCHECK(desc_idx > 0);
285  desc_idx -= 1; // BMFF descriptor index is one-based
286 
287  if (track->media.handler.type == kAudio) {
288  RCHECK(!samp_descr.audio_entries.empty());
289 
290  // It is not uncommon to find otherwise-valid files with incorrect sample
291  // description indices, so we fail gracefully in that case.
292  if (desc_idx >= samp_descr.audio_entries.size())
293  desc_idx = 0;
294  const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx];
295 
296  if (!(entry.format == FOURCC_MP4A || entry.format == FOURCC_EAC3 ||
297  (entry.format == FOURCC_ENCA &&
298  entry.sinf.format.format == FOURCC_MP4A))) {
299  LOG(ERROR) << "Unsupported audio format 0x"
300  << std::hex << entry.format << " in stsd box.";
301  return false;
302  }
303 
304  ObjectType audio_type = entry.esds.es_descriptor.object_type();
305  DVLOG(1) << "audio_type " << std::hex << audio_type;
306  if (audio_type == kForbidden && entry.format == FOURCC_EAC3) {
307  audio_type = kEAC3;
308  }
309 
310  AudioCodec codec = kUnknownAudioCodec;
311  uint8_t num_channels = 0;
312  uint32_t sampling_frequency = 0;
313  uint8_t audio_object_type = 0;
314  std::vector<uint8_t> extra_data;
315  // Check if it is MPEG4 AAC defined in ISO 14496 Part 3 or
316  // supported MPEG2 AAC variants.
317  if (entry.esds.es_descriptor.IsAAC()) {
318  codec = kCodecAAC;
319  const AACAudioSpecificConfig& aac_audio_specific_config =
320  entry.esds.aac_audio_specific_config;
321  num_channels = aac_audio_specific_config.num_channels();
322  sampling_frequency = aac_audio_specific_config.frequency();
323  audio_object_type = aac_audio_specific_config.audio_object_type();
324  extra_data = entry.esds.es_descriptor.decoder_specific_info();
325  } else if (audio_type == kEAC3) {
326  codec = kCodecEAC3;
327  num_channels = entry.channelcount;
328  sampling_frequency = entry.samplerate;
329  } else {
330  LOG(ERROR) << "Unsupported audio object type 0x"
331  << std::hex << audio_type << " in esds.";
332  return false;
333  }
334 
335  bool is_encrypted = entry.sinf.info.track_encryption.is_encrypted;
336  DVLOG(1) << "is_audio_track_encrypted_: " << is_encrypted;
337  streams.push_back(new AudioStreamInfo(
338  track->header.track_id,
339  timescale,
340  duration,
341  codec,
342  AudioStreamInfo::GetCodecString(codec, audio_object_type),
343  track->media.header.language,
344  entry.samplesize,
345  num_channels,
346  sampling_frequency,
347  extra_data.size() ? &extra_data[0] : NULL,
348  extra_data.size(),
349  is_encrypted));
350  }
351 
352  if (track->media.handler.type == kVideo) {
353  RCHECK(!samp_descr.video_entries.empty());
354  if (desc_idx >= samp_descr.video_entries.size())
355  desc_idx = 0;
356  const VideoSampleEntry& entry = samp_descr.video_entries[desc_idx];
357 
358  uint32_t coded_width = entry.width;
359  uint32_t coded_height = entry.height;
360  uint32_t pixel_width = entry.pixel_aspect.h_spacing;
361  uint32_t pixel_height = entry.pixel_aspect.v_spacing;
362  if (pixel_width == 0 && pixel_height == 0) {
363  pixel_width = 1;
364  pixel_height = 1;
365  }
366  std::string codec_string;
367  uint8_t nalu_length_size = 0;
368 
369  const FourCC actual_format = entry.GetActualFormat();
370  switch (actual_format) {
371  case FOURCC_AVC1: {
372  AVCDecoderConfiguration avc_config;
373  if (!avc_config.Parse(entry.codec_config_record.data)) {
374  LOG(ERROR) << "Failed to parse avcc.";
375  return false;
376  }
377  codec_string = avc_config.GetCodecString();
378  nalu_length_size = avc_config.length_size();
379 
380  if (coded_width != avc_config.coded_width() ||
381  coded_height != avc_config.coded_height()) {
382  LOG(WARNING) << "Resolution in VisualSampleEntry (" << coded_width
383  << "," << coded_height
384  << ") does not match with resolution in "
385  "AVCDecoderConfigurationRecord ("
386  << avc_config.coded_width() << ","
387  << avc_config.coded_height()
388  << "). Use AVCDecoderConfigurationRecord.";
389  coded_width = avc_config.coded_width();
390  coded_height = avc_config.coded_height();
391  }
392 
393  if (pixel_width != avc_config.pixel_width() ||
394  pixel_height != avc_config.pixel_height()) {
395  LOG_IF(WARNING, pixel_width != 1 || pixel_height != 1)
396  << "Pixel aspect ratio in PASP box (" << pixel_width << ","
397  << pixel_height
398  << ") does not match with SAR in AVCDecoderConfigurationRecord "
399  "("
400  << avc_config.pixel_width() << "," << avc_config.pixel_height()
401  << "). Use AVCDecoderConfigurationRecord.";
402  pixel_width = avc_config.pixel_width();
403  pixel_height = avc_config.pixel_height();
404  }
405  break;
406  }
407  default:
408  LOG(ERROR) << "Unsupported video format "
409  << FourCCToString(actual_format) << " in stsd box.";
410  return false;
411  }
412 
413  bool is_encrypted = entry.sinf.info.track_encryption.is_encrypted;
414  DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted;
415  streams.push_back(new VideoStreamInfo(
416  track->header.track_id, timescale, duration, kCodecH264,
417  codec_string, track->media.header.language, coded_width, coded_height,
418  pixel_width, pixel_height,
419  0, // trick_play_rate
420  nalu_length_size, vector_as_array(&entry.codec_config_record.data),
421  entry.codec_config_record.data.size(), is_encrypted));
422  }
423  }
424 
425  init_cb_.Run(streams);
426  if (!FetchKeysIfNecessary(moov_->pssh))
427  return false;
428  runs_.reset(new TrackRunIterator(moov_.get()));
429  RCHECK(runs_->Init());
430  ChangeState(kEmittingSamples);
431  return true;
432 }
433 
434 bool MP4MediaParser::ParseMoof(BoxReader* reader) {
435  // Must already have initialization segment.
436  RCHECK(moov_.get());
437  MovieFragment moof;
438  RCHECK(moof.Parse(reader));
439  if (!runs_)
440  runs_.reset(new TrackRunIterator(moov_.get()));
441  RCHECK(runs_->Init(moof));
442  if (!FetchKeysIfNecessary(moof.pssh))
443  return false;
444  ChangeState(kEmittingSamples);
445  return true;
446 }
447 
448 bool MP4MediaParser::FetchKeysIfNecessary(
449  const std::vector<ProtectionSystemSpecificHeader>& headers) {
450  if (headers.empty())
451  return true;
452 
453  // An error will be returned later if the samples need to be decrypted.
454  if (!decryption_key_source_)
455  return true;
456 
457  // TODO(tinskip): Pass in raw 'pssh' boxes to FetchKeys. This will allow
458  // supporting multiple keysystems. Move this to KeySource.
459  std::vector<uint8_t> widevine_system_id;
460  base::HexStringToBytes(kWidevineKeySystemId, &widevine_system_id);
461  for (std::vector<ProtectionSystemSpecificHeader>::const_iterator iter =
462  headers.begin(); iter != headers.end(); ++iter) {
463  if (iter->system_id == widevine_system_id) {
464  Status status = decryption_key_source_->FetchKeys(iter->data);
465  if (!status.ok()) {
466  LOG(ERROR) << "Error fetching decryption keys: " << status;
467  return false;
468  }
469  return true;
470  }
471  }
472 
473  LOG(ERROR) << "No viable 'pssh' box found for content decryption.";
474  return false;
475 }
476 
477 bool MP4MediaParser::EnqueueSample(bool* err) {
478  if (!runs_->IsRunValid()) {
479  // Remain in kEnqueueingSamples state, discarding data, until the end of
480  // the current 'mdat' box has been appended to the queue.
481  if (!queue_.Trim(mdat_tail_))
482  return false;
483 
484  ChangeState(kParsingBoxes);
485  return true;
486  }
487 
488  if (!runs_->IsSampleValid()) {
489  runs_->AdvanceRun();
490  return true;
491  }
492 
493  DCHECK(!(*err));
494 
495  const uint8_t* buf;
496  int buf_size;
497  queue_.Peek(&buf, &buf_size);
498  if (!buf_size)
499  return false;
500 
501  // Skip this entire track if it is not audio nor video.
502  if (!runs_->is_audio() && !runs_->is_video())
503  runs_->AdvanceRun();
504 
505  // Attempt to cache the auxiliary information first. Aux info is usually
506  // placed in a contiguous block before the sample data, rather than being
507  // interleaved. If we didn't cache it, this would require that we retain the
508  // start of the segment buffer while reading samples. Aux info is typically
509  // quite small compared to sample data, so this pattern is useful on
510  // memory-constrained devices where the source buffer consumes a substantial
511  // portion of the total system memory.
512  if (runs_->AuxInfoNeedsToBeCached()) {
513  queue_.PeekAt(runs_->aux_info_offset() + moof_head_, &buf, &buf_size);
514  if (buf_size < runs_->aux_info_size())
515  return false;
516  *err = !runs_->CacheAuxInfo(buf, buf_size);
517  return !*err;
518  }
519 
520  int64_t sample_offset = runs_->sample_offset() + moof_head_;
521  queue_.PeekAt(sample_offset, &buf, &buf_size);
522  if (buf_size < runs_->sample_size()) {
523  if (sample_offset < queue_.head()) {
524  LOG(ERROR) << "Incorrect sample offset " << sample_offset
525  << " < " << queue_.head();
526  *err = true;
527  }
528  return false;
529  }
530 
531  scoped_refptr<MediaSample> stream_sample(MediaSample::CopyFrom(
532  buf, runs_->sample_size(), runs_->is_keyframe()));
533  if (runs_->is_encrypted()) {
534  scoped_ptr<DecryptConfig> decrypt_config = runs_->GetDecryptConfig();
535  if (!decrypt_config ||
536  !DecryptSampleBuffer(decrypt_config.get(),
537  stream_sample->writable_data(),
538  stream_sample->data_size())) {
539  *err = true;
540  LOG(ERROR) << "Cannot decrypt samples.";
541  return false;
542  }
543  }
544 
545  stream_sample->set_dts(runs_->dts());
546  stream_sample->set_pts(runs_->cts());
547  stream_sample->set_duration(runs_->duration());
548 
549  DVLOG(3) << "Pushing frame: "
550  << ", key=" << runs_->is_keyframe()
551  << ", dur=" << runs_->duration()
552  << ", dts=" << runs_->dts()
553  << ", cts=" << runs_->cts()
554  << ", size=" << runs_->sample_size();
555 
556  if (!new_sample_cb_.Run(runs_->track_id(), stream_sample)) {
557  *err = true;
558  LOG(ERROR) << "Failed to process the sample.";
559  return false;
560  }
561 
562  runs_->AdvanceSample();
563  return true;
564 }
565 
566 bool MP4MediaParser::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
567  uint8_t* buffer,
568  size_t buffer_size) {
569  DCHECK(decrypt_config);
570  DCHECK(buffer);
571 
572  if (!decryption_key_source_) {
573  LOG(ERROR) << "Encrypted media sample encountered, but decryption is not "
574  "enabled";
575  return false;
576  }
577 
578  // Get the encryptor object.
579  AesCtrEncryptor* encryptor;
580  DecryptorMap::iterator found = decryptor_map_.find(decrypt_config->key_id());
581  if (found == decryptor_map_.end()) {
582  // Create new AesCtrEncryptor
583  EncryptionKey key;
584  Status status(decryption_key_source_->GetKey(decrypt_config->key_id(),
585  &key));
586  if (!status.ok()) {
587  LOG(ERROR) << "Error retrieving decryption key: " << status;
588  return false;
589  }
590  scoped_ptr<AesCtrEncryptor> new_encryptor(new AesCtrEncryptor);
591  if (!new_encryptor->InitializeWithIv(key.key, decrypt_config->iv())) {
592  LOG(ERROR) << "Failed to initialize AesCtrEncryptor for decryption.";
593  return false;
594  }
595  encryptor = new_encryptor.release();
596  decryptor_map_[decrypt_config->key_id()] = encryptor;
597  } else {
598  encryptor = found->second;
599  }
600  if (!encryptor->SetIv(decrypt_config->iv())) {
601  LOG(ERROR) << "Invalid initialization vector.";
602  return false;
603  }
604 
605  if (decrypt_config->subsamples().empty()) {
606  // Sample not encrypted using subsample encryption. Decrypt whole.
607  if (!encryptor->Decrypt(buffer, buffer_size, buffer)) {
608  LOG(ERROR) << "Error during bulk sample decryption.";
609  return false;
610  }
611  return true;
612  }
613 
614  // Subsample decryption.
615  const std::vector<SubsampleEntry>& subsamples = decrypt_config->subsamples();
616  uint8_t* current_ptr = buffer;
617  const uint8_t* buffer_end = buffer + buffer_size;
618  current_ptr += decrypt_config->data_offset();
619  if (current_ptr > buffer_end) {
620  LOG(ERROR) << "Subsample data_offset too large.";
621  return false;
622  }
623  for (std::vector<SubsampleEntry>::const_iterator iter = subsamples.begin();
624  iter != subsamples.end();
625  ++iter) {
626  if ((current_ptr + iter->clear_bytes + iter->cipher_bytes) > buffer_end) {
627  LOG(ERROR) << "Subsamples overflow sample buffer.";
628  return false;
629  }
630  current_ptr += iter->clear_bytes;
631  if (!encryptor->Decrypt(current_ptr, iter->cipher_bytes, current_ptr)) {
632  LOG(ERROR) << "Error decrypting subsample buffer.";
633  return false;
634  }
635  current_ptr += iter->cipher_bytes;
636  }
637  return true;
638 }
639 
640 bool MP4MediaParser::ReadAndDiscardMDATsUntil(const int64_t offset) {
641  bool err = false;
642  while (mdat_tail_ < offset) {
643  const uint8_t* buf;
644  int size;
645  queue_.PeekAt(mdat_tail_, &buf, &size);
646 
647  FourCC type;
648  uint64_t box_sz;
649  if (!BoxReader::StartTopLevelBox(buf, size, &type, &box_sz, &err))
650  break;
651 
652  mdat_tail_ += box_sz;
653  }
654  queue_.Trim(std::min(mdat_tail_, offset));
655  return !err;
656 }
657 
658 void MP4MediaParser::ChangeState(State new_state) {
659  DVLOG(2) << "Changing state: " << new_state;
660  state_ = new_state;
661 }
662 
663 } // namespace mp4
664 } // namespace media
665 } // namespace edash_packager
static BoxReader * ReadTopLevelBox(const uint8_t *buf, const size_t buf_size, bool *err)
Definition: box_reader.cc:37
virtual Status GetKey(TrackType track_type, EncryptionKey *key)
Definition: key_source.cc:46
bool Parse(const uint8_t *buf, int size) override
static scoped_refptr< MediaSample > CopyFrom(const uint8_t *data, size_t size, bool is_key_frame)
Definition: media_sample.cc:47
void PeekAt(int64_t offset, const uint8_t **buf, int *size)
bool LoadMoov(const std::string &file_path)
virtual Status FetchKeys(const std::vector< uint8_t > &content_id, const std::string &policy)
Definition: key_source.cc:30
KeySource is responsible for encryption key acquisition.
Definition: key_source.h:29
static bool StartTopLevelBox(const uint8_t *buf, const size_t buf_size, FourCC *type, uint64_t *box_size, bool *err) WARN_UNUSED_RESULT
Definition: box_reader.cc:60
void Init(const InitCB &init_cb, const NewSampleCB &new_sample_cb, KeySource *decryption_key_source) override
static File * OpenWithNoBuffering(const char *file_name, const char *mode)
Definition: file.cc:127
static std::string GetCodecString(AudioCodec codec, uint8_t audio_object_type)