Index: media/filters/gpu_video_decoder.cc |
diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc |
index b74ba5ba82e5c8a2563b6b7f84459d7402b24c55..6904bbb763e58da767f034815bef85bfd2646c34 100644 |
--- a/media/filters/gpu_video_decoder.cc |
+++ b/media/filters/gpu_video_decoder.cc |
@@ -9,6 +9,7 @@ |
#include "base/cpu.h" |
#include "base/message_loop.h" |
#include "base/stl_util.h" |
+#include "base/task_runner_util.h" |
#include "media/base/bind_to_loop.h" |
#include "media/base/decoder_buffer.h" |
#include "media/base/demuxer_stream.h" |
@@ -18,6 +19,114 @@ |
namespace media { |
+// Proxies calls to a VideoDecodeAccelerator::Client from the calling thread to |
+// the client's thread. |
+// |
+// TODO(scherkus): VDAClientProxy should hold onto GpuVideoDecoder::Factories |
+// and take care of some of the work that GpuVideoDecoder does to minimize |
+// thread hopping. See following for discussion: |
+// |
+// https://codereview.chromium.org/12989009/diff/27035/media/filters/gpu_video_decoder.cc#newcode23 |
+class VDAClientProxy |
+ : public base::RefCountedThreadSafe<VDAClientProxy>, |
+ public VideoDecodeAccelerator::Client { |
+ public: |
+ explicit VDAClientProxy(VideoDecodeAccelerator::Client* client); |
+ |
+ // Detaches the proxy. |client| will no longer be called and can be safely |
xhwang
2013/04/18 00:40:48
s/client/weak_client_/ ?
scherkus (not reviewing)
2013/04/18 18:45:29
Done.
|
+ // deleted. Any pending/future calls will be discarded. |
+ // |
+ // Must be called on |client_loop|. |
xhwang
2013/04/18 00:40:48
s/client_loop/client_loop_/
scherkus (not reviewing)
2013/04/18 18:45:29
Done.
|
+ void Detach(); |
+ |
+ // VideoDecodeAccelerator::Client implementation. |
+ virtual void NotifyInitializeDone() OVERRIDE; |
+ virtual void ProvidePictureBuffers(uint32 count, |
+ const gfx::Size& size, |
+ uint32 texture_target) OVERRIDE; |
+ virtual void DismissPictureBuffer(int32 id) OVERRIDE; |
+ virtual void PictureReady(const media::Picture& picture) OVERRIDE; |
+ virtual void NotifyEndOfBitstreamBuffer(int32 id) OVERRIDE; |
+ virtual void NotifyFlushDone() OVERRIDE; |
+ virtual void NotifyResetDone() OVERRIDE; |
+ virtual void NotifyError(media::VideoDecodeAccelerator::Error error) OVERRIDE; |
+ |
+ private: |
+ friend class base::RefCountedThreadSafe<VDAClientProxy>; |
+ virtual ~VDAClientProxy(); |
+ |
+ scoped_refptr<base::MessageLoopProxy> client_loop_; |
+ |
+ // Weak pointers are used to invalidate tasks posted to |client_loop_| that |
xhwang
2013/04/18 00:40:48
s/that/after ?
scherkus (not reviewing)
2013/04/18 18:45:29
Done.
|
+ // Detach() has been called. |
+ base::WeakPtrFactory<VideoDecodeAccelerator::Client> weak_client_factory_; |
+ base::WeakPtr<VideoDecodeAccelerator::Client> weak_client_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(VDAClientProxy); |
+}; |
+ |
+VDAClientProxy::VDAClientProxy(VideoDecodeAccelerator::Client* client) |
+ : client_loop_(base::MessageLoopProxy::current()), |
+ weak_client_factory_(client), |
+ weak_client_(weak_client_factory_.GetWeakPtr()) { |
+ DCHECK(weak_client_); |
+} |
+ |
+VDAClientProxy::~VDAClientProxy() { |
+ DCHECK(!weak_client_) << "Detach() was not called"; |
+} |
+ |
+void VDAClientProxy::Detach() { |
+ DCHECK(client_loop_->BelongsToCurrentThread()); |
+ DCHECK(weak_client_) << "Detach() already called"; |
+ weak_client_factory_.InvalidateWeakPtrs(); |
+} |
+ |
+void VDAClientProxy::NotifyInitializeDone() { |
+ client_loop_->PostTask(FROM_HERE, base::Bind( |
+ &VideoDecodeAccelerator::Client::NotifyInitializeDone, weak_client_)); |
+} |
+ |
+void VDAClientProxy::ProvidePictureBuffers(uint32 count, |
+ const gfx::Size& size, |
+ uint32 texture_target) { |
+ client_loop_->PostTask(FROM_HERE, base::Bind( |
+ &VideoDecodeAccelerator::Client::ProvidePictureBuffers, weak_client_, |
+ count, size, texture_target)); |
+} |
+ |
+void VDAClientProxy::DismissPictureBuffer(int32 id) { |
+ client_loop_->PostTask(FROM_HERE, base::Bind( |
+ &VideoDecodeAccelerator::Client::DismissPictureBuffer, weak_client_, id)); |
+} |
+ |
+void VDAClientProxy::PictureReady(const media::Picture& picture) { |
+ client_loop_->PostTask(FROM_HERE, base::Bind( |
+ &VideoDecodeAccelerator::Client::PictureReady, weak_client_, picture)); |
+} |
+ |
+void VDAClientProxy::NotifyEndOfBitstreamBuffer(int32 id) { |
+ client_loop_->PostTask(FROM_HERE, base::Bind( |
+ &VideoDecodeAccelerator::Client::NotifyEndOfBitstreamBuffer, weak_client_, |
+ id)); |
+} |
+ |
+void VDAClientProxy::NotifyFlushDone() { |
+ client_loop_->PostTask(FROM_HERE, base::Bind( |
+ &VideoDecodeAccelerator::Client::NotifyFlushDone, weak_client_)); |
+} |
+ |
+void VDAClientProxy::NotifyResetDone() { |
+ client_loop_->PostTask(FROM_HERE, base::Bind( |
+ &VideoDecodeAccelerator::Client::NotifyResetDone, weak_client_)); |
+} |
+ |
+void VDAClientProxy::NotifyError(media::VideoDecodeAccelerator::Error error) { |
+ client_loop_->PostTask(FROM_HERE, base::Bind( |
+ &VideoDecodeAccelerator::Client::NotifyError, weak_client_, error)); |
+} |
+ |
+ |
// Maximum number of concurrent VDA::Decode() operations GVD will maintain. |
// Higher values allow better pipelining in the GPU, but also require more |
// resources. |
@@ -54,6 +163,7 @@ GpuVideoDecoder::GpuVideoDecoder( |
const scoped_refptr<base::MessageLoopProxy>& message_loop, |
const scoped_refptr<Factories>& factories) |
: gvd_loop_proxy_(message_loop), |
+ weak_factory_(this), |
vda_loop_proxy_(factories->GetMessageLoop()), |
factories_(factories), |
state_(kNormal), |
@@ -71,7 +181,7 @@ void GpuVideoDecoder::Reset(const base::Closure& closure) { |
if (state_ == kDrainingDecoder && !factories_->IsAborted()) { |
gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
- &GpuVideoDecoder::Reset, this, closure)); |
+ &GpuVideoDecoder::Reset, weak_this_, closure)); |
// NOTE: if we're deferring Reset() until a Flush() completes, return |
// queued pictures to the VDA so they can be used to finish that Flush(). |
if (pending_read_cb_.is_null()) |
@@ -115,6 +225,8 @@ void GpuVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, |
const PipelineStatusCB& orig_status_cb, |
const StatisticsCB& statistics_cb) { |
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
+ weak_this_ = weak_factory_.GetWeakPtr(); |
+ |
PipelineStatusCB status_cb = CreateUMAReportingPipelineCB( |
"Media.GpuVideoDecoderInitializeStatus", |
BindToCurrentLoop(orig_status_cb)); |
@@ -152,8 +264,9 @@ void GpuVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, |
} |
} |
+ client_proxy_ = new VDAClientProxy(this); |
VideoDecodeAccelerator* vda = |
- factories_->CreateVideoDecodeAccelerator(config.profile(), this); |
+ factories_->CreateVideoDecodeAccelerator(config.profile(), client_proxy_); |
if (!vda) { |
status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
return; |
@@ -166,17 +279,21 @@ void GpuVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, |
statistics_cb_ = statistics_cb; |
DVLOG(1) << "GpuVideoDecoder::Initialize() succeeded."; |
- vda_loop_proxy_->PostTaskAndReply( |
- FROM_HERE, |
- base::Bind(&GpuVideoDecoder::SetVDA, this, vda), |
- base::Bind(status_cb, PIPELINE_OK)); |
+ PostTaskAndReplyWithResult( |
+ vda_loop_proxy_, FROM_HERE, |
+ base::Bind(&VideoDecodeAccelerator::AsWeakPtr, base::Unretained(vda)), |
+ base::Bind(&GpuVideoDecoder::SetVDA, weak_this_, status_cb, vda)); |
} |
-void GpuVideoDecoder::SetVDA(VideoDecodeAccelerator* vda) { |
- DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); |
+void GpuVideoDecoder::SetVDA( |
+ const PipelineStatusCB& status_cb, |
+ VideoDecodeAccelerator* vda, |
+ base::WeakPtr<VideoDecodeAccelerator> weak_vda) { |
+ DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
DCHECK(!vda_.get()); |
vda_.reset(vda); |
- weak_vda_ = vda->AsWeakPtr(); |
+ weak_vda_ = weak_vda; |
+ status_cb.Run(PIPELINE_OK); |
} |
void GpuVideoDecoder::DestroyTextures() { |
@@ -188,17 +305,24 @@ void GpuVideoDecoder::DestroyTextures() { |
picture_buffers_in_decoder_.clear(); |
} |
+static void DestroyVDAWithClientProxy( |
+ const scoped_refptr<VDAClientProxy>& client_proxy, |
+ base::WeakPtr<VideoDecodeAccelerator> weak_vda) { |
+ DCHECK(client_proxy->HasOneRef()); |
xhwang
2013/04/18 00:40:48
hmm, are we making assumption about vda implementa
scherkus (not reviewing)
2013/04/18 18:45:29
ah good catch -- fischman@ suggested this but you'
|
+ if (weak_vda) |
xhwang
2013/04/18 00:40:48
OOC, in which case can this happen? DestroyVDA cal
scherkus (not reviewing)
2013/04/18 18:45:29
you can only check/deref a weak pointer on the thr
|
+ weak_vda->Destroy(); |
xhwang
2013/04/18 00:40:48
weak_vda.reset()?
scherkus (not reviewing)
2013/04/18 18:45:29
Destroy() is a VDA method that takes care of delet
xhwang
2013/04/18 19:17:49
Sorry, I meant calling .reset() after ->Destroy().
scherkus (not reviewing)
2013/04/18 20:21:14
Done.
|
+} |
+ |
void GpuVideoDecoder::DestroyVDA() { |
DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
+ |
+ // |client_proxy| must stay alive until |weak_vda_| has been destroyed. |
+ vda_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
+ &DestroyVDAWithClientProxy, client_proxy_, weak_vda_)); |
+ |
VideoDecodeAccelerator* vda ALLOW_UNUSED = vda_.release(); |
- // Tricky: |this| needs to stay alive until after VDA::Destroy is actually |
- // called, not just posted, so we take an artificial ref to |this| and release |
- // it as |reply| after VDA::Destroy() returns. |
- AddRef(); |
- vda_loop_proxy_->PostTaskAndReply( |
- FROM_HERE, |
- base::Bind(&VideoDecodeAccelerator::Destroy, weak_vda_), |
- base::Bind(&GpuVideoDecoder::Release, this)); |
+ client_proxy_->Detach(); |
+ client_proxy_ = NULL; |
DestroyTextures(); |
} |
@@ -246,13 +370,9 @@ bool GpuVideoDecoder::CanMoreDecodeWorkBeDone() { |
void GpuVideoDecoder::RequestBufferDecode( |
DemuxerStream::Status status, |
const scoped_refptr<DecoderBuffer>& buffer) { |
+ DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; |
- if (!gvd_loop_proxy_->BelongsToCurrentThread()) { |
- gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
- &GpuVideoDecoder::RequestBufferDecode, this, status, buffer)); |
- return; |
- } |
demuxer_read_in_progress_ = false; |
if (status != DemuxerStream::kOk) { |
@@ -307,7 +427,7 @@ void GpuVideoDecoder::RequestBufferDecode( |
if (CanMoreDecodeWorkBeDone()) { |
// Force post here to prevent reentrancy into DemuxerStream. |
gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
- &GpuVideoDecoder::EnsureDemuxOrDecode, this)); |
+ &GpuVideoDecoder::EnsureDemuxOrDecode, weak_this_)); |
} |
} |
@@ -360,12 +480,7 @@ void GpuVideoDecoder::NotifyInitializeDone() { |
void GpuVideoDecoder::ProvidePictureBuffers(uint32 count, |
const gfx::Size& size, |
uint32 texture_target) { |
- if (!gvd_loop_proxy_->BelongsToCurrentThread()) { |
- gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
- &GpuVideoDecoder::ProvidePictureBuffers, this, count, size, |
- texture_target)); |
- return; |
- } |
+ DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
std::vector<uint32> texture_ids; |
decoder_texture_target_ = texture_target; |
@@ -395,11 +510,8 @@ void GpuVideoDecoder::ProvidePictureBuffers(uint32 count, |
} |
void GpuVideoDecoder::DismissPictureBuffer(int32 id) { |
- if (!gvd_loop_proxy_->BelongsToCurrentThread()) { |
- gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
- &GpuVideoDecoder::DismissPictureBuffer, this, id)); |
- return; |
- } |
+ DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
+ |
std::map<int32, PictureBuffer>::iterator it = |
picture_buffers_in_decoder_.find(id); |
if (it == picture_buffers_in_decoder_.end()) { |
@@ -411,11 +523,8 @@ void GpuVideoDecoder::DismissPictureBuffer(int32 id) { |
} |
void GpuVideoDecoder::PictureReady(const media::Picture& picture) { |
- if (!gvd_loop_proxy_->BelongsToCurrentThread()) { |
- gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
- &GpuVideoDecoder::PictureReady, this, picture)); |
- return; |
- } |
+ DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
+ |
std::map<int32, PictureBuffer>::iterator it = |
picture_buffers_in_decoder_.find(picture.picture_buffer_id()); |
if (it == picture_buffers_in_decoder_.end()) { |
@@ -439,8 +548,9 @@ void GpuVideoDecoder::PictureReady(const media::Picture& picture) { |
base::Bind(&Factories::ReadPixels, factories_, pb.texture_id(), |
decoder_texture_target_, |
gfx::Size(visible_rect.width(), visible_rect.height())), |
- base::Bind(&GpuVideoDecoder::ReusePictureBuffer, this, |
- picture.picture_buffer_id()))); |
+ BindToCurrentLoop(base::Bind( |
+ &GpuVideoDecoder::ReusePictureBuffer, weak_this_, |
+ picture.picture_buffer_id())))); |
CHECK_GT(available_pictures_, 0); |
available_pictures_--; |
@@ -469,11 +579,7 @@ void GpuVideoDecoder::EnqueueFrameAndTriggerFrameDelivery( |
} |
void GpuVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id) { |
- if (!gvd_loop_proxy_->BelongsToCurrentThread()) { |
- gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
- &GpuVideoDecoder::ReusePictureBuffer, this, picture_buffer_id)); |
- return; |
- } |
+ DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
CHECK_GE(available_pictures_, 0); |
available_pictures_++; |
@@ -506,11 +612,7 @@ void GpuVideoDecoder::PutSHM(SHMBuffer* shm_buffer) { |
} |
void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) { |
- if (!gvd_loop_proxy_->BelongsToCurrentThread()) { |
- gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
- &GpuVideoDecoder::NotifyEndOfBitstreamBuffer, this, id)); |
- return; |
- } |
+ DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
std::map<int32, BufferPair>::iterator it = |
bitstream_buffers_in_decoder_.find(id); |
@@ -565,27 +667,18 @@ void GpuVideoDecoder::EnsureDemuxOrDecode() { |
demuxer_read_in_progress_ = true; |
demuxer_stream_->Read(base::Bind( |
- &GpuVideoDecoder::RequestBufferDecode, this)); |
+ &GpuVideoDecoder::RequestBufferDecode, weak_this_)); |
} |
void GpuVideoDecoder::NotifyFlushDone() { |
- if (!gvd_loop_proxy_->BelongsToCurrentThread()) { |
- gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
- &GpuVideoDecoder::NotifyFlushDone, this)); |
- return; |
- } |
+ DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
DCHECK_EQ(state_, kDrainingDecoder); |
state_ = kDecoderDrained; |
EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEmptyFrame()); |
} |
void GpuVideoDecoder::NotifyResetDone() { |
- if (!gvd_loop_proxy_->BelongsToCurrentThread()) { |
- gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
- &GpuVideoDecoder::NotifyResetDone, this)); |
- return; |
- } |
- |
+ DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
DCHECK(ready_video_frames_.empty()); |
// This needs to happen after the Reset() on vda_ is done to ensure pictures |
@@ -600,11 +693,7 @@ void GpuVideoDecoder::NotifyResetDone() { |
} |
void GpuVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) { |
- if (!gvd_loop_proxy_->BelongsToCurrentThread()) { |
- gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind( |
- &GpuVideoDecoder::NotifyError, this, error)); |
- return; |
- } |
+ DCHECK(gvd_loop_proxy_->BelongsToCurrentThread()); |
if (!vda_.get()) |
return; |