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