track_run_iterator.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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 "media/mp4/track_run_iterator.h"
6
7#include <algorithm>
8
9#include "media/base/buffers.h"
10#include "media/base/stream_parser_buffer.h"
11#include "media/mp4/rcheck.h"
12
13namespace {
14static const uint32 kSampleIsDifferenceSampleFlagMask = 0x10000;
15}
16
17namespace media {
18namespace mp4 {
19
20struct SampleInfo {
21  int size;
22  int duration;
23  int cts_offset;
24  bool is_keyframe;
25};
26
27struct TrackRunInfo {
28  uint32 track_id;
29  std::vector<SampleInfo> samples;
30  int64 timescale;
31  int64 start_dts;
32  int64 sample_start_offset;
33
34  bool is_audio;
35  const AudioSampleEntry* audio_description;
36  const VideoSampleEntry* video_description;
37
38  int64 aux_info_start_offset;  // Only valid if aux_info_total_size > 0.
39  int aux_info_default_size;
40  std::vector<uint8> aux_info_sizes;  // Populated if default_size == 0.
41  int aux_info_total_size;
42
43  TrackRunInfo();
44  ~TrackRunInfo();
45};
46
47TrackRunInfo::TrackRunInfo()
48    : track_id(0),
49      timescale(-1),
50      start_dts(-1),
51      sample_start_offset(-1),
52      is_audio(false),
53      aux_info_start_offset(-1),
54      aux_info_default_size(-1),
55      aux_info_total_size(-1) {
56}
57TrackRunInfo::~TrackRunInfo() {}
58
59TimeDelta TimeDeltaFromRational(int64 numer, int64 denom) {
60  DCHECK_LT((numer > 0 ? numer : -numer),
61            kint64max / base::Time::kMicrosecondsPerSecond);
62  return TimeDelta::FromMicroseconds(
63        base::Time::kMicrosecondsPerSecond * numer / denom);
64}
65
66TrackRunIterator::TrackRunIterator(const Movie* moov,
67                                   const LogCB& log_cb)
68    : moov_(moov), log_cb_(log_cb), sample_offset_(0) {
69  CHECK(moov);
70}
71
72TrackRunIterator::~TrackRunIterator() {}
73
74static void PopulateSampleInfo(const TrackExtends& trex,
75                               const TrackFragmentHeader& tfhd,
76                               const TrackFragmentRun& trun,
77                               const int64 edit_list_offset,
78                               const uint32 i,
79                               SampleInfo* sample_info,
80                               const SampleDependsOn sample_depends_on) {
81  if (i < trun.sample_sizes.size()) {
82    sample_info->size = trun.sample_sizes[i];
83  } else if (tfhd.default_sample_size > 0) {
84    sample_info->size = tfhd.default_sample_size;
85  } else {
86    sample_info->size = trex.default_sample_size;
87  }
88
89  if (i < trun.sample_durations.size()) {
90    sample_info->duration = trun.sample_durations[i];
91  } else if (tfhd.default_sample_duration > 0) {
92    sample_info->duration = tfhd.default_sample_duration;
93  } else {
94    sample_info->duration = trex.default_sample_duration;
95  }
96
97  if (i < trun.sample_composition_time_offsets.size()) {
98    sample_info->cts_offset = trun.sample_composition_time_offsets[i];
99  } else {
100    sample_info->cts_offset = 0;
101  }
102  sample_info->cts_offset += edit_list_offset;
103
104  uint32 flags;
105  if (i < trun.sample_flags.size()) {
106    flags = trun.sample_flags[i];
107  } else if (tfhd.has_default_sample_flags) {
108    flags = tfhd.default_sample_flags;
109  } else {
110    flags = trex.default_sample_flags;
111  }
112
113  switch (sample_depends_on) {
114    case kSampleDependsOnUnknown:
115      sample_info->is_keyframe = !(flags & kSampleIsDifferenceSampleFlagMask);
116      break;
117
118    case kSampleDependsOnOthers:
119      sample_info->is_keyframe = false;
120      break;
121
122    case kSampleDependsOnNoOther:
123      sample_info->is_keyframe = true;
124      break;
125
126    case kSampleDependsOnReserved:
127      CHECK(false);
128  }
129}
130
131// In well-structured encrypted media, each track run will be immediately
132// preceded by its auxiliary information; this is the only optimal storage
133// pattern in terms of minimum number of bytes from a serial stream needed to
134// begin playback. It also allows us to optimize caching on memory-constrained
135// architectures, because we can cache the relatively small auxiliary
136// information for an entire run and then discard data from the input stream,
137// instead of retaining the entire 'mdat' box.
138//
139// We optimize for this situation (with no loss of generality) by sorting track
140// runs during iteration in order of their first data offset (either sample data
141// or auxiliary data).
142class CompareMinTrackRunDataOffset {
143 public:
144  bool operator()(const TrackRunInfo& a, const TrackRunInfo& b) {
145    int64 a_aux = a.aux_info_total_size ? a.aux_info_start_offset : kint64max;
146    int64 b_aux = b.aux_info_total_size ? b.aux_info_start_offset : kint64max;
147
148    int64 a_lesser = std::min(a_aux, a.sample_start_offset);
149    int64 a_greater = std::max(a_aux, a.sample_start_offset);
150    int64 b_lesser = std::min(b_aux, b.sample_start_offset);
151    int64 b_greater = std::max(b_aux, b.sample_start_offset);
152
153    if (a_lesser == b_lesser) return a_greater < b_greater;
154    return a_lesser < b_lesser;
155  }
156};
157
158bool TrackRunIterator::Init(const MovieFragment& moof) {
159  runs_.clear();
160
161  for (size_t i = 0; i < moof.tracks.size(); i++) {
162    const TrackFragment& traf = moof.tracks[i];
163
164    const Track* trak = NULL;
165    for (size_t t = 0; t < moov_->tracks.size(); t++) {
166      if (moov_->tracks[t].header.track_id == traf.header.track_id)
167        trak = &moov_->tracks[t];
168    }
169    RCHECK(trak);
170
171    const TrackExtends* trex = NULL;
172    for (size_t t = 0; t < moov_->extends.tracks.size(); t++) {
173      if (moov_->extends.tracks[t].track_id == traf.header.track_id)
174        trex = &moov_->extends.tracks[t];
175    }
176    RCHECK(trex);
177
178    const SampleDescription& stsd =
179        trak->media.information.sample_table.description;
180    if (stsd.type != kAudio && stsd.type != kVideo) {
181      DVLOG(1) << "Skipping unhandled track type";
182      continue;
183    }
184    size_t desc_idx = traf.header.sample_description_index;
185    if (!desc_idx) desc_idx = trex->default_sample_description_index;
186    RCHECK(desc_idx > 0);  // Descriptions are one-indexed in the file
187    desc_idx -= 1;
188
189    // Process edit list to remove CTS offset introduced in the presence of
190    // B-frames (those that contain a single edit with a nonnegative media
191    // time). Other uses of edit lists are not supported, as they are
192    // both uncommon and better served by higher-level protocols.
193    int64 edit_list_offset = 0;
194    const std::vector<EditListEntry>& edits = trak->edit.list.edits;
195    if (!edits.empty()) {
196      if (edits.size() > 1)
197        DVLOG(1) << "Multi-entry edit box detected; some components ignored.";
198
199      if (edits[0].media_time < 0) {
200        DVLOG(1) << "Empty edit list entry ignored.";
201      } else {
202        edit_list_offset = -edits[0].media_time;
203      }
204    }
205
206    int64 run_start_dts = traf.decode_time.decode_time;
207    int sample_count_sum = 0;
208
209    for (size_t j = 0; j < traf.runs.size(); j++) {
210      const TrackFragmentRun& trun = traf.runs[j];
211      TrackRunInfo tri;
212      tri.track_id = traf.header.track_id;
213      tri.timescale = trak->media.header.timescale;
214      tri.start_dts = run_start_dts;
215      tri.sample_start_offset = trun.data_offset;
216
217      tri.is_audio = (stsd.type == kAudio);
218      if (tri.is_audio) {
219        RCHECK(!stsd.audio_entries.empty());
220        if (desc_idx > stsd.audio_entries.size())
221          desc_idx = 0;
222        tri.audio_description = &stsd.audio_entries[desc_idx];
223      } else {
224        RCHECK(!stsd.video_entries.empty());
225        if (desc_idx > stsd.video_entries.size())
226          desc_idx = 0;
227        tri.video_description = &stsd.video_entries[desc_idx];
228      }
229
230      // Collect information from the auxiliary_offset entry with the same index
231      // in the 'saiz' container as the current run's index in the 'trun'
232      // container, if it is present.
233      if (traf.auxiliary_offset.offsets.size() > j) {
234        // There should be an auxiliary info entry corresponding to each sample
235        // in the auxiliary offset entry's corresponding track run.
236        RCHECK(traf.auxiliary_size.sample_count >=
237               sample_count_sum + trun.sample_count);
238        tri.aux_info_start_offset = traf.auxiliary_offset.offsets[j];
239        tri.aux_info_default_size =
240            traf.auxiliary_size.default_sample_info_size;
241        if (tri.aux_info_default_size == 0) {
242          const std::vector<uint8>& sizes =
243              traf.auxiliary_size.sample_info_sizes;
244          tri.aux_info_sizes.insert(tri.aux_info_sizes.begin(),
245              sizes.begin() + sample_count_sum,
246              sizes.begin() + sample_count_sum + trun.sample_count);
247        }
248
249        // If the default info size is positive, find the total size of the aux
250        // info block from it, otherwise sum over the individual sizes of each
251        // aux info entry in the aux_offset entry.
252        if (tri.aux_info_default_size) {
253          tri.aux_info_total_size =
254              tri.aux_info_default_size * trun.sample_count;
255        } else {
256          tri.aux_info_total_size = 0;
257          for (size_t k = 0; k < trun.sample_count; k++) {
258            tri.aux_info_total_size += tri.aux_info_sizes[k];
259          }
260        }
261      } else {
262        tri.aux_info_start_offset = -1;
263        tri.aux_info_total_size = 0;
264      }
265
266      tri.samples.resize(trun.sample_count);
267      for (size_t k = 0; k < trun.sample_count; k++) {
268        PopulateSampleInfo(*trex, traf.header, trun, edit_list_offset,
269                           k, &tri.samples[k], traf.sdtp.sample_depends_on(k));
270        run_start_dts += tri.samples[k].duration;
271      }
272      runs_.push_back(tri);
273      sample_count_sum += trun.sample_count;
274    }
275  }
276
277  std::sort(runs_.begin(), runs_.end(), CompareMinTrackRunDataOffset());
278  run_itr_ = runs_.begin();
279  ResetRun();
280  return true;
281}
282
283void TrackRunIterator::AdvanceRun() {
284  ++run_itr_;
285  ResetRun();
286}
287
288void TrackRunIterator::ResetRun() {
289  if (!IsRunValid()) return;
290  sample_dts_ = run_itr_->start_dts;
291  sample_offset_ = run_itr_->sample_start_offset;
292  sample_itr_ = run_itr_->samples.begin();
293  cenc_info_.clear();
294}
295
296void TrackRunIterator::AdvanceSample() {
297  DCHECK(IsSampleValid());
298  sample_dts_ += sample_itr_->duration;
299  sample_offset_ += sample_itr_->size;
300  ++sample_itr_;
301}
302
303// This implementation only indicates a need for caching if CENC auxiliary
304// info is available in the stream.
305bool TrackRunIterator::AuxInfoNeedsToBeCached() {
306  DCHECK(IsRunValid());
307  return is_encrypted() && aux_info_size() > 0 && cenc_info_.size() == 0;
308}
309
310// This implementation currently only caches CENC auxiliary info.
311bool TrackRunIterator::CacheAuxInfo(const uint8* buf, int buf_size) {
312  RCHECK(AuxInfoNeedsToBeCached() && buf_size >= aux_info_size());
313
314  cenc_info_.resize(run_itr_->samples.size());
315  int64 pos = 0;
316  for (size_t i = 0; i < run_itr_->samples.size(); i++) {
317    int info_size = run_itr_->aux_info_default_size;
318    if (!info_size)
319      info_size = run_itr_->aux_info_sizes[i];
320
321    BufferReader reader(buf + pos, info_size);
322    RCHECK(cenc_info_[i].Parse(track_encryption().default_iv_size, &reader));
323    pos += info_size;
324  }
325
326  return true;
327}
328
329bool TrackRunIterator::IsRunValid() const {
330  return run_itr_ != runs_.end();
331}
332
333bool TrackRunIterator::IsSampleValid() const {
334  return IsRunValid() && (sample_itr_ != run_itr_->samples.end());
335}
336
337// Because tracks are in sorted order and auxiliary information is cached when
338// returning samples, it is guaranteed that no data will be required before the
339// lesser of the minimum data offset of this track and the next in sequence.
340// (The stronger condition - that no data is required before the minimum data
341// offset of this track alone - is not guaranteed, because the BMFF spec does
342// not have any inter-run ordering restrictions.)
343int64 TrackRunIterator::GetMaxClearOffset() {
344  int64 offset = kint64max;
345
346  if (IsSampleValid()) {
347    offset = std::min(offset, sample_offset_);
348    if (AuxInfoNeedsToBeCached())
349      offset = std::min(offset, aux_info_offset());
350  }
351  if (run_itr_ != runs_.end()) {
352    std::vector<TrackRunInfo>::const_iterator next_run = run_itr_ + 1;
353    if (next_run != runs_.end()) {
354      offset = std::min(offset, next_run->sample_start_offset);
355      if (next_run->aux_info_total_size)
356        offset = std::min(offset, next_run->aux_info_start_offset);
357    }
358  }
359  if (offset == kint64max) return 0;
360  return offset;
361}
362
363uint32 TrackRunIterator::track_id() const {
364  DCHECK(IsRunValid());
365  return run_itr_->track_id;
366}
367
368bool TrackRunIterator::is_encrypted() const {
369  DCHECK(IsRunValid());
370  return track_encryption().is_encrypted;
371}
372
373int64 TrackRunIterator::aux_info_offset() const {
374  return run_itr_->aux_info_start_offset;
375}
376
377int TrackRunIterator::aux_info_size() const {
378  return run_itr_->aux_info_total_size;
379}
380
381bool TrackRunIterator::is_audio() const {
382  DCHECK(IsRunValid());
383  return run_itr_->is_audio;
384}
385
386const AudioSampleEntry& TrackRunIterator::audio_description() const {
387  DCHECK(is_audio());
388  DCHECK(run_itr_->audio_description);
389  return *run_itr_->audio_description;
390}
391
392const VideoSampleEntry& TrackRunIterator::video_description() const {
393  DCHECK(!is_audio());
394  DCHECK(run_itr_->video_description);
395  return *run_itr_->video_description;
396}
397
398int64 TrackRunIterator::sample_offset() const {
399  DCHECK(IsSampleValid());
400  return sample_offset_;
401}
402
403int TrackRunIterator::sample_size() const {
404  DCHECK(IsSampleValid());
405  return sample_itr_->size;
406}
407
408TimeDelta TrackRunIterator::dts() const {
409  DCHECK(IsSampleValid());
410  return TimeDeltaFromRational(sample_dts_, run_itr_->timescale);
411}
412
413TimeDelta TrackRunIterator::cts() const {
414  DCHECK(IsSampleValid());
415  return TimeDeltaFromRational(sample_dts_ + sample_itr_->cts_offset,
416                               run_itr_->timescale);
417}
418
419TimeDelta TrackRunIterator::duration() const {
420  DCHECK(IsSampleValid());
421  return TimeDeltaFromRational(sample_itr_->duration, run_itr_->timescale);
422}
423
424bool TrackRunIterator::is_keyframe() const {
425  DCHECK(IsSampleValid());
426  return sample_itr_->is_keyframe;
427}
428
429const TrackEncryption& TrackRunIterator::track_encryption() const {
430  if (is_audio())
431    return audio_description().sinf.info.track_encryption;
432  return video_description().sinf.info.track_encryption;
433}
434
435scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
436  size_t sample_idx = sample_itr_ - run_itr_->samples.begin();
437  DCHECK(sample_idx < cenc_info_.size());
438  const FrameCENCInfo& cenc_info = cenc_info_[sample_idx];
439  DCHECK(is_encrypted() && !AuxInfoNeedsToBeCached());
440
441  if (!cenc_info.subsamples.empty() &&
442      (cenc_info.GetTotalSizeOfSubsamples() !=
443       static_cast<size_t>(sample_size()))) {
444    MEDIA_LOG(log_cb_) << "Incorrect CENC subsample size.";
445    return scoped_ptr<DecryptConfig>();
446  }
447
448  const std::vector<uint8>& kid = track_encryption().default_kid;
449  return scoped_ptr<DecryptConfig>(new DecryptConfig(
450      std::string(reinterpret_cast<const char*>(&kid[0]), kid.size()),
451      std::string(reinterpret_cast<const char*>(cenc_info.iv),
452                  arraysize(cenc_info.iv)),
453      0,  // No offset to start of media data in MP4 using CENC.
454      cenc_info.subsamples));
455}
456
457}  // namespace mp4
458}  // namespace media
459