diff options
Diffstat (limited to 'media/filters')
21 files changed, 154 insertions, 103 deletions
diff --git a/media/filters/adaptive_demuxer.cc b/media/filters/adaptive_demuxer.cc index 9a94d11..5599b15 100644 --- a/media/filters/adaptive_demuxer.cc +++ b/media/filters/adaptive_demuxer.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/bind.h" #include "base/logging.h" #include "base/string_number_conversions.h" #include "base/string_split.h" @@ -160,6 +161,8 @@ Demuxer* AdaptiveDemuxer::current_demuxer(DemuxerStream::Type type) { // Helper class that wraps a FilterCallback and expects to get called a set // number of times, after which the wrapped callback is fired (and deleted). +// +// TODO: Remove this class once Stop() is converted to FilterStatusCB. class CountingCallback { public: CountingCallback(int count, FilterCallback* orig_cb) @@ -191,21 +194,69 @@ class CountingCallback { scoped_ptr<FilterCallback> orig_cb_; }; +// Helper class that wraps FilterStatusCB and expects to get called a set +// number of times, after which the wrapped callback is fired. If an error +// is reported in any of the callbacks, only the first error code is passed +// to the wrapped callback. +class CountingStatusCB : public base::RefCountedThreadSafe<CountingStatusCB> { + public: + CountingStatusCB(int count, const FilterStatusCB& orig_cb) + : remaining_count_(count), orig_cb_(orig_cb), + overall_status_(PIPELINE_OK) { + DCHECK_GT(remaining_count_, 0); + DCHECK(!orig_cb.is_null()); + } + + FilterStatusCB GetACallback() { + return base::Bind(&CountingStatusCB::OnChildCallbackDone, this); + } + + private: + void OnChildCallbackDone(PipelineStatus status) { + bool fire_orig_cb = false; + PipelineStatus overall_status = PIPELINE_OK; + + { + base::AutoLock auto_lock(lock_); + + if (overall_status_ == PIPELINE_OK && status != PIPELINE_OK) + overall_status_ = status; + + if (--remaining_count_ == 0) { + fire_orig_cb = true; + overall_status = overall_status_; + } + } + + if (fire_orig_cb) + orig_cb_.Run(overall_status); + } + + base::Lock lock_; + int remaining_count_; + FilterStatusCB orig_cb_; + PipelineStatus overall_status_; + + DISALLOW_COPY_AND_ASSIGN(CountingStatusCB); +}; + void AdaptiveDemuxer::Stop(FilterCallback* callback) { // Stop() must be called on all of the demuxers even though only one demuxer // is actively delivering audio and another one is delivering video. This // just satisfies the contract that all demuxers must have Stop() called on // them before they are destroyed. + // + // TODO: Remove CountingCallback once Stop() is converted to FilterStatusCB. CountingCallback* wrapper = new CountingCallback(demuxers_.size(), callback); for (size_t i = 0; i < demuxers_.size(); ++i) demuxers_[i]->Stop(wrapper->GetACallback()); } -void AdaptiveDemuxer::Seek(base::TimeDelta time, FilterCallback* callback) { +void AdaptiveDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) { Demuxer* audio = current_demuxer(DemuxerStream::AUDIO); Demuxer* video = current_demuxer(DemuxerStream::VIDEO); int count = (audio ? 1 : 0) + (video && audio != video ? 1 : 0); - CountingCallback* wrapper = new CountingCallback(count, callback); + CountingStatusCB* wrapper = new CountingStatusCB(count, cb); if (audio) audio->Seek(time, wrapper->GetACallback()); if (video && audio != video) diff --git a/media/filters/adaptive_demuxer.h b/media/filters/adaptive_demuxer.h index b4836bb..6402b03 100644 --- a/media/filters/adaptive_demuxer.h +++ b/media/filters/adaptive_demuxer.h @@ -79,7 +79,7 @@ class AdaptiveDemuxer : public Demuxer { // Filter implementation. virtual void Stop(FilterCallback* callback); - virtual void Seek(base::TimeDelta time, FilterCallback* callback); + virtual void Seek(base::TimeDelta time, const FilterStatusCB& cb); virtual void OnAudioRendererDisabled(); virtual void set_host(FilterHost* filter_host); virtual void SetPlaybackRate(float playback_rate); diff --git a/media/filters/audio_renderer_base.cc b/media/filters/audio_renderer_base.cc index 45dd699..a98b8a2 100644 --- a/media/filters/audio_renderer_base.cc +++ b/media/filters/audio_renderer_base.cc @@ -64,12 +64,13 @@ void AudioRendererBase::Stop(FilterCallback* callback) { } } -void AudioRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) { +void AudioRendererBase::Seek(base::TimeDelta time, const FilterStatusCB& cb) { base::AutoLock auto_lock(lock_); DCHECK_EQ(kPaused, state_); DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; + DCHECK(seek_cb_.is_null()); state_ = kSeeking; - seek_callback_.reset(callback); + seek_cb_ = cb; seek_timestamp_ = time; // Throw away everything and schedule our reads. @@ -159,13 +160,12 @@ void AudioRendererBase::ConsumeAudioSamples(scoped_refptr<Buffer> buffer_in) { // Check for our preroll complete condition. if (state_ == kSeeking) { - DCHECK(seek_callback_.get()); + DCHECK(!seek_cb_.is_null()); if (algorithm_->IsQueueFull() || recieved_end_of_stream_) { // Transition into paused whether we have data in |algorithm_| or not. // FillBuffer() will play silence if there's nothing to fill. state_ = kPaused; - seek_callback_->Run(); - seek_callback_.reset(); + ResetAndRunCB(&seek_cb_, PIPELINE_OK); } } else if (state_ == kPaused && pending_reads_ == 0) { // No more pending reads! We're now officially "paused". diff --git a/media/filters/audio_renderer_base.h b/media/filters/audio_renderer_base.h index 0b13e02a..1882743 100644 --- a/media/filters/audio_renderer_base.h +++ b/media/filters/audio_renderer_base.h @@ -37,7 +37,7 @@ class AudioRendererBase : public AudioRenderer { virtual void Pause(FilterCallback* callback); virtual void Stop(FilterCallback* callback); - virtual void Seek(base::TimeDelta time, FilterCallback* callback); + virtual void Seek(base::TimeDelta time, const FilterStatusCB& cb); // AudioRenderer implementation. virtual void Initialize(AudioDecoder* decoder, FilterCallback* callback); @@ -125,7 +125,7 @@ class AudioRendererBase : public AudioRenderer { // Filter callbacks. scoped_ptr<FilterCallback> pause_callback_; - scoped_ptr<FilterCallback> seek_callback_; + FilterStatusCB seek_cb_; base::TimeDelta seek_timestamp_; diff --git a/media/filters/audio_renderer_base_unittest.cc b/media/filters/audio_renderer_base_unittest.cc index 9385f53f..da616ae 100644 --- a/media/filters/audio_renderer_base_unittest.cc +++ b/media/filters/audio_renderer_base_unittest.cc @@ -117,7 +117,7 @@ TEST_F(AudioRendererBaseTest, Initialize_Successful) { // Now seek to trigger prerolling, verifying the callback hasn't been // executed yet. EXPECT_CALL(*renderer_, CheckPoint(0)); - renderer_->Seek(base::TimeDelta(), NewExpectedCallback()); + renderer_->Seek(base::TimeDelta(), NewExpectedStatusCB(PIPELINE_OK)); EXPECT_EQ(kMaxQueueSize, pending_reads_); renderer_->CheckPoint(0); @@ -145,7 +145,7 @@ TEST_F(AudioRendererBaseTest, OneCompleteReadCycle) { // Now seek to trigger prerolling, verifying the callback hasn't been // executed yet. EXPECT_CALL(*renderer_, CheckPoint(0)); - renderer_->Seek(base::TimeDelta(), NewExpectedCallback()); + renderer_->Seek(base::TimeDelta(), NewExpectedStatusCB(PIPELINE_OK)); EXPECT_EQ(kMaxQueueSize, pending_reads_); renderer_->CheckPoint(0); diff --git a/media/filters/decoder_base.h b/media/filters/decoder_base.h index 6cf4937..a889e19fb 100644 --- a/media/filters/decoder_base.h +++ b/media/filters/decoder_base.h @@ -34,11 +34,10 @@ class DecoderBase : public Decoder { NewRunnableMethod(this, &DecoderBase::StopTask, callback)); } - virtual void Seek(base::TimeDelta time, - FilterCallback* callback) { + virtual void Seek(base::TimeDelta time, const FilterStatusCB& cb) { message_loop_->PostTask( FROM_HERE, - NewRunnableMethod(this, &DecoderBase::SeekTask, time, callback)); + NewRunnableMethod(this, &DecoderBase::SeekTask, time, cb)); } // Decoder implementation. @@ -170,25 +169,22 @@ class DecoderBase : public Decoder { } } - void SeekTask(base::TimeDelta time, FilterCallback* callback) { + void SeekTask(base::TimeDelta time, const FilterStatusCB& cb) { DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK_EQ(0u, pending_reads_) << "Pending reads should have completed"; DCHECK_EQ(0u, pending_requests_) << "Pending requests should be empty"; // Delegate to the subclass first. - DoSeek(time, - NewRunnableMethod(this, &DecoderBase::OnSeekComplete, callback)); + DoSeek(time, NewRunnableMethod(this, &DecoderBase::OnSeekComplete, cb)); } - void OnSeekComplete(FilterCallback* callback) { + void OnSeekComplete(const FilterStatusCB& cb) { // Flush our decoded results. result_queue_.clear(); // Signal that we're done seeking. - if (callback) { - callback->Run(); - delete callback; - } + if (!cb.is_null()) + cb.Run(PIPELINE_OK); } void InitializeTask(DemuxerStream* demuxer_stream, diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc index 98ec882..8347019 100644 --- a/media/filters/ffmpeg_demuxer.cc +++ b/media/filters/ffmpeg_demuxer.cc @@ -313,13 +313,13 @@ void FFmpegDemuxer::Stop(FilterCallback* callback) { SignalReadCompleted(DataSource::kReadError); } -void FFmpegDemuxer::Seek(base::TimeDelta time, FilterCallback* callback) { +void FFmpegDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) { // TODO(hclam): by returning from this method, it is assumed that the seek // operation is completed and filters behind the demuxer is good to issue // more reads, but we are posting a task here, which makes the seek operation // asynchronous, should change how seek works to make it fully asynchronous. message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &FFmpegDemuxer::SeekTask, time, callback)); + NewRunnableMethod(this, &FFmpegDemuxer::SeekTask, time, cb)); } void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { @@ -528,9 +528,8 @@ void FFmpegDemuxer::InitializeTask(DataSource* data_source, callback->Run(PIPELINE_OK); } -void FFmpegDemuxer::SeekTask(base::TimeDelta time, FilterCallback* callback) { +void FFmpegDemuxer::SeekTask(base::TimeDelta time, const FilterStatusCB& cb) { DCHECK_EQ(MessageLoop::current(), message_loop_); - scoped_ptr<FilterCallback> c(callback); // Tell streams to flush buffers due to seeking. StreamVector::iterator iter; @@ -553,7 +552,7 @@ void FFmpegDemuxer::SeekTask(base::TimeDelta time, FilterCallback* callback) { } // Notify we're finished seeking. - callback->Run(); + cb.Run(PIPELINE_OK); } void FFmpegDemuxer::DemuxTask() { diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h index 3fe2cf1..d96622c 100644 --- a/media/filters/ffmpeg_demuxer.h +++ b/media/filters/ffmpeg_demuxer.h @@ -142,7 +142,7 @@ class FFmpegDemuxer : public Demuxer, // Filter implementation. virtual void Stop(FilterCallback* callback); - virtual void Seek(base::TimeDelta time, FilterCallback* callback); + virtual void Seek(base::TimeDelta time, const FilterStatusCB& cb); virtual void OnAudioRendererDisabled(); virtual void set_host(FilterHost* filter_host); virtual void SetPlaybackRate(float playback_rate); @@ -171,7 +171,7 @@ class FFmpegDemuxer : public Demuxer, DataSource* data_source, PipelineStatusCallback* callback); // Carries out a seek on the demuxer thread. - void SeekTask(base::TimeDelta time, FilterCallback* callback); + void SeekTask(base::TimeDelta time, const FilterStatusCB& cb); // Carries out demuxing and satisfying stream reads on the demuxer thread. void DemuxTask(); diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc index e13fbb1..74ce0ed 100644 --- a/media/filters/ffmpeg_demuxer_unittest.cc +++ b/media/filters/ffmpeg_demuxer_unittest.cc @@ -425,7 +425,7 @@ TEST_F(FFmpegDemuxerTest, Seek) { .WillOnce(Return(0)); // ...then our callback will be executed... - FilterCallback* seek_callback = NewExpectedCallback(); + FilterStatusCB seek_cb = NewExpectedStatusCB(PIPELINE_OK); EXPECT_CALL(mock_ffmpeg_, CheckPoint(2)); // ...followed by two audio packet reads we'll trigger... @@ -468,7 +468,7 @@ TEST_F(FFmpegDemuxerTest, Seek) { // Issue a simple forward seek, which should discard queued packets. demuxer_->Seek(base::TimeDelta::FromMicroseconds(kExpectedTimestamp), - seek_callback); + seek_cb); message_loop_.RunAllPending(); mock_ffmpeg_.CheckPoint(2); diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc index b281fb4..5487398 100644 --- a/media/filters/ffmpeg_video_decoder.cc +++ b/media/filters/ffmpeg_video_decoder.cc @@ -183,30 +183,27 @@ void FFmpegVideoDecoder::OnFlushComplete() { state_ = kNormal; } -void FFmpegVideoDecoder::Seek(base::TimeDelta time, - FilterCallback* callback) { +void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { if (MessageLoop::current() != message_loop_) { message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, - &FFmpegVideoDecoder::Seek, - time, - callback)); + NewRunnableMethod(this, &FFmpegVideoDecoder::Seek, + time, cb)); return; } DCHECK_EQ(MessageLoop::current(), message_loop_); - DCHECK(!seek_callback_.get()); + DCHECK(seek_cb_.is_null()); pts_stream_.Seek(time); - seek_callback_.reset(callback); + seek_cb_ = cb; decode_engine_->Seek(); } void FFmpegVideoDecoder::OnSeekComplete() { DCHECK_EQ(MessageLoop::current(), message_loop_); - DCHECK(seek_callback_.get()); + DCHECK(!seek_cb_.is_null()); - AutoCallbackRunner done_runner(seek_callback_.release()); + ResetAndRunCB(&seek_cb_, PIPELINE_OK); } void FFmpegVideoDecoder::OnError() { diff --git a/media/filters/ffmpeg_video_decoder.h b/media/filters/ffmpeg_video_decoder.h index ab27aad..3281193 100644 --- a/media/filters/ffmpeg_video_decoder.h +++ b/media/filters/ffmpeg_video_decoder.h @@ -28,7 +28,7 @@ class FFmpegVideoDecoder : public VideoDecoder, // Filter implementation. virtual void Stop(FilterCallback* callback); - virtual void Seek(base::TimeDelta time, FilterCallback* callback); + virtual void Seek(base::TimeDelta time, const FilterStatusCB& cb); virtual void Pause(FilterCallback* callback); virtual void Flush(FilterCallback* callback); @@ -100,7 +100,7 @@ class FFmpegVideoDecoder : public VideoDecoder, scoped_ptr<FilterCallback> initialize_callback_; scoped_ptr<FilterCallback> uninitialize_callback_; scoped_ptr<FilterCallback> flush_callback_; - scoped_ptr<FilterCallback> seek_callback_; + FilterStatusCB seek_cb_; scoped_ptr<StatisticsCallback> statistics_callback_; // Hold video frames when flush happens. diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc index 45869f4..38411d3 100644 --- a/media/filters/ffmpeg_video_decoder_unittest.cc +++ b/media/filters/ffmpeg_video_decoder_unittest.cc @@ -429,7 +429,7 @@ TEST_F(FFmpegVideoDecoderTest, DoSeek) { // Expect Seek and verify the results. EXPECT_CALL(*engine_, Seek()) .WillOnce(EngineSeek(engine_)); - decoder_->Seek(kZero, NewExpectedCallback()); + decoder_->Seek(kZero, NewExpectedStatusCB(PIPELINE_OK)); EXPECT_TRUE(kZero == decoder_->pts_stream_.current_duration()); EXPECT_EQ(FFmpegVideoDecoder::kNormal, decoder_->state_); diff --git a/media/filters/file_data_source_unittest.cc b/media/filters/file_data_source_unittest.cc index 1ae111e..5cf012b 100644 --- a/media/filters/file_data_source_unittest.cc +++ b/media/filters/file_data_source_unittest.cc @@ -101,7 +101,7 @@ TEST(FileDataSourceTest, Seek) { const base::TimeDelta kZero; scoped_refptr<FileDataSource> filter(new FileDataSource()); - filter->Seek(kZero, NewExpectedCallback()); + filter->Seek(kZero, NewExpectedStatusCB(PIPELINE_OK)); filter->Stop(NewExpectedCallback()); } diff --git a/media/filters/omx_video_decoder.cc b/media/filters/omx_video_decoder.cc index 43f8100..b185965 100644 --- a/media/filters/omx_video_decoder.cc +++ b/media/filters/omx_video_decoder.cc @@ -156,30 +156,29 @@ void OmxVideoDecoder::OnFlushComplete() { pts_stream_.Flush(); } -void OmxVideoDecoder::Seek(base::TimeDelta time, - FilterCallback* callback) { +void OmxVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { if (MessageLoop::current() != message_loop_) { message_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &OmxVideoDecoder::Seek, time, - callback)); + cb)); return; } DCHECK_EQ(MessageLoop::current(), message_loop_); - DCHECK(!seek_callback_.get()); + DCHECK(seek_cb_.is_null()); pts_stream_.Seek(time); - seek_callback_.reset(callback); + seek_cb_ = cb; decode_engine_->Seek(); } void OmxVideoDecoder::OnSeekComplete() { DCHECK_EQ(MessageLoop::current(), message_loop_); - DCHECK(seek_callback_.get()); + DCHECK(!seek_cb_.is_null()); - AutoCallbackRunner done_runner(seek_callback_.release()); + ResetAndRunCB(&seek_cb_, PIPELINE_OK); } void OmxVideoDecoder::OnError() { diff --git a/media/filters/omx_video_decoder.h b/media/filters/omx_video_decoder.h index c86b2fe..40bf721 100644 --- a/media/filters/omx_video_decoder.h +++ b/media/filters/omx_video_decoder.h @@ -34,7 +34,7 @@ class OmxVideoDecoder : public VideoDecoder, StatisticsCallback* stats_callback); virtual void Stop(FilterCallback* callback); virtual void Flush(FilterCallback* callback); - virtual void Seek(base::TimeDelta time, FilterCallback* callback); + virtual void Seek(base::TimeDelta time, const FilterStatusCB& cb); virtual void ProduceVideoFrame(scoped_refptr<VideoFrame> frame); virtual bool ProvidesBuffer(); virtual const MediaFormat& media_format(); @@ -67,7 +67,7 @@ class OmxVideoDecoder : public VideoDecoder, scoped_ptr<FilterCallback> initialize_callback_; scoped_ptr<FilterCallback> uninitialize_callback_; scoped_ptr<FilterCallback> flush_callback_; - scoped_ptr<FilterCallback> seek_callback_; + FilterStatusCB seek_cb_; scoped_ptr<StatisticsCallback> statistics_callback_; VideoCodecInfo info_; diff --git a/media/filters/rtc_video_decoder.cc b/media/filters/rtc_video_decoder.cc index 0ddf630..6744569 100644 --- a/media/filters/rtc_video_decoder.cc +++ b/media/filters/rtc_video_decoder.cc @@ -113,14 +113,11 @@ void RTCVideoDecoder::Stop(FilterCallback* callback) { // TODO(ronghuawu): Stop rtc } -void RTCVideoDecoder::Seek(base::TimeDelta time, - FilterCallback* callback) { +void RTCVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { if (MessageLoop::current() != message_loop_) { message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, - &RTCVideoDecoder::Seek, - time, - callback)); + NewRunnableMethod(this, &RTCVideoDecoder::Seek, + time, cb)); return; } @@ -165,8 +162,7 @@ void RTCVideoDecoder::Seek(base::TimeDelta time, state_ = kNormal; - callback->Run(); - delete callback; + cb.Run(PIPELINE_OK); // TODO(ronghuawu): Start rtc } diff --git a/media/filters/rtc_video_decoder.h b/media/filters/rtc_video_decoder.h index 02bd96f..6b5b914 100644 --- a/media/filters/rtc_video_decoder.h +++ b/media/filters/rtc_video_decoder.h @@ -36,7 +36,7 @@ class RTCVideoDecoder : public VideoDecoder, // Filter implementation. virtual void Play(FilterCallback* callback); - virtual void Seek(base::TimeDelta time, FilterCallback* callback); + virtual void Seek(base::TimeDelta time, const FilterStatusCB& cb); virtual void Pause(FilterCallback* callback); virtual void Stop(FilterCallback* callback); @@ -91,4 +91,3 @@ class RTCVideoDecoder : public VideoDecoder, } // namespace media #endif // MEDIA_FILTERS_RTC_VIDEO_DECODER_H_ - diff --git a/media/filters/rtc_video_decoder_unittest.cc b/media/filters/rtc_video_decoder_unittest.cc index f0970ad..71287d0 100644 --- a/media/filters/rtc_video_decoder_unittest.cc +++ b/media/filters/rtc_video_decoder_unittest.cc @@ -111,7 +111,7 @@ TEST_F(RTCVideoDecoderTest, DoSeek) { // Expect Seek and verify the results. EXPECT_CALL(*renderer_.get(), ConsumeVideoFrame(_)) .Times(Limits::kMaxVideoFrames); - decoder_->Seek(kZero, NewExpectedCallback()); + decoder_->Seek(kZero, NewExpectedStatusCB(PIPELINE_OK)); message_loop_.RunAllPending(); EXPECT_EQ(RTCVideoDecoder::kNormal, decoder_->state_); @@ -127,7 +127,7 @@ TEST_F(RTCVideoDecoderTest, DoDeliverFrame) { decoder_->set_consume_video_frame_callback( base::Bind(&RTCVideoDecoder::ProduceVideoFrame, base::Unretained(decoder_.get()))); - decoder_->Seek(kZero, NewExpectedCallback()); + decoder_->Seek(kZero, NewExpectedStatusCB(PIPELINE_OK)); decoder_->set_consume_video_frame_callback( base::Bind(&MockVideoRenderer::ConsumeVideoFrame, diff --git a/media/filters/video_renderer_base.cc b/media/filters/video_renderer_base.cc index 92bedba..7705946 100644 --- a/media/filters/video_renderer_base.cc +++ b/media/filters/video_renderer_base.cc @@ -129,28 +129,37 @@ void VideoRendererBase::SetPlaybackRate(float playback_rate) { playback_rate_ = playback_rate; } -void VideoRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) { - base::AutoLock auto_lock(lock_); - // There is a race condition between filters to receive SeekTask(). - // It turns out we could receive buffer from decoder before seek() - // is called on us. so we do the following: - // kFlushed => ( Receive first buffer or Seek() ) => kSeeking and - // kSeeking => ( Receive enough buffers) => kPrerolled. ) - DCHECK(kPrerolled == state_ || kFlushed == state_ || kSeeking == state_); - - if (state_ == kPrerolled) { - // Already get enough buffers from decoder. - callback->Run(); - delete callback; - } else { - // Otherwise we are either kFlushed or kSeeking, but without enough buffers; - // we should save the callback function and call it later. - state_ = kSeeking; - seek_callback_.reset(callback); +void VideoRendererBase::Seek(base::TimeDelta time, const FilterStatusCB& cb) { + bool run_callback = false; + + { + base::AutoLock auto_lock(lock_); + // There is a race condition between filters to receive SeekTask(). + // It turns out we could receive buffer from decoder before seek() + // is called on us. so we do the following: + // kFlushed => ( Receive first buffer or Seek() ) => kSeeking and + // kSeeking => ( Receive enough buffers) => kPrerolled. ) + DCHECK(kPrerolled == state_ || kFlushed == state_ || kSeeking == state_); + DCHECK(!cb.is_null()); + DCHECK(seek_cb_.is_null()); + + if (state_ == kPrerolled) { + // Already get enough buffers from decoder. + run_callback = true; + + } else { + // Otherwise we are either kFlushed or kSeeking, but without enough + // buffers we should save the callback function and call it later. + state_ = kSeeking; + seek_cb_ = cb; + } + + seek_timestamp_ = time; + ScheduleRead_Locked(); } - seek_timestamp_ = time; - ScheduleRead_Locked(); + if (run_callback) + cb.Run(PIPELINE_OK); } void VideoRendererBase::Initialize(VideoDecoder* decoder, @@ -462,9 +471,8 @@ void VideoRendererBase::ConsumeVideoFrame(scoped_refptr<VideoFrame> frame) { // If we reach prerolled state before Seek() is called by pipeline, // |seek_callback_| is not set, we will return immediately during // when Seek() is eventually called. - if ((seek_callback_.get())) { - seek_callback_->Run(); - seek_callback_.reset(); + if (!seek_cb_.is_null()) { + ResetAndRunCB(&seek_cb_, PIPELINE_OK); } } } else if (state_ == kFlushing && pending_reads_ == 0 && !pending_paint_) { @@ -578,7 +586,10 @@ void VideoRendererBase::EnterErrorState_Locked(PipelineStatus status) { lock_.AssertAcquired(); scoped_ptr<FilterCallback> callback; - switch (state_) { + State old_state = state_; + state_ = kError; + + switch (old_state) { case kUninitialized: case kPrerolled: case kPaused: @@ -593,8 +604,9 @@ void VideoRendererBase::EnterErrorState_Locked(PipelineStatus status) { break; case kSeeking: - CHECK(seek_callback_.get()); - callback.swap(seek_callback_); + CHECK(!seek_cb_.is_null()); + ResetAndRunCB(&seek_cb_, status); + return; break; case kStopped: @@ -604,7 +616,7 @@ void VideoRendererBase::EnterErrorState_Locked(PipelineStatus status) { case kError: return; } - state_ = kError; + host()->SetError(status); if (callback.get()) diff --git a/media/filters/video_renderer_base.h b/media/filters/video_renderer_base.h index 66eba6e..6a9a97e 100644 --- a/media/filters/video_renderer_base.h +++ b/media/filters/video_renderer_base.h @@ -51,7 +51,7 @@ class VideoRendererBase : public VideoRenderer, virtual void Flush(FilterCallback* callback); virtual void Stop(FilterCallback* callback); virtual void SetPlaybackRate(float playback_rate); - virtual void Seek(base::TimeDelta time, FilterCallback* callback); + virtual void Seek(base::TimeDelta time, const FilterStatusCB& cb); // VideoRenderer implementation. virtual void Initialize(VideoDecoder* decoder, @@ -213,7 +213,7 @@ class VideoRendererBase : public VideoRenderer, // Filter callbacks. scoped_ptr<FilterCallback> flush_callback_; - scoped_ptr<FilterCallback> seek_callback_; + FilterStatusCB seek_cb_; scoped_ptr<StatisticsCallback> statistics_callback_; base::TimeDelta seek_timestamp_; diff --git a/media/filters/video_renderer_base_unittest.cc b/media/filters/video_renderer_base_unittest.cc index c21d6c7..8a8c348 100644 --- a/media/filters/video_renderer_base_unittest.cc +++ b/media/filters/video_renderer_base_unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/bind.h" #include "base/stl_util-inl.h" #include "media/base/callback.h" #include "media/base/data_buffer.h" @@ -110,13 +111,15 @@ class VideoRendererBaseTest : public ::testing::Test { Seek(0); } - void StartSeeking(int64 timestamp) { + void StartSeeking(int64 timestamp, PipelineStatus expected_status) { EXPECT_FALSE(seeking_); // Seek to trigger prerolling. seeking_ = true; renderer_->Seek(base::TimeDelta::FromMicroseconds(timestamp), - NewCallback(this, &VideoRendererBaseTest::OnSeekComplete)); + base::Bind(&VideoRendererBaseTest::OnSeekComplete, + base::Unretained(this), + expected_status)); } void FinishSeeking() { @@ -132,7 +135,7 @@ class VideoRendererBaseTest : public ::testing::Test { } void Seek(int64 timestamp) { - StartSeeking(timestamp); + StartSeeking(timestamp, PIPELINE_OK); FinishSeeking(); } @@ -191,7 +194,8 @@ class VideoRendererBaseTest : public ::testing::Test { read_queue_.push_back(frame); } - void OnSeekComplete() { + void OnSeekComplete(PipelineStatus expected_status, PipelineStatus status) { + EXPECT_EQ(status, expected_status); EXPECT_TRUE(seeking_); seeking_ = false; } @@ -269,9 +273,7 @@ TEST_F(VideoRendererBaseTest, Error_Playing) { TEST_F(VideoRendererBaseTest, Error_Seeking) { Initialize(); Flush(); - StartSeeking(0); - - EXPECT_CALL(host_, SetError(PIPELINE_ERROR_DECODE)); + StartSeeking(0, PIPELINE_ERROR_DECODE); CreateError(); Flush(); } |