summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfischman@chromium.org <fischman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-14 00:53:31 +0000
committerfischman@chromium.org <fischman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-14 00:53:31 +0000
commit8d4359280e59014e85a6b230eec35598c2113498 (patch)
treeb3fb0c587d2aa94a8fe6f6c558013e96a683f89a
parent65267eb77b59758337dabc651f949e5feeac7cf5 (diff)
downloadchromium_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.cc8
-rw-r--r--content/renderer/render_view_impl.cc7
-rw-r--r--content/renderer/renderer_gpu_video_decoder_factories.cc77
-rw-r--r--content/renderer/renderer_gpu_video_decoder_factories.h34
-rw-r--r--media/base/filters.cc4
-rw-r--r--media/base/filters.h10
-rw-r--r--media/base/pipeline_impl.cc16
-rw-r--r--media/base/pipeline_impl.h6
-rw-r--r--media/filters/gpu_video_decoder.cc149
-rw-r--r--media/filters/gpu_video_decoder.h36
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);
};