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