Chromium Code Reviews| Index: media/mp4/track_run_iterator.cc |
| diff --git a/media/mp4/track_run_iterator.cc b/media/mp4/track_run_iterator.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..666b3d677ffe5be06afec2a5fca578d6f6f9b1a5 |
| --- /dev/null |
| +++ b/media/mp4/track_run_iterator.cc |
| @@ -0,0 +1,258 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "media/mp4/track_run_iterator.h" |
| + |
| +#include <algorithm> |
| + |
| +#include "media/mp4/rcheck.h" |
| + |
| +namespace media { |
| +namespace mp4 { |
| + |
| +base::TimeDelta TimeDeltaFromFrac(int64 numer, uint64 denom) { |
| + if (numer > 0) { |
| + DCHECK_LT(numer, numer * base::Time::kMicrosecondsPerSecond); |
|
acolwell GONE FROM CHROMIUM
2012/06/07 19:44:02
Hmm.. I was hoping this could be a single line che
strobe_
2012/06/07 22:04:22
Done.
|
| + } else { |
| + DCHECK_GE(numer, numer * base::Time::kMicrosecondsPerSecond); |
| + } |
| + |
| + return base::TimeDelta::FromMicroseconds( |
| + base::Time::kMicrosecondsPerSecond * numer / denom); |
| +} |
| + |
| +const static uint32 kSampleIsDifferenceSampleFlagMask = 0x10000; |
| + |
| +TrackRunInfo::TrackRunInfo() {} |
| +TrackRunInfo::~TrackRunInfo() {} |
| + |
| +TrackRunIterator::TrackRunIterator() {} |
| +TrackRunIterator::~TrackRunIterator() {} |
| + |
| +void PopulateSampleInfo(const Track& trak, |
| + const TrackExtends& trex, |
| + const TrackFragmentHeader& tfhd, |
| + const TrackFragmentRun& trun, |
| + const size_t i, |
| + SampleInfo* si) { |
|
acolwell GONE FROM CHROMIUM
2012/06/07 19:44:02
si -> sample_info
strobe_
2012/06/07 22:04:22
Done.
|
| + if (i < trun.sample_sizes.size()) { |
| + si->size = trun.sample_sizes[i]; |
| + } else if (tfhd.default_sample_size > 0) { |
| + si->size = tfhd.default_sample_size; |
| + } else { |
| + si->size = trex.default_sample_size; |
| + } |
| + |
| + const uint64 timescale = trak.media.header.timescale; |
| + uint64 duration; |
| + if (i < trun.sample_durations.size()) { |
| + duration = trun.sample_durations[i]; |
| + } else if (tfhd.default_sample_duration > 0) { |
| + duration = tfhd.default_sample_duration; |
| + } else { |
| + duration = trex.default_sample_duration; |
| + } |
| + si->duration = TimeDeltaFromFrac(duration, timescale); |
| + |
| + if (i < trun.sample_composition_time_offsets.size()) { |
| + si->cts_offset = TimeDeltaFromFrac(trun.sample_composition_time_offsets[i], |
| + timescale); |
| + } else { |
| + si->cts_offset = TimeDelta::FromMicroseconds(0); |
| + } |
| + |
| + uint32 flags; |
| + if (i < trun.sample_flags.size()) { |
| + flags = trun.sample_flags[i]; |
| + } else if (tfhd.has_default_sample_flags) { |
| + flags = tfhd.default_sample_flags; |
| + } else { |
| + flags = trex.default_sample_flags; |
| + } |
| + si->is_keyframe = !(flags & kSampleIsDifferenceSampleFlagMask); |
| +} |
| + |
| +class CompareOffset { |
| + public: |
| + bool operator()(TrackRunInfo a, TrackRunInfo b) { |
| + size_t a_min = a.sample_start_offset; |
| + if (a.is_encrypted && a.cenc_start_offset < a_min) |
| + a_min = a.cenc_start_offset; |
| + size_t b_min = b.sample_start_offset; |
| + if (b.is_encrypted && b.cenc_start_offset < b_min) |
| + b_min = b.cenc_start_offset; |
| + return a_min < b_min; |
| + } |
| +}; |
| + |
| +bool TrackRunIterator::Init(const Movie& moov, const MovieFragment& moof) { |
| + runs_.clear(); |
| + |
| + for (size_t i = 0; i < moof.tracks.size(); i++) { |
| + const TrackFragment& traf = moof.tracks[i]; |
| + |
| + const Track* trak = NULL; |
| + for (size_t t = 0; t < moov.tracks.size(); t++) { |
| + if (moov.tracks[t].header.track_id == traf.header.track_id) |
| + trak = &moov.tracks[t]; |
| + } |
| + |
| + const TrackExtends* trex = NULL; |
| + for (size_t t = 0; t < moov.extends.tracks.size(); t++) { |
| + if (moov.extends.tracks[t].track_id == traf.header.track_id) |
| + trex = &moov.extends.tracks[t]; |
| + } |
| + RCHECK(trak && trex); |
| + |
| + const ProtectionSchemeInfo* sinf = NULL; |
| + const SampleDescription& stsd = |
| + trak->media.information.sample_table.description; |
| + if (stsd.type == kAudio) { |
| + sinf = &stsd.audio_entries[0].sinf; |
| + } else if (stsd.type == kVideo) { |
| + sinf = &stsd.video_entries[0].sinf; |
| + } else { |
| + DVLOG(1) << "Skipping unhandled track type"; |
| + continue; |
| + } |
| + |
| + for (size_t j = 0; j < traf.runs.size(); j++) { |
| + const TrackFragmentRun& trun = traf.runs[j]; |
| + TrackRunInfo tri; |
| + tri.track_id = traf.header.track_id; |
| + tri.start_dts = TimeDeltaFromFrac(traf.decode_time.decode_time, |
| + trak->media.header.timescale); |
| + tri.sample_start_offset = trun.data_offset; |
| + tri.is_encrypted = sinf->info.track_encryption.is_encrypted; |
| + // TODO(strobe): CENC recovery and testing |
| + |
| + tri.samples.resize(trun.sample_count); |
| + |
| + for (size_t k = 0; k < trun.sample_count; k++) { |
| + PopulateSampleInfo(*trak, *trex, traf.header, trun, k, &tri.samples[k]); |
| + } |
| + runs_.push_back(tri); |
| + } |
| + } |
| + |
| + std::sort(runs_.begin(), runs_.end(), CompareOffset()); |
| + run_itr_ = runs_.begin(); |
| + min_clear_offset_itr_ = min_clear_offsets_.begin(); |
| + ResetRun(); |
| + return true; |
| +} |
| + |
| +void TrackRunIterator::AdvanceRun() { |
| + ++run_itr_; |
| + if (min_clear_offset_itr_ != min_clear_offsets_.end()) |
| + ++min_clear_offset_itr_; |
| + ResetRun(); |
| +} |
| + |
| +void TrackRunIterator::ResetRun() { |
| + if (!RunValid()) return; |
| + sample_dts_ = run_itr_->start_dts; |
| + sample_offset_ = run_itr_->sample_start_offset; |
| + sample_itr_ = run_itr_->samples.begin(); |
| +} |
| + |
| +void TrackRunIterator::AdvanceSample() { |
| + DCHECK(SampleValid()); |
| + sample_dts_ += sample_itr_->duration; |
| + sample_offset_ += sample_itr_->size; |
| + // TODO(strobe): frame_cenc_info_ |
| + ++sample_itr_; |
| +} |
| + |
| +bool TrackRunIterator::NeedsCENC() { |
| + CHECK(!is_encrypted()) << "TODO(strobe): Implement CENC."; |
| + return is_encrypted(); |
| +} |
| + |
| +bool TrackRunIterator::CacheCENC(const uint8* buf, size_t size) { |
| + LOG(FATAL) << "Not implemented"; |
| + return false; |
| +} |
| + |
| +bool TrackRunIterator::RunValid() const { |
| + return run_itr_ != runs_.end(); |
| +} |
| + |
| +bool TrackRunIterator::SampleValid() const { |
| + return RunValid() && (sample_itr_ != run_itr_->samples.end()); |
| +} |
| + |
| +size_t TrackRunIterator::GetMaxClearOffset() { |
| + size_t offset = kuint64max; |
| + |
| + if (SampleValid()) { |
| + offset = std::min(offset, sample_offset_); |
| + if (NeedsCENC()) { |
| + offset = std::min(offset, cenc_offset()); |
| + } |
| + } |
| + if (min_clear_offset_itr_ != min_clear_offsets_.end()) { |
| + offset = std::min(offset, *min_clear_offset_itr_); |
| + } |
| + if (offset == kuint64max) return 0; |
| + return offset; |
| +} |
| + |
| +uint32 TrackRunIterator::track_id() const { |
| + DCHECK(RunValid()); |
| + return run_itr_->track_id; |
| +} |
| + |
| +bool TrackRunIterator::is_encrypted() const { |
| + DCHECK(RunValid()); |
| + return run_itr_->is_encrypted; |
| +} |
| + |
| +size_t TrackRunIterator::cenc_offset() const { |
| + DCHECK(is_encrypted()); |
| + return run_itr_->cenc_start_offset; |
| +} |
| + |
| +size_t TrackRunIterator::cenc_size() const { |
| + DCHECK(is_encrypted()); |
| + return run_itr_->cenc_total_size; |
| +} |
| + |
| +size_t TrackRunIterator::offset() const { |
| + DCHECK(SampleValid()); |
| + return sample_offset_; |
| +} |
| + |
| +size_t TrackRunIterator::size() const { |
| + DCHECK(SampleValid()); |
| + return sample_itr_->size; |
| +} |
| + |
| +TimeDelta TrackRunIterator::dts() const { |
| + DCHECK(SampleValid()); |
| + return sample_dts_; |
| +} |
| + |
| +TimeDelta TrackRunIterator::cts() const { |
| + DCHECK(SampleValid()); |
| + return sample_dts_ + sample_itr_->cts_offset; |
| +} |
| + |
| +TimeDelta TrackRunIterator::duration() const { |
| + DCHECK(SampleValid()); |
| + return sample_itr_->duration; |
| +} |
| + |
| +bool TrackRunIterator::is_keyframe() const { |
| + DCHECK(SampleValid()); |
| + return sample_itr_->is_keyframe; |
| +} |
| + |
| +const FrameCENCInfo& TrackRunIterator::frame_cenc_info() { |
| + DCHECK(is_encrypted()); |
| + return frame_cenc_info_; |
| +} |
| + |
| +} // namespace mp4 |
| +} // namespace media |