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 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 { |
| 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) |
| 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 // Collect information from the auxiliary_offset entry with the same index |
| 139 tri.cenc_start_offset = 0; | 133 // in the 'saiz' container as the current run's index in the 'trun' |
| 140 tri.cenc_total_size = 0; | 134 // container, if it is present |
|
xhwang
2012/06/27 19:37:43
Add period "." at the end.
strobe_
2012/07/13 00:47:07
Done.
| |
| 141 tri.default_cenc_size = 0; | 135 if (traf.auxiliary_offset.offsets.size() > j) { |
| 136 // There should be an auxiliary info entry corresponding to each sample | |
| 137 // in the auxiliary offset entry's corresponding track run | |
| 138 RCHECK(traf.auxiliary_size.sample_count == trun.sample_count); | |
| 139 tri.aux_info_start_offset = traf.auxiliary_offset.offsets[j]; | |
| 140 tri.aux_info_default_size = | |
| 141 traf.auxiliary_size.default_sample_info_size; | |
| 142 tri.aux_info_sizes = traf.auxiliary_size.sample_info_sizes; | |
| 143 | |
| 144 // If the default info size is positive, find the total size of the aux | |
| 145 // info block from it, otherwise sum over the individual sizes of each | |
| 146 // aux info entry in the aux_offset entry | |
| 147 if (tri.aux_info_default_size) { | |
| 148 tri.aux_info_total_size = | |
| 149 tri.aux_info_default_size * trun.sample_count; | |
| 150 } else { | |
| 151 tri.aux_info_total_size = 0; | |
| 152 for (size_t k = 0; k < trun.sample_count; k++) { | |
| 153 tri.aux_info_total_size += tri.aux_info_sizes[k]; | |
| 154 } | |
| 155 } | |
| 156 } else { | |
| 157 tri.aux_info_start_offset = 0; | |
| 158 tri.aux_info_total_size = 0; | |
| 159 } | |
| 160 tri.track_encryption = sinf->info.track_encryption; | |
| 142 | 161 |
| 143 tri.samples.resize(trun.sample_count); | 162 tri.samples.resize(trun.sample_count); |
| 144 | |
| 145 for (size_t k = 0; k < trun.sample_count; k++) { | 163 for (size_t k = 0; k < trun.sample_count; k++) { |
| 146 PopulateSampleInfo(*trak, *trex, traf.header, trun, k, &tri.samples[k]); | 164 PopulateSampleInfo(*trak, *trex, traf.header, trun, k, &tri.samples[k]); |
| 147 } | 165 } |
| 148 runs_.push_back(tri); | 166 runs_.push_back(tri); |
| 149 } | 167 } |
| 150 } | 168 } |
| 151 | 169 |
| 152 std::sort(runs_.begin(), runs_.end(), CompareOffset()); | 170 std::sort(runs_.begin(), runs_.end(), CompareOffset()); |
| 153 run_itr_ = runs_.begin(); | 171 run_itr_ = runs_.begin(); |
| 154 min_clear_offset_itr_ = min_clear_offsets_.begin(); | 172 min_clear_offset_itr_ = min_clear_offsets_.begin(); |
| 155 ResetRun(); | 173 ResetRun(); |
| 156 return true; | 174 return true; |
| 157 } | 175 } |
| 158 | 176 |
| 159 void TrackRunIterator::AdvanceRun() { | 177 void TrackRunIterator::AdvanceRun() { |
| 160 ++run_itr_; | 178 ++run_itr_; |
| 161 if (min_clear_offset_itr_ != min_clear_offsets_.end()) | 179 if (min_clear_offset_itr_ != min_clear_offsets_.end()) |
| 162 ++min_clear_offset_itr_; | 180 ++min_clear_offset_itr_; |
| 163 ResetRun(); | 181 ResetRun(); |
| 164 } | 182 } |
| 165 | 183 |
| 166 void TrackRunIterator::ResetRun() { | 184 void TrackRunIterator::ResetRun() { |
| 167 if (!RunValid()) return; | 185 if (!RunValid()) return; |
| 168 sample_dts_ = run_itr_->start_dts; | 186 sample_dts_ = run_itr_->start_dts; |
| 169 sample_offset_ = run_itr_->sample_start_offset; | 187 sample_offset_ = run_itr_->sample_start_offset; |
| 170 sample_itr_ = run_itr_->samples.begin(); | 188 sample_itr_ = run_itr_->samples.begin(); |
| 189 cenc_info_.clear(); | |
| 171 } | 190 } |
| 172 | 191 |
| 173 void TrackRunIterator::AdvanceSample() { | 192 void TrackRunIterator::AdvanceSample() { |
| 174 DCHECK(SampleValid()); | 193 DCHECK(SampleValid()); |
| 175 sample_dts_ += sample_itr_->duration; | 194 sample_dts_ += sample_itr_->duration; |
| 176 sample_offset_ += sample_itr_->size; | 195 sample_offset_ += sample_itr_->size; |
| 177 ++sample_itr_; | 196 ++sample_itr_; |
| 178 } | 197 } |
| 179 | 198 |
| 180 bool TrackRunIterator::NeedsCENC() { | 199 // This implementation only indicates a need for caching if CENC-style auxiliary |
| 181 CHECK(!is_encrypted()) << "TODO(strobe): Implement CENC."; | 200 // info is available in the stream. |
| 182 return is_encrypted(); | 201 bool TrackRunIterator::AuxInfoNeedsToBeCached() { |
| 202 DCHECK(RunValid()); | |
| 203 return is_encrypted() && aux_info_size() > 0 && cenc_info_.size() == 0; | |
| 183 } | 204 } |
| 184 | 205 |
| 185 bool TrackRunIterator::CacheCENC(const uint8* buf, int size) { | 206 // This implementation currently only caches CENC auxiliary info. |
|
ddorwin
2012/07/03 21:03:47
"CENC-style" to be consistent with 199?
strobe_
2012/07/13 00:47:07
Done.
| |
| 186 LOG(FATAL) << "Not implemented"; | 207 bool TrackRunIterator::CacheAuxInfo(const uint8* buf, int buf_size) { |
| 187 return false; | 208 RCHECK(AuxInfoNeedsToBeCached() && buf_size >= aux_info_size()); |
| 209 | |
| 210 cenc_info_.resize(run_itr_->samples.size()); | |
| 211 int64 pos = 0; | |
| 212 for (size_t i = 0; i < run_itr_->samples.size(); i++) { | |
| 213 int info_size = run_itr_->aux_info_default_size; | |
| 214 if (!info_size) | |
| 215 info_size = run_itr_->aux_info_sizes[i]; | |
| 216 | |
| 217 BufferReader reader(buf + pos, info_size); | |
| 218 RCHECK(cenc_info_[i].Parse(run_itr_->track_encryption.default_iv_size, | |
| 219 &reader)); | |
| 220 pos += info_size; | |
| 221 } | |
| 222 | |
| 223 return true; | |
| 188 } | 224 } |
| 189 | 225 |
| 190 bool TrackRunIterator::RunValid() const { | 226 bool TrackRunIterator::RunValid() const { |
| 191 return run_itr_ != runs_.end(); | 227 return run_itr_ != runs_.end(); |
| 192 } | 228 } |
| 193 | 229 |
| 194 bool TrackRunIterator::SampleValid() const { | 230 bool TrackRunIterator::SampleValid() const { |
| 195 return RunValid() && (sample_itr_ != run_itr_->samples.end()); | 231 return RunValid() && (sample_itr_ != run_itr_->samples.end()); |
| 196 } | 232 } |
| 197 | 233 |
| 198 int64 TrackRunIterator::GetMaxClearOffset() { | 234 int64 TrackRunIterator::GetMaxClearOffset() { |
| 199 int64 offset = kint64max; | 235 int64 offset = kint64max; |
| 200 | 236 |
| 201 if (SampleValid()) { | 237 if (SampleValid()) { |
| 202 offset = std::min(offset, sample_offset_); | 238 offset = std::min(offset, sample_offset_); |
| 203 if (NeedsCENC()) { | 239 if (AuxInfoNeedsToBeCached()) { |
| 204 offset = std::min(offset, cenc_offset()); | 240 offset = std::min(offset, aux_info_offset()); |
| 205 } | 241 } |
| 206 } | 242 } |
| 207 if (min_clear_offset_itr_ != min_clear_offsets_.end()) { | 243 if (min_clear_offset_itr_ != min_clear_offsets_.end()) { |
| 208 offset = std::min(offset, *min_clear_offset_itr_); | 244 offset = std::min(offset, *min_clear_offset_itr_); |
| 209 } | 245 } |
| 210 if (offset == kint64max) return 0; | 246 if (offset == kint64max) return 0; |
| 211 return offset; | 247 return offset; |
| 212 } | 248 } |
| 213 | 249 |
| 214 TimeDelta TrackRunIterator::GetMinDecodeTimestamp() { | 250 TimeDelta TrackRunIterator::GetMinDecodeTimestamp() { |
| 215 TimeDelta dts = kInfiniteDuration(); | 251 TimeDelta dts = kInfiniteDuration(); |
| 216 for (size_t i = 0; i < runs_.size(); i++) { | 252 for (size_t i = 0; i < runs_.size(); i++) { |
| 217 if (runs_[i].start_dts < dts) | 253 if (runs_[i].start_dts < dts) |
| 218 dts = runs_[i].start_dts; | 254 dts = runs_[i].start_dts; |
| 219 } | 255 } |
| 220 return dts; | 256 return dts; |
| 221 } | 257 } |
| 222 | 258 |
| 223 uint32 TrackRunIterator::track_id() const { | 259 uint32 TrackRunIterator::track_id() const { |
| 224 DCHECK(RunValid()); | 260 DCHECK(RunValid()); |
| 225 return run_itr_->track_id; | 261 return run_itr_->track_id; |
| 226 } | 262 } |
| 227 | 263 |
| 228 bool TrackRunIterator::is_encrypted() const { | 264 bool TrackRunIterator::is_encrypted() const { |
| 229 DCHECK(RunValid()); | 265 DCHECK(RunValid()); |
| 230 return run_itr_->is_encrypted; | 266 return run_itr_->track_encryption.is_encrypted; |
| 231 } | 267 } |
| 232 | 268 |
| 233 int64 TrackRunIterator::cenc_offset() const { | 269 int64 TrackRunIterator::aux_info_offset() const { |
| 234 DCHECK(is_encrypted()); | 270 return run_itr_->aux_info_start_offset; |
| 235 return run_itr_->cenc_start_offset; | |
| 236 } | 271 } |
| 237 | 272 |
| 238 int TrackRunIterator::cenc_size() const { | 273 int TrackRunIterator::aux_info_size() const { |
| 239 DCHECK(is_encrypted()); | 274 return run_itr_->aux_info_total_size; |
| 240 return run_itr_->cenc_total_size; | |
| 241 } | 275 } |
| 242 | 276 |
| 243 int64 TrackRunIterator::offset() const { | 277 int64 TrackRunIterator::sample_offset() const { |
| 244 DCHECK(SampleValid()); | 278 DCHECK(SampleValid()); |
| 245 return sample_offset_; | 279 return sample_offset_; |
| 246 } | 280 } |
| 247 | 281 |
| 248 int TrackRunIterator::size() const { | 282 int TrackRunIterator::sample_size() const { |
| 249 DCHECK(SampleValid()); | 283 DCHECK(SampleValid()); |
| 250 return sample_itr_->size; | 284 return sample_itr_->size; |
| 251 } | 285 } |
| 252 | 286 |
| 253 TimeDelta TrackRunIterator::dts() const { | 287 TimeDelta TrackRunIterator::dts() const { |
| 254 DCHECK(SampleValid()); | 288 DCHECK(SampleValid()); |
| 255 return sample_dts_; | 289 return sample_dts_; |
| 256 } | 290 } |
| 257 | 291 |
| 258 TimeDelta TrackRunIterator::cts() const { | 292 TimeDelta TrackRunIterator::cts() const { |
| 259 DCHECK(SampleValid()); | 293 DCHECK(SampleValid()); |
| 260 return sample_dts_ + sample_itr_->cts_offset; | 294 return sample_dts_ + sample_itr_->cts_offset; |
| 261 } | 295 } |
| 262 | 296 |
| 263 TimeDelta TrackRunIterator::duration() const { | 297 TimeDelta TrackRunIterator::duration() const { |
| 264 DCHECK(SampleValid()); | 298 DCHECK(SampleValid()); |
| 265 return sample_itr_->duration; | 299 return sample_itr_->duration; |
| 266 } | 300 } |
| 267 | 301 |
| 268 bool TrackRunIterator::is_keyframe() const { | 302 bool TrackRunIterator::is_keyframe() const { |
| 269 DCHECK(SampleValid()); | 303 DCHECK(SampleValid()); |
| 270 return sample_itr_->is_keyframe; | 304 return sample_itr_->is_keyframe; |
| 271 } | 305 } |
| 272 | 306 |
| 273 const FrameCENCInfo& TrackRunIterator::frame_cenc_info() { | 307 scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() { |
| 274 DCHECK(is_encrypted()); | 308 const FrameCENCInfo& cenc_info = |
| 275 return frame_cenc_info_; | 309 cenc_info_[sample_itr_ - run_itr_->samples.begin()]; |
| 310 DCHECK(is_encrypted() && !AuxInfoNeedsToBeCached()); | |
| 311 | |
| 312 if (!cenc_info.subsamples.empty() && | |
| 313 (cenc_info.GetTotalSizeOfSubsamples() != | |
| 314 static_cast<size_t>(sample_size()))) { | |
| 315 DVLOG(1) << "Incorrect CENC subsample size."; | |
| 316 return scoped_ptr<DecryptConfig>(); | |
| 317 } | |
| 318 | |
| 319 return scoped_ptr<DecryptConfig>(new DecryptConfig( | |
| 320 &run_itr_->track_encryption.default_kid[0], | |
| 321 run_itr_->track_encryption.default_kid.size(), | |
| 322 cenc_info.iv, sizeof(cenc_info.iv), | |
| 323 &cenc_info.subsamples[0], cenc_info.subsamples.size())); | |
| 276 } | 324 } |
| 277 | 325 |
| 278 } // namespace mp4 | 326 } // namespace mp4 |
| 279 } // namespace media | 327 } // namespace media |
| OLD | NEW |