Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(969)

Unified Diff: content/common/gpu/media/dxva_video_decode_accelerator.cc

Issue 922003002: Support H.264 video decoding on Windows 8+ using DX11 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added a CHECK to catch GetData failures Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/common/gpu/media/dxva_video_decode_accelerator.cc
diff --git a/content/common/gpu/media/dxva_video_decode_accelerator.cc b/content/common/gpu/media/dxva_video_decode_accelerator.cc
index fb51f598811cc2a92f60922a9f49bc126b0660f0..ac813bd143587fa79ee28b9f4bb42cbaf345626c 100644
--- a/content/common/gpu/media/dxva_video_decode_accelerator.cc
+++ b/content/common/gpu/media/dxva_video_decode_accelerator.cc
@@ -10,6 +10,7 @@
#include <ks.h>
#include <codecapi.h>
+#include <dxgi1_2.h>
#include <mfapi.h>
#include <mferror.h>
#include <wmcodecdsp.h>
@@ -18,6 +19,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
+#include "base/debug/alias.h"
#include "base/file_version_info.h"
#include "base/files/file_path.h"
#include "base/logging.h"
@@ -29,6 +31,7 @@
#include "base/win/windows_version.h"
#include "media/video/video_decode_accelerator.h"
#include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/gl_switches.h"
@@ -83,10 +86,26 @@ const CLSID MEDIASUBTYPE_VP90 = {
{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
};
+// The CLSID of the video processor media foundation transform which we use for
+// texture color conversion in DX11.
+DEFINE_GUID(CLSID_VideoProcessorMFT,
+ 0x88753b26, 0x5b24, 0x49bd, 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78,
+ 0xc9, 0x82);
+
+// MF_XVP_PLAYBACK_MODE
+// Data type: UINT32 (treat as BOOL)
+// If this attribute is TRUE, the video processor will run in playback mode
+// where it allows callers to allocate output samples and allows last frame
+// regeneration (repaint).
+DEFINE_GUID(MF_XVP_PLAYBACK_MODE, 0x3c5d293f, 0xad67, 0x4e29, 0xaf, 0x12,
+ 0xcf, 0x3e, 0x23, 0x8a, 0xcc, 0xe9);
}
namespace content {
+CreateDXGIDeviceManager DXVAVideoDecodeAccelerator::create_dxgi_device_manager_
+ = NULL;
+
#define RETURN_ON_FAILURE(result, log, ret) \
do { \
if (!(result)) { \
@@ -158,6 +177,7 @@ static IMFSample* CreateEmptySampleWithBuffer(int buffer_length, int align) {
hr = sample->AddBuffer(buffer.get());
RETURN_ON_HR_FAILURE(hr, "Failed to add buffer to sample", NULL);
+ buffer->SetCurrentLength(0);
return sample.Detach();
}
@@ -229,6 +249,7 @@ struct DXVAVideoDecodeAccelerator::DXVAPictureBuffer {
bool CopyOutputSampleDataToPictureBuffer(
DXVAVideoDecodeAccelerator* decoder,
IDirect3DSurface9* dest_surface,
+ ID3D11Texture2D* dx11_texture,
int input_buffer_id);
bool available() const {
@@ -259,6 +280,7 @@ struct DXVAVideoDecodeAccelerator::DXVAPictureBuffer {
media::PictureBuffer picture_buffer_;
EGLSurface decoding_surface_;
base::win::ScopedComPtr<IDirect3DTexture9> decoding_texture_;
+ base::win::ScopedComPtr<ID3D11Texture2D> dx11_decoding_texture_;
// The following |IDirect3DSurface9| interface pointers are used to hold
// references on the surfaces during the course of a StretchRect operation
@@ -267,6 +289,11 @@ struct DXVAVideoDecodeAccelerator::DXVAPictureBuffer {
base::win::ScopedComPtr<IDirect3DSurface9> decoder_surface_;
base::win::ScopedComPtr<IDirect3DSurface9> target_surface_;
+ // This ID3D11Texture2D interface pointer is used to hold a reference to the
+ // decoder texture during the course of a copy operation. This reference is
+ // released when the copy completes.
+ base::win::ScopedComPtr<ID3D11Texture2D> decoder_dx11_texture_;
+
// Set to true if RGB is supported by the texture.
// Defaults to true.
bool use_rgb_;
@@ -315,17 +342,27 @@ DXVAVideoDecodeAccelerator::DXVAPictureBuffer::Create(
"Failed to query ANGLE surface pointer",
linked_ptr<DXVAPictureBuffer>(NULL));
- // TODO(dshwang): after moving to D3D11, use RGBA surface. crbug.com/438691
- HRESULT hr = decoder.device_->CreateTexture(
- buffer.size().width(),
- buffer.size().height(),
- 1,
- D3DUSAGE_RENDERTARGET,
- use_rgb ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8,
- D3DPOOL_DEFAULT,
- picture_buffer->decoding_texture_.Receive(),
- &share_handle);
-
+ HRESULT hr = E_FAIL;
+ if (decoder.d3d11_device_) {
+ base::win::ScopedComPtr<ID3D11Resource> resource;
+ hr = decoder.d3d11_device_->OpenSharedResource(
+ share_handle,
+ __uuidof(ID3D11Resource),
+ reinterpret_cast<void**>(resource.Receive()));
+ RETURN_ON_HR_FAILURE(hr, "Failed to open shared resource",
+ linked_ptr<DXVAPictureBuffer>(NULL));
+ hr = picture_buffer->dx11_decoding_texture_.QueryFrom(resource.get());
+ } else {
+ hr = decoder.d3d9_device_ex_->CreateTexture(
+ buffer.size().width(),
+ buffer.size().height(),
+ 1,
+ D3DUSAGE_RENDERTARGET,
+ use_rgb ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8,
+ D3DPOOL_DEFAULT,
+ picture_buffer->decoding_texture_.Receive(),
+ &share_handle);
+ }
RETURN_ON_HR_FAILURE(hr, "Failed to create texture",
linked_ptr<DXVAPictureBuffer>(NULL));
picture_buffer->use_rgb_ = !!use_rgb;
@@ -365,6 +402,7 @@ void DXVAVideoDecodeAccelerator::DXVAPictureBuffer::ReusePictureBuffer() {
EGL_BACK_BUFFER);
decoder_surface_.Release();
target_surface_.Release();
+ decoder_dx11_texture_.Release();
set_available(true);
}
@@ -372,9 +410,18 @@ bool DXVAVideoDecodeAccelerator::DXVAPictureBuffer::
CopyOutputSampleDataToPictureBuffer(
DXVAVideoDecodeAccelerator* decoder,
IDirect3DSurface9* dest_surface,
+ ID3D11Texture2D* dx11_texture,
int input_buffer_id) {
- DCHECK(dest_surface);
-
+ DCHECK(dest_surface || dx11_texture);
+ if (dx11_texture) {
+ // Grab a reference on the decoder texture. This reference will be released
+ // when we receive a notification that the copy was completed or when the
+ // DXVAPictureBuffer instance is destroyed.
+ decoder_dx11_texture_ = dx11_texture;
+ decoder->CopyTexture(dx11_texture, dx11_decoding_texture_.get(), NULL,
+ id(), input_buffer_id);
+ return true;
+ }
D3DSURFACE_DESC surface_desc;
HRESULT hr = dest_surface->GetDesc(&surface_desc);
RETURN_ON_HR_FAILURE(hr, "Failed to get surface description", false);
@@ -424,11 +471,15 @@ void DXVAVideoDecodeAccelerator::DXVAPictureBuffer::CopySurfaceComplete(
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- DCHECK_EQ(src_surface, decoder_surface_.get());
- DCHECK_EQ(dest_surface, target_surface_.get());
-
- decoder_surface_.Release();
- target_surface_.Release();
+ if (src_surface && dest_surface) {
+ DCHECK_EQ(src_surface, decoder_surface_.get());
+ DCHECK_EQ(dest_surface, target_surface_.get());
+ decoder_surface_.Release();
+ target_surface_.Release();
+ } else {
+ DCHECK(decoder_dx11_texture_.get());
+ decoder_dx11_texture_.Release();
+ }
EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay();
eglBindTexImage(
@@ -449,57 +500,12 @@ DXVAVideoDecodeAccelerator::PendingSampleInfo::PendingSampleInfo(
DXVAVideoDecodeAccelerator::PendingSampleInfo::~PendingSampleInfo() {}
-// static
-bool DXVAVideoDecodeAccelerator::CreateD3DDevManager() {
- TRACE_EVENT0("gpu", "DXVAVideoDecodeAccelerator_CreateD3DDevManager");
-
- 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_FPU_PRESERVE |
- D3DCREATE_SOFTWARE_VERTEXPROCESSING |
- D3DCREATE_DISABLE_PSGP_THREADING |
- D3DCREATE_MULTITHREADED,
- &present_params,
- NULL,
- device_.Receive());
- RETURN_ON_HR_FAILURE(hr, "Failed to create D3D device", false);
-
- hr = DXVA2CreateDirect3DDeviceManager9(&dev_manager_reset_token_,
- device_manager_.Receive());
- RETURN_ON_HR_FAILURE(hr, "DXVA2CreateDirect3DDeviceManager9 failed", false);
-
- hr = device_manager_->ResetDevice(device_.get(), dev_manager_reset_token_);
- RETURN_ON_HR_FAILURE(hr, "Failed to reset device", false);
-
- hr = device_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive());
- RETURN_ON_HR_FAILURE(hr, "Failed to create D3D device query", false);
- // Ensure query_ API works (to avoid an infinite loop later in
- // CopyOutputSampleDataToPictureBuffer).
- hr = query_->Issue(D3DISSUE_END);
- RETURN_ON_HR_FAILURE(hr, "Failed to issue END test query", false);
- return true;
-}
-
DXVAVideoDecodeAccelerator::DXVAVideoDecodeAccelerator(
- const base::Callback<bool(void)>& make_context_current)
+ const base::Callback<bool(void)>& make_context_current,
+ gfx::GLContext* gl_context)
: client_(NULL),
dev_manager_reset_token_(0),
+ dx11_dev_manager_reset_token_(0),
egl_config_(NULL),
state_(kUninitialized),
pictures_requested_(false),
@@ -510,7 +516,10 @@ DXVAVideoDecodeAccelerator::DXVAVideoDecodeAccelerator(
decoder_thread_("DXVAVideoDecoderThread"),
weak_this_factory_(this),
weak_ptr_(weak_this_factory_.GetWeakPtr()),
- pending_flush_(false) {
+ pending_flush_(false),
+ use_dx11_(false),
+ dx11_video_format_converter_media_type_needs_init_(true),
+ gl_context_(gl_context) {
memset(&input_stream_info_, 0, sizeof(input_stream_info_));
memset(&output_stream_info_, 0, sizeof(output_stream_info_));
}
@@ -525,14 +534,6 @@ bool DXVAVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile,
main_thread_task_runner_ = base::MessageLoop::current()->task_runner();
- // Not all versions of Windows 7 and later include Media Foundation DLLs.
- // Instead of crashing while delay loading the DLL when calling MFStartup()
- // below, probe whether we can successfully load the DLL now.
- //
- // See http://crbug.com/339678 for details.
- HMODULE mfplat_dll = ::LoadLibrary(L"MFPlat.dll");
- RETURN_ON_FAILURE(mfplat_dll, "MFPlat.dll is required for decoding", false);
-
if (profile != media::H264PROFILE_BASELINE &&
profile != media::H264PROFILE_MAIN &&
profile != media::H264PROFILE_HIGH &&
@@ -542,6 +543,40 @@ bool DXVAVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile,
"Unsupported h.264, vp8, or vp9 profile", PLATFORM_FAILURE, false);
}
+ // Not all versions of Windows 7 and later include Media Foundation DLLs.
+ // Instead of crashing while delay loading the DLL when calling MFStartup()
+ // below, probe whether we can successfully load the DLL now.
+ // See http://crbug.com/339678 for details.
+ HMODULE dxgi_manager_dll = NULL;
+ if ((dxgi_manager_dll = ::GetModuleHandle(L"MFPlat.dll")) == NULL) {
+ HMODULE mfplat_dll = ::LoadLibrary(L"MFPlat.dll");
+ RETURN_ON_FAILURE(mfplat_dll, "MFPlat.dll is required for decoding",
+ false);
+ // On Windows 8+ mfplat.dll provides the MFCreateDXGIDeviceManager API.
+ // On Windows 7 mshtmlmedia.dll provides it.
+ dxgi_manager_dll = mfplat_dll;
+ }
+
+ // TODO(ananta)
+ // The code below works, as in we can create the DX11 device manager for
+ // Windows 7. However the IMFTransform we use for texture conversion and
+ // copy does not exist on Windows 7. Look into an alternate approach
+ // and enable the code below.
+#if defined ENABLE_DX11_FOR_WIN7
+ if ((base::win::GetVersion() == base::win::VERSION_WIN7) &&
+ ((dxgi_manager_dll = ::GetModuleHandle(L"mshtmlmedia.dll")) == NULL)) {
+ HMODULE mshtml_media_dll = ::LoadLibrary(L"mshtmlmedia.dll");
+ if (mshtml_media_dll)
+ dxgi_manager_dll = mshtml_media_dll;
+ }
+#endif
+ // If we don't find the MFCreateDXGIDeviceManager API we fallback to D3D9
+ // decoding.
+ if (dxgi_manager_dll && !create_dxgi_device_manager_) {
+ create_dxgi_device_manager_ = reinterpret_cast<CreateDXGIDeviceManager>(
+ ::GetProcAddress(dxgi_manager_dll, "MFCreateDXGIDeviceManager"));
+ }
+
RETURN_AND_NOTIFY_ON_FAILURE(
gfx::g_driver_egl.ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle,
"EGL_ANGLE_surface_d3d_texture_2d_share_handle unavailable",
@@ -556,11 +591,6 @@ bool DXVAVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile,
RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "MFStartup failed.", PLATFORM_FAILURE,
false);
- RETURN_AND_NOTIFY_ON_FAILURE(CreateD3DDevManager(),
- "Failed to initialize D3D device and manager",
- PLATFORM_FAILURE,
- false);
-
RETURN_AND_NOTIFY_ON_FAILURE(InitDecoder(profile),
"Failed to initialize decoder", PLATFORM_FAILURE, false);
@@ -583,6 +613,128 @@ bool DXVAVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile,
return true;
}
+bool DXVAVideoDecodeAccelerator::CreateD3DDevManager() {
+ TRACE_EVENT0("gpu", "DXVAVideoDecodeAccelerator_CreateD3DDevManager");
+
+ 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_FPU_PRESERVE |
+ D3DCREATE_SOFTWARE_VERTEXPROCESSING |
+ D3DCREATE_DISABLE_PSGP_THREADING |
+ D3DCREATE_MULTITHREADED,
+ &present_params,
+ NULL,
+ d3d9_device_ex_.Receive());
+ RETURN_ON_HR_FAILURE(hr, "Failed to create D3D device", false);
+
+ hr = DXVA2CreateDirect3DDeviceManager9(&dev_manager_reset_token_,
+ device_manager_.Receive());
+ RETURN_ON_HR_FAILURE(hr, "DXVA2CreateDirect3DDeviceManager9 failed", false);
+
+ hr = device_manager_->ResetDevice(d3d9_device_ex_.get(),
+ dev_manager_reset_token_);
+ RETURN_ON_HR_FAILURE(hr, "Failed to reset device", false);
+
+ hr = d3d9_device_ex_->CreateQuery(D3DQUERYTYPE_EVENT, query_.Receive());
+ RETURN_ON_HR_FAILURE(hr, "Failed to create D3D device query", false);
+ // Ensure query_ API works (to avoid an infinite loop later in
+ // CopyOutputSampleDataToPictureBuffer).
+ hr = query_->Issue(D3DISSUE_END);
+ RETURN_ON_HR_FAILURE(hr, "Failed to issue END test query", false);
+ return true;
+}
+
+bool DXVAVideoDecodeAccelerator::CreateDX11DevManager() {
+ HRESULT hr = create_dxgi_device_manager_(&dx11_dev_manager_reset_token_,
+ d3d11_device_manager_.Receive());
+ RETURN_ON_HR_FAILURE(hr, "MFCreateDXGIDeviceManager failed", false);
+
+ // This array defines the set of DirectX hardware feature levels we support.
+ // The ordering MUST be preserved. All applications are assumed to support
+ // 9.1 unless otherwise stated by the application, which is not our case.
+ D3D_FEATURE_LEVEL feature_levels[] = {
+ D3D_FEATURE_LEVEL_11_1,
+ D3D_FEATURE_LEVEL_11_0,
+ D3D_FEATURE_LEVEL_10_1,
+ D3D_FEATURE_LEVEL_10_0,
+ D3D_FEATURE_LEVEL_9_3,
+ D3D_FEATURE_LEVEL_9_2,
+ D3D_FEATURE_LEVEL_9_1 };
+
+ UINT flags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
+
+#if defined _DEBUG
+ flags |= D3D11_CREATE_DEVICE_DEBUG;
+#endif
+
+ D3D_FEATURE_LEVEL feature_level_out = D3D_FEATURE_LEVEL_11_0;
+ hr = D3D11CreateDevice(NULL,
+ D3D_DRIVER_TYPE_HARDWARE,
+ NULL,
+ flags,
+ feature_levels,
+ arraysize(feature_levels),
+ D3D11_SDK_VERSION,
+ d3d11_device_.Receive(),
+ &feature_level_out,
+ d3d11_device_context_.Receive());
+ RETURN_ON_HR_FAILURE(hr, "Failed to create DX11 device", false);
+
+ // Enable multithreaded mode on the context. This ensures that accesses to
+ // context are synchronized across threads. We have multiple threads
+ // accessing the context, the media foundation decoder threads and the
+ // decoder thread via the video format conversion transform.
+ base::win::ScopedComPtr<ID3D10Multithread> multi_threaded;
+ hr = multi_threaded.QueryFrom(d3d11_device_context_.get());
+ RETURN_ON_HR_FAILURE(hr, "Failed to query ID3D10Multithread", false);
+ multi_threaded->SetMultithreadProtected(TRUE);
+
+ hr = d3d11_device_manager_->ResetDevice(d3d11_device_.get(),
+ dx11_dev_manager_reset_token_);
+ RETURN_ON_HR_FAILURE(hr, "Failed to reset device", false);
+
+ D3D11_QUERY_DESC query_desc;
+ query_desc.Query = D3D11_QUERY_EVENT;
+ query_desc.MiscFlags = 0;
+ hr = d3d11_device_->CreateQuery(
+ &query_desc,
+ d3d11_query_.Receive());
+ RETURN_ON_HR_FAILURE(hr, "Failed to create DX11 device query", false);
+
+ hr = ::CoCreateInstance(
+ CLSID_VideoProcessorMFT,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IMFTransform,
+ reinterpret_cast<void**>(video_format_converter_mft_.Receive()));
+
+ if (FAILED(hr)) {
+ base::debug::Alias(&hr);
+ // TODO(ananta)
+ // Remove this CHECK when the change to use DX11 for H/W decoding
+ // stablizes.
+ CHECK(false);
+ }
+ RETURN_ON_HR_FAILURE(hr, "Failed to create video format converter", false);
+ return true;
+}
+
void DXVAVideoDecodeAccelerator::Decode(
const media::BitstreamBuffer& bitstream_buffer) {
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
@@ -632,6 +784,7 @@ void DXVAVideoDecodeAccelerator::AssignPictureBuffers(
buffers[buffer_index].id(), picture_buffer)).second;
DCHECK(inserted);
}
+
ProcessPendingSamples();
if (pending_flush_) {
decoder_thread_task_runner_->PostTask(
@@ -840,10 +993,31 @@ bool DXVAVideoDecodeAccelerator::InitDecoder(media::VideoCodecProfile profile) {
RETURN_ON_FAILURE(CheckDecoderDxvaSupport(),
"Failed to check decoder DXVA support", false);
+ ULONG_PTR device_manager_to_use = NULL;
+ if (use_dx11_) {
+ CHECK(create_dxgi_device_manager_);
+ RETURN_AND_NOTIFY_ON_FAILURE(CreateDX11DevManager(),
+ "Failed to initialize DX11 device and manager",
+ PLATFORM_FAILURE,
+ false);
+ device_manager_to_use = reinterpret_cast<ULONG_PTR>(
+ d3d11_device_manager_.get());
+ } else {
+ RETURN_AND_NOTIFY_ON_FAILURE(CreateD3DDevManager(),
+ "Failed to initialize D3D device and manager",
+ PLATFORM_FAILURE,
+ false);
+ device_manager_to_use = reinterpret_cast<ULONG_PTR>(device_manager_.get());
+ }
+
hr = decoder_->ProcessMessage(
MFT_MESSAGE_SET_D3D_MANAGER,
- reinterpret_cast<ULONG_PTR>(device_manager_.get()));
- RETURN_ON_HR_FAILURE(hr, "Failed to pass D3D manager to decoder", false);
+ device_manager_to_use);
+ if (use_dx11_) {
+ RETURN_ON_HR_FAILURE(hr, "Failed to pass DX11 manager to decoder", false);
+ } else {
+ RETURN_ON_HR_FAILURE(hr, "Failed to pass D3D manager to decoder", false);
+ }
EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay();
@@ -890,6 +1064,20 @@ bool DXVAVideoDecodeAccelerator::CheckDecoderDxvaSupport() {
} else {
DVLOG(1) << "Failed to set Low latency mode on decoder. Error: " << hr;
}
+
+ // The decoder should use DX11 iff
+ // 1. The underlying H/W decoder supports it.
+ // 2. We have a pointer to the MFCreateDXGIDeviceManager function needed for
+ // this. This should always be true for Windows 8+.
+ // 3. ANGLE is using DX11.
+ DCHECK(gl_context_);
+ if (create_dxgi_device_manager_ &&
+ (gl_context_->GetGLRenderer().find("Direct3D11") !=
+ std::string::npos)) {
+ UINT32 dx11_aware = 0;
+ attributes->GetUINT32(MF_SA_D3D11_AWARE, &dx11_aware);
+ use_dx11_ = !!dx11_aware;
+ }
return true;
}
@@ -1054,16 +1242,6 @@ void DXVAVideoDecodeAccelerator::DoDecode() {
bool DXVAVideoDecodeAccelerator::ProcessOutputSample(IMFSample* sample) {
RETURN_ON_FAILURE(sample, "Decode succeeded with NULL output sample", false);
- 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.get(), MR_BUFFER_SERVICE,
- IID_PPV_ARGS(surface.Receive()));
- RETURN_ON_HR_FAILURE(hr, "Failed to get D3D surface from output sample",
- false);
-
LONGLONG input_buffer_id = 0;
RETURN_ON_HR_FAILURE(sample->GetSampleTime(&input_buffer_id),
"Failed to get input buffer id associated with sample",
@@ -1085,20 +1263,20 @@ bool DXVAVideoDecodeAccelerator::ProcessOutputSample(IMFSample* sample) {
return true;
}
- // We only read the surface description, which contains its width/height when
- // we need the picture buffers from the client. Once we have those, then they
- // are reused.
- D3DSURFACE_DESC surface_desc;
- hr = surface->GetDesc(&surface_desc);
- RETURN_ON_HR_FAILURE(hr, "Failed to get surface description", false);
+ int width = 0;
+ int height = 0;
+ if (!GetVideoFrameDimensions(sample, &width, &height)) {
+ RETURN_ON_FAILURE(false, "Failed to get D3D surface from output sample",
+ false);
+ }
// Go ahead and request picture buffers.
main_thread_task_runner_->PostTask(
FROM_HERE,
base::Bind(&DXVAVideoDecodeAccelerator::RequestPictureBuffers,
weak_this_factory_.GetWeakPtr(),
- surface_desc.Width,
- surface_desc.Height));
+ width,
+ height));
pictures_requested_ = true;
return true;
@@ -1130,31 +1308,43 @@ void DXVAVideoDecodeAccelerator::ProcessPendingSamples() {
pending_sample = &sample_info;
}
+ int width = 0;
+ int height = 0;
+ if (!GetVideoFrameDimensions(pending_sample->output_sample.get(),
+ &width, &height)) {
+ RETURN_AND_NOTIFY_ON_FAILURE(false,
+ "Failed to get D3D surface from output sample", PLATFORM_FAILURE,);
+ }
+
+ if (width != index->second->size().width() ||
+ height != index->second->size().height()) {
+ HandleResolutionChanged(width, height);
+ return;
+ }
+
base::win::ScopedComPtr<IMFMediaBuffer> output_buffer;
HRESULT hr = pending_sample->output_sample->GetBufferByIndex(
0, output_buffer.Receive());
- RETURN_AND_NOTIFY_ON_HR_FAILURE(
- hr, "Failed to get buffer from output sample", PLATFORM_FAILURE,);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to get buffer from output sample", PLATFORM_FAILURE,);
base::win::ScopedComPtr<IDirect3DSurface9> surface;
- hr = MFGetService(output_buffer.get(), MR_BUFFER_SERVICE,
- IID_PPV_ARGS(surface.Receive()));
- RETURN_AND_NOTIFY_ON_HR_FAILURE(
- hr, "Failed to get D3D surface from output sample",
- PLATFORM_FAILURE,);
-
- D3DSURFACE_DESC surface_desc;
- hr = surface->GetDesc(&surface_desc);
- RETURN_AND_NOTIFY_ON_HR_FAILURE(
- hr, "Failed to get surface description", PLATFORM_FAILURE,);
-
- if (surface_desc.Width !=
- static_cast<uint32>(index->second->size().width()) ||
- surface_desc.Height !=
- static_cast<uint32>(index->second->size().height())) {
- HandleResolutionChanged(surface_desc.Width, surface_desc.Height);
- return;
- }
+ base::win::ScopedComPtr<ID3D11Texture2D> d3d11_texture;
+
+ if (use_dx11_) {
+ base::win::ScopedComPtr<IMFDXGIBuffer> dxgi_buffer;
+ hr = dxgi_buffer.QueryFrom(output_buffer.get());
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to get DXGIBuffer from output sample", PLATFORM_FAILURE,);
+ hr = dxgi_buffer->GetResource(
+ __uuidof(ID3D11Texture2D),
+ reinterpret_cast<void**>(d3d11_texture.Receive()));
+ } else {
+ hr = MFGetService(output_buffer.get(), MR_BUFFER_SERVICE,
+ IID_PPV_ARGS(surface.Receive()));
+ }
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to get surface from output sample", PLATFORM_FAILURE,);
pending_sample->picture_buffer_id = index->second->id();
@@ -1162,8 +1352,9 @@ void DXVAVideoDecodeAccelerator::ProcessPendingSamples() {
index->second->CopyOutputSampleDataToPictureBuffer(
this,
surface.get(),
+ d3d11_texture.get(),
pending_sample->input_buffer_id),
- "Failed to copy output sample", PLATFORM_FAILURE, );
+ "Failed to copy output sample", PLATFORM_FAILURE,);
index->second->set_available(false);
}
@@ -1200,7 +1391,13 @@ void DXVAVideoDecodeAccelerator::Invalidate() {
pending_output_samples_.clear();
pending_input_buffers_.clear();
decoder_.Release();
+ if (video_format_converter_mft_.get()) {
+ video_format_converter_mft_->ProcessMessage(
+ MFT_MESSAGE_NOTIFY_END_STREAMING, 0);
+ video_format_converter_mft_.Release();
+ }
MFShutdown();
+ dx11_video_format_converter_media_type_needs_init_ = true;
SetState(kUninitialized);
}
@@ -1425,6 +1622,8 @@ void DXVAVideoDecodeAccelerator::DecodeInternal(
void DXVAVideoDecodeAccelerator::HandleResolutionChanged(int width,
int height) {
+ dx11_video_format_converter_media_type_needs_init_ = true;
+
main_thread_task_runner_->PostTask(
FROM_HERE,
base::Bind(&DXVAVideoDecodeAccelerator::DismissStaleBuffers,
@@ -1518,8 +1717,8 @@ void DXVAVideoDecodeAccelerator::CopySurface(IDirect3DSurface9* src_surface,
return;
}
- HRESULT hr = device_->StretchRect(src_surface, NULL, dest_surface,
- NULL, D3DTEXF_NONE);
+ HRESULT hr = d3d9_device_ex_->StretchRect(src_surface, NULL, dest_surface,
+ NULL, D3DTEXF_NONE);
RETURN_ON_HR_FAILURE(hr, "Colorspace conversion via StretchRect failed",);
// Ideally, this should be done immediately before the draw call that uses
@@ -1590,6 +1789,135 @@ void DXVAVideoDecodeAccelerator::CopySurfaceComplete(
base::Unretained(this)));
}
+void DXVAVideoDecodeAccelerator::CopyTexture(ID3D11Texture2D* src_texture,
+ ID3D11Texture2D* dest_texture,
+ IMFSample* video_frame,
+ int picture_buffer_id,
+ int input_buffer_id) {
+ HRESULT hr = E_FAIL;
+
+ DCHECK(use_dx11_);
+
+ if (!decoder_thread_task_runner_->BelongsToCurrentThread()) {
+ // The media foundation H.264 decoder outputs YUV12 textures which we
+ // cannot copy into ANGLE as they expect ARGB textures. In D3D land
+ // the StretchRect API in the IDirect3DDevice9Ex interface did the color
+ // space conversion for us. Sadly in DX11 land the API does not provide
+ // a straightforward way to do this.
+ // We use the video processor MFT.
+ // https://msdn.microsoft.com/en-us/library/hh162913(v=vs.85).aspx
+ // This object implements a media foundation transform (IMFTransform)
+ // which follows the same contract as the decoder. The color space
+ // conversion as per msdn is done in the GPU.
+
+ D3D11_TEXTURE2D_DESC source_desc;
+ src_texture->GetDesc(&source_desc);
+
+ // Set up the input and output types for the video processor MFT.
+ if (!InitializeDX11VideoFormatConverterMediaType(source_desc.Width,
+ source_desc.Height)) {
+ RETURN_AND_NOTIFY_ON_FAILURE(
+ false, "Failed to initialize media types for convesion.",
+ PLATFORM_FAILURE,);
+ }
+
+ // The input to the video processor is the output sample.
+ base::win::ScopedComPtr<IMFSample> input_sample_for_conversion;
+ {
+ base::AutoLock lock(decoder_lock_);
+ PendingSampleInfo& sample_info = pending_output_samples_.front();
+ input_sample_for_conversion = sample_info.output_sample;
+ }
+
+ decoder_thread_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&DXVAVideoDecodeAccelerator::CopyTexture,
+ base::Unretained(this),
+ src_texture,
+ dest_texture,
+ input_sample_for_conversion.Detach(),
+ picture_buffer_id,
+ input_buffer_id));
+ return;
+ }
+
+ DCHECK(video_frame);
+
+ base::win::ScopedComPtr<IMFSample> input_sample;
+ input_sample.Attach(video_frame);
+
+ DCHECK(video_format_converter_mft_.get());
+
+ // d3d11_device_context_->Begin(d3d11_query_.get());
+
+ hr = video_format_converter_mft_->ProcessInput(0, video_frame, 0);
+ if (FAILED(hr)) {
+ DCHECK(false);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to convert output sample format.", PLATFORM_FAILURE,);
+ }
+
+ // The video processor MFT requires output samples to be allocated by the
+ // caller. We create a sample with a buffer backed with the ID3D11Texture2D
+ // interface exposed by ANGLE. This works nicely as this ensures that the
+ // video processor coverts the color space of the output frame and copies
+ // the result into the ANGLE texture.
+ base::win::ScopedComPtr<IMFSample> output_sample;
+ hr = MFCreateSample(output_sample.Receive());
+ if (FAILED(hr)) {
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to create output sample.", PLATFORM_FAILURE,);
+ }
+
+ base::win::ScopedComPtr<IMFMediaBuffer> output_buffer;
+ hr = MFCreateDXGISurfaceBuffer(
+ __uuidof(ID3D11Texture2D), dest_texture, 0, FALSE,
+ output_buffer.Receive());
+ if (FAILED(hr)) {
+ base::debug::Alias(&hr);
+ // TODO(ananta)
+ // Remove this CHECK when the change to use DX11 for H/W decoding
+ // stablizes.
+ CHECK(false);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to create output sample.", PLATFORM_FAILURE,);
+ }
+
+ output_sample->AddBuffer(output_buffer.get());
+
+ DWORD status = 0;
+ MFT_OUTPUT_DATA_BUFFER format_converter_output = {};
+ format_converter_output.pSample = output_sample.get();
+ hr = video_format_converter_mft_->ProcessOutput(
+ 0, // No flags
+ 1, // # of out streams to pull from
+ &format_converter_output,
+ &status);
+
+ d3d11_device_context_->Flush();
+ d3d11_device_context_->End(d3d11_query_.get());
+
+ if (FAILED(hr)) {
+ base::debug::Alias(&hr);
+ // TODO(ananta)
+ // Remove this CHECK when the change to use DX11 for H/W decoding
+ // stablizes.
+ CHECK(false);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to convert output sample format.", PLATFORM_FAILURE,);
+ }
+
+ decoder_thread_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&DXVAVideoDecodeAccelerator::FlushDecoder,
+ base::Unretained(this), 0,
+ reinterpret_cast<IDirect3DSurface9*>(NULL),
+ reinterpret_cast<IDirect3DSurface9*>(NULL),
+ picture_buffer_id, input_buffer_id),
+ base::TimeDelta::FromMilliseconds(
+ kFlushDecoderSurfaceTimeoutMs));
+}
+
void DXVAVideoDecodeAccelerator::FlushDecoder(
int iterations,
IDirect3DSurface9* src_surface,
@@ -1611,7 +1939,22 @@ void DXVAVideoDecodeAccelerator::FlushDecoder(
// infinite loop.
// Workaround is to have an upper limit of 4 on the number of iterations to
// wait for the Flush to finish.
- HRESULT hr = query_->GetData(NULL, 0, D3DGETDATA_FLUSH);
+ HRESULT hr = E_FAIL;
+
+ if (use_dx11_) {
+ BOOL query_data = 0;
+ hr = d3d11_device_context_->GetData(d3d11_query_.get(), &query_data,
+ sizeof(BOOL), 0);
+ if (FAILED(hr)) {
+ base::debug::Alias(&hr);
+ // TODO(ananta)
+ // Remove this CHECK when the change to use DX11 for H/W decoding
+ // stablizes.
+ CHECK(false);
+ }
+ } else {
+ hr = query_->GetData(NULL, 0, D3DGETDATA_FLUSH);
+ }
if ((hr == S_FALSE) && (++iterations < kMaxIterationsForD3DFlush)) {
decoder_thread_task_runner_->PostDelayedTask(
FROM_HERE,
@@ -1621,14 +1964,185 @@ void DXVAVideoDecodeAccelerator::FlushDecoder(
base::TimeDelta::FromMilliseconds(kFlushDecoderSurfaceTimeoutMs));
return;
}
+
main_thread_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&DXVAVideoDecodeAccelerator::CopySurfaceComplete,
- weak_this_factory_.GetWeakPtr(),
- src_surface,
- dest_surface,
- picture_buffer_id,
- input_buffer_id));
+ FROM_HERE,
+ base::Bind(&DXVAVideoDecodeAccelerator::CopySurfaceComplete,
+ weak_this_factory_.GetWeakPtr(),
+ src_surface,
+ dest_surface,
+ picture_buffer_id,
+ input_buffer_id));
+}
+
+bool DXVAVideoDecodeAccelerator::InitializeDX11VideoFormatConverterMediaType(
+ int width, int height) {
+ if (!dx11_video_format_converter_media_type_needs_init_)
+ return true;
+
+ CHECK(video_format_converter_mft_.get());
+
+ HRESULT hr = video_format_converter_mft_->ProcessMessage(
+ MFT_MESSAGE_SET_D3D_MANAGER,
+ reinterpret_cast<ULONG_PTR>(
+ d3d11_device_manager_.get()));
+
+ if (FAILED(hr)) {
+ base::debug::Alias(&hr);
+ // TODO(ananta)
+ // Remove this CHECK when the change to use DX11 for H/W decoding
+ // stablizes.
+ CHECK(false);
+ }
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to initialize video format converter", PLATFORM_FAILURE, false);
+
+ video_format_converter_mft_->ProcessMessage(
+ MFT_MESSAGE_NOTIFY_END_STREAMING, 0);
+
+ base::win::ScopedComPtr<IMFMediaType> media_type;
+ hr = MFCreateMediaType(media_type.Receive());
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "MFCreateMediaType failed",
+ PLATFORM_FAILURE, false);
+
+ hr = media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to set major input type",
+ PLATFORM_FAILURE, false);
+
+ hr = media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to set input sub type",
+ PLATFORM_FAILURE, false);
+
+ hr = media_type->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to set attributes on media type", PLATFORM_FAILURE, false);
+
+ hr = media_type->SetUINT32(MF_MT_INTERLACE_MODE,
+ MFVideoInterlace_Progressive);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to set attributes on media type", PLATFORM_FAILURE, false);
+
+ base::win::ScopedComPtr<IMFAttributes> converter_attributes;
+ hr = video_format_converter_mft_->GetAttributes(
+ converter_attributes.Receive());
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to get converter attributes",
+ PLATFORM_FAILURE, false);
+
+ hr = converter_attributes->SetUINT32(MF_XVP_PLAYBACK_MODE, TRUE);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to set converter attributes",
+ PLATFORM_FAILURE, false);
+
+ hr = converter_attributes->SetUINT32(MF_LOW_LATENCY, FALSE);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to set converter attributes",
+ PLATFORM_FAILURE, false);
+
+ hr = MFSetAttributeSize(media_type.get(), MF_MT_FRAME_SIZE, width, height);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to set media type attributes",
+ PLATFORM_FAILURE, false);
+
+ hr = video_format_converter_mft_->SetInputType(0, media_type.get(), 0);
+ if (FAILED(hr)) {
+ base::debug::Alias(&hr);
+ // TODO(ananta)
+ // Remove this CHECK when the change to use DX11 for H/W decoding
+ // stablizes.
+ CHECK(false);
+ }
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to set converter input type",
+ PLATFORM_FAILURE, false);
+
+ base::win::ScopedComPtr<IMFMediaType> out_media_type;
+
+ for (uint32 i = 0;
+ SUCCEEDED(video_format_converter_mft_->GetOutputAvailableType(0, i,
+ out_media_type.Receive()));
+ ++i) {
+ GUID out_subtype = {0};
+ hr = out_media_type->GetGUID(MF_MT_SUBTYPE, &out_subtype);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr, "Failed to get output major type",
+ PLATFORM_FAILURE, false);
+
+ if (out_subtype == MFVideoFormat_ARGB32) {
+ hr = out_media_type->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to set attributes on media type", PLATFORM_FAILURE, false);
+
+ hr = out_media_type->SetUINT32(MF_MT_INTERLACE_MODE,
+ MFVideoInterlace_Progressive);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to set attributes on media type", PLATFORM_FAILURE, false);
+
+ hr = MFSetAttributeSize(out_media_type.get(), MF_MT_FRAME_SIZE, width,
+ height);
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to set media type attributes", PLATFORM_FAILURE, false);
+
+ hr = video_format_converter_mft_->SetOutputType(
+ 0, out_media_type.get(), 0); // No flags
+ if (FAILED(hr)) {
+ base::debug::Alias(&hr);
+ // TODO(ananta)
+ // Remove this CHECK when the change to use DX11 for H/W decoding
+ // stablizes.
+ CHECK(false);
+ }
+ RETURN_AND_NOTIFY_ON_HR_FAILURE(hr,
+ "Failed to set converter output type", PLATFORM_FAILURE, false);
+
+ hr = video_format_converter_mft_->ProcessMessage(
+ MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
+ if (FAILED(hr)) {
+ // TODO(ananta)
+ // Remove this CHECK when the change to use DX11 for H/W decoding
+ // stablizes.
+ RETURN_AND_NOTIFY_ON_FAILURE(
+ false, "Failed to initialize video converter.", PLATFORM_FAILURE,
+ false);
+ }
+ dx11_video_format_converter_media_type_needs_init_ = false;
+ return true;
+ }
+ out_media_type.Release();
+ }
+ return false;
+}
+
+bool DXVAVideoDecodeAccelerator::GetVideoFrameDimensions(
+ IMFSample* sample,
+ int* width,
+ int* height) {
+ 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);
+
+ if (use_dx11_) {
+ base::win::ScopedComPtr<IMFDXGIBuffer> dxgi_buffer;
+ base::win::ScopedComPtr<ID3D11Texture2D> d3d11_texture;
+ hr = dxgi_buffer.QueryFrom(output_buffer.get());
+ RETURN_ON_HR_FAILURE(hr, "Failed to get DXGIBuffer from output sample",
+ false);
+ hr = dxgi_buffer->GetResource(
+ __uuidof(ID3D11Texture2D),
+ reinterpret_cast<void**>(d3d11_texture.Receive()));
+ RETURN_ON_HR_FAILURE(hr, "Failed to get D3D11Texture from output buffer",
+ false);
+ D3D11_TEXTURE2D_DESC d3d11_texture_desc;
+ d3d11_texture->GetDesc(&d3d11_texture_desc);
+ *width = d3d11_texture_desc.Width;
+ *height = d3d11_texture_desc.Height;
+ } else {
+ base::win::ScopedComPtr<IDirect3DSurface9> surface;
+ hr = MFGetService(output_buffer.get(), 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);
+ *width = surface_desc.Width;
+ *height = surface_desc.Height;
+ }
+ return true;
}
} // namespace content
« no previous file with comments | « content/common/gpu/media/dxva_video_decode_accelerator.h ('k') | content/common/gpu/media/gpu_video_decode_accelerator.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698