Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(677)

Unified Diff: media/mp4/track_run_iterator.cc

Issue 10651006: Add Common Encryption support to BMFF, including subsample decryption. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Add wrong subsample size test Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/mp4/track_run_iterator.cc
diff --git a/media/mp4/track_run_iterator.cc b/media/mp4/track_run_iterator.cc
index 74bef9950ac2292e85a60ef77671ea92be70e31c..c291823a446ce7c5ddc93d4734d2859cdbc74ca1 100644
--- a/media/mp4/track_run_iterator.cc
+++ b/media/mp4/track_run_iterator.cc
@@ -12,30 +12,56 @@
namespace media {
namespace mp4 {
-base::TimeDelta TimeDeltaFromFrac(int64 numer, int64 denom) {
+struct SampleInfo {
+ int size;
+ int duration;
+ int cts_offset;
+ bool is_keyframe;
+};
+
+struct TrackRunInfo {
+ uint32 track_id;
+ std::vector<SampleInfo> samples;
+ int64 timescale;
+ int64 start_dts;
+ int64 sample_start_offset;
+
+ int64 aux_info_start_offset; // Only valid if aux_info_total_size > 0.
+ int aux_info_default_size;
+ std::vector<uint8> aux_info_sizes; // Present if default_size == 0.
ddorwin 2012/07/17 01:14:21 Present => Populated or Non-empty
strobe_ 2012/07/19 02:43:35 Done.
+ int aux_info_total_size;
+
+ TrackEncryption track_encryption;
+
+ TrackRunInfo();
+ ~TrackRunInfo();
+};
+
+TrackRunInfo::TrackRunInfo()
+ : track_id(0),
+ timescale(-1),
+ start_dts(-1),
+ sample_start_offset(-1),
+ aux_info_start_offset(-1),
+ aux_info_default_size(-1),
+ aux_info_total_size(-1) {
+}
+TrackRunInfo::~TrackRunInfo() {}
+
+TimeDelta TimeDeltaFromFrac(int64 numer, int64 denom) {
ddorwin 2012/07/17 01:14:21 Function and var names should usually be complete
ddorwin 2012/07/17 01:14:21 What does "FromFrac" mean? Isn't it really convert
strobe_ 2012/07/19 02:43:35 The time base is referred to as the denominator el
DCHECK_LT((numer > 0 ? numer : -numer),
ddorwin 2012/07/17 01:14:21 use abs()?
strobe_ 2012/07/19 02:43:35 Nope, breaks build on cros-tegra2.
kint64max / base::Time::kMicrosecondsPerSecond);
- return base::TimeDelta::FromMicroseconds(
+ return TimeDelta::FromMicroseconds(
base::Time::kMicrosecondsPerSecond * numer / denom);
}
static const uint32 kSampleIsDifferenceSampleFlagMask = 0x10000;
ddorwin 2012/07/17 01:14:21 file constant should be at the top of the file in
strobe_ 2012/07/19 02:43:35 Done.
-TrackRunInfo::TrackRunInfo()
- : track_id(0),
- sample_start_offset(-1),
- is_encrypted(false),
- cenc_start_offset(-1),
- cenc_total_size(-1),
- default_cenc_size(0) {}
-
-TrackRunInfo::~TrackRunInfo() {}
TrackRunIterator::TrackRunIterator() : sample_offset_(0) {}
TrackRunIterator::~TrackRunIterator() {}
-static void PopulateSampleInfo(const Track& trak,
- const TrackExtends& trex,
+static void PopulateSampleInfo(const TrackExtends& trex,
const TrackFragmentHeader& tfhd,
const TrackFragmentRun& trun,
const uint32 i,
@@ -48,22 +74,18 @@ static void PopulateSampleInfo(const Track& trak,
sample_info->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];
+ sample_info->duration = trun.sample_durations[i];
} else if (tfhd.default_sample_duration > 0) {
- duration = tfhd.default_sample_duration;
+ sample_info->duration = tfhd.default_sample_duration;
} else {
- duration = trex.default_sample_duration;
+ sample_info->duration = trex.default_sample_duration;
}
- sample_info->duration = TimeDeltaFromFrac(duration, timescale);
if (i < trun.sample_composition_time_offsets.size()) {
- sample_info->cts_offset =
- TimeDeltaFromFrac(trun.sample_composition_time_offsets[i], timescale);
+ sample_info->cts_offset = trun.sample_composition_time_offsets[i];
} else {
- sample_info->cts_offset = TimeDelta::FromMicroseconds(0);
+ sample_info->cts_offset = 0;
}
uint32 flags;
@@ -77,16 +99,19 @@ static void PopulateSampleInfo(const Track& trak,
sample_info->is_keyframe = !(flags & kSampleIsDifferenceSampleFlagMask);
}
-class CompareOffset {
+class CompareMinTrackRunDataOffset {
ddorwin 2012/07/17 01:14:21 What does this do?
strobe_ 2012/07/19 02:43:35 Comment added.
public:
bool operator()(const TrackRunInfo& a, const TrackRunInfo& b) {
- int64 a_min = a.sample_start_offset;
- if (a.is_encrypted && a.cenc_start_offset < a_min)
- a_min = a.cenc_start_offset;
- int64 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;
+ int64 a_aux = a.aux_info_total_size ? a.aux_info_start_offset : kint64max;
+ int64 b_aux = b.aux_info_total_size ? b.aux_info_start_offset : kint64max;
+
+ int64 a_lesser = std::min(a_aux, a.sample_start_offset);
+ int64 a_greater = std::max(a_aux, a.sample_start_offset);
+ int64 b_lesser = std::min(b_aux, b.sample_start_offset);
+ int64 b_greater = std::max(b_aux, b.sample_start_offset);
+
+ if (a_lesser == b_lesser) return a_greater < b_greater;
+ return a_lesser < b_lesser;
}
};
@@ -101,14 +126,16 @@ bool TrackRunIterator::Init(const Movie& moov, const MovieFragment& moof) {
if (moov.tracks[t].header.track_id == traf.header.track_id)
trak = &moov.tracks[t];
}
+ RCHECK(trak);
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);
+ RCHECK(trex);
+ // TODO(strobe): Support multiple sample description entries
const ProtectionSchemeInfo* sinf = NULL;
const SampleDescription& stsd =
trak->media.information.sample_table.description;
@@ -121,158 +148,213 @@ bool TrackRunIterator::Init(const Movie& moov, const MovieFragment& moof) {
continue;
}
- if (sinf->info.track_encryption.is_encrypted) {
- // TODO(strobe): CENC recovery and testing (http://crbug.com/132351)
- DVLOG(1) << "Encrypted tracks not handled";
- continue;
- }
+ int64 run_start_dts = traf.decode_time.decode_time;
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.timescale = trak->media.header.timescale;
+ tri.start_dts = run_start_dts;
tri.sample_start_offset = trun.data_offset;
- tri.is_encrypted = false;
- tri.cenc_start_offset = 0;
- tri.cenc_total_size = 0;
- tri.default_cenc_size = 0;
+ // Collect information from the auxiliary_offset entry with the same index
+ // in the 'saiz' container as the current run's index in the 'trun'
+ // container, if it is present.
+ if (traf.auxiliary_offset.offsets.size() > j) {
+ // There should be an auxiliary info entry corresponding to each sample
+ // in the auxiliary offset entry's corresponding track run.
+ RCHECK(traf.auxiliary_size.sample_count == trun.sample_count);
+ tri.aux_info_start_offset = traf.auxiliary_offset.offsets[j];
+ tri.aux_info_default_size =
+ traf.auxiliary_size.default_sample_info_size;
+ tri.aux_info_sizes = traf.auxiliary_size.sample_info_sizes;
+
+ // If the default info size is positive, find the total size of the aux
+ // info block from it, otherwise sum over the individual sizes of each
+ // aux info entry in the aux_offset entry.
+ if (tri.aux_info_default_size) {
+ tri.aux_info_total_size =
+ tri.aux_info_default_size * trun.sample_count;
+ } else {
+ tri.aux_info_total_size = 0;
+ for (size_t k = 0; k < trun.sample_count; k++) {
+ tri.aux_info_total_size += tri.aux_info_sizes[k];
+ }
+ }
+ } else {
+ tri.aux_info_start_offset = -1;
+ tri.aux_info_total_size = 0;
+ }
+ tri.track_encryption = sinf->info.track_encryption;
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]);
+ PopulateSampleInfo(*trex, traf.header, trun, k, &tri.samples[k]);
+ run_start_dts += tri.samples[k].duration;
}
runs_.push_back(tri);
}
}
- std::sort(runs_.begin(), runs_.end(), CompareOffset());
+ std::sort(runs_.begin(), runs_.end(), CompareMinTrackRunDataOffset());
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;
+ if (!RunIsValid()) return;
sample_dts_ = run_itr_->start_dts;
sample_offset_ = run_itr_->sample_start_offset;
sample_itr_ = run_itr_->samples.begin();
+ cenc_info_.clear();
}
void TrackRunIterator::AdvanceSample() {
- DCHECK(SampleValid());
+ DCHECK(SampleIsValid());
sample_dts_ += sample_itr_->duration;
sample_offset_ += sample_itr_->size;
++sample_itr_;
}
-bool TrackRunIterator::NeedsCENC() {
- CHECK(!is_encrypted()) << "TODO(strobe): Implement CENC.";
- return is_encrypted();
+// This implementation only indicates a need for caching if CENC auxiliary
+// info is available in the stream.
+bool TrackRunIterator::AuxInfoNeedsToBeCached() {
+ DCHECK(RunIsValid());
+ return is_encrypted() && aux_info_size() > 0 && cenc_info_.size() == 0;
}
-bool TrackRunIterator::CacheCENC(const uint8* buf, int size) {
- LOG(FATAL) << "Not implemented";
- return false;
+// This implementation currently only caches CENC auxiliary info.
+bool TrackRunIterator::CacheAuxInfo(const uint8* buf, int buf_size) {
+ RCHECK(AuxInfoNeedsToBeCached() && buf_size >= aux_info_size());
+
+ cenc_info_.resize(run_itr_->samples.size());
+ int64 pos = 0;
+ for (size_t i = 0; i < run_itr_->samples.size(); i++) {
+ int info_size = run_itr_->aux_info_default_size;
+ if (!info_size)
+ info_size = run_itr_->aux_info_sizes[i];
+
+ BufferReader reader(buf + pos, info_size);
+ RCHECK(cenc_info_[i].Parse(run_itr_->track_encryption.default_iv_size,
+ &reader));
+ pos += info_size;
+ }
+
+ return true;
}
-bool TrackRunIterator::RunValid() const {
+bool TrackRunIterator::RunIsValid() const {
return run_itr_ != runs_.end();
}
-bool TrackRunIterator::SampleValid() const {
- return RunValid() && (sample_itr_ != run_itr_->samples.end());
+bool TrackRunIterator::SampleIsValid() const {
+ return RunIsValid() && (sample_itr_ != run_itr_->samples.end());
}
int64 TrackRunIterator::GetMaxClearOffset() {
int64 offset = kint64max;
ddorwin 2012/07/17 01:14:21 max_offset?
strobe_ 2012/07/19 02:43:35 Well, internal to this function, it's really the m
- if (SampleValid()) {
+ if (SampleIsValid()) {
offset = std::min(offset, sample_offset_);
- if (NeedsCENC()) {
- offset = std::min(offset, cenc_offset());
+ if (AuxInfoNeedsToBeCached()) {
+ offset = std::min(offset, aux_info_offset());
}
}
- if (min_clear_offset_itr_ != min_clear_offsets_.end()) {
- offset = std::min(offset, *min_clear_offset_itr_);
+ if (run_itr_ != runs_.end()) {
+ std::vector<TrackRunInfo>::const_iterator next_run = run_itr_ + 1;
+ if (next_run != runs_.end()) {
+ offset = std::min(offset, next_run->sample_start_offset);
+ if (next_run->aux_info_total_size)
+ offset = std::min(offset, next_run->aux_info_start_offset);
+ }
}
- if (offset == kint64max) return 0;
return offset;
}
TimeDelta TrackRunIterator::GetMinDecodeTimestamp() {
TimeDelta dts = kInfiniteDuration();
for (size_t i = 0; i < runs_.size(); i++) {
- if (runs_[i].start_dts < dts)
- dts = runs_[i].start_dts;
+ TimeDelta run_dts = TimeDeltaFromFrac(runs_[i].start_dts,
+ runs_[i].timescale);
+ if (run_dts < dts)
+ dts = run_dts;
ddorwin 2012/07/17 01:14:21 Why?
strobe_ 2012/07/19 02:43:35 Addressed by rebase.
}
return dts;
}
uint32 TrackRunIterator::track_id() const {
- DCHECK(RunValid());
+ DCHECK(RunIsValid());
return run_itr_->track_id;
}
bool TrackRunIterator::is_encrypted() const {
- DCHECK(RunValid());
- return run_itr_->is_encrypted;
+ DCHECK(RunIsValid());
+ return run_itr_->track_encryption.is_encrypted;
}
-int64 TrackRunIterator::cenc_offset() const {
- DCHECK(is_encrypted());
- return run_itr_->cenc_start_offset;
+int64 TrackRunIterator::aux_info_offset() const {
+ return run_itr_->aux_info_start_offset;
}
-int TrackRunIterator::cenc_size() const {
- DCHECK(is_encrypted());
- return run_itr_->cenc_total_size;
+int TrackRunIterator::aux_info_size() const {
+ return run_itr_->aux_info_total_size;
}
-int64 TrackRunIterator::offset() const {
- DCHECK(SampleValid());
+int64 TrackRunIterator::sample_offset() const {
+ DCHECK(SampleIsValid());
return sample_offset_;
}
-int TrackRunIterator::size() const {
- DCHECK(SampleValid());
+int TrackRunIterator::sample_size() const {
+ DCHECK(SampleIsValid());
return sample_itr_->size;
}
TimeDelta TrackRunIterator::dts() const {
- DCHECK(SampleValid());
- return sample_dts_;
+ DCHECK(SampleIsValid());
+ return TimeDeltaFromFrac(sample_dts_, run_itr_->timescale);
}
TimeDelta TrackRunIterator::cts() const {
- DCHECK(SampleValid());
- return sample_dts_ + sample_itr_->cts_offset;
+ DCHECK(SampleIsValid());
+ return TimeDeltaFromFrac(sample_dts_ + sample_itr_->cts_offset,
+ run_itr_->timescale);
}
TimeDelta TrackRunIterator::duration() const {
- DCHECK(SampleValid());
- return sample_itr_->duration;
+ DCHECK(SampleIsValid());
+ return TimeDeltaFromFrac(sample_itr_->duration, run_itr_->timescale);
}
bool TrackRunIterator::is_keyframe() const {
- DCHECK(SampleValid());
+ DCHECK(SampleIsValid());
return sample_itr_->is_keyframe;
}
-const FrameCENCInfo& TrackRunIterator::frame_cenc_info() {
- DCHECK(is_encrypted());
- return frame_cenc_info_;
+scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
+ int sample_idx = sample_itr_ - run_itr_->samples.begin();
+ const FrameCENCInfo& cenc_info = cenc_info_[sample_idx];
ddorwin 2012/07/17 01:14:21 check size of the vector/array first.
strobe_ 2012/07/19 02:43:35 Done.
+ DCHECK(is_encrypted() && !AuxInfoNeedsToBeCached());
+
+ if (!cenc_info.subsamples.empty() &&
+ (cenc_info.GetTotalSizeOfSubsamples() !=
+ static_cast<size_t>(sample_size()))) {
+ DVLOG(1) << "Incorrect CENC subsample size.";
+ return scoped_ptr<DecryptConfig>();
+ }
+
+ return scoped_ptr<DecryptConfig>(new DecryptConfig(
+ &run_itr_->track_encryption.default_kid[0],
+ run_itr_->track_encryption.default_kid.size(),
+ std::string(cenc_info.iv, cenc_info.iv + arraysize(cenc_info.iv)),
ddorwin 2012/07/17 01:14:21 why not just size as second param?
strobe_ 2012/07/19 02:43:35 Would require typecasting.
+ cenc_info.subsamples));
}
} // namespace mp4

Powered by Google App Engine
This is Rietveld 408576698