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