diff options
author | fischman@chromium.org <fischman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-14 00:53:31 +0000 |
---|---|---|
committer | fischman@chromium.org <fischman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-14 00:53:31 +0000 |
commit | 8d4359280e59014e85a6b230eec35598c2113498 (patch) | |
tree | b3fb0c587d2aa94a8fe6f6c558013e96a683f89a /media | |
parent | 65267eb77b59758337dabc651f949e5feeac7cf5 (diff) | |
download | chromium_src-8d4359280e59014e85a6b230eec35598c2113498.zip chromium_src-8d4359280e59014e85a6b230eec35598c2113498.tar.gz chromium_src-8d4359280e59014e85a6b230eec35598c2113498.tar.bz2 |
Fix deadlock in WebMediaPlayerImpl::Destroy() when HW video decode is enabled.
Several unfortunate factors combine to require this CL:
1) WebMediaPlayerImpl::Destroy() holds the renderer thread
hostage for the duration of PipelineImpl::Stop()
2) PipelineImpl::Stop() walks all its filters through Pause,
Flush, and Stop, not starting one transition until the
previous is complete.
3) VideoRendererBase::Flush() doesn't complete until any pending
read to the video decoder is satisfied.
on the render thread (because of PipelineImpl locking preventing
re-entrancy, being triggered on stats update). Therefore GVD
must have its own thread. Because GpuVideoDecodeAcceleratorHost
expects to run on the renderer thread (or be rewritten), that
means trampolining vda_ calls from GVD through the renderer
thread. #2 & #1 mean that such trampolining must be
fire-and-forget during shutdown.
The new VideoDecoder::PrepareForShutdownHack method is
reminiscent of WebMediaPlayerProxy::AbortDataSources(), but
thankfully at least this version of that hack can be contained
within media/.
The OmxVideoDecodeAccelerator change to DCHECKs is unrelated to
the feature here but was uncovered during testing. Basically the
already-allowed current_state_change_ values for when Destroy()
is called implied a wider range of legal current_state_ values
than the DCHECK was asserting. Fixed that.
BUG=109397
TEST=manual test that reload during playback works.
Review URL: http://codereview.chromium.org/9211015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@117742 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/filters.cc | 4 | ||||
-rw-r--r-- | media/base/filters.h | 10 | ||||
-rw-r--r-- | media/base/pipeline_impl.cc | 16 | ||||
-rw-r--r-- | media/base/pipeline_impl.h | 6 | ||||
-rw-r--r-- | media/filters/gpu_video_decoder.cc | 149 | ||||
-rw-r--r-- | media/filters/gpu_video_decoder.h | 36 |
6 files changed, 141 insertions, 80 deletions
diff --git a/media/base/filters.cc b/media/base/filters.cc index ead0823..df7cfdf 100644 --- a/media/base/filters.cc +++ b/media/base/filters.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -79,6 +79,8 @@ bool VideoDecoder::HasAlpha() const { return false; } +void VideoDecoder::PrepareForShutdownHack() {} + AudioDecoder::AudioDecoder() {} AudioDecoder::~AudioDecoder() {} diff --git a/media/base/filters.h b/media/base/filters.h index adb6aff..8f186d6 100644 --- a/media/base/filters.h +++ b/media/base/filters.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -153,6 +153,14 @@ class MEDIA_EXPORT VideoDecoder : public Filter { // that return formats with an alpha channel. virtual bool HasAlpha() const; + // Prepare decoder for shutdown. This is a HACK needed because + // PipelineImpl::Stop() goes through a Pause/Flush/Stop dance to all its + // filters, waiting for each state transition to complete before starting the + // next, but WebMediaPlayerImpl::Destroy() holds the renderer loop hostage for + // the duration. Default implementation does nothing; derived decoders may + // override as needed. http://crbug.com/110228 tracks removing this. + virtual void PrepareForShutdownHack(); + protected: VideoDecoder(); virtual ~VideoDecoder(); diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc index e322299..320b133 100644 --- a/media/base/pipeline_impl.cc +++ b/media/base/pipeline_impl.cc @@ -130,7 +130,10 @@ void PipelineImpl::Stop(const PipelineStatusCB& stop_callback) { return; } - // Stop the pipeline, which will set |running_| to false on behalf. + if (video_decoder_) + video_decoder_->PrepareForShutdownHack(); + + // Stop the pipeline, which will set |running_| to false on our behalf. message_loop_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::StopTask, this, stop_callback)); } @@ -1214,19 +1217,18 @@ bool PipelineImpl::InitializeVideoDecoder( return false; } - scoped_refptr<VideoDecoder> video_decoder; - filter_collection_->SelectVideoDecoder(&video_decoder); + filter_collection_->SelectVideoDecoder(&video_decoder_); - if (!video_decoder) { + if (!video_decoder_) { SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); return false; } - if (!PrepareFilter(video_decoder)) + if (!PrepareFilter(video_decoder_)) return false; - pipeline_init_state_->video_decoder_ = video_decoder; - video_decoder->Initialize( + pipeline_init_state_->video_decoder_ = video_decoder_; + video_decoder_->Initialize( stream, base::Bind(&PipelineImpl::OnFilterInitialize, this), base::Bind(&PipelineImpl::OnUpdateStatistics, this)); diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h index 36a9175..5b9ee4e 100644 --- a/media/base/pipeline_impl.h +++ b/media/base/pipeline_impl.h @@ -477,6 +477,12 @@ class MEDIA_EXPORT PipelineImpl // Reference to the filter(s) that constitute the pipeline. scoped_refptr<Filter> pipeline_filter_; + // Decoder reference used for signalling imminent shutdown. + // This is a HACK necessary because WebMediaPlayerImpl::Destroy() holds the + // renderer thread loop hostage for until PipelineImpl::Stop() calls its + // callback. http://crbug.com/110228 tracks removing this hack. + scoped_refptr<VideoDecoder> video_decoder_; + // Renderer references used for setting the volume and determining // when playback has finished. scoped_refptr<AudioRenderer> audio_renderer_; diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc index cf91a5f..3b7ea08 100644 --- a/media/filters/gpu_video_decoder.cc +++ b/media/filters/gpu_video_decoder.cc @@ -41,18 +41,19 @@ GpuVideoDecoder::BufferTimeData::~BufferTimeData() {} GpuVideoDecoder::GpuVideoDecoder( MessageLoop* message_loop, - scoped_ptr<Factories> factories) - : message_loop_(message_loop), - factories_(factories.Pass()), + const scoped_refptr<Factories>& factories) + : gvd_loop_proxy_(message_loop->message_loop_proxy()), + render_loop_proxy_(base::MessageLoopProxy::current()), + factories_(factories), state_(kNormal), demuxer_read_in_progress_(false), next_picture_buffer_id_(0), - next_bitstream_buffer_id_(0) { - DCHECK(message_loop_ && factories_.get()); + next_bitstream_buffer_id_(0), + shutting_down_(false) { + DCHECK(gvd_loop_proxy_ && factories_); } GpuVideoDecoder::~GpuVideoDecoder() { - DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK(!vda_); // Stop should have been already called. DCHECK(pending_read_cb_.is_null()); for (size_t i = 0; i < available_shm_segments_.size(); ++i) { @@ -69,8 +70,8 @@ GpuVideoDecoder::~GpuVideoDecoder() { } void GpuVideoDecoder::Stop(const base::Closure& callback) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::Stop, this, callback)); return; } @@ -78,14 +79,15 @@ void GpuVideoDecoder::Stop(const base::Closure& callback) { callback.Run(); return; } - vda_->Destroy(); + render_loop_proxy_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Destroy, vda_)); vda_ = NULL; callback.Run(); } void GpuVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::Seek, this, time, cb)); return; } @@ -93,8 +95,8 @@ void GpuVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { } void GpuVideoDecoder::Pause(const base::Closure& callback) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::Pause, this, callback)); return; } @@ -102,14 +104,9 @@ void GpuVideoDecoder::Pause(const base::Closure& callback) { } void GpuVideoDecoder::Flush(const base::Closure& callback) { - // VRB::Flush() waits for pending reads to be satisfied, so it's important we - // don't reset the decoder while the renderer is still waiting. - // TODO(fischman): replace the pseudo-busy-loop here with a proper accounting - // of state transitions. - if (MessageLoop::current() != message_loop_ || - state_ == kDrainingDecoder || - !pending_read_cb_.is_null()) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread() || + state_ == kDrainingDecoder) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::Flush, this, callback)); return; } @@ -121,17 +118,31 @@ void GpuVideoDecoder::Flush(const base::Closure& callback) { callback.Run(); return; } + DCHECK(pending_reset_cb_.is_null()); DCHECK(!callback.is_null()); - pending_reset_cb_ = callback; - vda_->Reset(); + + if (shutting_down_) { + // VideoRendererBase::Flush() can't complete while it has a pending read to + // us, so we fulfill such a read here. + if (!pending_read_cb_.is_null()) + EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEmptyFrame()); + // Immediate fire the callback instead of waiting for the reset to complete + // (which will happen after PipelineImpl::Stop() completes). + callback.Run(); + } else { + pending_reset_cb_ = callback; + } + + render_loop_proxy_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Reset, vda_)); } void GpuVideoDecoder::Initialize(DemuxerStream* demuxer_stream, const PipelineStatusCB& callback, const StatisticsCallback& stats_callback) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::Initialize, this, make_scoped_refptr(demuxer_stream), callback, stats_callback)); return; @@ -170,8 +181,8 @@ void GpuVideoDecoder::Initialize(DemuxerStream* demuxer_stream, } void GpuVideoDecoder::Read(const ReadCB& callback) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::Read, this, callback)); return; } @@ -205,8 +216,8 @@ void GpuVideoDecoder::Read(const ReadCB& callback) { } void GpuVideoDecoder::RequestBufferDecode(const scoped_refptr<Buffer>& buffer) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::RequestBufferDecode, this, buffer)); return; } @@ -220,7 +231,8 @@ void GpuVideoDecoder::RequestBufferDecode(const scoped_refptr<Buffer>& buffer) { if (buffer->IsEndOfStream()) { if (state_ == kNormal) { state_ = kDrainingDecoder; - vda_->Flush(); + render_loop_proxy_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Flush, vda_)); } return; } @@ -235,7 +247,8 @@ void GpuVideoDecoder::RequestBufferDecode(const scoped_refptr<Buffer>& buffer) { DCHECK(inserted); RecordBufferTimeData(bitstream_buffer, *buffer); - vda_->Decode(bitstream_buffer); + render_loop_proxy_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::Decode, vda_, bitstream_buffer)); } void GpuVideoDecoder::RecordBufferTimeData( @@ -280,14 +293,23 @@ bool GpuVideoDecoder::HasAlpha() const { return true; } +void GpuVideoDecoder::PrepareForShutdownHack() { + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( + &GpuVideoDecoder::PrepareForShutdownHack, this)); + return; + } + shutting_down_ = true; +} + void GpuVideoDecoder::NotifyInitializeDone() { NOTREACHED() << "GpuVideoDecodeAcceleratorHost::Initialize is synchronous!"; } void GpuVideoDecoder::ProvidePictureBuffers(uint32 count, const gfx::Size& size) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::ProvidePictureBuffers, this, count, size)); return; } @@ -309,12 +331,13 @@ void GpuVideoDecoder::ProvidePictureBuffers(uint32 count, picture_buffers.back().id(), picture_buffers.back())).second; DCHECK(inserted); } - vda_->AssignPictureBuffers(picture_buffers); + render_loop_proxy_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::AssignPictureBuffers, vda_, picture_buffers)); } void GpuVideoDecoder::DismissPictureBuffer(int32 id) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::DismissPictureBuffer, this, id)); return; } @@ -324,16 +347,13 @@ void GpuVideoDecoder::DismissPictureBuffer(int32 id) { NOTREACHED() << "Missing picture buffer: " << id; return; } - if (!factories_->DeleteTexture(it->second.texture_id())) { - NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE); - return; - } + factories_->DeleteTexture(it->second.texture_id()); picture_buffers_in_decoder_.erase(it); } void GpuVideoDecoder::PictureReady(const media::Picture& picture) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::PictureReady, this, picture)); return; } @@ -362,7 +382,7 @@ void GpuVideoDecoder::PictureReady(const media::Picture& picture) { void GpuVideoDecoder::EnqueueFrameAndTriggerFrameDelivery( const scoped_refptr<VideoFrame>& frame) { - DCHECK(MessageLoop::current() == message_loop_); + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); // During a pending vda->Reset(), we don't accumulate frames. Drop it on the // floor and return. @@ -377,25 +397,26 @@ void GpuVideoDecoder::EnqueueFrameAndTriggerFrameDelivery( if (pending_read_cb_.is_null()) return; - message_loop_->PostTask(FROM_HERE, base::Bind( + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( pending_read_cb_, ready_video_frames_.front())); pending_read_cb_.Reset(); ready_video_frames_.pop_front(); } void GpuVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::ReusePictureBuffer, this, picture_buffer_id)); return; } if (!vda_) return; - vda_->ReusePictureBuffer(picture_buffer_id); + render_loop_proxy_->PostTask(FROM_HERE, base::Bind( + &VideoDecodeAccelerator::ReusePictureBuffer, vda_, picture_buffer_id)); } GpuVideoDecoder::SHMBuffer* GpuVideoDecoder::GetSHM(size_t min_size) { - DCHECK(MessageLoop::current() == message_loop_); + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); if (available_shm_segments_.empty() || available_shm_segments_.back()->size < min_size) { size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes); @@ -409,13 +430,13 @@ GpuVideoDecoder::SHMBuffer* GpuVideoDecoder::GetSHM(size_t min_size) { } void GpuVideoDecoder::PutSHM(SHMBuffer* shm_buffer) { - DCHECK(MessageLoop::current() == message_loop_); + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); available_shm_segments_.push_back(shm_buffer); } void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::NotifyEndOfBitstreamBuffer, this, id)); return; } @@ -446,7 +467,7 @@ void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) { } void GpuVideoDecoder::EnsureDemuxOrDecode() { - DCHECK(MessageLoop::current() == message_loop_); + DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); if (demuxer_read_in_progress_) return; demuxer_read_in_progress_ = true; @@ -455,8 +476,8 @@ void GpuVideoDecoder::EnsureDemuxOrDecode() { } void GpuVideoDecoder::NotifyFlushDone() { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::NotifyFlushDone, this)); return; } @@ -466,23 +487,31 @@ void GpuVideoDecoder::NotifyFlushDone() { } void GpuVideoDecoder::NotifyResetDone() { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::NotifyResetDone, this)); return; } + + if (!vda_) + return; + DCHECK(ready_video_frames_.empty()); - // This needs to happen after vda_->Reset() is done to ensure pictures + // This needs to happen after the Reset() on vda_ is done to ensure pictures // delivered during the reset can find their time data. input_buffer_time_data_.clear(); - ResetAndRunCB(&pending_reset_cb_); + if (!pending_reset_cb_.is_null()) + ResetAndRunCB(&pending_reset_cb_); + + if (!pending_read_cb_.is_null()) + EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEmptyFrame()); } void GpuVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) { - if (MessageLoop::current() != message_loop_) { - message_loop_->PostTask(FROM_HERE, base::Bind( + if (!gvd_loop_proxy_->BelongsToCurrentThread()) { + gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( &GpuVideoDecoder::NotifyError, this, error)); return; } diff --git a/media/filters/gpu_video_decoder.h b/media/filters/gpu_video_decoder.h index 80c8011..753a51b 100644 --- a/media/filters/gpu_video_decoder.h +++ b/media/filters/gpu_video_decoder.h @@ -9,7 +9,6 @@ #include <list> #include <map> -#include "base/memory/scoped_ptr.h" #include "media/base/filters.h" #include "media/base/pipeline_status.h" #include "media/base/pts_stream.h" @@ -17,7 +16,9 @@ #include "ui/gfx/size.h" class MessageLoop; +template <class T> class scoped_refptr; namespace base { +class MessageLoopProxy; class SharedMemory; } @@ -25,17 +26,15 @@ namespace media { // GPU-accelerated video decoder implementation. Relies on // AcceleratedVideoDecoderMsg_Decode and friends. -// All methods internally trampoline to the message_loop passed to the ctor. +// All methods internally trampoline to the |message_loop| passed to the ctor. class MEDIA_EXPORT GpuVideoDecoder : public VideoDecoder, public VideoDecodeAccelerator::Client { public: // Helper interface for specifying factories needed to instantiate a // GpuVideoDecoder. - class MEDIA_EXPORT Factories { + class MEDIA_EXPORT Factories : public base::RefCountedThreadSafe<Factories> { public: - virtual ~Factories(); - // Caller owns returned pointer. virtual VideoDecodeAccelerator* CreateVideoDecodeAccelerator( VideoDecodeAccelerator::Profile, VideoDecodeAccelerator::Client*) = 0; @@ -43,14 +42,19 @@ class MEDIA_EXPORT GpuVideoDecoder // Allocate & delete native textures. virtual bool CreateTextures(int32 count, const gfx::Size& size, std::vector<uint32>* texture_ids) = 0; - virtual bool DeleteTexture(uint32 texture_id) = 0; + virtual void DeleteTexture(uint32 texture_id) = 0; // Allocate & return a shared memory segment. Caller is responsible for // Close()ing the returned pointer. virtual base::SharedMemory* CreateSharedMemory(size_t size) = 0; + + protected: + friend class base::RefCountedThreadSafe<Factories>; + virtual ~Factories(); }; - GpuVideoDecoder(MessageLoop* message_loop, scoped_ptr<Factories> factories); + GpuVideoDecoder(MessageLoop* message_loop, + const scoped_refptr<Factories>& factories); virtual ~GpuVideoDecoder(); // Filter implementation. @@ -66,6 +70,7 @@ class MEDIA_EXPORT GpuVideoDecoder virtual void Read(const ReadCB& callback) OVERRIDE; virtual const gfx::Size& natural_size() OVERRIDE; virtual bool HasAlpha() const OVERRIDE; + virtual void PrepareForShutdownHack() OVERRIDE; // VideoDecodeAccelerator::Client implementation. virtual void NotifyInitializeDone() OVERRIDE; @@ -141,11 +146,16 @@ class MEDIA_EXPORT GpuVideoDecoder // Pointer to the demuxer stream that will feed us compressed buffers. scoped_refptr<DemuxerStream> demuxer_stream_; - // MessageLoop on which to do fire callbacks and to which trampoline calls to - // this class if they arrive on other loops. - MessageLoop* message_loop_; + // MessageLoop on which to fire callbacks and trampoline calls to this class + // if they arrive on other loops. + scoped_refptr<base::MessageLoopProxy> gvd_loop_proxy_; + + // Creation message loop (typically the render thread). All calls to vda_ + // must be made on this loop (and beware this loop is paused during the + // Pause/Flush/Stop dance PipelineImpl::Stop() goes through). + scoped_refptr<base::MessageLoopProxy> render_loop_proxy_; - scoped_ptr<Factories> factories_; + scoped_refptr<Factories> factories_; // Populated during Initialize() (on success) and unchanged thereafter. scoped_refptr<VideoDecodeAccelerator> vda_; @@ -190,6 +200,10 @@ class MEDIA_EXPORT GpuVideoDecoder int64 next_picture_buffer_id_; int64 next_bitstream_buffer_id_; + // Indicates PrepareForShutdownHack()'s been called. Makes further calls to + // this class not require the render thread's loop to be processing. + bool shutting_down_; + DISALLOW_COPY_AND_ASSIGN(GpuVideoDecoder); }; |