diff options
30 files changed, 2481 insertions, 3 deletions
diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index bee6232..c66facd 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -578,6 +578,8 @@ void BrowserRenderProcessHost::PropagateBrowserCommandLineToRenderer( switches::kEnableRemoting, switches::kEnableClickToPlay, switches::kPrelaunchGpuProcess, + switches::kEnableAcceleratedCompositing, + switches::kEnableAcceleratedDecoding, }; renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames, arraysize(kSwitchNames)); diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 2d7228d..4245b66 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -708,6 +708,12 @@ 'gpu/gpu_process.h', 'gpu/gpu_thread.cc', 'gpu/gpu_thread.h', + 'gpu/gpu_video_decoder.cc', + 'gpu/gpu_video_decoder.h', + 'gpu/gpu_video_decoder_mft.cc', + 'gpu/gpu_video_decoder_mft.h', + 'gpu/gpu_video_service.cc', + 'gpu/gpu_video_service.h', 'gpu/gpu_view_win.cc', 'gpu/gpu_view_win.h', ], diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index b019c63..c60359f 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -61,6 +61,8 @@ 'common/gpu_messages.cc', 'common/gpu_messages.h', 'common/gpu_messages_internal.h', + 'common/gpu_video_common.cc', + 'common/gpu_video_common.h', 'common/indexed_db_key.cc', 'common/indexed_db_key.h', 'common/logging_chrome.cc', diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index a775909..c616afd 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -18,6 +18,7 @@ '../skia/skia.gyp:skia', '../third_party/hunspell/hunspell.gyp:hunspell', '../third_party/cld/cld.gyp:cld', + '../third_party/ffmpeg/ffmpeg.gyp:ffmpeg', '../third_party/icu/icu.gyp:icui18n', '../third_party/icu/icu.gyp:icuuc', '../third_party/npapi/npapi.gyp:npapi', @@ -64,6 +65,8 @@ 'renderer/loadtimes_extension_bindings.cc', 'renderer/media/audio_renderer_impl.cc', 'renderer/media/audio_renderer_impl.h', + 'renderer/media/ipc_video_decoder.cc', + 'renderer/media/ipc_video_decoder.h', 'renderer/media/ipc_video_renderer.cc', 'renderer/media/ipc_video_renderer.h', 'renderer/net/predictor_queue.cc', @@ -102,6 +105,10 @@ 'renderer/geolocation_dispatcher.cc', 'renderer/geolocation_dispatcher.h', 'renderer/gpu_channel_host.cc', + 'renderer/gpu_video_decoder_host.cc', + 'renderer/gpu_video_decoder_host.h', + 'renderer/gpu_video_service_host.cc', + 'renderer/gpu_video_service_host.h', 'renderer/indexed_db_dispatcher.cc', 'renderer/indexed_db_dispatcher.h', 'renderer/gpu_channel_host.h', diff --git a/chrome/common/gpu_messages_internal.h b/chrome/common/gpu_messages_internal.h index b17135b..543f087 100644 --- a/chrome/common/gpu_messages_internal.h +++ b/chrome/common/gpu_messages_internal.h @@ -10,6 +10,7 @@ // from it via utility_messages.h. #include "base/shared_memory.h" #include "chrome/common/gpu_info.h" +#include "chrome/common/gpu_video_common.h" #include "gfx/size.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_message_macros.h" @@ -155,6 +156,21 @@ IPC_BEGIN_MESSAGES(GpuChannel) IPC_MESSAGE_CONTROL1(GpuChannelMsg_DestroyCommandBuffer, int32 /* instance_id */) + // Get hardware video service routing id. + IPC_SYNC_MESSAGE_CONTROL0_1(GpuChannelMsg_GetVideoService, + GpuVideoServiceInfoParam) + + // Create hardware video decoder && associate it with the output |decoder_id|; + // We need this to be control message because we had to map the GpuChannel and + // |decoder_id|. + IPC_SYNC_MESSAGE_CONTROL0_1(GpuChannelMsg_CreateVideoDecoder, + GpuVideoDecoderInfoParam) + + // Release all resource of the hardware video decoder which was assocaited + // with the input |decoder_id|. + IPC_SYNC_MESSAGE_CONTROL1_0(GpuChannelMsg_DestroyVideoDecoder, + int32 /* decoder_id */) + IPC_END_MESSAGES(GpuChannel) //------------------------------------------------------------------------------ @@ -241,3 +257,67 @@ IPC_BEGIN_MESSAGES(GpuCommandBuffer) #endif IPC_END_MESSAGES(GpuCommandBuffer) + +//------------------------------------------------------------------------------ + +// GpuVideoDecoderMsgs : send from renderer process to gpu process. +IPC_BEGIN_MESSAGES(GpuVideoDecoder) + // Initialize and configure GpuVideoDecoder asynchronously. + IPC_MESSAGE_ROUTED1(GpuVideoDecoderMsg_Initialize, + GpuVideoDecoderInitParam) + + // Destroy and release GpuVideoDecoder asynchronously. + IPC_MESSAGE_ROUTED0(GpuVideoDecoderMsg_Destroy) + + // Start decoder flushing operation. + IPC_MESSAGE_ROUTED0(GpuVideoDecoderMsg_Flush) + + // Send input buffer to GpuVideoDecoder. + IPC_MESSAGE_ROUTED1(GpuVideoDecoderMsg_EmptyThisBuffer, + GpuVideoDecoderInputBufferParam) + + // Require output buffer from GpuVideoDecoder. + IPC_MESSAGE_ROUTED1(GpuVideoDecoderMsg_FillThisBuffer, + GpuVideoDecoderOutputBufferParam) + + // GpuVideoDecoderHost has consumed the output buffer. + // NOTE: this may only useful for copy back solution + // where output transfer buffer had to be guarded. + IPC_MESSAGE_ROUTED0(GpuVideoDecoderMsg_FillThisBufferDoneACK) + +IPC_END_MESSAGES(GpuVideoDecoder) + +//------------------------------------------------------------------------------ + +// GpuVideoDecoderMsgs : send from gpu process to renderer process. +IPC_BEGIN_MESSAGES(GpuVideoDecoderHost) + // Confirm GpuVideoDecoder had been initialized or failed to initialize. + IPC_MESSAGE_ROUTED1(GpuVideoDecoderHostMsg_InitializeACK, + GpuVideoDecoderInitDoneParam) + + // Confrim GpuVideoDecoder had been destroyed properly. + IPC_MESSAGE_ROUTED0(GpuVideoDecoderHostMsg_DestroyACK) + + // Confirm decoder had been flushed. + IPC_MESSAGE_ROUTED0(GpuVideoDecoderHostMsg_FlushACK) + + // GpuVideoDecoder has consumed input buffer from transfer buffer. + IPC_MESSAGE_ROUTED0(GpuVideoDecoderHostMsg_EmptyThisBufferACK) + + // GpuVideoDecoder require new input buffer. + IPC_MESSAGE_ROUTED0(GpuVideoDecoderHostMsg_EmptyThisBufferDone) + + // GpuVideoDecoder report output buffer ready. + IPC_MESSAGE_ROUTED1(GpuVideoDecoderHostMsg_FillThisBufferDone, + GpuVideoDecoderOutputBufferParam) + + // GpuVideoDecoder report output format change. + IPC_MESSAGE_ROUTED1(GpuVideoDecoderHostMsg_MediaFormatChange, + GpuVideoDecoderFormatChangeParam) + + // GpuVideoDecoder report error. + IPC_MESSAGE_ROUTED1(GpuVideoDecoderHostMsg_ErrorNotification, + GpuVideoDecoderErrorInfoParam) + +IPC_END_MESSAGES(GpuVideoDecoderHost) + diff --git a/chrome/common/gpu_video_common.cc b/chrome/common/gpu_video_common.cc new file mode 100644 index 0000000..06116f9 --- /dev/null +++ b/chrome/common/gpu_video_common.cc @@ -0,0 +1,211 @@ +// 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/common/gpu_video_common.h" + +namespace IPC { + +void ParamTraits<GpuVideoServiceInfoParam>::Write( + Message* m, const GpuVideoServiceInfoParam& p) { + m->WriteInt(p.video_service_route_id_); + m->WriteInt(p.video_service_host_route_id_); + m->WriteInt(p.service_available_); +} + +bool ParamTraits<GpuVideoServiceInfoParam>::Read( + const Message* m, void** iter, GpuVideoServiceInfoParam* r) { + if (!m->ReadInt(iter, &r->video_service_route_id_) || + !m->ReadInt(iter, &r->video_service_host_route_id_) || + !m->ReadInt(iter, &r->service_available_)) + return false; + return true; +} + +void ParamTraits<GpuVideoServiceInfoParam>::Log( + const GpuVideoServiceInfoParam& p, std::wstring* l) { + l->append(StringPrintf(L"(%d, %d, %d)", + p.video_service_route_id_, + p.video_service_host_route_id_, + p.service_available_)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ParamTraits<GpuVideoDecoderInfoParam>::Write( + Message* m, const GpuVideoDecoderInfoParam& p) { + m->WriteInt(p.decoder_id_); + m->WriteInt(p.decoder_route_id_); + m->WriteInt(p.decoder_host_route_id_); +} + +bool ParamTraits<GpuVideoDecoderInfoParam>::Read( + const Message* m, void** iter, GpuVideoDecoderInfoParam* r) { + if (!m->ReadInt(iter, &r->decoder_id_) || + !m->ReadInt(iter, &r->decoder_route_id_) || + !m->ReadInt(iter, &r->decoder_host_route_id_)) + return false; + return true; +} + +void ParamTraits<GpuVideoDecoderInfoParam>::Log( + const GpuVideoDecoderInfoParam& p, std::wstring* l) { + l->append(StringPrintf(L"(%d, %d, %d)", + p.decoder_id_, + p.decoder_route_id_, + p.decoder_host_route_id_)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ParamTraits<GpuVideoDecoderInitParam>::Write( + Message* m, const GpuVideoDecoderInitParam& p) { + m->WriteInt(p.codec_id_); + m->WriteInt(p.width_); + m->WriteInt(p.height_); +} + +bool ParamTraits<GpuVideoDecoderInitParam>::Read( + const Message* m, void** iter, GpuVideoDecoderInitParam* r) { + if (!m->ReadInt(iter, &r->codec_id_) || + !m->ReadInt(iter, &r->width_) || + !m->ReadInt(iter, &r->height_)) + return false; + return true; +} + +void ParamTraits<GpuVideoDecoderInitParam>::Log( + const GpuVideoDecoderInitParam& p, std::wstring* l) { + l->append(StringPrintf(L"(%d, %d %d)", p.codec_id_, p.width_, p.height_)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ParamTraits<GpuVideoDecoderInitDoneParam>::Write( + Message* m, const GpuVideoDecoderInitDoneParam& p) { + m->WriteInt(p.success_); + m->WriteInt(p.stride_); + m->WriteInt(p.format_); + m->WriteInt(p.surface_type_); + m->WriteInt(p.input_buffer_size_); + m->WriteInt(p.output_buffer_size_); + IPC::ParamTraits<base::SharedMemoryHandle>::Write( + m, p.input_buffer_handle_); + IPC::ParamTraits<base::SharedMemoryHandle>::Write( + m, p.output_buffer_handle_); +} + +bool ParamTraits<GpuVideoDecoderInitDoneParam>::Read( + const Message* m, void** iter, GpuVideoDecoderInitDoneParam* r) { + if (!m->ReadInt(iter, &r->success_) || + !m->ReadInt(iter, &r->stride_) || + !m->ReadInt(iter, &r->format_) || + !m->ReadInt(iter, &r->surface_type_) || + !m->ReadInt(iter, &r->input_buffer_size_) || + !m->ReadInt(iter, &r->output_buffer_size_) || + !IPC::ParamTraits<base::SharedMemoryHandle>::Read( + m, iter, &r->input_buffer_handle_) || + !IPC::ParamTraits<base::SharedMemoryHandle>::Read( + m, iter, &r->output_buffer_handle_)) + return false; + return true; +} + +void ParamTraits<GpuVideoDecoderInitDoneParam>::Log( + const GpuVideoDecoderInitDoneParam& p, std::wstring* l) { + l->append(StringPrintf(L"(%d)", p.stride_)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ParamTraits<GpuVideoDecoderInputBufferParam>::Write( + Message* m, const GpuVideoDecoderInputBufferParam& p) { + m->WriteInt64(p.timestamp_); + m->WriteInt(p.offset_); + m->WriteInt(p.size_); +} + +bool ParamTraits<GpuVideoDecoderInputBufferParam>::Read( + const Message* m, void** iter, GpuVideoDecoderInputBufferParam* r) { + if (!m->ReadInt64(iter, &r->timestamp_) || + !m->ReadInt(iter, &r->offset_) || + !m->ReadInt(iter, &r->size_)) + return false; + return true; +} + +void ParamTraits<GpuVideoDecoderInputBufferParam>::Log( + const GpuVideoDecoderInputBufferParam& p, std::wstring* l) { + l->append(StringPrintf(L"(%d %d %d)", + static_cast<int>(p.timestamp_), + p.offset_, p.size_)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ParamTraits<GpuVideoDecoderOutputBufferParam>::Write( + Message* m, const GpuVideoDecoderOutputBufferParam& p) { + m->WriteInt64(p.timestamp_); + m->WriteInt64(p.duration_); + m->WriteInt(p.flags_); +} + +bool ParamTraits<GpuVideoDecoderOutputBufferParam>::Read( + const Message* m, void** iter, GpuVideoDecoderOutputBufferParam* r) { + if (!m->ReadInt64(iter, &r->timestamp_) || + !m->ReadInt64(iter, &r->duration_) || + !m->ReadInt(iter, &r->flags_)) + return false; + return true; +} + +void ParamTraits<GpuVideoDecoderOutputBufferParam>::Log( + const GpuVideoDecoderOutputBufferParam& p, std::wstring* l) { + l->append(StringPrintf(L"(%d %d) %x", + static_cast<int>(p.timestamp_), + static_cast<int>(p.duration_), + p.flags_)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ParamTraits<GpuVideoDecoderErrorInfoParam>::Write( + Message* m, const GpuVideoDecoderErrorInfoParam& p) { + m->WriteInt(p.error_id); +} + +bool ParamTraits<GpuVideoDecoderErrorInfoParam>::Read( + const Message* m, void** iter, GpuVideoDecoderErrorInfoParam* r) { + if (!m->ReadInt(iter, &r->error_id)) + return false; + return true; +} + +void ParamTraits<GpuVideoDecoderErrorInfoParam>::Log( + const GpuVideoDecoderErrorInfoParam& p, std::wstring* l) { + l->append(StringPrintf(L"(%d)", p.error_id)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ParamTraits<GpuVideoDecoderFormatChangeParam>::Write( + Message* m, const GpuVideoDecoderFormatChangeParam& p) { + m->WriteInt(p.input_buffer_size_); + m->WriteInt(p.output_buffer_size_); +} + +bool ParamTraits<GpuVideoDecoderFormatChangeParam>::Read( + const Message* m, void** iter, GpuVideoDecoderFormatChangeParam* r) { + if (!m->ReadInt(iter, &r->input_buffer_size_) || + !m->ReadInt(iter, &r->output_buffer_size_)) + return false; + return true; +} + +void ParamTraits<GpuVideoDecoderFormatChangeParam>::Log( + const GpuVideoDecoderFormatChangeParam& p, std::wstring* l) { + l->append(StringPrintf(L"(%d %d)", p.input_buffer_size_, + p.output_buffer_size_)); +} +}; + diff --git a/chrome/common/gpu_video_common.h b/chrome/common/gpu_video_common.h new file mode 100644 index 0000000..34974a6 --- /dev/null +++ b/chrome/common/gpu_video_common.h @@ -0,0 +1,172 @@ +// 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_COMMON_GPU_VIDEO_COMMON_H_ +#define CHROME_COMMON_GPU_VIDEO_COMMON_H_ + +#include "base/basictypes.h" +#include "base/shared_memory.h" +#include "chrome/common/common_param_traits.h" + +class GpuVideoServiceInfoParam { + public: + // route id for GpuVideoService on GPU process side for this channel. + int32 video_service_route_id_; + // route id for GpuVideoServiceHost on Render process side for this channel. + int32 video_service_host_route_id_; + // TODO(jiesun): define capabilities of video service. + int32 service_available_; +}; + +class GpuVideoDecoderInfoParam { + public: + // global decoder id. + int32 decoder_id_; + // route id for GpuVideoDecoder on GPU process side for this channel. + int32 decoder_route_id_; + // route id for GpuVideoServiceHost on Render process side for this channel. + int32 decoder_host_route_id_; +}; + +class GpuVideoDecoderInitParam { + public: + int32 codec_id_; + int32 width_; + int32 height_; + int32 profile_; + int32 level_; + int32 frame_rate_den_; + int32 frame_rate_num_; + int32 aspect_ratio_den_; + int32 aspect_ratio_num_; +}; + +class GpuVideoDecoderInitDoneParam { + public: + enum SurfaceType { + SurfaceTypeSystemMemory, + SurfaceTypeD3D, + SurfaceTypeEGLImage, + }; + enum SurfaceFormat { + SurfaceFormat_YV12, + SurfaceFormat_NV12, + SurfaceFormat_XRGB, + }; + int32 success_; // other parameter is only meaningful when this is true. + int32 provides_buffer; + int32 format_; + int32 surface_type_; + int32 stride_; + int32 input_buffer_size_; + int32 output_buffer_size_; + base::SharedMemoryHandle input_buffer_handle_; + // we do not need this if hardware composition is ready. + base::SharedMemoryHandle output_buffer_handle_; +}; + +class GpuVideoDecoderInputBufferParam { + public: + int64 timestamp_; // In unit of microseconds. + int32 offset_; + int32 size_; + int32 flags_; // miscellaneous flag bit mask +}; + +class GpuVideoDecoderOutputBufferParam { + public: + int64 timestamp_; // In unit of microseconds. + int64 duration_; // In unit of microseconds. + int32 flags_; // miscellaneous flag bit mask + + enum { + kFlagsEndOfStream = 0x00000001, + kFlagsDiscontinuous = 0x00000002, + }; +}; + +class GpuVideoDecoderErrorInfoParam { + public: + int32 error_id; // TODO(jiesun): define enum. +}; + +// TODO(jiesun): define this. +class GpuVideoDecoderFormatChangeParam { + public: + int32 stride_; + int32 input_buffer_size_; + int32 output_buffer_size_; + base::SharedMemoryHandle input_buffer_handle_; + base::SharedMemoryHandle output_buffer_handle_; +}; + +namespace IPC { + +template <> +struct ParamTraits<GpuVideoServiceInfoParam> { + typedef GpuVideoServiceInfoParam param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; + +template <> +struct ParamTraits<GpuVideoDecoderInfoParam> { + typedef GpuVideoDecoderInfoParam param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; + +template <> +struct ParamTraits<GpuVideoDecoderInitParam> { + typedef GpuVideoDecoderInitParam param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; + +template <> +struct ParamTraits<GpuVideoDecoderInitDoneParam> { + typedef GpuVideoDecoderInitDoneParam param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; + +template <> +struct ParamTraits<GpuVideoDecoderInputBufferParam> { + typedef GpuVideoDecoderInputBufferParam param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; + +template <> +struct ParamTraits<GpuVideoDecoderOutputBufferParam> { + typedef GpuVideoDecoderOutputBufferParam param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; + +template <> +struct ParamTraits<GpuVideoDecoderErrorInfoParam> { + typedef GpuVideoDecoderErrorInfoParam param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; + +template <> +struct ParamTraits<GpuVideoDecoderFormatChangeParam> { + typedef GpuVideoDecoderFormatChangeParam param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; +}; + +#endif // CHROME_COMMON_GPU_VIDEO_COMMON_H_ + diff --git a/chrome/gpu/gpu_channel.cc b/chrome/gpu/gpu_channel.cc index 69c66d4..a63faa9 100644 --- a/chrome/gpu/gpu_channel.cc +++ b/chrome/gpu/gpu_channel.cc @@ -17,6 +17,7 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/gpu_messages.h" #include "chrome/gpu/gpu_thread.h" +#include "chrome/gpu/gpu_video_service.h" #if defined(OS_POSIX) #include "ipc/ipc_channel_posix.h" @@ -88,6 +89,12 @@ void GpuChannel::OnControlMessageReceived(const IPC::Message& msg) { OnCreateOffscreenCommandBuffer) IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer, OnDestroyCommandBuffer) + IPC_MESSAGE_HANDLER(GpuChannelMsg_GetVideoService, + OnGetVideoService) + IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateVideoDecoder, + OnCreateVideoDecoder) + IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyVideoDecoder, + OnDestroyVideoDecoder) IPC_MESSAGE_UNHANDLED_ERROR() IPC_END_MESSAGE_MAP() } @@ -178,6 +185,50 @@ void GpuChannel::OnDestroyCommandBuffer(int32 route_id) { #endif } +void GpuChannel::OnGetVideoService(GpuVideoServiceInfoParam* info) { + info->service_available_ = 0; +#if defined(ENABLE_GPU) +#if defined(OS_WIN) + // TODO(jiesun): Not every windows platforms will support our media + // foundation implementation. Add more check here. + LOG(INFO) << "GpuChannel::OnGetVideoService"; + GpuVideoService* service = GpuVideoService::get(); + if (service == NULL) + return; + + info->video_service_host_route_id_ = GenerateRouteID(); + info->video_service_route_id_ = GenerateRouteID(); + // TODO(jiesun): we could had multiple entries in this routing table. + router_.AddRoute(info->video_service_route_id_, service); + info->service_available_ = 1; +#endif +#endif +} + +void GpuChannel::OnCreateVideoDecoder(GpuVideoDecoderInfoParam* info) { +#if defined(ENABLE_GPU) + LOG(INFO) << "GpuChannel::OnCreateVideoDecoder"; + info->decoder_id_ = -1; + GpuVideoService* service = GpuVideoService::get(); + if (service == NULL) + return; + + info->decoder_host_route_id_ = GenerateRouteID(); + info->decoder_route_id_ = GenerateRouteID(); + service->CreateVideoDecoder(this, &router_, info); +#endif +} + +void GpuChannel::OnDestroyVideoDecoder(int32 decoder_id) { +#if defined(ENABLE_GPU) + LOG(ERROR) << "GpuChannel::OnDestroyVideoDecoder"; + GpuVideoService* service = GpuVideoService::get(); + if (service == NULL) + return; + service->DestroyVideoDecoder(&router_, decoder_id); +#endif +} + bool GpuChannel::Init() { // Check whether we're already initialized. if (channel_.get()) @@ -198,6 +249,7 @@ bool GpuChannel::Init() { channel_name, IPC::Channel::MODE_SERVER, this, NULL, ChildProcess::current()->io_message_loop(), false, ChildProcess::current()->GetShutDownEvent())); + return true; } diff --git a/chrome/gpu/gpu_channel.h b/chrome/gpu/gpu_channel.h index da6601f..f21007f 100644 --- a/chrome/gpu/gpu_channel.h +++ b/chrome/gpu/gpu_channel.h @@ -12,6 +12,7 @@ #include "base/scoped_open_process.h" #include "base/scoped_ptr.h" #include "build/build_config.h" +#include "chrome/common/gpu_video_common.h" #include "chrome/common/message_router.h" #include "chrome/gpu/gpu_command_buffer_stub.h" #include "gfx/native_widget_types.h" @@ -72,6 +73,10 @@ class GpuChannel : public IPC::Channel::Listener, int32* route_id); void OnDestroyCommandBuffer(int32 route_id); + void OnGetVideoService(GpuVideoServiceInfoParam* info); + void OnCreateVideoDecoder(GpuVideoDecoderInfoParam* info); + void OnDestroyVideoDecoder(int32 decoder_id); + scoped_ptr<IPC::SyncChannel> channel_; // Handle to the renderer process who is on the other side of the channel. diff --git a/chrome/gpu/gpu_video_decoder.cc b/chrome/gpu/gpu_video_decoder.cc new file mode 100644 index 0000000..fa478bf --- /dev/null +++ b/chrome/gpu/gpu_video_decoder.cc @@ -0,0 +1,108 @@ +// 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/common/gpu_messages.h" +#include "chrome/gpu/gpu_channel.h" +#include "chrome/gpu/gpu_video_decoder.h" + +void GpuVideoDecoder::OnChannelConnected(int32 peer_pid) { +} + +void GpuVideoDecoder::OnChannelError() { +} + +void GpuVideoDecoder::OnMessageReceived(const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(GpuVideoDecoder, msg) + IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Initialize, + OnInitialize) + IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Destroy, + OnUninitialize) + IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_Flush, + OnFlush) + IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_EmptyThisBuffer, + OnEmptyThisBuffer) + IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_FillThisBuffer, + OnFillThisBuffer) + IPC_MESSAGE_HANDLER(GpuVideoDecoderMsg_FillThisBufferDoneACK, + OnFillThisBufferDoneACK) + IPC_MESSAGE_UNHANDLED_ERROR() + IPC_END_MESSAGE_MAP() +} + +GpuVideoDecoder::GpuVideoDecoder( + const GpuVideoDecoderInfoParam* param, + GpuChannel* channel, + base::ProcessHandle handle) + : decoder_host_route_id_(param->decoder_host_route_id_), + channel_(channel), renderer_handle_(handle) { +} + +void GpuVideoDecoder::OnInitialize(const GpuVideoDecoderInitParam& param) { + init_param_ = param; + done_param_.success_ = DoInitialize(init_param_, &done_param_); +} + +void GpuVideoDecoder::OnUninitialize() { + DoUninitialize(); +} + +void GpuVideoDecoder::OnFlush() { + DoFlush(); +} + +void GpuVideoDecoder::OnEmptyThisBuffer( + const GpuVideoDecoderInputBufferParam& buffer) { + DoEmptyThisBuffer(buffer); +} +void GpuVideoDecoder::OnFillThisBuffer( + const GpuVideoDecoderOutputBufferParam& frame) { + DoFillThisBuffer(frame); +} + +void GpuVideoDecoder::OnFillThisBufferDoneACK() { + DoFillThisBufferDoneACK(); +} + +void GpuVideoDecoder::SendInitializeDone( + const GpuVideoDecoderInitDoneParam& param) { + if (!channel_->Send( + new GpuVideoDecoderHostMsg_InitializeACK(route_id(), param))) { + LOG(ERROR) << "GpuVideoDecoderMsg_InitializeACK failed"; + } +} + +void GpuVideoDecoder::SendUninitializeDone() { + if (!channel_->Send(new GpuVideoDecoderHostMsg_DestroyACK(route_id()))) { + LOG(ERROR) << "GpuVideoDecoderMsg_DestroyACK failed"; + } +} + +void GpuVideoDecoder::SendFlushDone() { + if (!channel_->Send(new GpuVideoDecoderHostMsg_FlushACK(route_id()))) { + LOG(ERROR) << "GpuVideoDecoderMsg_FlushACK failed"; + } +} + +void GpuVideoDecoder::SendEmptyBufferDone() { + if (!channel_->Send( + new GpuVideoDecoderHostMsg_EmptyThisBufferDone(route_id()))) { + LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBufferDone failed"; + } +} + +void GpuVideoDecoder::SendEmptyBufferACK() { + if (!channel_->Send( + new GpuVideoDecoderHostMsg_EmptyThisBufferACK(route_id()))) { + LOG(ERROR) << "GpuVideoDecoderMsg_EmptyThisBufferACK failed"; + } +} + +void GpuVideoDecoder::SendFillBufferDone( + const GpuVideoDecoderOutputBufferParam& frame) { + if (!channel_->Send( + new GpuVideoDecoderHostMsg_FillThisBufferDone(route_id(), frame))) { + LOG(ERROR) << "GpuVideoDecoderMsg_FillThisBufferDone failed"; + } +} + diff --git a/chrome/gpu/gpu_video_decoder.h b/chrome/gpu/gpu_video_decoder.h new file mode 100644 index 0000000..62170fe --- /dev/null +++ b/chrome/gpu/gpu_video_decoder.h @@ -0,0 +1,76 @@ +// 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_GPU_GPU_VIDEO_DECODER_H_ +#define CHROME_GPU_GPU_VIDEO_DECODER_H_ + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "chrome/common/gpu_video_common.h" +#include "ipc/ipc_channel.h" + +class GpuChannel; + +class GpuVideoDecoder + : public IPC::Channel::Listener, + public base::RefCountedThreadSafe<GpuVideoDecoder> { + + public: + // IPC::Channel::Listener. + virtual void OnChannelConnected(int32 peer_pid); + virtual void OnChannelError(); + virtual void OnMessageReceived(const IPC::Message& message); + + virtual bool DoInitialize(const GpuVideoDecoderInitParam& init_param, + GpuVideoDecoderInitDoneParam* done_param) = 0; + virtual bool DoUninitialize() = 0; + virtual void DoFlush() = 0; + virtual void DoEmptyThisBuffer( + const GpuVideoDecoderInputBufferParam& buffer) = 0; + virtual void DoFillThisBuffer( + const GpuVideoDecoderOutputBufferParam& frame) = 0; + virtual void DoFillThisBufferDoneACK() = 0; + + GpuVideoDecoder(const GpuVideoDecoderInfoParam* param, + GpuChannel* channel_, + base::ProcessHandle handle); + virtual ~GpuVideoDecoder() {} + + protected: + // Output message helper. + void SendInitializeDone(const GpuVideoDecoderInitDoneParam& param); + void SendUninitializeDone(); + void SendFlushDone(); + void SendEmptyBufferDone(); + void SendEmptyBufferACK(); + void SendFillBufferDone(const GpuVideoDecoderOutputBufferParam& frame); + + int32 route_id() { return decoder_host_route_id_; } + + int32 decoder_host_route_id_; + GpuChannel* channel_; + base::ProcessHandle renderer_handle_; + + GpuVideoDecoderInitParam init_param_; + GpuVideoDecoderInitDoneParam done_param_; + + scoped_ptr<base::SharedMemory> input_transfer_buffer_; + scoped_ptr<base::SharedMemory> output_transfer_buffer_; + + private: + // Input message handler. + void OnInitialize(const GpuVideoDecoderInitParam& param); + void OnUninitialize(); + void OnFlush(); + void OnEmptyThisBuffer(const GpuVideoDecoderInputBufferParam& buffer); + void OnFillThisBuffer(const GpuVideoDecoderOutputBufferParam& frame); + void OnFillThisBufferDoneACK(); + + DISALLOW_COPY_AND_ASSIGN(GpuVideoDecoder); +}; + +#endif // CHROME_GPU_GPU_VIDEO_DECODER_H_ + diff --git a/chrome/gpu/gpu_video_decoder_mft.cc b/chrome/gpu/gpu_video_decoder_mft.cc new file mode 100644 index 0000000..8c16201 --- /dev/null +++ b/chrome/gpu/gpu_video_decoder_mft.cc @@ -0,0 +1,594 @@ +// 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/gpu/gpu_video_decoder_mft.h" + +#if defined(OS_WIN) + +#pragma comment(lib, "dxva2.lib") +#pragma comment(lib, "d3d9.lib") +#pragma comment(lib, "evr.lib") +#pragma comment(lib, "mf.lib") +#pragma comment(lib, "mfplat.lib") +#pragma comment(lib, "mfuuid.lib") +#pragma comment(lib, "strmiids.lib") + +GpuVideoDecoderMFT::GpuVideoDecoderMFT( + const GpuVideoDecoderInfoParam* param, + GpuChannel* channel_, + base::ProcessHandle handle) + : GpuVideoDecoder(param, channel_, handle), + state_(kNormal) { + output_transfer_buffer_busy_ = false; + pending_request_ = 0; +} + +bool GpuVideoDecoderMFT::StartupComLibraries() { + HRESULT hr; + hr = CoInitializeEx(NULL, + COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + if (FAILED(hr)) { + LOG(ERROR) << "CoInit fail"; + return false; + } + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + if (FAILED(hr)) { + LOG(ERROR) << "MFStartup fail"; + CoUninitialize(); + return false; + } + return true; +} + +void GpuVideoDecoderMFT::ShutdownComLibraries() { + HRESULT hr; + hr = MFShutdown(); + if (FAILED(hr)) { + LOG(WARNING) << "Warning: MF failed to shutdown"; + } + CoUninitialize(); +} + +// Creates a Media Foundation sample with one buffer containing a copy of the +// given Annex B stream data. +// If duration and sample_time are not known, provide 0. +// min_size specifies the minimum size of the buffer (might be required by +// the decoder for input). The times here should be given in 100ns units. +IMFSample* GpuVideoDecoderMFT::CreateInputSample(uint8* data, + int32 size, + int64 timestamp, + int64 duration, + int32 min_size) { + ScopedComPtr<IMFSample> sample; + HRESULT hr = MFCreateSample(sample.Receive()); + if (FAILED(hr) || !sample.get()) { + LOG(ERROR) << "Unable to create an empty sample"; + return NULL; + } + + ScopedComPtr<IMFMediaBuffer> buffer; + int32 buffer_length = min_size > size ? min_size : size; + hr = MFCreateMemoryBuffer(buffer_length, buffer.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Unable to create an empty buffer"; + return NULL; + } + + hr = sample->AddBuffer(buffer.get()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to add empty buffer to sample"; + return NULL; + } + + if (duration > 0 && FAILED(sample->SetSampleDuration(duration))) { + LOG(ERROR) << "Failed to set sample duration"; + return NULL; + } + + if (timestamp > 0 && FAILED(sample->SetSampleTime(timestamp))) { + LOG(ERROR) << "Failed to set sample time"; + return NULL; + } + + DWORD max_length, current_length; + uint8* buffer_data; + hr = buffer->Lock(&buffer_data, &max_length, ¤t_length); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to lock buffer"; + return NULL; + } + CHECK_GE(static_cast<int>(max_length), size); + memcpy(buffer_data, data, size); + CHECK(SUCCEEDED(buffer->Unlock())); + + hr = buffer->SetCurrentLength(size); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to set current length to " << size; + return NULL; + } + + return sample.Detach(); +} + +bool GpuVideoDecoderMFT::CreateD3DDevManager(HWND video_window) { + d3d9_.Attach(Direct3DCreate9(D3D_SDK_VERSION)); + if (d3d9_.get() == NULL) { + LOG(ERROR) << "Failed to create D3D9"; + return false; + } + + D3DPRESENT_PARAMETERS present_params = {0}; + present_params.BackBufferWidth = init_param_.width_; + present_params.BackBufferHeight = init_param_.height_; + present_params.BackBufferFormat = D3DFMT_UNKNOWN; + present_params.BackBufferCount = 1; + present_params.SwapEffect = D3DSWAPEFFECT_DISCARD; + present_params.hDeviceWindow = video_window; + present_params.Windowed = TRUE; + present_params.Flags = D3DPRESENTFLAG_VIDEO; + present_params.FullScreen_RefreshRateInHz = 0; + present_params.PresentationInterval = 0; + + // D3DCREATE_HARDWARE_VERTEXPROCESSING specifies hardware vertex processing. + // (Is it even needed for just video decoding?) + HRESULT hr = d3d9_->CreateDevice(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + video_window, + D3DCREATE_HARDWARE_VERTEXPROCESSING, + &present_params, + device_.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to create D3D Device"; + return false; + } + + UINT dev_manager_reset_token = 0; + hr = DXVA2CreateDirect3DDeviceManager9(&dev_manager_reset_token, + device_manager_.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Couldn't create D3D Device manager"; + return false; + } + + hr = device_manager_->ResetDevice(device_.get(), + dev_manager_reset_token); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to set device to device manager"; + return false; + } + + return true; +} + +bool GpuVideoDecoderMFT::InitMediaFoundation() { + if (!StartupComLibraries()) + return false; + + if (!CreateD3DDevManager(GetDesktopWindow())) + return false; + + if (!InitDecoder()) + return false; + + if (!GetStreamsInfoAndBufferReqs()) + return false; + + return SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM); +} + +bool GpuVideoDecoderMFT::InitDecoder() { + // TODO(jiesun): use MFEnum to get decoder CLSID. + HRESULT hr = CoCreateInstance(__uuidof(CMSH264DecoderMFT), + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IMFTransform), + reinterpret_cast<void**>(decoder_.Receive())); + if (FAILED(hr) || !decoder_.get()) { + LOG(ERROR) << "CoCreateInstance failed " << std::hex << std::showbase << hr; + return false; + } + + if (!CheckDecoderDxvaSupport()) + return false; + + hr = decoder_->ProcessMessage( + MFT_MESSAGE_SET_D3D_MANAGER, + reinterpret_cast<ULONG_PTR>(device_manager_.get())); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to set D3D9 device to decoder"; + return false; + } + + return SetDecoderMediaTypes(); +} + +bool GpuVideoDecoderMFT::CheckDecoderDxvaSupport() { + ScopedComPtr<IMFAttributes> attributes; + HRESULT hr = decoder_->GetAttributes(attributes.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Unlock: Failed to get attributes, hr = " + << std::hex << std::showbase << hr; + return false; + } + + UINT32 dxva; + hr = attributes->GetUINT32(MF_SA_D3D_AWARE, &dxva); + if (FAILED(hr) || !dxva) { + LOG(ERROR) << "Failed to get DXVA attr, hr = " + << std::hex << std::showbase << hr + << "this might not be the right decoder."; + return false; + } + return true; +} + +bool GpuVideoDecoderMFT::SetDecoderMediaTypes() { + return SetDecoderInputMediaType() && + SetDecoderOutputMediaType(MFVideoFormat_NV12); +} + +bool GpuVideoDecoderMFT::SetDecoderInputMediaType() { + ScopedComPtr<IMFMediaType> media_type; + HRESULT hr = MFCreateMediaType(media_type.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to create empty media type object"; + return false; + } + + hr = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); + if (FAILED(hr)) { + LOG(ERROR) << "SetGUID for major type failed"; + return false; + } + + hr = media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264); + if (FAILED(hr)) { + LOG(ERROR) << "SetGUID for subtype failed"; + return false; + } + + hr = decoder_->SetInputType(0, media_type.get(), 0); // No flags + if (FAILED(hr)) { + LOG(ERROR) << "Failed to set decoder's input type"; + return false; + } + + return true; +} + +bool GpuVideoDecoderMFT::SetDecoderOutputMediaType(const GUID subtype) { + DWORD i = 0; + IMFMediaType* out_media_type; + bool found = false; + while (SUCCEEDED(decoder_->GetOutputAvailableType(0, i, &out_media_type))) { + GUID out_subtype; + HRESULT hr = out_media_type->GetGUID(MF_MT_SUBTYPE, &out_subtype); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to GetGUID() on GetOutputAvailableType() " << i; + out_media_type->Release(); + continue; + } + if (out_subtype == subtype) { + hr = decoder_->SetOutputType(0, out_media_type, 0); // No flags + if (FAILED(hr)) { + LOG(ERROR) << "Failed to SetOutputType to |subtype| or obtain " + << "width/height/stride " << std::hex << hr; + } else { + out_media_type->Release(); + return true; + } + } + i++; + out_media_type->Release(); + } + return false; +} + +bool GpuVideoDecoderMFT::SendMFTMessage(MFT_MESSAGE_TYPE msg) { + HRESULT hr = decoder_->ProcessMessage(msg, NULL); + return SUCCEEDED(hr); +} + +// Prints out info about the input/output streams, gets the minimum buffer sizes +// for input and output samples. +// The MFT will not allocate buffer for neither input nor output, so we have +// to do it ourselves and make sure they're the correct size. +// Exception is when dxva is enabled, the decoder will allocate output. +bool GpuVideoDecoderMFT::GetStreamsInfoAndBufferReqs() { + HRESULT hr = decoder_->GetInputStreamInfo(0, &input_stream_info_); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get input stream info"; + return false; + } + LOG(INFO) << "Input stream info: "; + LOG(INFO) << "Max latency: " << input_stream_info_.hnsMaxLatency; + + // There should be three flags, one for requiring a whole frame be in a + // single sample, one for requiring there be one buffer only in a single + // sample, and one that specifies a fixed sample size. (as in cbSize) + LOG(INFO) << "Flags: " + << std::hex << std::showbase << input_stream_info_.dwFlags; + CHECK_EQ(static_cast<int>(input_stream_info_.dwFlags), 0x7); + LOG(INFO) << "Min buffer size: " << input_stream_info_.cbSize; + LOG(INFO) << "Max lookahead: " << input_stream_info_.cbMaxLookahead; + LOG(INFO) << "Alignment: " << input_stream_info_.cbAlignment; + if (input_stream_info_.cbAlignment > 0) { + LOG(WARNING) << "Warning: Decoder requires input to be aligned"; + } + + hr = decoder_->GetOutputStreamInfo(0, &output_stream_info_); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get output stream info"; + return false; + } + LOG(INFO) << "Output stream info: "; + + // The flags here should be the same and mean the same thing, except when + // DXVA is enabled, there is an extra 0x100 flag meaning decoder will + // allocate its own sample. + CHECK_EQ(static_cast<int>(output_stream_info_.dwFlags), 0x107); + LOG(INFO) << "Min buffer size: " << output_stream_info_.cbSize; + LOG(INFO) << "Alignment: " << output_stream_info_.cbAlignment; + if (output_stream_info_.cbAlignment > 0) { + LOG(WARNING) << "Warning: Decoder requires output to be aligned"; + } + + return true; +} + +bool GpuVideoDecoderMFT::DoInitialize( + const GpuVideoDecoderInitParam& param, + GpuVideoDecoderInitDoneParam* done_param) { + LOG(ERROR) << "GpuVideoDecoderMFT::DoInitialize"; + + done_param->format_ = + GpuVideoDecoderInitDoneParam::SurfaceFormat_YV12; + done_param->surface_type_ = + GpuVideoDecoderInitDoneParam::SurfaceTypeSystemMemory; + done_param->input_buffer_handle_ = base::SharedMemory::NULLHandle(); + done_param->output_buffer_handle_ = base::SharedMemory::NULLHandle(); + + do { + done_param->success_ = false; + + if (!InitMediaFoundation()) + break; + + // TODO(jiesun): Check the assumption of input size < original size. + done_param->input_buffer_size_ = param.width_ * param.height_ * 3 / 2; + input_transfer_buffer_.reset(new base::SharedMemory); + if (!input_transfer_buffer_->Create(std::wstring(), false, false, + done_param->input_buffer_size_)) + break; + if (!input_transfer_buffer_->Map(done_param->input_buffer_size_)) + break; + + // TODO(jiesun): Allocate this according to the surface format. + // The format actually could change during streaming, we need to + // notify GpuVideoDecoderHost side when this happened and renegotiate + // the transfer buffer. + done_param->output_buffer_size_ = param.width_ * param.height_ * 3 / 2; + output_transfer_buffer_.reset(new base::SharedMemory); + if (!output_transfer_buffer_->Create(std::wstring(), false, false, + done_param->output_buffer_size_)) + break; + if (!output_transfer_buffer_->Map(done_param->output_buffer_size_)) + break; + + if (!input_transfer_buffer_->ShareToProcess( + renderer_handle_, + &done_param->input_buffer_handle_)) + break; + if (!output_transfer_buffer_->ShareToProcess( + renderer_handle_, + &done_param->output_buffer_handle_)) + break; + + done_param->success_ = true; + } while (0); + + SendInitializeDone(*done_param); + return true; +} + +bool GpuVideoDecoderMFT::DoUninitialize() { + LOG(ERROR) << "GpuVideoDecoderMFT::DoUninitialize"; + SendUninitializeDone(); + return true; +} + +void GpuVideoDecoderMFT::DoEmptyThisBuffer( + const GpuVideoDecoderInputBufferParam& buffer) { + LOG(ERROR) << "GpuVideoDecoderMFT::EmptyThisBuffer"; + + CHECK(input_transfer_buffer_->memory()); + ScopedComPtr<IMFSample> sample; + if (buffer.size_) { + uint8* data = static_cast<uint8*>(input_transfer_buffer_->memory()); + sample.Attach(CreateInputSample(data, + buffer.size_, + buffer.timestamp_*10, + 0LL, + input_stream_info_.cbSize)); + CHECK(sample.get()); + } else { + state_ = kEosFlush; + } + + input_buffer_queue_.push_back(sample); + SendEmptyBufferACK(); + + while (pending_request_) + if (!DoDecode()) break; +} + +void GpuVideoDecoderMFT::DoFillThisBuffer( + const GpuVideoDecoderOutputBufferParam& frame) { + LOG(ERROR) << "GpuVideoDecoderMFT::FillThisBuffer"; + + pending_request_++; + while (pending_request_) + if (!DoDecode()) break; +} + +void GpuVideoDecoderMFT::DoFillThisBufferDoneACK() { + output_transfer_buffer_busy_ = false; + pending_request_--; + while (pending_request_) + if (!DoDecode()) break; +} + +void GpuVideoDecoderMFT::DoFlush() { + state_ = kFlushing; + + while (!input_buffer_queue_.empty()) + input_buffer_queue_.pop_front(); + pending_request_ = 0; + // TODO(jiesun): this is wrong?? + output_transfer_buffer_busy_ = false; + SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH); + + state_ = kNormal; + SendFlushDone(); +} + +bool GpuVideoDecoderMFT::DoDecode() { + if (state_ != kNormal && state_ != kEosFlush) return false; + if (output_transfer_buffer_busy_) return false; + + MFT_OUTPUT_DATA_BUFFER output_data_buffer; + memset(&output_data_buffer, 0, sizeof(output_data_buffer)); + output_data_buffer.dwStreamID = 0; + + ScopedComPtr<IMFSample> output_sample; + DWORD status; + HRESULT hr = decoder_->ProcessOutput(0, // No flags + 1, // # of out streams to pull from + &output_data_buffer, + &status); + + IMFCollection* events = output_data_buffer.pEvents; + if (events != NULL) { + LOG(INFO) << "Got events from ProcessOuput, but discarding"; + events->Release(); + } + + if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { + hr = SetDecoderOutputMediaType(MFVideoFormat_NV12); + CHECK(SUCCEEDED(hr)); + return true; + } + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { + if (input_buffer_queue_.empty()) { + if (state_ == kEosFlush) { + GpuVideoDecoderOutputBufferParam output_param; + output_param.timestamp_ = 0; + output_param.duration_ = 0; + output_param.flags_ = + GpuVideoDecoderOutputBufferParam::kFlagsEndOfStream; + output_transfer_buffer_busy_ = true; + SendFillBufferDone(output_param); + } + return false; + } + while (!input_buffer_queue_.empty()) { + ScopedComPtr<IMFSample> input_sample = input_buffer_queue_.front(); + input_buffer_queue_.pop_front(); + + if (input_sample.get()) { + HRESULT hr = decoder_->ProcessInput(0, input_sample.get(), 0); + if (hr == MF_E_NOTACCEPTING) return true; + CHECK(SUCCEEDED(hr)); + } else { + SendMFTMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM); + } + + // If we already received the input EOS, we do not need to issue + // more requests for new samples. + if (state_ != kEosFlush) + SendEmptyBufferDone(); + } + return true; + } + + CHECK(SUCCEEDED(hr)); + output_sample.Attach(output_data_buffer.pSample); + CHECK(output_sample.get()); + + int64 timestamp, duration; + output_sample->GetSampleTime(×tamp); + output_sample->GetSampleDuration(&duration); + + // The duration and timestamps are in 100-ns units, so divide by 10 + // to convert to microseconds. + timestamp /= 10; + duration /= 10; + + // Sanity checks for checking if there is really something in the sample. + DWORD buf_count; + hr = output_sample->GetBufferCount(&buf_count); + CHECK(SUCCEEDED(hr) && buf_count == 1); + + ScopedComPtr<IMFMediaBuffer> output_buffer; + hr = output_sample->GetBufferByIndex(0, output_buffer.Receive()); + CHECK(SUCCEEDED(hr)); + + ScopedComPtr<IDirect3DSurface9> surface; + hr = MFGetService(output_buffer, MR_BUFFER_SERVICE, + IID_PPV_ARGS(surface.Receive())); + CHECK(SUCCEEDED(hr)); + + + // NV12 to YV12 + D3DLOCKED_RECT d3dlocked_rect; + RECT rect = {0, 0, init_param_.width_, init_param_.height_}; + hr = surface->LockRect(&d3dlocked_rect, &rect, 0); + + if (SUCCEEDED(hr)) { + D3DSURFACE_DESC desc; + hr = surface->GetDesc(&desc); + CHECK(SUCCEEDED(hr)); + + uint32 src_stride = d3dlocked_rect.Pitch; + uint32 dst_stride = init_param_.width_; + uint8* src_y = static_cast<uint8*>(d3dlocked_rect.pBits); + uint8* src_uv = src_y + src_stride * desc.Height; + uint8* dst_y = static_cast<uint8*>(output_transfer_buffer_->memory()); + uint8* dst_u = dst_y + dst_stride * init_param_.height_; + uint8* dst_v = dst_u + dst_stride * init_param_.height_ / 4; + + for ( int y = 0 ; y < init_param_.height_; ++y ) { + for ( int x = 0 ; x < init_param_.width_ ; ++x ) { + dst_y[x] = src_y[x]; + if (!(y & 1)) { + if (x & 1) + dst_v[x>>1] = src_uv[x]; + else + dst_u[x>>1] = src_uv[x]; + } + } + dst_y += dst_stride; + src_y += src_stride; + if (!(y & 1)) { + src_uv += src_stride; + dst_v += dst_stride >> 1; + dst_u += dst_stride >> 1; + } + } + hr = surface->UnlockRect(); + CHECK(SUCCEEDED(hr)); + } + + GpuVideoDecoderOutputBufferParam output_param; + output_param.timestamp_ = timestamp; + output_param.duration_ = duration; + output_param.flags_ = 0; + output_transfer_buffer_busy_ = true; + SendFillBufferDone(output_param); + return true; +} + +#endif + diff --git a/chrome/gpu/gpu_video_decoder_mft.h b/chrome/gpu/gpu_video_decoder_mft.h new file mode 100644 index 0000000..3644617 --- /dev/null +++ b/chrome/gpu/gpu_video_decoder_mft.h @@ -0,0 +1,98 @@ +// 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_GPU_GPU_VIDEO_DECODER_MFT_H_ +#define CHROME_GPU_GPU_VIDEO_DECODER_MFT_H_ + +#include "build/build_config.h" // For OS_WIN. + +#if defined(OS_WIN) + +#include <d3d9.h> +#include <dxva2api.h> +#include <evr.h> +#include <initguid.h> +#include <mfapi.h> +#include <mferror.h> +#include <mfidl.h> +#include <shlwapi.h> +#include <wmcodecdsp.h> + +#include <deque> + +#include "base/scoped_comptr_win.h" +#include "chrome/gpu/gpu_video_decoder.h" + +class GpuVideoDecoderMFT : public GpuVideoDecoder { + public: + virtual bool DoInitialize(const GpuVideoDecoderInitParam& init_param, + GpuVideoDecoderInitDoneParam* done_param); + virtual bool DoUninitialize(); + virtual void DoFlush(); + virtual void DoEmptyThisBuffer(const GpuVideoDecoderInputBufferParam& buffer); + virtual void DoFillThisBuffer(const GpuVideoDecoderOutputBufferParam& frame); + virtual void DoFillThisBufferDoneACK(); + + private: + GpuVideoDecoderMFT(const GpuVideoDecoderInfoParam* param, + GpuChannel* channel_, + base::ProcessHandle handle); + + friend class GpuVideoService; + + // TODO(jiesun): Find a way to move all these to GpuVideoService.. + static bool StartupComLibraries(); + static void ShutdownComLibraries(); + bool CreateD3DDevManager(HWND video_window); + + // helper. + bool InitMediaFoundation(); + bool InitDecoder(); + bool CheckDecoderDxvaSupport(); + + bool SetDecoderMediaTypes(); + bool SetDecoderInputMediaType(); + bool SetDecoderOutputMediaType(const GUID subtype); + bool SendMFTMessage(MFT_MESSAGE_TYPE msg); + bool GetStreamsInfoAndBufferReqs(); + + // Help function to create IMFSample* out of input buffer. + // data are copied into IMFSample's own IMFMediaBuffer. + // Client should Release() the IMFSample*. + static IMFSample* CreateInputSample(uint8* data, + int32 size, + int64 timestamp, + int64 duration, + int32 min_size); + + bool DoDecode(); + + ScopedComPtr<IDirect3D9> d3d9_; + ScopedComPtr<IDirect3DDevice9> device_; + ScopedComPtr<IDirect3DDeviceManager9> device_manager_; + ScopedComPtr<IMFTransform> decoder_; + + MFT_INPUT_STREAM_INFO input_stream_info_; + MFT_OUTPUT_STREAM_INFO output_stream_info_; + + std::deque<ScopedComPtr<IMFSample> > input_buffer_queue_; + bool output_transfer_buffer_busy_; + + typedef enum { + kNormal, // normal playing state. + kFlushing, // upon received Flush(), before FlushDone() + kEosFlush, // upon input EOS received. + kStopped, // upon output EOS received. + } State; + State state_; + + int32 pending_request_; + + DISALLOW_COPY_AND_ASSIGN(GpuVideoDecoderMFT); +}; + +#endif + +#endif // CHROME_GPU_GPU_VIDEO_DECODER_MFT_H_ + diff --git a/chrome/gpu/gpu_video_service.cc b/chrome/gpu/gpu_video_service.cc new file mode 100644 index 0000000..45b7063 --- /dev/null +++ b/chrome/gpu/gpu_video_service.cc @@ -0,0 +1,76 @@ +// 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/common/gpu_messages.h" +#include "chrome/gpu/gpu_channel.h" +#include "chrome/gpu/gpu_video_decoder_mft.h" +#include "chrome/gpu/gpu_video_service.h" + +GpuVideoService::GpuVideoService() : next_available_decoder_id_(0) { + // TODO(jiesun): move this time consuming stuff out of here. + IntializeGpuVideoService(); +} +GpuVideoService::~GpuVideoService() { + // TODO(jiesun): move this time consuming stuff out of here. + UnintializeGpuVideoService(); +} + +void GpuVideoService::OnChannelConnected(int32 peer_pid) { + LOG(ERROR) << "GpuVideoService::OnChannelConnected"; +} + +void GpuVideoService::OnChannelError() { + LOG(ERROR) << "GpuVideoService::OnChannelError"; +} + +void GpuVideoService::OnMessageReceived(const IPC::Message& msg) { +#if 0 + IPC_BEGIN_MESSAGE_MAP(GpuVideoService, msg) + IPC_MESSAGE_UNHANDLED_ERROR() + IPC_END_MESSAGE_MAP() +#endif +} + +bool GpuVideoService::IntializeGpuVideoService() { + return true; +} + +bool GpuVideoService::UnintializeGpuVideoService() { + return true; +} + +bool GpuVideoService::CreateVideoDecoder( + GpuChannel* channel, + MessageRouter* router, + GpuVideoDecoderInfoParam* param) { + // TODO(jiesun): find a better way to determine which GpuVideoDecoder + // to return on current platform. +#if defined(OS_WIN) + GpuVideoDecoderInfo decoder_info; + int32 decoder_id = GetNextAvailableDecoderID(); + param->decoder_id_ = decoder_id; + base::ProcessHandle handle = channel->renderer_handle(); + decoder_info.decoder_ = new GpuVideoDecoderMFT(param, channel, handle); + decoder_info.channel_ = channel; + decoder_info.param = *param; + decoder_map_[decoder_id] = decoder_info; + router->AddRoute(param->decoder_route_id_, decoder_info.decoder_); + return true; +#else + return false; +#endif +} + +void GpuVideoService::DestroyVideoDecoder( + MessageRouter* router, + int32 decoder_id) { + int32 route_id = decoder_map_[decoder_id].param.decoder_route_id_; + router->RemoveRoute(route_id); + decoder_map_.erase(decoder_id); +} + +int32 GpuVideoService::GetNextAvailableDecoderID() { + return ++next_available_decoder_id_; +} + diff --git a/chrome/gpu/gpu_video_service.h b/chrome/gpu/gpu_video_service.h new file mode 100644 index 0000000..3172031 --- /dev/null +++ b/chrome/gpu/gpu_video_service.h @@ -0,0 +1,56 @@ +// 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_GPU_GPU_VIDEO_SERVICE_H_ +#define CHROME_GPU_GPU_VIDEO_SERVICE_H_ + +#include <map> + +#include "base/scoped_ptr.h" +#include "base/ref_counted.h" +#include "base/singleton.h" +#include "chrome/gpu/gpu_video_decoder.h" +#include "ipc/ipc_channel.h" + +class GpuChannel; + +class GpuVideoService : public IPC::Channel::Listener, + public Singleton<GpuVideoService> { + public: + // IPC::Channel::Listener. + virtual void OnChannelConnected(int32 peer_pid); + virtual void OnChannelError(); + virtual void OnMessageReceived(const IPC::Message& message); + + bool CreateVideoDecoder(GpuChannel* channel, + MessageRouter* router, + GpuVideoDecoderInfoParam* param); + void DestroyVideoDecoder(MessageRouter* router, + int32 decoder_id); + + private: + struct GpuVideoDecoderInfo { + scoped_refptr<GpuVideoDecoder> decoder_; + GpuChannel* channel_; + GpuVideoDecoderInfoParam param; + }; + + GpuVideoService(); + virtual ~GpuVideoService(); + + std::map<int32, GpuVideoDecoderInfo> decoder_map_; + int32 next_available_decoder_id_; + + // Specialize video service on different platform will override. + virtual bool IntializeGpuVideoService(); + virtual bool UnintializeGpuVideoService(); + + int32 GetNextAvailableDecoderID(); + + friend struct DefaultSingletonTraits<GpuVideoService>; + DISALLOW_COPY_AND_ASSIGN(GpuVideoService); +}; + +#endif // CHROME_GPU_GPU_VIDEO_SERVICE_H_ + 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(¶m))) { + 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); diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h index 7b1e498..1486a42 100644 --- a/ipc/ipc_message_utils.h +++ b/ipc/ipc_message_utils.h @@ -58,6 +58,8 @@ enum IPCMessageStart { GpuMsgStart, GpuHostMsgStart, GpuChannelMsgStart, + GpuVideoDecoderHostMsgStart, + GpuVideoDecoderMsgStart, ServiceMsgStart, ServiceHostMsgStart, // NOTE: When you add a new message class, also update diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc index 3b684ce..e2612c25 100644 --- a/media/base/media_switches.cc +++ b/media/base/media_switches.cc @@ -11,6 +11,9 @@ namespace switches { const char kAlsaDevice[] = "alsa-device"; #endif +// Enable hardware decoding through gpu process. +const char kEnableAcceleratedDecoding[] = "enable-accelerated-decoding"; + // Enable hardware decoding using OpenMax API. // In practice this is for ChromeOS ARM. const char kEnableOpenMax[] = "enable-openmax"; diff --git a/media/base/media_switches.h b/media/base/media_switches.h index 6e0e0d3..6dd553a 100644 --- a/media/base/media_switches.h +++ b/media/base/media_switches.h @@ -15,6 +15,7 @@ namespace switches { extern const char kAlsaDevice[]; #endif +extern const char kEnableAcceleratedDecoding[]; extern const char kEnableOpenMax[]; extern const char kVideoThreads[]; diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc index 5c2a913..7a74d13 100644 --- a/media/filters/ffmpeg_demuxer.cc +++ b/media/filters/ffmpeg_demuxer.cc @@ -426,8 +426,8 @@ void FFmpegDemuxer::InitializeTask(DataSource* data_source, // Initialize the bitstream if OpenMAX is enabled. // TODO(hclam): Should be enabled by the decoder. - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableOpenMax)) { + CommandLine* cmd = CommandLine::ForCurrentProcess(); + if (cmd->HasSwitch(switches::kEnableAcceleratedDecoding)) { // TODO(ajwong): Unittest this branch of the if statement. // TODO(hclam): In addition to codec we should also check the container. const char* filter_name = NULL; |