Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1121)

Side by Side Diff: media/mp4/track_run_iterator.cc

Issue 10651006: Add Common Encryption support to BMFF, including subsample decryption. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Remove references to non-public encrypted files in tests Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
OLDNEW
« media/mp4/track_run_iterator.h ('K') | « media/mp4/track_run_iterator.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698