| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "base/bind.h" | 5 #include "base/bind.h" |
| 6 #include "base/callback.h" | 6 #include "base/callback.h" |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
| 9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
| 10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
| (...skipping 344 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 355 const PipelineStatusCB& callback) { | 355 const PipelineStatusCB& callback) { |
| 356 message_loop_->PostTask( | 356 message_loop_->PostTask( |
| 357 FROM_HERE, | 357 FROM_HERE, |
| 358 base::Bind(&FFmpegDemuxer::InitializeTask, this, | 358 base::Bind(&FFmpegDemuxer::InitializeTask, this, |
| 359 make_scoped_refptr(data_source), | 359 make_scoped_refptr(data_source), |
| 360 callback)); | 360 callback)); |
| 361 } | 361 } |
| 362 | 362 |
| 363 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( | 363 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( |
| 364 DemuxerStream::Type type) { | 364 DemuxerStream::Type type) { |
| 365 DCHECK_GE(type, 0); | 365 StreamVector::iterator iter; |
| 366 DCHECK_LT(type, DemuxerStream::NUM_TYPES); | 366 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 367 return streams_[type]; | 367 if (*iter && (*iter)->type() == type) { |
| 368 return *iter; |
| 369 } |
| 370 } |
| 371 return NULL; |
| 368 } | 372 } |
| 369 | 373 |
| 370 base::TimeDelta FFmpegDemuxer::GetStartTime() const { | 374 base::TimeDelta FFmpegDemuxer::GetStartTime() const { |
| 371 return start_time_; | 375 return start_time_; |
| 372 } | 376 } |
| 373 | 377 |
| 374 size_t FFmpegDemuxer::Read(size_t size, uint8* data) { | 378 size_t FFmpegDemuxer::Read(size_t size, uint8* data) { |
| 375 DCHECK(data_source_); | 379 DCHECK(data_source_); |
| 376 | 380 |
| 377 // If read has ever failed, return with an error. | 381 // If read has ever failed, return with an error. |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 472 DCHECK(context); | 476 DCHECK(context); |
| 473 format_context_ = context; | 477 format_context_ = context; |
| 474 | 478 |
| 475 // Fully initialize AVFormatContext by parsing the stream a little. | 479 // Fully initialize AVFormatContext by parsing the stream a little. |
| 476 result = av_find_stream_info(format_context_); | 480 result = av_find_stream_info(format_context_); |
| 477 if (result < 0) { | 481 if (result < 0) { |
| 478 callback.Run(DEMUXER_ERROR_COULD_NOT_PARSE); | 482 callback.Run(DEMUXER_ERROR_COULD_NOT_PARSE); |
| 479 return; | 483 return; |
| 480 } | 484 } |
| 481 | 485 |
| 482 // Create demuxer streams for all supported streams. | 486 // Create demuxer stream entries for each possible AVStream. |
| 483 streams_.resize(DemuxerStream::NUM_TYPES); | 487 streams_.resize(format_context_->nb_streams); |
| 488 bool found_audio_stream = false; |
| 489 bool found_video_stream = false; |
| 490 |
| 484 base::TimeDelta max_duration; | 491 base::TimeDelta max_duration; |
| 485 bool no_supported_streams = true; | |
| 486 for (size_t i = 0; i < format_context_->nb_streams; ++i) { | 492 for (size_t i = 0; i < format_context_->nb_streams; ++i) { |
| 487 AVCodecContext* codec_context = format_context_->streams[i]->codec; | 493 AVCodecContext* codec_context = format_context_->streams[i]->codec; |
| 488 AVMediaType codec_type = codec_context->codec_type; | 494 AVMediaType codec_type = codec_context->codec_type; |
| 489 if (codec_type == AVMEDIA_TYPE_AUDIO || codec_type == AVMEDIA_TYPE_VIDEO) { | |
| 490 AVStream* stream = format_context_->streams[i]; | |
| 491 scoped_refptr<FFmpegDemuxerStream> demuxer_stream( | |
| 492 new FFmpegDemuxerStream(this, stream)); | |
| 493 if (!streams_[demuxer_stream->type()]) { | |
| 494 no_supported_streams = false; | |
| 495 streams_[demuxer_stream->type()] = demuxer_stream; | |
| 496 max_duration = std::max(max_duration, demuxer_stream->duration()); | |
| 497 | 495 |
| 498 if (stream->first_dts != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 496 if (codec_type == AVMEDIA_TYPE_AUDIO) { |
| 499 const base::TimeDelta first_dts = ConvertFromTimeBase( | 497 if (found_audio_stream) |
| 500 stream->time_base, stream->first_dts); | 498 continue; |
| 501 if (start_time_ == kNoTimestamp || first_dts < start_time_) | 499 found_audio_stream = true; |
| 502 start_time_ = first_dts; | 500 } else if (codec_type == AVMEDIA_TYPE_VIDEO) { |
| 503 } | 501 if (found_video_stream) |
| 504 } | 502 continue; |
| 505 packet_streams_.push_back(demuxer_stream); | 503 found_video_stream = true; |
| 506 } else { | 504 } else { |
| 507 packet_streams_.push_back(NULL); | 505 continue; |
| 506 } |
| 507 |
| 508 AVStream* stream = format_context_->streams[i]; |
| 509 scoped_refptr<FFmpegDemuxerStream> demuxer_stream( |
| 510 new FFmpegDemuxerStream(this, stream)); |
| 511 |
| 512 streams_[i] = demuxer_stream; |
| 513 max_duration = std::max(max_duration, demuxer_stream->duration()); |
| 514 |
| 515 if (stream->first_dts != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
| 516 const base::TimeDelta first_dts = ConvertFromTimeBase( |
| 517 stream->time_base, stream->first_dts); |
| 518 if (start_time_ == kNoTimestamp || first_dts < start_time_) |
| 519 start_time_ = first_dts; |
| 508 } | 520 } |
| 509 } | 521 } |
| 510 if (no_supported_streams) { | 522 |
| 523 if (!found_audio_stream && !found_video_stream) { |
| 511 callback.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); | 524 callback.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); |
| 512 return; | 525 return; |
| 513 } | 526 } |
| 527 |
| 514 if (format_context_->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 528 if (format_context_->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
| 515 // If there is a duration value in the container use that to find the | 529 // If there is a duration value in the container use that to find the |
| 516 // maximum between it and the duration from A/V streams. | 530 // maximum between it and the duration from A/V streams. |
| 517 const AVRational av_time_base = {1, AV_TIME_BASE}; | 531 const AVRational av_time_base = {1, AV_TIME_BASE}; |
| 518 max_duration = | 532 max_duration = |
| 519 std::max(max_duration, | 533 std::max(max_duration, |
| 520 ConvertFromTimeBase(av_time_base, format_context_->duration)); | 534 ConvertFromTimeBase(av_time_base, format_context_->duration)); |
| 521 } else { | 535 } else { |
| 522 // The duration is unknown, in which case this is likely a live stream. | 536 // The duration is unknown, in which case this is likely a live stream. |
| 523 max_duration = kInfiniteDuration; | 537 max_duration = kInfiniteDuration; |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 635 // the event. | 649 // the event. |
| 636 StreamHasEnded(); | 650 StreamHasEnded(); |
| 637 return; | 651 return; |
| 638 } | 652 } |
| 639 | 653 |
| 640 // Queue the packet with the appropriate stream. | 654 // Queue the packet with the appropriate stream. |
| 641 // TODO(scherkus): should we post this back to the pipeline thread? I'm | 655 // TODO(scherkus): should we post this back to the pipeline thread? I'm |
| 642 // worried about downstream filters (i.e., decoders) executing on this | 656 // worried about downstream filters (i.e., decoders) executing on this |
| 643 // thread. | 657 // thread. |
| 644 DCHECK_GE(packet->stream_index, 0); | 658 DCHECK_GE(packet->stream_index, 0); |
| 645 DCHECK_LT(packet->stream_index, static_cast<int>(packet_streams_.size())); | 659 DCHECK_LT(packet->stream_index, static_cast<int>(streams_.size())); |
| 646 FFmpegDemuxerStream* demuxer_stream = NULL; | 660 |
| 647 size_t i = packet->stream_index; | |
| 648 // Defend against ffmpeg giving us a bad stream index. | 661 // Defend against ffmpeg giving us a bad stream index. |
| 649 if (i < packet_streams_.size()) { | 662 if (packet->stream_index >= 0 && |
| 650 demuxer_stream = packet_streams_[i]; | 663 packet->stream_index < static_cast<int>(streams_.size()) && |
| 651 } | 664 streams_[packet->stream_index]) { |
| 652 if (demuxer_stream) { | 665 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index]; |
| 653 // Queue the packet with the appropriate stream. The stream takes | 666 |
| 654 // ownership of the AVPacket. | 667 // If a packet is returned by FFmpeg's av_parser_parse2() |
| 655 if (packet.get()) { | 668 // the packet will reference an inner memory of FFmpeg. |
| 656 // If a packet is returned by FFmpeg's av_parser_parse2() | 669 // In this case, the packet's "destruct" member is NULL, |
| 657 // the packet will reference an inner memory of FFmpeg. | 670 // and it MUST be duplicated. This fixes issue with MP3 and possibly |
| 658 // In this case, the packet's "destruct" member is NULL, | 671 // other codecs. It is safe to call this function even if the packet does |
| 659 // and it MUST be duplicated. This fixes issue with MP3 and possibly | 672 // not refer to inner memory from FFmpeg. |
| 660 // other codecs. It is safe to call this function even if the packet does | 673 av_dup_packet(packet.get()); |
| 661 // not refer to inner memory from FFmpeg. | 674 |
| 662 av_dup_packet(packet.get()); | 675 // The stream takes ownership of the AVPacket. |
| 663 demuxer_stream->EnqueuePacket(packet.release()); | 676 demuxer_stream->EnqueuePacket(packet.release()); |
| 664 } | |
| 665 } | 677 } |
| 666 | 678 |
| 667 // Create a loop by posting another task. This allows seek and message loop | 679 // Create a loop by posting another task. This allows seek and message loop |
| 668 // quit tasks to get processed. | 680 // quit tasks to get processed. |
| 669 if (StreamsHavePendingReads()) { | 681 if (StreamsHavePendingReads()) { |
| 670 PostDemuxTask(); | 682 PostDemuxTask(); |
| 671 } | 683 } |
| 672 } | 684 } |
| 673 | 685 |
| 674 void FFmpegDemuxer::StopTask(const base::Closure& callback) { | 686 void FFmpegDemuxer::StopTask(const base::Closure& callback) { |
| 675 DCHECK_EQ(MessageLoop::current(), message_loop_); | 687 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 676 StreamVector::iterator iter; | 688 StreamVector::iterator iter; |
| 677 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 689 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 678 if (*iter) | 690 if (*iter) |
| 679 (*iter)->Stop(); | 691 (*iter)->Stop(); |
| 680 } | 692 } |
| 681 if (data_source_) { | 693 if (data_source_) { |
| 682 data_source_->Stop(callback); | 694 data_source_->Stop(callback); |
| 683 } else { | 695 } else { |
| 684 callback.Run(); | 696 callback.Run(); |
| 685 } | 697 } |
| 686 } | 698 } |
| 687 | 699 |
| 688 void FFmpegDemuxer::DisableAudioStreamTask() { | 700 void FFmpegDemuxer::DisableAudioStreamTask() { |
| 689 DCHECK_EQ(MessageLoop::current(), message_loop_); | 701 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 690 | |
| 691 StreamVector::iterator iter; | 702 StreamVector::iterator iter; |
| 692 for (size_t i = 0; i < packet_streams_.size(); ++i) { | 703 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 693 if (!packet_streams_[i]) | 704 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { |
| 694 continue; | 705 (*iter)->Stop(); |
| 695 | |
| 696 // If the codec type is audio, remove the reference. DemuxTask() will | |
| 697 // look for such reference, and this will result in deleting the | |
| 698 // audio packets after they are demuxed. | |
| 699 if (packet_streams_[i]->type() == DemuxerStream::AUDIO) { | |
| 700 packet_streams_[i] = NULL; | |
| 701 } | 706 } |
| 702 } | 707 } |
| 703 } | 708 } |
| 704 | 709 |
| 705 bool FFmpegDemuxer::StreamsHavePendingReads() { | 710 bool FFmpegDemuxer::StreamsHavePendingReads() { |
| 706 DCHECK_EQ(MessageLoop::current(), message_loop_); | 711 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 707 StreamVector::iterator iter; | 712 StreamVector::iterator iter; |
| 708 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 713 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 709 if (*iter && (*iter)->HasPendingReads()) { | 714 if (*iter && (*iter)->HasPendingReads()) { |
| 710 return true; | 715 return true; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 733 read_event_.Wait(); | 738 read_event_.Wait(); |
| 734 return last_read_bytes_; | 739 return last_read_bytes_; |
| 735 } | 740 } |
| 736 | 741 |
| 737 void FFmpegDemuxer::SignalReadCompleted(size_t size) { | 742 void FFmpegDemuxer::SignalReadCompleted(size_t size) { |
| 738 last_read_bytes_ = size; | 743 last_read_bytes_ = size; |
| 739 read_event_.Signal(); | 744 read_event_.Signal(); |
| 740 } | 745 } |
| 741 | 746 |
| 742 } // namespace media | 747 } // namespace media |
| OLD | NEW |