Shaka Packager SDK
track_run_iterator.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/track_run_iterator.h>
6 
7 #include <algorithm>
8 #include <limits>
9 
10 #include <absl/flags/flag.h>
11 #include <absl/log/check.h>
12 
13 #include <packager/macros/logging.h>
14 #include <packager/media/base/buffer_reader.h>
15 #include <packager/media/base/fourccs.h>
16 #include <packager/media/base/rcheck.h>
17 #include <packager/media/formats/mp4/chunk_info_iterator.h>
18 #include <packager/media/formats/mp4/composition_offset_iterator.h>
19 #include <packager/media/formats/mp4/decoding_time_iterator.h>
20 #include <packager/media/formats/mp4/sync_sample_iterator.h>
21 
22 ABSL_FLAG(bool,
23  mp4_reset_initial_composition_offset_to_zero,
24  true,
25  "MP4 only. If it is true, reset the initial composition offset to "
26  "zero, i.e. by assuming that there is a missing EditList.");
27 
28 namespace {
29 const int64_t kInvalidOffset = std::numeric_limits<int64_t>::max();
30 
31 int64_t Rescale(int64_t time_in_old_scale,
32  int32_t old_scale,
33  int32_t new_scale) {
34  return (static_cast<double>(time_in_old_scale) / old_scale) * new_scale;
35 }
36 
37 } // namespace
38 
39 namespace shaka {
40 namespace media {
41 namespace mp4 {
42 
43 struct SampleInfo {
44  int64_t size;
45  int64_t duration;
46  int64_t cts_offset;
47  bool is_keyframe;
48 };
49 
50 struct TrackRunInfo {
51  uint32_t track_id;
52  std::vector<SampleInfo> samples;
53  int64_t timescale;
54  int64_t start_dts;
55  int64_t sample_start_offset;
56 
57  TrackType track_type;
58  const AudioSampleEntry* audio_description;
59  const VideoSampleEntry* video_description;
60 
61  // Stores sample encryption entries, which is populated from 'senc' box if it
62  // is available, otherwise will try to load from cenc auxiliary information.
63  std::vector<SampleEncryptionEntry> sample_encryption_entries;
64 
65  // These variables are useful to load |sample_encryption_entries| from cenc
66  // auxiliary information when 'senc' box is not available.
67  int64_t aux_info_start_offset; // Only valid if aux_info_total_size > 0.
68  int aux_info_default_size;
69  std::vector<uint8_t> aux_info_sizes; // Populated if default_size == 0.
70  int aux_info_total_size;
71 
72  TrackRunInfo();
73  ~TrackRunInfo();
74 };
75 
76 TrackRunInfo::TrackRunInfo()
77  : track_id(0),
78  timescale(-1),
79  start_dts(-1),
80  sample_start_offset(-1),
81  track_type(kInvalid),
82  audio_description(NULL),
83  video_description(NULL),
84  aux_info_start_offset(-1),
85  aux_info_default_size(0),
86  aux_info_total_size(0) {}
87 TrackRunInfo::~TrackRunInfo() {}
88 
89 TrackRunIterator::TrackRunIterator(const Movie* moov)
90  : moov_(moov), sample_dts_(0), sample_offset_(0) {
91  CHECK(moov);
92 }
93 
94 TrackRunIterator::~TrackRunIterator() {}
95 
96 static void PopulateSampleInfo(const TrackExtends& trex,
97  const TrackFragmentHeader& tfhd,
98  const TrackFragmentRun& trun,
99  const size_t i,
100  SampleInfo* sample_info) {
101  if (i < trun.sample_sizes.size()) {
102  sample_info->size = trun.sample_sizes[i];
103  } else if (tfhd.default_sample_size > 0) {
104  sample_info->size = tfhd.default_sample_size;
105  } else {
106  sample_info->size = trex.default_sample_size;
107  }
108 
109  if (i < trun.sample_durations.size()) {
110  sample_info->duration = trun.sample_durations[i];
111  } else if (tfhd.default_sample_duration > 0) {
112  sample_info->duration = tfhd.default_sample_duration;
113  } else {
114  sample_info->duration = trex.default_sample_duration;
115  }
116 
117  if (i < trun.sample_composition_time_offsets.size()) {
118  sample_info->cts_offset = trun.sample_composition_time_offsets[i];
119  } else {
120  sample_info->cts_offset = 0;
121  }
122 
123  uint32_t flags;
124  if (i < trun.sample_flags.size()) {
125  flags = trun.sample_flags[i];
126  } else if (tfhd.flags & TrackFragmentHeader::kDefaultSampleFlagsPresentMask) {
127  flags = tfhd.default_sample_flags;
128  } else {
129  flags = trex.default_sample_flags;
130  }
131  sample_info->is_keyframe = !(flags & TrackFragmentHeader::kNonKeySampleMask);
132 }
133 
134 // In well-structured encrypted media, each track run will be immediately
135 // preceded by its auxiliary information; this is the only optimal storage
136 // pattern in terms of minimum number of bytes from a serial stream needed to
137 // begin playback. It also allows us to optimize caching on memory-constrained
138 // architectures, because we can cache the relatively small auxiliary
139 // information for an entire run and then discard data from the input stream,
140 // instead of retaining the entire 'mdat' box.
141 //
142 // We optimize for this situation (with no loss of generality) by sorting track
143 // runs during iteration in order of their first data offset (either sample data
144 // or auxiliary data).
145 class CompareMinTrackRunDataOffset {
146  public:
147  bool operator()(const TrackRunInfo& a, const TrackRunInfo& b) {
148  int64_t a_aux = a.aux_info_total_size ? a.aux_info_start_offset : kInvalidOffset;
149  int64_t b_aux = b.aux_info_total_size ? b.aux_info_start_offset : kInvalidOffset;
150 
151  int64_t a_lesser = std::min(a_aux, a.sample_start_offset);
152  int64_t a_greater = std::max(a_aux, a.sample_start_offset);
153  int64_t b_lesser = std::min(b_aux, b.sample_start_offset);
154  int64_t b_greater = std::max(b_aux, b.sample_start_offset);
155 
156  if (a_lesser == b_lesser)
157  return a_greater < b_greater;
158  return a_lesser < b_lesser;
159  }
160 };
161 
163  runs_.clear();
164 
165  for (std::vector<Track>::const_iterator trak = moov_->tracks.begin();
166  trak != moov_->tracks.end(); ++trak) {
167  const SampleDescription& stsd =
168  trak->media.information.sample_table.description;
169  if (stsd.type != kAudio && stsd.type != kVideo) {
170  DVLOG(1) << "Skipping unhandled track type";
171  continue;
172  }
173 
174  DecodingTimeIterator decoding_time(
175  trak->media.information.sample_table.decoding_time_to_sample);
176  CompositionOffsetIterator composition_offset(
177  trak->media.information.sample_table.composition_time_to_sample);
178  bool has_composition_offset = composition_offset.IsValid();
179  ChunkInfoIterator chunk_info(
180  trak->media.information.sample_table.sample_to_chunk);
181  SyncSampleIterator sync_sample(
182  trak->media.information.sample_table.sync_sample);
183  // Skip processing saiz and saio boxes for non-fragmented mp4 as we
184  // don't support encrypted non-fragmented mp4.
185 
186  const SampleSize& sample_size =
187  trak->media.information.sample_table.sample_size;
188  const std::vector<uint64_t>& chunk_offset_vector =
189  trak->media.information.sample_table.chunk_large_offset.offsets;
190 
191  // dts is directly adjusted, which then propagates to pts as pts is encoded
192  // as difference (composition offset) to dts in mp4.
193  int64_t run_start_dts = GetTimestampAdjustment(*moov_, *trak, nullptr);
194 
195  uint32_t num_samples = sample_size.sample_count;
196  uint32_t num_chunks = static_cast<uint32_t>(chunk_offset_vector.size());
197 
198  // Check that total number of samples match.
199  DCHECK_EQ(num_samples, decoding_time.NumSamples());
200  if (has_composition_offset) {
201  DCHECK_EQ(num_samples, composition_offset.NumSamples());
202  }
203  if (num_chunks > 0) {
204  DCHECK_EQ(num_samples, chunk_info.NumSamples(1, num_chunks));
205  }
206  DCHECK_GE(num_chunks, chunk_info.LastFirstChunk());
207 
208  if (num_samples > 0) {
209  // Verify relevant tables are not empty.
210  RCHECK(decoding_time.IsValid());
211  RCHECK(chunk_info.IsValid());
212  }
213 
214  uint32_t sample_index = 0;
215  for (uint32_t chunk_index = 0; chunk_index < num_chunks; ++chunk_index) {
216  RCHECK(chunk_info.current_chunk() == chunk_index + 1);
217 
218  TrackRunInfo tri;
219  tri.track_id = trak->header.track_id;
220  tri.timescale = trak->media.header.timescale;
221  tri.start_dts = run_start_dts;
222  tri.sample_start_offset = chunk_offset_vector[chunk_index];
223 
224  uint32_t desc_idx = chunk_info.sample_description_index();
225  RCHECK(desc_idx > 0); // Descriptions are one-indexed in the file.
226  desc_idx -= 1;
227 
228  tri.track_type = stsd.type;
229  if (tri.track_type == kAudio) {
230  RCHECK(!stsd.audio_entries.empty());
231  if (desc_idx > stsd.audio_entries.size())
232  desc_idx = 0;
233  tri.audio_description = &stsd.audio_entries[desc_idx];
234  // We don't support encrypted non-fragmented mp4 for now.
235  RCHECK(tri.audio_description->sinf.info.track_encryption
236  .default_is_protected == 0);
237  } else if (tri.track_type == kVideo) {
238  RCHECK(!stsd.video_entries.empty());
239  if (desc_idx > stsd.video_entries.size())
240  desc_idx = 0;
241  tri.video_description = &stsd.video_entries[desc_idx];
242  // We don't support encrypted non-fragmented mp4 for now.
243  RCHECK(tri.video_description->sinf.info.track_encryption
244  .default_is_protected == 0);
245  }
246 
247  uint32_t samples_per_chunk = chunk_info.samples_per_chunk();
248  tri.samples.resize(samples_per_chunk);
249  for (uint32_t k = 0; k < samples_per_chunk; ++k) {
250  SampleInfo& sample = tri.samples[k];
251  sample.size = sample_size.sample_size != 0
252  ? sample_size.sample_size
253  : sample_size.sizes[sample_index];
254  sample.duration = decoding_time.sample_delta();
255  sample.cts_offset =
256  has_composition_offset ? composition_offset.sample_offset() : 0;
257  sample.is_keyframe = sync_sample.IsSyncSample();
258 
259  run_start_dts += sample.duration;
260 
261  // Advance to next sample. Should success except for last sample.
262  ++sample_index;
263  RCHECK(chunk_info.AdvanceSample() && sync_sample.AdvanceSample());
264  if (sample_index == num_samples) {
265  // We should hit end of tables for decoding time and composition
266  // offset.
267  RCHECK(!decoding_time.AdvanceSample());
268  if (has_composition_offset)
269  RCHECK(!composition_offset.AdvanceSample());
270  } else {
271  RCHECK(decoding_time.AdvanceSample());
272  if (has_composition_offset)
273  RCHECK(composition_offset.AdvanceSample());
274  }
275  }
276 
277  runs_.push_back(tri);
278  }
279  }
280 
281  std::sort(runs_.begin(), runs_.end(), CompareMinTrackRunDataOffset());
282  run_itr_ = runs_.begin();
283  ResetRun();
284  return true;
285 }
286 
288  runs_.clear();
289 
290  const auto track_count = std::max(moof.tracks.size(), moov_->tracks.size());
291  next_fragment_start_dts_.resize(track_count, 0);
292  for (size_t i = 0; i < moof.tracks.size(); i++) {
293  const TrackFragment& traf = moof.tracks[i];
294  const auto track_index = traf.header.track_id - 1;
295  const Track* trak = NULL;
296  for (size_t t = 0; t < moov_->tracks.size(); t++) {
297  if (moov_->tracks[t].header.track_id == traf.header.track_id)
298  trak = &moov_->tracks[t];
299  }
300  RCHECK(trak);
301 
302  const TrackExtends* trex = NULL;
303  for (size_t t = 0; t < moov_->extends.tracks.size(); t++) {
304  if (moov_->extends.tracks[t].track_id == traf.header.track_id)
305  trex = &moov_->extends.tracks[t];
306  }
307  RCHECK(trex);
308 
309  const SampleDescription& stsd =
310  trak->media.information.sample_table.description;
311  if (stsd.type != kAudio && stsd.type != kVideo) {
312  DVLOG(1) << "Skipping unhandled track type";
313  continue;
314  }
315  size_t desc_idx = traf.header.sample_description_index;
316  if (!desc_idx)
317  desc_idx = trex->default_sample_description_index;
318  RCHECK(desc_idx > 0); // Descriptions are one-indexed in the file
319  desc_idx -= 1;
320 
321  const AudioSampleEntry* audio_sample_entry = NULL;
322  const VideoSampleEntry* video_sample_entry = NULL;
323  switch (stsd.type) {
324  case kAudio:
325  RCHECK(!stsd.audio_entries.empty());
326  if (desc_idx > stsd.audio_entries.size())
327  desc_idx = 0;
328  audio_sample_entry = &stsd.audio_entries[desc_idx];
329  break;
330  case kVideo:
331  RCHECK(!stsd.video_entries.empty());
332  if (desc_idx > stsd.video_entries.size())
333  desc_idx = 0;
334  video_sample_entry = &stsd.video_entries[desc_idx];
335  break;
336  default:
337  NOTIMPLEMENTED();
338  break;
339  }
340 
341  // SampleEncryptionEntries should not have been parsed, without having
342  // iv_size. Parse the box now.
343  DCHECK(traf.sample_encryption.sample_encryption_entries.empty());
344  std::vector<SampleEncryptionEntry> sample_encryption_entries;
345  if (!traf.sample_encryption.sample_encryption_data.empty()) {
346  RCHECK(audio_sample_entry || video_sample_entry);
347  const uint8_t default_per_sample_iv_size =
348  audio_sample_entry
349  ? audio_sample_entry->sinf.info.track_encryption
350  .default_per_sample_iv_size
351  : video_sample_entry->sinf.info.track_encryption
352  .default_per_sample_iv_size;
353  RCHECK(traf.sample_encryption.ParseFromSampleEncryptionData(
354  default_per_sample_iv_size, &sample_encryption_entries));
355  }
356 
357  int64_t run_start_dts = traf.decode_time_absent
358  ? next_fragment_start_dts_[track_index]
359  : traf.decode_time.decode_time;
360 
361  // dts is directly adjusted, which then propagates to pts as pts is encoded
362  // as difference (composition offset) to dts in mp4.
363  run_start_dts += GetTimestampAdjustment(*moov_, *trak, &traf);
364 
365  int sample_count_sum = 0;
366 
367  for (size_t j = 0; j < traf.runs.size(); j++) {
368  const TrackFragmentRun& trun = traf.runs[j];
369  TrackRunInfo tri;
370  tri.track_id = traf.header.track_id;
371  tri.timescale = trak->media.header.timescale;
372  tri.start_dts = run_start_dts;
373  tri.sample_start_offset = trun.data_offset;
374 
375  tri.track_type = stsd.type;
376  tri.audio_description = audio_sample_entry;
377  tri.video_description = video_sample_entry;
378 
379  tri.aux_info_start_offset = -1;
380  tri.aux_info_total_size = 0;
381  // Populate sample encryption entries from SampleEncryption 'senc' box if
382  // it is available; otherwise initialize aux_info variables, which will
383  // be used to populate sample encryption entries later in CacheAuxInfo.
384  if (!sample_encryption_entries.empty()) {
385  RCHECK(sample_encryption_entries.size() >=
386  sample_count_sum + trun.sample_count);
387  for (size_t k = 0; k < trun.sample_count; ++k) {
388  tri.sample_encryption_entries.push_back(
389  sample_encryption_entries[sample_count_sum + k]);
390  }
391  } else if (traf.auxiliary_offset.offsets.size() > j) {
392  // Collect information from the auxiliary_offset entry with the same
393  // index in the 'saiz' container as the current run's index in the
394  // 'trun' container, if it is present.
395  tri.aux_info_start_offset = traf.auxiliary_offset.offsets[j];
396  // There should be an auxiliary info entry corresponding to each sample
397  // in the auxiliary offset entry's corresponding track run.
398  RCHECK(traf.auxiliary_size.sample_count >=
399  sample_count_sum + trun.sample_count);
400  tri.aux_info_default_size =
401  traf.auxiliary_size.default_sample_info_size;
402  if (tri.aux_info_default_size == 0) {
403  const std::vector<uint8_t>& sizes =
404  traf.auxiliary_size.sample_info_sizes;
405  tri.aux_info_sizes.insert(
406  tri.aux_info_sizes.begin(),
407  sizes.begin() + sample_count_sum,
408  sizes.begin() + sample_count_sum + trun.sample_count);
409  }
410 
411  // If the default info size is positive, find the total size of the aux
412  // info block from it, otherwise sum over the individual sizes of each
413  // aux info entry in the aux_offset entry.
414  if (tri.aux_info_default_size) {
415  tri.aux_info_total_size =
416  tri.aux_info_default_size * trun.sample_count;
417  } else {
418  tri.aux_info_total_size = 0;
419  for (size_t k = 0; k < trun.sample_count; k++) {
420  tri.aux_info_total_size += tri.aux_info_sizes[k];
421  }
422  }
423  }
424 
425  tri.samples.resize(trun.sample_count);
426  for (size_t k = 0; k < trun.sample_count; k++) {
427  PopulateSampleInfo(*trex, traf.header, trun, k, &tri.samples[k]);
428  run_start_dts += tri.samples[k].duration;
429  }
430  runs_.push_back(tri);
431  sample_count_sum += trun.sample_count;
432  }
433  next_fragment_start_dts_[track_index] = run_start_dts;
434  }
435 
436  std::sort(runs_.begin(), runs_.end(), CompareMinTrackRunDataOffset());
437  run_itr_ = runs_.begin();
438  ResetRun();
439  return true;
440 }
441 
443  ++run_itr_;
444  ResetRun();
445 }
446 
447 void TrackRunIterator::ResetRun() {
448  if (!IsRunValid())
449  return;
450  sample_dts_ = run_itr_->start_dts;
451  sample_offset_ = run_itr_->sample_start_offset;
452  sample_itr_ = run_itr_->samples.begin();
453 }
454 
456  DCHECK(IsSampleValid());
457  sample_dts_ += sample_itr_->duration;
458  sample_offset_ += sample_itr_->size;
459  ++sample_itr_;
460 }
461 
462 // This implementation only indicates a need for caching if CENC auxiliary
463 // info is available in the stream.
465  DCHECK(IsRunValid());
466  return is_encrypted() && aux_info_size() > 0 &&
467  run_itr_->sample_encryption_entries.size() == 0;
468 }
469 
470 // This implementation currently only caches CENC auxiliary info.
471 bool TrackRunIterator::CacheAuxInfo(const uint8_t* buf, int buf_size) {
472  RCHECK(AuxInfoNeedsToBeCached() && buf_size >= aux_info_size());
473 
474  std::vector<SampleEncryptionEntry>& sample_encryption_entries =
475  runs_[run_itr_ - runs_.begin()].sample_encryption_entries;
476  sample_encryption_entries.resize(run_itr_->samples.size());
477  int64_t pos = 0;
478  for (size_t i = 0; i < run_itr_->samples.size(); i++) {
479  int info_size = run_itr_->aux_info_default_size;
480  if (!info_size)
481  info_size = run_itr_->aux_info_sizes[i];
482 
483  BufferReader reader(buf + pos, info_size);
484  const bool has_subsamples =
485  info_size > track_encryption().default_per_sample_iv_size;
486  RCHECK(sample_encryption_entries[i].ParseFromBuffer(
487  track_encryption().default_per_sample_iv_size, has_subsamples,
488  &reader));
489  pos += info_size;
490  }
491 
492  return true;
493 }
494 
495 bool TrackRunIterator::IsRunValid() const { return run_itr_ != runs_.end(); }
496 
498  return IsRunValid() && (sample_itr_ != run_itr_->samples.end());
499 }
500 
501 // Because tracks are in sorted order and auxiliary information is cached when
502 // returning samples, it is guaranteed that no data will be required before the
503 // lesser of the minimum data offset of this track and the next in sequence.
504 // (The stronger condition - that no data is required before the minimum data
505 // offset of this track alone - is not guaranteed, because the BMFF spec does
506 // not have any inter-run ordering restrictions.)
508  int64_t offset = kInvalidOffset;
509 
510  if (IsSampleValid()) {
511  offset = std::min(offset, sample_offset_);
513  offset = std::min(offset, aux_info_offset());
514  }
515  if (run_itr_ != runs_.end()) {
516  std::vector<TrackRunInfo>::const_iterator next_run = run_itr_ + 1;
517  if (next_run != runs_.end()) {
518  offset = std::min(offset, next_run->sample_start_offset);
519  if (next_run->aux_info_total_size)
520  offset = std::min(offset, next_run->aux_info_start_offset);
521  }
522  }
523  if (offset == kInvalidOffset)
524  return runs_.empty() ? 0 : runs_[0].sample_start_offset;
525  return offset;
526 }
527 
528 uint32_t TrackRunIterator::track_id() const {
529  DCHECK(IsRunValid());
530  return run_itr_->track_id;
531 }
532 
533 bool TrackRunIterator::is_encrypted() const {
534  DCHECK(IsRunValid());
535  return track_encryption().default_is_protected == 1;
536 }
537 
538 int64_t TrackRunIterator::aux_info_offset() const {
539  return run_itr_->aux_info_start_offset;
540 }
541 
542 int TrackRunIterator::aux_info_size() const {
543  return run_itr_->aux_info_total_size;
544 }
545 
546 bool TrackRunIterator::is_audio() const {
547  DCHECK(IsRunValid());
548  return run_itr_->track_type == kAudio;
549 }
550 
551 bool TrackRunIterator::is_video() const {
552  DCHECK(IsRunValid());
553  return run_itr_->track_type == kVideo;
554 }
555 
557  DCHECK(is_audio());
558  DCHECK(run_itr_->audio_description);
559  return *run_itr_->audio_description;
560 }
561 
563  DCHECK(is_video());
564  DCHECK(run_itr_->video_description);
565  return *run_itr_->video_description;
566 }
567 
568 int64_t TrackRunIterator::sample_offset() const {
569  DCHECK(IsSampleValid());
570  return sample_offset_;
571 }
572 
573 int TrackRunIterator::sample_size() const {
574  DCHECK(IsSampleValid());
575  return sample_itr_->size;
576 }
577 
578 int64_t TrackRunIterator::dts() const {
579  DCHECK(IsSampleValid());
580  return sample_dts_;
581 }
582 
583 int64_t TrackRunIterator::cts() const {
584  DCHECK(IsSampleValid());
585  return sample_dts_ + sample_itr_->cts_offset;
586 }
587 
588 int64_t TrackRunIterator::duration() const {
589  DCHECK(IsSampleValid());
590  return sample_itr_->duration;
591 }
592 
593 bool TrackRunIterator::is_keyframe() const {
594  DCHECK(IsSampleValid());
595  return sample_itr_->is_keyframe;
596 }
597 
598 const TrackEncryption& TrackRunIterator::track_encryption() const {
599  if (is_audio())
600  return audio_description().sinf.info.track_encryption;
601  DCHECK(is_video());
602  return video_description().sinf.info.track_encryption;
603 }
604 
605 std::unique_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
606  std::vector<uint8_t> iv;
607  std::vector<SubsampleEntry> subsamples;
608 
609  size_t sample_idx = sample_itr_ - run_itr_->samples.begin();
610  if (sample_idx < run_itr_->sample_encryption_entries.size()) {
611  const SampleEncryptionEntry& sample_encryption_entry =
612  run_itr_->sample_encryption_entries[sample_idx];
613  DCHECK(is_encrypted());
614  DCHECK(!AuxInfoNeedsToBeCached());
615 
616  const size_t total_size_of_subsamples =
617  sample_encryption_entry.GetTotalSizeOfSubsamples();
618  if (total_size_of_subsamples != 0 &&
619  total_size_of_subsamples != static_cast<size_t>(sample_size())) {
620  LOG(ERROR) << "Incorrect CENC subsample size.";
621  return std::unique_ptr<DecryptConfig>();
622  }
623 
624  iv = sample_encryption_entry.initialization_vector;
625  subsamples = sample_encryption_entry.subsamples;
626  }
627 
628  FourCC protection_scheme = is_audio() ? audio_description().sinf.type.type
629  : video_description().sinf.type.type;
630  if (iv.empty()) {
631  if (protection_scheme != FOURCC_cbcs) {
632  LOG(WARNING)
633  << "Constant IV should only be used with 'cbcs' protection scheme.";
634  }
635  iv = track_encryption().default_constant_iv;
636  if (iv.empty()) {
637  LOG(ERROR) << "IV cannot be empty.";
638  return std::unique_ptr<DecryptConfig>();
639  }
640  }
641  return std::unique_ptr<DecryptConfig>(new DecryptConfig(
642  track_encryption().default_kid, iv, subsamples, protection_scheme,
643  track_encryption().default_crypt_byte_block,
644  track_encryption().default_skip_byte_block));
645 }
646 
647 int64_t TrackRunIterator::GetTimestampAdjustment(const Movie& movie,
648  const Track& track,
649  const TrackFragment* traf) {
650  const uint32_t track_id = track.header.track_id;
651  const auto iter = timestamp_adjustment_map_.find(track_id);
652  if (iter != timestamp_adjustment_map_.end())
653  return iter->second;
654 
655  int64_t timestamp_adjustment = 0;
656  const std::vector<EditListEntry>& edits = track.edit.list.edits;
657  if (!edits.empty()) {
658  // ISO/IEC 14496-12:2015 8.6.6 Edit List Box.
659  for (const EditListEntry& edit : edits) {
660  if (edit.media_rate_integer != 1) {
661  LOG(INFO) << "dwell EditListEntry is ignored.";
662  continue;
663  }
664 
665  if (edit.media_time < 0) {
666  // This is an empty edit. |segment_duration| is in movie's timescale
667  // instead of track's timescale.
668  const int64_t scaled_time =
669  Rescale(edit.segment_duration, movie.header.timescale,
670  track.media.header.timescale);
671  timestamp_adjustment += scaled_time;
672  } else {
673  timestamp_adjustment -= edit.media_time;
674  }
675  }
676  }
677 
678  if (timestamp_adjustment == 0) {
679  int64_t composition_offset = 0;
680  if (traf && !traf->runs.empty()) {
681  const auto& cts_offsets =
682  traf->runs.front().sample_composition_time_offsets;
683  if (!cts_offsets.empty())
684  composition_offset = cts_offsets.front();
685  } else {
686  CompositionOffsetIterator composition_offset_iter(
687  track.media.information.sample_table.composition_time_to_sample);
688  if (!composition_offset_iter.IsValid()) {
689  // This is the init (sub)segment of a fragmented mp4, which does not
690  // contain any samples. Exit with 0 adjustment and without storing
691  // |timestamp_adjustment|. This function will be called again later
692  // with track fragment |traf|. |timestamp_adjustment| will be computed
693  // and stored then.
694  return 0;
695  }
696  composition_offset = composition_offset_iter.sample_offset();
697  }
698 
699  int64_t decode_time = 0;
700  if (traf)
701  decode_time = traf->decode_time.decode_time;
702  if (composition_offset != 0 && decode_time == 0) {
703  LOG(WARNING) << "Seeing non-zero composition offset "
704  << composition_offset
705  << ". An EditList is probably missing.";
706  if (absl::GetFlag(FLAGS_mp4_reset_initial_composition_offset_to_zero)) {
707  LOG(WARNING)
708  << "Adjusting timestamps by " << -composition_offset
709  << ". Please file a bug to "
710  "https://github.com/shaka-project/shaka-packager/issues if you "
711  "do not think it is right or if you are seeing any problems.";
712  timestamp_adjustment = -composition_offset;
713  }
714  }
715  }
716 
717  timestamp_adjustment_map_.insert(
718  std::make_pair(track_id, timestamp_adjustment));
719  return timestamp_adjustment;
720 }
721 
722 } // namespace mp4
723 } // namespace media
724 } // namespace shaka
uint32_t NumSamples(uint32_t start_chunk, uint32_t end_chunk) const
const VideoSampleEntry & video_description() const
Only valid if is_video() is true.
const AudioSampleEntry & audio_description() const
Only valid if is_audio() is true.
bool CacheAuxInfo(const uint8_t *buf, int size)
std::unique_ptr< DecryptConfig > GetDecryptConfig()
All the methods that are virtual are virtual for mocking.
Definition: crypto_flags.cc:66
std::vector< uint8_t > sample_encryption_data
bool ParseFromSampleEncryptionData(uint8_t l_iv_size, std::vector< SampleEncryptionEntry > *l_sample_encryption_entries) const