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/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/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/time.h" | 10 #include "base/time.h" |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
165 } | 165 } |
166 } | 166 } |
167 RCHECK(desc_idx > 0); | 167 RCHECK(desc_idx > 0); |
168 desc_idx -= 1; // BMFF descriptor index is one-based | 168 desc_idx -= 1; // BMFF descriptor index is one-based |
169 | 169 |
170 if (track->media.handler.type == kAudio && !audio_config.IsValidConfig()) { | 170 if (track->media.handler.type == kAudio && !audio_config.IsValidConfig()) { |
171 RCHECK(!samp_descr.audio_entries.empty()); | 171 RCHECK(!samp_descr.audio_entries.empty()); |
172 | 172 |
173 // It is not uncommon to find otherwise-valid files with incorrect sample | 173 // It is not uncommon to find otherwise-valid files with incorrect sample |
174 // description indices, so we fail gracefully in that case. | 174 // description indices, so we fail gracefully in that case. |
175 if (static_cast<uint32>(desc_idx) >= samp_descr.audio_entries.size()) | 175 if (desc_idx >= samp_descr.audio_entries.size()) |
176 desc_idx = 0; | 176 desc_idx = 0; |
177 const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx]; | 177 const AudioSampleEntry& entry = samp_descr.audio_entries[desc_idx]; |
178 | 178 |
179 // TODO(strobe): We accept all format values, pending clarification on | 179 RCHECK(entry.format == FOURCC_MP4A || |
180 // the formats used for encrypted media (http://crbug.com/132351). | 180 (entry.format == FOURCC_ENCA && |
181 // RCHECK(entry.format == FOURCC_MP4A || | 181 entry.sinf.format.format == FOURCC_MP4A)); |
182 // (entry.format == FOURCC_ENCA && | 182 RCHECK(EmitKeyNeeded(entry.sinf.info.track_encryption)); |
183 // entry.sinf.format.format == FOURCC_MP4A)); | |
184 | 183 |
185 const ChannelLayout layout = | 184 const ChannelLayout layout = |
186 AVC::ConvertAACChannelCountToChannelLayout(entry.channelcount); | 185 AVC::ConvertAACChannelCountToChannelLayout(entry.channelcount); |
187 audio_config.Initialize(kCodecAAC, entry.samplesize, layout, | 186 audio_config.Initialize(kCodecAAC, entry.samplesize, layout, |
188 entry.samplerate, NULL, 0, false); | 187 entry.samplerate, NULL, 0, false); |
189 has_audio_ = true; | 188 has_audio_ = true; |
190 audio_track_id_ = track->header.track_id; | 189 audio_track_id_ = track->header.track_id; |
191 } | 190 } |
192 if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) { | 191 if (track->media.handler.type == kVideo && !video_config.IsValidConfig()) { |
193 RCHECK(!samp_descr.video_entries.empty()); | 192 RCHECK(!samp_descr.video_entries.empty()); |
194 if (static_cast<uint32>(desc_idx) >= samp_descr.video_entries.size()) | 193 if (desc_idx >= samp_descr.video_entries.size()) |
195 desc_idx = 0; | 194 desc_idx = 0; |
196 const VideoSampleEntry& entry = samp_descr.video_entries[desc_idx]; | 195 const VideoSampleEntry& entry = samp_descr.video_entries[desc_idx]; |
197 | 196 |
198 // RCHECK(entry.format == FOURCC_AVC1 || | 197 RCHECK(entry.format == FOURCC_AVC1 || |
199 // (entry.format == FOURCC_ENCV && | 198 (entry.format == FOURCC_ENCV && |
200 // entry.sinf.format.format == FOURCC_AVC1)); | 199 entry.sinf.format.format == FOURCC_AVC1)); |
| 200 RCHECK(EmitKeyNeeded(entry.sinf.info.track_encryption)); |
201 | 201 |
202 // TODO(strobe): Recover correct crop box | 202 // TODO(strobe): Recover correct crop box |
203 video_config.Initialize(kCodecH264, H264PROFILE_MAIN, VideoFrame::YV12, | 203 video_config.Initialize(kCodecH264, H264PROFILE_MAIN, VideoFrame::YV12, |
204 gfx::Size(entry.width, entry.height), | 204 gfx::Size(entry.width, entry.height), |
205 gfx::Rect(0, 0, entry.width, entry.height), | 205 gfx::Rect(0, 0, entry.width, entry.height), |
206 // Framerate of zero is provided to signal that | 206 // Framerate of zero is provided to signal that |
207 // the decoder should trust demuxer timestamps | 207 // the decoder should trust demuxer timestamps |
208 0, 1, | 208 0, 1, |
209 entry.pixel_aspect.h_spacing, | 209 entry.pixel_aspect.h_spacing, |
210 entry.pixel_aspect.v_spacing, | 210 entry.pixel_aspect.v_spacing, |
211 // No decoder-specific buffer needed for AVC; | 211 // No decoder-specific buffer needed for AVC; |
212 // SPS/PPS are embedded in the video stream | 212 // SPS/PPS are embedded in the video stream |
213 NULL, 0, false); | 213 NULL, 0, false); |
214 has_video_ = true; | 214 has_video_ = true; |
215 video_track_id_ = track->header.track_id; | 215 video_track_id_ = track->header.track_id; |
216 } | 216 } |
217 } | 217 } |
218 | 218 |
219 // TODO(strobe): For now, we avoid sending new configs on a new | 219 // TODO(strobe): For now, we avoid sending new configs on a new |
220 // reinitialization segment, and instead simply embed the updated parameter | 220 // reinitialization segment, and instead simply embed the updated parameter |
221 // sets into the video stream. The conditional should be removed when | 221 // sets into the video stream. The conditional should be removed when |
222 // http://crbug.com/122913 is fixed. | 222 // http://crbug.com/122913 is fixed. |
223 if (!init_cb_.is_null()) | 223 if (!init_cb_.is_null()) |
224 RCHECK(config_cb_.Run(audio_config, video_config)); | 224 RCHECK(config_cb_.Run(audio_config, video_config)); |
225 | 225 |
226 base::TimeDelta duration; | 226 base::TimeDelta duration; |
227 if (moov_->extends.header.fragment_duration > 0) { | 227 if (moov_->extends.header.fragment_duration > 0) { |
228 duration = TimeDeltaFromFrac(moov_->extends.header.fragment_duration, | 228 duration = TimeDeltaFromRational(moov_->extends.header.fragment_duration, |
229 moov_->header.timescale); | 229 moov_->header.timescale); |
230 } else if (moov_->header.duration > 0) { | 230 } else if (moov_->header.duration > 0) { |
231 duration = TimeDeltaFromFrac(moov_->header.duration, | 231 duration = TimeDeltaFromRational(moov_->header.duration, |
232 moov_->header.timescale); | 232 moov_->header.timescale); |
233 } else { | 233 } else { |
234 duration = kInfiniteDuration(); | 234 duration = kInfiniteDuration(); |
235 } | 235 } |
236 | 236 |
237 if (!init_cb_.is_null()) | 237 if (!init_cb_.is_null()) |
238 base::ResetAndReturn(&init_cb_).Run(true, duration); | 238 base::ResetAndReturn(&init_cb_).Run(true, duration); |
239 return true; | 239 return true; |
240 } | 240 } |
241 | 241 |
242 bool MP4StreamParser::ParseMoof(BoxReader* reader) { | 242 bool MP4StreamParser::ParseMoof(BoxReader* reader) { |
243 RCHECK(moov_.get()); // Must already have initialization segment | 243 RCHECK(moov_.get()); // Must already have initialization segment |
244 MovieFragment moof; | 244 MovieFragment moof; |
245 RCHECK(moof.Parse(reader)); | 245 RCHECK(moof.Parse(reader)); |
246 RCHECK(runs_->Init(moof)); | 246 RCHECK(runs_->Init(moof)); |
247 new_segment_cb_.Run(runs_->GetMinDecodeTimestamp()); | 247 new_segment_cb_.Run(runs_->GetMinDecodeTimestamp()); |
248 ChangeState(kEmittingSamples); | 248 ChangeState(kEmittingSamples); |
249 return true; | 249 return true; |
250 } | 250 } |
251 | 251 |
| 252 bool MP4StreamParser::EmitKeyNeeded(const TrackEncryption& track_encryption) { |
| 253 // TODO(strobe): Send the correct value for initData. The format of initData |
| 254 // has not yet been defined; see |
| 255 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17673. |
| 256 if (!track_encryption.is_encrypted) return true; |
| 257 scoped_array<uint8> kid(new uint8[track_encryption.default_kid.size()]); |
| 258 memcpy(kid.get(), &track_encryption.default_kid[0], |
| 259 track_encryption.default_kid.size()); |
| 260 return need_key_cb_.Run(kid.Pass(), track_encryption.default_kid.size()); |
| 261 } |
| 262 |
| 263 bool MP4StreamParser::PrepareAVCBuffer( |
| 264 const AVCDecoderConfigurationRecord& avc_config, |
| 265 std::vector<uint8>* frame_buf, |
| 266 std::vector<SubsampleEntry>* subsamples) const { |
| 267 // Convert the AVC NALU length fields to Annex B headers, as expected by |
| 268 // decoding libraries. Since this may enlarge the size of the buffer, we also |
| 269 // update the clear byte count for each subsample if encryption is used. |
| 270 RCHECK(AVC::ConvertFrameToAnnexB(avc_config.length_size, frame_buf)); |
| 271 if (!subsamples->empty()) { |
| 272 const int nalu_size_diff = 4 - avc_config.length_size; |
| 273 size_t expected_size = runs_->sample_size() + |
| 274 subsamples->size() * nalu_size_diff; |
| 275 RCHECK(frame_buf->size() == expected_size); |
| 276 for (size_t i = 0; i < subsamples->size(); i++) |
| 277 (*subsamples)[i].clear_bytes += nalu_size_diff; |
| 278 } |
| 279 |
| 280 if (runs_->is_keyframe()) { |
| 281 // If this is a keyframe, we (re-)inject SPS and PPS headers at the start of |
| 282 // a frame. If subsample info is present, we also update the clear byte |
| 283 // count for that first subsample. |
| 284 const AVCDecoderConfigurationRecord* avc_config = NULL; |
| 285 for (size_t t = 0; t < moov_->tracks.size(); t++) { |
| 286 if (moov_->tracks[t].header.track_id == runs_->track_id()) { |
| 287 avc_config = &moov_->tracks[t].media.information. |
| 288 sample_table.description.video_entries[0].avcc; |
| 289 break; |
| 290 } |
| 291 } |
| 292 RCHECK(avc_config != NULL); |
| 293 std::vector<uint8> param_sets; |
| 294 RCHECK(AVC::ConvertParameterSetsToAnnexB(*avc_config, ¶m_sets)); |
| 295 frame_buf->insert(frame_buf->begin(), |
| 296 param_sets.begin(), param_sets.end()); |
| 297 if (!subsamples->empty()) |
| 298 (*subsamples)[0].clear_bytes += param_sets.size(); |
| 299 } |
| 300 return true; |
| 301 } |
| 302 |
252 bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, | 303 bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, |
253 BufferQueue* video_buffers, | 304 BufferQueue* video_buffers, |
254 bool* err) { | 305 bool* err) { |
255 if (!runs_->RunIsValid()) { | 306 if (!runs_->IsRunValid()) { |
256 // Flush any buffers we've gotten in this chunk so that buffers don't | 307 // Flush any buffers we've gotten in this chunk so that buffers don't |
257 // cross NewSegment() calls | 308 // cross NewSegment() calls |
258 *err = !SendAndFlushSamples(audio_buffers, video_buffers); | 309 *err = !SendAndFlushSamples(audio_buffers, video_buffers); |
259 if (*err) return false; | 310 if (*err) return false; |
260 ChangeState(kParsingBoxes); | 311 ChangeState(kParsingBoxes); |
261 return true; | 312 return true; |
262 } | 313 } |
263 | 314 |
264 if (!runs_->SampleIsValid()) { | 315 if (!runs_->IsSampleValid()) { |
265 runs_->AdvanceRun(); | 316 runs_->AdvanceRun(); |
266 return true; | 317 return true; |
267 } | 318 } |
268 | 319 |
269 DCHECK(!(*err)); | 320 DCHECK(!(*err)); |
270 | 321 |
271 const uint8* buf; | 322 const uint8* buf; |
272 int size; | 323 int buf_size; |
273 queue_.Peek(&buf, &size); | 324 queue_.Peek(&buf, &buf_size); |
274 if (!size) return false; | 325 if (!buf_size) return false; |
275 | 326 |
276 bool audio = has_audio_ && audio_track_id_ == runs_->track_id(); | 327 bool audio = has_audio_ && audio_track_id_ == runs_->track_id(); |
277 bool video = has_video_ && video_track_id_ == runs_->track_id(); | 328 bool video = has_video_ && video_track_id_ == runs_->track_id(); |
278 | 329 |
279 // Skip this entire track if it's not one we're interested in | 330 // Skip this entire track if it's not one we're interested in |
280 if (!audio && !video) runs_->AdvanceRun(); | 331 if (!audio && !video) runs_->AdvanceRun(); |
281 | 332 |
282 queue_.PeekAt(runs_->sample_offset() + moof_head_, &buf, &size); | 333 // Attempt to cache the auxiliary information first. Aux info is usually |
283 if (size < runs_->sample_size()) return false; | 334 // placed in a contiguous block before the sample data, rather than being |
| 335 // interleaved. If we didn't cache it, this would require that we retain the |
| 336 // start of the segment buffer while reading samples. Aux info is typically |
| 337 // quite small compared to sample data, so this pattern is useful on |
| 338 // memory-constrained devices where the source buffer consumes a substantial |
| 339 // portion of the total system memory. |
| 340 if (runs_->AuxInfoNeedsToBeCached()) { |
| 341 queue_.PeekAt(runs_->aux_info_offset() + moof_head_, &buf, &buf_size); |
| 342 if (buf_size < runs_->aux_info_size()) return false; |
| 343 *err = !runs_->CacheAuxInfo(buf, buf_size); |
| 344 return !*err; |
| 345 } |
| 346 |
| 347 queue_.PeekAt(runs_->sample_offset() + moof_head_, &buf, &buf_size); |
| 348 if (buf_size < runs_->sample_size()) return false; |
| 349 |
| 350 scoped_ptr<DecryptConfig> decrypt_config; |
| 351 if (runs_->is_encrypted()) |
| 352 decrypt_config = runs_->GetDecryptConfig(); |
284 | 353 |
285 std::vector<uint8> frame_buf(buf, buf + runs_->sample_size()); | 354 std::vector<uint8> frame_buf(buf, buf + runs_->sample_size()); |
286 if (video) { | 355 if (video) { |
287 const AVCDecoderConfigurationRecord& avc_config = | 356 std::vector<SubsampleEntry> subsamples; |
288 runs_->video_description().avcc; | 357 if (decrypt_config.get()) |
289 RCHECK(AVC::ConvertToAnnexB(avc_config.length_size, &frame_buf)); | 358 subsamples = decrypt_config->subsamples(); |
290 if (runs_->is_keyframe()) | 359 RCHECK(PrepareAVCBuffer(runs_->video_description().avcc, |
291 RCHECK(AVC::InsertParameterSets(avc_config, &frame_buf)); | 360 &frame_buf, &subsamples)); |
| 361 if (!subsamples.empty()) { |
| 362 decrypt_config.reset(new DecryptConfig( |
| 363 decrypt_config->key_id(), |
| 364 decrypt_config->key_id_size(), |
| 365 decrypt_config->iv(), |
| 366 subsamples)); |
| 367 } |
292 } | 368 } |
293 | 369 |
294 scoped_refptr<StreamParserBuffer> stream_buf = | 370 scoped_refptr<StreamParserBuffer> stream_buf = |
295 StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(), | 371 StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(), |
296 runs_->is_keyframe()); | 372 runs_->is_keyframe()); |
297 | 373 |
| 374 if (runs_->is_encrypted()) |
| 375 stream_buf->SetDecryptConfig(decrypt_config.Pass()); |
| 376 |
298 stream_buf->SetDuration(runs_->duration()); | 377 stream_buf->SetDuration(runs_->duration()); |
299 stream_buf->SetTimestamp(runs_->cts()); | 378 stream_buf->SetTimestamp(runs_->cts()); |
300 stream_buf->SetDecodeTimestamp(runs_->dts()); | 379 stream_buf->SetDecodeTimestamp(runs_->dts()); |
301 | 380 |
302 DVLOG(3) << "Pushing frame: aud=" << audio | 381 DVLOG(3) << "Pushing frame: aud=" << audio |
303 << ", key=" << runs_->is_keyframe() | 382 << ", key=" << runs_->is_keyframe() |
304 << ", dur=" << runs_->duration().InMilliseconds() | 383 << ", dur=" << runs_->duration().InMilliseconds() |
305 << ", dts=" << runs_->dts().InMilliseconds() | 384 << ", dts=" << runs_->dts().InMilliseconds() |
306 << ", cts=" << runs_->cts().InMilliseconds() | 385 << ", cts=" << runs_->cts().InMilliseconds() |
307 << ", size=" << runs_->sample_size(); | 386 << ", size=" << runs_->sample_size(); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
356 return true; | 435 return true; |
357 } | 436 } |
358 | 437 |
359 void MP4StreamParser::ChangeState(State new_state) { | 438 void MP4StreamParser::ChangeState(State new_state) { |
360 DVLOG(2) << "Changing state: " << new_state; | 439 DVLOG(2) << "Changing state: " << new_state; |
361 state_ = new_state; | 440 state_ = new_state; |
362 } | 441 } |
363 | 442 |
364 } // namespace mp4 | 443 } // namespace mp4 |
365 } // namespace media | 444 } // namespace media |
OLD | NEW |