Chromium Code Reviews| Index: media/gpu/d3d11_video_decode_accelerator_win.cc |
| diff --git a/media/gpu/d3d11_video_decode_accelerator_win.cc b/media/gpu/d3d11_video_decode_accelerator_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..dad1805b34987802de2c66d088982d81271844e5 |
| --- /dev/null |
| +++ b/media/gpu/d3d11_video_decode_accelerator_win.cc |
| @@ -0,0 +1,324 @@ |
| +// Copyright 2016 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. |
| + |
| +#define INITGUID |
|
sandersd (OOO until July 31)
2016/12/02 00:52:57
Should this be undef'd later?
|
| +#include "media/gpu/d3d11_video_decode_accelerator_win.h" |
| + |
| +#include <d3d11.h> |
| + |
| +#include "base/bits.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "base/memory/shared_memory.h" |
| +#include "base/threading/thread_task_runner_handle.h" |
| +#include "base/trace_event/trace_event.h" |
| +#include "base/win/scoped_comptr.h" |
| +#include "media/gpu/d3d11_video_decoder.h" |
| +#include "media/gpu/h264_decoder.h" |
| +#include "media/gpu/h264_dpb.h" |
| +#include "third_party/angle/include/EGL/egl.h" |
| +#include "third_party/angle/include/EGL/eglext.h" |
| +#include "ui/gfx/color_space.h" |
| +#include "ui/gl/gl_bindings.h" |
| +#include "ui/gl/gl_context.h" |
| +#include "ui/gl/gl_surface_egl.h" |
| + |
| +namespace media { |
| + |
| +#define RETURN_ON_FAILURE(result, log, ret) \ |
| + do { \ |
| + if (!(result)) { \ |
| + DLOG(ERROR) << log; \ |
| + return ret; \ |
| + } \ |
| + } while (0) |
| + |
| +// Helper function to query the ANGLE device object. The template argument T |
| +// identifies the device interface being queried. IDirect3DDevice9Ex for d3d9 |
| +// and ID3D11Device for dx11. |
| +template <class T> |
| +base::win::ScopedComPtr<T> QueryDeviceObjectFromANGLE(int object_type) { |
| + base::win::ScopedComPtr<T> device_object; |
| + |
| + EGLDisplay egl_display = nullptr; |
| + intptr_t egl_device = 0; |
| + intptr_t device = 0; |
| + |
| + { |
| + TRACE_EVENT0("gpu", "QueryDeviceObjectFromANGLE. GetHardwareDisplay"); |
| + egl_display = gl::GLSurfaceEGL::GetHardwareDisplay(); |
| + } |
| + |
| + RETURN_ON_FAILURE(gl::GLSurfaceEGL::HasEGLExtension("EGL_EXT_device_query"), |
| + "EGL_EXT_device_query missing", device_object); |
| + |
| + PFNEGLQUERYDISPLAYATTRIBEXTPROC QueryDisplayAttribEXT = nullptr; |
| + |
| + { |
| + TRACE_EVENT0("gpu", "QueryDeviceObjectFromANGLE. eglGetProcAddress"); |
| + |
| + QueryDisplayAttribEXT = reinterpret_cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>( |
| + eglGetProcAddress("eglQueryDisplayAttribEXT")); |
| + |
| + RETURN_ON_FAILURE( |
| + QueryDisplayAttribEXT, |
| + "Failed to get the eglQueryDisplayAttribEXT function from ANGLE", |
| + device_object); |
| + } |
| + |
| + PFNEGLQUERYDEVICEATTRIBEXTPROC QueryDeviceAttribEXT = nullptr; |
| + |
| + { |
| + TRACE_EVENT0("gpu", "QueryDeviceObjectFromANGLE. eglGetProcAddress"); |
| + |
| + QueryDeviceAttribEXT = reinterpret_cast<PFNEGLQUERYDEVICEATTRIBEXTPROC>( |
| + eglGetProcAddress("eglQueryDeviceAttribEXT")); |
| + |
| + RETURN_ON_FAILURE( |
| + QueryDeviceAttribEXT, |
| + "Failed to get the eglQueryDeviceAttribEXT function from ANGLE", |
| + device_object); |
| + } |
| + |
| + { |
| + TRACE_EVENT0("gpu", "QueryDeviceObjectFromANGLE. QueryDisplayAttribEXT"); |
| + |
| + RETURN_ON_FAILURE( |
| + QueryDisplayAttribEXT(egl_display, EGL_DEVICE_EXT, &egl_device), |
| + "The eglQueryDisplayAttribEXT function failed to get the EGL device", |
| + device_object); |
| + } |
| + |
| + RETURN_ON_FAILURE(egl_device, "Failed to get the EGL device", device_object); |
| + |
| + { |
| + TRACE_EVENT0("gpu", "QueryDeviceObjectFromANGLE. QueryDisplayAttribEXT"); |
| + |
| + RETURN_ON_FAILURE( |
| + QueryDeviceAttribEXT(reinterpret_cast<EGLDeviceEXT>(egl_device), |
| + object_type, &device), |
| + "The eglQueryDeviceAttribEXT function failed to get the device", |
| + device_object); |
| + |
| + RETURN_ON_FAILURE(device, "Failed to get the ANGLE device", device_object); |
| + } |
| + |
| + device_object = reinterpret_cast<T*>(device); |
| + return device_object; |
| +} |
| + |
| +D3D11VideoDecodeAccelerator::D3D11VideoDecodeAccelerator( |
| + const GetGLContextCallback& get_gl_context_cb, |
| + const MakeGLContextCurrentCallback& make_context_current_cb) |
| + : get_gl_context_cb_(get_gl_context_cb), |
| + make_context_current_cb_(make_context_current_cb) {} |
| + |
| +D3D11VideoDecodeAccelerator::~D3D11VideoDecodeAccelerator() {} |
| + |
| +bool D3D11VideoDecodeAccelerator::Initialize(const Config& config, |
| + Client* client) { |
| + client_ = client; |
| + make_context_current_cb_.Run(); |
| + |
| + device_ = QueryDeviceObjectFromANGLE<ID3D11Device>(EGL_D3D11_DEVICE_ANGLE); |
| + device_->GetImmediateContext(device_context_.Receive()); |
| + |
| + HRESULT hr = device_context_.QueryInterface(video_context_.Receive()); |
| + CHECK(SUCCEEDED(hr)); |
| + |
| + hr = device_.QueryInterface(video_device_.Receive()); |
| + CHECK(SUCCEEDED(hr)); |
| + |
| + bool is_h264 = |
| + config.profile >= H264PROFILE_MIN && config.profile <= H264PROFILE_MAX; |
| + if (!is_h264) |
| + return false; |
| + |
| + GUID needed_guid; |
| + memcpy(&needed_guid, &D3D11_DECODER_PROFILE_H264_VLD_NOFGT, |
| + sizeof(needed_guid)); |
| + GUID decoder_guid = {}; |
| + |
| + { |
| + // Enumerate supported video profiles and look for the H264 profile. |
| + bool found = false; |
| + UINT profile_count = video_device_->GetVideoDecoderProfileCount(); |
| + for (UINT profile_idx = 0; profile_idx < profile_count; profile_idx++) { |
| + GUID profile_id = {}; |
| + hr = video_device_->GetVideoDecoderProfile(profile_idx, &profile_id); |
| + if (SUCCEEDED(hr) && (profile_id == needed_guid)) { |
| + decoder_guid = profile_id; |
| + found = true; |
| + break; |
| + } |
| + } |
| + CHECK(found); |
| + } |
| + |
| + D3D11_VIDEO_DECODER_DESC desc = {}; |
| + desc.Guid = decoder_guid; |
| + desc.SampleWidth = 1920; |
| + desc.SampleHeight = 1088; |
| + desc.OutputFormat = DXGI_FORMAT_NV12; |
| + UINT config_count = 0; |
| + hr = video_device_->GetVideoDecoderConfigCount(&desc, &config_count); |
| + if (FAILED(hr) || config_count == 0) |
| + CHECK(false); |
| + |
| + D3D11_VIDEO_DECODER_CONFIG dec_config = {}; |
| + for (UINT i = 0; i < config_count; i++) { |
| + hr = video_device_->GetVideoDecoderConfig(&desc, i, &dec_config); |
| + if (FAILED(hr)) |
| + CHECK(false); |
| + if (dec_config.ConfigBitstreamRaw == 2) |
| + break; |
| + } |
| + memcpy(&decoder_guid_, &decoder_guid, sizeof decoder_guid_); |
| + |
| + base::win::ScopedComPtr<ID3D11VideoDecoder> video_decoder; |
| + hr = video_device_->CreateVideoDecoder(&desc, &dec_config, |
| + video_decoder.Receive()); |
| + CHECK(video_decoder.get()); |
| + |
| + h264_accelerator_.reset(new D3D11H264Accelerator( |
| + this, video_decoder, video_device_, video_context_)); |
| + decoder_.reset(new media::H264Decoder(h264_accelerator_.get())); |
| + |
| + return true; |
| +} |
| + |
| +void D3D11VideoDecodeAccelerator::Decode( |
| + const BitstreamBuffer& bitstream_buffer) { |
| + input_buffer_queue_.push_back(bitstream_buffer); |
| + base::ThreadTaskRunnerHandle::Get()->PostTask( |
| + FROM_HERE, base::Bind(&D3D11VideoDecodeAccelerator::DoDecode, |
| + base::Unretained(this))); |
| +} |
| + |
| +void D3D11VideoDecodeAccelerator::DoDecode() { |
| + if (!bitstream_buffer_) { |
| + if (input_buffer_queue_.empty()) |
| + return; |
| + BitstreamBuffer buffer = input_buffer_queue_.front(); |
| + bitstream_buffer_ = |
| + base::MakeUnique<base::SharedMemory>(buffer.handle(), true); |
| + bitstream_buffer_->Map(buffer.size()); |
| + bitstream_buffer_size_ = buffer.size(); |
| + input_buffer_id_ = buffer.id(); |
| + input_buffer_queue_.pop_front(); |
| + decoder_->SetStream((const uint8_t*)bitstream_buffer_->memory(), |
| + bitstream_buffer_size_); |
| + } |
| + |
| + while (true) { |
| + media::AcceleratedVideoDecoder::DecodeResult result = decoder_->Decode(); |
| + if (result == media::AcceleratedVideoDecoder::kRanOutOfStreamData) { |
| + client_->NotifyEndOfBitstreamBuffer(input_buffer_id_); |
| + bitstream_buffer_.reset(); |
| + break; |
| + } |
| + if (result == media::AcceleratedVideoDecoder::kRanOutOfSurfaces) { |
| + return; |
| + } |
| + if (result == media::AcceleratedVideoDecoder::kAllocateNewSurfaces) { |
| + client_->ProvidePictureBuffers(20, PIXEL_FORMAT_NV12, 2, |
| + decoder_->GetPicSize(), |
| + GL_TEXTURE_EXTERNAL_OES); |
| + return; |
| + |
| + } else { |
| + LOG(ERROR) << "VDA Error " << result; |
| + CHECK(false); |
| + } |
| + } |
| + |
| + base::ThreadTaskRunnerHandle::Get()->PostTask( |
| + FROM_HERE, base::Bind(&D3D11VideoDecodeAccelerator::DoDecode, |
| + base::Unretained(this))); |
| +} |
| + |
| +void D3D11VideoDecodeAccelerator::AssignPictureBuffers( |
| + const std::vector<PictureBuffer>& buffers) { |
| + D3D11_TEXTURE2D_DESC texture_desc = {}; |
| + texture_desc.Width = decoder_->GetPicSize().width(); |
| + texture_desc.Height = decoder_->GetPicSize().height(); |
| + texture_desc.MipLevels = 1; |
| + texture_desc.ArraySize = buffers.size(); |
| + texture_desc.Format = DXGI_FORMAT_NV12; |
| + texture_desc.SampleDesc.Count = 1; |
| + texture_desc.Usage = D3D11_USAGE_DEFAULT; |
| + texture_desc.BindFlags = D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE; |
| + texture_desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; |
| + |
| + base::win::ScopedComPtr<ID3D11Texture2D> out_texture; |
| + HRESULT hr = |
| + device_->CreateTexture2D(&texture_desc, nullptr, out_texture.Receive()); |
| + CHECK(SUCCEEDED(hr)); |
| + |
| + make_context_current_cb_.Run(); |
| + picture_buffers_.clear(); |
| + |
| + for (size_t i = 0; i < buffers.size(); i++) { |
| + picture_buffers_.push_back( |
| + base::MakeUnique<D3D11PictureBuffer>(buffers[i], i)); |
| + picture_buffers_[i]->Init(video_device_, out_texture, decoder_guid_); |
| + } |
| + base::ThreadTaskRunnerHandle::Get()->PostTask( |
| + FROM_HERE, base::Bind(&D3D11VideoDecodeAccelerator::DoDecode, |
| + base::Unretained(this))); |
| +} |
| + |
| +void D3D11VideoDecodeAccelerator::ReusePictureBuffer( |
| + int32_t picture_buffer_id) { |
| + make_context_current_cb_.Run(); |
| + for (auto& buffer : picture_buffers_) { |
| + if (buffer->picture_buffer().id() == picture_buffer_id) { |
| + buffer->set_in_client_use(false); |
| + |
| + break; |
| + } |
| + } |
| + base::ThreadTaskRunnerHandle::Get()->PostTask( |
| + FROM_HERE, base::Bind(&D3D11VideoDecodeAccelerator::DoDecode, |
| + base::Unretained(this))); |
| +} |
| + |
| +D3D11PictureBuffer* D3D11VideoDecodeAccelerator::GetPicture() { |
| + for (auto& buffer : picture_buffers_) |
| + if (!buffer->in_client_use() && !buffer->in_picture_use()) { |
| + return buffer.get(); |
| + } |
| + return nullptr; |
| +} |
| + |
| +void D3D11VideoDecodeAccelerator::Flush() { |
| + client_->NotifyFlushDone(); |
| +} |
| + |
| +void D3D11VideoDecodeAccelerator::Reset() { |
| + client_->NotifyResetDone(); |
| +} |
| +void D3D11VideoDecodeAccelerator::Destroy() {} |
| + |
| +bool D3D11VideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( |
| + const base::WeakPtr<Client>& decode_client, |
| + const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner) { |
| + return false; |
| +} |
| + |
| +GLenum D3D11VideoDecodeAccelerator::GetSurfaceInternalFormat() const { |
| + return GL_BGRA_EXT; |
| +} |
| + |
| +size_t D3D11VideoDecodeAccelerator::input_buffer_id() const { |
| + return input_buffer_id_; |
| +} |
| + |
| +void D3D11VideoDecodeAccelerator::OutputResult(D3D11PictureBuffer* buffer, |
| + size_t input_buffer_id) { |
| + buffer->set_in_client_use(true); |
| + Picture picture(buffer->picture_buffer().id(), input_buffer_id, |
| + gfx::Rect(0, 0), gfx::ColorSpace(), false); |
| + client_->PictureReady(picture); |
| +} |
| +} // namespace media |