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

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: Add wrong subsample size test Created 8 years, 5 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, int64 denom) { 15 struct SampleInfo {
16 int size;
17 int duration;
18 int cts_offset;
19 bool is_keyframe;
20 };
21
22 struct TrackRunInfo {
23 uint32 track_id;
24 std::vector<SampleInfo> samples;
25 int64 timescale;
26 int64 start_dts;
27 int64 sample_start_offset;
28
29 int64 aux_info_start_offset; // Only valid if aux_info_total_size > 0.
30 int aux_info_default_size;
31 std::vector<uint8> aux_info_sizes; // Present if default_size == 0.
ddorwin 2012/07/17 01:14:21 Present => Populated or Non-empty
strobe_ 2012/07/19 02:43:35 Done.
32 int aux_info_total_size;
33
34 TrackEncryption track_encryption;
35
36 TrackRunInfo();
37 ~TrackRunInfo();
38 };
39
40 TrackRunInfo::TrackRunInfo()
41 : track_id(0),
42 timescale(-1),
43 start_dts(-1),
44 sample_start_offset(-1),
45 aux_info_start_offset(-1),
46 aux_info_default_size(-1),
47 aux_info_total_size(-1) {
48 }
49 TrackRunInfo::~TrackRunInfo() {}
50
51 TimeDelta TimeDeltaFromFrac(int64 numer, int64 denom) {
ddorwin 2012/07/17 01:14:21 Function and var names should usually be complete
ddorwin 2012/07/17 01:14:21 What does "FromFrac" mean? Isn't it really convert
strobe_ 2012/07/19 02:43:35 The time base is referred to as the denominator el
16 DCHECK_LT((numer > 0 ? numer : -numer), 52 DCHECK_LT((numer > 0 ? numer : -numer),
ddorwin 2012/07/17 01:14:21 use abs()?
strobe_ 2012/07/19 02:43:35 Nope, breaks build on cros-tegra2.
17 kint64max / base::Time::kMicrosecondsPerSecond); 53 kint64max / base::Time::kMicrosecondsPerSecond);
18 return base::TimeDelta::FromMicroseconds( 54 return TimeDelta::FromMicroseconds(
19 base::Time::kMicrosecondsPerSecond * numer / denom); 55 base::Time::kMicrosecondsPerSecond * numer / denom);
20 } 56 }
21 57
22 static const uint32 kSampleIsDifferenceSampleFlagMask = 0x10000; 58 static const uint32 kSampleIsDifferenceSampleFlagMask = 0x10000;
ddorwin 2012/07/17 01:14:21 file constant should be at the top of the file in
strobe_ 2012/07/19 02:43:35 Done.
23 59
24 TrackRunInfo::TrackRunInfo()
25 : track_id(0),
26 sample_start_offset(-1),
27 is_encrypted(false),
28 cenc_start_offset(-1),
29 cenc_total_size(-1),
30 default_cenc_size(0) {}
31
32 TrackRunInfo::~TrackRunInfo() {}
33 60
34 TrackRunIterator::TrackRunIterator() : sample_offset_(0) {} 61 TrackRunIterator::TrackRunIterator() : sample_offset_(0) {}
35 TrackRunIterator::~TrackRunIterator() {} 62 TrackRunIterator::~TrackRunIterator() {}
36 63
37 static void PopulateSampleInfo(const Track& trak, 64 static void PopulateSampleInfo(const TrackExtends& trex,
38 const TrackExtends& trex,
39 const TrackFragmentHeader& tfhd, 65 const TrackFragmentHeader& tfhd,
40 const TrackFragmentRun& trun, 66 const TrackFragmentRun& trun,
41 const uint32 i, 67 const uint32 i,
42 SampleInfo* sample_info) { 68 SampleInfo* sample_info) {
43 if (i < trun.sample_sizes.size()) { 69 if (i < trun.sample_sizes.size()) {
44 sample_info->size = trun.sample_sizes[i]; 70 sample_info->size = trun.sample_sizes[i];
45 } else if (tfhd.default_sample_size > 0) { 71 } else if (tfhd.default_sample_size > 0) {
46 sample_info->size = tfhd.default_sample_size; 72 sample_info->size = tfhd.default_sample_size;
47 } else { 73 } else {
48 sample_info->size = trex.default_sample_size; 74 sample_info->size = trex.default_sample_size;
49 } 75 }
50 76
51 const uint64 timescale = trak.media.header.timescale;
52 uint64 duration;
53 if (i < trun.sample_durations.size()) { 77 if (i < trun.sample_durations.size()) {
54 duration = trun.sample_durations[i]; 78 sample_info->duration = trun.sample_durations[i];
55 } else if (tfhd.default_sample_duration > 0) { 79 } else if (tfhd.default_sample_duration > 0) {
56 duration = tfhd.default_sample_duration; 80 sample_info->duration = tfhd.default_sample_duration;
57 } else { 81 } else {
58 duration = trex.default_sample_duration; 82 sample_info->duration = trex.default_sample_duration;
59 } 83 }
60 sample_info->duration = TimeDeltaFromFrac(duration, timescale);
61 84
62 if (i < trun.sample_composition_time_offsets.size()) { 85 if (i < trun.sample_composition_time_offsets.size()) {
63 sample_info->cts_offset = 86 sample_info->cts_offset = trun.sample_composition_time_offsets[i];
64 TimeDeltaFromFrac(trun.sample_composition_time_offsets[i], timescale);
65 } else { 87 } else {
66 sample_info->cts_offset = TimeDelta::FromMicroseconds(0); 88 sample_info->cts_offset = 0;
67 } 89 }
68 90
69 uint32 flags; 91 uint32 flags;
70 if (i < trun.sample_flags.size()) { 92 if (i < trun.sample_flags.size()) {
71 flags = trun.sample_flags[i]; 93 flags = trun.sample_flags[i];
72 } else if (tfhd.has_default_sample_flags) { 94 } else if (tfhd.has_default_sample_flags) {
73 flags = tfhd.default_sample_flags; 95 flags = tfhd.default_sample_flags;
74 } else { 96 } else {
75 flags = trex.default_sample_flags; 97 flags = trex.default_sample_flags;
76 } 98 }
77 sample_info->is_keyframe = !(flags & kSampleIsDifferenceSampleFlagMask); 99 sample_info->is_keyframe = !(flags & kSampleIsDifferenceSampleFlagMask);
78 } 100 }
79 101
80 class CompareOffset { 102 class CompareMinTrackRunDataOffset {
ddorwin 2012/07/17 01:14:21 What does this do?
strobe_ 2012/07/19 02:43:35 Comment added.
81 public: 103 public:
82 bool operator()(const TrackRunInfo& a, const TrackRunInfo& b) { 104 bool operator()(const TrackRunInfo& a, const TrackRunInfo& b) {
83 int64 a_min = a.sample_start_offset; 105 int64 a_aux = a.aux_info_total_size ? a.aux_info_start_offset : kint64max;
84 if (a.is_encrypted && a.cenc_start_offset < a_min) 106 int64 b_aux = b.aux_info_total_size ? b.aux_info_start_offset : kint64max;
85 a_min = a.cenc_start_offset; 107
86 int64 b_min = b.sample_start_offset; 108 int64 a_lesser = std::min(a_aux, a.sample_start_offset);
87 if (b.is_encrypted && b.cenc_start_offset < b_min) 109 int64 a_greater = std::max(a_aux, a.sample_start_offset);
88 b_min = b.cenc_start_offset; 110 int64 b_lesser = std::min(b_aux, b.sample_start_offset);
89 return a_min < b_min; 111 int64 b_greater = std::max(b_aux, b.sample_start_offset);
112
113 if (a_lesser == b_lesser) return a_greater < b_greater;
114 return a_lesser < b_lesser;
90 } 115 }
91 }; 116 };
92 117
93 bool TrackRunIterator::Init(const Movie& moov, const MovieFragment& moof) { 118 bool TrackRunIterator::Init(const Movie& moov, const MovieFragment& moof) {
94 runs_.clear(); 119 runs_.clear();
95 120
96 for (size_t i = 0; i < moof.tracks.size(); i++) { 121 for (size_t i = 0; i < moof.tracks.size(); i++) {
97 const TrackFragment& traf = moof.tracks[i]; 122 const TrackFragment& traf = moof.tracks[i];
98 123
99 const Track* trak = NULL; 124 const Track* trak = NULL;
100 for (size_t t = 0; t < moov.tracks.size(); t++) { 125 for (size_t t = 0; t < moov.tracks.size(); t++) {
101 if (moov.tracks[t].header.track_id == traf.header.track_id) 126 if (moov.tracks[t].header.track_id == traf.header.track_id)
102 trak = &moov.tracks[t]; 127 trak = &moov.tracks[t];
103 } 128 }
129 RCHECK(trak);
104 130
105 const TrackExtends* trex = NULL; 131 const TrackExtends* trex = NULL;
106 for (size_t t = 0; t < moov.extends.tracks.size(); t++) { 132 for (size_t t = 0; t < moov.extends.tracks.size(); t++) {
107 if (moov.extends.tracks[t].track_id == traf.header.track_id) 133 if (moov.extends.tracks[t].track_id == traf.header.track_id)
108 trex = &moov.extends.tracks[t]; 134 trex = &moov.extends.tracks[t];
109 } 135 }
110 RCHECK(trak && trex); 136 RCHECK(trex);
111 137
138 // TODO(strobe): Support multiple sample description entries
112 const ProtectionSchemeInfo* sinf = NULL; 139 const ProtectionSchemeInfo* sinf = NULL;
113 const SampleDescription& stsd = 140 const SampleDescription& stsd =
114 trak->media.information.sample_table.description; 141 trak->media.information.sample_table.description;
115 if (stsd.type == kAudio) { 142 if (stsd.type == kAudio) {
116 sinf = &stsd.audio_entries[0].sinf; 143 sinf = &stsd.audio_entries[0].sinf;
117 } else if (stsd.type == kVideo) { 144 } else if (stsd.type == kVideo) {
118 sinf = &stsd.video_entries[0].sinf; 145 sinf = &stsd.video_entries[0].sinf;
119 } else { 146 } else {
120 DVLOG(1) << "Skipping unhandled track type"; 147 DVLOG(1) << "Skipping unhandled track type";
121 continue; 148 continue;
122 } 149 }
123 150
124 if (sinf->info.track_encryption.is_encrypted) { 151 int64 run_start_dts = traf.decode_time.decode_time;
125 // TODO(strobe): CENC recovery and testing (http://crbug.com/132351)
126 DVLOG(1) << "Encrypted tracks not handled";
127 continue;
128 }
129 152
130 for (size_t j = 0; j < traf.runs.size(); j++) { 153 for (size_t j = 0; j < traf.runs.size(); j++) {
131 const TrackFragmentRun& trun = traf.runs[j]; 154 const TrackFragmentRun& trun = traf.runs[j];
132 TrackRunInfo tri; 155 TrackRunInfo tri;
133 tri.track_id = traf.header.track_id; 156 tri.track_id = traf.header.track_id;
134 tri.start_dts = TimeDeltaFromFrac(traf.decode_time.decode_time, 157 tri.timescale = trak->media.header.timescale;
135 trak->media.header.timescale); 158 tri.start_dts = run_start_dts;
136 tri.sample_start_offset = trun.data_offset; 159 tri.sample_start_offset = trun.data_offset;
137 160
138 tri.is_encrypted = false; 161 // Collect information from the auxiliary_offset entry with the same index
139 tri.cenc_start_offset = 0; 162 // in the 'saiz' container as the current run's index in the 'trun'
140 tri.cenc_total_size = 0; 163 // container, if it is present.
141 tri.default_cenc_size = 0; 164 if (traf.auxiliary_offset.offsets.size() > j) {
165 // There should be an auxiliary info entry corresponding to each sample
166 // in the auxiliary offset entry's corresponding track run.
167 RCHECK(traf.auxiliary_size.sample_count == trun.sample_count);
168 tri.aux_info_start_offset = traf.auxiliary_offset.offsets[j];
169 tri.aux_info_default_size =
170 traf.auxiliary_size.default_sample_info_size;
171 tri.aux_info_sizes = traf.auxiliary_size.sample_info_sizes;
172
173 // If the default info size is positive, find the total size of the aux
174 // info block from it, otherwise sum over the individual sizes of each
175 // aux info entry in the aux_offset entry.
176 if (tri.aux_info_default_size) {
177 tri.aux_info_total_size =
178 tri.aux_info_default_size * trun.sample_count;
179 } else {
180 tri.aux_info_total_size = 0;
181 for (size_t k = 0; k < trun.sample_count; k++) {
182 tri.aux_info_total_size += tri.aux_info_sizes[k];
183 }
184 }
185 } else {
186 tri.aux_info_start_offset = -1;
187 tri.aux_info_total_size = 0;
188 }
189 tri.track_encryption = sinf->info.track_encryption;
142 190
143 tri.samples.resize(trun.sample_count); 191 tri.samples.resize(trun.sample_count);
144
145 for (size_t k = 0; k < trun.sample_count; k++) { 192 for (size_t k = 0; k < trun.sample_count; k++) {
146 PopulateSampleInfo(*trak, *trex, traf.header, trun, k, &tri.samples[k]); 193 PopulateSampleInfo(*trex, traf.header, trun, k, &tri.samples[k]);
194 run_start_dts += tri.samples[k].duration;
147 } 195 }
148 runs_.push_back(tri); 196 runs_.push_back(tri);
149 } 197 }
150 } 198 }
151 199
152 std::sort(runs_.begin(), runs_.end(), CompareOffset()); 200 std::sort(runs_.begin(), runs_.end(), CompareMinTrackRunDataOffset());
153 run_itr_ = runs_.begin(); 201 run_itr_ = runs_.begin();
154 min_clear_offset_itr_ = min_clear_offsets_.begin();
155 ResetRun(); 202 ResetRun();
156 return true; 203 return true;
157 } 204 }
158 205
159 void TrackRunIterator::AdvanceRun() { 206 void TrackRunIterator::AdvanceRun() {
160 ++run_itr_; 207 ++run_itr_;
161 if (min_clear_offset_itr_ != min_clear_offsets_.end())
162 ++min_clear_offset_itr_;
163 ResetRun(); 208 ResetRun();
164 } 209 }
165 210
166 void TrackRunIterator::ResetRun() { 211 void TrackRunIterator::ResetRun() {
167 if (!RunValid()) return; 212 if (!RunIsValid()) return;
168 sample_dts_ = run_itr_->start_dts; 213 sample_dts_ = run_itr_->start_dts;
169 sample_offset_ = run_itr_->sample_start_offset; 214 sample_offset_ = run_itr_->sample_start_offset;
170 sample_itr_ = run_itr_->samples.begin(); 215 sample_itr_ = run_itr_->samples.begin();
216 cenc_info_.clear();
171 } 217 }
172 218
173 void TrackRunIterator::AdvanceSample() { 219 void TrackRunIterator::AdvanceSample() {
174 DCHECK(SampleValid()); 220 DCHECK(SampleIsValid());
175 sample_dts_ += sample_itr_->duration; 221 sample_dts_ += sample_itr_->duration;
176 sample_offset_ += sample_itr_->size; 222 sample_offset_ += sample_itr_->size;
177 ++sample_itr_; 223 ++sample_itr_;
178 } 224 }
179 225
180 bool TrackRunIterator::NeedsCENC() { 226 // This implementation only indicates a need for caching if CENC auxiliary
181 CHECK(!is_encrypted()) << "TODO(strobe): Implement CENC."; 227 // info is available in the stream.
182 return is_encrypted(); 228 bool TrackRunIterator::AuxInfoNeedsToBeCached() {
183 } 229 DCHECK(RunIsValid());
184 230 return is_encrypted() && aux_info_size() > 0 && cenc_info_.size() == 0;
185 bool TrackRunIterator::CacheCENC(const uint8* buf, int size) { 231 }
186 LOG(FATAL) << "Not implemented"; 232
187 return false; 233 // This implementation currently only caches CENC auxiliary info.
188 } 234 bool TrackRunIterator::CacheAuxInfo(const uint8* buf, int buf_size) {
189 235 RCHECK(AuxInfoNeedsToBeCached() && buf_size >= aux_info_size());
190 bool TrackRunIterator::RunValid() const { 236
237 cenc_info_.resize(run_itr_->samples.size());
238 int64 pos = 0;
239 for (size_t i = 0; i < run_itr_->samples.size(); i++) {
240 int info_size = run_itr_->aux_info_default_size;
241 if (!info_size)
242 info_size = run_itr_->aux_info_sizes[i];
243
244 BufferReader reader(buf + pos, info_size);
245 RCHECK(cenc_info_[i].Parse(run_itr_->track_encryption.default_iv_size,
246 &reader));
247 pos += info_size;
248 }
249
250 return true;
251 }
252
253 bool TrackRunIterator::RunIsValid() const {
191 return run_itr_ != runs_.end(); 254 return run_itr_ != runs_.end();
192 } 255 }
193 256
194 bool TrackRunIterator::SampleValid() const { 257 bool TrackRunIterator::SampleIsValid() const {
195 return RunValid() && (sample_itr_ != run_itr_->samples.end()); 258 return RunIsValid() && (sample_itr_ != run_itr_->samples.end());
196 } 259 }
197 260
198 int64 TrackRunIterator::GetMaxClearOffset() { 261 int64 TrackRunIterator::GetMaxClearOffset() {
199 int64 offset = kint64max; 262 int64 offset = kint64max;
ddorwin 2012/07/17 01:14:21 max_offset?
strobe_ 2012/07/19 02:43:35 Well, internal to this function, it's really the m
200 263
201 if (SampleValid()) { 264 if (SampleIsValid()) {
202 offset = std::min(offset, sample_offset_); 265 offset = std::min(offset, sample_offset_);
203 if (NeedsCENC()) { 266 if (AuxInfoNeedsToBeCached()) {
204 offset = std::min(offset, cenc_offset()); 267 offset = std::min(offset, aux_info_offset());
205 } 268 }
206 } 269 }
207 if (min_clear_offset_itr_ != min_clear_offsets_.end()) { 270 if (run_itr_ != runs_.end()) {
208 offset = std::min(offset, *min_clear_offset_itr_); 271 std::vector<TrackRunInfo>::const_iterator next_run = run_itr_ + 1;
209 } 272 if (next_run != runs_.end()) {
210 if (offset == kint64max) return 0; 273 offset = std::min(offset, next_run->sample_start_offset);
274 if (next_run->aux_info_total_size)
275 offset = std::min(offset, next_run->aux_info_start_offset);
276 }
277 }
211 return offset; 278 return offset;
212 } 279 }
213 280
214 TimeDelta TrackRunIterator::GetMinDecodeTimestamp() { 281 TimeDelta TrackRunIterator::GetMinDecodeTimestamp() {
215 TimeDelta dts = kInfiniteDuration(); 282 TimeDelta dts = kInfiniteDuration();
216 for (size_t i = 0; i < runs_.size(); i++) { 283 for (size_t i = 0; i < runs_.size(); i++) {
217 if (runs_[i].start_dts < dts) 284 TimeDelta run_dts = TimeDeltaFromFrac(runs_[i].start_dts,
218 dts = runs_[i].start_dts; 285 runs_[i].timescale);
286 if (run_dts < dts)
287 dts = run_dts;
ddorwin 2012/07/17 01:14:21 Why?
strobe_ 2012/07/19 02:43:35 Addressed by rebase.
219 } 288 }
220 return dts; 289 return dts;
221 } 290 }
222 291
223 uint32 TrackRunIterator::track_id() const { 292 uint32 TrackRunIterator::track_id() const {
224 DCHECK(RunValid()); 293 DCHECK(RunIsValid());
225 return run_itr_->track_id; 294 return run_itr_->track_id;
226 } 295 }
227 296
228 bool TrackRunIterator::is_encrypted() const { 297 bool TrackRunIterator::is_encrypted() const {
229 DCHECK(RunValid()); 298 DCHECK(RunIsValid());
230 return run_itr_->is_encrypted; 299 return run_itr_->track_encryption.is_encrypted;
231 } 300 }
232 301
233 int64 TrackRunIterator::cenc_offset() const { 302 int64 TrackRunIterator::aux_info_offset() const {
234 DCHECK(is_encrypted()); 303 return run_itr_->aux_info_start_offset;
235 return run_itr_->cenc_start_offset; 304 }
236 } 305
237 306 int TrackRunIterator::aux_info_size() const {
238 int TrackRunIterator::cenc_size() const { 307 return run_itr_->aux_info_total_size;
239 DCHECK(is_encrypted()); 308 }
240 return run_itr_->cenc_total_size; 309
241 } 310 int64 TrackRunIterator::sample_offset() const {
242 311 DCHECK(SampleIsValid());
243 int64 TrackRunIterator::offset() const {
244 DCHECK(SampleValid());
245 return sample_offset_; 312 return sample_offset_;
246 } 313 }
247 314
248 int TrackRunIterator::size() const { 315 int TrackRunIterator::sample_size() const {
249 DCHECK(SampleValid()); 316 DCHECK(SampleIsValid());
250 return sample_itr_->size; 317 return sample_itr_->size;
251 } 318 }
252 319
253 TimeDelta TrackRunIterator::dts() const { 320 TimeDelta TrackRunIterator::dts() const {
254 DCHECK(SampleValid()); 321 DCHECK(SampleIsValid());
255 return sample_dts_; 322 return TimeDeltaFromFrac(sample_dts_, run_itr_->timescale);
256 } 323 }
257 324
258 TimeDelta TrackRunIterator::cts() const { 325 TimeDelta TrackRunIterator::cts() const {
259 DCHECK(SampleValid()); 326 DCHECK(SampleIsValid());
260 return sample_dts_ + sample_itr_->cts_offset; 327 return TimeDeltaFromFrac(sample_dts_ + sample_itr_->cts_offset,
328 run_itr_->timescale);
261 } 329 }
262 330
263 TimeDelta TrackRunIterator::duration() const { 331 TimeDelta TrackRunIterator::duration() const {
264 DCHECK(SampleValid()); 332 DCHECK(SampleIsValid());
265 return sample_itr_->duration; 333 return TimeDeltaFromFrac(sample_itr_->duration, run_itr_->timescale);
266 } 334 }
267 335
268 bool TrackRunIterator::is_keyframe() const { 336 bool TrackRunIterator::is_keyframe() const {
269 DCHECK(SampleValid()); 337 DCHECK(SampleIsValid());
270 return sample_itr_->is_keyframe; 338 return sample_itr_->is_keyframe;
271 } 339 }
272 340
273 const FrameCENCInfo& TrackRunIterator::frame_cenc_info() { 341 scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
274 DCHECK(is_encrypted()); 342 int sample_idx = sample_itr_ - run_itr_->samples.begin();
275 return frame_cenc_info_; 343 const FrameCENCInfo& cenc_info = cenc_info_[sample_idx];
ddorwin 2012/07/17 01:14:21 check size of the vector/array first.
strobe_ 2012/07/19 02:43:35 Done.
344 DCHECK(is_encrypted() && !AuxInfoNeedsToBeCached());
345
346 if (!cenc_info.subsamples.empty() &&
347 (cenc_info.GetTotalSizeOfSubsamples() !=
348 static_cast<size_t>(sample_size()))) {
349 DVLOG(1) << "Incorrect CENC subsample size.";
350 return scoped_ptr<DecryptConfig>();
351 }
352
353 return scoped_ptr<DecryptConfig>(new DecryptConfig(
354 &run_itr_->track_encryption.default_kid[0],
355 run_itr_->track_encryption.default_kid.size(),
356 std::string(cenc_info.iv, cenc_info.iv + arraysize(cenc_info.iv)),
ddorwin 2012/07/17 01:14:21 why not just size as second param?
strobe_ 2012/07/19 02:43:35 Would require typecasting.
357 cenc_info.subsamples));
276 } 358 }
277 359
278 } // namespace mp4 360 } // namespace mp4
279 } // namespace media 361 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698