summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/common/gpu/gpu_channel.cc8
-rw-r--r--content/common/gpu/gpu_channel.h7
-rw-r--r--content/common/gpu/gpu_command_buffer_stub.cc2
-rw-r--r--content/common/gpu/media/android_video_decode_accelerator.h5
-rw-r--r--content/common/gpu/media/dxva_video_decode_accelerator.h3
-rw-r--r--content/common/gpu/media/exynos_video_decode_accelerator.cc38
-rw-r--r--content/common/gpu/media/exynos_video_decode_accelerator.h17
-rw-r--r--content/common/gpu/media/gpu_video_decode_accelerator.cc78
-rw-r--r--content/common/gpu/media/gpu_video_decode_accelerator.h21
-rw-r--r--content/common/gpu/media/vaapi_video_decode_accelerator.h5
-rw-r--r--content/common/gpu/media/video_decode_accelerator_impl.cc15
-rw-r--r--content/common/gpu/media/video_decode_accelerator_impl.h28
-rw-r--r--content/common/gpu/media/video_decode_accelerator_unittest.cc4
-rw-r--r--content/content_common.gypi2
14 files changed, 191 insertions, 42 deletions
diff --git a/content/common/gpu/gpu_channel.cc b/content/common/gpu/gpu_channel.cc
index 2b920d3..3078887 100644
--- a/content/common/gpu/gpu_channel.cc
+++ b/content/common/gpu/gpu_channel.cc
@@ -985,4 +985,12 @@ void GpuChannel::CacheShader(const std::string& key,
new GpuHostMsg_CacheShader(client_id_, key, shader));
}
+void GpuChannel::AddFilter(IPC::ChannelProxy::MessageFilter* filter) {
+ channel_->AddFilter(filter);
+}
+
+void GpuChannel::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) {
+ channel_->RemoveFilter(filter);
+}
+
} // namespace content
diff --git a/content/common/gpu/gpu_channel.h b/content/common/gpu/gpu_channel.h
index 0692b90..23c27e0 100644
--- a/content/common/gpu/gpu_channel.h
+++ b/content/common/gpu/gpu_channel.h
@@ -86,6 +86,10 @@ class GpuChannel : public IPC::Listener,
base::ProcessId renderer_pid() const { return channel_->peer_pid(); }
+ scoped_refptr<base::MessageLoopProxy> io_message_loop() const {
+ return io_message_loop_;
+ }
+
// IPC::Listener implementation:
virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
virtual void OnChannelError() OVERRIDE;
@@ -156,6 +160,9 @@ class GpuChannel : public IPC::Listener,
void CacheShader(const std::string& key, const std::string& shader);
+ void AddFilter(IPC::ChannelProxy::MessageFilter* filter);
+ void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter);
+
protected:
virtual ~GpuChannel();
diff --git a/content/common/gpu/gpu_command_buffer_stub.cc b/content/common/gpu/gpu_command_buffer_stub.cc
index 78c0a49..cdc4a6b 100644
--- a/content/common/gpu/gpu_command_buffer_stub.cc
+++ b/content/common/gpu/gpu_command_buffer_stub.cc
@@ -722,7 +722,7 @@ void GpuCommandBufferStub::OnCreateVideoDecoder(
int decoder_route_id = channel_->GenerateRouteID();
GpuVideoDecodeAccelerator* decoder =
new GpuVideoDecodeAccelerator(decoder_route_id, this);
- decoder->Initialize(profile, reply_message);
+ decoder->Initialize(profile, reply_message, channel_->io_message_loop());
// decoder is registered as a DestructionObserver of this stub and will
// self-delete during destruction of this stub.
}
diff --git a/content/common/gpu/media/android_video_decode_accelerator.h b/content/common/gpu/media/android_video_decode_accelerator.h
index 3bc60c8..abcb1db 100644
--- a/content/common/gpu/media/android_video_decode_accelerator.h
+++ b/content/common/gpu/media/android_video_decode_accelerator.h
@@ -15,6 +15,7 @@
#include "base/compiler_specific.h"
#include "base/threading/thread_checker.h"
#include "content/common/content_export.h"
+#include "content/common/gpu/media/video_decode_accelerator_impl.h"
#include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "media/base/android/media_codec_bridge.h"
@@ -28,8 +29,8 @@ namespace content {
// A VideoDecodeAccelerator implementation for Android.
// This class decodes the input encoded stream by using Android's MediaCodec
// class. http://developer.android.com/reference/android/media/MediaCodec.html
-class CONTENT_EXPORT AndroidVideoDecodeAccelerator :
- public media::VideoDecodeAccelerator {
+class CONTENT_EXPORT AndroidVideoDecodeAccelerator
+ : public VideoDecodeAcceleratorImpl {
public:
// Does not take ownership of |client| which must outlive |*this|.
AndroidVideoDecodeAccelerator(
diff --git a/content/common/gpu/media/dxva_video_decode_accelerator.h b/content/common/gpu/media/dxva_video_decode_accelerator.h
index 26b1ba7..206099cf 100644
--- a/content/common/gpu/media/dxva_video_decode_accelerator.h
+++ b/content/common/gpu/media/dxva_video_decode_accelerator.h
@@ -17,6 +17,7 @@
#include "base/threading/non_thread_safe.h"
#include "base/win/scoped_comptr.h"
#include "content/common/content_export.h"
+#include "content/common/gpu/media/video_decode_accelerator_impl.h"
#include "media/video/video_decode_accelerator.h"
interface IMFSample;
@@ -29,7 +30,7 @@ namespace content {
// This class lives on a single thread and DCHECKs that it is never accessed
// from any other.
class CONTENT_EXPORT DXVAVideoDecodeAccelerator
- : public media::VideoDecodeAccelerator,
+ : public VideoDecodeAcceleratorImpl,
NON_EXPORTED_BASE(public base::NonThreadSafe) {
public:
enum State {
diff --git a/content/common/gpu/media/exynos_video_decode_accelerator.cc b/content/common/gpu/media/exynos_video_decode_accelerator.cc
index 677df9c..e643969 100644
--- a/content/common/gpu/media/exynos_video_decode_accelerator.cc
+++ b/content/common/gpu/media/exynos_video_decode_accelerator.cc
@@ -205,8 +205,10 @@ ExynosVideoDecodeAccelerator::ExynosVideoDecodeAccelerator(
EGLDisplay egl_display,
EGLContext egl_context,
Client* client,
- const base::Callback<bool(void)>& make_context_current)
+ const base::Callback<bool(void)>& make_context_current,
+ const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy)
: child_message_loop_proxy_(base::MessageLoopProxy::current()),
+ io_message_loop_proxy_(io_message_loop_proxy),
weak_this_(base::AsWeakPtr(this)),
client_ptr_factory_(client),
client_(client_ptr_factory_.GetWeakPtr()),
@@ -415,23 +417,12 @@ void ExynosVideoDecodeAccelerator::Decode(
const media::BitstreamBuffer& bitstream_buffer) {
DVLOG(1) << "Decode(): input_id=" << bitstream_buffer.id()
<< ", size=" << bitstream_buffer.size();
- DCHECK(child_message_loop_proxy_->BelongsToCurrentThread());
-
- scoped_ptr<BitstreamBufferRef> bitstream_record(new BitstreamBufferRef(
- client_, child_message_loop_proxy_,
- new base::SharedMemory(bitstream_buffer.handle(), true),
- bitstream_buffer.size(), bitstream_buffer.id()));
- if (!bitstream_record->shm->Map(bitstream_buffer.size())) {
- DLOG(ERROR) << "Decode(): could not map bitstream_buffer";
- NOTIFY_ERROR(UNREADABLE_INPUT);
- return;
- }
- DVLOG(3) << "Decode(): mapped to addr=" << bitstream_record->shm->memory();
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
// DecodeTask() will take care of running a DecodeBufferTask().
decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
&ExynosVideoDecodeAccelerator::DecodeTask, base::Unretained(this),
- base::Passed(&bitstream_record)));
+ bitstream_buffer));
}
void ExynosVideoDecodeAccelerator::AssignPictureBuffers(
@@ -576,6 +567,8 @@ void ExynosVideoDecodeAccelerator::Destroy() {
delete this;
}
+bool ExynosVideoDecodeAccelerator::CanDecodeOnIOThread() { return true; }
+
// static
void ExynosVideoDecodeAccelerator::PreSandboxInitialization() {
DVLOG(3) << "PreSandboxInitialization()";
@@ -610,12 +603,23 @@ bool ExynosVideoDecodeAccelerator::PostSandboxInitialization() {
}
void ExynosVideoDecodeAccelerator::DecodeTask(
- scoped_ptr<BitstreamBufferRef> bitstream_record) {
- DVLOG(3) << "DecodeTask(): input_id=" << bitstream_record->input_id;
+ const media::BitstreamBuffer& bitstream_buffer) {
+ DVLOG(3) << "DecodeTask(): input_id=" << bitstream_buffer.id();
DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
DCHECK_NE(decoder_state_, kUninitialized);
TRACE_EVENT1("Video Decoder", "EVDA::DecodeTask", "input_id",
- bitstream_record->input_id);
+ bitstream_buffer.id());
+
+ scoped_ptr<BitstreamBufferRef> bitstream_record(new BitstreamBufferRef(
+ client_, child_message_loop_proxy_,
+ new base::SharedMemory(bitstream_buffer.handle(), true),
+ bitstream_buffer.size(), bitstream_buffer.id()));
+ if (!bitstream_record->shm->Map(bitstream_buffer.size())) {
+ DLOG(ERROR) << "Decode(): could not map bitstream_buffer";
+ NOTIFY_ERROR(UNREADABLE_INPUT);
+ return;
+ }
+ DVLOG(3) << "Decode(): mapped to addr=" << bitstream_record->shm->memory();
if (decoder_state_ == kResetting || decoder_flushing_) {
// In the case that we're resetting or flushing, we need to delay decoding
diff --git a/content/common/gpu/media/exynos_video_decode_accelerator.h b/content/common/gpu/media/exynos_video_decode_accelerator.h
index 77471402..09264b9 100644
--- a/content/common/gpu/media/exynos_video_decode_accelerator.h
+++ b/content/common/gpu/media/exynos_video_decode_accelerator.h
@@ -16,8 +16,8 @@
#include "base/memory/scoped_ptr.h"
#include "base/threading/thread.h"
#include "content/common/content_export.h"
+#include "content/common/gpu/media/video_decode_accelerator_impl.h"
#include "media/base/video_decoder_config.h"
-#include "media/video/video_decode_accelerator.h"
#include "ui/gfx/size.h"
#include "ui/gl/gl_bindings.h"
@@ -54,14 +54,15 @@ class H264Parser;
// decoder_thread_, so there are no synchronization issues.
// ... well, there are, but it's a matter of getting messages posted in the
// right order, not fiddling with locks.
-class CONTENT_EXPORT ExynosVideoDecodeAccelerator :
- public media::VideoDecodeAccelerator {
+class CONTENT_EXPORT ExynosVideoDecodeAccelerator
+ : public VideoDecodeAcceleratorImpl {
public:
ExynosVideoDecodeAccelerator(
EGLDisplay egl_display,
EGLContext egl_context,
Client* client,
- const base::Callback<bool(void)>& make_context_current);
+ const base::Callback<bool(void)>& make_context_current,
+ const scoped_refptr<base::MessageLoopProxy>& io_message_loop_proxy);
virtual ~ExynosVideoDecodeAccelerator();
// media::VideoDecodeAccelerator implementation.
@@ -75,6 +76,9 @@ class CONTENT_EXPORT ExynosVideoDecodeAccelerator :
virtual void Reset() OVERRIDE;
virtual void Destroy() OVERRIDE;
+ // VideoDecodeAcceleratorImpl implementation.
+ virtual bool CanDecodeOnIOThread() OVERRIDE;
+
// Do any necessary initialization before the sandbox is enabled.
static void PreSandboxInitialization();
@@ -178,7 +182,7 @@ class CONTENT_EXPORT ExynosVideoDecodeAccelerator :
// Enqueue a BitstreamBuffer to decode. This will enqueue a buffer to the
// decoder_input_queue_, then queue a DecodeBufferTask() to actually decode
// the buffer.
- void DecodeTask(scoped_ptr<BitstreamBufferRef> bitstream_record);
+ void DecodeTask(const media::BitstreamBuffer& bitstream_buffer);
// Decode from the buffers queued in decoder_input_queue_. Calls
// DecodeBufferInitial() or DecodeBufferContinue() as appropriate.
@@ -311,6 +315,9 @@ class CONTENT_EXPORT ExynosVideoDecodeAccelerator :
// Our original calling message loop for the child thread.
scoped_refptr<base::MessageLoopProxy> child_message_loop_proxy_;
+ // Message loop of the IO thread.
+ scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
+
// WeakPtr<> pointing to |this| for use in posting tasks from the decoder or
// device worker threads back to the child thread. Because the worker threads
// are members of this class, any task running on those threads is guaranteed
diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.cc b/content/common/gpu/media/gpu_video_decode_accelerator.cc
index 2b0bb84..a1062e3 100644
--- a/content/common/gpu/media/gpu_video_decode_accelerator.cc
+++ b/content/common/gpu/media/gpu_video_decode_accelerator.cc
@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
#include "base/stl_util.h"
#include "content/common/gpu/gpu_channel.h"
@@ -54,6 +55,36 @@ static bool MakeDecoderContextCurrent(
return true;
}
+class GpuVideoDecodeAccelerator::MessageFilter
+ : public IPC::ChannelProxy::MessageFilter {
+ public:
+ MessageFilter(GpuVideoDecodeAccelerator* owner, int32 host_route_id)
+ : owner_(owner), host_route_id_(host_route_id) {}
+
+ virtual void OnFilterRemoved() OVERRIDE {
+ // This will delete |owner_| and |this|.
+ owner_->OnFilterRemoved();
+ }
+ virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE {
+ if (msg.routing_id() != host_route_id_)
+ return false;
+
+ IPC_BEGIN_MESSAGE_MAP(MessageFilter, msg)
+ IPC_MESSAGE_FORWARD(AcceleratedVideoDecoderMsg_Decode, owner_,
+ GpuVideoDecodeAccelerator::OnDecode)
+ IPC_MESSAGE_UNHANDLED(return false;)
+ IPC_END_MESSAGE_MAP()
+ return true;
+ }
+
+ protected:
+ virtual ~MessageFilter() {}
+
+ private:
+ GpuVideoDecodeAccelerator* owner_;
+ int32 host_route_id_;
+};
+
GpuVideoDecodeAccelerator::GpuVideoDecodeAccelerator(int32 host_route_id,
GpuCommandBufferStub* stub)
: init_done_msg_(NULL),
@@ -63,17 +94,14 @@ GpuVideoDecodeAccelerator::GpuVideoDecodeAccelerator(int32 host_route_id,
DCHECK(stub_);
stub_->AddDestructionObserver(this);
stub_->channel()->AddRoute(host_route_id_, this);
+ child_message_loop_ = base::MessageLoopProxy::current();
make_context_current_ =
base::Bind(&MakeDecoderContextCurrent, stub_->AsWeakPtr());
}
GpuVideoDecodeAccelerator::~GpuVideoDecodeAccelerator() {
- DCHECK(stub_);
if (video_decode_accelerator_)
video_decode_accelerator_.release()->Destroy();
-
- stub_->channel()->RemoveRoute(host_route_id_);
- stub_->RemoveDestructionObserver(this);
}
bool GpuVideoDecodeAccelerator::OnMessageReceived(const IPC::Message& msg) {
@@ -149,7 +177,8 @@ void GpuVideoDecodeAccelerator::NotifyError(
void GpuVideoDecodeAccelerator::Initialize(
const media::VideoCodecProfile profile,
- IPC::Message* init_done_msg) {
+ IPC::Message* init_done_msg,
+ const scoped_refptr<base::MessageLoopProxy>& io_message_loop) {
DCHECK(stub_);
DCHECK(!video_decode_accelerator_.get());
DCHECK(!init_done_msg_);
@@ -179,7 +208,8 @@ void GpuVideoDecodeAccelerator::Initialize(
gfx::GLSurfaceEGL::GetHardwareDisplay(),
stub_->decoder()->GetGLContext()->GetHandle(),
this,
- make_context_current_));
+ make_context_current_,
+ io_message_loop));
#elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY) && defined(USE_X11)
gfx::GLContextGLX* glx_context =
static_cast<gfx::GLContextGLX*>(stub_->decoder()->GetGLContext());
@@ -199,16 +229,31 @@ void GpuVideoDecodeAccelerator::Initialize(
return;
#endif
+ if (video_decode_accelerator_->CanDecodeOnIOThread()) {
+ filter_ = new MessageFilter(this, host_route_id_);
+ stub_->channel()->AddFilter(filter_.get());
+ }
+
if (!video_decode_accelerator_->Initialize(profile))
NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
}
+// Runs on IO thread if video_decode_accelerator_->CanDecodeOnIOThread() is
+// true, otherwise on the main thread.
void GpuVideoDecodeAccelerator::OnDecode(
base::SharedMemoryHandle handle, int32 id, uint32 size) {
DCHECK(video_decode_accelerator_.get());
if (id < 0) {
DLOG(FATAL) << "BitstreamBuffer id " << id << " out of range";
- NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT);
+ if (child_message_loop_->BelongsToCurrentThread()) {
+ NotifyError(media::VideoDecodeAccelerator::INVALID_ARGUMENT);
+ } else {
+ child_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&GpuVideoDecodeAccelerator::NotifyError,
+ base::Unretained(this),
+ media::VideoDecodeAccelerator::INVALID_ARGUMENT));
+ }
return;
}
video_decode_accelerator_->Decode(media::BitstreamBuffer(id, handle, size));
@@ -297,7 +342,20 @@ void GpuVideoDecodeAccelerator::OnReset() {
void GpuVideoDecodeAccelerator::OnDestroy() {
DCHECK(video_decode_accelerator_.get());
- delete this;
+ DCHECK(stub_);
+ stub_->channel()->RemoveRoute(host_route_id_);
+ stub_->RemoveDestructionObserver(this);
+ if (filter_.get()) {
+ // Remove the filter first because the member variables can be accessed on
+ // IO thread. When filter is removed, OnFilterRemoved will delete |this|.
+ stub_->channel()->RemoveFilter(filter_.get());
+ } else {
+ delete this;
+ }
+}
+
+void GpuVideoDecodeAccelerator::OnFilterRemoved() {
+ child_message_loop_->DeleteSoon(FROM_HERE, this);
}
void GpuVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer(
@@ -328,9 +386,7 @@ void GpuVideoDecodeAccelerator::NotifyResetDone() {
DLOG(ERROR) << "Send(AcceleratedVideoDecoderHostMsg_ResetDone) failed";
}
-void GpuVideoDecodeAccelerator::OnWillDestroyStub() {
- delete this;
-}
+void GpuVideoDecodeAccelerator::OnWillDestroyStub() { OnDestroy(); }
bool GpuVideoDecodeAccelerator::Send(IPC::Message* message) {
DCHECK(stub_);
diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.h b/content/common/gpu/media/gpu_video_decode_accelerator.h
index be09302..70d9a44 100644
--- a/content/common/gpu/media/gpu_video_decode_accelerator.h
+++ b/content/common/gpu/media/gpu_video_decode_accelerator.h
@@ -11,10 +11,15 @@
#include "base/memory/ref_counted.h"
#include "base/memory/shared_memory.h"
#include "content/common/gpu/gpu_command_buffer_stub.h"
+#include "content/common/gpu/media/video_decode_accelerator_impl.h"
#include "ipc/ipc_listener.h"
#include "ipc/ipc_sender.h"
#include "media/video/video_decode_accelerator.h"
+namespace base {
+class MessageLoopProxy;
+}
+
namespace content {
class GpuVideoDecodeAccelerator
@@ -55,9 +60,12 @@ class GpuVideoDecodeAccelerator
// The renderer process handle is valid as long as we have a channel between
// GPU process and the renderer.
void Initialize(const media::VideoCodecProfile profile,
- IPC::Message* init_done_msg);
+ IPC::Message* init_done_msg,
+ const scoped_refptr<base::MessageLoopProxy>& io_message_loop);
private:
+ class MessageFilter;
+
// Handlers for IPC messages.
void OnDecode(base::SharedMemoryHandle handle, int32 id, uint32 size);
void OnAssignPictureBuffers(
@@ -70,6 +78,9 @@ class GpuVideoDecodeAccelerator
void OnReset();
void OnDestroy();
+ // Called on IO thread when |filter_| has been removed.
+ void OnFilterRemoved();
+
// Message to Send() when initialization is done. Is only non-NULL during
// initialization and is owned by the IPC channel underlying the
// GpuCommandBufferStub.
@@ -82,7 +93,7 @@ class GpuVideoDecodeAccelerator
GpuCommandBufferStub* stub_;
// The underlying VideoDecodeAccelerator.
- scoped_ptr<media::VideoDecodeAccelerator> video_decode_accelerator_;
+ scoped_ptr<VideoDecodeAcceleratorImpl> video_decode_accelerator_;
// Callback for making the relevant context current for GL calls.
// Returns false if failed.
@@ -91,6 +102,12 @@ class GpuVideoDecodeAccelerator
// The texture target as requested by ProvidePictureBuffers().
uint32 texture_target_;
+ // The message filter to run VDA::Decode on IO thread if VDA supports it.
+ scoped_refptr<MessageFilter> filter_;
+
+ // GPU child message loop.
+ scoped_refptr<base::MessageLoopProxy> child_message_loop_;
+
DISALLOW_IMPLICIT_CONSTRUCTORS(GpuVideoDecodeAccelerator);
};
diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator.h b/content/common/gpu/media/vaapi_video_decode_accelerator.h
index 9ac73da35..1755b98 100644
--- a/content/common/gpu/media/vaapi_video_decode_accelerator.h
+++ b/content/common/gpu/media/vaapi_video_decode_accelerator.h
@@ -25,6 +25,7 @@
#include "content/common/content_export.h"
#include "content/common/gpu/media/vaapi_h264_decoder.h"
#include "content/common/gpu/media/vaapi_wrapper.h"
+#include "content/common/gpu/media/video_decode_accelerator_impl.h"
#include "media/base/bitstream_buffer.h"
#include "media/video/picture.h"
#include "media/video/video_decode_accelerator.h"
@@ -40,8 +41,8 @@ namespace content {
// ChildThread. A few methods on it are called on the decoder thread which is
// stopped during |this->Destroy()|, so any tasks posted to the decoder thread
// can assume |*this| is still alive. See |weak_this_| below for more details.
-class CONTENT_EXPORT VaapiVideoDecodeAccelerator :
- public media::VideoDecodeAccelerator {
+class CONTENT_EXPORT VaapiVideoDecodeAccelerator
+ : public VideoDecodeAcceleratorImpl {
public:
VaapiVideoDecodeAccelerator(
Display* x_display, GLXContext glx_context,
diff --git a/content/common/gpu/media/video_decode_accelerator_impl.cc b/content/common/gpu/media/video_decode_accelerator_impl.cc
new file mode 100644
index 0000000..34f40ef
--- /dev/null
+++ b/content/common/gpu/media/video_decode_accelerator_impl.cc
@@ -0,0 +1,15 @@
+// Copyright 2013 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.
+
+#include "content/common/gpu/media/video_decode_accelerator_impl.h"
+
+namespace content {
+
+VideoDecodeAcceleratorImpl::VideoDecodeAcceleratorImpl() {}
+
+VideoDecodeAcceleratorImpl::~VideoDecodeAcceleratorImpl() {}
+
+bool VideoDecodeAcceleratorImpl::CanDecodeOnIOThread() { return false; }
+
+} // namespace content
diff --git a/content/common/gpu/media/video_decode_accelerator_impl.h b/content/common/gpu/media/video_decode_accelerator_impl.h
new file mode 100644
index 0000000..146a8ef
--- /dev/null
+++ b/content/common/gpu/media/video_decode_accelerator_impl.h
@@ -0,0 +1,28 @@
+// Copyright 2013 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.
+
+#ifndef CONTENT_COMMON_GPU_MEDIA_VIDEO_DECODE_ACCELERATOR_IMPL_H_
+#define CONTENT_COMMON_GPU_MEDIA_VIDEO_DECODE_ACCELERATOR_IMPL_H_
+
+#include "content/common/content_export.h"
+#include "media/video/video_decode_accelerator.h"
+
+namespace content {
+
+class CONTENT_EXPORT VideoDecodeAcceleratorImpl
+ : public media::VideoDecodeAccelerator {
+ public:
+ VideoDecodeAcceleratorImpl();
+ virtual ~VideoDecodeAcceleratorImpl();
+
+ // Returns true if media::VideoDecodeAccelerator::Decode can run on the IO
+ // thread. Otherwise Decode will run on the GPU child thread. The purpose of
+ // running Decode on the IO thread is to reduce decode latency. Note Decode
+ // should return as soon as possible and not block on the IO thread.
+ virtual bool CanDecodeOnIOThread();
+};
+
+} // namespace content
+
+#endif // CONTENT_COMMON_GPU_MEDIA_VIDEO_DECODE_ACCELERATOR_IMPL_H_
diff --git a/content/common/gpu/media/video_decode_accelerator_unittest.cc b/content/common/gpu/media/video_decode_accelerator_unittest.cc
index a180ff6..5b3e114 100644
--- a/content/common/gpu/media/video_decode_accelerator_unittest.cc
+++ b/content/common/gpu/media/video_decode_accelerator_unittest.cc
@@ -32,6 +32,7 @@
#include "base/file_util.h"
#include "base/format_macros.h"
#include "base/md5.h"
+#include "base/message_loop/message_loop_proxy.h"
#include "base/platform_file.h"
#include "base/process/process.h"
#include "base/stl_util.h"
@@ -616,7 +617,8 @@ void GLRenderingVDAClient::CreateDecoder() {
static_cast<EGLDisplay>(rendering_helper_->GetGLDisplay()),
static_cast<EGLContext>(rendering_helper_->GetGLContext()),
client,
- base::Bind(&DoNothingReturnTrue)));
+ base::Bind(&DoNothingReturnTrue),
+ base::MessageLoopProxy::current()));
#elif defined(ARCH_CPU_X86_FAMILY)
decoder_.reset(new VaapiVideoDecodeAccelerator(
static_cast<Display*>(rendering_helper_->GetGLDisplay()),
diff --git a/content/content_common.gypi b/content/content_common.gypi
index 3b04b88..31b2950 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -238,6 +238,8 @@
'common/gpu/media/gpu_video_decode_accelerator.h',
'common/gpu/media/gpu_video_encode_accelerator.cc',
'common/gpu/media/gpu_video_encode_accelerator.h',
+ 'common/gpu/media/video_decode_accelerator_impl.cc',
+ 'common/gpu/media/video_decode_accelerator_impl.h',
'common/gpu/sync_point_manager.h',
'common/gpu/sync_point_manager.cc',
'common/gpu/texture_image_transport_surface.h',