Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/mp4/track_run_iterator.h" | 5 #include "media/mp4/track_run_iterator.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "media/base/stream_parser_buffer.h" | 9 #include "media/base/stream_parser_buffer.h" |
| 10 #include "media/mp4/rcheck.h" | 10 #include "media/mp4/rcheck.h" |
| 11 | 11 |
| 12 namespace media { | 12 namespace media { |
| 13 namespace mp4 { | 13 namespace mp4 { |
| 14 | 14 |
| 15 base::TimeDelta TimeDeltaFromFrac(int64 numer, uint64 denom) { | 15 base::TimeDelta TimeDeltaFromFrac(int64 numer, uint64 denom) { |
| 16 DCHECK_LT((numer > 0 ? numer : -numer), | 16 DCHECK_LT((numer > 0 ? numer : -numer), |
| 17 kint64max / base::Time::kMicrosecondsPerSecond); | 17 kint64max / base::Time::kMicrosecondsPerSecond); |
| 18 return base::TimeDelta::FromMicroseconds( | 18 return base::TimeDelta::FromMicroseconds( |
| 19 base::Time::kMicrosecondsPerSecond * numer / denom); | 19 base::Time::kMicrosecondsPerSecond * numer / denom); |
| 20 } | 20 } |
| 21 | 21 |
| 22 static const uint32 kSampleIsDifferenceSampleFlagMask = 0x10000; | 22 static const uint32 kSampleIsDifferenceSampleFlagMask = 0x10000; |
| 23 | 23 |
| 24 TrackRunInfo::TrackRunInfo() | 24 TrackRunInfo::TrackRunInfo() |
| 25 : track_id(0), | 25 : track_id(0), |
| 26 sample_start_offset(-1), | 26 sample_start_offset(-1), |
| 27 is_encrypted(false), | 27 aux_info_start_offset(-1), |
| 28 cenc_start_offset(-1), | 28 aux_info_default_size(-1), |
| 29 cenc_total_size(-1), | 29 aux_info_total_size(-1) { |
| 30 default_cenc_size(0) {} | 30 } |
| 31 | 31 |
| 32 TrackRunInfo::~TrackRunInfo() {} | 32 TrackRunInfo::~TrackRunInfo() {} |
| 33 | 33 |
| 34 TrackRunIterator::TrackRunIterator() : sample_offset_(0) {} | 34 TrackRunIterator::TrackRunIterator() : sample_offset_(0) {} |
| 35 TrackRunIterator::~TrackRunIterator() {} | 35 TrackRunIterator::~TrackRunIterator() {} |
| 36 | 36 |
| 37 static void PopulateSampleInfo(const Track& trak, | 37 static void PopulateSampleInfo(const Track& trak, |
| 38 const TrackExtends& trex, | 38 const TrackExtends& trex, |
| 39 const TrackFragmentHeader& tfhd, | 39 const TrackFragmentHeader& tfhd, |
| 40 const TrackFragmentRun& trun, | 40 const TrackFragmentRun& trun, |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 70 if (i < trun.sample_flags.size()) { | 70 if (i < trun.sample_flags.size()) { |
| 71 flags = trun.sample_flags[i]; | 71 flags = trun.sample_flags[i]; |
| 72 } else if (tfhd.has_default_sample_flags) { | 72 } else if (tfhd.has_default_sample_flags) { |
| 73 flags = tfhd.default_sample_flags; | 73 flags = tfhd.default_sample_flags; |
| 74 } else { | 74 } else { |
| 75 flags = trex.default_sample_flags; | 75 flags = trex.default_sample_flags; |
| 76 } | 76 } |
| 77 sample_info->is_keyframe = !(flags & kSampleIsDifferenceSampleFlagMask); | 77 sample_info->is_keyframe = !(flags & kSampleIsDifferenceSampleFlagMask); |
| 78 } | 78 } |
| 79 | 79 |
| 80 class CompareOffset { | 80 class CompareOffset { |
|
ddorwin
2012/06/26 06:09:19
This seems to compare specific types of offsets. I
ddorwin
2012/07/03 21:03:46
Ping.
strobe_
2012/07/13 00:47:07
Done.
| |
| 81 public: | 81 public: |
| 82 bool operator()(const TrackRunInfo& a, const TrackRunInfo& b) { | 82 bool operator()(const TrackRunInfo& a, const TrackRunInfo& b) { |
| 83 int64 a_min = a.sample_start_offset; | 83 int64 a_min = a.sample_start_offset; |
| 84 if (a.is_encrypted && a.cenc_start_offset < a_min) | 84 if (a.aux_info_total_size > 0 && a.aux_info_start_offset < a_min) |
|
ddorwin
2012/06/26 06:09:19
The .h comment seems to say is_encrypted and aux i
strobe_
2012/06/27 02:01:21
Hopefully addressed by renames.
| |
| 85 a_min = a.cenc_start_offset; | 85 a_min = a.aux_info_start_offset; |
| 86 int64 b_min = b.sample_start_offset; | 86 int64 b_min = b.sample_start_offset; |
| 87 if (b.is_encrypted && b.cenc_start_offset < b_min) | 87 if (b.aux_info_total_size > 0 && b.aux_info_start_offset < b_min) |
| 88 b_min = b.cenc_start_offset; | 88 b_min = b.aux_info_start_offset; |
| 89 return a_min < b_min; | 89 return a_min < b_min; |
| 90 } | 90 } |
| 91 }; | 91 }; |
| 92 | 92 |
| 93 bool TrackRunIterator::Init(const Movie& moov, const MovieFragment& moof) { | 93 bool TrackRunIterator::Init(const Movie& moov, const MovieFragment& moof) { |
| 94 runs_.clear(); | 94 runs_.clear(); |
| 95 | 95 |
| 96 for (size_t i = 0; i < moof.tracks.size(); i++) { | 96 for (size_t i = 0; i < moof.tracks.size(); i++) { |
| 97 const TrackFragment& traf = moof.tracks[i]; | 97 const TrackFragment& traf = moof.tracks[i]; |
| 98 | 98 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 114 trak->media.information.sample_table.description; | 114 trak->media.information.sample_table.description; |
| 115 if (stsd.type == kAudio) { | 115 if (stsd.type == kAudio) { |
| 116 sinf = &stsd.audio_entries[0].sinf; | 116 sinf = &stsd.audio_entries[0].sinf; |
| 117 } else if (stsd.type == kVideo) { | 117 } else if (stsd.type == kVideo) { |
| 118 sinf = &stsd.video_entries[0].sinf; | 118 sinf = &stsd.video_entries[0].sinf; |
| 119 } else { | 119 } else { |
| 120 DVLOG(1) << "Skipping unhandled track type"; | 120 DVLOG(1) << "Skipping unhandled track type"; |
| 121 continue; | 121 continue; |
| 122 } | 122 } |
| 123 | 123 |
| 124 if (sinf->info.track_encryption.is_encrypted) { | |
| 125 // TODO(strobe): CENC recovery and testing (http://crbug.com/132351) | |
| 126 DVLOG(1) << "Encrypted tracks not handled"; | |
| 127 continue; | |
| 128 } | |
| 129 | |
| 130 for (size_t j = 0; j < traf.runs.size(); j++) { | 124 for (size_t j = 0; j < traf.runs.size(); j++) { |
| 131 const TrackFragmentRun& trun = traf.runs[j]; | 125 const TrackFragmentRun& trun = traf.runs[j]; |
| 132 TrackRunInfo tri; | 126 TrackRunInfo tri; |
| 133 tri.track_id = traf.header.track_id; | 127 tri.track_id = traf.header.track_id; |
| 134 tri.start_dts = TimeDeltaFromFrac(traf.decode_time.decode_time, | 128 tri.start_dts = TimeDeltaFromFrac(traf.decode_time.decode_time, |
| 135 trak->media.header.timescale); | 129 trak->media.header.timescale); |
| 136 tri.sample_start_offset = trun.data_offset; | 130 tri.sample_start_offset = trun.data_offset; |
| 137 | 131 |
| 138 tri.is_encrypted = false; | 132 if (traf.auxiliary_offset.offsets.size() > j) { |
|
ddorwin
2012/06/26 06:09:19
What? How is an iterator related to some other col
strobe_
2012/06/27 02:01:21
Done.
| |
| 139 tri.cenc_start_offset = 0; | 133 RCHECK(traf.auxiliary_size.sample_count == trun.sample_count); |
|
ddorwin
2012/06/26 06:09:19
I have no idea what the rest of this code is doing
strobe_
2012/06/27 02:01:21
Done.
| |
| 140 tri.cenc_total_size = 0; | 134 tri.aux_info_start_offset = traf.auxiliary_offset.offsets[j]; |
| 141 tri.default_cenc_size = 0; | 135 tri.aux_info_default_size = |
| 136 traf.auxiliary_size.default_sample_info_size; | |
| 137 tri.aux_info_sizes = traf.auxiliary_size.sample_info_sizes; | |
| 138 if (tri.aux_info_default_size) { | |
| 139 tri.aux_info_total_size = | |
| 140 tri.aux_info_default_size * trun.sample_count; | |
| 141 } else { | |
| 142 tri.aux_info_total_size = 0; | |
| 143 for (size_t k = 0; k < trun.sample_count; k++) { | |
| 144 tri.aux_info_total_size += tri.aux_info_sizes[k]; | |
| 145 } | |
| 146 } | |
| 147 } else { | |
| 148 tri.aux_info_start_offset = 0; | |
| 149 tri.aux_info_total_size = 0; | |
| 150 } | |
| 151 tri.track_encryption = sinf->info.track_encryption; | |
| 142 | 152 |
| 143 tri.samples.resize(trun.sample_count); | 153 tri.samples.resize(trun.sample_count); |
| 144 | |
| 145 for (size_t k = 0; k < trun.sample_count; k++) { | 154 for (size_t k = 0; k < trun.sample_count; k++) { |
| 146 PopulateSampleInfo(*trak, *trex, traf.header, trun, k, &tri.samples[k]); | 155 PopulateSampleInfo(*trak, *trex, traf.header, trun, k, &tri.samples[k]); |
| 147 } | 156 } |
| 148 runs_.push_back(tri); | 157 runs_.push_back(tri); |
| 149 } | 158 } |
| 150 } | 159 } |
| 151 | 160 |
| 152 std::sort(runs_.begin(), runs_.end(), CompareOffset()); | 161 std::sort(runs_.begin(), runs_.end(), CompareOffset()); |
| 153 run_itr_ = runs_.begin(); | 162 run_itr_ = runs_.begin(); |
| 154 min_clear_offset_itr_ = min_clear_offsets_.begin(); | 163 min_clear_offset_itr_ = min_clear_offsets_.begin(); |
| 155 ResetRun(); | 164 ResetRun(); |
| 156 return true; | 165 return true; |
| 157 } | 166 } |
| 158 | 167 |
| 159 void TrackRunIterator::AdvanceRun() { | 168 void TrackRunIterator::AdvanceRun() { |
| 160 ++run_itr_; | 169 ++run_itr_; |
| 161 if (min_clear_offset_itr_ != min_clear_offsets_.end()) | 170 if (min_clear_offset_itr_ != min_clear_offsets_.end()) |
| 162 ++min_clear_offset_itr_; | 171 ++min_clear_offset_itr_; |
| 163 ResetRun(); | 172 ResetRun(); |
| 164 } | 173 } |
| 165 | 174 |
| 166 void TrackRunIterator::ResetRun() { | 175 void TrackRunIterator::ResetRun() { |
| 167 if (!RunValid()) return; | 176 if (!RunValid()) return; |
| 168 sample_dts_ = run_itr_->start_dts; | 177 sample_dts_ = run_itr_->start_dts; |
| 169 sample_offset_ = run_itr_->sample_start_offset; | 178 sample_offset_ = run_itr_->sample_start_offset; |
| 170 sample_itr_ = run_itr_->samples.begin(); | 179 sample_itr_ = run_itr_->samples.begin(); |
| 180 cenc_info_.clear(); | |
| 171 } | 181 } |
| 172 | 182 |
| 173 void TrackRunIterator::AdvanceSample() { | 183 void TrackRunIterator::AdvanceSample() { |
| 174 DCHECK(SampleValid()); | 184 DCHECK(SampleValid()); |
| 175 sample_dts_ += sample_itr_->duration; | 185 sample_dts_ += sample_itr_->duration; |
| 176 sample_offset_ += sample_itr_->size; | 186 sample_offset_ += sample_itr_->size; |
| 177 ++sample_itr_; | 187 ++sample_itr_; |
| 178 } | 188 } |
| 179 | 189 |
| 180 bool TrackRunIterator::NeedsCENC() { | 190 bool TrackRunIterator::AuxInfoRequired() { |
|
ddorwin
2012/06/26 06:09:19
What does this function really mean? "Need to read
strobe_
2012/06/27 02:01:21
Hopefully addressed by rename.
| |
| 181 CHECK(!is_encrypted()) << "TODO(strobe): Implement CENC."; | 191 DCHECK(RunValid()); |
| 182 return is_encrypted(); | 192 // The only kind of aux info that we handle is CENC info, so we only cache it |
|
ddorwin
2012/06/26 06:09:19
Seems like a function-level comment.
strobe_
2012/06/27 02:01:21
Done.
| |
| 193 // if encryption is active | |
| 194 return is_encrypted() && aux_info_size() > 0 && cenc_info_.size() == 0; | |
| 183 } | 195 } |
| 184 | 196 |
| 185 bool TrackRunIterator::CacheCENC(const uint8* buf, int size) { | 197 bool TrackRunIterator::CacheAuxInfo(const uint8* buf, int size) { |
|
ddorwin
2012/06/26 06:09:19
buf_size would make this easier to read.
ddorwin
2012/06/26 06:09:19
Add a function-level comment that this only caches
strobe_
2012/06/27 02:01:21
Done.
strobe_
2012/06/27 02:01:21
Done.
| |
| 186 LOG(FATAL) << "Not implemented"; | 198 RCHECK(AuxInfoRequired() && size >= aux_info_size()); |
|
ddorwin
2012/06/26 06:09:19
Nothing in the loop below verifies that we don't g
strobe_
2012/06/27 02:01:21
As noted below, the RCHECK() does this.
| |
| 187 return false; | 199 |
| 200 cenc_info_.resize(run_itr_->samples.size()); | |
| 201 int64 pos = 0; | |
| 202 for (size_t i = 0; i < run_itr_->samples.size(); i++) { | |
| 203 int info_size = run_itr_->aux_info_default_size; | |
| 204 if (!info_size) | |
| 205 info_size = run_itr_->aux_info_sizes[i]; | |
| 206 | |
| 207 BufferReader reader(buf + pos, info_size); | |
| 208 RCHECK(cenc_info_[i].Parse(run_itr_->track_encryption.default_iv_size, | |
|
ddorwin
2012/06/26 06:09:19
does something verify we don't exceed info_size (p
strobe_
2012/06/27 02:01:21
Yes, if a request would read past the given size,
ddorwin
2012/07/03 21:03:46
We should have tests for this.
| |
| 209 &reader)); | |
| 210 pos += info_size; | |
| 211 } | |
| 212 | |
| 213 return true; | |
| 188 } | 214 } |
| 189 | 215 |
| 190 bool TrackRunIterator::RunValid() const { | 216 bool TrackRunIterator::RunValid() const { |
| 191 return run_itr_ != runs_.end(); | 217 return run_itr_ != runs_.end(); |
| 192 } | 218 } |
| 193 | 219 |
| 194 bool TrackRunIterator::SampleValid() const { | 220 bool TrackRunIterator::SampleValid() const { |
| 195 return RunValid() && (sample_itr_ != run_itr_->samples.end()); | 221 return RunValid() && (sample_itr_ != run_itr_->samples.end()); |
| 196 } | 222 } |
| 197 | 223 |
| 198 int64 TrackRunIterator::GetMaxClearOffset() { | 224 int64 TrackRunIterator::GetMaxClearOffset() { |
| 199 int64 offset = kint64max; | 225 int64 offset = kint64max; |
| 200 | 226 |
| 201 if (SampleValid()) { | 227 if (SampleValid()) { |
| 202 offset = std::min(offset, sample_offset_); | 228 offset = std::min(offset, sample_offset_); |
| 203 if (NeedsCENC()) { | 229 if (AuxInfoRequired()) { |
| 204 offset = std::min(offset, cenc_offset()); | 230 offset = std::min(offset, aux_info_offset()); |
| 205 } | 231 } |
| 206 } | 232 } |
| 207 if (min_clear_offset_itr_ != min_clear_offsets_.end()) { | 233 if (min_clear_offset_itr_ != min_clear_offsets_.end()) { |
| 208 offset = std::min(offset, *min_clear_offset_itr_); | 234 offset = std::min(offset, *min_clear_offset_itr_); |
| 209 } | 235 } |
| 210 if (offset == kint64max) return 0; | 236 if (offset == kint64max) return 0; |
| 211 return offset; | 237 return offset; |
| 212 } | 238 } |
| 213 | 239 |
| 214 TimeDelta TrackRunIterator::GetMinDecodeTimestamp() { | 240 TimeDelta TrackRunIterator::GetMinDecodeTimestamp() { |
| 215 TimeDelta dts = kInfiniteDuration(); | 241 TimeDelta dts = kInfiniteDuration(); |
| 216 for (size_t i = 0; i < runs_.size(); i++) { | 242 for (size_t i = 0; i < runs_.size(); i++) { |
| 217 if (runs_[i].start_dts < dts) | 243 if (runs_[i].start_dts < dts) |
| 218 dts = runs_[i].start_dts; | 244 dts = runs_[i].start_dts; |
| 219 } | 245 } |
| 220 return dts; | 246 return dts; |
| 221 } | 247 } |
| 222 | 248 |
| 223 uint32 TrackRunIterator::track_id() const { | 249 uint32 TrackRunIterator::track_id() const { |
| 224 DCHECK(RunValid()); | 250 DCHECK(RunValid()); |
| 225 return run_itr_->track_id; | 251 return run_itr_->track_id; |
| 226 } | 252 } |
| 227 | 253 |
| 228 bool TrackRunIterator::is_encrypted() const { | 254 bool TrackRunIterator::is_encrypted() const { |
| 229 DCHECK(RunValid()); | 255 DCHECK(RunValid()); |
| 230 return run_itr_->is_encrypted; | 256 return run_itr_->track_encryption.is_encrypted; |
| 231 } | 257 } |
| 232 | 258 |
| 233 int64 TrackRunIterator::cenc_offset() const { | 259 int64 TrackRunIterator::aux_info_offset() const { |
| 234 DCHECK(is_encrypted()); | 260 return run_itr_->aux_info_start_offset; |
| 235 return run_itr_->cenc_start_offset; | |
| 236 } | 261 } |
| 237 | 262 |
| 238 int TrackRunIterator::cenc_size() const { | 263 int TrackRunIterator::aux_info_size() const { |
| 239 DCHECK(is_encrypted()); | 264 return run_itr_->aux_info_total_size; |
| 240 return run_itr_->cenc_total_size; | |
| 241 } | 265 } |
| 242 | 266 |
| 243 int64 TrackRunIterator::offset() const { | 267 int64 TrackRunIterator::offset() const { |
| 244 DCHECK(SampleValid()); | 268 DCHECK(SampleValid()); |
| 245 return sample_offset_; | 269 return sample_offset_; |
| 246 } | 270 } |
| 247 | 271 |
| 248 int TrackRunIterator::size() const { | 272 int TrackRunIterator::size() const { |
| 249 DCHECK(SampleValid()); | 273 DCHECK(SampleValid()); |
| 250 return sample_itr_->size; | 274 return sample_itr_->size; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 263 TimeDelta TrackRunIterator::duration() const { | 287 TimeDelta TrackRunIterator::duration() const { |
| 264 DCHECK(SampleValid()); | 288 DCHECK(SampleValid()); |
| 265 return sample_itr_->duration; | 289 return sample_itr_->duration; |
| 266 } | 290 } |
| 267 | 291 |
| 268 bool TrackRunIterator::is_keyframe() const { | 292 bool TrackRunIterator::is_keyframe() const { |
| 269 DCHECK(SampleValid()); | 293 DCHECK(SampleValid()); |
| 270 return sample_itr_->is_keyframe; | 294 return sample_itr_->is_keyframe; |
| 271 } | 295 } |
| 272 | 296 |
| 273 const FrameCENCInfo& TrackRunIterator::frame_cenc_info() { | 297 scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() { |
|
ddorwin
2012/06/26 06:09:19
What tests do we have for this? :)
strobe_
2012/06/27 02:01:21
Yeah, this whole class needs a big ol' test. On sk
| |
| 274 DCHECK(is_encrypted()); | 298 const FrameCENCInfo& cenc_info = |
| 275 return frame_cenc_info_; | 299 cenc_info_[sample_itr_ - run_itr_->samples.begin()]; |
|
ddorwin
2012/06/26 06:09:19
What is this math doing?
strobe_
2012/06/27 02:01:21
Retrieves the index of the sample to which sample_
ddorwin
2012/07/03 21:03:46
Since it's already two lines, an index temp variab
strobe_
2012/07/13 00:47:07
Done.
| |
| 300 DCHECK(is_encrypted() && !AuxInfoRequired()); | |
| 301 | |
| 302 if (!cenc_info.subsamples.empty() && | |
| 303 (cenc_info.GetTotalSize() != static_cast<size_t>(size()))) { | |
|
ddorwin
2012/06/26 06:09:19
It's unclear how these are related.
strobe_
2012/06/27 02:01:21
Hopefully renames make this clearer.
| |
| 304 DVLOG(1) << "Incorrect CENC subsample size."; | |
| 305 return scoped_ptr<DecryptConfig>(); | |
| 306 } | |
| 307 | |
| 308 return scoped_ptr<DecryptConfig>(new DecryptConfig( | |
| 309 &run_itr_->track_encryption.default_kid[0], | |
|
ddorwin
2012/06/26 06:09:19
Does this code not support sample groups?
strobe_
2012/06/27 02:01:21
Correct, it does not. The restricted BMFF format u
ddorwin
2012/07/03 21:03:46
We should document this somewhere.
strobe_
2012/07/13 00:47:07
Expanded comment in mp4/box_definitions.h.
| |
| 310 run_itr_->track_encryption.default_kid.size(), | |
| 311 cenc_info.iv, sizeof(cenc_info.iv), | |
|
ddorwin
2012/06/26 06:09:19
iv is a fixed size now, so you'l always send 16 bu
strobe_
2012/06/27 02:01:21
According to the CENC spec, an 8-byte IV shall be
| |
| 312 &cenc_info.subsamples[0], cenc_info.subsamples.size())); | |
| 276 } | 313 } |
| 277 | 314 |
| 278 } // namespace mp4 | 315 } // namespace mp4 |
| 279 } // namespace media | 316 } // namespace media |
| OLD | NEW |