Chromium Code Reviews| Index: media/filters/ffmpeg_demuxer.cc |
| diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc |
| index 326b8818998e8e79d6a4937e144e59a1e6a668ef..3e39708a4f5eb09f8e7d1cd44aae08fe0b224b97 100644 |
| --- a/media/filters/ffmpeg_demuxer.cc |
| +++ b/media/filters/ffmpeg_demuxer.cc |
| @@ -28,6 +28,18 @@ |
| namespace media { |
| +// Helpers for making ReadCBs always run on a new execution stack. |
| +static void RunReadCB(const scoped_refptr<base::MessageLoopProxy>& message_loop, |
| + const DemuxerStream::ReadCB& read_cb, |
| + DemuxerStream::Status status, |
| + const scoped_refptr<DecoderBuffer>& buffer) { |
| + message_loop->PostTask(FROM_HERE, base::Bind(read_cb, status, buffer)); |
| +} |
| + |
| +static DemuxerStream::ReadCB WrapReadCB(const DemuxerStream::ReadCB& read_cb) { |
| + return base::Bind(&RunReadCB, base::MessageLoopProxy::current(), read_cb); |
| +} |
| + |
| // |
| // FFmpegDemuxerStream |
| // |
| @@ -134,45 +146,41 @@ base::TimeDelta FFmpegDemuxerStream::duration() { |
| } |
| DemuxerStream::Type FFmpegDemuxerStream::type() { |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| return type_; |
| } |
| void FFmpegDemuxerStream::Read(const ReadCB& read_cb) { |
| - if (!message_loop_->BelongsToCurrentThread()) { |
| - message_loop_->PostTask(FROM_HERE, base::Bind( |
| - &FFmpegDemuxerStream::Read, this, read_cb)); |
| - return; |
| - } |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| + ReadCB wrapped_read_cb = WrapReadCB(read_cb); |
| // Don't accept any additional reads if we've been told to stop. |
| // The |demuxer_| may have been destroyed in the pipeline thread. |
| // |
| // TODO(scherkus): it would be cleaner to reply with an error message. |
| if (stopped_) { |
| - read_cb.Run(DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
| + wrapped_read_cb.Run(DemuxerStream::kOk, DecoderBuffer::CreateEOSBuffer()); |
| return; |
| } |
| - read_queue_.push_back(read_cb); |
| + read_queue_.push_back(wrapped_read_cb); |
|
acolwell GONE FROM CHROMIUM
2012/12/03 23:14:21
Do we still need this queue?
scherkus (not reviewing)
2012/12/07 22:18:55
We don't! Removed.
|
| SatisfyPendingReads(); |
| } |
| void FFmpegDemuxerStream::EnableBitstreamConverter() { |
| - if (!message_loop_->BelongsToCurrentThread()) { |
| - message_loop_->PostTask(FROM_HERE, base::Bind( |
| - &FFmpegDemuxerStream::EnableBitstreamConverter, this)); |
| - return; |
| - } |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| CHECK(bitstream_converter_.get()); |
| bitstream_converter_enabled_ = true; |
| } |
| const AudioDecoderConfig& FFmpegDemuxerStream::audio_decoder_config() { |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| CHECK_EQ(type_, AUDIO); |
| return audio_config_; |
| } |
| const VideoDecoderConfig& FFmpegDemuxerStream::video_decoder_config() { |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| CHECK_EQ(type_, VIDEO); |
| return video_config_; |
| } |
| @@ -207,8 +215,7 @@ void FFmpegDemuxerStream::SatisfyPendingReads() { |
| // Send buffer back on a new execution stack to avoid recursing. |
| ReadCB read_cb = read_queue_.front(); |
| read_queue_.pop_front(); |
| - message_loop_->PostTask(FROM_HERE, base::Bind( |
| - read_cb, DemuxerStream::kOk, buffer)); |
| + read_cb.Run(DemuxerStream::kOk, buffer); |
| } |
| // Have capacity? Ask for more! |
| @@ -257,34 +264,78 @@ FFmpegDemuxer::FFmpegDemuxer( |
| FFmpegDemuxer::~FFmpegDemuxer() {} |
| void FFmpegDemuxer::Stop(const base::Closure& callback) { |
| - // Post a task to notify the streams to stop as well. |
| - message_loop_->PostTask(FROM_HERE, |
| - base::Bind(&FFmpegDemuxer::StopTask, this, callback)); |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| + url_protocol_.Abort(); |
| + data_source_->Stop(BindToLoop(message_loop_, base::Bind( |
| + &FFmpegDemuxer::OnDataSourceStopped, this, callback))); |
| } |
| void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
| - message_loop_->PostTask(FROM_HERE, |
| - base::Bind(&FFmpegDemuxer::SeekTask, this, time, cb)); |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| + CHECK(!pending_seek_); |
| + |
| + // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, |
| + // otherwise we can end up waiting for a pre-seek read to complete even though |
| + // we know we're going to drop it on the floor. |
| + |
| + // Always seek to a timestamp less than or equal to the desired timestamp. |
| + int flags = AVSEEK_FLAG_BACKWARD; |
| + |
| + // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg |
| + // will attempt to use the lowest-index video stream, if present, followed by |
| + // the lowest-index audio stream. |
| + pending_seek_ = true; |
| + base::PostTaskAndReplyWithResult( |
| + blocking_thread_.message_loop_proxy(), FROM_HERE, |
| + base::Bind(&av_seek_frame, glue_->format_context(), -1, |
| + time.InMicroseconds(), flags), |
| + base::Bind(&FFmpegDemuxer::OnSeekFrameDone, this, cb)); |
| } |
| void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { |
| - DCHECK(data_source_.get()); |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| data_source_->SetPlaybackRate(playback_rate); |
| } |
| void FFmpegDemuxer::OnAudioRendererDisabled() { |
| - message_loop_->PostTask(FROM_HERE, base::Bind( |
| - &FFmpegDemuxer::DisableAudioStreamTask, this)); |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| + audio_disabled_ = true; |
| + StreamVector::iterator iter; |
| + for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| + if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { |
| + (*iter)->Stop(); |
| + } |
| + } |
| } |
| void FFmpegDemuxer::Initialize(DemuxerHost* host, |
| const PipelineStatusCB& status_cb) { |
| - message_loop_->PostTask(FROM_HERE, base::Bind( |
| - &FFmpegDemuxer::InitializeTask, this, host, status_cb)); |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| + host_ = host; |
| + |
| + // TODO(scherkus): DataSource should have a host by this point, |
| + // see http://crbug.com/122071 |
| + data_source_->set_host(host); |
| + |
| + glue_.reset(new FFmpegGlue(&url_protocol_)); |
| + AVFormatContext* format_context = glue_->format_context(); |
| + |
| + // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we |
| + // don't use. FFmpeg will only read ID3v1 tags if no other metadata is |
| + // available, so add a metadata entry to ensure some is always present. |
| + av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); |
| + |
| + // Open the AVFormatContext using our glue layer. |
| + CHECK(blocking_thread_.Start()); |
| + base::PostTaskAndReplyWithResult( |
| + blocking_thread_.message_loop_proxy(), FROM_HERE, |
| + base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), |
| + base::Bind(&FFmpegDemuxer::OnOpenContextDone, this, status_cb)); |
| } |
| scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( |
| DemuxerStream::Type type) { |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| return GetFFmpegStream(type); |
| } |
| @@ -300,6 +351,7 @@ scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream( |
| } |
| base::TimeDelta FFmpegDemuxer::GetStartTime() const { |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| return start_time_; |
| } |
| @@ -339,31 +391,6 @@ static int CalculateBitrate( |
| return bytes * 8000000.0 / duration_us; |
| } |
| -void FFmpegDemuxer::InitializeTask(DemuxerHost* host, |
| - const PipelineStatusCB& status_cb) { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - host_ = host; |
| - |
| - // TODO(scherkus): DataSource should have a host by this point, |
| - // see http://crbug.com/122071 |
| - data_source_->set_host(host); |
| - |
| - glue_.reset(new FFmpegGlue(&url_protocol_)); |
| - AVFormatContext* format_context = glue_->format_context(); |
| - |
| - // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we |
| - // don't use. FFmpeg will only read ID3v1 tags if no other metadata is |
| - // available, so add a metadata entry to ensure some is always present. |
| - av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); |
| - |
| - // Open the AVFormatContext using our glue layer. |
| - CHECK(blocking_thread_.Start()); |
| - base::PostTaskAndReplyWithResult( |
| - blocking_thread_.message_loop_proxy(), FROM_HERE, |
| - base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), |
| - base::Bind(&FFmpegDemuxer::OnOpenContextDone, this, status_cb)); |
| -} |
| - |
| void FFmpegDemuxer::OnOpenContextDone(const PipelineStatusCB& status_cb, |
| bool result) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| @@ -478,28 +505,6 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, |
| status_cb.Run(PIPELINE_OK); |
| } |
| -void FFmpegDemuxer::SeekTask(base::TimeDelta time, const PipelineStatusCB& cb) { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - CHECK(!pending_seek_); |
| - |
| - // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, |
| - // otherwise we can end up waiting for a pre-seek read to complete even though |
| - // we know we're going to drop it on the floor. |
| - |
| - // Always seek to a timestamp less than or equal to the desired timestamp. |
| - int flags = AVSEEK_FLAG_BACKWARD; |
| - |
| - // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg |
| - // will attempt to use the lowest-index video stream, if present, followed by |
| - // the lowest-index audio stream. |
| - pending_seek_ = true; |
| - base::PostTaskAndReplyWithResult( |
| - blocking_thread_.message_loop_proxy(), FROM_HERE, |
| - base::Bind(&av_seek_frame, glue_->format_context(), -1, |
| - time.InMicroseconds(), flags), |
| - base::Bind(&FFmpegDemuxer::OnSeekFrameDone, this, cb)); |
| -} |
| - |
| void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| CHECK(pending_seek_); |
| @@ -524,17 +529,17 @@ void FFmpegDemuxer::OnSeekFrameDone(const PipelineStatusCB& cb, int result) { |
| (*iter)->FlushBuffers(); |
| } |
| - // Resume demuxing until capacity. |
| - DemuxTask(); |
| + // Resume reading until capacity. |
| + ReadFrameIfNeeded(); |
| // Notify we're finished seeking. |
| cb.Run(PIPELINE_OK); |
| } |
| -void FFmpegDemuxer::DemuxTask() { |
| +void FFmpegDemuxer::ReadFrameIfNeeded() { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| - // Make sure we have work to do before demuxing. |
| + // Make sure we have work to do before reading. |
| if (!blocking_thread_.IsRunning() || !StreamsHaveAvailableCapacity() || |
| pending_read_ || pending_seek_) { |
| return; |
| @@ -600,15 +605,8 @@ void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { |
| demuxer_stream->EnqueuePacket(packet.Pass()); |
| } |
| - // Keep demuxing until we've reached capacity. |
| - DemuxTask(); |
| -} |
| - |
| -void FFmpegDemuxer::StopTask(const base::Closure& callback) { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - url_protocol_.Abort(); |
| - data_source_->Stop(BindToLoop(message_loop_, base::Bind( |
| - &FFmpegDemuxer::OnDataSourceStopped, this, callback))); |
| + // Keep reading until we've reached capacity. |
| + ReadFrameIfNeeded(); |
| } |
| void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) { |
| @@ -628,17 +626,6 @@ void FFmpegDemuxer::OnDataSourceStopped(const base::Closure& callback) { |
| callback.Run(); |
| } |
| -void FFmpegDemuxer::DisableAudioStreamTask() { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - audio_disabled_ = true; |
| - StreamVector::iterator iter; |
| - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| - if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { |
| - (*iter)->Stop(); |
| - } |
| - } |
| -} |
| - |
| bool FFmpegDemuxer::StreamsHaveAvailableCapacity() { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| StreamVector::iterator iter; |
| @@ -664,7 +651,7 @@ void FFmpegDemuxer::StreamHasEnded() { |
| void FFmpegDemuxer::NotifyCapacityAvailable() { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| - DemuxTask(); |
| + ReadFrameIfNeeded(); |
| } |
| void FFmpegDemuxer::NotifyBufferingChanged() { |