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