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 |