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 351 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
362 // | 362 // |
363 // FFmpegDemuxer | 363 // FFmpegDemuxer |
364 // | 364 // |
365 FFmpegDemuxer::FFmpegDemuxer( | 365 FFmpegDemuxer::FFmpegDemuxer( |
366 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 366 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
367 DataSource* data_source, | 367 DataSource* data_source, |
368 const NeedKeyCB& need_key_cb, | 368 const NeedKeyCB& need_key_cb, |
369 const scoped_refptr<MediaLog>& media_log) | 369 const scoped_refptr<MediaLog>& media_log) |
370 : host_(NULL), | 370 : host_(NULL), |
371 task_runner_(task_runner), | 371 task_runner_(task_runner), |
372 weak_factory_(this), | |
373 blocking_thread_("FFmpegDemuxer"), | 372 blocking_thread_("FFmpegDemuxer"), |
374 pending_read_(false), | 373 pending_read_(false), |
375 pending_seek_(false), | 374 pending_seek_(false), |
376 data_source_(data_source), | 375 data_source_(data_source), |
377 media_log_(media_log), | 376 media_log_(media_log), |
378 bitrate_(0), | 377 bitrate_(0), |
379 start_time_(kNoTimestamp()), | 378 start_time_(kNoTimestamp()), |
380 audio_disabled_(false), | 379 audio_disabled_(false), |
381 text_enabled_(false), | 380 text_enabled_(false), |
382 duration_known_(false), | 381 duration_known_(false), |
383 need_key_cb_(need_key_cb) { | 382 need_key_cb_(need_key_cb), |
| 383 weak_factory_(this) { |
384 DCHECK(task_runner_.get()); | 384 DCHECK(task_runner_.get()); |
385 DCHECK(data_source_); | 385 DCHECK(data_source_); |
386 } | 386 } |
387 | 387 |
388 FFmpegDemuxer::~FFmpegDemuxer() {} | 388 FFmpegDemuxer::~FFmpegDemuxer() {} |
389 | 389 |
390 void FFmpegDemuxer::Stop(const base::Closure& callback) { | 390 void FFmpegDemuxer::Stop(const base::Closure& callback) { |
391 DCHECK(task_runner_->BelongsToCurrentThread()); | 391 DCHECK(task_runner_->BelongsToCurrentThread()); |
392 url_protocol_->Abort(); | 392 url_protocol_->Abort(); |
393 data_source_->Stop(BindToCurrentLoop(base::Bind( | 393 data_source_->Stop( |
394 &FFmpegDemuxer::OnDataSourceStopped, weak_this_, | 394 BindToCurrentLoop(base::Bind(&FFmpegDemuxer::OnDataSourceStopped, |
395 BindToCurrentLoop(callback)))); | 395 weak_factory_.GetWeakPtr(), |
| 396 BindToCurrentLoop(callback)))); |
396 data_source_ = NULL; | 397 data_source_ = NULL; |
397 } | 398 } |
398 | 399 |
399 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { | 400 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
400 DCHECK(task_runner_->BelongsToCurrentThread()); | 401 DCHECK(task_runner_->BelongsToCurrentThread()); |
401 CHECK(!pending_seek_); | 402 CHECK(!pending_seek_); |
402 | 403 |
403 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, | 404 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, |
404 // otherwise we can end up waiting for a pre-seek read to complete even though | 405 // otherwise we can end up waiting for a pre-seek read to complete even though |
405 // we know we're going to drop it on the floor. | 406 // we know we're going to drop it on the floor. |
406 | 407 |
407 // Always seek to a timestamp less than or equal to the desired timestamp. | 408 // Always seek to a timestamp less than or equal to the desired timestamp. |
408 int flags = AVSEEK_FLAG_BACKWARD; | 409 int flags = AVSEEK_FLAG_BACKWARD; |
409 | 410 |
410 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg | 411 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg |
411 // will attempt to use the lowest-index video stream, if present, followed by | 412 // will attempt to use the lowest-index video stream, if present, followed by |
412 // the lowest-index audio stream. | 413 // the lowest-index audio stream. |
413 pending_seek_ = true; | 414 pending_seek_ = true; |
414 base::PostTaskAndReplyWithResult( | 415 base::PostTaskAndReplyWithResult( |
415 blocking_thread_.message_loop_proxy().get(), | 416 blocking_thread_.message_loop_proxy().get(), |
416 FROM_HERE, | 417 FROM_HERE, |
417 base::Bind(&av_seek_frame, | 418 base::Bind(&av_seek_frame, |
418 glue_->format_context(), | 419 glue_->format_context(), |
419 -1, | 420 -1, |
420 time.InMicroseconds(), | 421 time.InMicroseconds(), |
421 flags), | 422 flags), |
422 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_this_, cb)); | 423 base::Bind( |
| 424 &FFmpegDemuxer::OnSeekFrameDone, weak_factory_.GetWeakPtr(), cb)); |
423 } | 425 } |
424 | 426 |
425 void FFmpegDemuxer::OnAudioRendererDisabled() { | 427 void FFmpegDemuxer::OnAudioRendererDisabled() { |
426 DCHECK(task_runner_->BelongsToCurrentThread()); | 428 DCHECK(task_runner_->BelongsToCurrentThread()); |
427 audio_disabled_ = true; | 429 audio_disabled_ = true; |
428 StreamVector::iterator iter; | 430 StreamVector::iterator iter; |
429 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 431 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
430 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { | 432 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { |
431 (*iter)->Stop(); | 433 (*iter)->Stop(); |
432 } | 434 } |
433 } | 435 } |
434 } | 436 } |
435 | 437 |
436 void FFmpegDemuxer::Initialize(DemuxerHost* host, | 438 void FFmpegDemuxer::Initialize(DemuxerHost* host, |
437 const PipelineStatusCB& status_cb, | 439 const PipelineStatusCB& status_cb, |
438 bool enable_text_tracks) { | 440 bool enable_text_tracks) { |
439 DCHECK(task_runner_->BelongsToCurrentThread()); | 441 DCHECK(task_runner_->BelongsToCurrentThread()); |
440 host_ = host; | 442 host_ = host; |
441 weak_this_ = weak_factory_.GetWeakPtr(); | |
442 text_enabled_ = enable_text_tracks; | 443 text_enabled_ = enable_text_tracks; |
443 | 444 |
444 // TODO(scherkus): DataSource should have a host by this point, | 445 // TODO(scherkus): DataSource should have a host by this point, |
445 // see http://crbug.com/122071 | 446 // see http://crbug.com/122071 |
446 data_source_->set_host(host); | 447 data_source_->set_host(host); |
447 | 448 |
448 url_protocol_.reset(new BlockingUrlProtocol(data_source_, BindToCurrentLoop( | 449 url_protocol_.reset(new BlockingUrlProtocol(data_source_, BindToCurrentLoop( |
449 base::Bind(&FFmpegDemuxer::OnDataSourceError, base::Unretained(this))))); | 450 base::Bind(&FFmpegDemuxer::OnDataSourceError, base::Unretained(this))))); |
450 glue_.reset(new FFmpegGlue(url_protocol_.get())); | 451 glue_.reset(new FFmpegGlue(url_protocol_.get())); |
451 AVFormatContext* format_context = glue_->format_context(); | 452 AVFormatContext* format_context = glue_->format_context(); |
452 | 453 |
453 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we | 454 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we |
454 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is | 455 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is |
455 // available, so add a metadata entry to ensure some is always present. | 456 // available, so add a metadata entry to ensure some is always present. |
456 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); | 457 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); |
457 | 458 |
458 // Open the AVFormatContext using our glue layer. | 459 // Open the AVFormatContext using our glue layer. |
459 CHECK(blocking_thread_.Start()); | 460 CHECK(blocking_thread_.Start()); |
460 base::PostTaskAndReplyWithResult( | 461 base::PostTaskAndReplyWithResult( |
461 blocking_thread_.message_loop_proxy().get(), | 462 blocking_thread_.message_loop_proxy().get(), |
462 FROM_HERE, | 463 FROM_HERE, |
463 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), | 464 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), |
464 base::Bind(&FFmpegDemuxer::OnOpenContextDone, weak_this_, status_cb)); | 465 base::Bind(&FFmpegDemuxer::OnOpenContextDone, |
| 466 weak_factory_.GetWeakPtr(), |
| 467 status_cb)); |
465 } | 468 } |
466 | 469 |
467 DemuxerStream* FFmpegDemuxer::GetStream(DemuxerStream::Type type) { | 470 DemuxerStream* FFmpegDemuxer::GetStream(DemuxerStream::Type type) { |
468 DCHECK(task_runner_->BelongsToCurrentThread()); | 471 DCHECK(task_runner_->BelongsToCurrentThread()); |
469 return GetFFmpegStream(type); | 472 return GetFFmpegStream(type); |
470 } | 473 } |
471 | 474 |
472 FFmpegDemuxerStream* FFmpegDemuxer::GetFFmpegStream( | 475 FFmpegDemuxerStream* FFmpegDemuxer::GetFFmpegStream( |
473 DemuxerStream::Type type) const { | 476 DemuxerStream::Type type) const { |
474 StreamVector::const_iterator iter; | 477 StreamVector::const_iterator iter; |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
553 return; | 556 return; |
554 } | 557 } |
555 | 558 |
556 // Fully initialize AVFormatContext by parsing the stream a little. | 559 // Fully initialize AVFormatContext by parsing the stream a little. |
557 base::PostTaskAndReplyWithResult( | 560 base::PostTaskAndReplyWithResult( |
558 blocking_thread_.message_loop_proxy().get(), | 561 blocking_thread_.message_loop_proxy().get(), |
559 FROM_HERE, | 562 FROM_HERE, |
560 base::Bind(&avformat_find_stream_info, | 563 base::Bind(&avformat_find_stream_info, |
561 glue_->format_context(), | 564 glue_->format_context(), |
562 static_cast<AVDictionary**>(NULL)), | 565 static_cast<AVDictionary**>(NULL)), |
563 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, weak_this_, status_cb)); | 566 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, |
| 567 weak_factory_.GetWeakPtr(), |
| 568 status_cb)); |
564 } | 569 } |
565 | 570 |
566 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, | 571 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, |
567 int result) { | 572 int result) { |
568 DCHECK(task_runner_->BelongsToCurrentThread()); | 573 DCHECK(task_runner_->BelongsToCurrentThread()); |
569 if (!blocking_thread_.IsRunning() || !data_source_) { | 574 if (!blocking_thread_.IsRunning() || !data_source_) { |
570 status_cb.Run(PIPELINE_ERROR_ABORT); | 575 status_cb.Run(PIPELINE_ERROR_ABORT); |
571 return; | 576 return; |
572 } | 577 } |
573 | 578 |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
786 // evaluation order of packet.get() and base::Passed(&packet) is | 791 // evaluation order of packet.get() and base::Passed(&packet) is |
787 // undefined. | 792 // undefined. |
788 ScopedAVPacket packet(new AVPacket()); | 793 ScopedAVPacket packet(new AVPacket()); |
789 AVPacket* packet_ptr = packet.get(); | 794 AVPacket* packet_ptr = packet.get(); |
790 | 795 |
791 pending_read_ = true; | 796 pending_read_ = true; |
792 base::PostTaskAndReplyWithResult( | 797 base::PostTaskAndReplyWithResult( |
793 blocking_thread_.message_loop_proxy().get(), | 798 blocking_thread_.message_loop_proxy().get(), |
794 FROM_HERE, | 799 FROM_HERE, |
795 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), | 800 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), |
796 base::Bind( | 801 base::Bind(&FFmpegDemuxer::OnReadFrameDone, |
797 &FFmpegDemuxer::OnReadFrameDone, weak_this_, base::Passed(&packet))); | 802 weak_factory_.GetWeakPtr(), |
| 803 base::Passed(&packet))); |
798 } | 804 } |
799 | 805 |
800 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { | 806 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { |
801 DCHECK(task_runner_->BelongsToCurrentThread()); | 807 DCHECK(task_runner_->BelongsToCurrentThread()); |
802 DCHECK(pending_read_); | 808 DCHECK(pending_read_); |
803 pending_read_ = false; | 809 pending_read_ = false; |
804 | 810 |
805 if (!blocking_thread_.IsRunning() || pending_seek_) { | 811 if (!blocking_thread_.IsRunning() || pending_seek_) { |
806 return; | 812 return; |
807 } | 813 } |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
976 } | 982 } |
977 for (size_t i = 0; i < buffered.size(); ++i) | 983 for (size_t i = 0; i < buffered.size(); ++i) |
978 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 984 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
979 } | 985 } |
980 | 986 |
981 void FFmpegDemuxer::OnDataSourceError() { | 987 void FFmpegDemuxer::OnDataSourceError() { |
982 host_->OnDemuxerError(PIPELINE_ERROR_READ); | 988 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
983 } | 989 } |
984 | 990 |
985 } // namespace media | 991 } // namespace media |
OLD | NEW |