From fff90422d181744cd75dbf011687ee7095f02875 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 6 Jun 2017 18:51:07 +0200 Subject: [PATCH 1/5] lavu: add new D3D11 pixfmt and hwcontext To be used with the new d3d11 hwaccel decode API. With the new hwaccel API, we don't want surfaces to depend on the decoder (other than the required dimension and format). The old D3D11VA pixfmt uses ID3D11VideoDecoderOutputView pointers, which include the decoder configuration, and thus is incompatible with the new hwaccel API. This patch introduces AV_PIX_FMT_D3D11, which uses ID3D11Texture2D and an index. It's simpler and compatible with the new hwaccel API. The introduced hwcontext supports only the new pixfmt. Frame upload code untested. Significantly based on work by Steve Lhomme , but with heavy changes/rewrites. Signed-off-by: Diego Biurrun --- doc/APIchanges | 3 + libavutil/Makefile | 3 + libavutil/hwcontext.c | 4 + libavutil/hwcontext.h | 1 + libavutil/hwcontext_d3d11va.c | 490 +++++++++++++++++++++++++++++++++ libavutil/hwcontext_d3d11va.h | 160 +++++++++++ libavutil/hwcontext_internal.h | 1 + libavutil/pixdesc.c | 4 + libavutil/pixfmt.h | 14 +- libavutil/version.h | 4 +- 10 files changed, 681 insertions(+), 3 deletions(-) create mode 100644 libavutil/hwcontext_d3d11va.c create mode 100644 libavutil/hwcontext_d3d11va.h diff --git a/doc/APIchanges b/doc/APIchanges index a251c4ca82..a81e41833d 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -13,6 +13,9 @@ libavutil: 2017-03-23 API changes, most recent first: +2017-xx-xx - xxxxxxx - lavu 56.2.0 - hwcontext.h + Add AV_HWDEVICE_TYPE_D3D11VA and AV_PIX_FMT_D3D11. + 2017-04-30 - xxxxxxx - lavu 56.1.1 - hwcontext.h av_hwframe_ctx_create_derived() now takes some AV_HWFRAME_MAP_* combination as its flags argument (which was previously unused). diff --git a/libavutil/Makefile b/libavutil/Makefile index 60e180c79d..6fb24db678 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -27,6 +27,7 @@ HEADERS = adler32.h \ hmac.h \ hwcontext.h \ hwcontext_cuda.h \ + hwcontext_d3d11va.h \ hwcontext_dxva2.h \ hwcontext_qsv.h \ hwcontext_vaapi.h \ @@ -112,6 +113,7 @@ OBJS = adler32.o \ xtea.o \ OBJS-$(CONFIG_CUDA) += hwcontext_cuda.o +OBJS-$(CONFIG_D3D11VA) += hwcontext_d3d11va.o OBJS-$(CONFIG_DXVA2) += hwcontext_dxva2.o OBJS-$(CONFIG_LIBMFX) += hwcontext_qsv.o OBJS-$(CONFIG_LZO) += lzo.o @@ -121,6 +123,7 @@ OBJS-$(CONFIG_VDPAU) += hwcontext_vdpau.o OBJS += $(COMPAT_OBJS:%=../compat/%) SKIPHEADERS-$(CONFIG_CUDA) += hwcontext_cuda.h +SKIPHEADERS-$(CONFIG_D3D11VA) += hwcontext_d3d11va.h SKIPHEADERS-$(CONFIG_DXVA2) += hwcontext_dxva2.h SKIPHEADERS-$(CONFIG_LIBMFX) += hwcontext_qsv.h SKIPHEADERS-$(CONFIG_VAAPI) += hwcontext_vaapi.h diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c index 360b01205c..d82df56abf 100644 --- a/libavutil/hwcontext.c +++ b/libavutil/hwcontext.c @@ -32,6 +32,9 @@ static const HWContextType * const hw_table[] = { #if CONFIG_CUDA &ff_hwcontext_type_cuda, #endif +#if CONFIG_D3D11VA + &ff_hwcontext_type_d3d11va, +#endif #if CONFIG_DXVA2 &ff_hwcontext_type_dxva2, #endif @@ -50,6 +53,7 @@ static const HWContextType * const hw_table[] = { const char *hw_type_names[] = { [AV_HWDEVICE_TYPE_CUDA] = "cuda", [AV_HWDEVICE_TYPE_DXVA2] = "dxva2", + [AV_HWDEVICE_TYPE_D3D11VA] = "d3d11va", [AV_HWDEVICE_TYPE_QSV] = "qsv", [AV_HWDEVICE_TYPE_VAAPI] = "vaapi", [AV_HWDEVICE_TYPE_VDPAU] = "vdpau", diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h index eaf40c46f1..203ea510ec 100644 --- a/libavutil/hwcontext.h +++ b/libavutil/hwcontext.h @@ -31,6 +31,7 @@ enum AVHWDeviceType { AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_DXVA2, AV_HWDEVICE_TYPE_QSV, + AV_HWDEVICE_TYPE_D3D11VA, }; typedef struct AVHWDeviceInternal AVHWDeviceInternal; diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c new file mode 100644 index 0000000000..3940502868 --- /dev/null +++ b/libavutil/hwcontext_d3d11va.c @@ -0,0 +1,490 @@ +/* + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 +#endif +#define COBJMACROS + +#include +#include +#include + +#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" + +typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory); + +typedef struct D3D11VAFramesContext { + 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 }, +}; + +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) +{ + AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx; + D3D11VAFramesContext *s = ctx->internal->priv; + + if (frames_hwctx->texture) + ID3D11Texture2D_Release(frames_hwctx->texture); + + if (s->staging_texture) + ID3D11Texture2D_Release(s->staging_texture); +} + +static void free_texture(void *opaque, uint8_t *data) +{ + ID3D11Texture2D_Release((ID3D11Texture2D *)opaque); +} + +static AVBufferRef *wrap_texture_buf(ID3D11Texture2D *tex, int index) +{ + AVBufferRef *buf; + AVD3D11FrameDescriptor *desc = av_mallocz(sizeof(*desc)); + if (!desc) { + ID3D11Texture2D_Release(tex); + return NULL; + } + + 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->internal->priv; + AVD3D11VAFramesContext *hwctx = ctx->hwctx; + 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(tex, 0); +} + +static AVBufferRef *d3d11va_pool_alloc(void *opaque, int size) +{ + AVHWFramesContext *ctx = (AVHWFramesContext*)opaque; + D3D11VAFramesContext *s = ctx->internal->priv; + AVD3D11VAFramesContext *hwctx = ctx->hwctx; + 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(hwctx->texture, s->nb_surfaces_used++); +} + +static int d3d11va_frames_init(AVHWFramesContext *ctx) +{ + AVD3D11VAFramesContext *hwctx = ctx->hwctx; + AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx; + D3D11VAFramesContext *s = ctx->internal->priv; + + 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); + } + } else if (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; + } + } + + texDesc.ArraySize = 1; + texDesc.Usage = D3D11_USAGE_STAGING; + texDesc.BindFlags = 0; + texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; + texDesc.MiscFlags = 0; + 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; + } + + ctx->internal->pool_internal = av_buffer_pool_init2(sizeof(AVD3D11FrameDescriptor), + ctx, d3d11va_pool_alloc, NULL); + if (!ctx->internal->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) +{ + 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; + + *formats = fmts; + + 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->internal->priv; + 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 = (ID3D11Resource *)s->staging_texture; + 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; + + if (frame->hw_frames_ctx->data != (uint8_t *)ctx || other->format != ctx->sw_format) + return AVERROR(EINVAL); + + device_hwctx->lock(device_hwctx->lock_ctx); + + 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_copy(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_copy(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); + + if (device_hwctx->device_context) + ID3D11DeviceContext_Release(device_hwctx->device_context); + + if (device_hwctx->video_device) + ID3D11VideoDevice_Release(device_hwctx->video_device); + + if (device_hwctx->video_context) + ID3D11VideoContext_Release(device_hwctx->video_context); + + if (device_hwctx->lock == d3d11va_default_lock) + CloseHandle(device_hwctx->lock_ctx); +} + +static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device, + AVDictionary *opts, int flags) +{ + AVD3D11VADeviceContext *device_hwctx = ctx->hwctx; + HANDLE d3dlib; + + HRESULT hr; + PFN_D3D11_CREATE_DEVICE createD3D; + IDXGIAdapter *pAdapter = NULL; + ID3D10Multithread *pMultithread; + UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT; + + if (device) { + PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory; + HMODULE dxgilib = LoadLibrary("dxgi.dll"); + if (!dxgilib) + return AVERROR_UNKNOWN; + + mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory"); + if (mCreateDXGIFactory) { + IDXGIFactory2 *pDXGIFactory; + hr = mCreateDXGIFactory(&IID_IDXGIFactory2, (void **)&pDXGIFactory); + if (SUCCEEDED(hr)) { + int adapter = atoi(device); + if (FAILED(IDXGIFactory2_EnumAdapters(pDXGIFactory, adapter, &pAdapter))) + pAdapter = NULL; + IDXGIFactory2_Release(pDXGIFactory); + } + } + FreeLibrary(dxgilib); + } + + // We let this "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. + d3dlib = LoadLibrary("d3d11.dll"); + if (!d3dlib) { + av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library\n"); + return AVERROR_UNKNOWN; + } + + createD3D = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3dlib, "D3D11CreateDevice"); + if (!createD3D) { + av_log(ctx, AV_LOG_ERROR, "Failed to locate D3D11CreateDevice\n"); + return AVERROR_UNKNOWN; + } + + hr = createD3D(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); + } + + return 0; +} + +const HWContextType ff_hwcontext_type_d3d11va = { + .type = AV_HWDEVICE_TYPE_D3D11VA, + .name = "D3D11VA", + + .device_hwctx_size = sizeof(AVD3D11VADeviceContext), + .frames_hwctx_size = sizeof(AVD3D11VAFramesContext), + .frames_priv_size = sizeof(D3D11VAFramesContext), + + .device_create = d3d11va_device_create, + .device_init = d3d11va_device_init, + .device_uninit = d3d11va_device_uninit, + .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 }, +}; diff --git a/libavutil/hwcontext_d3d11va.h b/libavutil/hwcontext_d3d11va.h new file mode 100644 index 0000000000..676349d7b8 --- /dev/null +++ b/libavutil/hwcontext_d3d11va.h @@ -0,0 +1,160 @@ +/* + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_D3D11VA_H +#define AVUTIL_HWCONTEXT_D3D11VA_H + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_D3D11VA. + * + * The default pool implementation will be fixed-size if initial_pool_size is + * set (and allocate elements from an array texture). Otherwise it will allocate + * individual textures. Be aware that decoding requires a single array texture. + */ + +#include + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVD3D11VADeviceContext { + /** + * Device used for texture creation and access. This can also be used to + * set the libavcodec decoding device. + * + * Must be set by the user. This is the only mandatory field - the other + * device context fields are set from this and are available for convenience. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11Device *device; + + /** + * If unset, this will be set from the device field on init. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11DeviceContext *device_context; + + /** + * If unset, this will be set from the device field on init. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11VideoDevice *video_device; + + /** + * If unset, this will be set from the device_context field on init. + * + * Deallocating the AVHWDeviceContext will always release this interface, + * and it does not matter whether it was user-allocated. + */ + ID3D11VideoContext *video_context; + + /** + * Callbacks for locking. They protect accesses to device_context and + * video_context calls. They also protect access to the internal staging + * texture (for av_hwframe_transfer_data() calls). They do NOT protect + * access to hwcontext or decoder state in general. + * + * If unset on init, the hwcontext implementation will set them to use an + * internal mutex. + * + * The underlying lock must be recursive. lock_ctx is for free use by the + * locking implementation. + */ + void (*lock)(void *lock_ctx); + void (*unlock)(void *lock_ctx); + void *lock_ctx; +} AVD3D11VADeviceContext; + +/** + * D3D11 frame descriptor for pool allocation. + * + * In user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs + * with the data pointer pointing at an object of this type describing the + * planes of the frame. + * + * This has no use outside of custom allocation, and AVFrame AVBufferRef do not + * necessarily point to an instance of this struct. + */ +typedef struct AVD3D11FrameDescriptor { + /** + * The texture in which the frame is located. The reference count is + * managed by the AVBufferRef, and destroying the reference will release + * the interface. + * + * Normally stored in AVFrame.data[0]. + */ + ID3D11Texture2D *texture; + + /** + * The index into the array texture element representing the frame, or 0 + * if the texture is not an array texture. + * + * Normally stored in AVFrame.data[1] (cast from intptr_t). + */ + intptr_t index; +} AVD3D11FrameDescriptor; + +/** + * This struct is allocated as AVHWFramesContext.hwctx + */ +typedef struct AVD3D11VAFramesContext { + /** + * The canonical texture used for pool allocation. If this is set to NULL + * on init, the hwframes implementation will allocate and set an array + * texture if initial_pool_size > 0. + * + * The only situation when the API user should set this is: + * - the user wants to do manual pool allocation (setting + * AVHWFramesContext.pool), instead of letting AVHWFramesContext + * allocate the pool + * - of an array texture + * - and wants it to use it for decoding + * - this has to be done before calling av_hwframe_ctx_init() + * + * Deallocating the AVHWFramesContext will always release this interface, + * and it does not matter whether it was user-allocated. + * + * This is in particular used by the libavcodec D3D11VA hwaccel, which + * requires a single array texture. It will create ID3D11VideoDecoderOutputView + * objects for each array texture element on decoder initialization. + */ + ID3D11Texture2D *texture; + + /** + * D3D11_TEXTURE2D_DESC.BindFlags used for texture creation. The user must + * at least set D3D11_BIND_DECODER if the frames context is to be used for + * video decoding. + * This field is ignored/invalid if a user-allocated texture is provided. + */ + UINT BindFlags; + + /** + * D3D11_TEXTURE2D_DESC.MiscFlags used for texture creation. + * This field is ignored/invalid if a user-allocated texture is provided. + */ + UINT MiscFlags; +} AVD3D11VAFramesContext; + +#endif /* AVUTIL_HWCONTEXT_D3D11VA_H */ diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h index 7cf6cb07c7..99612931f2 100644 --- a/libavutil/hwcontext_internal.h +++ b/libavutil/hwcontext_internal.h @@ -158,6 +158,7 @@ int ff_hwframe_map_create(AVBufferRef *hwframe_ref, extern const HWContextType ff_hwcontext_type_cuda; +extern const HWContextType ff_hwcontext_type_d3d11va; extern const HWContextType ff_hwcontext_type_dxva2; extern const HWContextType ff_hwcontext_type_qsv; extern const HWContextType ff_hwcontext_type_vaapi; diff --git a/libavutil/pixdesc.c b/libavutil/pixdesc.c index fb2e1a12f3..7fa6dd7c0b 100644 --- a/libavutil/pixdesc.c +++ b/libavutil/pixdesc.c @@ -1739,6 +1739,10 @@ static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = { .flags = AV_PIX_FMT_FLAG_BE | AV_PIX_FMT_FLAG_PLANAR | AV_PIX_FMT_FLAG_RGB | AV_PIX_FMT_FLAG_ALPHA, }, + [AV_PIX_FMT_D3D11] = { + .name = "d3d11", + .flags = AV_PIX_FMT_FLAG_HWACCEL, + }, }; #if FF_API_PLUS1_MINUS1 FF_ENABLE_DEPRECATION_WARNINGS diff --git a/libavutil/pixfmt.h b/libavutil/pixfmt.h index fc1969ef12..2ba7ad1c88 100644 --- a/libavutil/pixfmt.h +++ b/libavutil/pixfmt.h @@ -205,7 +205,7 @@ enum AVPixelFormat { */ AV_PIX_FMT_MMAL, - AV_PIX_FMT_D3D11VA_VLD, ///< HW decoding through Direct3D11, Picture.data[3] contains a ID3D11VideoDecoderOutputView pointer + AV_PIX_FMT_D3D11VA_VLD, ///< HW decoding through Direct3D11 via old API, Picture.data[3] contains a ID3D11VideoDecoderOutputView pointer /** * HW acceleration through CUDA. data[i] contain CUdeviceptr pointers @@ -237,6 +237,18 @@ enum AVPixelFormat { AV_PIX_FMT_GBRAP10BE, ///< planar GBR 4:4:4:4 40bpp, big-endian AV_PIX_FMT_GBRAP10LE, ///< planar GBR 4:4:4:4 40bpp, little-endian + /** + * Hardware surfaces for Direct3D11. + * + * This is preferred over the legacy AV_PIX_FMT_D3D11VA_VLD. The new D3D11 + * hwaccel API and filtering support AV_PIX_FMT_D3D11 only. + * + * data[0] contains a ID3D11Texture2D pointer, and data[1] contains the + * texture array index of the frame as intptr_t if the ID3D11Texture2D is + * an array texture (or always 0 if it's a normal texture). + */ + AV_PIX_FMT_D3D11, + AV_PIX_FMT_NB, ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions }; diff --git a/libavutil/version.h b/libavutil/version.h index 7779755870..2c85120b64 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -54,8 +54,8 @@ */ #define LIBAVUTIL_VERSION_MAJOR 56 -#define LIBAVUTIL_VERSION_MINOR 1 -#define LIBAVUTIL_VERSION_MICRO 1 +#define LIBAVUTIL_VERSION_MINOR 2 +#define LIBAVUTIL_VERSION_MICRO 0 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ LIBAVUTIL_VERSION_MINOR, \ From bd747b9226414007f0207fa201976af7217e3b77 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 6 Jun 2017 18:51:08 +0200 Subject: [PATCH 2/5] lavc: set avctx->hwaccel before init So a hwaccel can access avctx->hwaccel in init for whatever reason. This is for the new d3d hwaccel API. We could create separate entrypoints for each of the 3 hwaccel types (dxva2, d3d11va, new d3d11va), but this seems nicer. Signed-off-by: Diego Biurrun --- libavcodec/decode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libavcodec/decode.c b/libavcodec/decode.c index bb58dfc561..ae2c715677 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -746,16 +746,16 @@ static int setup_hwaccel(AVCodecContext *avctx, return AVERROR(ENOMEM); } + avctx->hwaccel = hwa; if (hwa->init) { ret = hwa->init(avctx); if (ret < 0) { av_freep(&avctx->internal->hwaccel_priv_data); + avctx->hwaccel = NULL; return ret; } } - avctx->hwaccel = hwa; - return 0; } From 4dec101acc393fbfe9a8ce0237b9efbae3f20139 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 6 Jun 2017 18:51:09 +0200 Subject: [PATCH 3/5] dxva: preparations for new hwaccel API The actual hwaccel code will need to access an internal context instead of avctx->hwaccel_context, so add a new DXVA_CONTEXT() macro, that will dispatch between the "old" external and the new internal context. Also, the new API requires a new D3D11 pixfmt, so all places which check for the pixfmt need to be adjusted. Introduce a ff_dxva2_is_d3d11() function, which does the check. Signed-off-by: Diego Biurrun --- libavcodec/dxva2.c | 32 ++++++++++++++++++++------------ libavcodec/dxva2_h264.c | 14 +++++++------- libavcodec/dxva2_hevc.c | 10 +++++----- libavcodec/dxva2_internal.h | 22 +++++++++++++--------- libavcodec/dxva2_mpeg2.c | 10 +++++----- libavcodec/dxva2_vc1.c | 10 +++++----- 6 files changed, 55 insertions(+), 43 deletions(-) diff --git a/libavcodec/dxva2.c b/libavcodec/dxva2.c index b0452b6a9a..fe6dbbc030 100644 --- a/libavcodec/dxva2.c +++ b/libavcodec/dxva2.c @@ -71,7 +71,7 @@ int ff_dxva2_commit_buffer(AVCodecContext *avctx, HRESULT hr; #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) + if (ff_dxva2_is_d3d11(avctx)) hr = ID3D11VideoContext_GetDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, type, @@ -91,7 +91,7 @@ int ff_dxva2_commit_buffer(AVCodecContext *avctx, memcpy(dxva_data, data, size); #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { D3D11_VIDEO_DECODER_BUFFER_DESC *dsc11 = dsc; memset(dsc11, 0, sizeof(*dsc11)); dsc11->BufferType = type; @@ -116,7 +116,7 @@ int ff_dxva2_commit_buffer(AVCodecContext *avctx, } #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) + if (ff_dxva2_is_d3d11(avctx)) hr = ID3D11VideoContext_ReleaseDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, type); #endif #if CONFIG_DXVA2 @@ -139,7 +139,7 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, DECODER_BUFFER_DESC *bs, DECODER_BUFFER_DESC *slice)) { - AVDXVAContext *ctx = avctx->hwaccel_context; + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); unsigned buffer_count = 0; #if CONFIG_D3D11VA D3D11_VIDEO_DECODER_BUFFER_DESC buffer11[4]; @@ -154,7 +154,7 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, do { #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) WaitForSingleObjectEx(D3D11VA_CONTEXT(ctx)->context_mutex, INFINITE, FALSE); hr = ID3D11VideoContext_DecoderBeginFrame(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, @@ -171,7 +171,7 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, if (hr != E_PENDING || ++runs > 50) break; #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) + if (ff_dxva2_is_d3d11(avctx)) if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex); #endif @@ -181,7 +181,7 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, if (FAILED(hr)) { av_log(avctx, AV_LOG_ERROR, "Failed to begin frame: 0x%x\n", hr); #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) + if (ff_dxva2_is_d3d11(avctx)) if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex); #endif @@ -189,7 +189,7 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, } #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { buffer = &buffer11[buffer_count]; type = D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS; } @@ -212,7 +212,7 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, if (qm_size > 0) { #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { buffer = &buffer11[buffer_count]; type = D3D11_VIDEO_DECODER_BUFFER_INVERSE_QUANTIZATION_MATRIX; } @@ -235,7 +235,7 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, } #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { buffer = &buffer11[buffer_count + 0]; buffer_slice = &buffer11[buffer_count + 1]; } @@ -262,7 +262,7 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, assert(buffer_count == 1 + (qm_size > 0) + 2); #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) + if (ff_dxva2_is_d3d11(avctx)) hr = ID3D11VideoContext_SubmitDecoderBuffers(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, buffer_count, buffer11); @@ -284,7 +284,7 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, end: #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { hr = ID3D11VideoContext_DecoderEndFrame(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder); if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex); @@ -301,3 +301,11 @@ end: return result; } + +int ff_dxva2_is_d3d11(const AVCodecContext *avctx) +{ + if (CONFIG_D3D11VA) + return avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD; + else + return 0; +} diff --git a/libavcodec/dxva2_h264.c b/libavcodec/dxva2_h264.c index 84959c532d..f58a45807f 100644 --- a/libavcodec/dxva2_h264.c +++ b/libavcodec/dxva2_h264.c @@ -220,7 +220,7 @@ static void fill_slice_long(AVCodecContext *avctx, DXVA_Slice_H264_Long *slice, { const H264Context *h = avctx->priv_data; H264SliceContext *sl = &h->slice_ctx[0]; - AVDXVAContext *ctx = avctx->hwaccel_context; + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); unsigned list; memset(slice, 0, sizeof(*slice)); @@ -302,7 +302,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, { const H264Context *h = avctx->priv_data; const unsigned mb_count = h->mb_width * h->mb_height; - AVDXVAContext *ctx = avctx->hwaccel_context; + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); const H264Picture *current_picture = h->cur_pic_ptr; struct dxva2_picture_context *ctx_pic = current_picture->hwaccel_picture_private; DXVA_Slice_H264_Short *slice = NULL; @@ -317,7 +317,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, /* Create an annex B bitstream buffer with only slice NAL and finalize slice */ #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { type = D3D11_VIDEO_DECODER_BUFFER_BITSTREAM; if (FAILED(ID3D11VideoContext_GetDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, @@ -388,7 +388,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, slice->SliceBytesInBuffer += padding; } #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) + if (ff_dxva2_is_d3d11(avctx)) if (FAILED(ID3D11VideoContext_ReleaseDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, type))) return -1; #endif @@ -401,7 +401,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, return -1; #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { D3D11_VIDEO_DECODER_BUFFER_DESC *dsc11 = bs; memset(dsc11, 0, sizeof(*dsc11)); dsc11->BufferType = type; @@ -442,7 +442,7 @@ static int dxva2_h264_start_frame(AVCodecContext *avctx, av_unused uint32_t size) { const H264Context *h = avctx->priv_data; - AVDXVAContext *ctx = avctx->hwaccel_context; + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); struct dxva2_picture_context *ctx_pic = h->cur_pic_ptr->hwaccel_picture_private; if (!DXVA_CONTEXT_VALID(avctx, ctx)) @@ -467,7 +467,7 @@ static int dxva2_h264_decode_slice(AVCodecContext *avctx, { const H264Context *h = avctx->priv_data; const H264SliceContext *sl = &h->slice_ctx[0]; - AVDXVAContext *ctx = avctx->hwaccel_context; + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); const H264Picture *current_picture = h->cur_pic_ptr; struct dxva2_picture_context *ctx_pic = current_picture->hwaccel_picture_private; unsigned position; diff --git a/libavcodec/dxva2_hevc.c b/libavcodec/dxva2_hevc.c index 17548d25bb..f2bb8b26a5 100644 --- a/libavcodec/dxva2_hevc.c +++ b/libavcodec/dxva2_hevc.c @@ -243,7 +243,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, DECODER_BUFFER_DESC *sc) { const HEVCContext *h = avctx->priv_data; - AVDXVAContext *ctx = avctx->hwaccel_context; + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); const HEVCFrame *current_picture = h->ref; struct hevc_dxva2_picture_context *ctx_pic = current_picture->hwaccel_picture_private; DXVA_Slice_HEVC_Short *slice = NULL; @@ -258,7 +258,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, /* Create an annex B bitstream buffer with only slice NAL and finalize slice */ #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { type = D3D11_VIDEO_DECODER_BUFFER_BITSTREAM; if (FAILED(ID3D11VideoContext_GetDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, @@ -312,7 +312,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, slice->SliceBytesInBuffer += padding; } #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) + if (ff_dxva2_is_d3d11(avctx)) if (FAILED(ID3D11VideoContext_ReleaseDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, type))) return -1; #endif @@ -325,7 +325,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, return -1; #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { D3D11_VIDEO_DECODER_BUFFER_DESC *dsc11 = bs; memset(dsc11, 0, sizeof(*dsc11)); dsc11->BufferType = type; @@ -362,7 +362,7 @@ static int dxva2_hevc_start_frame(AVCodecContext *avctx, av_unused uint32_t size) { const HEVCContext *h = avctx->priv_data; - AVDXVAContext *ctx = avctx->hwaccel_context; + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); struct hevc_dxva2_picture_context *ctx_pic = h->ref->hwaccel_picture_private; if (!DXVA_CONTEXT_VALID(avctx, ctx)) diff --git a/libavcodec/dxva2_internal.h b/libavcodec/dxva2_internal.h index 8b774b14f3..af0d607487 100644 --- a/libavcodec/dxva2_internal.h +++ b/libavcodec/dxva2_internal.h @@ -59,21 +59,23 @@ typedef union { #endif } AVDXVAContext; +#define DXVA_CONTEXT(avctx) ((AVDXVAContext *)(avctx)->hwaccel_context) + #define D3D11VA_CONTEXT(ctx) (&ctx->d3d11va) #define DXVA2_CONTEXT(ctx) (&ctx->dxva2) #if CONFIG_D3D11VA && CONFIG_DXVA2 -#define DXVA_CONTEXT_WORKAROUND(avctx, ctx) (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD ? ctx->d3d11va.workaround : ctx->dxva2.workaround) -#define DXVA_CONTEXT_COUNT(avctx, ctx) (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD ? ctx->d3d11va.surface_count : ctx->dxva2.surface_count) -#define DXVA_CONTEXT_DECODER(avctx, ctx) (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD ? ctx->d3d11va.decoder : ctx->dxva2.decoder) -#define DXVA_CONTEXT_REPORT_ID(avctx, ctx) (*(avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD ? &ctx->d3d11va.report_id : &ctx->dxva2.report_id)) -#define DXVA_CONTEXT_CFG(avctx, ctx) (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD ? ctx->d3d11va.cfg : ctx->dxva2.cfg) -#define DXVA_CONTEXT_CFG_BITSTREAM(avctx, ctx) (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD ? ctx->d3d11va.cfg->ConfigBitstreamRaw : ctx->dxva2.cfg->ConfigBitstreamRaw) -#define DXVA_CONTEXT_CFG_INTRARESID(avctx, ctx) (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD ? ctx->d3d11va.cfg->ConfigIntraResidUnsigned : ctx->dxva2.cfg->ConfigIntraResidUnsigned) -#define DXVA_CONTEXT_CFG_RESIDACCEL(avctx, ctx) (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD ? ctx->d3d11va.cfg->ConfigResidDiffAccelerator : ctx->dxva2.cfg->ConfigResidDiffAccelerator) +#define DXVA_CONTEXT_WORKAROUND(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.workaround : ctx->dxva2.workaround) +#define DXVA_CONTEXT_COUNT(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.surface_count : ctx->dxva2.surface_count) +#define DXVA_CONTEXT_DECODER(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.decoder : ctx->dxva2.decoder) +#define DXVA_CONTEXT_REPORT_ID(avctx, ctx) (*(ff_dxva2_is_d3d11(avctx) ? &ctx->d3d11va.report_id : &ctx->dxva2.report_id)) +#define DXVA_CONTEXT_CFG(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.cfg : ctx->dxva2.cfg) +#define DXVA_CONTEXT_CFG_BITSTREAM(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.cfg->ConfigBitstreamRaw : ctx->dxva2.cfg->ConfigBitstreamRaw) +#define DXVA_CONTEXT_CFG_INTRARESID(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.cfg->ConfigIntraResidUnsigned : ctx->dxva2.cfg->ConfigIntraResidUnsigned) +#define DXVA_CONTEXT_CFG_RESIDACCEL(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.cfg->ConfigResidDiffAccelerator : ctx->dxva2.cfg->ConfigResidDiffAccelerator) #define DXVA_CONTEXT_VALID(avctx, ctx) (DXVA_CONTEXT_DECODER(avctx, ctx) && \ DXVA_CONTEXT_CFG(avctx, ctx) && \ - (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD || ctx->dxva2.surface_count)) + (ff_dxva2_is_d3d11(avctx) || ctx->dxva2.surface_count)) #elif CONFIG_DXVA2 #define DXVA_CONTEXT_WORKAROUND(avctx, ctx) (ctx->dxva2.workaround) #define DXVA_CONTEXT_COUNT(avctx, ctx) (ctx->dxva2.surface_count) @@ -113,4 +115,6 @@ int ff_dxva2_common_end_frame(AVCodecContext *, AVFrame *, DECODER_BUFFER_DESC *bs, DECODER_BUFFER_DESC *slice)); +int ff_dxva2_is_d3d11(const AVCodecContext *avctx); + #endif /* AVCODEC_DXVA2_INTERNAL_H */ diff --git a/libavcodec/dxva2_mpeg2.c b/libavcodec/dxva2_mpeg2.c index a45904963c..6ae30a5773 100644 --- a/libavcodec/dxva2_mpeg2.c +++ b/libavcodec/dxva2_mpeg2.c @@ -156,7 +156,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, DECODER_BUFFER_DESC *sc) { const struct MpegEncContext *s = avctx->priv_data; - AVDXVAContext *ctx = avctx->hwaccel_context; + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); struct dxva2_picture_context *ctx_pic = s->current_picture_ptr->hwaccel_picture_private; const int is_field = s->picture_structure != PICT_FRAME; @@ -168,7 +168,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, unsigned type; #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { type = D3D11_VIDEO_DECODER_BUFFER_BITSTREAM; if (FAILED(ID3D11VideoContext_GetDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, @@ -212,7 +212,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, current += size; } #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) + if (ff_dxva2_is_d3d11(avctx)) if (FAILED(ID3D11VideoContext_ReleaseDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, type))) return -1; #endif @@ -225,7 +225,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, return -1; #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { D3D11_VIDEO_DECODER_BUFFER_DESC *dsc11 = bs; memset(dsc11, 0, sizeof(*dsc11)); dsc11->BufferType = type; @@ -259,7 +259,7 @@ static int dxva2_mpeg2_start_frame(AVCodecContext *avctx, av_unused uint32_t size) { const struct MpegEncContext *s = avctx->priv_data; - AVDXVAContext *ctx = avctx->hwaccel_context; + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); struct dxva2_picture_context *ctx_pic = s->current_picture_ptr->hwaccel_picture_private; diff --git a/libavcodec/dxva2_vc1.c b/libavcodec/dxva2_vc1.c index 0672c97a48..b63580ed2e 100644 --- a/libavcodec/dxva2_vc1.c +++ b/libavcodec/dxva2_vc1.c @@ -165,7 +165,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, DECODER_BUFFER_DESC *sc) { const VC1Context *v = avctx->priv_data; - AVDXVAContext *ctx = avctx->hwaccel_context; + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); const MpegEncContext *s = &v->s; struct dxva2_picture_context *ctx_pic = s->current_picture_ptr->hwaccel_picture_private; @@ -184,7 +184,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, unsigned type; #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { type = D3D11_VIDEO_DECODER_BUFFER_BITSTREAM; if (FAILED(ID3D11VideoContext_GetDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, @@ -215,7 +215,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, slice->dwSliceBitsInBuffer = 8 * data_size; } #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) + if (ff_dxva2_is_d3d11(avctx)) if (FAILED(ID3D11VideoContext_ReleaseDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, type))) return -1; #endif @@ -228,7 +228,7 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, return result; #if CONFIG_D3D11VA - if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { + if (ff_dxva2_is_d3d11(avctx)) { D3D11_VIDEO_DECODER_BUFFER_DESC *dsc11 = bs; memset(dsc11, 0, sizeof(*dsc11)); dsc11->BufferType = type; @@ -261,7 +261,7 @@ static int dxva2_vc1_start_frame(AVCodecContext *avctx, av_unused uint32_t size) { const VC1Context *v = avctx->priv_data; - AVDXVAContext *ctx = avctx->hwaccel_context; + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); struct dxva2_picture_context *ctx_pic = v->s.current_picture_ptr->hwaccel_picture_private; if (!DXVA_CONTEXT_VALID(avctx, ctx)) From 831cfe10b40414915fe7b6088158421fe02e2b2d Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 6 Jun 2017 18:51:10 +0200 Subject: [PATCH 4/5] dxva: move d3d11 locking/unlocking to functions I want to make it non-mandatory to set a mutex in the D3D11 device context, and replacing it with user callbacks seems like the best solution. This is preparation for it. Also makes the code slightly more readable. Signed-off-by: Diego Biurrun --- libavcodec/dxva2.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/libavcodec/dxva2.c b/libavcodec/dxva2.c index fe6dbbc030..5bc9b30653 100644 --- a/libavcodec/dxva2.c +++ b/libavcodec/dxva2.c @@ -29,6 +29,28 @@ #include "avcodec.h" #include "dxva2_internal.h" +static void ff_dxva2_lock(AVCodecContext *avctx) +{ +#if CONFIG_D3D11VA + if (ff_dxva2_is_d3d11(avctx)) { + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); + if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) + WaitForSingleObjectEx(D3D11VA_CONTEXT(ctx)->context_mutex, INFINITE, FALSE); + } +#endif +} + +static void ff_dxva2_unlock(AVCodecContext *avctx) +{ +#if CONFIG_D3D11VA + if (ff_dxva2_is_d3d11(avctx)) { + AVDXVAContext *ctx = DXVA_CONTEXT(avctx); + if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) + ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex); + } +#endif +} + static void *get_surface(const AVFrame *frame) { return frame->data[3]; @@ -153,14 +175,12 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, unsigned type; do { + ff_dxva2_lock(avctx); #if CONFIG_D3D11VA - if (ff_dxva2_is_d3d11(avctx)) { - if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) - WaitForSingleObjectEx(D3D11VA_CONTEXT(ctx)->context_mutex, INFINITE, FALSE); + if (ff_dxva2_is_d3d11(avctx)) hr = ID3D11VideoContext_DecoderBeginFrame(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, get_surface(frame), 0, NULL); - } #endif #if CONFIG_DXVA2 if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) @@ -170,21 +190,13 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, #endif if (hr != E_PENDING || ++runs > 50) break; -#if CONFIG_D3D11VA - if (ff_dxva2_is_d3d11(avctx)) - if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) - ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex); -#endif + ff_dxva2_unlock(avctx); av_usleep(2000); } while(1); if (FAILED(hr)) { av_log(avctx, AV_LOG_ERROR, "Failed to begin frame: 0x%x\n", hr); -#if CONFIG_D3D11VA - if (ff_dxva2_is_d3d11(avctx)) - if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) - ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex); -#endif + ff_dxva2_unlock(avctx); return -1; } @@ -284,16 +296,14 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, end: #if CONFIG_D3D11VA - if (ff_dxva2_is_d3d11(avctx)) { + if (ff_dxva2_is_d3d11(avctx)) hr = ID3D11VideoContext_DecoderEndFrame(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder); - if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) - ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex); - } #endif #if CONFIG_DXVA2 if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) hr = IDirectXVideoDecoder_EndFrame(DXVA2_CONTEXT(ctx)->decoder, NULL); #endif + ff_dxva2_unlock(avctx); if (FAILED(hr)) { av_log(avctx, AV_LOG_ERROR, "Failed to end frame: 0x%x\n", hr); result = -1; From f9e7a2f95a7194a8736cc1416a03a1a0155a3e9f Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 7 Jun 2017 17:11:17 +0200 Subject: [PATCH 5/5] dxva: add support for new dxva2 and d3d11 hwaccel APIs This also adds support to avconv (which is trivial due to the new hwaccel API being generic enough). The new decoder setup code in dxva2.c is significantly based on work by Steve Lhomme , but with heavy changes/rewrites. Signed-off-by: Diego Biurrun --- Changelog | 1 + avtools/Makefile | 1 - avtools/avconv.h | 1 + avtools/avconv_dxva2.c | 440 ------------------------ avtools/avconv_opt.c | 10 +- configure | 19 +- doc/APIchanges | 6 + libavcodec/allcodecs.c | 5 + libavcodec/dxva2.c | 663 +++++++++++++++++++++++++++++++++++- libavcodec/dxva2_h264.c | 22 ++ libavcodec/dxva2_hevc.c | 22 ++ libavcodec/dxva2_internal.h | 43 ++- libavcodec/dxva2_mpeg2.c | 22 ++ libavcodec/dxva2_vc1.c | 44 +++ libavcodec/h264_slice.c | 3 +- libavcodec/hevcdec.c | 3 +- libavcodec/mpeg12dec.c | 1 + libavcodec/vc1dec.c | 1 + libavcodec/version.h | 4 +- libavutil/hwcontext_dxva2.h | 3 + 20 files changed, 853 insertions(+), 461 deletions(-) delete mode 100644 avtools/avconv_dxva2.c diff --git a/Changelog b/Changelog index 6fd30fddb9..e44df54c93 100644 --- a/Changelog +++ b/Changelog @@ -15,6 +15,7 @@ version : - VP9 superframe split/merge bitstream filters - FM Screen Capture Codec decoder - ClearVideo decoder (I-frames only) +- support for decoding through D3D11VA in avconv version 12: diff --git a/avtools/Makefile b/avtools/Makefile index c23a605a55..d95e2d9fc2 100644 --- a/avtools/Makefile +++ b/avtools/Makefile @@ -12,7 +12,6 @@ OBJS-avconv += avtools/avconv_opt.o avtools/avconv_filter.o \ avtools/avconv_hw.o OBJS-avconv-$(CONFIG_LIBMFX) += avtools/avconv_qsv.o OBJS-avconv-$(CONFIG_VDA) += avtools/avconv_vda.o -OBJS-avconv-$(HAVE_DXVA2_LIB) += avtools/avconv_dxva2.o define DOAVTOOL OBJS-$(1) += avtools/cmdutils.o avtools/$(1).o $(OBJS-$(1)-yes) diff --git a/avtools/avconv.h b/avtools/avconv.h index 3354c50444..4c699333a5 100644 --- a/avtools/avconv.h +++ b/avtools/avconv.h @@ -57,6 +57,7 @@ enum HWAccelID { HWACCEL_VDA, HWACCEL_QSV, HWACCEL_VAAPI, + HWACCEL_D3D11VA, }; typedef struct HWAccel { diff --git a/avtools/avconv_dxva2.c b/avtools/avconv_dxva2.c deleted file mode 100644 index 7578c3f632..0000000000 --- a/avtools/avconv_dxva2.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * This file is part of Libav. - * - * Libav 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. - * - * Libav 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 Libav; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif -#define _WIN32_WINNT 0x0600 -#define DXVA2API_USE_BITFIELDS -#define COBJMACROS - -#include - -#include -#include - -#include "avconv.h" - -#include "libavcodec/dxva2.h" - -#include "libavutil/avassert.h" -#include "libavutil/buffer.h" -#include "libavutil/frame.h" -#include "libavutil/imgutils.h" -#include "libavutil/pixfmt.h" - -#include "libavutil/hwcontext.h" -#include "libavutil/hwcontext_dxva2.h" - -/* define all the GUIDs used directly here, - to avoid problems with inconsistent dxva2api.h versions in mingw-w64 and different MSVC version */ -#include -DEFINE_GUID(IID_IDirectXVideoDecoderService, 0xfc51a551,0xd5e7,0x11d9,0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02); - -DEFINE_GUID(DXVA2_ModeMPEG2_VLD, 0xee27417f, 0x5e28,0x4e65,0xbe,0xea,0x1d,0x26,0xb5,0x08,0xad,0xc9); -DEFINE_GUID(DXVA2_ModeMPEG2and1_VLD, 0x86695f12, 0x340e,0x4f04,0x9f,0xd3,0x92,0x53,0xdd,0x32,0x74,0x60); -DEFINE_GUID(DXVA2_ModeH264_E, 0x1b81be68, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); -DEFINE_GUID(DXVA2_ModeH264_F, 0x1b81be69, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); -DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68, 0x4951,0x4C54,0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6); -DEFINE_GUID(DXVA2_ModeVC1_D, 0x1b81beA3, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); -DEFINE_GUID(DXVA2_ModeVC1_D2010, 0x1b81beA4, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); -DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main, 0x5b11d51b, 0x2f4c,0x4452,0xbc,0xc3,0x09,0xf2,0xa1,0x16,0x0c,0xc0); -DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main10,0x107af0e0, 0xef1a,0x4d19,0xab,0xa8,0x67,0xa1,0x63,0x07,0x3d,0x13); -DEFINE_GUID(DXVA2_NoEncrypt, 0x1b81beD0, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); -DEFINE_GUID(GUID_NULL, 0x00000000, 0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00); - -typedef struct dxva2_mode { - const GUID *guid; - enum AVCodecID codec; -} dxva2_mode; - -static const dxva2_mode dxva2_modes[] = { - /* MPEG-2 */ - { &DXVA2_ModeMPEG2_VLD, AV_CODEC_ID_MPEG2VIDEO }, - { &DXVA2_ModeMPEG2and1_VLD, AV_CODEC_ID_MPEG2VIDEO }, - - /* H.264 */ - { &DXVA2_ModeH264_F, AV_CODEC_ID_H264 }, - { &DXVA2_ModeH264_E, AV_CODEC_ID_H264 }, - /* Intel specific H.264 mode */ - { &DXVADDI_Intel_ModeH264_E, AV_CODEC_ID_H264 }, - - /* VC-1 / WMV3 */ - { &DXVA2_ModeVC1_D2010, AV_CODEC_ID_VC1 }, - { &DXVA2_ModeVC1_D2010, AV_CODEC_ID_WMV3 }, - { &DXVA2_ModeVC1_D, AV_CODEC_ID_VC1 }, - { &DXVA2_ModeVC1_D, AV_CODEC_ID_WMV3 }, - - /* HEVC/H.265 */ - { &DXVA2_ModeHEVC_VLD_Main, AV_CODEC_ID_HEVC }, - { &DXVA2_ModeHEVC_VLD_Main10, AV_CODEC_ID_HEVC }, - - { NULL, 0 }, -}; - -typedef struct DXVA2Context { - IDirectXVideoDecoder *decoder; - - GUID decoder_guid; - DXVA2_ConfigPictureDecode decoder_config; - IDirectXVideoDecoderService *decoder_service; - - AVFrame *tmp_frame; - - AVBufferRef *hw_device_ctx; - AVBufferRef *hw_frames_ctx; -} DXVA2Context; - -static void dxva2_uninit(AVCodecContext *s) -{ - InputStream *ist = s->opaque; - DXVA2Context *ctx = ist->hwaccel_ctx; - - ist->hwaccel_uninit = NULL; - ist->hwaccel_get_buffer = NULL; - ist->hwaccel_retrieve_data = NULL; - - if (ctx->decoder_service) - IDirectXVideoDecoderService_Release(ctx->decoder_service); - - av_buffer_unref(&ctx->hw_frames_ctx); - av_buffer_unref(&ctx->hw_device_ctx); - - av_frame_free(&ctx->tmp_frame); - - av_freep(&ist->hwaccel_ctx); - av_freep(&s->hwaccel_context); -} - -static int dxva2_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) -{ - InputStream *ist = s->opaque; - DXVA2Context *ctx = ist->hwaccel_ctx; - - return av_hwframe_get_buffer(ctx->hw_frames_ctx, frame, 0); -} - -static int dxva2_retrieve_data(AVCodecContext *s, AVFrame *frame) -{ - InputStream *ist = s->opaque; - DXVA2Context *ctx = ist->hwaccel_ctx; - int ret; - - ret = av_hwframe_transfer_data(ctx->tmp_frame, frame, 0); - if (ret < 0) - return ret; - - ret = av_frame_copy_props(ctx->tmp_frame, frame); - if (ret < 0) { - av_frame_unref(ctx->tmp_frame); - return ret; - } - - av_frame_unref(frame); - av_frame_move_ref(frame, ctx->tmp_frame); - - return 0; -} - -static int dxva2_alloc(AVCodecContext *s) -{ - InputStream *ist = s->opaque; - int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; - DXVA2Context *ctx; - HANDLE device_handle; - HRESULT hr; - - AVHWDeviceContext *device_ctx; - AVDXVA2DeviceContext *device_hwctx; - int ret; - - ctx = av_mallocz(sizeof(*ctx)); - if (!ctx) - return AVERROR(ENOMEM); - - ist->hwaccel_ctx = ctx; - ist->hwaccel_uninit = dxva2_uninit; - ist->hwaccel_get_buffer = dxva2_get_buffer; - ist->hwaccel_retrieve_data = dxva2_retrieve_data; - - ret = av_hwdevice_ctx_create(&ctx->hw_device_ctx, AV_HWDEVICE_TYPE_DXVA2, - ist->hwaccel_device, NULL, 0); - if (ret < 0) - goto fail; - device_ctx = (AVHWDeviceContext*)ctx->hw_device_ctx->data; - device_hwctx = device_ctx->hwctx; - - hr = IDirect3DDeviceManager9_OpenDeviceHandle(device_hwctx->devmgr, - &device_handle); - if (FAILED(hr)) { - av_log(NULL, loglevel, "Failed to open a device handle\n"); - goto fail; - } - - hr = IDirect3DDeviceManager9_GetVideoService(device_hwctx->devmgr, device_handle, - &IID_IDirectXVideoDecoderService, - (void **)&ctx->decoder_service); - IDirect3DDeviceManager9_CloseDeviceHandle(device_hwctx->devmgr, device_handle); - if (FAILED(hr)) { - av_log(NULL, loglevel, "Failed to create IDirectXVideoDecoderService\n"); - goto fail; - } - - ctx->tmp_frame = av_frame_alloc(); - if (!ctx->tmp_frame) - goto fail; - - s->hwaccel_context = av_mallocz(sizeof(struct dxva_context)); - if (!s->hwaccel_context) - goto fail; - - return 0; -fail: - dxva2_uninit(s); - return AVERROR(EINVAL); -} - -static int dxva2_get_decoder_configuration(AVCodecContext *s, const GUID *device_guid, - const DXVA2_VideoDesc *desc, - DXVA2_ConfigPictureDecode *config) -{ - InputStream *ist = s->opaque; - int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; - DXVA2Context *ctx = ist->hwaccel_ctx; - unsigned cfg_count = 0, best_score = 0; - DXVA2_ConfigPictureDecode *cfg_list = NULL; - DXVA2_ConfigPictureDecode best_cfg = {{0}}; - HRESULT hr; - int i; - - hr = IDirectXVideoDecoderService_GetDecoderConfigurations(ctx->decoder_service, device_guid, desc, NULL, &cfg_count, &cfg_list); - if (FAILED(hr)) { - av_log(NULL, loglevel, "Unable to retrieve decoder configurations\n"); - return AVERROR(EINVAL); - } - - for (i = 0; i < cfg_count; i++) { - DXVA2_ConfigPictureDecode *cfg = &cfg_list[i]; - - unsigned score; - if (cfg->ConfigBitstreamRaw == 1) - score = 1; - else if (s->codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2) - score = 2; - else - continue; - if (IsEqualGUID(&cfg->guidConfigBitstreamEncryption, &DXVA2_NoEncrypt)) - score += 16; - if (score > best_score) { - best_score = score; - best_cfg = *cfg; - } - } - CoTaskMemFree(cfg_list); - - if (!best_score) { - av_log(NULL, loglevel, "No valid decoder configuration available\n"); - return AVERROR(EINVAL); - } - - *config = best_cfg; - return 0; -} - -static int dxva2_create_decoder(AVCodecContext *s) -{ - InputStream *ist = s->opaque; - int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; - DXVA2Context *ctx = ist->hwaccel_ctx; - struct dxva_context *dxva_ctx = s->hwaccel_context; - GUID *guid_list = NULL; - unsigned guid_count = 0, i, j; - GUID device_guid = GUID_NULL; - const D3DFORMAT surface_format = s->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ? - MKTAG('P', '0', '1', '0') : MKTAG('N', 'V', '1', '2'); - D3DFORMAT target_format = 0; - DXVA2_VideoDesc desc = { 0 }; - DXVA2_ConfigPictureDecode config; - HRESULT hr; - int surface_alignment, num_surfaces; - int ret; - - AVDXVA2FramesContext *frames_hwctx; - AVHWFramesContext *frames_ctx; - - hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(ctx->decoder_service, &guid_count, &guid_list); - if (FAILED(hr)) { - av_log(NULL, loglevel, "Failed to retrieve decoder device GUIDs\n"); - goto fail; - } - - for (i = 0; dxva2_modes[i].guid; i++) { - D3DFORMAT *target_list = NULL; - unsigned target_count = 0; - const dxva2_mode *mode = &dxva2_modes[i]; - if (mode->codec != s->codec_id) - continue; - - for (j = 0; j < guid_count; j++) { - if (IsEqualGUID(mode->guid, &guid_list[j])) - break; - } - if (j == guid_count) - continue; - - hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, mode->guid, &target_count, &target_list); - if (FAILED(hr)) { - continue; - } - for (j = 0; j < target_count; j++) { - const D3DFORMAT format = target_list[j]; - if (format == surface_format) { - target_format = format; - break; - } - } - CoTaskMemFree(target_list); - if (target_format) { - device_guid = *mode->guid; - break; - } - } - CoTaskMemFree(guid_list); - - if (IsEqualGUID(&device_guid, &GUID_NULL)) { - av_log(NULL, loglevel, "No decoder device for codec found\n"); - goto fail; - } - - desc.SampleWidth = s->coded_width; - desc.SampleHeight = s->coded_height; - desc.Format = target_format; - - ret = dxva2_get_decoder_configuration(s, &device_guid, &desc, &config); - if (ret < 0) { - goto fail; - } - - /* decoding MPEG-2 requires additional alignment on some Intel GPUs, - but it causes issues for H.264 on certain AMD GPUs..... */ - if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO) - surface_alignment = 32; - /* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure - all coding features have enough room to work with */ - else if (s->codec_id == AV_CODEC_ID_HEVC) - surface_alignment = 128; - else - surface_alignment = 16; - - /* 4 base work surfaces */ - num_surfaces = 4; - - /* add surfaces based on number of possible refs */ - if (s->codec_id == AV_CODEC_ID_H264 || s->codec_id == AV_CODEC_ID_HEVC) - num_surfaces += 16; - else - num_surfaces += 2; - - /* add extra surfaces for frame threading */ - if (s->active_thread_type & FF_THREAD_FRAME) - num_surfaces += s->thread_count; - - ctx->hw_frames_ctx = av_hwframe_ctx_alloc(ctx->hw_device_ctx); - if (!ctx->hw_frames_ctx) - goto fail; - frames_ctx = (AVHWFramesContext*)ctx->hw_frames_ctx->data; - frames_hwctx = frames_ctx->hwctx; - - frames_ctx->format = AV_PIX_FMT_DXVA2_VLD; - frames_ctx->sw_format = s->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ? - AV_PIX_FMT_P010 : AV_PIX_FMT_NV12; - frames_ctx->width = FFALIGN(s->coded_width, surface_alignment); - frames_ctx->height = FFALIGN(s->coded_height, surface_alignment); - frames_ctx->initial_pool_size = num_surfaces; - - frames_hwctx->surface_type = DXVA2_VideoDecoderRenderTarget; - - ret = av_hwframe_ctx_init(ctx->hw_frames_ctx); - if (ret < 0) { - av_log(NULL, loglevel, "Failed to initialize the HW frames context\n"); - goto fail; - } - - hr = IDirectXVideoDecoderService_CreateVideoDecoder(ctx->decoder_service, &device_guid, - &desc, &config, frames_hwctx->surfaces, - frames_hwctx->nb_surfaces, &frames_hwctx->decoder_to_release); - if (FAILED(hr)) { - av_log(NULL, loglevel, "Failed to create DXVA2 video decoder\n"); - goto fail; - } - - ctx->decoder_guid = device_guid; - ctx->decoder_config = config; - - dxva_ctx->cfg = &ctx->decoder_config; - dxva_ctx->decoder = frames_hwctx->decoder_to_release; - dxva_ctx->surface = frames_hwctx->surfaces; - dxva_ctx->surface_count = frames_hwctx->nb_surfaces; - - if (IsEqualGUID(&ctx->decoder_guid, &DXVADDI_Intel_ModeH264_E)) - dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; - - return 0; -fail: - av_buffer_unref(&ctx->hw_frames_ctx); - return AVERROR(EINVAL); -} - -int dxva2_init(AVCodecContext *s) -{ - InputStream *ist = s->opaque; - int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; - DXVA2Context *ctx; - int ret; - - if (!ist->hwaccel_ctx) { - ret = dxva2_alloc(s); - if (ret < 0) - return ret; - } - ctx = ist->hwaccel_ctx; - - if (s->codec_id == AV_CODEC_ID_H264 && - (s->profile & ~FF_PROFILE_H264_CONSTRAINED) > FF_PROFILE_H264_HIGH) { - av_log(NULL, loglevel, "Unsupported H.264 profile for DXVA2 HWAccel: %d\n", s->profile); - return AVERROR(EINVAL); - } - - if (s->codec_id == AV_CODEC_ID_HEVC && - s->profile != FF_PROFILE_HEVC_MAIN && s->profile != FF_PROFILE_HEVC_MAIN_10) { - av_log(NULL, loglevel, "Unsupported HEVC profile for DXVA2 HWAccel: %d\n", s->profile); - return AVERROR(EINVAL); - } - - av_buffer_unref(&ctx->hw_frames_ctx); - - ret = dxva2_create_decoder(s); - if (ret < 0) { - av_log(NULL, loglevel, "Error creating the DXVA2 decoder\n"); - return ret; - } - - return 0; -} diff --git a/avtools/avconv_opt.c b/avtools/avconv_opt.c index 9839a2269e..575ce120dd 100644 --- a/avtools/avconv_opt.c +++ b/avtools/avconv_opt.c @@ -60,9 +60,13 @@ const HWAccel hwaccels[] = { { "vdpau", hwaccel_decode_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU, AV_HWDEVICE_TYPE_VDPAU }, #endif -#if HAVE_DXVA2_LIB - { "dxva2", dxva2_init, HWACCEL_DXVA2, AV_PIX_FMT_DXVA2_VLD, - AV_HWDEVICE_TYPE_NONE }, +#if CONFIG_D3D11VA + { "d3d11va", hwaccel_decode_init, HWACCEL_D3D11VA, AV_PIX_FMT_D3D11, + AV_HWDEVICE_TYPE_D3D11VA }, +#endif +#if CONFIG_DXVA2 + { "dxva2", hwaccel_decode_init, HWACCEL_DXVA2, AV_PIX_FMT_DXVA2_VLD, + AV_HWDEVICE_TYPE_DXVA2 }, #endif #if CONFIG_VDA { "vda", vda_init, HWACCEL_VDA, AV_PIX_FMT_VDA, diff --git a/configure b/configure index 0281f54505..019902cee6 100755 --- a/configure +++ b/configure @@ -1713,7 +1713,6 @@ HAVE_LIST=" $TOOLCHAIN_FEATURES $TYPES_LIST dos_paths - dxva2_lib libc_msvcrt MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS sdl @@ -2166,8 +2165,7 @@ zmbv_encoder_deps="zlib" # hardware accelerators d3d11va_deps="d3d11_h dxva_h ID3D11VideoDecoder" -dxva2_deps="dxva2api_h DXVA2_ConfigPictureDecode" -dxva2_lib_deps="dxva2" +dxva2_deps="dxva2api_h DXVA2_ConfigPictureDecode ole32" vda_deps="VideoDecodeAcceleration_VDADecoder_h blocks_extension pthreads" vda_extralibs="-framework CoreFoundation -framework VideoDecodeAcceleration -framework QuartzCore" @@ -2175,6 +2173,8 @@ h263_vaapi_hwaccel_deps="vaapi" h263_vaapi_hwaccel_select="h263_decoder" h264_d3d11va_hwaccel_deps="d3d11va" h264_d3d11va_hwaccel_select="h264_decoder" +h264_d3d11va2_hwaccel_deps="d3d11va" +h264_d3d11va2_hwaccel_select="h264_decoder" h264_dxva2_hwaccel_deps="dxva2" h264_dxva2_hwaccel_select="h264_decoder" h264_mmal_hwaccel_deps="mmal" @@ -2189,6 +2189,8 @@ h264_vdpau_hwaccel_deps="vdpau" h264_vdpau_hwaccel_select="h264_decoder" hevc_d3d11va_hwaccel_deps="d3d11va DXVA_PicParams_HEVC" hevc_d3d11va_hwaccel_select="hevc_decoder" +hevc_d3d11va2_hwaccel_deps="d3d11va DXVA_PicParams_HEVC" +hevc_d3d11va2_hwaccel_select="hevc_decoder" hevc_dxva2_hwaccel_deps="dxva2 DXVA_PicParams_HEVC" hevc_dxva2_hwaccel_select="hevc_decoder" hevc_qsv_hwaccel_deps="libmfx" @@ -2200,6 +2202,8 @@ mpeg1_vdpau_hwaccel_deps="vdpau" mpeg1_vdpau_hwaccel_select="mpeg1video_decoder" mpeg2_d3d11va_hwaccel_deps="d3d11va" mpeg2_d3d11va_hwaccel_select="mpeg2video_decoder" +mpeg2_d3d11va2_hwaccel_deps="d3d11va" +mpeg2_d3d11va2_hwaccel_select="mpeg2video_decoder" mpeg2_dxva2_hwaccel_deps="dxva2" mpeg2_dxva2_hwaccel_select="mpeg2video_decoder" mpeg2_mmal_hwaccel_deps="mmal" @@ -2214,6 +2218,8 @@ mpeg4_vdpau_hwaccel_deps="vdpau" mpeg4_vdpau_hwaccel_select="mpeg4_decoder" vc1_d3d11va_hwaccel_deps="d3d11va" vc1_d3d11va_hwaccel_select="vc1_decoder" +vc1_d3d11va2_hwaccel_deps="d3d11va" +vc1_d3d11va2_hwaccel_select="vc1_decoder" vc1_dxva2_hwaccel_deps="dxva2" vc1_dxva2_hwaccel_select="vc1_decoder" vc1_mmal_hwaccel_deps="mmal" @@ -2226,6 +2232,7 @@ vp8_qsv_hwaccel_deps="libmfx" vp8_vaapi_hwaccel_deps="vaapi VAPictureParameterBufferVP8" vp8_vaapi_hwaccel_select="vp8_decoder" wmv3_d3d11va_hwaccel_select="vc1_d3d11va_hwaccel" +wmv3_d3d11va2_hwaccel_select="vc1_d3d11va2_hwaccel" wmv3_dxva2_hwaccel_select="vc1_dxva2_hwaccel" wmv3_vaapi_hwaccel_select="vc1_vaapi_hwaccel" wmv3_vdpau_hwaccel_select="vc1_vdpau_hwaccel" @@ -2544,7 +2551,7 @@ avconv_deps="avcodec avfilter avformat avresample swscale" avconv_select="aformat_filter anull_filter asyncts_filter atrim_filter format_filter fps_filter null_filter resample_filter scale_filter trim_filter" -avconv_suggest="dxva2_lib ole32 psapi shell32" +avconv_suggest="psapi shell32" avplay_deps="avcodec avfilter avformat avresample sdl" avplay_select="rdft format_filter transpose_filter hflip_filter vflip_filter" avplay_suggest="shell32" @@ -4670,6 +4677,7 @@ check_header windows.h # so we also check that atomics actually work here check_builtin stdatomic_h stdatomic.h "atomic_int foo; atomic_store(&foo, 0)" +check_lib ole32 "windows.h" CoTaskMemFree -lole32 check_lib shell32 "windows.h shellapi.h" CommandLineToArgvW -lshell32 check_lib wincrypt "windows.h wincrypt.h" CryptGenRandom -ladvapi32 check_lib psapi "windows.h psapi.h" GetProcessMemoryInfo -lpsapi @@ -4877,9 +4885,6 @@ if enabled libxcb; then check_pkg_config libxcb_xfixes xcb-xfixes xcb/xfixes.h xcb_xfixes_get_cursor_image fi -enabled dxva2 && - check_lib dxva2_lib windows.h CoTaskMemFree -lole32 - enabled vaapi && require vaapi va/va.h vaInitialize -lva enabled vaapi && diff --git a/doc/APIchanges b/doc/APIchanges index a81e41833d..0f7c839573 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -13,6 +13,12 @@ libavutil: 2017-03-23 API changes, most recent first: +2017-xx-xx - xxxxxxx - lavc 58.4.0 - avcodec.h + DXVA2 and D3D11 hardware accelerated decoding now supports the new hwaccel API, + which can create the decoder context and allocate hardware frame automatically. + See AVCodecContext.hw_device_ctx and AVCodecContext.hw_frames_ctx. For D3D11, + the new AV_PIX_FMT_D3D11 pixfmt must be used with the new API. + 2017-xx-xx - xxxxxxx - lavu 56.2.0 - hwcontext.h Add AV_HWDEVICE_TYPE_D3D11VA and AV_PIX_FMT_D3D11. diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index dc9a961440..70c35e9b4d 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -69,6 +69,7 @@ void avcodec_register_all(void) /* hardware accelerators */ REGISTER_HWACCEL(H263_VAAPI, h263_vaapi); REGISTER_HWACCEL(H264_D3D11VA, h264_d3d11va); + REGISTER_HWACCEL(H264_D3D11VA2, h264_d3d11va2); REGISTER_HWACCEL(H264_DXVA2, h264_dxva2); REGISTER_HWACCEL(H264_MMAL, h264_mmal); REGISTER_HWACCEL(H264_QSV, h264_qsv); @@ -77,12 +78,14 @@ void avcodec_register_all(void) REGISTER_HWACCEL(H264_VDA_OLD, h264_vda_old); REGISTER_HWACCEL(H264_VDPAU, h264_vdpau); REGISTER_HWACCEL(HEVC_D3D11VA, hevc_d3d11va); + REGISTER_HWACCEL(HEVC_D3D11VA2, hevc_d3d11va2); REGISTER_HWACCEL(HEVC_DXVA2, hevc_dxva2); REGISTER_HWACCEL(HEVC_QSV, hevc_qsv); REGISTER_HWACCEL(HEVC_VAAPI, hevc_vaapi); REGISTER_HWACCEL(HEVC_VDPAU, hevc_vdpau); REGISTER_HWACCEL(MPEG1_VDPAU, mpeg1_vdpau); REGISTER_HWACCEL(MPEG2_D3D11VA, mpeg2_d3d11va); + REGISTER_HWACCEL(MPEG2_D3D11VA2, mpeg2_d3d11va2); REGISTER_HWACCEL(MPEG2_DXVA2, mpeg2_dxva2); REGISTER_HWACCEL(MPEG2_MMAL, mpeg2_mmal); REGISTER_HWACCEL(MPEG2_QSV, mpeg2_qsv); @@ -91,6 +94,7 @@ void avcodec_register_all(void) REGISTER_HWACCEL(MPEG4_VAAPI, mpeg4_vaapi); REGISTER_HWACCEL(MPEG4_VDPAU, mpeg4_vdpau); REGISTER_HWACCEL(VC1_D3D11VA, vc1_d3d11va); + REGISTER_HWACCEL(VC1_D3D11VA2, vc1_d3d11va2); REGISTER_HWACCEL(VC1_DXVA2, vc1_dxva2); REGISTER_HWACCEL(VC1_QSV, vc1_qsv); REGISTER_HWACCEL(VC1_VAAPI, vc1_vaapi); @@ -99,6 +103,7 @@ void avcodec_register_all(void) REGISTER_HWACCEL(VP8_QSV, vp8_qsv); REGISTER_HWACCEL(VP8_VAAPI, vp8_vaapi); REGISTER_HWACCEL(WMV3_D3D11VA, wmv3_d3d11va); + REGISTER_HWACCEL(WMV3_D3D11VA2, wmv3_d3d11va2); REGISTER_HWACCEL(WMV3_DXVA2, wmv3_dxva2); REGISTER_HWACCEL(WMV3_VAAPI, wmv3_vaapi); REGISTER_HWACCEL(WMV3_VDPAU, wmv3_vdpau); diff --git a/libavcodec/dxva2.c b/libavcodec/dxva2.c index 5bc9b30653..e0c54068d6 100644 --- a/libavcodec/dxva2.c +++ b/libavcodec/dxva2.c @@ -22,20 +22,446 @@ #include #include +#include +#include "libavutil/common.h" #include "libavutil/log.h" #include "libavutil/time.h" #include "avcodec.h" #include "dxva2_internal.h" +/* define all the GUIDs used directly here, + to avoid problems with inconsistent dxva2api.h versions in mingw-w64 and different MSVC version */ +DEFINE_GUID(ff_DXVA2_ModeMPEG2_VLD, 0xee27417f, 0x5e28,0x4e65,0xbe,0xea,0x1d,0x26,0xb5,0x08,0xad,0xc9); +DEFINE_GUID(ff_DXVA2_ModeMPEG2and1_VLD, 0x86695f12, 0x340e,0x4f04,0x9f,0xd3,0x92,0x53,0xdd,0x32,0x74,0x60); +DEFINE_GUID(ff_DXVA2_ModeH264_E, 0x1b81be68, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); +DEFINE_GUID(ff_DXVA2_ModeH264_F, 0x1b81be69, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); +DEFINE_GUID(ff_DXVADDI_Intel_ModeH264_E, 0x604F8E68, 0x4951,0x4C54,0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6); +DEFINE_GUID(ff_DXVA2_ModeVC1_D, 0x1b81beA3, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); +DEFINE_GUID(ff_DXVA2_ModeVC1_D2010, 0x1b81beA4, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); +DEFINE_GUID(ff_DXVA2_ModeHEVC_VLD_Main, 0x5b11d51b, 0x2f4c,0x4452,0xbc,0xc3,0x09,0xf2,0xa1,0x16,0x0c,0xc0); +DEFINE_GUID(ff_DXVA2_ModeHEVC_VLD_Main10,0x107af0e0, 0xef1a,0x4d19,0xab,0xa8,0x67,0xa1,0x63,0x07,0x3d,0x13); +DEFINE_GUID(ff_DXVA2_NoEncrypt, 0x1b81beD0, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); +DEFINE_GUID(ff_GUID_NULL, 0x00000000, 0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00); +DEFINE_GUID(ff_IID_IDirectXVideoDecoderService, 0xfc51a551,0xd5e7,0x11d9,0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02); + +typedef struct dxva_mode { + const GUID *guid; + enum AVCodecID codec; +} dxva_mode; + +static const dxva_mode dxva_modes[] = { + /* MPEG-2 */ + { &ff_DXVA2_ModeMPEG2_VLD, AV_CODEC_ID_MPEG2VIDEO }, + { &ff_DXVA2_ModeMPEG2and1_VLD, AV_CODEC_ID_MPEG2VIDEO }, + + /* H.264 */ + { &ff_DXVA2_ModeH264_F, AV_CODEC_ID_H264 }, + { &ff_DXVA2_ModeH264_E, AV_CODEC_ID_H264 }, + /* Intel specific H.264 mode */ + { &ff_DXVADDI_Intel_ModeH264_E, AV_CODEC_ID_H264 }, + + /* VC-1 / WMV3 */ + { &ff_DXVA2_ModeVC1_D2010, AV_CODEC_ID_VC1 }, + { &ff_DXVA2_ModeVC1_D2010, AV_CODEC_ID_WMV3 }, + { &ff_DXVA2_ModeVC1_D, AV_CODEC_ID_VC1 }, + { &ff_DXVA2_ModeVC1_D, AV_CODEC_ID_WMV3 }, + + /* HEVC/H.265 */ + { &ff_DXVA2_ModeHEVC_VLD_Main, AV_CODEC_ID_HEVC }, + { &ff_DXVA2_ModeHEVC_VLD_Main10, AV_CODEC_ID_HEVC }, + + { NULL, 0 }, +}; + +static int dxva_get_decoder_configuration(AVCodecContext *avctx, + const void *cfg_list, + unsigned cfg_count) +{ + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); + unsigned i, best_score = 0; + int best_cfg = -1; + + for (i = 0; i < cfg_count; i++) { + unsigned score; + UINT ConfigBitstreamRaw; + GUID guidConfigBitstreamEncryption; + +#if CONFIG_D3D11VA + if (sctx->pix_fmt == AV_PIX_FMT_D3D11) { + D3D11_VIDEO_DECODER_CONFIG *cfg = &((D3D11_VIDEO_DECODER_CONFIG *)cfg_list)[i]; + ConfigBitstreamRaw = cfg->ConfigBitstreamRaw; + guidConfigBitstreamEncryption = cfg->guidConfigBitstreamEncryption; + } +#endif +#if CONFIG_DXVA2 + if (sctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) { + DXVA2_ConfigPictureDecode *cfg = &((DXVA2_ConfigPictureDecode *)cfg_list)[i]; + ConfigBitstreamRaw = cfg->ConfigBitstreamRaw; + guidConfigBitstreamEncryption = cfg->guidConfigBitstreamEncryption; + } +#endif + + if (ConfigBitstreamRaw == 1) + score = 1; + else if (avctx->codec_id == AV_CODEC_ID_H264 && ConfigBitstreamRaw == 2) + score = 2; + else + continue; + if (IsEqualGUID(&guidConfigBitstreamEncryption, &ff_DXVA2_NoEncrypt)) + score += 16; + if (score > best_score) { + best_score = score; + best_cfg = i; + } + } + + if (!best_score) { + av_log(avctx, AV_LOG_VERBOSE, "No valid decoder configuration available\n"); + return AVERROR(EINVAL); + } + + return best_cfg; +} + +#if CONFIG_D3D11VA +static int d3d11va_validate_output(void *service, GUID guid, void *surface_format) +{ + HRESULT hr; + BOOL is_supported = FALSE; + hr = ID3D11VideoDevice_CheckVideoDecoderFormat((ID3D11VideoDevice *)service, + &guid, + *(DXGI_FORMAT *)surface_format, + &is_supported); + return SUCCEEDED(hr) && is_supported; +} +#endif + +#if CONFIG_DXVA2 +static int dxva2_validate_output(void *decoder_service, GUID guid, void *surface_format) +{ + HRESULT hr; + int ret = 0; + unsigned j, target_count; + D3DFORMAT *target_list; + hr = IDirectXVideoDecoderService_GetDecoderRenderTargets((IDirectXVideoDecoderService *)decoder_service, &guid, &target_count, &target_list); + if (SUCCEEDED(hr)) { + for (j = 0; j < target_count; j++) { + const D3DFORMAT format = target_list[j]; + if (format == *(D3DFORMAT *)surface_format) { + ret = 1; + break; + } + } + CoTaskMemFree(target_list); + } + return ret; +} +#endif + +static int dxva_get_decoder_guid(AVCodecContext *avctx, void *service, void *surface_format, + unsigned guid_count, const GUID *guid_list, GUID *decoder_guid) +{ + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); + unsigned i, j; + + *decoder_guid = ff_GUID_NULL; + for (i = 0; dxva_modes[i].guid; i++) { + const dxva_mode *mode = &dxva_modes[i]; + int validate; + if (mode->codec != avctx->codec_id) + continue; + + for (j = 0; j < guid_count; j++) { + if (IsEqualGUID(mode->guid, &guid_list[j])) + break; + } + if (j == guid_count) + continue; + +#if CONFIG_D3D11VA + if (sctx->pix_fmt == AV_PIX_FMT_D3D11) + validate = d3d11va_validate_output(service, *mode->guid, surface_format); +#endif +#if CONFIG_DXVA2 + if (sctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) + validate = dxva2_validate_output(service, *mode->guid, surface_format); +#endif + if (validate) { + *decoder_guid = *mode->guid; + break; + } + } + + if (IsEqualGUID(decoder_guid, &ff_GUID_NULL)) { + av_log(avctx, AV_LOG_VERBOSE, "No decoder device for codec found\n"); + return AVERROR(EINVAL); + } + + if (IsEqualGUID(decoder_guid, &ff_DXVADDI_Intel_ModeH264_E)) + sctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; + + return 0; +} + +static void bufref_free_interface(void *opaque, uint8_t *data) +{ + IUnknown_Release((IUnknown *)opaque); +} + +static AVBufferRef *bufref_wrap_interface(IUnknown *iface) +{ + return av_buffer_create((uint8_t*)iface, 1, bufref_free_interface, iface, 0); +} + +#if CONFIG_DXVA2 + +static int dxva2_get_decoder_configuration(AVCodecContext *avctx, const GUID *device_guid, + const DXVA2_VideoDesc *desc, + DXVA2_ConfigPictureDecode *config) +{ + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); + unsigned cfg_count; + DXVA2_ConfigPictureDecode *cfg_list; + HRESULT hr; + int ret; + + hr = IDirectXVideoDecoderService_GetDecoderConfigurations(sctx->dxva2_service, device_guid, desc, NULL, &cfg_count, &cfg_list); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Unable to retrieve decoder configurations\n"); + return AVERROR(EINVAL); + } + + ret = dxva_get_decoder_configuration(avctx, cfg_list, cfg_count); + if (ret >= 0) + *config = cfg_list[ret]; + CoTaskMemFree(cfg_list); + return ret; +} + +static int dxva2_create_decoder(AVCodecContext *avctx) +{ + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); + GUID *guid_list; + unsigned guid_count; + GUID device_guid; + D3DFORMAT surface_format = avctx->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ? + MKTAG('P', '0', '1', '0') : MKTAG('N', 'V', '1', '2'); + DXVA2_VideoDesc desc = { 0 }; + DXVA2_ConfigPictureDecode config; + HRESULT hr; + int ret; + HANDLE device_handle; + AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data; + AVDXVA2FramesContext *frames_hwctx = frames_ctx->hwctx; + AVDXVA2DeviceContext *device_hwctx = frames_ctx->device_ctx->hwctx; + + hr = IDirect3DDeviceManager9_OpenDeviceHandle(device_hwctx->devmgr, + &device_handle); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to open a device handle\n"); + goto fail; + } + + hr = IDirect3DDeviceManager9_GetVideoService(device_hwctx->devmgr, device_handle, + &ff_IID_IDirectXVideoDecoderService, + (void **)&sctx->dxva2_service); + IDirect3DDeviceManager9_CloseDeviceHandle(device_hwctx->devmgr, device_handle); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create IDirectXVideoDecoderService\n"); + goto fail; + } + + hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(sctx->dxva2_service, &guid_count, &guid_list); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to retrieve decoder device GUIDs\n"); + goto fail; + } + + ret = dxva_get_decoder_guid(avctx, sctx->dxva2_service, &surface_format, + guid_count, guid_list, &device_guid); + CoTaskMemFree(guid_list); + if (ret < 0) { + goto fail; + } + + desc.SampleWidth = avctx->coded_width; + desc.SampleHeight = avctx->coded_height; + desc.Format = surface_format; + + ret = dxva2_get_decoder_configuration(avctx, &device_guid, &desc, &config); + if (ret < 0) { + goto fail; + } + + hr = IDirectXVideoDecoderService_CreateVideoDecoder(sctx->dxva2_service, &device_guid, + &desc, &config, frames_hwctx->surfaces, + frames_hwctx->nb_surfaces, &sctx->dxva2_decoder); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create DXVA2 video decoder\n"); + goto fail; + } + + sctx->dxva2_config = config; + + sctx->decoder_ref = bufref_wrap_interface((IUnknown *)sctx->dxva2_decoder); + if (!sctx->decoder_ref) + return AVERROR(ENOMEM); + + return 0; +fail: + return AVERROR(EINVAL); +} + +#endif + +#if CONFIG_D3D11VA + +static int d3d11va_get_decoder_configuration(AVCodecContext *avctx, + ID3D11VideoDevice *video_device, + const D3D11_VIDEO_DECODER_DESC *desc, + D3D11_VIDEO_DECODER_CONFIG *config) +{ + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); + unsigned cfg_count = 0; + D3D11_VIDEO_DECODER_CONFIG *cfg_list = NULL; + HRESULT hr; + int i, ret; + + hr = ID3D11VideoDevice_GetVideoDecoderConfigCount(video_device, desc, &cfg_count); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Unable to retrieve decoder configurations\n"); + return AVERROR(EINVAL); + } + + cfg_list = av_malloc_array(cfg_count, sizeof(D3D11_VIDEO_DECODER_CONFIG)); + if (cfg_list == NULL) + return AVERROR(ENOMEM); + for (i = 0; i < cfg_count; i++) { + hr = ID3D11VideoDevice_GetVideoDecoderConfig(video_device, desc, i, &cfg_list[i]); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Unable to retrieve decoder configurations. (hr=0x%lX)\n", hr); + av_free(cfg_list); + return AVERROR(EINVAL); + } + } + + ret = dxva_get_decoder_configuration(avctx, cfg_list, cfg_count); + if (ret >= 0) + *config = cfg_list[ret]; + av_free(cfg_list); + return ret; +} + +static int d3d11va_create_decoder(AVCodecContext *avctx) +{ + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); + GUID *guid_list; + unsigned guid_count, i; + GUID decoder_guid; + DXGI_FORMAT surface_format = avctx->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ? + DXGI_FORMAT_P010 : DXGI_FORMAT_NV12; + D3D11_VIDEO_DECODER_DESC desc = { 0 }; + D3D11_VIDEO_DECODER_CONFIG config; + AVHWFramesContext *frames_ctx = (AVHWFramesContext *)avctx->hw_frames_ctx->data; + AVD3D11VADeviceContext *device_hwctx = frames_ctx->device_ctx->hwctx; + AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx; + D3D11_TEXTURE2D_DESC texdesc; + HRESULT hr; + int ret; + + if (!frames_hwctx->texture) { + av_log(avctx, AV_LOG_ERROR, "AVD3D11VAFramesContext.texture not set.\n"); + return AVERROR(EINVAL); + } + ID3D11Texture2D_GetDesc(frames_hwctx->texture, &texdesc); + + guid_count = ID3D11VideoDevice_GetVideoDecoderProfileCount(device_hwctx->video_device); + guid_list = av_malloc_array(guid_count, sizeof(*guid_list)); + if (guid_list == NULL || guid_count == 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to get the decoder GUIDs\n"); + av_free(guid_list); + return AVERROR(EINVAL); + } + for (i = 0; i < guid_count; i++) { + hr = ID3D11VideoDevice_GetVideoDecoderProfile(device_hwctx->video_device, i, &guid_list[i]); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to retrieve decoder GUID %d\n", i); + av_free(guid_list); + return AVERROR(EINVAL); + } + } + + ret = dxva_get_decoder_guid(avctx, device_hwctx->video_device, &surface_format, + guid_count, guid_list, &decoder_guid); + av_free(guid_list); + if (ret < 0) + return AVERROR(EINVAL); + + desc.SampleWidth = avctx->coded_width; + desc.SampleHeight = avctx->coded_height; + desc.OutputFormat = surface_format; + desc.Guid = decoder_guid; + + ret = d3d11va_get_decoder_configuration(avctx, device_hwctx->video_device, &desc, &config); + if (ret < 0) + return AVERROR(EINVAL); + + sctx->d3d11_views = av_mallocz_array(texdesc.ArraySize, sizeof(sctx->d3d11_views[0])); + if (!sctx->d3d11_views) + return AVERROR(ENOMEM); + sctx->nb_d3d11_views = texdesc.ArraySize; + + for (i = 0; i < sctx->nb_d3d11_views; i++) { + D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc = { + .DecodeProfile = decoder_guid, + .ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D, + .Texture2D = { + .ArraySlice = i, + } + }; + hr = ID3D11VideoDevice_CreateVideoDecoderOutputView(device_hwctx->video_device, + (ID3D11Resource*) frames_hwctx->texture, + &viewDesc, + (ID3D11VideoDecoderOutputView**) &sctx->d3d11_views[i]); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Could not create the decoder output view %d\n", i); + return AVERROR_UNKNOWN; + } + } + + hr = ID3D11VideoDevice_CreateVideoDecoder(device_hwctx->video_device, &desc, + &config, &sctx->d3d11_decoder); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create D3D11VA video decoder\n"); + return AVERROR(EINVAL); + } + + sctx->d3d11_config = config; + sctx->d3d11_texture = frames_hwctx->texture; + + sctx->decoder_ref = bufref_wrap_interface((IUnknown *)sctx->d3d11_decoder); + if (!sctx->decoder_ref) + return AVERROR(ENOMEM); + + return 0; +} + +#endif + static void ff_dxva2_lock(AVCodecContext *avctx) { #if CONFIG_D3D11VA if (ff_dxva2_is_d3d11(avctx)) { + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); AVDXVAContext *ctx = DXVA_CONTEXT(avctx); if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) WaitForSingleObjectEx(D3D11VA_CONTEXT(ctx)->context_mutex, INFINITE, FALSE); + if (sctx->device_ctx) { + AVD3D11VADeviceContext *hwctx = sctx->device_ctx->hwctx; + hwctx->lock(hwctx->lock_ctx); + } } #endif } @@ -44,15 +470,216 @@ static void ff_dxva2_unlock(AVCodecContext *avctx) { #if CONFIG_D3D11VA if (ff_dxva2_is_d3d11(avctx)) { + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); AVDXVAContext *ctx = DXVA_CONTEXT(avctx); if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex); + if (sctx->device_ctx) { + AVD3D11VADeviceContext *hwctx = sctx->device_ctx->hwctx; + hwctx->unlock(hwctx->lock_ctx); + } } #endif } -static void *get_surface(const AVFrame *frame) +// This must work before the decoder is created. +// This somehow needs to be exported to the user. +static void dxva_adjust_hwframes(AVCodecContext *avctx, AVHWFramesContext *frames_ctx) { + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); + int surface_alignment, num_surfaces; + + frames_ctx->format = sctx->pix_fmt; + + /* decoding MPEG-2 requires additional alignment on some Intel GPUs, + but it causes issues for H.264 on certain AMD GPUs..... */ + if (avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) + surface_alignment = 32; + /* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure + all coding features have enough room to work with */ + else if (avctx->codec_id == AV_CODEC_ID_HEVC) + surface_alignment = 128; + else + surface_alignment = 16; + + /* 4 base work surfaces */ + num_surfaces = 4; + + /* add surfaces based on number of possible refs */ + if (avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_HEVC) + num_surfaces += 16; + else + num_surfaces += 2; + + /* add extra surfaces for frame threading */ + if (avctx->active_thread_type & FF_THREAD_FRAME) + num_surfaces += avctx->thread_count; + + frames_ctx->sw_format = avctx->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ? + AV_PIX_FMT_P010 : AV_PIX_FMT_NV12; + frames_ctx->width = FFALIGN(avctx->coded_width, surface_alignment); + frames_ctx->height = FFALIGN(avctx->coded_height, surface_alignment); + frames_ctx->initial_pool_size = num_surfaces; + + +#if CONFIG_DXVA2 + if (frames_ctx->format == AV_PIX_FMT_DXVA2_VLD) { + AVDXVA2FramesContext *frames_hwctx = frames_ctx->hwctx; + + frames_hwctx->surface_type = DXVA2_VideoDecoderRenderTarget; + } +#endif + +#if CONFIG_D3D11VA + if (frames_ctx->format == AV_PIX_FMT_D3D11) { + AVD3D11VAFramesContext *frames_hwctx = frames_ctx->hwctx; + + frames_hwctx->BindFlags |= D3D11_BIND_DECODER; + } +#endif +} + +int ff_dxva2_decode_init(AVCodecContext *avctx) +{ + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); + AVHWFramesContext *frames_ctx = NULL; + int ret = 0; + + // Old API. + if (avctx->hwaccel_context) + return 0; + + // (avctx->pix_fmt is not updated yet at this point) + sctx->pix_fmt = avctx->hwaccel->pix_fmt; + + if (avctx->codec_id == AV_CODEC_ID_H264 && + (avctx->profile & ~FF_PROFILE_H264_CONSTRAINED) > FF_PROFILE_H264_HIGH) { + av_log(avctx, AV_LOG_VERBOSE, "Unsupported H.264 profile for DXVA HWAccel: %d\n",avctx->profile); + return AVERROR(ENOTSUP); + } + + if (avctx->codec_id == AV_CODEC_ID_HEVC && + avctx->profile != FF_PROFILE_HEVC_MAIN && avctx->profile != FF_PROFILE_HEVC_MAIN_10) { + av_log(avctx, AV_LOG_VERBOSE, "Unsupported HEVC profile for DXVA HWAccel: %d\n", avctx->profile); + return AVERROR(ENOTSUP); + } + + if (!avctx->hw_frames_ctx && !avctx->hw_device_ctx) { + av_log(avctx, AV_LOG_ERROR, "Either a hw_frames_ctx or a hw_device_ctx needs to be set for hardware decoding.\n"); + return AVERROR(EINVAL); + } + + if (avctx->hw_frames_ctx) { + frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data; + } else { + avctx->hw_frames_ctx = av_hwframe_ctx_alloc(avctx->hw_device_ctx); + if (!avctx->hw_frames_ctx) + return AVERROR(ENOMEM); + + frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data; + + dxva_adjust_hwframes(avctx, frames_ctx); + + ret = av_hwframe_ctx_init(avctx->hw_frames_ctx); + if (ret < 0) + goto fail; + } + + sctx->device_ctx = frames_ctx->device_ctx; + + if (frames_ctx->format != sctx->pix_fmt || + !((sctx->pix_fmt == AV_PIX_FMT_D3D11 && CONFIG_D3D11VA) || + (sctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD && CONFIG_DXVA2))) { + av_log(avctx, AV_LOG_ERROR, "Invalid pixfmt for hwaccel!\n"); + ret = AVERROR(EINVAL); + goto fail; + } + +#if CONFIG_D3D11VA + if (sctx->pix_fmt == AV_PIX_FMT_D3D11) { + AVD3D11VADeviceContext *device_hwctx = frames_ctx->device_ctx->hwctx; + AVD3D11VAContext *d3d11_ctx = &sctx->ctx.d3d11va; + HRESULT hr; + + ff_dxva2_lock(avctx); + ret = d3d11va_create_decoder(avctx); + ff_dxva2_unlock(avctx); + if (ret < 0) + goto fail; + + d3d11_ctx->decoder = sctx->d3d11_decoder; + d3d11_ctx->video_context = device_hwctx->video_context; + d3d11_ctx->cfg = &sctx->d3d11_config; + d3d11_ctx->surface_count = sctx->nb_d3d11_views; + d3d11_ctx->surface = sctx->d3d11_views; + d3d11_ctx->workaround = sctx->workaround; + d3d11_ctx->context_mutex = INVALID_HANDLE_VALUE; + } +#endif + +#if CONFIG_DXVA2 + if (sctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) { + AVDXVA2FramesContext *frames_hwctx = frames_ctx->hwctx; + struct dxva_context *dxva_ctx = &sctx->ctx.dxva2; + + ff_dxva2_lock(avctx); + ret = dxva2_create_decoder(avctx); + ff_dxva2_unlock(avctx); + if (ret < 0) + goto fail; + + dxva_ctx->decoder = sctx->dxva2_decoder; + dxva_ctx->cfg = &sctx->dxva2_config; + dxva_ctx->surface = frames_hwctx->surfaces; + dxva_ctx->surface_count = frames_hwctx->nb_surfaces; + dxva_ctx->workaround = sctx->workaround; + } +#endif + + return 0; + +fail: + ff_dxva2_decode_uninit(avctx); + return ret; +} + +int ff_dxva2_decode_uninit(AVCodecContext *avctx) +{ + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); + int i; + + av_buffer_unref(&sctx->decoder_ref); + +#if CONFIG_D3D11VA + for (i = 0; i < sctx->nb_d3d11_views; i++) { + if (sctx->d3d11_views[i]) + ID3D11VideoDecoderOutputView_Release(sctx->d3d11_views[i]); + } + av_freep(&sctx->d3d11_views); +#endif + +#if CONFIG_DXVA2 + if (sctx->dxva2_service) + IDirectXVideoDecoderService_Release(sctx->dxva2_service); +#endif + + return 0; +} + +static void *get_surface(AVCodecContext *avctx, const AVFrame *frame) +{ +#if CONFIG_D3D11VA + if (frame->format == AV_PIX_FMT_D3D11) { + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); + intptr_t index = (intptr_t)frame->data[1]; + if (index < 0 || index >= sctx->nb_d3d11_views || + sctx->d3d11_texture != (ID3D11Texture2D *)frame->data[0]) { + av_log(avctx, AV_LOG_ERROR, "get_buffer frame is invalid!\n"); + return NULL; + } + return sctx->d3d11_views[index]; + } +#endif return frame->data[3]; } @@ -60,10 +687,12 @@ unsigned ff_dxva2_get_surface_index(const AVCodecContext *avctx, const AVDXVAContext *ctx, const AVFrame *frame) { - void *surface = get_surface(frame); + void *surface = get_surface(avctx, frame); unsigned i; #if CONFIG_D3D11VA + if (avctx->pix_fmt == AV_PIX_FMT_D3D11) + return (intptr_t)frame->data[1]; if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc; ID3D11VideoDecoderOutputView_GetDesc((ID3D11VideoDecoderOutputView*) surface, &viewDesc); @@ -154,6 +783,22 @@ int ff_dxva2_commit_buffer(AVCodecContext *avctx, return result; } +static int frame_add_buf(AVFrame *frame, AVBufferRef *ref) +{ + int i; + + for (i = 0; i < AV_NUM_DATA_POINTERS; i++) { + if (!frame->buf[i]) { + frame->buf[i] = av_buffer_ref(ref); + return frame->buf[i] ? 0 : AVERROR(ENOMEM); + } + } + + // For now we expect that the caller does not use more than + // AV_NUM_DATA_POINTERS-1 buffers if the user uses a custom pool. + return AVERROR(EINVAL); +} + int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, const void *pp, unsigned pp_size, const void *qm, unsigned qm_size, @@ -173,19 +818,26 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, int result, runs = 0; HRESULT hr; unsigned type; + FFDXVASharedContext *sctx = DXVA_SHARED_CONTEXT(avctx); + + if (sctx->decoder_ref) { + result = frame_add_buf(frame, sctx->decoder_ref); + if (result < 0) + return result; + } do { ff_dxva2_lock(avctx); #if CONFIG_D3D11VA if (ff_dxva2_is_d3d11(avctx)) hr = ID3D11VideoContext_DecoderBeginFrame(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, - get_surface(frame), + get_surface(avctx, frame), 0, NULL); #endif #if CONFIG_DXVA2 if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) hr = IDirectXVideoDecoder_BeginFrame(DXVA2_CONTEXT(ctx)->decoder, - get_surface(frame), + get_surface(avctx, frame), NULL); #endif if (hr != E_PENDING || ++runs > 50) @@ -315,7 +967,8 @@ end: int ff_dxva2_is_d3d11(const AVCodecContext *avctx) { if (CONFIG_D3D11VA) - return avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD; + return avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD || + avctx->pix_fmt == AV_PIX_FMT_D3D11; else return 0; } diff --git a/libavcodec/dxva2_h264.c b/libavcodec/dxva2_h264.c index f58a45807f..de0885058a 100644 --- a/libavcodec/dxva2_h264.c +++ b/libavcodec/dxva2_h264.c @@ -518,10 +518,13 @@ AVHWAccel ff_h264_dxva2_hwaccel = { .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_H264, .pix_fmt = AV_PIX_FMT_DXVA2_VLD, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_h264_start_frame, .decode_slice = dxva2_h264_decode_slice, .end_frame = dxva2_h264_end_frame, .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), }; #endif @@ -531,9 +534,28 @@ AVHWAccel ff_h264_d3d11va_hwaccel = { .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_H264, .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_h264_start_frame, .decode_slice = dxva2_h264_decode_slice, .end_frame = dxva2_h264_end_frame, .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), +}; +#endif + +#if CONFIG_H264_D3D11VA2_HWACCEL +AVHWAccel ff_h264_d3d11va2_hwaccel = { + .name = "h264_d3d11va2", + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_H264, + .pix_fmt = AV_PIX_FMT_D3D11, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, + .start_frame = dxva2_h264_start_frame, + .decode_slice = dxva2_h264_decode_slice, + .end_frame = dxva2_h264_end_frame, + .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), }; #endif diff --git a/libavcodec/dxva2_hevc.c b/libavcodec/dxva2_hevc.c index f2bb8b26a5..4bff26d6a8 100644 --- a/libavcodec/dxva2_hevc.c +++ b/libavcodec/dxva2_hevc.c @@ -427,10 +427,13 @@ AVHWAccel ff_hevc_dxva2_hwaccel = { .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_HEVC, .pix_fmt = AV_PIX_FMT_DXVA2_VLD, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_hevc_start_frame, .decode_slice = dxva2_hevc_decode_slice, .end_frame = dxva2_hevc_end_frame, .frame_priv_data_size = sizeof(struct hevc_dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), }; #endif @@ -440,9 +443,28 @@ AVHWAccel ff_hevc_d3d11va_hwaccel = { .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_HEVC, .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_hevc_start_frame, .decode_slice = dxva2_hevc_decode_slice, .end_frame = dxva2_hevc_end_frame, .frame_priv_data_size = sizeof(struct hevc_dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), +}; +#endif + +#if CONFIG_HEVC_D3D11VA2_HWACCEL +AVHWAccel ff_hevc_d3d11va2_hwaccel = { + .name = "hevc_d3d11va2", + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_HEVC, + .pix_fmt = AV_PIX_FMT_D3D11, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, + .start_frame = dxva2_hevc_start_frame, + .decode_slice = dxva2_hevc_decode_slice, + .end_frame = dxva2_hevc_end_frame, + .frame_priv_data_size = sizeof(struct hevc_dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), }; #endif diff --git a/libavcodec/dxva2_internal.h b/libavcodec/dxva2_internal.h index af0d607487..901cc11144 100644 --- a/libavcodec/dxva2_internal.h +++ b/libavcodec/dxva2_internal.h @@ -32,9 +32,11 @@ #if CONFIG_DXVA2 #include "dxva2.h" +#include "libavutil/hwcontext_dxva2.h" #endif #if CONFIG_D3D11VA #include "d3d11va.h" +#include "libavutil/hwcontext_d3d11va.h" #endif #if HAVE_DXVA_H /* When targeting WINAPI_FAMILY_PHONE_APP or WINAPI_FAMILY_APP, dxva.h @@ -46,7 +48,10 @@ #include #endif +#include "libavutil/hwcontext.h" + #include "avcodec.h" +#include "internal.h" typedef void DECODER_BUFFER_DESC; @@ -59,7 +64,39 @@ typedef union { #endif } AVDXVAContext; -#define DXVA_CONTEXT(avctx) ((AVDXVAContext *)(avctx)->hwaccel_context) +typedef struct FFDXVASharedContext { + AVBufferRef *decoder_ref; + + // FF_DXVA2_WORKAROUND_* flags + uint64_t workaround; + + // E.g. AV_PIX_FMT_D3D11 (same as AVCodecContext.pix_fmt, except during init) + enum AVPixelFormat pix_fmt; + + AVHWDeviceContext *device_ctx; + +#if CONFIG_D3D11VA + ID3D11VideoDecoder *d3d11_decoder; + D3D11_VIDEO_DECODER_CONFIG d3d11_config; + ID3D11VideoDecoderOutputView **d3d11_views; + int nb_d3d11_views; + ID3D11Texture2D *d3d11_texture; +#endif + +#if CONFIG_DXVA2 + IDirectXVideoDecoder *dxva2_decoder; + IDirectXVideoDecoderService *dxva2_service; + DXVA2_ConfigPictureDecode dxva2_config; +#endif + + // Legacy (but used by code outside of setup) + // In generic mode, DXVA_CONTEXT() will return a pointer to this. + AVDXVAContext ctx; +} FFDXVASharedContext; + +#define DXVA_SHARED_CONTEXT(avctx) ((FFDXVASharedContext *)((avctx)->internal->hwaccel_priv_data)) + +#define DXVA_CONTEXT(avctx) (AVDXVAContext *)((avctx)->hwaccel_context ? (avctx)->hwaccel_context : (&(DXVA_SHARED_CONTEXT(avctx)->ctx))) #define D3D11VA_CONTEXT(ctx) (&ctx->d3d11va) #define DXVA2_CONTEXT(ctx) (&ctx->dxva2) @@ -115,6 +152,10 @@ int ff_dxva2_common_end_frame(AVCodecContext *, AVFrame *, DECODER_BUFFER_DESC *bs, DECODER_BUFFER_DESC *slice)); +int ff_dxva2_decode_init(AVCodecContext *avctx); + +int ff_dxva2_decode_uninit(AVCodecContext *avctx); + int ff_dxva2_is_d3d11(const AVCodecContext *avctx); #endif /* AVCODEC_DXVA2_INTERNAL_H */ diff --git a/libavcodec/dxva2_mpeg2.c b/libavcodec/dxva2_mpeg2.c index 6ae30a5773..ab80ca300a 100644 --- a/libavcodec/dxva2_mpeg2.c +++ b/libavcodec/dxva2_mpeg2.c @@ -323,10 +323,13 @@ AVHWAccel ff_mpeg2_dxva2_hwaccel = { .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_MPEG2VIDEO, .pix_fmt = AV_PIX_FMT_DXVA2_VLD, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_mpeg2_start_frame, .decode_slice = dxva2_mpeg2_decode_slice, .end_frame = dxva2_mpeg2_end_frame, .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), }; #endif @@ -336,9 +339,28 @@ AVHWAccel ff_mpeg2_d3d11va_hwaccel = { .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_MPEG2VIDEO, .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_mpeg2_start_frame, .decode_slice = dxva2_mpeg2_decode_slice, .end_frame = dxva2_mpeg2_end_frame, .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), +}; +#endif + +#if CONFIG_MPEG2_D3D11VA2_HWACCEL +AVHWAccel ff_mpeg2_d3d11va2_hwaccel = { + .name = "mpeg2_d3d11va2", + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_MPEG2VIDEO, + .pix_fmt = AV_PIX_FMT_D3D11, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, + .start_frame = dxva2_mpeg2_start_frame, + .decode_slice = dxva2_mpeg2_decode_slice, + .end_frame = dxva2_mpeg2_end_frame, + .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), }; #endif diff --git a/libavcodec/dxva2_vc1.c b/libavcodec/dxva2_vc1.c index b63580ed2e..22d3d299b6 100644 --- a/libavcodec/dxva2_vc1.c +++ b/libavcodec/dxva2_vc1.c @@ -323,10 +323,13 @@ AVHWAccel ff_wmv3_dxva2_hwaccel = { .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_WMV3, .pix_fmt = AV_PIX_FMT_DXVA2_VLD, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, .decode_slice = dxva2_vc1_decode_slice, .end_frame = dxva2_vc1_end_frame, .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), }; #endif @@ -336,10 +339,13 @@ AVHWAccel ff_vc1_dxva2_hwaccel = { .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_VC1, .pix_fmt = AV_PIX_FMT_DXVA2_VLD, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, .decode_slice = dxva2_vc1_decode_slice, .end_frame = dxva2_vc1_end_frame, .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), }; #endif @@ -349,10 +355,29 @@ AVHWAccel ff_wmv3_d3d11va_hwaccel = { .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_WMV3, .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, .decode_slice = dxva2_vc1_decode_slice, .end_frame = dxva2_vc1_end_frame, .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), +}; +#endif + +#if CONFIG_WMV3_D3D11VA2_HWACCEL +AVHWAccel ff_wmv3_d3d11va2_hwaccel = { + .name = "wmv3_d3d11va2", + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_WMV3, + .pix_fmt = AV_PIX_FMT_D3D11, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, + .start_frame = dxva2_vc1_start_frame, + .decode_slice = dxva2_vc1_decode_slice, + .end_frame = dxva2_vc1_end_frame, + .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), }; #endif @@ -362,9 +387,28 @@ AVHWAccel ff_vc1_d3d11va_hwaccel = { .type = AVMEDIA_TYPE_VIDEO, .id = AV_CODEC_ID_VC1, .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, .decode_slice = dxva2_vc1_decode_slice, .end_frame = dxva2_vc1_end_frame, .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), +}; +#endif + +#if CONFIG_VC1_D3D11VA2_HWACCEL +AVHWAccel ff_vc1_d3d11va2_hwaccel = { + .name = "vc1_d3d11va2", + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_VC1, + .pix_fmt = AV_PIX_FMT_D3D11, + .init = ff_dxva2_decode_init, + .uninit = ff_dxva2_decode_uninit, + .start_frame = dxva2_vc1_start_frame, + .decode_slice = dxva2_vc1_decode_slice, + .end_frame = dxva2_vc1_end_frame, + .frame_priv_data_size = sizeof(struct dxva2_picture_context), + .priv_data_size = sizeof(FFDXVASharedContext), }; #endif diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c index 95e366605c..c9f1dbb86f 100644 --- a/libavcodec/h264_slice.c +++ b/libavcodec/h264_slice.c @@ -717,7 +717,7 @@ static void init_scan_tables(H264Context *h) static enum AVPixelFormat get_pixel_format(H264Context *h) { #define HWACCEL_MAX (CONFIG_H264_DXVA2_HWACCEL + \ - CONFIG_H264_D3D11VA_HWACCEL + \ + (CONFIG_H264_D3D11VA_HWACCEL * 2) + \ CONFIG_H264_VAAPI_HWACCEL + \ (CONFIG_H264_VDA_HWACCEL * 2) + \ CONFIG_H264_VDPAU_HWACCEL) @@ -769,6 +769,7 @@ static enum AVPixelFormat get_pixel_format(H264Context *h) #endif #if CONFIG_H264_D3D11VA_HWACCEL *fmt++ = AV_PIX_FMT_D3D11VA_VLD; + *fmt++ = AV_PIX_FMT_D3D11; #endif #if CONFIG_H264_VAAPI_HWACCEL *fmt++ = AV_PIX_FMT_VAAPI; diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c index 69d5908551..7a9182af9b 100644 --- a/libavcodec/hevcdec.c +++ b/libavcodec/hevcdec.c @@ -383,7 +383,7 @@ static void export_stream_params(AVCodecContext *avctx, const HEVCParamSets *ps, static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) { - #define HWACCEL_MAX (CONFIG_HEVC_DXVA2_HWACCEL + CONFIG_HEVC_D3D11VA_HWACCEL + \ + #define HWACCEL_MAX (CONFIG_HEVC_DXVA2_HWACCEL + CONFIG_HEVC_D3D11VA_HWACCEL * 2 + \ CONFIG_HEVC_VAAPI_HWACCEL + CONFIG_HEVC_VDPAU_HWACCEL) enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmt = pix_fmts; @@ -391,6 +391,7 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) sps->pix_fmt == AV_PIX_FMT_YUV420P10) { #if CONFIG_HEVC_D3D11VA_HWACCEL *fmt++ = AV_PIX_FMT_D3D11VA_VLD; + *fmt++ = AV_PIX_FMT_D3D11; #endif #if CONFIG_HEVC_DXVA2_HWACCEL *fmt++ = AV_PIX_FMT_DXVA2_VLD; diff --git a/libavcodec/mpeg12dec.c b/libavcodec/mpeg12dec.c index 6702ad1ed7..9a9a92701a 100644 --- a/libavcodec/mpeg12dec.c +++ b/libavcodec/mpeg12dec.c @@ -1111,6 +1111,7 @@ static const enum AVPixelFormat mpeg12_hwaccel_pixfmt_list_420[] = { #endif #if CONFIG_MPEG2_D3D11VA_HWACCEL AV_PIX_FMT_D3D11VA_VLD, + AV_PIX_FMT_D3D11, #endif #if CONFIG_MPEG2_VAAPI_HWACCEL AV_PIX_FMT_VAAPI, diff --git a/libavcodec/vc1dec.c b/libavcodec/vc1dec.c index 51745c12ec..5e00a33e3d 100644 --- a/libavcodec/vc1dec.c +++ b/libavcodec/vc1dec.c @@ -962,6 +962,7 @@ static const enum AVPixelFormat vc1_hwaccel_pixfmt_list_420[] = { #endif #if CONFIG_VC1_D3D11VA_HWACCEL AV_PIX_FMT_D3D11VA_VLD, + AV_PIX_FMT_D3D11, #endif #if CONFIG_VC1_VAAPI_HWACCEL AV_PIX_FMT_VAAPI, diff --git a/libavcodec/version.h b/libavcodec/version.h index 88f17a1f78..bc5b8304bd 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,8 +28,8 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 58 -#define LIBAVCODEC_VERSION_MINOR 3 -#define LIBAVCODEC_VERSION_MICRO 1 +#define LIBAVCODEC_VERSION_MINOR 4 +#define LIBAVCODEC_VERSION_MICRO 0 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \ diff --git a/libavutil/hwcontext_dxva2.h b/libavutil/hwcontext_dxva2.h index 2290c26066..c8e7a5c978 100644 --- a/libavutil/hwcontext_dxva2.h +++ b/libavutil/hwcontext_dxva2.h @@ -65,6 +65,9 @@ typedef struct AVDXVA2FramesContext { * * If it is non-NULL, libavutil will call IDirectXVideoDecoder_Release() on * it just before the internal surface pool is freed. + * + * This is for convenience only. Some code uses other methods to manage the + * decoder reference. */ IDirectXVideoDecoder *decoder_to_release; } AVDXVA2FramesContext;