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/filters/ffmpeg_demuxer.h" | 5 #include "media/filters/ffmpeg_demuxer.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/base64.h" | 10 #include "base/base64.h" |
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
265 | 265 |
266 // | 266 // |
267 // FFmpegDemuxer | 267 // FFmpegDemuxer |
268 // | 268 // |
269 FFmpegDemuxer::FFmpegDemuxer( | 269 FFmpegDemuxer::FFmpegDemuxer( |
270 const scoped_refptr<base::MessageLoopProxy>& message_loop, | 270 const scoped_refptr<base::MessageLoopProxy>& message_loop, |
271 const scoped_refptr<DataSource>& data_source, | 271 const scoped_refptr<DataSource>& data_source, |
272 const FFmpegNeedKeyCB& need_key_cb) | 272 const FFmpegNeedKeyCB& need_key_cb) |
273 : host_(NULL), | 273 : host_(NULL), |
274 message_loop_(message_loop), | 274 message_loop_(message_loop), |
275 weak_factory_(this), | |
275 blocking_thread_("FFmpegDemuxer"), | 276 blocking_thread_("FFmpegDemuxer"), |
276 pending_read_(false), | 277 pending_read_(false), |
277 pending_seek_(false), | 278 pending_seek_(false), |
278 data_source_(data_source), | 279 data_source_(data_source), |
279 bitrate_(0), | 280 bitrate_(0), |
280 start_time_(kNoTimestamp()), | 281 start_time_(kNoTimestamp()), |
281 audio_disabled_(false), | 282 audio_disabled_(false), |
282 duration_known_(false), | 283 duration_known_(false), |
283 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind( | 284 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind( |
284 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))), | 285 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))), |
285 need_key_cb_(need_key_cb) { | 286 need_key_cb_(need_key_cb) { |
286 DCHECK(message_loop_); | 287 DCHECK(message_loop_); |
287 DCHECK(data_source_); | 288 DCHECK(data_source_); |
288 } | 289 } |
289 | 290 |
290 FFmpegDemuxer::~FFmpegDemuxer() {} | 291 FFmpegDemuxer::~FFmpegDemuxer() {} |
291 | 292 |
292 void FFmpegDemuxer::Stop(const base::Closure& callback) { | 293 void FFmpegDemuxer::Stop(const base::Closure& callback) { |
293 DCHECK(message_loop_->BelongsToCurrentThread()); | 294 DCHECK(message_loop_->BelongsToCurrentThread()); |
294 url_protocol_.Abort(); | 295 url_protocol_.Abort(); |
295 data_source_->Stop(BindToCurrentLoop(base::Bind( | 296 data_source_->Stop(BindToCurrentLoop(base::Bind( |
296 &FFmpegDemuxer::OnDataSourceStopped, this, BindToCurrentLoop(callback)))); | 297 &FFmpegDemuxer::OnDataSourceStopped, weak_this_, |
298 BindToCurrentLoop(callback)))); | |
297 } | 299 } |
298 | 300 |
299 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { | 301 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
300 DCHECK(message_loop_->BelongsToCurrentThread()); | 302 DCHECK(message_loop_->BelongsToCurrentThread()); |
301 CHECK(!pending_seek_); | 303 CHECK(!pending_seek_); |
302 | 304 |
303 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, | 305 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, |
304 // otherwise we can end up waiting for a pre-seek read to complete even though | 306 // otherwise we can end up waiting for a pre-seek read to complete even though |
305 // we know we're going to drop it on the floor. | 307 // we know we're going to drop it on the floor. |
306 | 308 |
307 // Always seek to a timestamp less than or equal to the desired timestamp. | 309 // Always seek to a timestamp less than or equal to the desired timestamp. |
308 int flags = AVSEEK_FLAG_BACKWARD; | 310 int flags = AVSEEK_FLAG_BACKWARD; |
309 | 311 |
310 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg | 312 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg |
311 // will attempt to use the lowest-index video stream, if present, followed by | 313 // will attempt to use the lowest-index video stream, if present, followed by |
312 // the lowest-index audio stream. | 314 // the lowest-index audio stream. |
313 pending_seek_ = true; | 315 pending_seek_ = true; |
314 base::PostTaskAndReplyWithResult( | 316 base::PostTaskAndReplyWithResult( |
315 blocking_thread_.message_loop_proxy(), FROM_HERE, | 317 blocking_thread_.message_loop_proxy(), FROM_HERE, |
316 base::Bind(&av_seek_frame, glue_->format_context(), -1, | 318 base::Bind(&av_seek_frame, glue_->format_context(), -1, |
317 time.InMicroseconds(), flags), | 319 time.InMicroseconds(), flags), |
318 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, this, cb)); | 320 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_this_, cb)); |
319 } | 321 } |
320 | 322 |
321 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { | 323 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { |
322 DCHECK(message_loop_->BelongsToCurrentThread()); | 324 DCHECK(message_loop_->BelongsToCurrentThread()); |
323 data_source_->SetPlaybackRate(playback_rate); | 325 data_source_->SetPlaybackRate(playback_rate); |
324 } | 326 } |
325 | 327 |
326 void FFmpegDemuxer::OnAudioRendererDisabled() { | 328 void FFmpegDemuxer::OnAudioRendererDisabled() { |
327 DCHECK(message_loop_->BelongsToCurrentThread()); | 329 DCHECK(message_loop_->BelongsToCurrentThread()); |
328 audio_disabled_ = true; | 330 audio_disabled_ = true; |
329 StreamVector::iterator iter; | 331 StreamVector::iterator iter; |
330 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 332 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
331 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { | 333 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { |
332 (*iter)->Stop(); | 334 (*iter)->Stop(); |
333 } | 335 } |
334 } | 336 } |
335 } | 337 } |
336 | 338 |
337 void FFmpegDemuxer::Initialize(DemuxerHost* host, | 339 void FFmpegDemuxer::Initialize(DemuxerHost* host, |
338 const PipelineStatusCB& status_cb) { | 340 const PipelineStatusCB& status_cb) { |
339 DCHECK(message_loop_->BelongsToCurrentThread()); | 341 DCHECK(message_loop_->BelongsToCurrentThread()); |
340 host_ = host; | 342 host_ = host; |
343 weak_this_ = weak_factory_.GetWeakPtr(); | |
acolwell GONE FROM CHROMIUM
2013/04/17 20:24:53
nit: Why is this deferred until here? Why not do t
scherkus (not reviewing)
2013/04/19 01:07:22
WeakPtr binds to thread it's created on.
Since we
| |
341 | 344 |
342 // TODO(scherkus): DataSource should have a host by this point, | 345 // TODO(scherkus): DataSource should have a host by this point, |
343 // see http://crbug.com/122071 | 346 // see http://crbug.com/122071 |
344 data_source_->set_host(host); | 347 data_source_->set_host(host); |
345 | 348 |
346 glue_.reset(new FFmpegGlue(&url_protocol_)); | 349 glue_.reset(new FFmpegGlue(&url_protocol_)); |
347 AVFormatContext* format_context = glue_->format_context(); | 350 AVFormatContext* format_context = glue_->format_context(); |
348 | 351 |
349 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we | 352 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we |
350 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is | 353 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is |
351 // available, so add a metadata entry to ensure some is always present. | 354 // available, so add a metadata entry to ensure some is always present. |
352 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); | 355 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); |
353 | 356 |
354 // Open the AVFormatContext using our glue layer. | 357 // Open the AVFormatContext using our glue layer. |
355 CHECK(blocking_thread_.Start()); | 358 CHECK(blocking_thread_.Start()); |
356 base::PostTaskAndReplyWithResult( | 359 base::PostTaskAndReplyWithResult( |
357 blocking_thread_.message_loop_proxy(), FROM_HERE, | 360 blocking_thread_.message_loop_proxy(), FROM_HERE, |
358 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), | 361 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), |
359 base::Bind(&FFmpegDemuxer::OnOpenContextDone, this, status_cb)); | 362 base::Bind(&FFmpegDemuxer::OnOpenContextDone, weak_this_, status_cb)); |
360 } | 363 } |
361 | 364 |
362 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( | 365 DemuxerStream* FFmpegDemuxer::GetStream(DemuxerStream::Type type) { |
363 DemuxerStream::Type type) { | |
364 DCHECK(message_loop_->BelongsToCurrentThread()); | 366 DCHECK(message_loop_->BelongsToCurrentThread()); |
365 return GetFFmpegStream(type); | 367 return GetFFmpegStream(type); |
366 } | 368 } |
367 | 369 |
368 scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream( | 370 FFmpegDemuxerStream* FFmpegDemuxer::GetFFmpegStream( |
369 DemuxerStream::Type type) const { | 371 DemuxerStream::Type type) const { |
370 StreamVector::const_iterator iter; | 372 StreamVector::const_iterator iter; |
371 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 373 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
372 if (*iter && (*iter)->type() == type) { | 374 if (*iter && (*iter)->type() == type) { |
373 return *iter; | 375 return *iter; |
374 } | 376 } |
375 } | 377 } |
376 return NULL; | 378 return NULL; |
377 } | 379 } |
378 | 380 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
428 if (!result) { | 430 if (!result) { |
429 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | 431 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); |
430 return; | 432 return; |
431 } | 433 } |
432 | 434 |
433 // Fully initialize AVFormatContext by parsing the stream a little. | 435 // Fully initialize AVFormatContext by parsing the stream a little. |
434 base::PostTaskAndReplyWithResult( | 436 base::PostTaskAndReplyWithResult( |
435 blocking_thread_.message_loop_proxy(), FROM_HERE, | 437 blocking_thread_.message_loop_proxy(), FROM_HERE, |
436 base::Bind(&avformat_find_stream_info, glue_->format_context(), | 438 base::Bind(&avformat_find_stream_info, glue_->format_context(), |
437 static_cast<AVDictionary**>(NULL)), | 439 static_cast<AVDictionary**>(NULL)), |
438 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, this, status_cb)); | 440 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, weak_this_, status_cb)); |
439 } | 441 } |
440 | 442 |
441 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, | 443 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, |
442 int result) { | 444 int result) { |
443 DCHECK(message_loop_->BelongsToCurrentThread()); | 445 DCHECK(message_loop_->BelongsToCurrentThread()); |
444 if (!blocking_thread_.IsRunning()) { | 446 if (!blocking_thread_.IsRunning()) { |
445 status_cb.Run(PIPELINE_ERROR_ABORT); | 447 status_cb.Run(PIPELINE_ERROR_ABORT); |
446 return; | 448 return; |
447 } | 449 } |
448 | 450 |
(...skipping 25 matching lines...) Expand all Loading... | |
474 continue; | 476 continue; |
475 // Ensure the codec is supported. | 477 // Ensure the codec is supported. |
476 if (CodecIDToVideoCodec(codec_context->codec_id) == kUnknownVideoCodec) | 478 if (CodecIDToVideoCodec(codec_context->codec_id) == kUnknownVideoCodec) |
477 continue; | 479 continue; |
478 found_video_stream = true; | 480 found_video_stream = true; |
479 } else { | 481 } else { |
480 continue; | 482 continue; |
481 } | 483 } |
482 | 484 |
483 AVStream* stream = format_context->streams[i]; | 485 AVStream* stream = format_context->streams[i]; |
484 scoped_refptr<FFmpegDemuxerStream> demuxer_stream( | 486 streams_[i] = new FFmpegDemuxerStream(this, stream); |
485 new FFmpegDemuxerStream(this, stream)); | 487 max_duration = std::max(max_duration, streams_[i]->duration()); |
486 | |
487 streams_[i] = demuxer_stream; | |
488 max_duration = std::max(max_duration, demuxer_stream->duration()); | |
489 | 488 |
490 if (stream->first_dts != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 489 if (stream->first_dts != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
491 const base::TimeDelta first_dts = ConvertFromTimeBase( | 490 const base::TimeDelta first_dts = ConvertFromTimeBase( |
492 stream->time_base, stream->first_dts); | 491 stream->time_base, stream->first_dts); |
493 if (start_time_ == kNoTimestamp() || first_dts < start_time_) | 492 if (start_time_ == kNoTimestamp() || first_dts < start_time_) |
494 start_time_ = first_dts; | 493 start_time_ = first_dts; |
495 } | 494 } |
496 } | 495 } |
497 | 496 |
498 if (!found_audio_stream && !found_video_stream) { | 497 if (!found_audio_stream && !found_video_stream) { |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
579 // Allocate and read an AVPacket from the media. Save |packet_ptr| since | 578 // Allocate and read an AVPacket from the media. Save |packet_ptr| since |
580 // evaluation order of packet.get() and base::Passed(&packet) is | 579 // evaluation order of packet.get() and base::Passed(&packet) is |
581 // undefined. | 580 // undefined. |
582 ScopedAVPacket packet(new AVPacket()); | 581 ScopedAVPacket packet(new AVPacket()); |
583 AVPacket* packet_ptr = packet.get(); | 582 AVPacket* packet_ptr = packet.get(); |
584 | 583 |
585 pending_read_ = true; | 584 pending_read_ = true; |
586 base::PostTaskAndReplyWithResult( | 585 base::PostTaskAndReplyWithResult( |
587 blocking_thread_.message_loop_proxy(), FROM_HERE, | 586 blocking_thread_.message_loop_proxy(), FROM_HERE, |
588 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), | 587 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), |
589 base::Bind(&FFmpegDemuxer::OnReadFrameDone, this, base::Passed(&packet))); | 588 base::Bind(&FFmpegDemuxer::OnReadFrameDone, weak_this_, |
589 base::Passed(&packet))); | |
590 } | 590 } |
591 | 591 |
592 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { | 592 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { |
593 DCHECK(message_loop_->BelongsToCurrentThread()); | 593 DCHECK(message_loop_->BelongsToCurrentThread()); |
594 DCHECK(pending_read_); | 594 DCHECK(pending_read_); |
595 pending_read_ = false; | 595 pending_read_ = false; |
596 | 596 |
597 if (!blocking_thread_.IsRunning() || pending_seek_) { | 597 if (!blocking_thread_.IsRunning() || pending_seek_) { |
598 return; | 598 return; |
599 } | 599 } |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
709 } | 709 } |
710 | 710 |
711 void FFmpegDemuxer::NotifyCapacityAvailable() { | 711 void FFmpegDemuxer::NotifyCapacityAvailable() { |
712 DCHECK(message_loop_->BelongsToCurrentThread()); | 712 DCHECK(message_loop_->BelongsToCurrentThread()); |
713 ReadFrameIfNeeded(); | 713 ReadFrameIfNeeded(); |
714 } | 714 } |
715 | 715 |
716 void FFmpegDemuxer::NotifyBufferingChanged() { | 716 void FFmpegDemuxer::NotifyBufferingChanged() { |
717 DCHECK(message_loop_->BelongsToCurrentThread()); | 717 DCHECK(message_loop_->BelongsToCurrentThread()); |
718 Ranges<base::TimeDelta> buffered; | 718 Ranges<base::TimeDelta> buffered; |
719 scoped_refptr<FFmpegDemuxerStream> audio = | 719 FFmpegDemuxerStream* audio = |
720 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); | 720 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); |
721 scoped_refptr<FFmpegDemuxerStream> video = | 721 FFmpegDemuxerStream* video = GetFFmpegStream(DemuxerStream::VIDEO); |
722 GetFFmpegStream(DemuxerStream::VIDEO); | |
723 if (audio && video) { | 722 if (audio && video) { |
724 buffered = audio->GetBufferedRanges().IntersectionWith( | 723 buffered = audio->GetBufferedRanges().IntersectionWith( |
725 video->GetBufferedRanges()); | 724 video->GetBufferedRanges()); |
726 } else if (audio) { | 725 } else if (audio) { |
727 buffered = audio->GetBufferedRanges(); | 726 buffered = audio->GetBufferedRanges(); |
728 } else if (video) { | 727 } else if (video) { |
729 buffered = video->GetBufferedRanges(); | 728 buffered = video->GetBufferedRanges(); |
730 } | 729 } |
731 for (size_t i = 0; i < buffered.size(); ++i) | 730 for (size_t i = 0; i < buffered.size(); ++i) |
732 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 731 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
733 } | 732 } |
734 | 733 |
735 void FFmpegDemuxer::OnDataSourceError() { | 734 void FFmpegDemuxer::OnDataSourceError() { |
736 host_->OnDemuxerError(PIPELINE_ERROR_READ); | 735 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
737 } | 736 } |
738 | 737 |
739 } // namespace media | 738 } // namespace media |
OLD | NEW |