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

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: 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/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());
ddorwin 2012/06/26 23:16:20 For ISO, the initData is supposed to be the PSSH.
strobe_ 2012/06/27 02:01:21 Oh, duh, that makes a lot more sense! Where's that
ddorwin 2012/07/03 21:03:46 This is based on discussions with others, though t
strobe_ 2012/07/13 00:47:07 OK, comment added.
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/06/26 06:09:19 buf_size would make this function easier to read.
strobe_ 2012/07/13 00:47:07 Done.
strobe_ 2012/07/13 00:47:07 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_.AuxInfoRequired()) {
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_.offset() + moof_head_, &buf, &size);
269 if (size < runs_.size()) return false; 282 if (size < runs_.size()) return false;
270 283
284 scoped_ptr<DecryptConfig> decrypt_config;
285 if (runs_.is_encrypted())
286 decrypt_config = runs_.GetDecryptConfig();
287
271 std::vector<uint8> frame_buf(buf, buf + runs_.size()); 288 std::vector<uint8> frame_buf(buf, buf + runs_.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 const int nalu_size_diff = 4 - size_of_nalu_length_;
295 size_t expected_size = runs_.size() +
ddorwin 2012/06/26 06:09:19 expected size of what? what is runs_.size()? It se
strobe_ 2012/06/27 02:01:21 size() is the size of the current sample; IV and s
296 decrypt_config->subsample_count() * nalu_size_diff;
297 RCHECK(frame_buf.size() == expected_size);
298 for (int i = 0; i < decrypt_config->subsample_count(); i++)
299 decrypt_config->mutable_subsamples()[i].clear_bytes += nalu_size_diff;
ddorwin 2012/06/26 06:09:19 Rather than mutable_subsamples, you could have an
strobe_ 2012/06/27 02:01:21 Two reasons we need to mutate: 1. To convert each
ddorwin 2012/07/03 21:03:46 Since we are just passing the DecryptConfig to som
strobe_ 2012/07/13 00:47:07 Done.
300 }
301
274 if (!parameter_sets_inserted_) { 302 if (!parameter_sets_inserted_) {
303 if (!runs_.is_keyframe()) {
304 LOG(INFO) << "XXX skipping initial non-keyframe sample";
305 runs_.AdvanceSample();
306 return true;
307 }
308
275 const AVCDecoderConfigurationRecord* avc_config = NULL; 309 const AVCDecoderConfigurationRecord* avc_config = NULL;
276 for (size_t t = 0; t < moov_->tracks.size(); t++) { 310 for (size_t t = 0; t < moov_->tracks.size(); t++) {
277 if (moov_->tracks[t].header.track_id == runs_.track_id()) { 311 if (moov_->tracks[t].header.track_id == runs_.track_id()) {
278 avc_config = &moov_->tracks[t].media.information. 312 avc_config = &moov_->tracks[t].media.information.
279 sample_table.description.video_entries[0].avcc; 313 sample_table.description.video_entries[0].avcc;
280 break; 314 break;
281 } 315 }
282 } 316 }
283 RCHECK(avc_config != NULL); 317 RCHECK(avc_config != NULL);
284 RCHECK(AVC::InsertParameterSets(*avc_config, &frame_buf)); 318 std::vector<uint8> param_sets;
319 RCHECK(AVC::ConvertParameterSets(*avc_config, &param_sets));
320 frame_buf.insert(frame_buf.begin(),
321 param_sets.begin(), param_sets.end());
322 if (decrypt_config.get()) {
323 decrypt_config->mutable_subsamples()[0].clear_bytes +=
ddorwin 2012/06/26 06:09:19 Hmm, another mutable use. I guess it's not as simp
strobe_ 2012/06/27 02:01:21 see above
324 param_sets.size();
325 }
285 parameter_sets_inserted_ = true; 326 parameter_sets_inserted_ = true;
286 } 327 }
287 } 328 }
288 329
289 scoped_refptr<StreamParserBuffer> stream_buf = 330 scoped_refptr<StreamParserBuffer> stream_buf =
290 StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(), 331 StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(),
291 runs_.is_keyframe()); 332 runs_.is_keyframe());
292 333
334 if (runs_.is_encrypted())
335 stream_buf->SetDecryptConfig(decrypt_config.Pass());
336
293 stream_buf->SetDuration(runs_.duration()); 337 stream_buf->SetDuration(runs_.duration());
294 // We depend on the decoder performing frame reordering without reordering 338 // We depend on the decoder performing frame reordering without reordering
295 // timestamps, and only provide the decode timestamp in the buffer. 339 // timestamps, and only provide the decode timestamp in the buffer.
296 stream_buf->SetTimestamp(runs_.dts()); 340 stream_buf->SetTimestamp(runs_.dts());
297 341
298 DVLOG(3) << "Pushing frame: aud=" << audio 342 DVLOG(3) << "Pushing frame: aud=" << audio
299 << ", key=" << runs_.is_keyframe() 343 << ", key=" << runs_.is_keyframe()
300 << ", dur=" << runs_.duration().InMilliseconds() 344 << ", dur=" << runs_.duration().InMilliseconds()
301 << ", dts=" << runs_.dts().InMilliseconds() 345 << ", dts=" << runs_.dts().InMilliseconds()
302 << ", size=" << runs_.size(); 346 << ", size=" << runs_.size();
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
336 return true; 380 return true;
337 } 381 }
338 382
339 void MP4StreamParser::ChangeState(State new_state) { 383 void MP4StreamParser::ChangeState(State new_state) {
340 DVLOG(2) << "Changing state: " << new_state; 384 DVLOG(2) << "Changing state: " << new_state;
341 state_ = new_state; 385 state_ = new_state;
342 } 386 }
343 387
344 } // namespace mp4 388 } // namespace mp4
345 } // namespace media 389 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698