summaryrefslogtreecommitdiffstats
path: root/chrome/renderer
diff options
context:
space:
mode:
authorjiesun@google.com <jiesun@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-09 15:59:44 +0000
committerjiesun@google.com <jiesun@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-09 15:59:44 +0000
commitc9f28a7d16b71b17185b456e3b53e4e638d005d0 (patch)
tree5940cfb4244935b01608e286f84a83413e0329a1 /chrome/renderer
parent6b1b2b6bc21270ef1b90a28293a1cb743e933383 (diff)
downloadchromium_src-c9f28a7d16b71b17185b456e3b53e4e638d005d0.zip
chromium_src-c9f28a7d16b71b17185b456e3b53e4e638d005d0.tar.gz
chromium_src-c9f28a7d16b71b17185b456e3b53e4e638d005d0.tar.bz2
Special thanks for in-ming cheng's MFT hardware decodering code.
1. ipc_video_decoder.cc/h is media pipeline filter which use the gpu decoder facilities in video stack. it is only enabled when (a) hardware composition is on (b) hardware decoding command line is on (c) h264 codec is specified. 2. gpu_video_service.cc/h is a singleton in gpu process which provide video services for renderer process, through it we could create decoder. ( in my imagination, in the future, we could create encoder or capturer too) 3. gpu_video_decoder.cc/h. abstract interface for hardware decoder. 4. gpu_video_decoder_mft.cc/h media foundation transform hardware decoder which run on windows 7 only. 5. gpu_video_service_host.cc/h is singleton in renderer process which provide proxy for gpu_video_service. 6. gpu_video_decoder_host.cc/h is proxy for gpu_video_decoder. (1 to 1 map).basically there is one global GpuVideoService in GPU process, one GpuVideoServiceHost in Renderer process. for each renderer process, there are could be multiple renderer view, each could had multiple GpuVideoDecoderHost the connect to GpuVideoDeocder through GPUCHannelHOst/GpuChannel. 7. gpu_video_common.cc/h: IPC message definition and pickle/marshaling support. ISSUES: 1. in media pipeline, we need let decoder to determine if bit stream filter should be used instead of let command line to determine it. 2. stop readback from D3D surface use ANGLE. 3. Flush logic still need fine tuning. 4. CreateThread in GpuVideoDecoder, and post message in message handler, and derived classs handle message loop. ? 5. Error handling. 6. Input ring buffer implementation. Current impl is naive. 7.Add output queue for MFT decoder. 8. Query Capabilities at GetVideoServices()... BUG=None TEST=Windows7 Review URL: http://codereview.chromium.org/2873089 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@55405 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r--chrome/renderer/DEPS1
-rw-r--r--chrome/renderer/gpu_channel_host.cc4
-rw-r--r--chrome/renderer/gpu_channel_host.h1
-rw-r--r--chrome/renderer/gpu_video_decoder_host.cc199
-rw-r--r--chrome/renderer/gpu_video_decoder_host.h126
-rw-r--r--chrome/renderer/gpu_video_service_host.cc91
-rw-r--r--chrome/renderer/gpu_video_service_host.h51
-rw-r--r--chrome/renderer/media/ipc_video_decoder.cc344
-rw-r--r--chrome/renderer/media/ipc_video_decoder.h100
-rw-r--r--chrome/renderer/render_thread.cc3
-rw-r--r--chrome/renderer/render_view.cc9
11 files changed, 928 insertions, 1 deletions
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index b05975c..dc83927 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -4,6 +4,7 @@ include_rules = [
"+grit", # For generated headers
"+media/audio",
"+media/base",
+ "+media/ffmpeg",
"+media/filters",
"+sandbox/src",
"+skia/include",
diff --git a/chrome/renderer/gpu_channel_host.cc b/chrome/renderer/gpu_channel_host.cc
index 70a5224..bb98148 100644
--- a/chrome/renderer/gpu_channel_host.cc
+++ b/chrome/renderer/gpu_channel_host.cc
@@ -7,6 +7,7 @@
#include "chrome/common/child_process.h"
#include "chrome/common/gpu_messages.h"
#include "chrome/renderer/command_buffer_proxy.h"
+#include "chrome/renderer/gpu_video_service_host.h"
GpuChannelHost::GpuChannelHost() : state_(UNCONNECTED) {
}
@@ -35,6 +36,9 @@ void GpuChannelHost::OnMessageReceived(const IPC::Message& message) {
}
void GpuChannelHost::OnChannelConnected(int32 peer_pid) {
+ GpuVideoServiceHost::get()->OnGpuChannelConnected(this,
+ &router_,
+ channel_.get());
}
void GpuChannelHost::OnChannelError() {
diff --git a/chrome/renderer/gpu_channel_host.h b/chrome/renderer/gpu_channel_host.h
index 6c33acb..e35d40d 100644
--- a/chrome/renderer/gpu_channel_host.h
+++ b/chrome/renderer/gpu_channel_host.h
@@ -61,7 +61,6 @@ class GpuChannelHost : public IPC::Channel::Listener,
const gfx::Size& size,
uint32 parent_texture_id);
-
// Destroy a command buffer created by this channel.
void DestroyCommandBuffer(CommandBufferProxy* command_buffer);
diff --git a/chrome/renderer/gpu_video_decoder_host.cc b/chrome/renderer/gpu_video_decoder_host.cc
new file mode 100644
index 0000000..5e0f727
--- /dev/null
+++ b/chrome/renderer/gpu_video_decoder_host.cc
@@ -0,0 +1,199 @@
+// Copyright (c) 2010 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 "chrome/renderer/gpu_video_decoder_host.h"
+
+#include "chrome/common/gpu_messages.h"
+#include "chrome/renderer/gpu_video_service_host.h"
+#include "chrome/renderer/render_thread.h"
+
+void GpuVideoDecoderHost::OnChannelError() {
+ channel_host_.release();
+}
+
+void GpuVideoDecoderHost::OnMessageReceived(const IPC::Message& msg) {
+ IPC_BEGIN_MESSAGE_MAP(GpuVideoDecoderHost, msg)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_InitializeACK,
+ OnInitializeDone)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_DestroyACK,
+ OnUninitializeDone)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_FlushACK,
+ OnFlushDone)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_EmptyThisBufferACK,
+ OnEmptyThisBufferACK)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_EmptyThisBufferDone,
+ OnEmptyThisBufferDone)
+ IPC_MESSAGE_HANDLER(GpuVideoDecoderHostMsg_FillThisBufferDone,
+ OnFillThisBufferDone)
+ IPC_MESSAGE_UNHANDLED_ERROR()
+ IPC_END_MESSAGE_MAP()
+}
+
+bool GpuVideoDecoderHost::Initialize(const GpuVideoDecoderInitParam& param) {
+ DCHECK_EQ(state_, kStateUninitialized);
+
+ init_param_ = param;
+ if (!channel_host_->Send(
+ new GpuVideoDecoderMsg_Initialize(route_id(), param))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_Initialize failed";
+ return false;
+ }
+ return true;
+}
+
+bool GpuVideoDecoderHost::Uninitialize() {
+ if (!channel_host_->Send(new GpuVideoDecoderMsg_Destroy(route_id()))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_Destroy failed";
+ return false;
+ }
+ return true;
+}
+
+void GpuVideoDecoderHost::EmptyThisBuffer(scoped_refptr<Buffer> buffer) {
+ DCHECK_NE(state_, kStateUninitialized);
+ DCHECK_NE(state_, kStateFlushing);
+
+ // We never own input buffers, therefore when client in flush state, it
+ // never call us with EmptyThisBuffer.
+ if (state_ != kStateNormal)
+ return;
+
+ input_buffer_queue_.push_back(buffer);
+ SendInputBufferToGpu();
+}
+
+void GpuVideoDecoderHost::FillThisBuffer(scoped_refptr<VideoFrame> frame) {
+ DCHECK_NE(state_, kStateUninitialized);
+
+ // Depends on who provides buffer. client could return buffer to
+ // us while flushing.
+ if (state_ == kStateError)
+ return;
+
+ GpuVideoDecoderOutputBufferParam param;
+ if (!channel_host_->Send(
+ new GpuVideoDecoderMsg_FillThisBuffer(route_id(), param))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_FillThisBuffer failed";
+ }
+}
+
+bool GpuVideoDecoderHost::Flush() {
+ state_ = kStateFlushing;
+ if (!channel_host_->Send(new GpuVideoDecoderMsg_Flush(route_id()))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_Flush failed";
+ return false;
+ }
+ input_buffer_queue_.clear();
+ // TODO(jiesun): because GpuVideoDeocder/GpuVideoDecoder are asynchronously.
+ // We need a way to make flush logic more clear. but I think ring buffer
+ // should make the busy flag obsolete, therefore I will leave it for now.
+ input_buffer_busy_ = false;
+ return true;
+}
+
+void GpuVideoDecoderHost::OnInitializeDone(
+ const GpuVideoDecoderInitDoneParam& param) {
+ done_param_ = param;
+ bool success = false;
+
+ do {
+ if (!param.success_)
+ break;
+
+ if (!base::SharedMemory::IsHandleValid(param.input_buffer_handle_))
+ break;
+ input_transfer_buffer_.reset(
+ new base::SharedMemory(param.input_buffer_handle_, false));
+ if (!input_transfer_buffer_->Map(param.input_buffer_size_))
+ break;
+
+ if (!base::SharedMemory::IsHandleValid(param.output_buffer_handle_))
+ break;
+ output_transfer_buffer_.reset(
+ new base::SharedMemory(param.output_buffer_handle_, false));
+ if (!output_transfer_buffer_->Map(param.output_buffer_size_))
+ break;
+
+ success = true;
+ } while (0);
+
+ state_ = success ? kStateNormal : kStateError;
+ event_handler_->OnInitializeDone(success, param);
+}
+
+void GpuVideoDecoderHost::OnUninitializeDone() {
+ input_transfer_buffer_.reset();
+ output_transfer_buffer_.reset();
+
+ event_handler_->OnUninitializeDone();
+}
+
+void GpuVideoDecoderHost::OnFlushDone() {
+ state_ = kStateNormal;
+ event_handler_->OnFlushDone();
+}
+
+void GpuVideoDecoderHost::OnEmptyThisBufferDone() {
+ scoped_refptr<Buffer> buffer;
+ event_handler_->OnEmptyBufferDone(buffer);
+}
+
+void GpuVideoDecoderHost::OnFillThisBufferDone(
+ const GpuVideoDecoderOutputBufferParam& param) {
+ scoped_refptr<VideoFrame> frame;
+
+ if (param.flags_ & GpuVideoDecoderOutputBufferParam::kFlagsEndOfStream) {
+ VideoFrame::CreateEmptyFrame(&frame);
+ } else {
+ VideoFrame::CreateFrame(VideoFrame::YV12,
+ init_param_.width_,
+ init_param_.height_,
+ base::TimeDelta::FromMicroseconds(param.timestamp_),
+ base::TimeDelta::FromMicroseconds(param.duration_),
+ &frame);
+
+ uint8* src = static_cast<uint8*>(output_transfer_buffer_->memory());
+ uint8* data0 = frame->data(0);
+ uint8* data1 = frame->data(1);
+ uint8* data2 = frame->data(2);
+ int32 size = init_param_.width_ * init_param_.height_;
+ memcpy(data0, src, size);
+ memcpy(data1, src + size, size / 4);
+ memcpy(data2, src + size + size / 4, size / 4);
+ }
+
+ event_handler_->OnFillBufferDone(frame);
+ if (!channel_host_->Send(
+ new GpuVideoDecoderMsg_FillThisBufferDoneACK(route_id()))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_FillThisBufferDoneACK failed";
+ }
+}
+
+void GpuVideoDecoderHost::OnEmptyThisBufferACK() {
+ input_buffer_busy_ = false;
+ SendInputBufferToGpu();
+}
+
+void GpuVideoDecoderHost::SendInputBufferToGpu() {
+ if (input_buffer_busy_) return;
+ if (input_buffer_queue_.empty()) return;
+
+ input_buffer_busy_ = true;
+
+ scoped_refptr<Buffer> buffer;
+ buffer = input_buffer_queue_.front();
+ input_buffer_queue_.pop_front();
+
+ // Send input data to GPU process.
+ GpuVideoDecoderInputBufferParam param;
+ param.offset_ = 0;
+ param.size_ = buffer->GetDataSize();
+ param.timestamp_ = buffer->GetTimestamp().InMicroseconds();
+ memcpy(input_transfer_buffer_->memory(), buffer->GetData(), param.size_);
+ if (!channel_host_->Send(
+ new GpuVideoDecoderMsg_EmptyThisBuffer(route_id(), param))) {
+ LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBuffer failed";
+ }
+}
+
diff --git a/chrome/renderer/gpu_video_decoder_host.h b/chrome/renderer/gpu_video_decoder_host.h
new file mode 100644
index 0000000..2bd2f5b
--- /dev/null
+++ b/chrome/renderer/gpu_video_decoder_host.h
@@ -0,0 +1,126 @@
+// Copyright (c) 2010 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 CHROME_RENDERER_GPU_VIDEO_DECODER_HOST_H_
+#define CHROME_RENDERER_GPU_VIDEO_DECODER_HOST_H_
+
+#include <deque>
+
+#include "base/singleton.h"
+#include "chrome/common/gpu_video_common.h"
+#include "chrome/renderer/gpu_channel_host.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "media/base/buffers.h"
+#include "media/base/video_frame.h"
+
+using media::VideoFrame;
+using media::Buffer;
+
+class GpuVideoServiceHost;
+
+class GpuVideoDecoderHost
+ : public base::RefCountedThreadSafe<GpuVideoDecoderHost>,
+ public IPC::Channel::Listener {
+ public:
+ class EventHandler {
+ public:
+ virtual void OnInitializeDone(
+ bool success,
+ const GpuVideoDecoderInitDoneParam& param) = 0;
+ virtual void OnUninitializeDone() = 0;
+ virtual void OnFlushDone() = 0;
+ virtual void OnEmptyBufferDone(scoped_refptr<Buffer> buffer) = 0;
+ virtual void OnFillBufferDone(scoped_refptr<VideoFrame> frame) = 0;
+ virtual void OnDeviceError() = 0;
+ };
+
+ typedef enum {
+ kStateUninitialized,
+ kStateNormal,
+ kStateError,
+ kStateFlushing,
+ } GpuVideoDecoderHostState;
+
+ // IPC::Channel::Listener.
+ virtual void OnChannelConnected(int32 peer_pid) {}
+ virtual void OnChannelError();
+ virtual void OnMessageReceived(const IPC::Message& message);
+
+ bool Initialize(const GpuVideoDecoderInitParam& param);
+ bool Uninitialize();
+ void EmptyThisBuffer(scoped_refptr<Buffer> buffer);
+ void FillThisBuffer(scoped_refptr<VideoFrame> frame);
+ bool Flush();
+
+ int32 decoder_id() { return decoder_info_.decoder_id_; }
+ int32 route_id() { return decoder_info_.decoder_route_id_; }
+ int32 my_route_id() { return decoder_info_.decoder_host_route_id_; }
+
+ virtual ~GpuVideoDecoderHost() {}
+
+ private:
+ GpuVideoDecoderHost(GpuVideoServiceHost* service_host,
+ GpuChannelHost* channel_host,
+ EventHandler* event_handler,
+ GpuVideoDecoderInfoParam decoder_info)
+ : gpu_video_service_host_(service_host),
+ channel_host_(channel_host),
+ event_handler_(event_handler),
+ decoder_info_(decoder_info),
+ buffer_id_serial_(0),
+ state_(kStateUninitialized),
+ input_buffer_busy_(false) {}
+ friend class GpuVideoServiceHost;
+
+ // Input message handler.
+ void OnInitializeDone(const GpuVideoDecoderInitDoneParam& param);
+ void OnUninitializeDone();
+ void OnFlushDone();
+ void OnEmptyThisBufferDone();
+ void OnFillThisBufferDone(const GpuVideoDecoderOutputBufferParam& param);
+ void OnEmptyThisBufferACK();
+
+ // Helper function.
+ void SendInputBufferToGpu();
+
+ // We expect that GpuVideoServiceHost's always available during our life span.
+ GpuVideoServiceHost* gpu_video_service_host_;
+
+ scoped_refptr<GpuChannelHost> channel_host_;
+
+ // We expect that the client of us will always available during our life span.
+ EventHandler* event_handler_;
+
+ // Globally identify this decoder in the GPU process.
+ GpuVideoDecoderInfoParam decoder_info_;
+
+ // Input buffer id serial number generator.
+ int32 buffer_id_serial_;
+
+ // Hold information about GpuVideoDecoder configuration.
+ GpuVideoDecoderInitParam init_param_;
+
+ // Hold information about output surface format, etc.
+ GpuVideoDecoderInitDoneParam done_param_;
+
+ // Current state of video decoder.
+ GpuVideoDecoderHostState state_;
+
+ // We are not able to push all received buffer to gpu process at once.
+ std::deque<scoped_refptr<Buffer> > input_buffer_queue_;
+
+ // Currently we do not use ring buffer in input buffer, therefore before
+ // GPU process had finished access it, we should not touch it.
+ bool input_buffer_busy_;
+
+ // Transfer buffers for both input and output.
+ // TODO(jiesun): remove output buffer when hardware composition is ready.
+ scoped_ptr<base::SharedMemory> input_transfer_buffer_;
+ scoped_ptr<base::SharedMemory> output_transfer_buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuVideoDecoderHost);
+};
+
+#endif // CHROME_RENDERER_GPU_VIDEO_DECODER_HOST_H_
+
diff --git a/chrome/renderer/gpu_video_service_host.cc b/chrome/renderer/gpu_video_service_host.cc
new file mode 100644
index 0000000..de5e9a0
--- /dev/null
+++ b/chrome/renderer/gpu_video_service_host.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2010 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 "chrome/renderer/gpu_video_service_host.h"
+
+#include "chrome/common/gpu_messages.h"
+#include "chrome/renderer/gpu_video_decoder_host.h"
+#include "chrome/renderer/render_thread.h"
+
+void GpuVideoServiceHost::OnChannelError() {
+ LOG(ERROR) << "GpuVideoServiceHost::OnChannelError";
+ channel_host_.release();
+ router_ = NULL;
+}
+
+void GpuVideoServiceHost::OnMessageReceived(const IPC::Message& msg) {
+#if 0
+ IPC_BEGIN_MESSAGE_MAP(GpuVideoServiceHost, msg)
+ IPC_MESSAGE_UNHANDLED_ERROR()
+ IPC_END_MESSAGE_MAP()
+#endif
+}
+
+scoped_refptr<GpuVideoDecoderHost> GpuVideoServiceHost::CreateVideoDecoder(
+ GpuVideoDecoderHost::EventHandler* event_handler) {
+ DCHECK(RenderThread::current());
+
+ if (!channel_host_.get() || !service_info_.service_available_)
+ return NULL;
+
+ GpuVideoDecoderInfoParam param;
+ if (!channel_host_->Send(new GpuChannelMsg_CreateVideoDecoder(&param))) {
+ LOG(ERROR) << "GpuChannelMsg_CreateVideoDecoder failed";
+ return NULL;
+ }
+
+ scoped_refptr<GpuVideoDecoderHost> gpu_video_decoder_host =
+ new GpuVideoDecoderHost(this, channel_host_, event_handler, param);
+ if (!gpu_video_decoder_host.get()) {
+ if (!channel_host_->Send(
+ new GpuChannelMsg_DestroyVideoDecoder(param.decoder_id_))) {
+ LOG(ERROR) << "GpuChannelMsg_DestroyVideoDecoder failed";
+ }
+ return NULL;
+ }
+
+ router_->AddRoute(gpu_video_decoder_host->my_route_id(),
+ gpu_video_decoder_host.get());
+ return gpu_video_decoder_host;
+}
+
+void GpuVideoServiceHost::DestroyVideoDecoder(
+ scoped_refptr<GpuVideoDecoderHost> gpu_video_decoder_host) {
+ DCHECK(RenderThread::current());
+
+ if (!channel_host_.get() || !service_info_.service_available_)
+ return;
+
+ DCHECK(gpu_video_decoder_host.get());
+
+ int32 decoder_id = gpu_video_decoder_host->decoder_id();
+ if (!channel_host_->Send(new GpuChannelMsg_DestroyVideoDecoder(decoder_id))) {
+ LOG(ERROR) << "GpuChannelMsg_DestroyVideoDecoder failed";
+ }
+
+ router_->RemoveRoute(gpu_video_decoder_host->my_route_id());
+}
+
+void GpuVideoServiceHost::OnRendererThreadInit(MessageLoop* message_loop) {
+ message_loop_ = message_loop;
+}
+
+void GpuVideoServiceHost::OnGpuChannelConnected(
+ GpuChannelHost* channel_host,
+ MessageRouter* router,
+ IPC::SyncChannel* channel) {
+
+ channel_host_ = channel_host;
+ router_ = router;
+
+ // Get the routing_id of video service in GPU process.
+ service_info_.service_available_ = 0;
+ if (!channel_host_->Send(new GpuChannelMsg_GetVideoService(&service_info_))) {
+ LOG(ERROR) << "GpuChannelMsg_GetVideoService failed";
+ }
+
+ if (service_info_.service_available_)
+ router->AddRoute(service_info_.video_service_host_route_id_, this);
+}
+
diff --git a/chrome/renderer/gpu_video_service_host.h b/chrome/renderer/gpu_video_service_host.h
new file mode 100644
index 0000000..7b9fb65
--- /dev/null
+++ b/chrome/renderer/gpu_video_service_host.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2010 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 CHROME_RENDERER_GPU_VIDEO_SERVICE_HOST_H_
+#define CHROME_RENDERER_GPU_VIDEO_SERVICE_HOST_H_
+
+#include <map>
+
+#include "base/singleton.h"
+#include "chrome/common/gpu_video_common.h"
+#include "chrome/renderer/gpu_channel_host.h"
+#include "chrome/renderer/gpu_video_decoder_host.h"
+#include "ipc/ipc_channel.h"
+#include "media/base/buffers.h"
+#include "media/base/video_frame.h"
+
+class GpuVideoServiceHost : public IPC::Channel::Listener,
+ public Singleton<GpuVideoServiceHost> {
+ public:
+ // IPC::Channel::Listener.
+ virtual void OnChannelConnected(int32 peer_pid) {}
+ virtual void OnChannelError();
+ virtual void OnMessageReceived(const IPC::Message& message);
+
+ void OnRendererThreadInit(MessageLoop* message_loop);
+ void OnGpuChannelConnected(GpuChannelHost* channel_host,
+ MessageRouter* router,
+ IPC::SyncChannel* channel);
+
+ // call at RenderThread. one per renderer process.
+ scoped_refptr<GpuVideoDecoderHost> CreateVideoDecoder(
+ GpuVideoDecoderHost::EventHandler* event_handler);
+ void DestroyVideoDecoder(scoped_refptr<GpuVideoDecoderHost>);
+
+ private:
+ GpuVideoServiceHost() : message_loop_(NULL) {
+ service_info_.service_available_ = 0;
+ }
+
+ scoped_refptr<GpuChannelHost> channel_host_;
+ MessageRouter* router_;
+ GpuVideoServiceInfoParam service_info_;
+ MessageLoop* message_loop_; // Message loop of render thread.
+
+ friend struct DefaultSingletonTraits<GpuVideoServiceHost>;
+ DISALLOW_COPY_AND_ASSIGN(GpuVideoServiceHost);
+};
+
+#endif // CHROME_RENDERER_GPU_VIDEO_SERVICE_HOST_H_
+
diff --git a/chrome/renderer/media/ipc_video_decoder.cc b/chrome/renderer/media/ipc_video_decoder.cc
new file mode 100644
index 0000000..9d47260
--- /dev/null
+++ b/chrome/renderer/media/ipc_video_decoder.cc
@@ -0,0 +1,344 @@
+// Copyright (c) 2010 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 "chrome/renderer/media/ipc_video_decoder.h"
+
+#include "base/task.h"
+#include "media/base/callback.h"
+#include "media/base/filters.h"
+#include "media/base/filter_host.h"
+#include "media/base/limits.h"
+#include "media/base/media_format.h"
+#include "media/base/video_frame.h"
+#include "media/ffmpeg/ffmpeg_common.h"
+#include "media/ffmpeg/ffmpeg_util.h"
+#include "media/filters/ffmpeg_interfaces.h"
+
+namespace media {
+
+IpcVideoDecoder::IpcVideoDecoder(MessageLoop* message_loop)
+ : width_(0),
+ height_(0),
+ state_(kUnInitialized),
+ pending_reads_(0),
+ pending_requests_(0),
+ renderer_thread_message_loop_(message_loop) {
+}
+
+IpcVideoDecoder::~IpcVideoDecoder() {
+}
+
+void IpcVideoDecoder::Initialize(DemuxerStream* demuxer_stream,
+ FilterCallback* callback) {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::Initialize,
+ demuxer_stream,
+ callback));
+ return;
+ }
+
+ CHECK(!demuxer_stream_);
+ demuxer_stream_ = demuxer_stream;
+ initialize_callback_.reset(callback);
+
+ // Get the AVStream by querying for the provider interface.
+ AVStreamProvider* av_stream_provider;
+ if (!demuxer_stream->QueryInterface(&av_stream_provider)) {
+ GpuVideoDecoderInitDoneParam param;
+ OnInitializeDone(false, param);
+ return;
+ }
+
+ AVStream* av_stream = av_stream_provider->GetAVStream();
+ width_ = av_stream->codec->width;
+ height_ = av_stream->codec->height;
+
+ // Create hardware decoder instance.
+ GpuVideoServiceHost* gpu_video_service_host = GpuVideoServiceHost::get();
+ gpu_video_decoder_host_ = gpu_video_service_host->CreateVideoDecoder(this);
+
+ // Initialize hardware decoder.
+ GpuVideoDecoderInitParam param;
+ param.width_ = width_;
+ param.height_ = height_;
+ if (!gpu_video_decoder_host_->Initialize(param)) {
+ GpuVideoDecoderInitDoneParam param;
+ OnInitializeDone(false, param);
+ }
+}
+
+void IpcVideoDecoder::OnInitializeDone(
+ bool success, const GpuVideoDecoderInitDoneParam& param) {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::OnInitializeDone,
+ success,
+ param));
+ return;
+ }
+
+ AutoCallbackRunner done_runner(initialize_callback_.release());
+
+ if (success) {
+ media_format_.SetAsString(MediaFormat::kMimeType,
+ mime_type::kUncompressedVideo);
+ media_format_.SetAsInteger(MediaFormat::kWidth, width_);
+ media_format_.SetAsInteger(MediaFormat::kHeight, height_);
+ media_format_.SetAsInteger(MediaFormat::kSurfaceType,
+ static_cast<int>(param.surface_type_));
+ media_format_.SetAsInteger(MediaFormat::kSurfaceFormat,
+ static_cast<int>(param.format_));
+ state_ = kPlaying;
+ } else {
+ LOG(ERROR) << "IpcVideoDecoder initialization failed!";
+ host()->SetError(PIPELINE_ERROR_DECODE);
+ }
+}
+
+void IpcVideoDecoder::Stop(FilterCallback* callback) {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::Stop,
+ callback));
+ return;
+ }
+
+ stop_callback_.reset(callback);
+ if (!gpu_video_decoder_host_->Uninitialize()) {
+ LOG(ERROR) << "gpu video decoder destroy failed";
+ IpcVideoDecoder::OnUninitializeDone();
+ }
+}
+
+void IpcVideoDecoder::OnUninitializeDone() {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::OnUninitializeDone));
+ return;
+ }
+
+ AutoCallbackRunner done_runner(stop_callback_.release());
+
+ state_ = kStopped;
+}
+
+void IpcVideoDecoder::Pause(FilterCallback* callback) {
+ Flush(callback); // TODO(jiesun): move this to flush().
+}
+
+void IpcVideoDecoder::Flush(FilterCallback* callback) {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::Flush,
+ callback));
+ return;
+ }
+
+ state_ = kFlushing;
+
+ flush_callback_.reset(callback);
+
+ if (!gpu_video_decoder_host_->Flush()) {
+ LOG(ERROR) << "gpu video decoder flush failed";
+ OnFlushDone();
+ }
+}
+
+void IpcVideoDecoder::OnFlushDone() {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::OnFlushDone));
+ return;
+ }
+
+ if (pending_reads_ == 0 && pending_requests_ == 0 && flush_callback_.get()) {
+ flush_callback_->Run();
+ flush_callback_.reset();
+ }
+}
+
+void IpcVideoDecoder::Seek(base::TimeDelta time, FilterCallback* callback) {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::Seek,
+ time,
+ callback));
+ return;
+ }
+
+ OnSeekComplete(callback);
+}
+
+void IpcVideoDecoder::OnSeekComplete(FilterCallback* callback) {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::OnSeekComplete,
+ callback));
+ return;
+ }
+
+ AutoCallbackRunner done_runner(callback);
+
+ state_ = kPlaying;
+
+ for (int i = 0; i < 20; ++i) {
+ demuxer_stream_->Read(
+ NewCallback(this,
+ &IpcVideoDecoder::OnReadComplete));
+ ++pending_reads_;
+ }
+}
+
+void IpcVideoDecoder::OnReadComplete(Buffer* buffer) {
+ scoped_refptr<Buffer> buffer_ref = buffer;
+ ReadCompleteTask(buffer_ref);
+}
+
+void IpcVideoDecoder::ReadCompleteTask(scoped_refptr<Buffer> buffer) {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::ReadCompleteTask,
+ buffer));
+ return;
+ }
+
+ DCHECK_GT(pending_reads_, 0u);
+ --pending_reads_;
+
+ if (state_ == kStopped || state_ == kEnded) {
+ // Just discard the input buffers
+ return;
+ }
+
+ if (state_ == kFlushing) {
+ if (pending_reads_ == 0 && pending_requests_ == 0) {
+ CHECK(flush_callback_.get());
+ flush_callback_->Run();
+ flush_callback_.reset();
+ state_ = kPlaying;
+ }
+ return;
+ }
+ // Transition to kFlushCodec on the first end of input stream buffer.
+ if (state_ == kPlaying && buffer->IsEndOfStream()) {
+ state_ = kFlushCodec;
+ }
+
+ gpu_video_decoder_host_->EmptyThisBuffer(buffer);
+}
+
+void IpcVideoDecoder::FillThisBuffer(scoped_refptr<VideoFrame> video_frame) {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::FillThisBuffer,
+ video_frame));
+ return;
+ }
+
+ // Synchronized flushing before stop should prevent this.
+ CHECK_NE(state_, kStopped);
+
+ // Notify decode engine the available of new frame.
+ ++pending_requests_;
+ gpu_video_decoder_host_->FillThisBuffer(video_frame);
+}
+
+void IpcVideoDecoder::OnFillBufferDone(scoped_refptr<VideoFrame> video_frame) {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::OnFillBufferDone,
+ video_frame));
+ return;
+ }
+
+ if (video_frame.get()) {
+ --pending_requests_;
+ fill_buffer_done_callback()->Run(video_frame);
+ if (state_ == kFlushing && pending_reads_ == 0 && pending_requests_ == 0) {
+ CHECK(flush_callback_.get());
+ flush_callback_->Run();
+ flush_callback_.reset();
+ state_ = kPlaying;
+ }
+
+ } else {
+ if (state_ == kFlushCodec) {
+ // When in kFlushCodec, any errored decode, or a 0-lengthed frame,
+ // is taken as a signal to stop decoding.
+ state_ = kEnded;
+ scoped_refptr<VideoFrame> video_frame;
+ VideoFrame::CreateEmptyFrame(&video_frame);
+ fill_buffer_done_callback()->Run(video_frame);
+ }
+ }
+}
+
+void IpcVideoDecoder::OnEmptyBufferDone(scoped_refptr<Buffer> buffer) {
+ if (MessageLoop::current() != renderer_thread_message_loop_) {
+ renderer_thread_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &IpcVideoDecoder::OnEmptyBufferDone,
+ buffer));
+ return;
+ }
+
+ // TODO(jiesun): We haven't recycle input buffer yet.
+ demuxer_stream_->Read(NewCallback(this, &IpcVideoDecoder::OnReadComplete));
+ ++pending_reads_;
+}
+
+void IpcVideoDecoder::OnDeviceError() {
+ host()->SetError(PIPELINE_ERROR_DECODE);
+}
+
+bool IpcVideoDecoder::ProvidesBuffer() {
+ return true;
+}
+
+// static
+FilterFactory* IpcVideoDecoder::CreateFactory(MessageLoop* message_loop) {
+ return new FilterFactoryImpl1<IpcVideoDecoder, MessageLoop*>(message_loop);
+}
+
+// static
+bool IpcVideoDecoder::IsMediaFormatSupported(const MediaFormat& format) {
+ std::string mime_type;
+ if (!format.GetAsString(MediaFormat::kMimeType, &mime_type) &&
+ mime_type::kFFmpegVideo != mime_type)
+ return false;
+
+ // TODO(jiesun): Although we current only support H264 hardware decoding,
+ // in the future, we should query GpuVideoService for capabilities.
+ int codec_id;
+ return format.GetAsInteger(MediaFormat::kFFmpegCodecID, &codec_id) &&
+ codec_id == CODEC_ID_H264;
+}
+
+} // namespace media
+
diff --git a/chrome/renderer/media/ipc_video_decoder.h b/chrome/renderer/media/ipc_video_decoder.h
new file mode 100644
index 0000000..40a8098
--- /dev/null
+++ b/chrome/renderer/media/ipc_video_decoder.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2010 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 CHROME_RENDERER_MEDIA_IPC_VIDEO_DECODER_H_
+#define CHROME_RENDERER_MEDIA_IPC_VIDEO_DECODER_H_
+
+#include "base/time.h"
+#include "chrome/renderer/gpu_video_service_host.h"
+#include "media/base/pts_heap.h"
+#include "media/base/video_frame.h"
+#include "media/filters/decoder_base.h"
+
+struct AVRational;
+
+namespace media {
+
+class VideoDecodeEngine;
+
+class IpcVideoDecoder : public VideoDecoder,
+ public GpuVideoDecoderHost::EventHandler {
+ public:
+ explicit IpcVideoDecoder(MessageLoop* message_loop);
+ virtual ~IpcVideoDecoder();
+
+ static FilterFactory* CreateFactory(MessageLoop* message_loop);
+ static bool IsMediaFormatSupported(const MediaFormat& media_format);
+
+ // MediaFilter implementation.
+ virtual void Stop(FilterCallback* callback);
+ virtual void Seek(base::TimeDelta time, FilterCallback* callback);
+ virtual void Pause(FilterCallback* callback);
+ virtual void Flush(FilterCallback* callback);
+
+ // Decoder implementation.
+ virtual void Initialize(DemuxerStream* demuxer_stream,
+ FilterCallback* callback);
+ virtual const MediaFormat& media_format() { return media_format_; }
+ virtual void FillThisBuffer(scoped_refptr<VideoFrame> video_frame);
+
+ // GpuVideoDecoderHost::EventHandler.
+ virtual void OnInitializeDone(bool success,
+ const GpuVideoDecoderInitDoneParam& param);
+ virtual void OnUninitializeDone();
+ virtual void OnFlushDone();
+ virtual void OnEmptyBufferDone(scoped_refptr<Buffer> buffer);
+ virtual void OnFillBufferDone(scoped_refptr<VideoFrame> frame);
+ virtual void OnDeviceError();
+
+ virtual bool ProvidesBuffer();
+
+ private:
+ void OnSeekComplete(FilterCallback* callback);
+ void OnReadComplete(Buffer* buffer);
+ void ReadCompleteTask(scoped_refptr<Buffer> buffer);
+
+ private:
+ friend class FilterFactoryImpl2<IpcVideoDecoder,
+ VideoDecodeEngine*,
+ MessageLoop*>;
+
+ private:
+ int32 width_;
+ int32 height_;
+ MediaFormat media_format_;
+
+ scoped_ptr<FilterCallback> flush_callback_;
+ scoped_ptr<FilterCallback> initialize_callback_;
+ scoped_ptr<FilterCallback> stop_callback_;
+
+ enum DecoderState {
+ kUnInitialized,
+ kPlaying,
+ kFlushing,
+ kPausing,
+ kFlushCodec,
+ kEnded,
+ kStopped,
+ };
+ DecoderState state_;
+
+ // Tracks the number of asynchronous reads issued to |demuxer_stream_|.
+ // Using size_t since it is always compared against deque::size().
+ size_t pending_reads_;
+ // Tracks the number of asynchronous reads issued from renderer.
+ size_t pending_requests_;
+
+ // Pointer to the demuxer stream that will feed us compressed buffers.
+ scoped_refptr<DemuxerStream> demuxer_stream_;
+
+ MessageLoop* renderer_thread_message_loop_;
+ scoped_refptr<GpuVideoDecoderHost> gpu_video_decoder_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(IpcVideoDecoder);
+};
+
+} // namespace media
+
+#endif // CHROME_RENDERER_MEDIA_IPC_VIDEO_DECODER_H_
+
diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc
index 8ea6c08..7ab9ae8 100644
--- a/chrome/renderer/render_thread.cc
+++ b/chrome/renderer/render_thread.cc
@@ -49,6 +49,7 @@
#include "chrome/renderer/extensions/renderer_extension_bindings.h"
#include "chrome/renderer/external_extension.h"
#include "chrome/renderer/gpu_channel_host.h"
+#include "chrome/renderer/gpu_video_service_host.h"
#include "chrome/renderer/indexed_db_dispatcher.h"
#include "chrome/renderer/loadtimes_extension_bindings.h"
#include "chrome/renderer/net/renderer_net_predictor.h"
@@ -273,6 +274,8 @@ void RenderThread::Init() {
switches::kPrelaunchGpuProcess)) {
EstablishGpuChannel();
}
+
+ GpuVideoServiceHost::get()->OnRendererThreadInit(MessageLoop::current());
}
RenderThread::~RenderThread() {
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index bc400ef..c314f1c 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -52,6 +52,7 @@
#include "chrome/renderer/geolocation_dispatcher.h"
#include "chrome/renderer/localized_error.h"
#include "chrome/renderer/media/audio_renderer_impl.h"
+#include "chrome/renderer/media/ipc_video_decoder.h"
#include "chrome/renderer/media/ipc_video_renderer.h"
#include "chrome/renderer/navigation_state.h"
#include "chrome/renderer/notification_provider.h"
@@ -77,6 +78,7 @@
#include "gfx/rect.h"
#include "grit/generated_resources.h"
#include "grit/renderer_resources.h"
+#include "media/base/media_switches.h"
#include "net/base/data_url.h"
#include "net/base/escape.h"
#include "net/base/net_errors.h"
@@ -2325,6 +2327,13 @@ WebMediaPlayer* RenderView::createMediaPlayer(
AudioRendererImpl::CreateFactory(audio_message_filter()));
}
+ if (cmd_line->HasSwitch(switches::kEnableAcceleratedDecoding) &&
+ cmd_line->HasSwitch(switches::kEnableAcceleratedCompositing)) {
+ // Add the hardware video decoder factory.
+ factory->AddFactory(
+ media::IpcVideoDecoder::CreateFactory(MessageLoop::current()));
+ }
+
WebApplicationCacheHostImpl* appcache_host =
WebApplicationCacheHostImpl::FromFrame(frame);