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