mirror of https://github.com/FFmpeg/FFmpeg.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
726 lines
24 KiB
726 lines
24 KiB
/* |
|
* 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 "mem.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, "CreateDXGIFactory1"); |
|
if (!mCreateDXGIFactory) |
|
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); |
|
av_log(ctx, AV_LOG_INFO, "Enabled dxgi debugging.\n"); |
|
} else { |
|
av_log(ctx, AV_LOG_WARNING, "Failed enabling dxgi debugging.\n"); |
|
} |
|
} else { |
|
av_log(ctx, AV_LOG_WARNING, "Failed getting dxgi debug interface.\n"); |
|
} |
|
} else { |
|
av_log(ctx, AV_LOG_WARNING, "Failed loading dxgi debug library.\n"); |
|
} |
|
} |
|
#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 }, |
|
};
|
|
|