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

Side by Side Diff: media/mp4/mp4_stream_parser.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: Fix another case issue 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/mp4_stream_parser.h" 5 #include "media/mp4/mp4_stream_parser.h"
6 6
7 #include "base/callback.h" 7 #include "base/callback.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/time.h" 9 #include "base/time.h"
10 #include "media/base/audio_decoder_config.h" 10 #include "media/base/audio_decoder_config.h"
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
93 } while (result && !err); 93 } while (result && !err);
94 94
95 if (err) { 95 if (err) {
96 DLOG(ERROR) << "Unknown error while parsing MP4"; 96 DLOG(ERROR) << "Unknown error while parsing MP4";
97 queue_.Reset(); 97 queue_.Reset();
98 moov_.reset(); 98 moov_.reset();
99 ChangeState(kError); 99 ChangeState(kError);
100 return false; 100 return false;
101 } 101 }
102 102
103 if (!audio_buffers.empty() && 103 if (!audio_buffers.empty()) {
104 (audio_cb_.is_null() || !audio_cb_.Run(audio_buffers))) 104 CHECK(!audio_cb_.is_null());
105 return false; 105 RCHECK(audio_cb_.Run(audio_buffers));
106 if (!video_buffers.empty() && 106 }
107 (video_cb_.is_null() || !video_cb_.Run(video_buffers))) 107 if (!video_buffers.empty()) {
108 return false; 108 CHECK(!video_cb_.is_null());
109 109 RCHECK(video_cb_.Run(video_buffers));
110 }
110 return true; 111 return true;
111 } 112 }
112 113
113 bool MP4StreamParser::ParseBox(bool* err) { 114 bool MP4StreamParser::ParseBox(bool* err) {
114 const uint8* buf; 115 const uint8* buf;
115 int size; 116 int size;
116 queue_.Peek(&buf, &size); 117 queue_.Peek(&buf, &size);
117 if (!size) return false; 118 if (!size) return false;
118 119
119 scoped_ptr<BoxReader> reader(BoxReader::ReadTopLevelBox(buf, size, err)); 120 scoped_ptr<BoxReader> reader(BoxReader::ReadTopLevelBox(buf, size, err));
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 track->media.information.sample_table.description; 160 track->media.information.sample_table.description;
160 if (track->media.handler.type == kAudio && !audio_config.IsValidConfig()) { 161 if (track->media.handler.type == kAudio && !audio_config.IsValidConfig()) {
161 RCHECK(!samp_descr.audio_entries.empty()); 162 RCHECK(!samp_descr.audio_entries.empty());
162 const AudioSampleEntry& entry = samp_descr.audio_entries[0]; 163 const AudioSampleEntry& entry = samp_descr.audio_entries[0];
163 164
164 // TODO(strobe): We accept all format values, pending clarification on 165 // TODO(strobe): We accept all format values, pending clarification on
165 // the formats used for encrypted media (http://crbug.com/132351). 166 // the formats used for encrypted media (http://crbug.com/132351).
166 // RCHECK(entry.format == FOURCC_MP4A || 167 // RCHECK(entry.format == FOURCC_MP4A ||
167 // (entry.format == FOURCC_ENCA && 168 // (entry.format == FOURCC_ENCA &&
168 // entry.sinf.format.format == FOURCC_MP4A)); 169 // entry.sinf.format.format == FOURCC_MP4A));
170 RCHECK(EmitKeyNeeded(entry.sinf.info.track_encryption));
169 171
170 const ChannelLayout layout = 172 const ChannelLayout layout =
171 AVC::ConvertAACChannelCountToChannelLayout(entry.channelcount); 173 AVC::ConvertAACChannelCountToChannelLayout(entry.channelcount);
172 audio_config.Initialize(kCodecAAC, entry.samplesize, layout, 174 audio_config.Initialize(kCodecAAC, entry.samplesize, layout,
173 entry.samplerate, NULL, 0, false); 175 entry.samplerate, NULL, 0, false);
174 has_audio_ = true; 176 has_audio_ = true;
175 audio_track_id_ = track->header.track_id; 177 audio_track_id_ = track->header.track_id;
176 } 178 }
177 if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) { 179 if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) {
178 RCHECK(!samp_descr.video_entries.empty()); 180 RCHECK(!samp_descr.video_entries.empty());
179 const VideoSampleEntry& entry = samp_descr.video_entries[0]; 181 const VideoSampleEntry& entry = samp_descr.video_entries[0];
180 182
181 // RCHECK(entry.format == FOURCC_AVC1 || 183 // RCHECK(entry.format == FOURCC_AVC1 ||
182 // (entry.format == FOURCC_ENCV && 184 // (entry.format == FOURCC_ENCV &&
183 // entry.sinf.format.format == FOURCC_AVC1)); 185 // entry.sinf.format.format == FOURCC_AVC1));
186 RCHECK(EmitKeyNeeded(entry.sinf.info.track_encryption));
184 187
185 // TODO(strobe): Recover correct crop box and pixel aspect ratio 188 // TODO(strobe): Recover correct crop box and pixel aspect ratio
186 video_config.Initialize(kCodecH264, H264PROFILE_MAIN, VideoFrame::YV12, 189 video_config.Initialize(kCodecH264, H264PROFILE_MAIN, VideoFrame::YV12,
187 gfx::Size(entry.width, entry.height), 190 gfx::Size(entry.width, entry.height),
188 gfx::Rect(0, 0, entry.width, entry.height), 191 gfx::Rect(0, 0, entry.width, entry.height),
189 // Bogus duration used for framerate, since real 192 // Bogus duration used for framerate, since real
190 // framerate may be variable 193 // framerate may be variable
191 1000, track->media.header.timescale, 194 1000, track->media.header.timescale,
192 1, 1, 195 1, 1,
193 // No decoder-specific buffer needed for AVC; 196 // No decoder-specific buffer needed for AVC;
(...skipping 26 matching lines...) Expand all
220 bool MP4StreamParser::ParseMoof(BoxReader* reader) { 223 bool MP4StreamParser::ParseMoof(BoxReader* reader) {
221 RCHECK(moov_.get()); // Must already have initialization segment 224 RCHECK(moov_.get()); // Must already have initialization segment
222 MovieFragment moof; 225 MovieFragment moof;
223 RCHECK(moof.Parse(reader)); 226 RCHECK(moof.Parse(reader));
224 RCHECK(runs_.Init(*moov_, moof)); 227 RCHECK(runs_.Init(*moov_, moof));
225 new_segment_cb_.Run(runs_.GetMinDecodeTimestamp()); 228 new_segment_cb_.Run(runs_.GetMinDecodeTimestamp());
226 ChangeState(kEmittingSamples); 229 ChangeState(kEmittingSamples);
227 return true; 230 return true;
228 } 231 }
229 232
233 bool MP4StreamParser::EmitKeyNeeded(const TrackEncryption& track_encryption) {
234 if (!track_encryption.is_encrypted) return true;
235 scoped_array<uint8> kid(new uint8[track_encryption.default_kid.size()]);
236 memcpy(kid.get(), &track_encryption.default_kid[0],
237 track_encryption.default_kid.size());
238 return need_key_cb_.Run(kid.Pass(), track_encryption.default_kid.size());
239 }
240
230 bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, 241 bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers,
231 BufferQueue* video_buffers, 242 BufferQueue* video_buffers,
232 bool* err) { 243 bool* err) {
233 if (!runs_.RunValid()) { 244 if (!runs_.RunValid()) {
234 ChangeState(kParsingBoxes); 245 ChangeState(kParsingBoxes);
235 return true; 246 return true;
236 } 247 }
237 248
238 if (!runs_.SampleValid()) { 249 if (!runs_.SampleValid()) {
239 runs_.AdvanceRun(); 250 runs_.AdvanceRun();
240 return true; 251 return true;
241 } 252 }
242 253
243 DCHECK(!(*err)); 254 DCHECK(!(*err));
244 255
245 const uint8* buf; 256 const uint8* buf;
246 int size; 257 int size;
ddorwin 2012/07/03 21:03:47 Is there a reason you didn't change the name to bu
strobe_ 2012/07/19 02:43:34 Done.
247 queue_.Peek(&buf, &size); 258 queue_.Peek(&buf, &size);
248 if (!size) return false; 259 if (!size) return false;
249 260
250 bool audio = has_audio_ && audio_track_id_ == runs_.track_id(); 261 bool audio = has_audio_ && audio_track_id_ == runs_.track_id();
251 bool video = has_video_ && video_track_id_ == runs_.track_id(); 262 bool video = has_video_ && video_track_id_ == runs_.track_id();
252 263
253 // Skip this entire track if it's not one we're interested in 264 // Skip this entire track if it's not one we're interested in
254 if (!audio && !video) runs_.AdvanceRun(); 265 if (!audio && !video) runs_.AdvanceRun();
255 266
256 // Attempt to cache the auxiliary information first. Aux info is usually 267 // Attempt to cache the auxiliary information first. Aux info is usually
257 // placed in a contiguous block before the sample data, rather than being 268 // placed in a contiguous block before the sample data, rather than being
258 // interleaved. If we didn't cache it, this would require that we retain the 269 // interleaved. If we didn't cache it, this would require that we retain the
259 // start of the segment buffer while reading samples. Aux info is typically 270 // start of the segment buffer while reading samples. Aux info is typically
260 // quite small compared to sample data, so this pattern is useful on 271 // quite small compared to sample data, so this pattern is useful on
261 // memory-constrained devices where the source buffer consumes a substantial 272 // memory-constrained devices where the source buffer consumes a substantial
262 // portion of the total system memory. 273 // portion of the total system memory.
263 if (runs_.NeedsCENC()) { 274 if (runs_.AuxInfoNeedsToBeCached()) {
264 queue_.PeekAt(runs_.cenc_offset() + moof_head_, &buf, &size); 275 queue_.PeekAt(runs_.aux_info_offset() + moof_head_, &buf, &size);
265 return runs_.CacheCENC(buf, size); 276 if (size < runs_.aux_info_size()) return false;
277 *err = !runs_.CacheAuxInfo(buf, size);
278 return !*err;
266 } 279 }
267 280
268 queue_.PeekAt(runs_.offset() + moof_head_, &buf, &size); 281 queue_.PeekAt(runs_.sample_offset() + moof_head_, &buf, &size);
269 if (size < runs_.size()) return false; 282 if (size < runs_.sample_size()) return false;
270 283
271 std::vector<uint8> frame_buf(buf, buf + runs_.size()); 284 scoped_ptr<DecryptConfig> decrypt_config;
285 if (runs_.is_encrypted())
286 decrypt_config = runs_.GetDecryptConfig();
287
288 std::vector<uint8> frame_buf(buf, buf + runs_.sample_size());
272 if (video) { 289 if (video) {
290 // TODO(strobe): move all this to separate method, add unittest
273 RCHECK(AVC::ConvertToAnnexB(size_of_nalu_length_, &frame_buf)); 291 RCHECK(AVC::ConvertToAnnexB(size_of_nalu_length_, &frame_buf));
292
293 if (decrypt_config.get()) {
294 // XXX(strobe): decide how best to make these modifications
ddorwin 2012/07/03 21:03:47 TODO
strobe_ 2012/07/13 00:47:07 Sorry, "XXX" is personal shorthand for "resolve be
295 std::vector<SubsampleEntry>* subsamples =
296 decrypt_config->mutable_subsamples();
297
298 const int nalu_size_diff = 4 - size_of_nalu_length_;
299 size_t expected_size = runs_.sample_size() +
300 decrypt_config->subsamples().size() * nalu_size_diff;
301 RCHECK(frame_buf.size() == expected_size);
302 for (size_t i = 0; i < decrypt_config->subsamples().size(); i++)
303 (*subsamples)[i].clear_bytes += nalu_size_diff;
304 }
305
274 if (!parameter_sets_inserted_) { 306 if (!parameter_sets_inserted_) {
307 if (!runs_.is_keyframe()) {
308 LOG(INFO) << "XXX skipping initial non-keyframe sample";
309 runs_.AdvanceSample();
310 return true;
311 }
312
275 const AVCDecoderConfigurationRecord* avc_config = NULL; 313 const AVCDecoderConfigurationRecord* avc_config = NULL;
276 for (size_t t = 0; t < moov_->tracks.size(); t++) { 314 for (size_t t = 0; t < moov_->tracks.size(); t++) {
277 if (moov_->tracks[t].header.track_id == runs_.track_id()) { 315 if (moov_->tracks[t].header.track_id == runs_.track_id()) {
278 avc_config = &moov_->tracks[t].media.information. 316 avc_config = &moov_->tracks[t].media.information.
279 sample_table.description.video_entries[0].avcc; 317 sample_table.description.video_entries[0].avcc;
280 break; 318 break;
281 } 319 }
282 } 320 }
283 RCHECK(avc_config != NULL); 321 RCHECK(avc_config != NULL);
284 RCHECK(AVC::InsertParameterSets(*avc_config, &frame_buf)); 322 std::vector<uint8> param_sets;
323 RCHECK(AVC::ConvertParameterSets(*avc_config, &param_sets));
324 frame_buf.insert(frame_buf.begin(),
325 param_sets.begin(), param_sets.end());
326 if (decrypt_config.get()) {
327 (*decrypt_config->mutable_subsamples())[0].clear_bytes +=
328 param_sets.size();
329 }
285 parameter_sets_inserted_ = true; 330 parameter_sets_inserted_ = true;
286 } 331 }
287 } 332 }
288 333
289 scoped_refptr<StreamParserBuffer> stream_buf = 334 scoped_refptr<StreamParserBuffer> stream_buf =
290 StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(), 335 StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(),
291 runs_.is_keyframe()); 336 runs_.is_keyframe());
292 337
338 if (runs_.is_encrypted())
339 stream_buf->SetDecryptConfig(decrypt_config.Pass());
340
293 stream_buf->SetDuration(runs_.duration()); 341 stream_buf->SetDuration(runs_.duration());
294 // We depend on the decoder performing frame reordering without reordering 342 // We depend on the decoder performing frame reordering without reordering
295 // timestamps, and only provide the decode timestamp in the buffer. 343 // timestamps, and only provide the decode timestamp in the buffer.
296 stream_buf->SetTimestamp(runs_.dts()); 344 stream_buf->SetTimestamp(runs_.dts());
297 345
298 DVLOG(3) << "Pushing frame: aud=" << audio 346 DVLOG(3) << "Pushing frame: aud=" << audio
299 << ", key=" << runs_.is_keyframe() 347 << ", key=" << runs_.is_keyframe()
300 << ", dur=" << runs_.duration().InMilliseconds() 348 << ", dur=" << runs_.duration().InMilliseconds()
301 << ", dts=" << runs_.dts().InMilliseconds() 349 << ", dts=" << runs_.dts().InMilliseconds()
302 << ", size=" << runs_.size(); 350 << ", size=" << runs_.sample_size();
303 351
304 if (audio) { 352 if (audio) {
305 audio_buffers->push_back(stream_buf); 353 audio_buffers->push_back(stream_buf);
306 } else { 354 } else {
307 video_buffers->push_back(stream_buf); 355 video_buffers->push_back(stream_buf);
308 } 356 }
309 357
310 runs_.AdvanceSample(); 358 runs_.AdvanceSample();
311 return true; 359 return true;
312 } 360 }
(...skipping 23 matching lines...) Expand all
336 return true; 384 return true;
337 } 385 }
338 386
339 void MP4StreamParser::ChangeState(State new_state) { 387 void MP4StreamParser::ChangeState(State new_state) {
340 DVLOG(2) << "Changing state: " << new_state; 388 DVLOG(2) << "Changing state: " << new_state;
341 state_ = new_state; 389 state_ = new_state;
342 } 390 }
343 391
344 } // namespace mp4 392 } // namespace mp4
345 } // namespace media 393 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698