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 | |
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
-rw-r--r-- | content/common/gpu/media/omx_video_decode_accelerator.cc | 8 | ||||
-rw-r--r-- | content/renderer/render_view_impl.cc | 7 | ||||
-rw-r--r-- | content/renderer/renderer_gpu_video_decoder_factories.cc | 77 | ||||
-rw-r--r-- | content/renderer/renderer_gpu_video_decoder_factories.h | 34 | ||||
-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 |
10 files changed, 245 insertions, 102 deletions
diff --git a/content/common/gpu/media/omx_video_decode_accelerator.cc b/content/common/gpu/media/omx_video_decode_accelerator.cc index e98f678..c2c7949 100644 --- a/content/common/gpu/media/omx_video_decode_accelerator.cc +++ b/content/common/gpu/media/omx_video_decode_accelerator.cc @@ -457,7 +457,9 @@ void OmxVideoDecodeAccelerator::Destroy() { ShutdownComponent(); return; } - DCHECK_EQ(client_state_, OMX_StateExecuting); + DCHECK(client_state_ == OMX_StateExecuting || + client_state_ == OMX_StateIdle || + client_state_ == OMX_StatePause); current_state_change_ = DESTROYING; client_ = NULL; BeginTransitionToState(OMX_StateIdle); @@ -572,7 +574,9 @@ void OmxVideoDecodeAccelerator::BusyLoopInDestroying() { } void OmxVideoDecodeAccelerator::OnReachedIdleInDestroying() { - DCHECK_EQ(client_state_, OMX_StateExecuting); + DCHECK(client_state_ == OMX_StateExecuting || + client_state_ == OMX_StateIdle || + client_state_ == OMX_StatePause); client_state_ = OMX_StateIdle; // Note that during the Executing -> Idle transition, the OMX spec guarantees diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index dedaf5e..8d5d6e5 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -1977,10 +1977,9 @@ WebMediaPlayer* RenderViewImpl::createMediaPlayer( RenderThreadImpl::current()->EstablishGpuChannelSync( content::CAUSE_FOR_GPU_LAUNCH_VIDEODECODEACCELERATOR_INITIALIZE); collection->AddVideoDecoder(new media::GpuVideoDecoder( - MessageLoop::current(), - scoped_ptr<media::GpuVideoDecoder::Factories>( - new RendererGpuVideoDecoderFactories( - gpu_channel_host, context3d->context()->AsWeakPtr())))); + message_loop_factory->GetMessageLoop("GpuVideoDecoder"), + new RendererGpuVideoDecoderFactories( + gpu_channel_host, context3d->context()->AsWeakPtr()))); } #endif diff --git a/content/renderer/renderer_gpu_video_decoder_factories.cc b/content/renderer/renderer_gpu_video_decoder_factories.cc index 5195d3e..70b27ac 100644 --- a/content/renderer/renderer_gpu_video_decoder_factories.cc +++ b/content/renderer/renderer_gpu_video_decoder_factories.cc @@ -4,6 +4,8 @@ #include "content/renderer/renderer_gpu_video_decoder_factories.h" +#include "base/bind.h" +#include "base/synchronization/waitable_event.h" #include "content/common/child_thread.h" #include "content/renderer/gpu/command_buffer_proxy.h" #include "content/renderer/gpu/gpu_channel_host.h" @@ -13,7 +15,8 @@ RendererGpuVideoDecoderFactories::~RendererGpuVideoDecoderFactories() {} RendererGpuVideoDecoderFactories::RendererGpuVideoDecoderFactories( GpuChannelHost* gpu_channel_host, base::WeakPtr<RendererGLContext> context) - : gpu_channel_host_(gpu_channel_host), + : message_loop_(MessageLoop::current()), + gpu_channel_host_(gpu_channel_host), context_(context) { } @@ -21,16 +24,48 @@ media::VideoDecodeAccelerator* RendererGpuVideoDecoderFactories::CreateVideoDecodeAccelerator( media::VideoDecodeAccelerator::Profile profile, media::VideoDecodeAccelerator::Client* client) { - if (!context_) - return NULL; - return gpu_channel_host_->CreateVideoDecoder( - context_->GetCommandBufferProxy()->route_id(), profile, client); + media::VideoDecodeAccelerator* vda = NULL; + base::WaitableEvent waiter(false, false); + message_loop_->PostTask(FROM_HERE, base::Bind( + &RendererGpuVideoDecoderFactories::AsyncCreateVideoDecodeAccelerator, + this, profile, client, &vda, &waiter)); + waiter.Wait(); + return vda; +} + +void RendererGpuVideoDecoderFactories::AsyncCreateVideoDecodeAccelerator( + media::VideoDecodeAccelerator::Profile profile, + media::VideoDecodeAccelerator::Client* client, + media::VideoDecodeAccelerator** vda, + base::WaitableEvent* waiter) { + if (context_) { + *vda = gpu_channel_host_->CreateVideoDecoder( + context_->GetCommandBufferProxy()->route_id(), profile, client); + } else { + *vda = NULL; + } + waiter->Signal(); } bool RendererGpuVideoDecoderFactories::CreateTextures( int32 count, const gfx::Size& size, std::vector<uint32>* texture_ids) { - if (!context_) - return false; + bool success = false; + base::WaitableEvent waiter(false, false); + message_loop_->PostTask(FROM_HERE, base::Bind( + &RendererGpuVideoDecoderFactories::AsyncCreateTextures, this, + count, size, texture_ids, &success, &waiter)); + waiter.Wait(); + return success; +} + +void RendererGpuVideoDecoderFactories::AsyncCreateTextures( + int32 count, const gfx::Size& size, std::vector<uint32>* texture_ids, + bool* success, base::WaitableEvent* waiter) { + if (!context_) { + *success = false; + waiter->Signal(); + return; + } gpu::gles2::GLES2Implementation* gles2 = context_->GetImplementation(); texture_ids->resize(count); gles2->GenTextures(count, &texture_ids->at(0)); @@ -50,19 +85,37 @@ bool RendererGpuVideoDecoderFactories::CreateTextures( // reused, this should not be unacceptably expensive. gles2->Flush(); DCHECK_EQ(gles2->GetError(), static_cast<GLenum>(GL_NO_ERROR)); - return true; + *success = true; + waiter->Signal(); +} + +void RendererGpuVideoDecoderFactories::DeleteTexture(uint32 texture_id) { + message_loop_->PostTask(FROM_HERE, base::Bind( + &RendererGpuVideoDecoderFactories::AsyncDeleteTexture, this, texture_id)); } -bool RendererGpuVideoDecoderFactories::DeleteTexture(uint32 texture_id) { +void RendererGpuVideoDecoderFactories::AsyncDeleteTexture(uint32 texture_id) { + DCHECK_EQ(MessageLoop::current(), message_loop_); if (!context_) - return false; + return; gpu::gles2::GLES2Implementation* gles2 = context_->GetImplementation(); gles2->DeleteTextures(1, &texture_id); DCHECK_EQ(gles2->GetError(), static_cast<GLenum>(GL_NO_ERROR)); - return true; } base::SharedMemory* RendererGpuVideoDecoderFactories::CreateSharedMemory( size_t size) { - return ChildThread::current()->AllocateSharedMemory(size); + base::SharedMemory* shm = NULL; + base::WaitableEvent waiter(false, false); + message_loop_->PostTask(FROM_HERE, base::Bind( + &RendererGpuVideoDecoderFactories::AsyncCreateSharedMemory, this, + size, &shm, &waiter)); + waiter.Wait(); + return shm; +} + +void RendererGpuVideoDecoderFactories::AsyncCreateSharedMemory( + size_t size, base::SharedMemory** shm, base::WaitableEvent* waiter) { + *shm = ChildThread::current()->AllocateSharedMemory(size); + waiter->Signal(); } diff --git a/content/renderer/renderer_gpu_video_decoder_factories.h b/content/renderer/renderer_gpu_video_decoder_factories.h index 29cbfa6..c7a7bb5 100644 --- a/content/renderer/renderer_gpu_video_decoder_factories.h +++ b/content/renderer/renderer_gpu_video_decoder_factories.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. @@ -15,11 +15,18 @@ class GpuChannelHost; class RendererGLContext; +namespace base { +class WaitableEvent; +} // Glue code to expose functionality needed by media::GpuVideoDecoder to // RenderViewImpl. This class is entirely an implementation detail of // RenderViewImpl and only has its own header to allow extraction of its // implementation from render_view_impl.cc which is already far too large. +// +// The public methods of the class can be called from any thread, and are +// internally trampolined to the thread on which the class was constructed +// (de-facto, the renderer thread) if called from a different thread. class CONTENT_EXPORT RendererGpuVideoDecoderFactories : public media::GpuVideoDecoder::Factories { public: @@ -28,8 +35,6 @@ class CONTENT_EXPORT RendererGpuVideoDecoderFactories RendererGpuVideoDecoderFactories(GpuChannelHost* gpu_channel_host, base::WeakPtr<RendererGLContext> context); - virtual ~RendererGpuVideoDecoderFactories(); - virtual media::VideoDecodeAccelerator* CreateVideoDecodeAccelerator( media::VideoDecodeAccelerator::Profile profile, media::VideoDecodeAccelerator::Client* client) OVERRIDE; @@ -37,11 +42,32 @@ class CONTENT_EXPORT RendererGpuVideoDecoderFactories virtual bool CreateTextures(int32 count, const gfx::Size& size, std::vector<uint32>* texture_ids) OVERRIDE; - virtual bool DeleteTexture(uint32 texture_id) OVERRIDE; + virtual void DeleteTexture(uint32 texture_id) OVERRIDE; virtual base::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE; + protected: + friend class base::RefCountedThreadSafe<RendererGpuVideoDecoderFactories>; + virtual ~RendererGpuVideoDecoderFactories(); + private: + // Async versions of the public methods. These all run on |message_loop_| + // exclusively, and use output parameters instead of return values. Finally, + // each takes a WaitableEvent* param to signal completion (except for + // DeleteTexture, which is fire-and-forget). + void AsyncCreateVideoDecodeAccelerator( + media::VideoDecodeAccelerator::Profile profile, + media::VideoDecodeAccelerator::Client* client, + media::VideoDecodeAccelerator** vda, + base::WaitableEvent* waiter); + void AsyncCreateTextures( + int32 count, const gfx::Size& size, std::vector<uint32>* texture_ids, + bool* success, base::WaitableEvent* waiter); + void AsyncDeleteTexture(uint32 texture_id); + void AsyncCreateSharedMemory( + size_t size, base::SharedMemory** shm, base::WaitableEvent* waiter); + + MessageLoop* message_loop_; scoped_refptr<GpuChannelHost> gpu_channel_host_; base::WeakPtr<RendererGLContext> context_; DISALLOW_IMPLICIT_CONSTRUCTORS(RendererGpuVideoDecoderFactories); 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); }; |