mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-26 19:01:44 +02:00
ae5453503d
At least on latest Win 11 and Visual Studio 2022, that DLL does not exist anymore and can't be installed via any of the usual means. However, debugging works just fine regardless, so this check makes debugging impossible. D3D11CreateDevice will fail anyway if debugging is not supported, so let's rely on that instead.
716 lines
24 KiB
C
716 lines
24 KiB
C
/*
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* FFmpeg is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <windows.h>
|
|
|
|
#define COBJMACROS
|
|
|
|
#include <initguid.h>
|
|
#include <d3d11.h>
|
|
#include <dxgi1_2.h>
|
|
|
|
#if HAVE_DXGIDEBUG_H
|
|
#include <dxgidebug.h>
|
|
#endif
|
|
|
|
#include "avassert.h"
|
|
#include "common.h"
|
|
#include "hwcontext.h"
|
|
#include "hwcontext_d3d11va.h"
|
|
#include "hwcontext_internal.h"
|
|
#include "imgutils.h"
|
|
#include "pixdesc.h"
|
|
#include "pixfmt.h"
|
|
#include "thread.h"
|
|
#include "compat/w32dlfcn.h"
|
|
|
|
typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
|
|
|
|
static AVOnce functions_loaded = AV_ONCE_INIT;
|
|
|
|
static PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
|
|
static PFN_D3D11_CREATE_DEVICE mD3D11CreateDevice;
|
|
|
|
static av_cold void load_functions(void)
|
|
{
|
|
#if !HAVE_UWP
|
|
// We let these "leak" - this is fine, as unloading has no great benefit, and
|
|
// Windows will mark a DLL as loaded forever if its internal refcount overflows
|
|
// from too many LoadLibrary calls.
|
|
HANDLE d3dlib, dxgilib;
|
|
|
|
d3dlib = dlopen("d3d11.dll", 0);
|
|
dxgilib = dlopen("dxgi.dll", 0);
|
|
if (!d3dlib || !dxgilib)
|
|
return;
|
|
|
|
mD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3dlib, "D3D11CreateDevice");
|
|
mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory");
|
|
#else
|
|
// In UWP (which lacks LoadLibrary), CreateDXGIFactory isn't available,
|
|
// only CreateDXGIFactory1
|
|
mD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) D3D11CreateDevice;
|
|
mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) CreateDXGIFactory1;
|
|
#endif
|
|
}
|
|
|
|
typedef struct D3D11VAFramesContext {
|
|
/**
|
|
* The public AVD3D11VAFramesContext. See hwcontext_d3d11va.h for it.
|
|
*/
|
|
AVD3D11VAFramesContext p;
|
|
|
|
int nb_surfaces;
|
|
int nb_surfaces_used;
|
|
|
|
DXGI_FORMAT format;
|
|
|
|
ID3D11Texture2D *staging_texture;
|
|
} D3D11VAFramesContext;
|
|
|
|
static const struct {
|
|
DXGI_FORMAT d3d_format;
|
|
enum AVPixelFormat pix_fmt;
|
|
} supported_formats[] = {
|
|
{ DXGI_FORMAT_NV12, AV_PIX_FMT_NV12 },
|
|
{ DXGI_FORMAT_P010, AV_PIX_FMT_P010 },
|
|
{ DXGI_FORMAT_B8G8R8A8_UNORM, AV_PIX_FMT_BGRA },
|
|
{ DXGI_FORMAT_R10G10B10A2_UNORM, AV_PIX_FMT_X2BGR10 },
|
|
{ DXGI_FORMAT_R16G16B16A16_FLOAT, AV_PIX_FMT_RGBAF16 },
|
|
{ DXGI_FORMAT_AYUV, AV_PIX_FMT_VUYX },
|
|
{ DXGI_FORMAT_YUY2, AV_PIX_FMT_YUYV422 },
|
|
{ DXGI_FORMAT_Y210, AV_PIX_FMT_Y210 },
|
|
{ DXGI_FORMAT_Y410, AV_PIX_FMT_XV30 },
|
|
{ DXGI_FORMAT_P016, AV_PIX_FMT_P012 },
|
|
{ DXGI_FORMAT_Y216, AV_PIX_FMT_Y212 },
|
|
{ DXGI_FORMAT_Y416, AV_PIX_FMT_XV36 },
|
|
// Special opaque formats. The pix_fmt is merely a place holder, as the
|
|
// opaque format cannot be accessed directly.
|
|
{ DXGI_FORMAT_420_OPAQUE, AV_PIX_FMT_YUV420P },
|
|
};
|
|
|
|
static void d3d11va_default_lock(void *ctx)
|
|
{
|
|
WaitForSingleObjectEx(ctx, INFINITE, FALSE);
|
|
}
|
|
|
|
static void d3d11va_default_unlock(void *ctx)
|
|
{
|
|
ReleaseMutex(ctx);
|
|
}
|
|
|
|
static void d3d11va_frames_uninit(AVHWFramesContext *ctx)
|
|
{
|
|
D3D11VAFramesContext *s = ctx->hwctx;
|
|
AVD3D11VAFramesContext *frames_hwctx = &s->p;
|
|
|
|
if (frames_hwctx->texture)
|
|
ID3D11Texture2D_Release(frames_hwctx->texture);
|
|
frames_hwctx->texture = NULL;
|
|
|
|
if (s->staging_texture)
|
|
ID3D11Texture2D_Release(s->staging_texture);
|
|
s->staging_texture = NULL;
|
|
|
|
av_freep(&frames_hwctx->texture_infos);
|
|
}
|
|
|
|
static int d3d11va_frames_get_constraints(AVHWDeviceContext *ctx,
|
|
const void *hwconfig,
|
|
AVHWFramesConstraints *constraints)
|
|
{
|
|
AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
|
|
int nb_sw_formats = 0;
|
|
HRESULT hr;
|
|
int i;
|
|
|
|
constraints->valid_sw_formats = av_malloc_array(FF_ARRAY_ELEMS(supported_formats) + 1,
|
|
sizeof(*constraints->valid_sw_formats));
|
|
if (!constraints->valid_sw_formats)
|
|
return AVERROR(ENOMEM);
|
|
|
|
for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
|
|
UINT format_support = 0;
|
|
hr = ID3D11Device_CheckFormatSupport(device_hwctx->device, supported_formats[i].d3d_format, &format_support);
|
|
if (SUCCEEDED(hr) && (format_support & D3D11_FORMAT_SUPPORT_TEXTURE2D))
|
|
constraints->valid_sw_formats[nb_sw_formats++] = supported_formats[i].pix_fmt;
|
|
}
|
|
constraints->valid_sw_formats[nb_sw_formats] = AV_PIX_FMT_NONE;
|
|
|
|
constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats));
|
|
if (!constraints->valid_hw_formats)
|
|
return AVERROR(ENOMEM);
|
|
|
|
constraints->valid_hw_formats[0] = AV_PIX_FMT_D3D11;
|
|
constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void free_texture(void *opaque, uint8_t *data)
|
|
{
|
|
ID3D11Texture2D_Release((ID3D11Texture2D *)opaque);
|
|
av_free(data);
|
|
}
|
|
|
|
static AVBufferRef *wrap_texture_buf(AVHWFramesContext *ctx, ID3D11Texture2D *tex, int index)
|
|
{
|
|
AVBufferRef *buf;
|
|
AVD3D11FrameDescriptor *desc = av_mallocz(sizeof(*desc));
|
|
D3D11VAFramesContext *s = ctx->hwctx;
|
|
AVD3D11VAFramesContext *frames_hwctx = &s->p;
|
|
if (!desc) {
|
|
ID3D11Texture2D_Release(tex);
|
|
return NULL;
|
|
}
|
|
|
|
if (s->nb_surfaces <= s->nb_surfaces_used) {
|
|
frames_hwctx->texture_infos = av_realloc_f(frames_hwctx->texture_infos,
|
|
s->nb_surfaces_used + 1,
|
|
sizeof(*frames_hwctx->texture_infos));
|
|
if (!frames_hwctx->texture_infos) {
|
|
ID3D11Texture2D_Release(tex);
|
|
return NULL;
|
|
}
|
|
s->nb_surfaces = s->nb_surfaces_used + 1;
|
|
}
|
|
|
|
frames_hwctx->texture_infos[s->nb_surfaces_used].texture = tex;
|
|
frames_hwctx->texture_infos[s->nb_surfaces_used].index = index;
|
|
s->nb_surfaces_used++;
|
|
|
|
desc->texture = tex;
|
|
desc->index = index;
|
|
|
|
buf = av_buffer_create((uint8_t *)desc, sizeof(desc), free_texture, tex, 0);
|
|
if (!buf) {
|
|
ID3D11Texture2D_Release(tex);
|
|
av_free(desc);
|
|
return NULL;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static AVBufferRef *d3d11va_alloc_single(AVHWFramesContext *ctx)
|
|
{
|
|
D3D11VAFramesContext *s = ctx->hwctx;
|
|
AVD3D11VAFramesContext *hwctx = &s->p;
|
|
AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
|
|
HRESULT hr;
|
|
ID3D11Texture2D *tex;
|
|
D3D11_TEXTURE2D_DESC texDesc = {
|
|
.Width = ctx->width,
|
|
.Height = ctx->height,
|
|
.MipLevels = 1,
|
|
.Format = s->format,
|
|
.SampleDesc = { .Count = 1 },
|
|
.ArraySize = 1,
|
|
.Usage = D3D11_USAGE_DEFAULT,
|
|
.BindFlags = hwctx->BindFlags,
|
|
.MiscFlags = hwctx->MiscFlags,
|
|
};
|
|
|
|
hr = ID3D11Device_CreateTexture2D(device_hwctx->device, &texDesc, NULL, &tex);
|
|
if (FAILED(hr)) {
|
|
av_log(ctx, AV_LOG_ERROR, "Could not create the texture (%lx)\n", (long)hr);
|
|
return NULL;
|
|
}
|
|
|
|
return wrap_texture_buf(ctx, tex, 0);
|
|
}
|
|
|
|
static AVBufferRef *d3d11va_pool_alloc(void *opaque, size_t size)
|
|
{
|
|
AVHWFramesContext *ctx = (AVHWFramesContext*)opaque;
|
|
D3D11VAFramesContext *s = ctx->hwctx;
|
|
AVD3D11VAFramesContext *hwctx = &s->p;
|
|
D3D11_TEXTURE2D_DESC texDesc;
|
|
|
|
if (!hwctx->texture)
|
|
return d3d11va_alloc_single(ctx);
|
|
|
|
ID3D11Texture2D_GetDesc(hwctx->texture, &texDesc);
|
|
|
|
if (s->nb_surfaces_used >= texDesc.ArraySize) {
|
|
av_log(ctx, AV_LOG_ERROR, "Static surface pool size exceeded.\n");
|
|
return NULL;
|
|
}
|
|
|
|
ID3D11Texture2D_AddRef(hwctx->texture);
|
|
return wrap_texture_buf(ctx, hwctx->texture, s->nb_surfaces_used);
|
|
}
|
|
|
|
static int d3d11va_frames_init(AVHWFramesContext *ctx)
|
|
{
|
|
AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
|
|
D3D11VAFramesContext *s = ctx->hwctx;
|
|
AVD3D11VAFramesContext *hwctx = &s->p;
|
|
|
|
int i;
|
|
HRESULT hr;
|
|
D3D11_TEXTURE2D_DESC texDesc;
|
|
|
|
for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
|
|
if (ctx->sw_format == supported_formats[i].pix_fmt) {
|
|
s->format = supported_formats[i].d3d_format;
|
|
break;
|
|
}
|
|
}
|
|
if (i == FF_ARRAY_ELEMS(supported_formats)) {
|
|
av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n",
|
|
av_get_pix_fmt_name(ctx->sw_format));
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
texDesc = (D3D11_TEXTURE2D_DESC){
|
|
.Width = ctx->width,
|
|
.Height = ctx->height,
|
|
.MipLevels = 1,
|
|
.Format = s->format,
|
|
.SampleDesc = { .Count = 1 },
|
|
.ArraySize = ctx->initial_pool_size,
|
|
.Usage = D3D11_USAGE_DEFAULT,
|
|
.BindFlags = hwctx->BindFlags,
|
|
.MiscFlags = hwctx->MiscFlags,
|
|
};
|
|
|
|
if (hwctx->texture) {
|
|
D3D11_TEXTURE2D_DESC texDesc2;
|
|
ID3D11Texture2D_GetDesc(hwctx->texture, &texDesc2);
|
|
|
|
if (texDesc.Width != texDesc2.Width ||
|
|
texDesc.Height != texDesc2.Height ||
|
|
texDesc.Format != texDesc2.Format) {
|
|
av_log(ctx, AV_LOG_ERROR, "User-provided texture has mismatching parameters\n");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
ctx->initial_pool_size = texDesc2.ArraySize;
|
|
hwctx->BindFlags = texDesc2.BindFlags;
|
|
hwctx->MiscFlags = texDesc2.MiscFlags;
|
|
} else if (!(texDesc.BindFlags & D3D11_BIND_RENDER_TARGET) && texDesc.ArraySize > 0) {
|
|
hr = ID3D11Device_CreateTexture2D(device_hwctx->device, &texDesc, NULL, &hwctx->texture);
|
|
if (FAILED(hr)) {
|
|
av_log(ctx, AV_LOG_ERROR, "Could not create the texture (%lx)\n", (long)hr);
|
|
return AVERROR_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
hwctx->texture_infos = av_realloc_f(NULL, ctx->initial_pool_size, sizeof(*hwctx->texture_infos));
|
|
if (!hwctx->texture_infos)
|
|
return AVERROR(ENOMEM);
|
|
s->nb_surfaces = ctx->initial_pool_size;
|
|
|
|
ffhwframesctx(ctx)->pool_internal =
|
|
av_buffer_pool_init2(sizeof(AVD3D11FrameDescriptor),
|
|
ctx, d3d11va_pool_alloc, NULL);
|
|
if (!ffhwframesctx(ctx)->pool_internal)
|
|
return AVERROR(ENOMEM);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
|
|
{
|
|
AVD3D11FrameDescriptor *desc;
|
|
|
|
frame->buf[0] = av_buffer_pool_get(ctx->pool);
|
|
if (!frame->buf[0])
|
|
return AVERROR(ENOMEM);
|
|
|
|
desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
|
|
|
|
frame->data[0] = (uint8_t *)desc->texture;
|
|
frame->data[1] = (uint8_t *)desc->index;
|
|
frame->format = AV_PIX_FMT_D3D11;
|
|
frame->width = ctx->width;
|
|
frame->height = ctx->height;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int d3d11va_transfer_get_formats(AVHWFramesContext *ctx,
|
|
enum AVHWFrameTransferDirection dir,
|
|
enum AVPixelFormat **formats)
|
|
{
|
|
D3D11VAFramesContext *s = ctx->hwctx;
|
|
enum AVPixelFormat *fmts;
|
|
|
|
fmts = av_malloc_array(2, sizeof(*fmts));
|
|
if (!fmts)
|
|
return AVERROR(ENOMEM);
|
|
|
|
fmts[0] = ctx->sw_format;
|
|
fmts[1] = AV_PIX_FMT_NONE;
|
|
|
|
// Don't signal support for opaque formats. Actual access would fail.
|
|
if (s->format == DXGI_FORMAT_420_OPAQUE)
|
|
fmts[0] = AV_PIX_FMT_NONE;
|
|
|
|
*formats = fmts;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int d3d11va_create_staging_texture(AVHWFramesContext *ctx, DXGI_FORMAT format)
|
|
{
|
|
AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
|
|
D3D11VAFramesContext *s = ctx->hwctx;
|
|
HRESULT hr;
|
|
D3D11_TEXTURE2D_DESC texDesc = {
|
|
.Width = ctx->width,
|
|
.Height = ctx->height,
|
|
.MipLevels = 1,
|
|
.Format = format,
|
|
.SampleDesc = { .Count = 1 },
|
|
.ArraySize = 1,
|
|
.Usage = D3D11_USAGE_STAGING,
|
|
.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE,
|
|
};
|
|
|
|
hr = ID3D11Device_CreateTexture2D(device_hwctx->device, &texDesc, NULL, &s->staging_texture);
|
|
if (FAILED(hr)) {
|
|
av_log(ctx, AV_LOG_ERROR, "Could not create the staging texture (%lx)\n", (long)hr);
|
|
return AVERROR_UNKNOWN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void fill_texture_ptrs(uint8_t *data[4], int linesize[4],
|
|
AVHWFramesContext *ctx,
|
|
D3D11_TEXTURE2D_DESC *desc,
|
|
D3D11_MAPPED_SUBRESOURCE *map)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
linesize[i] = map->RowPitch;
|
|
|
|
av_image_fill_pointers(data, ctx->sw_format, desc->Height,
|
|
(uint8_t*)map->pData, linesize);
|
|
}
|
|
|
|
static int d3d11va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
|
|
const AVFrame *src)
|
|
{
|
|
AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
|
|
D3D11VAFramesContext *s = ctx->hwctx;
|
|
int download = src->format == AV_PIX_FMT_D3D11;
|
|
const AVFrame *frame = download ? src : dst;
|
|
const AVFrame *other = download ? dst : src;
|
|
// (The interface types are compatible.)
|
|
ID3D11Resource *texture = (ID3D11Resource *)(ID3D11Texture2D *)frame->data[0];
|
|
int index = (intptr_t)frame->data[1];
|
|
ID3D11Resource *staging;
|
|
int w = FFMIN(dst->width, src->width);
|
|
int h = FFMIN(dst->height, src->height);
|
|
uint8_t *map_data[4];
|
|
int map_linesize[4];
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
D3D11_MAPPED_SUBRESOURCE map;
|
|
HRESULT hr;
|
|
int res;
|
|
|
|
if (frame->hw_frames_ctx->data != (uint8_t *)ctx || other->format != ctx->sw_format)
|
|
return AVERROR(EINVAL);
|
|
|
|
device_hwctx->lock(device_hwctx->lock_ctx);
|
|
|
|
if (!s->staging_texture) {
|
|
ID3D11Texture2D_GetDesc((ID3D11Texture2D *)texture, &desc);
|
|
res = d3d11va_create_staging_texture(ctx, desc.Format);
|
|
if (res < 0)
|
|
return res;
|
|
}
|
|
|
|
staging = (ID3D11Resource *)s->staging_texture;
|
|
|
|
ID3D11Texture2D_GetDesc(s->staging_texture, &desc);
|
|
|
|
if (download) {
|
|
ID3D11DeviceContext_CopySubresourceRegion(device_hwctx->device_context,
|
|
staging, 0, 0, 0, 0,
|
|
texture, index, NULL);
|
|
|
|
hr = ID3D11DeviceContext_Map(device_hwctx->device_context,
|
|
staging, 0, D3D11_MAP_READ, 0, &map);
|
|
if (FAILED(hr))
|
|
goto map_failed;
|
|
|
|
fill_texture_ptrs(map_data, map_linesize, ctx, &desc, &map);
|
|
|
|
av_image_copy2(dst->data, dst->linesize, map_data, map_linesize,
|
|
ctx->sw_format, w, h);
|
|
|
|
ID3D11DeviceContext_Unmap(device_hwctx->device_context, staging, 0);
|
|
} else {
|
|
hr = ID3D11DeviceContext_Map(device_hwctx->device_context,
|
|
staging, 0, D3D11_MAP_WRITE, 0, &map);
|
|
if (FAILED(hr))
|
|
goto map_failed;
|
|
|
|
fill_texture_ptrs(map_data, map_linesize, ctx, &desc, &map);
|
|
|
|
av_image_copy2(map_data, map_linesize, src->data, src->linesize,
|
|
ctx->sw_format, w, h);
|
|
|
|
ID3D11DeviceContext_Unmap(device_hwctx->device_context, staging, 0);
|
|
|
|
ID3D11DeviceContext_CopySubresourceRegion(device_hwctx->device_context,
|
|
texture, index, 0, 0, 0,
|
|
staging, 0, NULL);
|
|
}
|
|
|
|
device_hwctx->unlock(device_hwctx->lock_ctx);
|
|
return 0;
|
|
|
|
map_failed:
|
|
av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface (%lx)\n", (long)hr);
|
|
device_hwctx->unlock(device_hwctx->lock_ctx);
|
|
return AVERROR_UNKNOWN;
|
|
}
|
|
|
|
static int d3d11va_device_init(AVHWDeviceContext *hwdev)
|
|
{
|
|
AVD3D11VADeviceContext *device_hwctx = hwdev->hwctx;
|
|
HRESULT hr;
|
|
|
|
if (!device_hwctx->lock) {
|
|
device_hwctx->lock_ctx = CreateMutex(NULL, 0, NULL);
|
|
if (device_hwctx->lock_ctx == INVALID_HANDLE_VALUE) {
|
|
av_log(NULL, AV_LOG_ERROR, "Failed to create a mutex\n");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
device_hwctx->lock = d3d11va_default_lock;
|
|
device_hwctx->unlock = d3d11va_default_unlock;
|
|
}
|
|
|
|
if (!device_hwctx->device_context) {
|
|
ID3D11Device_GetImmediateContext(device_hwctx->device, &device_hwctx->device_context);
|
|
if (!device_hwctx->device_context)
|
|
return AVERROR_UNKNOWN;
|
|
}
|
|
|
|
if (!device_hwctx->video_device) {
|
|
hr = ID3D11DeviceContext_QueryInterface(device_hwctx->device, &IID_ID3D11VideoDevice,
|
|
(void **)&device_hwctx->video_device);
|
|
if (FAILED(hr))
|
|
return AVERROR_UNKNOWN;
|
|
}
|
|
|
|
if (!device_hwctx->video_context) {
|
|
hr = ID3D11DeviceContext_QueryInterface(device_hwctx->device_context, &IID_ID3D11VideoContext,
|
|
(void **)&device_hwctx->video_context);
|
|
if (FAILED(hr))
|
|
return AVERROR_UNKNOWN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void d3d11va_device_uninit(AVHWDeviceContext *hwdev)
|
|
{
|
|
AVD3D11VADeviceContext *device_hwctx = hwdev->hwctx;
|
|
|
|
if (device_hwctx->device) {
|
|
ID3D11Device_Release(device_hwctx->device);
|
|
device_hwctx->device = NULL;
|
|
}
|
|
|
|
if (device_hwctx->device_context) {
|
|
ID3D11DeviceContext_Release(device_hwctx->device_context);
|
|
device_hwctx->device_context = NULL;
|
|
}
|
|
|
|
if (device_hwctx->video_device) {
|
|
ID3D11VideoDevice_Release(device_hwctx->video_device);
|
|
device_hwctx->video_device = NULL;
|
|
}
|
|
|
|
if (device_hwctx->video_context) {
|
|
ID3D11VideoContext_Release(device_hwctx->video_context);
|
|
device_hwctx->video_context = NULL;
|
|
}
|
|
|
|
if (device_hwctx->lock == d3d11va_default_lock) {
|
|
CloseHandle(device_hwctx->lock_ctx);
|
|
device_hwctx->lock_ctx = INVALID_HANDLE_VALUE;
|
|
device_hwctx->lock = NULL;
|
|
}
|
|
}
|
|
|
|
static int d3d11va_device_find_adapter_by_vendor_id(AVHWDeviceContext *ctx, uint32_t flags, const char *vendor_id)
|
|
{
|
|
HRESULT hr;
|
|
IDXGIAdapter *adapter = NULL;
|
|
IDXGIFactory2 *factory;
|
|
int adapter_id = 0;
|
|
long int id = strtol(vendor_id, NULL, 0);
|
|
|
|
hr = mCreateDXGIFactory(&IID_IDXGIFactory2, (void **)&factory);
|
|
if (FAILED(hr)) {
|
|
av_log(ctx, AV_LOG_ERROR, "CreateDXGIFactory returned error\n");
|
|
return -1;
|
|
}
|
|
|
|
while (IDXGIFactory2_EnumAdapters(factory, adapter_id++, &adapter) != DXGI_ERROR_NOT_FOUND) {
|
|
ID3D11Device* device = NULL;
|
|
DXGI_ADAPTER_DESC adapter_desc;
|
|
|
|
hr = mD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, NULL, 0, D3D11_SDK_VERSION, &device, NULL, NULL);
|
|
if (FAILED(hr)) {
|
|
av_log(ctx, AV_LOG_DEBUG, "D3D11CreateDevice returned error, try next adapter\n");
|
|
IDXGIAdapter_Release(adapter);
|
|
continue;
|
|
}
|
|
|
|
hr = IDXGIAdapter2_GetDesc(adapter, &adapter_desc);
|
|
ID3D11Device_Release(device);
|
|
IDXGIAdapter_Release(adapter);
|
|
if (FAILED(hr)) {
|
|
av_log(ctx, AV_LOG_DEBUG, "IDXGIAdapter2_GetDesc returned error, try next adapter\n");
|
|
continue;
|
|
} else if (adapter_desc.VendorId == id) {
|
|
IDXGIFactory2_Release(factory);
|
|
return adapter_id - 1;
|
|
}
|
|
}
|
|
|
|
IDXGIFactory2_Release(factory);
|
|
return -1;
|
|
}
|
|
|
|
static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
|
|
AVDictionary *opts, int flags)
|
|
{
|
|
AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
|
|
|
|
HRESULT hr;
|
|
IDXGIAdapter *pAdapter = NULL;
|
|
ID3D10Multithread *pMultithread;
|
|
UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
|
|
int is_debug = !!av_dict_get(opts, "debug", NULL, 0);
|
|
int ret;
|
|
int adapter = -1;
|
|
|
|
if (is_debug) {
|
|
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
|
|
av_log(ctx, AV_LOG_INFO, "Enabling d3d11 debugging.\n");
|
|
}
|
|
|
|
if ((ret = ff_thread_once(&functions_loaded, load_functions)) != 0)
|
|
return AVERROR_UNKNOWN;
|
|
if (!mD3D11CreateDevice || !mCreateDXGIFactory) {
|
|
av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library or its functions\n");
|
|
return AVERROR_UNKNOWN;
|
|
}
|
|
|
|
if (device) {
|
|
adapter = atoi(device);
|
|
} else {
|
|
AVDictionaryEntry *e = av_dict_get(opts, "vendor_id", NULL, 0);
|
|
if (e && e->value) {
|
|
adapter = d3d11va_device_find_adapter_by_vendor_id(ctx, creationFlags, e->value);
|
|
if (adapter < 0) {
|
|
av_log(ctx, AV_LOG_ERROR, "Failed to find d3d11va adapter by "
|
|
"vendor id %s\n", e->value);
|
|
return AVERROR_UNKNOWN;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (adapter >= 0) {
|
|
IDXGIFactory2 *pDXGIFactory;
|
|
|
|
av_log(ctx, AV_LOG_VERBOSE, "Selecting d3d11va adapter %d\n", adapter);
|
|
hr = mCreateDXGIFactory(&IID_IDXGIFactory2, (void **)&pDXGIFactory);
|
|
if (SUCCEEDED(hr)) {
|
|
if (FAILED(IDXGIFactory2_EnumAdapters(pDXGIFactory, adapter, &pAdapter)))
|
|
pAdapter = NULL;
|
|
IDXGIFactory2_Release(pDXGIFactory);
|
|
}
|
|
}
|
|
|
|
if (pAdapter) {
|
|
DXGI_ADAPTER_DESC desc;
|
|
hr = IDXGIAdapter2_GetDesc(pAdapter, &desc);
|
|
if (!FAILED(hr)) {
|
|
av_log(ctx, AV_LOG_INFO, "Using device %04x:%04x (%ls).\n",
|
|
desc.VendorId, desc.DeviceId, desc.Description);
|
|
}
|
|
}
|
|
|
|
hr = mD3D11CreateDevice(pAdapter, pAdapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, NULL, creationFlags, NULL, 0,
|
|
D3D11_SDK_VERSION, &device_hwctx->device, NULL, NULL);
|
|
if (pAdapter)
|
|
IDXGIAdapter_Release(pAdapter);
|
|
if (FAILED(hr)) {
|
|
av_log(ctx, AV_LOG_ERROR, "Failed to create Direct3D device (%lx)\n", (long)hr);
|
|
return AVERROR_UNKNOWN;
|
|
}
|
|
|
|
hr = ID3D11Device_QueryInterface(device_hwctx->device, &IID_ID3D10Multithread, (void **)&pMultithread);
|
|
if (SUCCEEDED(hr)) {
|
|
ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
|
|
ID3D10Multithread_Release(pMultithread);
|
|
}
|
|
|
|
#if !HAVE_UWP && HAVE_DXGIDEBUG_H
|
|
if (is_debug) {
|
|
HANDLE dxgidebug_dll = LoadLibrary("dxgidebug.dll");
|
|
if (dxgidebug_dll) {
|
|
HRESULT (WINAPI * pf_DXGIGetDebugInterface)(const GUID *riid, void **ppDebug)
|
|
= (void *)GetProcAddress(dxgidebug_dll, "DXGIGetDebugInterface");
|
|
if (pf_DXGIGetDebugInterface) {
|
|
IDXGIDebug *dxgi_debug = NULL;
|
|
hr = pf_DXGIGetDebugInterface(&IID_IDXGIDebug, (void**)&dxgi_debug);
|
|
if (SUCCEEDED(hr) && dxgi_debug)
|
|
IDXGIDebug_ReportLiveObjects(dxgi_debug, DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
const HWContextType ff_hwcontext_type_d3d11va = {
|
|
.type = AV_HWDEVICE_TYPE_D3D11VA,
|
|
.name = "D3D11VA",
|
|
|
|
.device_hwctx_size = sizeof(AVD3D11VADeviceContext),
|
|
.frames_hwctx_size = sizeof(D3D11VAFramesContext),
|
|
|
|
.device_create = d3d11va_device_create,
|
|
.device_init = d3d11va_device_init,
|
|
.device_uninit = d3d11va_device_uninit,
|
|
.frames_get_constraints = d3d11va_frames_get_constraints,
|
|
.frames_init = d3d11va_frames_init,
|
|
.frames_uninit = d3d11va_frames_uninit,
|
|
.frames_get_buffer = d3d11va_get_buffer,
|
|
.transfer_get_formats = d3d11va_transfer_get_formats,
|
|
.transfer_data_to = d3d11va_transfer_data,
|
|
.transfer_data_from = d3d11va_transfer_data,
|
|
|
|
.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_D3D11, AV_PIX_FMT_NONE },
|
|
};
|