diff options
-rw-r--r-- | content/common/gpu/gpu_command_buffer_stub.cc | 3 | ||||
-rw-r--r-- | content/common/gpu/media/dxva_video_decode_accelerator.cc | 849 | ||||
-rw-r--r-- | content/common/gpu/media/dxva_video_decode_accelerator.h | 221 | ||||
-rw-r--r-- | content/common/gpu/media/gpu_video_decode_accelerator.cc | 37 | ||||
-rw-r--r-- | content/common/gpu/media/gpu_video_decode_accelerator.h | 5 | ||||
-rw-r--r-- | content/common/gpu/media/omx_video_decode_accelerator_unittest.cc (renamed from content/common/gpu/media/video_decode_accelerator_unittest.cc) | 261 | ||||
-rw-r--r-- | content/content_common.gypi | 36 | ||||
-rw-r--r-- | content/content_tests.gypi | 20 | ||||
-rw-r--r-- | content/gpu/gpu_main.cc | 3 |
9 files changed, 98 insertions, 1337 deletions
diff --git a/content/common/gpu/gpu_command_buffer_stub.cc b/content/common/gpu/gpu_command_buffer_stub.cc index b1d340c..8c3ae57 100644 --- a/content/common/gpu/gpu_command_buffer_stub.cc +++ b/content/common/gpu/gpu_command_buffer_stub.cc @@ -461,8 +461,7 @@ void GpuCommandBufferStub::OnCreateVideoDecoder( new GpuVideoDecodeAccelerator(this, decoder_route_id, this); video_decoders_.AddWithID(decoder, decoder_route_id); channel_->AddRoute(decoder_route_id, decoder); - decoder->Initialize(profile, reply_message, - channel_->renderer_process()); + decoder->Initialize(profile, reply_message); } void GpuCommandBufferStub::OnDestroyVideoDecoder(int decoder_route_id) { diff --git a/content/common/gpu/media/dxva_video_decode_accelerator.cc b/content/common/gpu/media/dxva_video_decode_accelerator.cc deleted file mode 100644 index 02c4b5f..0000000 --- a/content/common/gpu/media/dxva_video_decode_accelerator.cc +++ /dev/null @@ -1,849 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/common/gpu/media/dxva_video_decode_accelerator.h" - -#if !defined(OS_WIN) -#error This file should only be built on Windows. -#endif // !defined(OS_WIN) - -#include <ks.h> -#include <codecapi.h> -#include <d3dx9tex.h> -#include <mfapi.h> -#include <mferror.h> -#include <wmcodecdsp.h> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/debug/trace_event.h" -#include "base/logging.h" -#include "base/memory/scoped_handle.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "base/process_util.h" -#include "base/shared_memory.h" -#include "media/video/video_decode_accelerator.h" -#include "third_party/angle/include/GLES2/gl2.h" -#include "third_party/angle/include/GLES2/gl2ext.h" - -// We only request 5 picture buffers from the client which are used to hold the -// decoded samples. These buffers are then reused when the client tells us that -// it is done with the buffer. -static const int kNumPictureBuffers = 5; - -bool DXVAVideoDecodeAccelerator::pre_sandbox_init_done_ = false; -uint32 DXVAVideoDecodeAccelerator::dev_manager_reset_token_ = 0; -IDirect3DDeviceManager9* DXVAVideoDecodeAccelerator::device_manager_ = NULL; -IDirect3DDevice9Ex* DXVAVideoDecodeAccelerator::device_ = NULL; - -#define RETURN_ON_FAILURE(result, log, ret) \ - do { \ - if (!(result)) { \ - DLOG(ERROR) << log; \ - return ret; \ - } \ - } while (0) - -#define RETURN_ON_HR_FAILURE(result, log, ret) \ - RETURN_ON_FAILURE(SUCCEEDED(result), \ - log << ", HRESULT: 0x" << std::hex << result, \ - ret); - -#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \ - do { \ - if (!(result)) { \ - DVLOG(1) << log; \ - StopOnError(error_code); \ - return ret; \ - } \ - } while (0) - -#define RETURN_AND_NOTIFY_ON_HR_FAILURE(result, log, error_code, ret) \ - RETURN_AND_NOTIFY_ON_FAILURE(SUCCEEDED(result), \ - log << ", HRESULT: 0x" << std::hex << result, \ - error_code, ret); - -static IMFSample* CreateEmptySample() { - base::win::ScopedComPtr<IMFSample> sample; - HRESULT hr = MFCreateSample(sample.Receive()); - RETURN_ON_HR_FAILURE(hr, "MFCreateSample failed", NULL); - return sample.Detach(); -} - -// Creates a Media Foundation sample with one buffer of length |buffer_length| -// on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0. -static IMFSample* CreateEmptySampleWithBuffer(int buffer_length, int align) { - CHECK_GT(buffer_length, 0); - - base::win::ScopedComPtr<IMFSample> sample; - sample.Attach(CreateEmptySample()); - - base::win::ScopedComPtr<IMFMediaBuffer> buffer; - HRESULT hr = E_FAIL; - if (align == 0) { - // Note that MFCreateMemoryBuffer is same as MFCreateAlignedMemoryBuffer - // with the align argument being 0. - hr = MFCreateMemoryBuffer(buffer_length, buffer.Receive()); - } else { - hr = MFCreateAlignedMemoryBuffer(buffer_length, - align - 1, - buffer.Receive()); - } - RETURN_ON_HR_FAILURE(hr, "Failed to create memory buffer for sample", NULL); - - hr = sample->AddBuffer(buffer); - RETURN_ON_HR_FAILURE(hr, "Failed to add buffer to sample", NULL); - - return sample.Detach(); -} - -// 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). If no alignment is required, provide 0. -static IMFSample* CreateInputSample(const uint8* stream, int size, - int min_size, int alignment) { - CHECK(stream); - CHECK_GT(size, 0); - base::win::ScopedComPtr<IMFSample> sample; - sample.Attach(CreateEmptySampleWithBuffer(std::max(min_size, size), - alignment)); - RETURN_ON_FAILURE(sample, "Failed to create empty sample", NULL); - - base::win::ScopedComPtr<IMFMediaBuffer> buffer; - HRESULT hr = sample->GetBufferByIndex(0, buffer.Receive()); - RETURN_ON_HR_FAILURE(hr, "Failed to get buffer from sample", NULL); - - DWORD max_length = 0; - DWORD current_length = 0; - uint8* destination = NULL; - hr = buffer->Lock(&destination, &max_length, ¤t_length); - RETURN_ON_HR_FAILURE(hr, "Failed to lock buffer", NULL); - - CHECK_EQ(current_length, 0u); - CHECK_GE(static_cast<int>(max_length), size); - memcpy(destination, stream, size); - - hr = buffer->Unlock(); - RETURN_ON_HR_FAILURE(hr, "Failed to unlock buffer", NULL); - - hr = buffer->SetCurrentLength(size); - RETURN_ON_HR_FAILURE(hr, "Failed to set buffer length", NULL); - - return sample.Detach(); -} - -static IMFSample* CreateSampleFromInputBuffer( - const media::BitstreamBuffer& bitstream_buffer, - base::ProcessHandle renderer_process, - DWORD stream_size, - DWORD alignment) { - HANDLE shared_memory_handle = NULL; - RETURN_ON_FAILURE(::DuplicateHandle(renderer_process, - bitstream_buffer.handle(), - base::GetCurrentProcessHandle(), - &shared_memory_handle, - 0, - FALSE, - DUPLICATE_SAME_ACCESS), - "Duplicate handle failed", NULL); - - base::SharedMemory shm(shared_memory_handle, true); - RETURN_ON_FAILURE(shm.Map(bitstream_buffer.size()), - "Failed in base::SharedMemory::Map", NULL); - - return CreateInputSample(reinterpret_cast<const uint8*>(shm.memory()), - bitstream_buffer.size(), - stream_size, - alignment); -} - -DXVAVideoDecodeAccelerator::DXVAPictureBuffer::DXVAPictureBuffer( - const media::PictureBuffer& buffer) - : available(true), - picture_buffer(buffer) { -} - -DXVAVideoDecodeAccelerator::PendingSampleInfo::PendingSampleInfo( - int32 buffer_id, IDirect3DSurface9* surface) - : input_buffer_id(buffer_id), - dest_surface(surface) { -} - -DXVAVideoDecodeAccelerator::PendingSampleInfo::~PendingSampleInfo() {} - -// static -void DXVAVideoDecodeAccelerator::PreSandboxInitialization() { - // Should be called only once during program startup. - DCHECK(!pre_sandbox_init_done_); - - static wchar_t* decoding_dlls[] = { - L"d3d9.dll", - L"d3dx9_43.dll", - L"dxva2.dll", - L"mf.dll", - L"mfplat.dll", - L"msmpeg2vdec.dll", - }; - - for (int i = 0; i < arraysize(decoding_dlls); ++i) { - if (!::LoadLibrary(decoding_dlls[i])) { - DLOG(ERROR) << "Failed to load decoder dll: " << decoding_dlls[i] - << ", Error: " << ::GetLastError(); - return; - } - } - - RETURN_ON_FAILURE(CreateD3DDevManager(), - "Failed to initialize D3D device and manager",); - pre_sandbox_init_done_ = true; -} - -// static -bool DXVAVideoDecodeAccelerator::CreateD3DDevManager() { - base::win::ScopedComPtr<IDirect3D9Ex> d3d9; - - HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, d3d9.Receive()); - RETURN_ON_HR_FAILURE(hr, "Direct3DCreate9Ex failed", false); - - D3DPRESENT_PARAMETERS present_params = {0}; - present_params.BackBufferWidth = 1; - present_params.BackBufferHeight = 1; - present_params.BackBufferFormat = D3DFMT_UNKNOWN; - present_params.BackBufferCount = 1; - present_params.SwapEffect = D3DSWAPEFFECT_DISCARD; - present_params.hDeviceWindow = ::GetShellWindow(); - present_params.Windowed = TRUE; - present_params.Flags = D3DPRESENTFLAG_VIDEO; - present_params.FullScreen_RefreshRateInHz = 0; - present_params.PresentationInterval = 0; - - hr = d3d9->CreateDeviceEx(D3DADAPTER_DEFAULT, - D3DDEVTYPE_HAL, - ::GetShellWindow(), - D3DCREATE_SOFTWARE_VERTEXPROCESSING, - &present_params, - NULL, - &device_); - RETURN_ON_HR_FAILURE(hr, "Failed to create D3D device", false); - - hr = DXVA2CreateDirect3DDeviceManager9(&dev_manager_reset_token_, - &device_manager_); - RETURN_ON_HR_FAILURE(hr, "DXVA2CreateDirect3DDeviceManager9 failed", false); - - hr = device_manager_->ResetDevice(device_, dev_manager_reset_token_); - RETURN_ON_HR_FAILURE(hr, "Failed to reset device", false); - return true; -} - -DXVAVideoDecodeAccelerator::DXVAVideoDecodeAccelerator( - media::VideoDecodeAccelerator::Client* client, - base::ProcessHandle renderer_process) - : client_(client), - state_(kUninitialized), - pictures_requested_(false), - renderer_process_(renderer_process), - last_input_buffer_id_(-1), - inputs_before_decode_(0) { -} - -DXVAVideoDecodeAccelerator::~DXVAVideoDecodeAccelerator() { - client_ = NULL; -} - -bool DXVAVideoDecodeAccelerator::Initialize(Profile) { - DCHECK(CalledOnValidThread()); - - RETURN_AND_NOTIFY_ON_FAILURE(pre_sandbox_init_done_, - "PreSandbox initialization not completed", PLATFORM_FAILURE, false); - - RETURN_AND_NOTIFY_ON_FAILURE((state_ == kUninitialized), - "Initialize: invalid state: " << state_, ILLEGAL_STATE, false); - - HRESULT hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); - RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "MFStartup failed.", PLATFORM_FAILURE, - false); - - RETURN_AND_NOTIFY_ON_FAILURE(InitDecoder(), - "Failed to initialize decoder", PLATFORM_FAILURE, false); - - RETURN_AND_NOTIFY_ON_FAILURE(GetStreamsInfoAndBufferReqs(), - "Failed to get input/output stream info.", PLATFORM_FAILURE, false); - - RETURN_AND_NOTIFY_ON_FAILURE( - SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0), - "Failed to start decoder", PLATFORM_FAILURE, false); - - state_ = kNormal; - MessageLoop::current()->PostTask(FROM_HERE, - base::Bind(&DXVAVideoDecodeAccelerator::NotifyInitializeDone, this)); - return true; -} - -void DXVAVideoDecodeAccelerator::Decode( - const media::BitstreamBuffer& bitstream_buffer) { - DCHECK(CalledOnValidThread()); - - RETURN_AND_NOTIFY_ON_FAILURE((state_ == kNormal || state_ == kStopped), - "Invalid state: " << state_, ILLEGAL_STATE,); - - base::win::ScopedComPtr<IMFSample> sample; - sample.Attach(CreateSampleFromInputBuffer(bitstream_buffer, - renderer_process_, - input_stream_info_.cbSize, - input_stream_info_.cbAlignment)); - RETURN_AND_NOTIFY_ON_FAILURE(sample, "Failed to create input sample", - PLATFORM_FAILURE,); - if (!inputs_before_decode_) { - TRACE_EVENT_BEGIN_ETW("DXVAVideoDecodeAccelerator.Decoding", this, ""); - } - inputs_before_decode_++; - - RETURN_AND_NOTIFY_ON_FAILURE( - SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0), - "Failed to create input sample", PLATFORM_FAILURE,); - - HRESULT hr = decoder_->ProcessInput(0, sample, 0); - RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to process input sample", - PLATFORM_FAILURE,); - - RETURN_AND_NOTIFY_ON_FAILURE( - SendMFTMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0), - "Failed to send eos message to MFT", PLATFORM_FAILURE,); - state_ = kEosDrain; - - last_input_buffer_id_ = bitstream_buffer.id(); - - DoDecode(); - - RETURN_AND_NOTIFY_ON_FAILURE((state_ == kStopped || state_ == kNormal), - "Failed to process output. Unexpected decoder state: " << state_, - ILLEGAL_STATE,); - - // The Microsoft Media foundation decoder internally buffers up to 30 frames - // before returning a decoded frame. We need to inform the client that this - // input buffer is processed as it may stop sending us further input. - // Note: This may break clients which expect every input buffer to be - // associated with a decoded output buffer. - // TODO(ananta) - // Do some more investigation into whether it is possible to get the MFT - // decoder to emit an output packet for every input packet. - // http://code.google.com/p/chromium/issues/detail?id=108121 - MessageLoop::current()->PostTask(FROM_HERE, base::Bind( - &DXVAVideoDecodeAccelerator::NotifyInputBufferRead, this, - bitstream_buffer.id())); -} - -void DXVAVideoDecodeAccelerator::AssignPictureBuffers( - const std::vector<media::PictureBuffer>& buffers) { - DCHECK(CalledOnValidThread()); - // Copy the picture buffers provided by the client to the available list, - // and mark these buffers as available for use. - for (size_t buffer_index = 0; buffer_index < buffers.size(); - ++buffer_index) { - bool inserted = output_picture_buffers_.insert(std::make_pair( - buffers[buffer_index].id(), - DXVAPictureBuffer(buffers[buffer_index]))).second; - DCHECK(inserted); - } - ProcessPendingSamples(); -} - -void DXVAVideoDecodeAccelerator::ReusePictureBuffer( - int32 picture_buffer_id) { - DCHECK(CalledOnValidThread()); - - OutputBuffers::iterator it = output_picture_buffers_.find(picture_buffer_id); - RETURN_AND_NOTIFY_ON_FAILURE(it != output_picture_buffers_.end(), - "Invalid picture id: " << picture_buffer_id, INVALID_ARGUMENT,); - - it->second.available = true; - ProcessPendingSamples(); -} - -void DXVAVideoDecodeAccelerator::Flush() { - DCHECK(CalledOnValidThread()); - - DVLOG(1) << "DXVAVideoDecodeAccelerator::Flush"; - - RETURN_AND_NOTIFY_ON_FAILURE((state_ == kNormal || state_ == kStopped), - "Unexpected decoder state: " << state_, ILLEGAL_STATE,); - - state_ = kEosDrain; - - RETURN_AND_NOTIFY_ON_FAILURE(SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0), - "Failed to send drain message", PLATFORM_FAILURE,); - - // As per MSDN docs after the client sends this message, it calls - // IMFTransform::ProcessOutput in a loop, until ProcessOutput returns the - // error code MF_E_TRANSFORM_NEED_MORE_INPUT. The DoDecode function sets - // the state to kStopped when the decoder returns - // MF_E_TRANSFORM_NEED_MORE_INPUT. - // The MFT decoder can buffer upto 30 frames worth of input before returning - // an output frame. This loop here attempts to retrieve as many output frames - // as possible from the buffered set. - while (state_ != kStopped) { - DoDecode(); - } - - MessageLoop::current()->PostTask(FROM_HERE, base::Bind( - &DXVAVideoDecodeAccelerator::NotifyFlushDone, this)); - - state_ = kNormal; -} - -void DXVAVideoDecodeAccelerator::Reset() { - DCHECK(CalledOnValidThread()); - - DVLOG(1) << "DXVAVideoDecodeAccelerator::Reset"; - - RETURN_AND_NOTIFY_ON_FAILURE((state_ == kNormal || state_ == kStopped), - "Reset: invalid state: " << state_, ILLEGAL_STATE,); - - state_ = kResetting; - - RETURN_AND_NOTIFY_ON_FAILURE(SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH, 0), - "Reset: Failed to send message.", PLATFORM_FAILURE,); - - MessageLoop::current()->PostTask(FROM_HERE, base::Bind( - &DXVAVideoDecodeAccelerator::NotifyResetDone, this)); - - state_ = DXVAVideoDecodeAccelerator::kNormal; -} - -void DXVAVideoDecodeAccelerator::Destroy() { - DCHECK(CalledOnValidThread()); - Invalidate(); -} - -bool DXVAVideoDecodeAccelerator::InitDecoder() { - // We cannot use CoCreateInstance to instantiate the decoder object as that - // fails in the sandbox. We mimic the steps CoCreateInstance uses to - // instantiate the object. - HMODULE decoder_dll = ::GetModuleHandle(L"msmpeg2vdec.dll"); - RETURN_ON_FAILURE(decoder_dll, - "msmpeg2vdec.dll required for decoding is not loaded", - false); - - typedef HRESULT (WINAPI* GetClassObject)(const CLSID& clsid, - const IID& iid, - void** object); - - GetClassObject get_class_object = reinterpret_cast<GetClassObject>( - GetProcAddress(decoder_dll, "DllGetClassObject")); - RETURN_ON_FAILURE(get_class_object, - "Failed to get DllGetClassObject pointer", false); - - base::win::ScopedComPtr<IClassFactory> factory; - HRESULT hr = get_class_object(__uuidof(CMSH264DecoderMFT), - __uuidof(IClassFactory), - reinterpret_cast<void**>(factory.Receive())); - RETURN_ON_HR_FAILURE(hr, "DllGetClassObject for decoder failed", false); - - hr = factory->CreateInstance(NULL, __uuidof(IMFTransform), - reinterpret_cast<void**>(decoder_.Receive())); - RETURN_ON_HR_FAILURE(hr, "Failed to create decoder instance", false); - - RETURN_ON_FAILURE(CheckDecoderDxvaSupport(), - "Failed to check decoder DXVA support", false); - - hr = decoder_->ProcessMessage( - MFT_MESSAGE_SET_D3D_MANAGER, - reinterpret_cast<ULONG_PTR>(device_manager_)); - RETURN_ON_HR_FAILURE(hr, "Failed to pass D3D manager to decoder", false); - - return SetDecoderMediaTypes(); -} - -bool DXVAVideoDecodeAccelerator::CheckDecoderDxvaSupport() { - base::win::ScopedComPtr<IMFAttributes> attributes; - HRESULT hr = decoder_->GetAttributes(attributes.Receive()); - RETURN_ON_HR_FAILURE(hr, "Failed to get decoder attributes", false); - - UINT32 dxva = 0; - hr = attributes->GetUINT32(MF_SA_D3D_AWARE, &dxva); - RETURN_ON_HR_FAILURE(hr, "Failed to check if decoder supports DXVA", false); - - hr = attributes->SetUINT32(CODECAPI_AVDecVideoAcceleration_H264, TRUE); - RETURN_ON_HR_FAILURE(hr, "Failed to enable DXVA H/W decoding", false); - return true; -} - -bool DXVAVideoDecodeAccelerator::SetDecoderMediaTypes() { - RETURN_ON_FAILURE(SetDecoderInputMediaType(), - "Failed to set decoder input media type", false); - return SetDecoderOutputMediaType(MFVideoFormat_NV12); -} - -bool DXVAVideoDecodeAccelerator::SetDecoderInputMediaType() { - base::win::ScopedComPtr<IMFMediaType> media_type; - HRESULT hr = MFCreateMediaType(media_type.Receive()); - RETURN_ON_HR_FAILURE(hr, "MFCreateMediaType failed", false); - - hr = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); - RETURN_ON_HR_FAILURE(hr, "Failed to set major input type", false); - - hr = media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264); - RETURN_ON_HR_FAILURE(hr, "Failed to set subtype", false); - - hr = decoder_->SetInputType(0, media_type, 0); // No flags - RETURN_ON_HR_FAILURE(hr, "Failed to set decoder input type", false); - return true; -} - -bool DXVAVideoDecodeAccelerator::SetDecoderOutputMediaType( - const GUID& subtype) { - base::win::ScopedComPtr<IMFMediaType> out_media_type; - - for (uint32 i = 0; - SUCCEEDED(decoder_->GetOutputAvailableType(0, i, - out_media_type.Receive())); - ++i) { - GUID out_subtype = {0}; - HRESULT hr = out_media_type->GetGUID(MF_MT_SUBTYPE, &out_subtype); - RETURN_ON_HR_FAILURE(hr, "Failed to get output major type", false); - - if (out_subtype == subtype) { - hr = decoder_->SetOutputType(0, out_media_type, 0); // No flags - RETURN_ON_HR_FAILURE(hr, "Failed to set decoder output type", false); - return true; - } - out_media_type.Release(); - } - return false; -} - -bool DXVAVideoDecodeAccelerator::SendMFTMessage(MFT_MESSAGE_TYPE msg, - int32 param) { - HRESULT hr = decoder_->ProcessMessage(msg, param); - return SUCCEEDED(hr); -} - -// Gets the minimum buffer sizes for input and output samples. The MFT will not -// allocate buffer for input nor output, so we have to do it ourselves and make -// sure they're the correct size. We only provide decoding if DXVA is enabled. -bool DXVAVideoDecodeAccelerator::GetStreamsInfoAndBufferReqs() { - HRESULT hr = decoder_->GetInputStreamInfo(0, &input_stream_info_); - RETURN_ON_HR_FAILURE(hr, "Failed to get input stream info", false); - - hr = decoder_->GetOutputStreamInfo(0, &output_stream_info_); - RETURN_ON_HR_FAILURE(hr, "Failed to get decoder output stream info", false); - - DVLOG(1) << "Input stream info: "; - DVLOG(1) << "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) - CHECK_EQ(input_stream_info_.dwFlags, 0x7u); - - DVLOG(1) << "Min buffer size: " << input_stream_info_.cbSize; - DVLOG(1) << "Max lookahead: " << input_stream_info_.cbMaxLookahead; - DVLOG(1) << "Alignment: " << input_stream_info_.cbAlignment; - - DVLOG(1) << "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. - DVLOG(1) << "Flags: " - << std::hex << std::showbase << output_stream_info_.dwFlags; - CHECK_EQ(output_stream_info_.dwFlags, 0x107u); - DVLOG(1) << "Min buffer size: " << output_stream_info_.cbSize; - DVLOG(1) << "Alignment: " << output_stream_info_.cbAlignment; - return true; -} - -void DXVAVideoDecodeAccelerator::DoDecode() { - // This function is also called from Flush in a loop which could result - // in the state transitioning to kNormal due to decoded output. - RETURN_AND_NOTIFY_ON_FAILURE((state_ == kNormal || state_ == kEosDrain), - "DoDecode: not in normal/drain state", ILLEGAL_STATE,); - - MFT_OUTPUT_DATA_BUFFER output_data_buffer = {0}; - DWORD status = 0; - - 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) { - VLOG(1) << "Got events from ProcessOuput, but discarding"; - events->Release(); - } - if (FAILED(hr)) { - // A stream change needs further ProcessInput calls to get back decoder - // output which is why we need to set the state to stopped. - if (hr == MF_E_TRANSFORM_STREAM_CHANGE) { - if (!SetDecoderOutputMediaType(MFVideoFormat_NV12)) { - // Decoder didn't let us set NV12 output format. Not sure as to why - // this can happen. Give up in disgust. - NOTREACHED() << "Failed to set decoder output media type to NV12"; - state_ = kStopped; - } else { - DVLOG(1) << "Received output format change from the decoder." - " Recursively invoking DoDecode"; - DoDecode(); - } - return; - } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) { - // No more output from the decoder. Stop playback. - state_ = kStopped; - return; - } else { - NOTREACHED() << "Unhandled error in DoDecode()"; - return; - } - } - TRACE_EVENT_END_ETW("DXVAVideoDecodeAccelerator.Decoding", this, ""); - - TRACE_COUNTER1("DXVA Decoding", "TotalPacketsBeforeDecode", - inputs_before_decode_); - - inputs_before_decode_ = 0; - - RETURN_AND_NOTIFY_ON_FAILURE(ProcessOutputSample(output_data_buffer.pSample), - "Failed to process output sample.", PLATFORM_FAILURE,); - - state_ = kNormal; -} - -bool DXVAVideoDecodeAccelerator::ProcessOutputSample(IMFSample* sample) { - RETURN_ON_FAILURE(sample, "Decode succeeded with NULL output sample", false); - - base::win::ScopedComPtr<IMFSample> output_sample; - output_sample.Attach(sample); - - base::win::ScopedComPtr<IMFMediaBuffer> output_buffer; - HRESULT hr = sample->GetBufferByIndex(0, output_buffer.Receive()); - RETURN_ON_HR_FAILURE(hr, "Failed to get buffer from output sample", false); - - base::win::ScopedComPtr<IDirect3DSurface9> surface; - hr = MFGetService(output_buffer, MR_BUFFER_SERVICE, - IID_PPV_ARGS(surface.Receive())); - RETURN_ON_HR_FAILURE(hr, "Failed to get D3D surface from output sample", - false); - - D3DSURFACE_DESC surface_desc; - hr = surface->GetDesc(&surface_desc); - RETURN_ON_HR_FAILURE(hr, "Failed to get surface description", false); - - TRACE_EVENT_BEGIN_ETW("DXVAVideoDecodeAccelerator.SurfaceCreation", this, - ""); - // TODO(ananta) - // The code below may not be necessary once we have an ANGLE extension which - // allows us to pass the Direct 3D surface directly for rendering. - - // The decoded bits in the source direct 3d surface are in the YUV - // format. Angle does not support that. As a workaround we create an - // offscreen surface in the RGB format and copy the source surface - // to this surface. - base::win::ScopedComPtr<IDirect3DSurface9> dest_surface; - hr = device_->CreateOffscreenPlainSurface(surface_desc.Width, - surface_desc.Height, - D3DFMT_A8R8G8B8, - D3DPOOL_DEFAULT, - dest_surface.Receive(), - NULL); - RETURN_ON_HR_FAILURE(hr, "Failed to create offscreen surface", false); - - hr = D3DXLoadSurfaceFromSurface(dest_surface, NULL, NULL, surface, NULL, - NULL, D3DX_DEFAULT, 0); - RETURN_ON_HR_FAILURE(hr, "D3DXLoadSurfaceFromSurface failed", false); - - TRACE_EVENT_END_ETW("DXVAVideoDecodeAccelerator.SurfaceCreation", this, ""); - - pending_output_samples_.push_back( - PendingSampleInfo(last_input_buffer_id_, dest_surface)); - - // If we have available picture buffers to copy the output data then use the - // first one and then flag it as not being available for use. - if (output_picture_buffers_.size()) { - ProcessPendingSamples(); - return true; - } - if (pictures_requested_) { - DVLOG(1) << "Waiting for picture slots from the client."; - return true; - } - // Go ahead and request picture buffers. - MessageLoop::current()->PostTask(FROM_HERE, base::Bind( - &DXVAVideoDecodeAccelerator::RequestPictureBuffers, - this, surface_desc.Width, surface_desc.Height)); - - pictures_requested_ = true; - return true; -} - -bool DXVAVideoDecodeAccelerator::CopyOutputSampleDataToPictureBuffer( - IDirect3DSurface9* dest_surface, media::PictureBuffer picture_buffer, - int input_buffer_id) { - DCHECK(dest_surface); - - D3DSURFACE_DESC surface_desc; - HRESULT hr = dest_surface->GetDesc(&surface_desc); - RETURN_ON_HR_FAILURE(hr, "Failed to get surface description", false); - - scoped_array<char> bits; - RETURN_ON_FAILURE(GetBitmapFromSurface(dest_surface, &bits), - "Failed to get bitmap from surface for rendering", false); - - // This function currently executes in the context of IPC handlers in the - // GPU process which ensures that there is always a OpenGL context. - GLint current_texture = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); - - glBindTexture(GL_TEXTURE_2D, picture_buffer.texture_id()); - glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, surface_desc.Width, - surface_desc.Height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, - reinterpret_cast<GLvoid*>(bits.get())); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - glBindTexture(GL_TEXTURE_2D, current_texture); - - media::Picture output_picture(picture_buffer.id(), input_buffer_id); - MessageLoop::current()->PostTask(FROM_HERE, base::Bind( - &DXVAVideoDecodeAccelerator::NotifyPictureReady, this, output_picture)); - return true; -} - -void DXVAVideoDecodeAccelerator::ProcessPendingSamples() { - if (pending_output_samples_.empty()) - return; - - OutputBuffers::iterator index; - - for (index = output_picture_buffers_.begin(); - index != output_picture_buffers_.end() && - !pending_output_samples_.empty(); - ++index) { - if (index->second.available) { - PendingSampleInfo sample_info = pending_output_samples_.front(); - - CopyOutputSampleDataToPictureBuffer(sample_info.dest_surface, - index->second.picture_buffer, - sample_info.input_buffer_id); - index->second.available = false; - pending_output_samples_.pop_front(); - } - } -} - -void DXVAVideoDecodeAccelerator::ClearState() { - last_input_buffer_id_ = -1; - output_picture_buffers_.clear(); - pending_output_samples_.clear(); -} - -void DXVAVideoDecodeAccelerator::StopOnError( - media::VideoDecodeAccelerator::Error error) { - DCHECK(CalledOnValidThread()); - - if (client_) - client_->NotifyError(error); - client_ = NULL; - - if (state_ != kUninitialized) { - Invalidate(); - } -} - -bool DXVAVideoDecodeAccelerator::GetBitmapFromSurface( - IDirect3DSurface9* surface, - scoped_array<char>* bits) { - // Get the currently loaded bitmap from the DC. - HDC hdc = NULL; - HRESULT hr = surface->GetDC(&hdc); - RETURN_ON_HR_FAILURE(hr, "Failed to get HDC from surface", false); - - HBITMAP bitmap = - reinterpret_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); - if (!bitmap) { - NOTREACHED() << "Failed to get bitmap from DC"; - surface->ReleaseDC(hdc); - return false; - } - // TODO(ananta) - // The code below may not be necessary once we have an ANGLE extension which - // allows us to pass the Direct 3D surface directly for rendering. - // The Device dependent bitmap is upside down for OpenGL. We convert the - // bitmap to a DIB and render it on the texture instead. - BITMAP bitmap_basic_info = {0}; - if (!GetObject(bitmap, sizeof(BITMAP), &bitmap_basic_info)) { - NOTREACHED() << "Failed to read bitmap info"; - surface->ReleaseDC(hdc); - return false; - } - BITMAPINFO bitmap_info = {0}; - bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bitmap_info.bmiHeader.biWidth = bitmap_basic_info.bmWidth; - bitmap_info.bmiHeader.biHeight = bitmap_basic_info.bmHeight; - bitmap_info.bmiHeader.biPlanes = 1; - bitmap_info.bmiHeader.biBitCount = bitmap_basic_info.bmBitsPixel; - bitmap_info.bmiHeader.biCompression = BI_RGB; - bitmap_info.bmiHeader.biSizeImage = 0; - bitmap_info.bmiHeader.biClrUsed = 0; - - int ret = GetDIBits(hdc, bitmap, 0, 0, NULL, &bitmap_info, DIB_RGB_COLORS); - if (!ret || bitmap_info.bmiHeader.biSizeImage <= 0) { - NOTREACHED() << "Failed to read bitmap size"; - surface->ReleaseDC(hdc); - return false; - } - - bits->reset(new char[bitmap_info.bmiHeader.biSizeImage]); - ret = GetDIBits(hdc, bitmap, 0, bitmap_basic_info.bmHeight, bits->get(), - &bitmap_info, DIB_RGB_COLORS); - if (!ret) { - NOTREACHED() << "Failed to retrieve bitmap bits."; - } - surface->ReleaseDC(hdc); - return !!ret; -} - -void DXVAVideoDecodeAccelerator::Invalidate() { - if (state_ == kUninitialized) - return; - ClearState(); - decoder_.Release(); - MFShutdown(); - state_ = kUninitialized; -} - -void DXVAVideoDecodeAccelerator::NotifyInitializeDone() { - if (client_) - client_->NotifyInitializeDone(); -} - -void DXVAVideoDecodeAccelerator::NotifyInputBufferRead(int input_buffer_id) { - if (client_) - client_->NotifyEndOfBitstreamBuffer(input_buffer_id); -} - -void DXVAVideoDecodeAccelerator::NotifyFlushDone() { - if (client_) - client_->NotifyFlushDone(); -} - -void DXVAVideoDecodeAccelerator::NotifyResetDone() { - if (client_) - client_->NotifyResetDone(); -} - -void DXVAVideoDecodeAccelerator::RequestPictureBuffers(int width, int height) { - // This task could execute after the decoder has been torn down. - if (state_ != kUninitialized && client_) { - client_->ProvidePictureBuffers(kNumPictureBuffers, - gfx::Size(width, height)); - } -} - -void DXVAVideoDecodeAccelerator::NotifyPictureReady( - const media::Picture& picture) { - // This task could execute after the decoder has been torn down. - if (state_ != kUninitialized && client_) - client_->PictureReady(picture); -} - diff --git a/content/common/gpu/media/dxva_video_decode_accelerator.h b/content/common/gpu/media/dxva_video_decode_accelerator.h deleted file mode 100644 index 0e0b0c4..0000000 --- a/content/common/gpu/media/dxva_video_decode_accelerator.h +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_COMMON_GPU_MEDIA_DXVA_VIDEO_DECODE_ACCELERATOR_H_ -#define CONTENT_COMMON_GPU_MEDIA_DXVA_VIDEO_DECODE_ACCELERATOR_H_ - -#include <d3d9.h> -#include <dxva2api.h> -#include <list> -#include <map> -#include <mfidl.h> -#include <vector> - -#include "base/threading/non_thread_safe.h" -#include "base/win/scoped_comptr.h" -#include "media/video/video_decode_accelerator.h" -#include "third_party/angle/include/EGL/egl.h" -#include "third_party/angle/include/EGL/eglext.h" - -interface IMFSample; -interface IDirect3DSurface9; - -// Class to provide a DXVA 2.0 based accelerator using the Microsoft Media -// foundation APIs via the VideoDecodeAccelerator interface. -// This class lives on a single thread and DCHECKs that it is never accessed -// from any other. -class DXVAVideoDecodeAccelerator : public media::VideoDecodeAccelerator, - public base::NonThreadSafe { - public: - enum State { - kUninitialized, // un-initialized. - kNormal, // normal playing state. - kResetting, // upon received Reset(), before ResetDone() - kEosDrain, // upon input EOS received. - kStopped, // upon output EOS received. - }; - - // Does not take ownership of |client| which must outlive |*this|. - DXVAVideoDecodeAccelerator( - media::VideoDecodeAccelerator::Client* client, - base::ProcessHandle renderer_process); - virtual ~DXVAVideoDecodeAccelerator(); - - // media::VideoDecodeAccelerator implementation. - virtual bool Initialize(Profile) OVERRIDE; - virtual void Decode(const media::BitstreamBuffer& bitstream_buffer) OVERRIDE; - virtual void AssignPictureBuffers( - const std::vector<media::PictureBuffer>& buffers) OVERRIDE; - virtual void ReusePictureBuffer(int32 picture_buffer_id) OVERRIDE; - virtual void Flush() OVERRIDE; - virtual void Reset() OVERRIDE; - virtual void Destroy() OVERRIDE; - - // Initialization work needed before the process is sandboxed. - // This includes:- - // 1. Loads the dlls like mf/mfplat/d3d9, etc required for decoding. - // 2. Setting up the device manager instance which is shared between all - // decoder instances. - static void PreSandboxInitialization(); - - private: - // Creates and initializes an instance of the D3D device and the - // corresponding device manager. The device manager instance is eventually - // passed to the IMFTransform interface implemented by the h.264 decoder. - static bool CreateD3DDevManager(); - - // Creates, initializes and sets the media types for the h.264 decoder. - bool InitDecoder(); - - // Validates whether the h.264 decoder supports hardware video acceleration. - bool CheckDecoderDxvaSupport(); - - // Returns information about the input and output streams. This includes - // alignment information, decoder support flags, minimum sample size, etc. - bool GetStreamsInfoAndBufferReqs(); - - // Registers the input and output media types on the h.264 decoder. This - // includes the expected input and output formats. - bool SetDecoderMediaTypes(); - - // Registers the input media type for the h.264 decoder. - bool SetDecoderInputMediaType(); - - // Registers the output media type for the h.264 decoder. - bool SetDecoderOutputMediaType(const GUID& subtype); - - // Passes a command message to the decoder. This includes commands like - // start of stream, end of stream, flush, drain the decoder, etc. - bool SendMFTMessage(MFT_MESSAGE_TYPE msg, int32 param); - - // The bulk of the decoding happens here. This function handles errors, - // format changes and processes decoded output. - void DoDecode(); - - // Invoked when we have a valid decoded output sample. Retrieves the D3D - // surface and maintains a copy of it which is passed eventually to the - // client when we have a picture buffer to copy the surface contents to. - bool ProcessOutputSample(IMFSample* sample); - - // Copies the output sample data to the picture buffer provided by the - // client. - bool CopyOutputSampleDataToPictureBuffer(IDirect3DSurface9* dest_surface, - media::PictureBuffer picture_buffer, - int32 input_buffer_id); - - // Processes pending output samples by copying them to available picture - // slots. - void ProcessPendingSamples(); - - // Clears local state maintained by the decoder. - void ClearState(); - - // Helper function to notify the accelerator client about the error. - void StopOnError(media::VideoDecodeAccelerator::Error error); - - // Transitions the decoder to the uninitialized state. The decoder will stop - // accepting requests in this state. - void Invalidate(); - - // Helper function to read the bitmap from the D3D surface passed in. - bool GetBitmapFromSurface(IDirect3DSurface9* surface, - scoped_array<char>* bits); - - // Notifies the client that the input buffer identifed by input_buffer_id has - // been processed. - void NotifyInputBufferRead(int input_buffer_id); - - // Notifies the client that initialize was completed. - void NotifyInitializeDone(); - - // Notifies the client that the decoder was flushed. - void NotifyFlushDone(); - - // Notifies the client that the decoder was reset. - void NotifyResetDone(); - - // Requests picture buffers from the client. - void RequestPictureBuffers(int width, int height); - - // Notifies the client about the availability of a picture. - void NotifyPictureReady(const media::Picture& picture); - - // To expose client callbacks from VideoDecodeAccelerator. - media::VideoDecodeAccelerator::Client* client_; - - base::win::ScopedComPtr<IMFTransform> decoder_; - - // These interface pointers are initialized before the process is sandboxed. - // They are not released when the GPU process exits. This is ok for now - // because the GPU process does not exit normally on Windows. It is always - // terminated. The device manager instance is shared among all decoder - // instances. This is OK because there is internal locking performed by the - // device manager. - static IDirect3DDeviceManager9* device_manager_; - static IDirect3DDevice9Ex* device_; - - // Current state of the decoder. - State state_; - - MFT_INPUT_STREAM_INFO input_stream_info_; - MFT_OUTPUT_STREAM_INFO output_stream_info_; - - // Contains information about a decoded sample. - struct PendingSampleInfo { - PendingSampleInfo(int32 buffer_id, IDirect3DSurface9* surface); - ~PendingSampleInfo(); - - int32 input_buffer_id; - base::win::ScopedComPtr<IDirect3DSurface9> dest_surface; - }; - - typedef std::list<PendingSampleInfo> PendingOutputSamples; - - // List of decoded output samples. - PendingOutputSamples pending_output_samples_; - - // Maintains information about a DXVA picture buffer, i.e. whether it is - // available for rendering, the texture information, etc. - struct DXVAPictureBuffer { - explicit DXVAPictureBuffer(const media::PictureBuffer& buffer); - - bool available; - media::PictureBuffer picture_buffer; - }; - - // This map maintains the picture buffers passed the client for decoding. - // The key is the picture buffer id. - typedef std::map<int32, DXVAPictureBuffer> OutputBuffers; - OutputBuffers output_picture_buffers_; - - // Set to true if we requested picture slots from the client. - bool pictures_requested_; - - // Contains the id of the last input buffer received from the client. - int32 last_input_buffer_id_; - - // Handle to the renderer process. - base::ProcessHandle renderer_process_; - - // Ideally the reset token would be a stack variable which is used while - // creating the device manager. However it seems that the device manager - // holds onto the token and attempts to access it if the underlying device - // changes. - // TODO(ananta): This needs to be verified. - static uint32 dev_manager_reset_token_; - - // Counter which holds the number of input packets before a successful - // decode. - int inputs_before_decode_; - - // Set to true if all necessary initialization needed before the GPU process - // is sandboxed is done. - // This includes the following: - // 1. All required decoder dlls were successfully loaded. - // 2. The device manager initialization completed. - static bool pre_sandbox_init_done_; -}; - -#endif // CONTENT_COMMON_GPU_MEDIA_DXVA_VIDEO_DECODE_ACCELERATOR_H_ - diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.cc b/content/common/gpu/media/gpu_video_decode_accelerator.cc index 9dbd2e2..1014997 100644 --- a/content/common/gpu/media/gpu_video_decode_accelerator.cc +++ b/content/common/gpu/media/gpu_video_decode_accelerator.cc @@ -9,28 +9,17 @@ #include "base/bind.h" #include "base/logging.h" #include "base/stl_util.h" - -#if defined(OS_WIN) -#include "base/win/windows_version.h" -#endif // OS_WIN - #include "gpu/command_buffer/common/command_buffer.h" #include "ipc/ipc_message_macros.h" #include "ipc/ipc_message_utils.h" #include "content/common/gpu/gpu_channel.h" #include "content/common/gpu/gpu_command_buffer_stub.h" #include "content/common/gpu/gpu_messages.h" - -#if (defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL)) || defined(OS_WIN) -#if defined(OS_WIN) -#include "content/common/gpu/media/dxva_video_decode_accelerator.h" -#else // OS_WIN +#if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) #include "content/common/gpu/media/omx_video_decode_accelerator.h" -#endif // OS_WIN #include "ui/gfx/gl/gl_context.h" #include "ui/gfx/gl/gl_surface_egl.h" #endif - #include "gpu/command_buffer/service/texture_manager.h" #include "ui/gfx/size.h" @@ -120,32 +109,18 @@ void GpuVideoDecodeAccelerator::NotifyError( void GpuVideoDecodeAccelerator::Initialize( const media::VideoDecodeAccelerator::Profile profile, - IPC::Message* init_done_msg, - base::ProcessHandle renderer_process) { + IPC::Message* init_done_msg) { DCHECK(!video_decode_accelerator_.get()); DCHECK(!init_done_msg_); DCHECK(init_done_msg); init_done_msg_ = init_done_msg; - -#if (defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL)) || defined(OS_WIN) +#if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) DCHECK(stub_ && stub_->decoder()); -#if defined(OS_WIN) - if (base::win::GetVersion() < base::win::VERSION_WIN7) { - NOTIMPLEMENTED() << "HW video decode acceleration not available."; - NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); - return; - } - DLOG(INFO) << "Initializing DXVA HW decoder for windows."; - DXVAVideoDecodeAccelerator* video_decoder = - new DXVAVideoDecodeAccelerator(this, renderer_process); -#else // OS_WIN - OmxVideoDecodeAccelerator* video_decoder = - new OmxVideoDecodeAccelerator(this); - video_decoder->SetEglState( + OmxVideoDecodeAccelerator* omx_decoder = new OmxVideoDecodeAccelerator(this); + omx_decoder->SetEglState( gfx::GLSurfaceEGL::GetHardwareDisplay(), stub_->decoder()->GetGLContext()->GetHandle()); -#endif // OS_WIN - video_decode_accelerator_ = video_decoder; + video_decode_accelerator_ = omx_decoder; video_decode_accelerator_->Initialize(profile); #else // Update RenderViewImpl::createMediaPlayer when adding clauses. NOTIMPLEMENTED() << "HW video decode acceleration not available."; diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.h b/content/common/gpu/media/gpu_video_decode_accelerator.h index aa9f726..3acf3f6 100644 --- a/content/common/gpu/media/gpu_video_decode_accelerator.h +++ b/content/common/gpu/media/gpu_video_decode_accelerator.h @@ -45,11 +45,8 @@ class GpuVideoDecodeAccelerator // Initialize the accelerator with the given profile and send the // |init_done_msg| when done. - // The renderer process handle is valid as long as we have a channel between - // GPU process and the renderer. void Initialize(const media::VideoDecodeAccelerator::Profile profile, - IPC::Message* init_done_msg, - base::ProcessHandle renderer_process); + IPC::Message* init_done_msg); private: diff --git a/content/common/gpu/media/video_decode_accelerator_unittest.cc b/content/common/gpu/media/omx_video_decode_accelerator_unittest.cc index f6bebc7..727b7fe 100644 --- a/content/common/gpu/media/video_decode_accelerator_unittest.cc +++ b/content/common/gpu/media/omx_video_decode_accelerator_unittest.cc @@ -28,7 +28,6 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/file_util.h" -#include "base/process_util.h" #include "base/stl_util.h" #include "base/string_number_conversions.h" #include "base/string_split.h" @@ -37,21 +36,14 @@ #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" -#include "base/utf_string_conversions.h" - -#if (!defined(OS_CHROMEOS) || !defined(ARCH_CPU_ARMEL)) && !defined(OS_WIN) -#error The VideoAccelerator tests are only supported on cros/ARM/Windows. -#endif - -#if defined(OS_WIN) -#include "content/common/gpu/media/dxva_video_decode_accelerator.h" -#else // OS_WIN #include "content/common/gpu/media/omx_video_decode_accelerator.h" -#endif // defined(OS_WIN) - #include "third_party/angle/include/EGL/egl.h" #include "third_party/angle/include/GLES2/gl2.h" +#if !defined(OS_CHROMEOS) || !defined(ARCH_CPU_ARMEL) +#error This test (and OmxVideoDecodeAccelerator) are only supported on cros/ARM! +#endif + using media::VideoDecodeAccelerator; namespace { @@ -69,21 +61,20 @@ namespace { // (the latter tests just decode speed). // - |profile| is the media::H264Profile set during Initialization. // An empty value for a numeric field means "ignore". -const FilePath::CharType* test_video_data = - FILE_PATH_LITERAL("test-25fps.h264:320:240:250:258:50:175:1"); +const char* test_video_data = "test-25fps.h264:320:240:250:258:50:175:1"; // Parse |data| into its constituent parts and set the various output fields // accordingly. CHECK-fails on unexpected or missing required data. // Unspecified optional fields are set to -1. -void ParseTestVideoData(FilePath::StringType data, - FilePath::StringType* file_name, +void ParseTestVideoData(std::string data, + std::string* file_name, int* width, int* height, int* num_frames, int* num_NALUs, int* min_fps_render, int* min_fps_no_render, int* profile) { - std::vector<FilePath::StringType> elements; + std::vector<std::string> elements; base::SplitString(data, ':', &elements); CHECK_GE(elements.size(), 1U) << data; CHECK_LE(elements.size(), 8U) << data; @@ -107,12 +98,17 @@ void ParseTestVideoData(FilePath::StringType data, CHECK(base::StringToInt(elements[7], profile)); } -// Provides functionality for managing EGL, GLES2 and UI resources. -// This class is not thread safe and thus all the methods of this class -// (except for ctor/dtor) ensure they're being run on a single thread. + +// Helper for managing X11, EGL, and GLES2 resources. Xlib is not thread-safe, +// and GL state is thread-specific, so all the methods of this class (except for +// ctor/dtor) ensure they're being run on a single thread. +// +// TODO(fischman): consider moving this into media/ if we can de-dup some of the +// code that ends up getting copy/pasted all over the place (esp. the GL setup +// code). class RenderingHelper { public: - RenderingHelper(); + explicit RenderingHelper(); ~RenderingHelper(); // Initialize all structures to prepare to render to one or more windows of @@ -123,8 +119,8 @@ class RenderingHelper { // then all the usual work is done, except for the final swap of the EGL // surface to the display. This cuts test times over 50% so is worth doing // when testing non-rendering-related aspects. - void Initialize(bool suppress_swap_to_display, int num_windows, int width, - int height, base::WaitableEvent* done); + void Initialize(bool suppress_swap_to_display, int num_windows, + int width, int height, base::WaitableEvent* done); // Undo the effects of Initialize() and signal |*done|. void UnInitialize(base::WaitableEvent* done); @@ -140,43 +136,26 @@ class RenderingHelper { // Delete |texture_id|. void DeleteTexture(GLuint texture_id); - // Platform specific Init/Uninit. - void PlatformInitialize(); - void PlatformUnInitialize(); - - // Platform specific window creation. - EGLNativeWindowType PlatformCreateWindow(int top_left_x, int top_left_y); - - // Platform specific display surface returned here. - EGLDisplay PlatformGetDisplay(); - EGLDisplay egl_display() { return egl_display_; } - EGLContext egl_context() { return egl_context_; } - MessageLoop* message_loop() { return message_loop_; } - protected: + private: + // Zero-out internal state. Helper for ctor & UnInitialize(). void Clear(); - // We ensure all operations are carried out on the same thread by remembering - // where we were Initialized. - MessageLoop* message_loop_; + bool suppress_swap_to_display_; int width_; int height_; - bool suppress_swap_to_display_; - + Display* x_display_; + std::vector<Window> x_windows_; EGLDisplay egl_display_; EGLContext egl_context_; std::vector<EGLSurface> egl_surfaces_; std::map<GLuint, int> texture_id_to_surface_index_; - -#if defined(OS_WIN) - std::vector<HWND> windows_; -#else // OS_WIN - Display* x_display_; - std::vector<Window> x_windows_; -#endif // OS_WIN + // We ensure all operations are carried out on the same thread by remembering + // where we were Initialized. + MessageLoop* message_loop_; }; RenderingHelper::RenderingHelper() { @@ -185,7 +164,19 @@ RenderingHelper::RenderingHelper() { RenderingHelper::~RenderingHelper() { CHECK_EQ(width_, 0) << "Must call UnInitialize before dtor."; - Clear(); +} + +void RenderingHelper::Clear() { + suppress_swap_to_display_ = false; + width_ = 0; + height_ = 0; + x_display_ = NULL; + x_windows_.clear(); + egl_display_ = EGL_NO_DISPLAY; + egl_context_ = EGL_NO_CONTEXT; + egl_surfaces_.clear(); + texture_id_to_surface_index_.clear(); + message_loop_ = NULL; } // Helper for Shader creation. @@ -209,8 +200,7 @@ static void CreateShader( void RenderingHelper::Initialize( bool suppress_swap_to_display, int num_windows, - int width, - int height, + int width, int height, base::WaitableEvent* done) { // Use width_ != 0 as a proxy for the class having already been // Initialize()'d, and UnInitialize() before continuing. @@ -228,10 +218,15 @@ void RenderingHelper::Initialize( message_loop_ = MessageLoop::current(); CHECK_GT(num_windows, 0); - PlatformInitialize(); - - egl_display_ = PlatformGetDisplay(); + // Per-display X11 & EGL initialization. + CHECK(x_display_ = XOpenDisplay(NULL)); + int depth = DefaultDepth(x_display_, DefaultScreen(x_display_)); + XSetWindowAttributes window_attributes; + window_attributes.background_pixel = + BlackPixel(x_display_, DefaultScreen(x_display_)); + window_attributes.override_redirect = true; + egl_display_ = eglGetDisplay(x_display_); EGLint major; EGLint minor; CHECK(eglInitialize(egl_display_, &major, &minor)) << eglGetError(); @@ -258,16 +253,28 @@ void RenderingHelper::Initialize( // Arrange X windows whimsically, with some padding. int top_left_x = (width + 20) * (i % 4); int top_left_y = (height + 12) * (i % 3); + Window x_window = XCreateWindow( + x_display_, DefaultRootWindow(x_display_), + top_left_x, top_left_y, width_, height_, + 0 /* border width */, + depth, CopyFromParent /* class */, CopyFromParent /* visual */, + (CWBackPixel | CWOverrideRedirect), &window_attributes); + x_windows_.push_back(x_window); + XStoreName(x_display_, x_window, "OmxVideoDecodeAcceleratorTest"); + XSelectInput(x_display_, x_window, ExposureMask); + XMapWindow(x_display_, x_window); - EGLNativeWindowType window = PlatformCreateWindow(top_left_x, top_left_y); EGLSurface egl_surface = - eglCreateWindowSurface(egl_display_, egl_config, window, NULL); + eglCreateWindowSurface(egl_display_, egl_config, x_window, NULL); egl_surfaces_.push_back(egl_surface); CHECK_NE(egl_surface, EGL_NO_SURFACE); } CHECK(eglMakeCurrent(egl_display_, egl_surfaces_[0], egl_surfaces_[0], egl_context_)) << eglGetError(); + // GLES2 initialization. Note: This is pretty much copy/pasted from + // media/tools/player_x11/gles_video_renderer.cc, with some simplification + // applied. static const float kVertices[] = { -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f, -1.f, }; static const float kTextureCoordsEgl[] = { 0, 1, 0, 0, 1, 1, 1, 0, }; @@ -312,33 +319,28 @@ void RenderingHelper::Initialize( glEnableVertexAttribArray(tc_location); glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0, kTextureCoordsEgl); + done->Signal(); } void RenderingHelper::UnInitialize(base::WaitableEvent* done) { CHECK_EQ(MessageLoop::current(), message_loop_); + // Destroy resources acquired in Initialize, in reverse-acquisition order. CHECK(eglMakeCurrent(egl_display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) << eglGetError(); CHECK(eglDestroyContext(egl_display_, egl_context_)); for (size_t i = 0; i < egl_surfaces_.size(); ++i) CHECK(eglDestroySurface(egl_display_, egl_surfaces_[i])); CHECK(eglTerminate(egl_display_)); + for (size_t i = 0; i < x_windows_.size(); ++i) { + CHECK(XUnmapWindow(x_display_, x_windows_[i])); + CHECK(XDestroyWindow(x_display_, x_windows_[i])); + } + // Mimic newly-created object. Clear(); done->Signal(); } -void RenderingHelper::Clear() { - suppress_swap_to_display_ = false; - width_ = 0; - height_ = 0; - texture_id_to_surface_index_.clear(); - message_loop_ = NULL; - egl_display_ = EGL_NO_DISPLAY; - egl_context_ = EGL_NO_CONTEXT; - egl_surfaces_.clear(); - PlatformUnInitialize(); -} - void RenderingHelper::CreateTexture(int window_id, GLuint* texture_id, base::WaitableEvent* done) { if (MessageLoop::current() != message_loop_) { @@ -388,76 +390,6 @@ void RenderingHelper::DeleteTexture(GLuint texture_id) { CHECK_EQ(static_cast<int>(glGetError()), GL_NO_ERROR); } -#if defined(OS_WIN) -void RenderingHelper::PlatformInitialize() {} - -void RenderingHelper::PlatformUnInitialize() { - for (size_t i = 0; i < windows_.size(); ++i) { - DestroyWindow(windows_[i]); - } - windows_.clear(); -} - -EGLNativeWindowType RenderingHelper::PlatformCreateWindow( - int top_left_x, int top_left_y) { - HWND window = CreateWindowEx(0, L"Static", L"VideoDecodeAcceleratorTest", - WS_OVERLAPPEDWINDOW | WS_VISIBLE, top_left_x, - top_left_y, width_, height_, NULL, NULL, NULL, - NULL); - CHECK(window != NULL); - windows_.push_back(window); - return window; -} - -EGLDisplay RenderingHelper::PlatformGetDisplay() { - return eglGetDisplay(EGL_DEFAULT_DISPLAY); -} - -#else // OS_WIN - -void RenderingHelper::PlatformInitialize() { - CHECK(x_display_ = XOpenDisplay(NULL)); -} - -void RenderingHelper::PlatformUnInitialize() { - // Destroy resources acquired in Initialize, in reverse-acquisition order. - for (size_t i = 0; i < x_windows_.size(); ++i) { - CHECK(XUnmapWindow(x_display_, x_windows_[i])); - CHECK(XDestroyWindow(x_display_, x_windows_[i])); - } - // Mimic newly created object. - x_display_ = NULL; - x_windows_.clear(); -} - -EGLDisplay RenderingHelper::PlatformGetDisplay() { - return eglGetDisplay(x_display_); -} - -EGLNativeWindowType RenderingHelper::PlatformCreateWindow(int top_left_x, - int top_left_y) { - int depth = DefaultDepth(x_display_, DefaultScreen(x_display_)); - - XSetWindowAttributes window_attributes; - window_attributes.background_pixel = - BlackPixel(x_display_, DefaultScreen(x_display_)); - window_attributes.override_redirect = true; - - Window x_window = XCreateWindow( - x_display_, DefaultRootWindow(x_display_), - top_left_x, top_left_y, width_, height_, - 0 /* border width */, - depth, CopyFromParent /* class */, CopyFromParent /* visual */, - (CWBackPixel | CWOverrideRedirect), &window_attributes); - x_windows_.push_back(x_window); - XStoreName(x_display_, x_window, "VideoDecodeAcceleratorTest"); - XSelectInput(x_display_, x_window, ExposureMask); - XMapWindow(x_display_, x_window); - return x_window; -} - -#endif // OS_WIN - // State of the EglRenderingVDAClient below. Order matters here as the test // makes assumptions about it. enum ClientState { @@ -591,7 +523,7 @@ class EglRenderingVDAClient : public VideoDecodeAccelerator::Client { size_t encoded_data_next_pos_to_decode_; int next_bitstream_buffer_id_; ClientStateNotification* note_; - scoped_refptr<VideoDecodeAccelerator> decoder_; + scoped_refptr<OmxVideoDecodeAccelerator> decoder_; std::set<int> outstanding_texture_ids_; int reset_after_frame_num_; int delete_decoder_state_; @@ -637,15 +569,8 @@ EglRenderingVDAClient::~EglRenderingVDAClient() { void EglRenderingVDAClient::CreateDecoder() { CHECK(decoder_deleted()); -#if defined(OS_WIN) - scoped_refptr<DXVAVideoDecodeAccelerator> decoder = - new DXVAVideoDecodeAccelerator(this, base::GetCurrentProcessHandle()); -#else // OS_WIN - scoped_refptr<OmxVideoDecodeAccelerator> decoder = - new OmxVideoDecodeAccelerator(this); - decoder->SetEglState(egl_display(), egl_context()); -#endif // OS_WIN - decoder_ = decoder.release(); + decoder_ = new OmxVideoDecodeAccelerator(this); + decoder_->SetEglState(egl_display(), egl_context()); SetState(CS_DECODER_SET); if (decoder_deleted()) return; @@ -860,7 +785,7 @@ double EglRenderingVDAClient::frames_per_second() { // - Number of concurrent in-flight Decode() calls per decoder. // - reset_after_frame_num: see EglRenderingVDAClient ctor. // - delete_decoder_phase: see EglRenderingVDAClient ctor. -class VideoDecodeAcceleratorTest +class OmxVideoDecodeAcceleratorTest : public ::testing::TestWithParam< Tuple5<int, int, int, ResetPoint, ClientState> > { }; @@ -884,7 +809,7 @@ enum { kMinSupportedNumConcurrentDecoders = 3 }; // Test the most straightforward case possible: data is decoded from a single // chunk and rendered to the screen. -TEST_P(VideoDecodeAcceleratorTest, TestSimpleDecode) { +TEST_P(OmxVideoDecodeAcceleratorTest, TestSimpleDecode) { // Can be useful for debugging VLOGs from OVDA. // logging::SetMinLogLevel(-1); @@ -897,7 +822,7 @@ TEST_P(VideoDecodeAcceleratorTest, TestSimpleDecode) { const int reset_after_frame_num = GetParam().d; const int delete_decoder_state = GetParam().e; - FilePath::StringType test_video_file; + std::string test_video_file; int frame_width, frame_height; int num_frames, num_NALUs, min_fps_render, min_fps_no_render, profile; ParseTestVideoData(test_video_data, &test_video_file, &frame_width, @@ -924,15 +849,7 @@ TEST_P(VideoDecodeAcceleratorTest, TestSimpleDecode) { // Initialize the rendering helper. base::Thread rendering_thread("EglRenderingVDAClientThread"); - base::Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_DEFAULT; -#if defined(OS_WIN) - // For windows the decoding thread initializes the media foundation decoder - // which uses COM. We need the thread to be a UI thread. - options.message_loop_type = MessageLoop::TYPE_UI; -#endif // OS_WIN - - rendering_thread.StartWithOptions(options); + rendering_thread.Start(); RenderingHelper rendering_helper; base::WaitableEvent done(false, false); @@ -972,8 +889,7 @@ TEST_P(VideoDecodeAcceleratorTest, TestSimpleDecode) { // We expect initialization to fail only when more than the supported // number of decoders is instantiated. Assert here that something else // didn't trigger failure. - ASSERT_GT(num_concurrent_decoders, - static_cast<size_t>(kMinSupportedNumConcurrentDecoders)); + ASSERT_GT(num_concurrent_decoders, kMinSupportedNumConcurrentDecoders); continue; } ASSERT_EQ(state, CS_INITIALIZED); @@ -1031,14 +947,14 @@ TEST_P(VideoDecodeAcceleratorTest, TestSimpleDecode) { // Test that Reset() mid-stream works fine and doesn't affect decoding even when // Decode() calls are made during the reset. INSTANTIATE_TEST_CASE_P( - MidStreamReset, VideoDecodeAcceleratorTest, + MidStreamReset, OmxVideoDecodeAcceleratorTest, ::testing::Values( MakeTuple(1, 1, 1, static_cast<ResetPoint>(100), CS_RESET))); // Test that Destroy() mid-stream works fine (primarily this is testing that no // crashes occur). INSTANTIATE_TEST_CASE_P( - TearDownTiming, VideoDecodeAcceleratorTest, + TearDownTiming, OmxVideoDecodeAcceleratorTest, ::testing::Values( MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_DECODER_SET), MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_INITIALIZED), @@ -1054,7 +970,7 @@ INSTANTIATE_TEST_CASE_P( // Test that decoding various variation works: multiple concurrent decoders and // multiple NALUs per Decode() call. INSTANTIATE_TEST_CASE_P( - DecodeVariations, VideoDecodeAcceleratorTest, + DecodeVariations, OmxVideoDecodeAcceleratorTest, ::testing::Values( MakeTuple(1, 1, 1, END_OF_STREAM_RESET, CS_RESET), MakeTuple(1, 1, 10, END_OF_STREAM_RESET, CS_RESET), @@ -1075,7 +991,7 @@ INSTANTIATE_TEST_CASE_P( // Find out how many concurrent decoders can go before we exhaust system // resources. INSTANTIATE_TEST_CASE_P( - ResourceExhaustion, VideoDecodeAcceleratorTest, + ResourceExhaustion, OmxVideoDecodeAcceleratorTest, ::testing::Values( // +0 hack below to promote enum to int. MakeTuple(1, kMinSupportedNumConcurrentDecoders + 0, 1, @@ -1093,21 +1009,16 @@ INSTANTIATE_TEST_CASE_P( int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); // Removes gtest-specific args. - CommandLine::Init(argc, argv); - - CommandLine* cmd_line = CommandLine::ForCurrentProcess(); - DCHECK(cmd_line); - - CommandLine::SwitchMap switches = cmd_line->GetSwitches(); + CommandLine cmd_line(argc, argv); // Must run after InitGoogleTest. + CommandLine::SwitchMap switches = cmd_line.GetSwitches(); for (CommandLine::SwitchMap::const_iterator it = switches.begin(); it != switches.end(); ++it) { if (it->first == "test_video_data") { test_video_data = it->second.c_str(); + continue; } LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second; } -#if defined(OS_WIN) - DXVAVideoDecodeAccelerator::PreSandboxInitialization(); -#endif // OS_WIN + return RUN_ALL_TESTS(); } diff --git a/content/content_common.gypi b/content/content_common.gypi index 7f0a3a3..229e4a5 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -327,42 +327,6 @@ ], }, }], - ['OS=="win"', { - 'dependencies': [ - '../media/media.gyp:media', - '../third_party/angle/src/build_angle.gyp:libEGL', - '../third_party/angle/src/build_angle.gyp:libGLESv2', - ], - 'link_settings': { - 'libraries': [ - '-ld3d9.lib', - '-ld3dx9.lib', - '-ldxva2.lib', - '-lstrmiids.lib', - '-lmf.lib', - '-lmfplat.lib', - '-lmfuuid.lib', - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'DelayLoadDLLs': [ - 'd3d9.dll', - 'd3dx9_43.dll', - 'dxva2.dll', - 'mf.dll', - 'mfplat.dll', - ], - }, - }, - }, - 'sources': [ - 'common/gpu/media/dxva_video_decode_accelerator.cc', - 'common/gpu/media/dxva_video_decode_accelerator.h', - ], - 'include_dirs': [ - '<(DEPTH)/third_party/angle/include', - ], - }], ['OS=="win" and directxsdk_exists=="True"', { 'actions': [ { diff --git a/content/content_tests.gypi b/content/content_tests.gypi index fcddd70..e8e36c0 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -402,23 +402,10 @@ }, ], 'conditions': [ - ['target_arch=="arm" or OS=="win"', { + ['target_arch=="arm"', { 'targets': [ { - 'conditions': [ - ['target_arch=="arm"', { - 'target_name': 'omx_video_decode_accelerator_unittest', - 'include_dirs': [ - '<(DEPTH)/third_party/openmax/il', - ], - }], - ['OS=="win"', { - 'target_name': 'dxva_video_decode_accelerator_unittest', - 'dependencies': [ - '../base/allocator/allocator.gyp:allocator', - ], - }], - ], + 'target_name': 'omx_video_decode_accelerator_unittest', 'defines!': ['CONTENT_IMPLEMENTATION'], 'type': 'executable', 'dependencies': [ @@ -428,9 +415,10 @@ ], 'include_dirs': [ '<(DEPTH)/third_party/angle/include', + '<(DEPTH)/third_party/openmax/il', ], 'sources': [ - 'common/gpu/media/video_decode_accelerator_unittest.cc', + 'common/gpu/media/omx_video_decode_accelerator_unittest.cc', ], } ], diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc index c15cab2..ca26cc9 100644 --- a/content/gpu/gpu_main.cc +++ b/content/gpu/gpu_main.cc @@ -23,7 +23,6 @@ #include "ui/gfx/gl/gl_switches.h" #if defined(OS_WIN) -#include "content/common/gpu/media/dxva_video_decode_accelerator.h" #include "sandbox/src/sandbox.h" #endif @@ -84,8 +83,6 @@ int GpuMain(const content::MainFunctionParams& parameters) { #if defined(OS_WIN) sandbox::TargetServices* target_services = parameters.sandbox_info->target_services; - // Initialize H/W video decoding stuff which fails in the sandbox. - DXVAVideoDecodeAccelerator::PreSandboxInitialization(); // For windows, if the target_services interface is not zero, the process // is sandboxed and we must call LowerToken() before rendering untrusted // content. |