summaryrefslogtreecommitdiffstats
path: root/media
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 /media
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
Diffstat (limited to 'media')
-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
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);
};