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