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 |