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 <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
22ABSL_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
28namespace {
29const int64_t kInvalidOffset = std::numeric_limits<int64_t>::max();
30
31int64_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
39namespace shaka {
40namespace media {
41namespace mp4 {
42
43struct SampleInfo {
44 int64_t size;
45 int64_t duration;
46 int64_t cts_offset;
47 bool is_keyframe;
48};
49
50struct 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
76TrackRunInfo::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) {}
87TrackRunInfo::~TrackRunInfo() {}
88
89TrackRunIterator::TrackRunIterator(const Movie* moov)
90 : moov_(moov), sample_dts_(0), sample_offset_(0) {
91 CHECK(moov);
92}
93
94TrackRunIterator::~TrackRunIterator() {}
95
96static 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).
145class 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
447void 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.
471bool 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
495bool 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
528uint32_t TrackRunIterator::track_id() const {
529 DCHECK(IsRunValid());
530 return run_itr_->track_id;
531}
532
533bool TrackRunIterator::is_encrypted() const {
534 DCHECK(IsRunValid());
535 return track_encryption().default_is_protected == 1;
536}
537
538int64_t TrackRunIterator::aux_info_offset() const {
539 return run_itr_->aux_info_start_offset;
540}
541
542int TrackRunIterator::aux_info_size() const {
543 return run_itr_->aux_info_total_size;
544}
545
546bool TrackRunIterator::is_audio() const {
547 DCHECK(IsRunValid());
548 return run_itr_->track_type == kAudio;
549}
550
551bool 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
568int64_t TrackRunIterator::sample_offset() const {
569 DCHECK(IsSampleValid());
570 return sample_offset_;
571}
572
573int TrackRunIterator::sample_size() const {
574 DCHECK(IsSampleValid());
575 return sample_itr_->size;
576}
577
578int64_t TrackRunIterator::dts() const {
579 DCHECK(IsSampleValid());
580 return sample_dts_;
581}
582
583int64_t TrackRunIterator::cts() const {
584 DCHECK(IsSampleValid());
585 return sample_dts_ + sample_itr_->cts_offset;
586}
587
588int64_t TrackRunIterator::duration() const {
589 DCHECK(IsSampleValid());
590 return sample_itr_->duration;
591}
592
593bool TrackRunIterator::is_keyframe() const {
594 DCHECK(IsSampleValid());
595 return sample_itr_->is_keyframe;
596}
597
598const 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
605std::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
647int64_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.
std::vector< uint8_t > sample_encryption_data
bool ParseFromSampleEncryptionData(uint8_t l_iv_size, std::vector< SampleEncryptionEntry > *l_sample_encryption_entries) const