diff options
Diffstat (limited to 'chrome/gpu')
-rw-r--r-- | chrome/gpu/gpu_channel.cc | 52 | ||||
-rw-r--r-- | chrome/gpu/gpu_channel.h | 5 | ||||
-rw-r--r-- | chrome/gpu/gpu_video_decoder.cc | 108 | ||||
-rw-r--r-- | chrome/gpu/gpu_video_decoder.h | 76 | ||||
-rw-r--r-- | chrome/gpu/gpu_video_decoder_mft.cc | 594 | ||||
-rw-r--r-- | chrome/gpu/gpu_video_decoder_mft.h | 98 | ||||
-rw-r--r-- | chrome/gpu/gpu_video_service.cc | 76 | ||||
-rw-r--r-- | chrome/gpu/gpu_video_service.h | 56 |
8 files changed, 1065 insertions, 0 deletions
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_ + |