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/h264_parser.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  if (!(entry.format == FOURCC_AVC1 ||
359  (entry.format == FOURCC_ENCV &&
360  entry.sinf.format.format == FOURCC_AVC1))) {
361  LOG(ERROR) << "Unsupported video format 0x"
362  << std::hex << entry.format << " in stsd box.";
363  return false;
364  }
365 
366  const std::string codec_string =
368  entry.avcc.profile_indication,
369  entry.avcc.profile_compatibility,
370  entry.avcc.avc_level);
371 
372  uint32_t coded_width = 0;
373  uint32_t coded_height = 0;
374  uint32_t pixel_width = 0;
375  uint32_t pixel_height = 0;
376 
377  if (entry.avcc.sps_list.empty()) {
378  LOG(ERROR) << "Cannot find sps in avc decoder configuration record.";
379  return false;
380  }
381  const std::vector<uint8_t>& sps = entry.avcc.sps_list[0];
382  if (!ExtractResolutionFromSpsData(&sps[0], sps.size(), &coded_width,
383  &coded_height, &pixel_width,
384  &pixel_height)) {
385  LOG(ERROR) << "Failed to parse SPS.";
386  return false;
387  }
388 
389  LOG_IF(WARNING,
390  entry.width != coded_width || entry.height != coded_height)
391  << "Resolution in VisualSampleEntry (" << entry.width << ","
392  << entry.height << ") does not match with resolution in "
393  "AVCDecoderConfigurationRecord ("
394  << coded_width << "," << coded_height
395  << "). Use AVCDecoderConfigurationRecord.";
396 
397  if (entry.pixel_aspect.h_spacing != 0 || entry.pixel_aspect.v_spacing != 0) {
398  LOG_IF(WARNING, entry.pixel_aspect.h_spacing != pixel_width ||
399  entry.pixel_aspect.v_spacing != pixel_height)
400  << "Pixel aspect ratio in PASP box ("
401  << entry.pixel_aspect.h_spacing << ","
402  << entry.pixel_aspect.v_spacing
403  << ") does not match with SAR in AVCDecoderConfigurationRecord ("
404  << pixel_width << "," << pixel_height
405  << "). Use AVCDecoderConfigurationRecord.";
406  }
407 
408  bool is_encrypted = entry.sinf.info.track_encryption.is_encrypted;
409  DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted;
410  streams.push_back(new VideoStreamInfo(track->header.track_id,
411  timescale,
412  duration,
413  kCodecH264,
414  codec_string,
415  track->media.header.language,
416  coded_width,
417  coded_height,
418  pixel_width,
419  pixel_height,
420  0, // trick_play_rate
421  entry.avcc.length_size,
422  &entry.avcc.data[0],
423  entry.avcc.data.size(),
424  is_encrypted));
425  }
426  }
427 
428  init_cb_.Run(streams);
429  if (!FetchKeysIfNecessary(moov_->pssh))
430  return false;
431  runs_.reset(new TrackRunIterator(moov_.get()));
432  RCHECK(runs_->Init());
433  ChangeState(kEmittingSamples);
434  return true;
435 }
436 
437 bool MP4MediaParser::ParseMoof(BoxReader* reader) {
438  // Must already have initialization segment.
439  RCHECK(moov_.get());
440  MovieFragment moof;
441  RCHECK(moof.Parse(reader));
442  if (!runs_)
443  runs_.reset(new TrackRunIterator(moov_.get()));
444  RCHECK(runs_->Init(moof));
445  if (!FetchKeysIfNecessary(moof.pssh))
446  return false;
447  ChangeState(kEmittingSamples);
448  return true;
449 }
450 
451 bool MP4MediaParser::FetchKeysIfNecessary(
452  const std::vector<ProtectionSystemSpecificHeader>& headers) {
453  if (headers.empty())
454  return true;
455 
456  // An error will be returned later if the samples need to be decrypted.
457  if (!decryption_key_source_)
458  return true;
459 
460  // TODO(tinskip): Pass in raw 'pssh' boxes to FetchKeys. This will allow
461  // supporting multiple keysystems. Move this to KeySource.
462  std::vector<uint8_t> widevine_system_id;
463  base::HexStringToBytes(kWidevineKeySystemId, &widevine_system_id);
464  for (std::vector<ProtectionSystemSpecificHeader>::const_iterator iter =
465  headers.begin(); iter != headers.end(); ++iter) {
466  if (iter->system_id == widevine_system_id) {
467  Status status = decryption_key_source_->FetchKeys(iter->data);
468  if (!status.ok()) {
469  LOG(ERROR) << "Error fetching decryption keys: " << status;
470  return false;
471  }
472  return true;
473  }
474  }
475 
476  LOG(ERROR) << "No viable 'pssh' box found for content decryption.";
477  return false;
478 }
479 
480 bool MP4MediaParser::EnqueueSample(bool* err) {
481  if (!runs_->IsRunValid()) {
482  // Remain in kEnqueueingSamples state, discarding data, until the end of
483  // the current 'mdat' box has been appended to the queue.
484  if (!queue_.Trim(mdat_tail_))
485  return false;
486 
487  ChangeState(kParsingBoxes);
488  return true;
489  }
490 
491  if (!runs_->IsSampleValid()) {
492  runs_->AdvanceRun();
493  return true;
494  }
495 
496  DCHECK(!(*err));
497 
498  const uint8_t* buf;
499  int buf_size;
500  queue_.Peek(&buf, &buf_size);
501  if (!buf_size)
502  return false;
503 
504  // Skip this entire track if it is not audio nor video.
505  if (!runs_->is_audio() && !runs_->is_video())
506  runs_->AdvanceRun();
507 
508  // Attempt to cache the auxiliary information first. Aux info is usually
509  // placed in a contiguous block before the sample data, rather than being
510  // interleaved. If we didn't cache it, this would require that we retain the
511  // start of the segment buffer while reading samples. Aux info is typically
512  // quite small compared to sample data, so this pattern is useful on
513  // memory-constrained devices where the source buffer consumes a substantial
514  // portion of the total system memory.
515  if (runs_->AuxInfoNeedsToBeCached()) {
516  queue_.PeekAt(runs_->aux_info_offset() + moof_head_, &buf, &buf_size);
517  if (buf_size < runs_->aux_info_size())
518  return false;
519  *err = !runs_->CacheAuxInfo(buf, buf_size);
520  return !*err;
521  }
522 
523  int64_t sample_offset = runs_->sample_offset() + moof_head_;
524  queue_.PeekAt(sample_offset, &buf, &buf_size);
525  if (buf_size < runs_->sample_size()) {
526  if (sample_offset < queue_.head()) {
527  LOG(ERROR) << "Incorrect sample offset " << sample_offset
528  << " < " << queue_.head();
529  *err = true;
530  }
531  return false;
532  }
533 
534  scoped_refptr<MediaSample> stream_sample(MediaSample::CopyFrom(
535  buf, runs_->sample_size(), runs_->is_keyframe()));
536  if (runs_->is_encrypted()) {
537  scoped_ptr<DecryptConfig> decrypt_config = runs_->GetDecryptConfig();
538  if (!decrypt_config ||
539  !DecryptSampleBuffer(decrypt_config.get(),
540  stream_sample->writable_data(),
541  stream_sample->data_size())) {
542  *err = true;
543  LOG(ERROR) << "Cannot decrypt samples.";
544  return false;
545  }
546  }
547 
548  stream_sample->set_dts(runs_->dts());
549  stream_sample->set_pts(runs_->cts());
550  stream_sample->set_duration(runs_->duration());
551 
552  DVLOG(3) << "Pushing frame: "
553  << ", key=" << runs_->is_keyframe()
554  << ", dur=" << runs_->duration()
555  << ", dts=" << runs_->dts()
556  << ", cts=" << runs_->cts()
557  << ", size=" << runs_->sample_size();
558 
559  if (!new_sample_cb_.Run(runs_->track_id(), stream_sample)) {
560  *err = true;
561  LOG(ERROR) << "Failed to process the sample.";
562  return false;
563  }
564 
565  runs_->AdvanceSample();
566  return true;
567 }
568 
569 bool MP4MediaParser::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
570  uint8_t* buffer,
571  size_t buffer_size) {
572  DCHECK(decrypt_config);
573  DCHECK(buffer);
574 
575  if (!decryption_key_source_) {
576  LOG(ERROR) << "Encrypted media sample encountered, but decryption is not "
577  "enabled";
578  return false;
579  }
580 
581  // Get the encryptor object.
582  AesCtrEncryptor* encryptor;
583  DecryptorMap::iterator found = decryptor_map_.find(decrypt_config->key_id());
584  if (found == decryptor_map_.end()) {
585  // Create new AesCtrEncryptor
586  EncryptionKey key;
587  Status status(decryption_key_source_->GetKey(decrypt_config->key_id(),
588  &key));
589  if (!status.ok()) {
590  LOG(ERROR) << "Error retrieving decryption key: " << status;
591  return false;
592  }
593  scoped_ptr<AesCtrEncryptor> new_encryptor(new AesCtrEncryptor);
594  if (!new_encryptor->InitializeWithIv(key.key, decrypt_config->iv())) {
595  LOG(ERROR) << "Failed to initialize AesCtrEncryptor for decryption.";
596  return false;
597  }
598  encryptor = new_encryptor.release();
599  decryptor_map_[decrypt_config->key_id()] = encryptor;
600  } else {
601  encryptor = found->second;
602  }
603  if (!encryptor->SetIv(decrypt_config->iv())) {
604  LOG(ERROR) << "Invalid initialization vector.";
605  return false;
606  }
607 
608  if (decrypt_config->subsamples().empty()) {
609  // Sample not encrypted using subsample encryption. Decrypt whole.
610  if (!encryptor->Decrypt(buffer, buffer_size, buffer)) {
611  LOG(ERROR) << "Error during bulk sample decryption.";
612  return false;
613  }
614  return true;
615  }
616 
617  // Subsample decryption.
618  const std::vector<SubsampleEntry>& subsamples = decrypt_config->subsamples();
619  uint8_t* current_ptr = buffer;
620  const uint8_t* buffer_end = buffer + buffer_size;
621  current_ptr += decrypt_config->data_offset();
622  if (current_ptr > buffer_end) {
623  LOG(ERROR) << "Subsample data_offset too large.";
624  return false;
625  }
626  for (std::vector<SubsampleEntry>::const_iterator iter = subsamples.begin();
627  iter != subsamples.end();
628  ++iter) {
629  if ((current_ptr + iter->clear_bytes + iter->cipher_bytes) > buffer_end) {
630  LOG(ERROR) << "Subsamples overflow sample buffer.";
631  return false;
632  }
633  current_ptr += iter->clear_bytes;
634  if (!encryptor->Decrypt(current_ptr, iter->cipher_bytes, current_ptr)) {
635  LOG(ERROR) << "Error decrypting subsample buffer.";
636  return false;
637  }
638  current_ptr += iter->cipher_bytes;
639  }
640  return true;
641 }
642 
643 bool MP4MediaParser::ReadAndDiscardMDATsUntil(const int64_t offset) {
644  bool err = false;
645  while (mdat_tail_ < offset) {
646  const uint8_t* buf;
647  int size;
648  queue_.PeekAt(mdat_tail_, &buf, &size);
649 
650  FourCC type;
651  uint64_t box_sz;
652  if (!BoxReader::StartTopLevelBox(buf, size, &type, &box_sz, &err))
653  break;
654 
655  mdat_tail_ += box_sz;
656  }
657  queue_.Trim(std::min(mdat_tail_, offset));
658  return !err;
659 }
660 
661 void MP4MediaParser::ChangeState(State new_state) {
662  DVLOG(2) << "Changing state: " << new_state;
663  state_ = new_state;
664 }
665 
666 } // namespace mp4
667 } // namespace media
668 } // 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 std::string GetCodecString(VideoCodec codec, uint8_t profile, uint8_t compatible_profiles, uint8_t level)
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)