diff mbox

[FFmpeg-devel,2/5] libautil: add support for AV_HWDEVICE_TYPE_D3D11VA

Message ID 20161216101205.1360-2-robUx4@gmail.com
State New
Headers show

Commit Message

Steve Lhomme Dec. 16, 2016, 10:12 a.m. UTC
From: Steve Lhomme <robux4@gmail.com>

---
 libavutil/Makefile             |   3 +
 libavutil/hwcontext.c          |   3 +
 libavutil/hwcontext.h          |   1 +
 libavutil/hwcontext_d3d11va.c  | 457 +++++++++++++++++++++++++++++++++++++++++
 libavutil/hwcontext_d3d11va.h  |  81 ++++++++
 libavutil/hwcontext_internal.h |   1 +
 libavutil/version.h            |   2 +-
 7 files changed, 547 insertions(+), 1 deletion(-)
 create mode 100644 libavutil/hwcontext_d3d11va.c
 create mode 100644 libavutil/hwcontext_d3d11va.h

Comments

wm4 Dec. 16, 2016, 4:32 p.m. UTC | #1
On Fri, 16 Dec 2016 11:12:02 +0100
Steve Lhomme <robux4@gmail.com> wrote:

> From: Steve Lhomme <robux4@gmail.com>
> 
> ---
>  libavutil/Makefile             |   3 +
>  libavutil/hwcontext.c          |   3 +
>  libavutil/hwcontext.h          |   1 +
>  libavutil/hwcontext_d3d11va.c  | 457 +++++++++++++++++++++++++++++++++++++++++
>  libavutil/hwcontext_d3d11va.h  |  81 ++++++++
>  libavutil/hwcontext_internal.h |   1 +
>  libavutil/version.h            |   2 +-
>  7 files changed, 547 insertions(+), 1 deletion(-)
>  create mode 100644 libavutil/hwcontext_d3d11va.c
>  create mode 100644 libavutil/hwcontext_d3d11va.h
> 
> diff --git a/libavutil/Makefile b/libavutil/Makefile
> index 9841645..9b8ce22 100644
> --- a/libavutil/Makefile
> +++ b/libavutil/Makefile
> @@ -34,6 +34,7 @@ HEADERS = adler32.h                                                     \
>            hmac.h                                                        \
>            hwcontext.h                                                   \
>            hwcontext_cuda.h                                              \
> +          hwcontext_d3d11va.h                                           \
>            hwcontext_dxva2.h                                             \
>            hwcontext_qsv.h                                               \
>            hwcontext_vaapi.h                                             \
> @@ -156,6 +157,7 @@ OBJS = adler32.o                                                        \
>  OBJS-$(!HAVE_ATOMICS_NATIVE)            += atomic.o                     \
>  
>  OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
> +OBJS-$(CONFIG_D3D11VA)                  += hwcontext_d3d11va.o
>  OBJS-$(CONFIG_DXVA2)                    += hwcontext_dxva2.o
>  OBJS-$(CONFIG_QSV)                   += hwcontext_qsv.o
>  OBJS-$(CONFIG_LZO)                      += lzo.o
> @@ -170,6 +172,7 @@ SLIBOBJS-$(HAVE_GNU_WINDRES)            += avutilres.o
>  
>  SKIPHEADERS-$(HAVE_CUDA_H)             += hwcontext_cuda.h
>  SKIPHEADERS-$(CONFIG_CUDA)             += hwcontext_cuda_internal.h
> +SKIPHEADERS-$(CONFIG_D3D11VA)          += hwcontext_d3d11va.h
>  SKIPHEADERS-$(CONFIG_DXVA2)            += hwcontext_dxva2.h
>  SKIPHEADERS-$(CONFIG_QSV)           += hwcontext_qsv.h
>  SKIPHEADERS-$(CONFIG_VAAPI)            += hwcontext_vaapi.h
> diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
> index 615f1f7..a9db84b 100644
> --- a/libavutil/hwcontext.c
> +++ b/libavutil/hwcontext.c
> @@ -32,6 +32,9 @@ static const HWContextType *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
> diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
> index 785da09..1325917 100644
> --- a/libavutil/hwcontext.h
> +++ b/libavutil/hwcontext.h
> @@ -30,6 +30,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 0000000..0618da3
> --- /dev/null
> +++ b/libavutil/hwcontext_d3d11va.c
> @@ -0,0 +1,457 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include <windows.h>
> +
> +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600
> +#undef _WIN32_WINNT
> +#define _WIN32_WINNT 0x0600
> +#endif
> +#define COBJMACROS
> +
> +#include <initguid.h>
> +#include <d3d11.h>
> +#include <dxgi1_2.h>
> +
> +#include "avassert.h"
> +#include "common.h"
> +#include "hwcontext.h"
> +#include "hwcontext_d3d11va.h"
> +#include "hwcontext_internal.h"
> +#include "imgutils.h"
> +#include "pixdesc.h"
> +#include "pixfmt.h"
> +#include "compat/w32dlfcn.h"
> +
> +typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
> +
> +typedef struct D3D11VAFramesContext {
> +    ID3D11VideoDecoderOutputView **surfaces_internal;
> +    int              nb_surfaces_used;
> +
> +    ID3D11DeviceContext *d3d11_context;
> +
> +    DXGI_FORMAT format;
> +} D3D11VAFramesContext;
> +
> +typedef struct D3D11VADevicePriv {
> +    HMODULE d3dlib;
> +} D3D11VADevicePriv;
> +
> +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_frames_uninit(AVHWFramesContext *ctx)
> +{
> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
> +    D3D11VAFramesContext *s = ctx->internal->priv;
> +
> +    if (frames_hwctx->decoder_to_release)
> +        ID3D11VideoDecoder_Release(frames_hwctx->decoder_to_release);
> +
> +    av_freep(&s->surfaces_internal);
> +
> +    if (frames_hwctx->staging_texture)
> +        ID3D11Texture2D_Release(frames_hwctx->staging_texture);
> +
> +    if (s->d3d11_context) {
> +        ID3D11DeviceContext_Release(s->d3d11_context);
> +        s->d3d11_context = NULL;
> +    }
> +
> +    if (frames_hwctx->dev_ctx_mutex != INVALID_HANDLE_VALUE)
> +        ReleaseMutex(frames_hwctx->dev_ctx_mutex);
> +}
> +
> +static void free_surface(void *opaque, uint8_t *data)
> +{
> +    ID3D11VideoDecoderOutputView_Release((ID3D11VideoDecoderOutputView*)data);
> +}
> +
> +static AVBufferRef *d3d11va_pool_alloc(void *opaque, int size)
> +{
> +    AVHWFramesContext        *ctx = (AVHWFramesContext*)opaque;
> +    D3D11VAFramesContext       *s = ctx->internal->priv;
> +    AVD3D11VAFramesContext *hwctx = ctx->hwctx;
> +
> +    if (s->nb_surfaces_used < hwctx->nb_surfaces) {
> +        s->nb_surfaces_used++;
> +        return av_buffer_create((uint8_t*)s->surfaces_internal[s->nb_surfaces_used - 1],
> +                                sizeof(*hwctx->surfaces), free_surface, 0, 0);
> +    }
> +
> +    return NULL;
> +}
> +
> +static int d3d11va_init_pool(AVHWFramesContext *ctx)
> +{
> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
> +    AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
> +    D3D11VAFramesContext              *s = ctx->internal->priv;
> +
> +    int i;
> +    HRESULT hr;
> +    D3D11_TEXTURE2D_DESC texDesc;
> +    ID3D11Texture2D *p_texture;
> +    D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
> +    D3D11_VIDEO_DECODER_DESC decoderDesc;
> +    D3D11_VIDEO_DECODER_CONFIG decoderConfig;
> +    ID3D11Device *d3d11_device;
> +
> +    if (ctx->initial_pool_size <= 0)
> +        return 0;
> +
> +    hr = ID3D11VideoContext_QueryInterface(device_hwctx->video_context, &IID_ID3D11DeviceContext,
> +                                           (void **)&s->d3d11_context);
> +    if (FAILED(hr)) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the device context %lx\n", hr);
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    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);
> +    }
> +
> +    s->surfaces_internal = av_mallocz_array(ctx->initial_pool_size,
> +                                            sizeof(*s->surfaces_internal));
> +    if (!s->surfaces_internal)
> +        return AVERROR(ENOMEM);
> +
> +    ZeroMemory(&texDesc, sizeof(texDesc));
> +    texDesc.Width = ctx->width;
> +    texDesc.Height = ctx->height;
> +    texDesc.MipLevels = 1;
> +    texDesc.Format = s->format;
> +    texDesc.SampleDesc.Count = 1;
> +    texDesc.ArraySize = ctx->initial_pool_size;
> +    texDesc.Usage = D3D11_USAGE_DEFAULT;
> +    texDesc.BindFlags = D3D11_BIND_DECODER;
> +
> +    frames_hwctx->dev_ctx_mutex = CreateMutex(NULL, 0, NULL);
> +    if (frames_hwctx->dev_ctx_mutex == INVALID_HANDLE_VALUE) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to get a mutex for the D3D11VA decoder\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    hr = ID3D11VideoDevice_QueryInterface(device_hwctx->video_device, &IID_ID3D11Device, (void **)&d3d11_device);
> +    if (FAILED(hr)) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the device %lx\n", hr);
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    hr = ID3D11Device_CreateTexture2D(d3d11_device, &texDesc, NULL, &p_texture);
> +    if (FAILED(hr)) {
> +        av_log(ctx, AV_LOG_ERROR, "Could not create the texture %lx\n", hr);
> +        ID3D11Device_Release(d3d11_device);
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    hr = ID3D11VideoDecoder_GetCreationParameters(frames_hwctx->decoder_to_release, &decoderDesc, &decoderConfig);
> +    if (FAILED(hr)) {
> +        av_log(ctx, AV_LOG_ERROR, "Could not get the decoder config %lx\n", hr);
> +        ID3D11Texture2D_Release(p_texture);
> +        ID3D11Device_Release(d3d11_device);
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    ZeroMemory(&viewDesc, sizeof(viewDesc));
> +    viewDesc.DecodeProfile = decoderDesc.Guid;
> +    viewDesc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
> +    for (i=0; i<ctx->initial_pool_size; i++)
> +    {
> +        hr = ID3D11VideoDevice_CreateVideoDecoderOutputView(device_hwctx->video_device,
> +                                                            (ID3D11Resource*) p_texture,
> +                                                            &viewDesc,
> +                                                            (ID3D11VideoDecoderOutputView**) &s->surfaces_internal[i]);
> +        if (FAILED(hr)) {
> +            av_log(ctx, AV_LOG_ERROR, "Could not create the decoder output %d\n", i);
> +            while (--i >= 0) {
> +                ID3D11VideoDecoderOutputView_Release(s->surfaces_internal[i]);
> +                s->surfaces_internal[i] = NULL;
> +            }
> +            ID3D11Texture2D_Release(p_texture);
> +            ID3D11Device_Release(d3d11_device);
> +            return AVERROR_UNKNOWN;
> +        }
> +    }
> +    ID3D11Texture2D_Release(p_texture);
> +
> +    texDesc.ArraySize = 1;
> +    texDesc.Usage = D3D11_USAGE_STAGING;
> +    texDesc.BindFlags = 0;
> +    texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
> +    hr = ID3D11Device_CreateTexture2D(d3d11_device, &texDesc, NULL, &frames_hwctx->staging_texture);
> +    ID3D11Device_Release(d3d11_device);
> +    if (FAILED(hr)) {
> +        av_log(ctx, AV_LOG_ERROR, "Could not create the staging texture %lx\n", hr);
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    ctx->internal->pool_internal = av_buffer_pool_init2(sizeof(*s->surfaces_internal),
> +                                                        ctx, d3d11va_pool_alloc, NULL);
> +    if (!ctx->internal->pool_internal)
> +        return AVERROR(ENOMEM);
> +
> +    frames_hwctx->surfaces    = s->surfaces_internal;
> +    frames_hwctx->nb_surfaces = ctx->initial_pool_size;
> +
> +    return 0;
> +}
> +
> +static int d3d11va_frames_init(AVHWFramesContext *ctx)
> +{
> +    int ret;
> +
> +    /* init the frame pool if the caller didn't provide one */
> +    if (!ctx->pool) {
> +        ret = d3d11va_init_pool(ctx);
> +        if (ret < 0) {
> +            av_log(ctx, AV_LOG_ERROR, "Error creating an internal frame pool\n");
> +            return ret;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
> +{
> +    frame->buf[0] = av_buffer_pool_get(ctx->pool);
> +    if (!frame->buf[0])
> +        return AVERROR(ENOMEM);
> +
> +    frame->data[3] = frame->buf[0]->data;
> +    frame->format  = AV_PIX_FMT_D3D11VA_VLD;
> +    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 int d3d11va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
> +                                 const AVFrame *src)
> +{
> +    ID3D11VideoDecoderOutputView *surface;
> +    D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC    surfaceDesc;
> +    D3D11_TEXTURE2D_DESC dstDesc;
> +    D3D11_MAPPED_SUBRESOURCE     LockedRect;
> +    ID3D11Resource *pTexture;
> +    HRESULT            hr;
> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
> +    D3D11VAFramesContext              *s = ctx->internal->priv;
> +
> +    uint8_t *surf_data[4]     = { NULL };
> +    int      surf_linesize[4] = { 0 };
> +    int i;
> +
> +    int download = !!src->hw_frames_ctx;
> +
> +    surface = (ID3D11VideoDecoderOutputView*)(download ? src->data[3] : dst->data[3]);
> +
> +    ID3D11VideoDecoderOutputView_GetDesc(surface, &surfaceDesc);
> +    ID3D11VideoDecoderOutputView_GetResource(surface, &pTexture);
> +    ID3D11Texture2D_GetDesc(frames_hwctx->staging_texture, &dstDesc);
> +
> +    WaitForSingleObjectEx(frames_hwctx->dev_ctx_mutex, INFINITE, FALSE);
> +
> +    ID3D11DeviceContext_CopySubresourceRegion(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture,
> +                                              0, 0, 0, 0,
> +                                              (ID3D11Resource*)pTexture, surfaceDesc.Texture2D.ArraySlice,
> +                                              NULL);
> +    ID3D11Resource_Release(pTexture);
> +
> +    hr = ID3D11DeviceContext_Map(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture,
> +                                 0, download ? D3D11_MAP_READ : D3D11_MAP_WRITE, 0, &LockedRect);
> +    if (FAILED(hr)) {
> +        av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface %lx\n", hr);
> +        ReleaseMutex(frames_hwctx->dev_ctx_mutex);
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    for (i = 0; download ? dst->data[i] : src->data[i]; i++)
> +        surf_linesize[i] = LockedRect.RowPitch;
> +
> +    av_image_fill_pointers(surf_data, ctx->sw_format, dstDesc.Height,
> +                           (uint8_t*)LockedRect.pData, surf_linesize);
> +
> +    if (download) {
> +        av_image_copy(dst->data, dst->linesize, surf_data, surf_linesize,
> +                      ctx->sw_format, src->width, src->height);
> +    } else {
> +        av_image_copy(surf_data, surf_linesize, src->data, src->linesize,
> +                      ctx->sw_format, src->width, src->height);
> +    }
> +
> +    ID3D11DeviceContext_Unmap(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture, 0);
> +    ReleaseMutex(frames_hwctx->dev_ctx_mutex);
> +
> +    return 0;
> +}
> +
> +static void d3d11va_device_free(AVHWDeviceContext *ctx)
> +{
> +    AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
> +    D3D11VADevicePriv       *priv = ctx->user_opaque;
> +
> +    if (device_hwctx->video_device)
> +        ID3D11Device_Release(device_hwctx->video_device);
> +
> +    if (device_hwctx->video_context)
> +        ID3D11VideoContext_Release(device_hwctx->video_context);
> +
> +    if (priv->d3dlib)
> +        dlclose(priv->d3dlib);
> +
> +    av_freep(&ctx->user_opaque);
> +}
> +
> +static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
> +                                 AVDictionary *opts, int flags)
> +{
> +    AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
> +    ID3D11Device           *d3d11_device;
> +    ID3D11DeviceContext    *d3d11_context;
> +    ID3D10Multithread      *pMultithread;
> +    D3D11VADevicePriv      *priv;
> +
> +    HRESULT hr;
> +    PFN_D3D11_CREATE_DEVICE createD3D;
> +    IDXGIAdapter *pAdapter = NULL;
> +    UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
> +    /* if the DirectX SDK is installed creationFlags |= D3D11_CREATE_DEVICE_DEBUG; */
> +
> +    if (device) {
> +        HMODULE dxgilib = dlopen("dxgi.dll", 0);
> +        if (dxgilib) {
> +            PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
> +            mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) dlsym(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);
> +                }
> +            }
> +            dlclose(dxgilib);
> +        }
> +    }
> +
> +    priv = av_mallocz(sizeof(*priv));
> +    if (!priv)
> +        return AVERROR(ENOMEM);
> +
> +    ctx->user_opaque = priv;
> +    ctx->free        = d3d11va_device_free;
> +
> +    priv->d3dlib = dlopen("d3d11.dll", 0);
> +    if (!priv->d3dlib) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library\n");
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    createD3D = (PFN_D3D11_CREATE_DEVICE) dlsym(priv->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, &d3d11_device, NULL, &d3d11_context);
> +    if (FAILED(hr)) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to create Direct3D device %lx\n", hr);
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    hr = ID3D11Device_QueryInterface(d3d11_device, &IID_ID3D10Multithread, (void **)&pMultithread);
> +    if (SUCCEEDED(hr)) {
> +        ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
> +        ID3D10Multithread_Release(pMultithread);
> +    }
> +
> +    hr = ID3D11DeviceContext_QueryInterface(d3d11_context, &IID_ID3D11VideoContext,
> +                                            (void **)&device_hwctx->video_context);
> +    ID3D11DeviceContext_Release(d3d11_context);
> +    if (FAILED(hr)) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the Video Context %lx\n", hr);
> +        ID3D11Device_Release(d3d11_device);
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    hr = ID3D11Device_QueryInterface(d3d11_device, &IID_ID3D11VideoDevice,
> +                                     (void **)&device_hwctx->video_device);
> +    ID3D11Device_Release(d3d11_device);
> +    if (FAILED(hr)) {
> +        av_log(NULL, AV_LOG_ERROR, "Failed to get the Video Device %lx\n", hr);
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    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,
> +    .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_D3D11VA_VLD, AV_PIX_FMT_NONE },
> +};
> diff --git a/libavutil/hwcontext_d3d11va.h b/libavutil/hwcontext_d3d11va.h
> new file mode 100644
> index 0000000..e8d03f4
> --- /dev/null
> +++ b/libavutil/hwcontext_d3d11va.h
> @@ -0,0 +1,81 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +
> +#ifndef AVUTIL_HWCONTEXT_D3D11VA_H
> +#define AVUTIL_HWCONTEXT_D3D11VA_H
> +
> +/**
> + * @file
> + * An API-specific header for AV_HWDEVICE_TYPE_D3D11VA.
> + *
> + * Only fixed-size pools are supported.
> + *
> + * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
> + * with the data pointer set to a pointer to ID3D11VideoDecoderOutputView.
> + */
> +
> +#include <d3d11.h>
> +
> +/**
> + * This struct is allocated as AVHWDeviceContext.hwctx
> + */
> +typedef struct AVD3D11VADeviceContext {
> +    ID3D11VideoDevice   *video_device;
> +    ID3D11VideoContext  *video_context;
> +} AVD3D11VADeviceContext;
> +
> +/**
> + * This struct is allocated as AVHWFramesContext.hwctx
> + */
> +typedef struct AVD3D11VAFramesContext {
> +    /**
> +     * The surface pool. When an external pool is not provided by the caller,
> +     * this will be managed (allocated and filled on init, freed on uninit) by
> +     * libavutil.
> +     * When it is provided the allocation/deallocation is up to the caller.
> +     */
> +    ID3D11VideoDecoderOutputView **surfaces;
> +    int                         nb_surfaces;
> +
> +    /**
> +     * Certain drivers require the decoder to be destroyed before the surfaces.
> +     * To allow internally managed pools to work properly in such cases, this
> +     * field is provided.
> +     *
> +     * The decoder must be created before the surface pool.
> +     *
> +     * If it is non-NULL, libavutil will call ID3D11VideoDecoder_Release() on
> +     * it just before the internal surface pool is freed.
> +     */
> +    ID3D11VideoDecoder  *decoder_to_release;
> +    
> +    /**
> +     * Internal texture to get access to the decoded pixels from the CPU as the
> +     * decoder texture needs D3D11_BIND_DECODER which forbdis CPU access.

Typo: forbids

> +     */
> +    ID3D11Texture2D     *staging_texture;
> +
> +    /**
> +     * Internal Mutex to avoid accessing the ID3D11DeviceContext from multiple
> +     * threads simultaneously.
> +     */
> +    HANDLE               dev_ctx_mutex;
> +} AVD3D11VAFramesContext;

If this is internal, then why is it exposed here? Same could probably
be said about the staging texture.

Also, I believe using AVMutex from libavutil/thread.h would be better,
because it can use fast userspace mutexes on Vista+. But probably
doesn't matter in this case.

> +
> +#endif /* AVUTIL_HWCONTEXT_D3D11VA_H */
> diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h
> index 079e42b..aaa6286 100644
> --- a/libavutil/hwcontext_internal.h
> +++ b/libavutil/hwcontext_internal.h
> @@ -100,6 +100,7 @@ struct AVHWFramesInternal {
>  };
>  
>  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/version.h b/libavutil/version.h
> index 9f8c4c2..6dac240 100644
> --- a/libavutil/version.h
> +++ b/libavutil/version.h
> @@ -80,7 +80,7 @@
>  
>  #define LIBAVUTIL_VERSION_MAJOR  55
>  #define LIBAVUTIL_VERSION_MINOR  43
> -#define LIBAVUTIL_VERSION_MICRO 100
> +#define LIBAVUTIL_VERSION_MICRO 101
>  
>  #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
>                                                 LIBAVUTIL_VERSION_MINOR, \
Hendrik Leppkes Dec. 16, 2016, 6:04 p.m. UTC | #2
On Fri, Dec 16, 2016 at 5:32 PM, wm4 <nfxjfg@googlemail.com> wrote:
> On Fri, 16 Dec 2016 11:12:02 +0100
> Steve Lhomme <robux4@gmail.com> wrote:
>
>> From: Steve Lhomme <robux4@gmail.com>
>>
>> ---
>>  libavutil/Makefile             |   3 +
>>  libavutil/hwcontext.c          |   3 +
>>  libavutil/hwcontext.h          |   1 +
>>  libavutil/hwcontext_d3d11va.c  | 457 +++++++++++++++++++++++++++++++++++++++++
>>  libavutil/hwcontext_d3d11va.h  |  81 ++++++++
>>  libavutil/hwcontext_internal.h |   1 +
>>  libavutil/version.h            |   2 +-
>>  7 files changed, 547 insertions(+), 1 deletion(-)
>>  create mode 100644 libavutil/hwcontext_d3d11va.c
>>  create mode 100644 libavutil/hwcontext_d3d11va.h
>>
>> diff --git a/libavutil/Makefile b/libavutil/Makefile
>> index 9841645..9b8ce22 100644
>> --- a/libavutil/Makefile
>> +++ b/libavutil/Makefile
>> @@ -34,6 +34,7 @@ HEADERS = adler32.h                                                     \
>>            hmac.h                                                        \
>>            hwcontext.h                                                   \
>>            hwcontext_cuda.h                                              \
>> +          hwcontext_d3d11va.h                                           \
>>            hwcontext_dxva2.h                                             \
>>            hwcontext_qsv.h                                               \
>>            hwcontext_vaapi.h                                             \
>> @@ -156,6 +157,7 @@ OBJS = adler32.o                                                        \
>>  OBJS-$(!HAVE_ATOMICS_NATIVE)            += atomic.o                     \
>>
>>  OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
>> +OBJS-$(CONFIG_D3D11VA)                  += hwcontext_d3d11va.o
>>  OBJS-$(CONFIG_DXVA2)                    += hwcontext_dxva2.o
>>  OBJS-$(CONFIG_QSV)                   += hwcontext_qsv.o
>>  OBJS-$(CONFIG_LZO)                      += lzo.o
>> @@ -170,6 +172,7 @@ SLIBOBJS-$(HAVE_GNU_WINDRES)            += avutilres.o
>>
>>  SKIPHEADERS-$(HAVE_CUDA_H)             += hwcontext_cuda.h
>>  SKIPHEADERS-$(CONFIG_CUDA)             += hwcontext_cuda_internal.h
>> +SKIPHEADERS-$(CONFIG_D3D11VA)          += hwcontext_d3d11va.h
>>  SKIPHEADERS-$(CONFIG_DXVA2)            += hwcontext_dxva2.h
>>  SKIPHEADERS-$(CONFIG_QSV)           += hwcontext_qsv.h
>>  SKIPHEADERS-$(CONFIG_VAAPI)            += hwcontext_vaapi.h
>> diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
>> index 615f1f7..a9db84b 100644
>> --- a/libavutil/hwcontext.c
>> +++ b/libavutil/hwcontext.c
>> @@ -32,6 +32,9 @@ static const HWContextType *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
>> diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
>> index 785da09..1325917 100644
>> --- a/libavutil/hwcontext.h
>> +++ b/libavutil/hwcontext.h
>> @@ -30,6 +30,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 0000000..0618da3
>> --- /dev/null
>> +++ b/libavutil/hwcontext_d3d11va.c
>> @@ -0,0 +1,457 @@
>> +/*
>> + * This file is part of FFmpeg.
>> + *
>> + * FFmpeg is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * FFmpeg is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with FFmpeg; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>> + */
>> +
>> +#include <windows.h>
>> +
>> +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600
>> +#undef _WIN32_WINNT
>> +#define _WIN32_WINNT 0x0600
>> +#endif
>> +#define COBJMACROS
>> +
>> +#include <initguid.h>
>> +#include <d3d11.h>
>> +#include <dxgi1_2.h>
>> +
>> +#include "avassert.h"
>> +#include "common.h"
>> +#include "hwcontext.h"
>> +#include "hwcontext_d3d11va.h"
>> +#include "hwcontext_internal.h"
>> +#include "imgutils.h"
>> +#include "pixdesc.h"
>> +#include "pixfmt.h"
>> +#include "compat/w32dlfcn.h"
>> +
>> +typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
>> +
>> +typedef struct D3D11VAFramesContext {
>> +    ID3D11VideoDecoderOutputView **surfaces_internal;
>> +    int              nb_surfaces_used;
>> +
>> +    ID3D11DeviceContext *d3d11_context;
>> +
>> +    DXGI_FORMAT format;
>> +} D3D11VAFramesContext;
>> +
>> +typedef struct D3D11VADevicePriv {
>> +    HMODULE d3dlib;
>> +} D3D11VADevicePriv;
>> +
>> +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_frames_uninit(AVHWFramesContext *ctx)
>> +{
>> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
>> +    D3D11VAFramesContext *s = ctx->internal->priv;
>> +
>> +    if (frames_hwctx->decoder_to_release)
>> +        ID3D11VideoDecoder_Release(frames_hwctx->decoder_to_release);
>> +
>> +    av_freep(&s->surfaces_internal);
>> +
>> +    if (frames_hwctx->staging_texture)
>> +        ID3D11Texture2D_Release(frames_hwctx->staging_texture);
>> +
>> +    if (s->d3d11_context) {
>> +        ID3D11DeviceContext_Release(s->d3d11_context);
>> +        s->d3d11_context = NULL;
>> +    }
>> +
>> +    if (frames_hwctx->dev_ctx_mutex != INVALID_HANDLE_VALUE)
>> +        ReleaseMutex(frames_hwctx->dev_ctx_mutex);
>> +}
>> +
>> +static void free_surface(void *opaque, uint8_t *data)
>> +{
>> +    ID3D11VideoDecoderOutputView_Release((ID3D11VideoDecoderOutputView*)data);
>> +}
>> +
>> +static AVBufferRef *d3d11va_pool_alloc(void *opaque, int size)
>> +{
>> +    AVHWFramesContext        *ctx = (AVHWFramesContext*)opaque;
>> +    D3D11VAFramesContext       *s = ctx->internal->priv;
>> +    AVD3D11VAFramesContext *hwctx = ctx->hwctx;
>> +
>> +    if (s->nb_surfaces_used < hwctx->nb_surfaces) {
>> +        s->nb_surfaces_used++;
>> +        return av_buffer_create((uint8_t*)s->surfaces_internal[s->nb_surfaces_used - 1],
>> +                                sizeof(*hwctx->surfaces), free_surface, 0, 0);
>> +    }
>> +
>> +    return NULL;
>> +}
>> +
>> +static int d3d11va_init_pool(AVHWFramesContext *ctx)
>> +{
>> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
>> +    AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
>> +    D3D11VAFramesContext              *s = ctx->internal->priv;
>> +
>> +    int i;
>> +    HRESULT hr;
>> +    D3D11_TEXTURE2D_DESC texDesc;
>> +    ID3D11Texture2D *p_texture;
>> +    D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
>> +    D3D11_VIDEO_DECODER_DESC decoderDesc;
>> +    D3D11_VIDEO_DECODER_CONFIG decoderConfig;
>> +    ID3D11Device *d3d11_device;
>> +
>> +    if (ctx->initial_pool_size <= 0)
>> +        return 0;
>> +
>> +    hr = ID3D11VideoContext_QueryInterface(device_hwctx->video_context, &IID_ID3D11DeviceContext,
>> +                                           (void **)&s->d3d11_context);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the device context %lx\n", hr);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    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);
>> +    }
>> +
>> +    s->surfaces_internal = av_mallocz_array(ctx->initial_pool_size,
>> +                                            sizeof(*s->surfaces_internal));
>> +    if (!s->surfaces_internal)
>> +        return AVERROR(ENOMEM);
>> +
>> +    ZeroMemory(&texDesc, sizeof(texDesc));
>> +    texDesc.Width = ctx->width;
>> +    texDesc.Height = ctx->height;
>> +    texDesc.MipLevels = 1;
>> +    texDesc.Format = s->format;
>> +    texDesc.SampleDesc.Count = 1;
>> +    texDesc.ArraySize = ctx->initial_pool_size;
>> +    texDesc.Usage = D3D11_USAGE_DEFAULT;
>> +    texDesc.BindFlags = D3D11_BIND_DECODER;
>> +
>> +    frames_hwctx->dev_ctx_mutex = CreateMutex(NULL, 0, NULL);
>> +    if (frames_hwctx->dev_ctx_mutex == INVALID_HANDLE_VALUE) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to get a mutex for the D3D11VA decoder\n");
>> +        return AVERROR(EINVAL);
>> +    }
>> +
>> +    hr = ID3D11VideoDevice_QueryInterface(device_hwctx->video_device, &IID_ID3D11Device, (void **)&d3d11_device);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the device %lx\n", hr);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    hr = ID3D11Device_CreateTexture2D(d3d11_device, &texDesc, NULL, &p_texture);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Could not create the texture %lx\n", hr);
>> +        ID3D11Device_Release(d3d11_device);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    hr = ID3D11VideoDecoder_GetCreationParameters(frames_hwctx->decoder_to_release, &decoderDesc, &decoderConfig);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Could not get the decoder config %lx\n", hr);
>> +        ID3D11Texture2D_Release(p_texture);
>> +        ID3D11Device_Release(d3d11_device);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    ZeroMemory(&viewDesc, sizeof(viewDesc));
>> +    viewDesc.DecodeProfile = decoderDesc.Guid;
>> +    viewDesc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
>> +    for (i=0; i<ctx->initial_pool_size; i++)
>> +    {
>> +        hr = ID3D11VideoDevice_CreateVideoDecoderOutputView(device_hwctx->video_device,
>> +                                                            (ID3D11Resource*) p_texture,
>> +                                                            &viewDesc,
>> +                                                            (ID3D11VideoDecoderOutputView**) &s->surfaces_internal[i]);
>> +        if (FAILED(hr)) {
>> +            av_log(ctx, AV_LOG_ERROR, "Could not create the decoder output %d\n", i);
>> +            while (--i >= 0) {
>> +                ID3D11VideoDecoderOutputView_Release(s->surfaces_internal[i]);
>> +                s->surfaces_internal[i] = NULL;
>> +            }
>> +            ID3D11Texture2D_Release(p_texture);
>> +            ID3D11Device_Release(d3d11_device);
>> +            return AVERROR_UNKNOWN;
>> +        }
>> +    }
>> +    ID3D11Texture2D_Release(p_texture);
>> +
>> +    texDesc.ArraySize = 1;
>> +    texDesc.Usage = D3D11_USAGE_STAGING;
>> +    texDesc.BindFlags = 0;
>> +    texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
>> +    hr = ID3D11Device_CreateTexture2D(d3d11_device, &texDesc, NULL, &frames_hwctx->staging_texture);
>> +    ID3D11Device_Release(d3d11_device);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Could not create the staging texture %lx\n", hr);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    ctx->internal->pool_internal = av_buffer_pool_init2(sizeof(*s->surfaces_internal),
>> +                                                        ctx, d3d11va_pool_alloc, NULL);
>> +    if (!ctx->internal->pool_internal)
>> +        return AVERROR(ENOMEM);
>> +
>> +    frames_hwctx->surfaces    = s->surfaces_internal;
>> +    frames_hwctx->nb_surfaces = ctx->initial_pool_size;
>> +
>> +    return 0;
>> +}
>> +
>> +static int d3d11va_frames_init(AVHWFramesContext *ctx)
>> +{
>> +    int ret;
>> +
>> +    /* init the frame pool if the caller didn't provide one */
>> +    if (!ctx->pool) {
>> +        ret = d3d11va_init_pool(ctx);
>> +        if (ret < 0) {
>> +            av_log(ctx, AV_LOG_ERROR, "Error creating an internal frame pool\n");
>> +            return ret;
>> +        }
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
>> +{
>> +    frame->buf[0] = av_buffer_pool_get(ctx->pool);
>> +    if (!frame->buf[0])
>> +        return AVERROR(ENOMEM);
>> +
>> +    frame->data[3] = frame->buf[0]->data;
>> +    frame->format  = AV_PIX_FMT_D3D11VA_VLD;
>> +    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 int d3d11va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
>> +                                 const AVFrame *src)
>> +{
>> +    ID3D11VideoDecoderOutputView *surface;
>> +    D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC    surfaceDesc;
>> +    D3D11_TEXTURE2D_DESC dstDesc;
>> +    D3D11_MAPPED_SUBRESOURCE     LockedRect;
>> +    ID3D11Resource *pTexture;
>> +    HRESULT            hr;
>> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
>> +    D3D11VAFramesContext              *s = ctx->internal->priv;
>> +
>> +    uint8_t *surf_data[4]     = { NULL };
>> +    int      surf_linesize[4] = { 0 };
>> +    int i;
>> +
>> +    int download = !!src->hw_frames_ctx;
>> +
>> +    surface = (ID3D11VideoDecoderOutputView*)(download ? src->data[3] : dst->data[3]);
>> +
>> +    ID3D11VideoDecoderOutputView_GetDesc(surface, &surfaceDesc);
>> +    ID3D11VideoDecoderOutputView_GetResource(surface, &pTexture);
>> +    ID3D11Texture2D_GetDesc(frames_hwctx->staging_texture, &dstDesc);
>> +
>> +    WaitForSingleObjectEx(frames_hwctx->dev_ctx_mutex, INFINITE, FALSE);
>> +
>> +    ID3D11DeviceContext_CopySubresourceRegion(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture,
>> +                                              0, 0, 0, 0,
>> +                                              (ID3D11Resource*)pTexture, surfaceDesc.Texture2D.ArraySlice,
>> +                                              NULL);
>> +    ID3D11Resource_Release(pTexture);
>> +
>> +    hr = ID3D11DeviceContext_Map(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture,
>> +                                 0, download ? D3D11_MAP_READ : D3D11_MAP_WRITE, 0, &LockedRect);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface %lx\n", hr);
>> +        ReleaseMutex(frames_hwctx->dev_ctx_mutex);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    for (i = 0; download ? dst->data[i] : src->data[i]; i++)
>> +        surf_linesize[i] = LockedRect.RowPitch;
>> +
>> +    av_image_fill_pointers(surf_data, ctx->sw_format, dstDesc.Height,
>> +                           (uint8_t*)LockedRect.pData, surf_linesize);
>> +
>> +    if (download) {
>> +        av_image_copy(dst->data, dst->linesize, surf_data, surf_linesize,
>> +                      ctx->sw_format, src->width, src->height);
>> +    } else {
>> +        av_image_copy(surf_data, surf_linesize, src->data, src->linesize,
>> +                      ctx->sw_format, src->width, src->height);
>> +    }
>> +
>> +    ID3D11DeviceContext_Unmap(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture, 0);
>> +    ReleaseMutex(frames_hwctx->dev_ctx_mutex);
>> +
>> +    return 0;
>> +}
>> +
>> +static void d3d11va_device_free(AVHWDeviceContext *ctx)
>> +{
>> +    AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
>> +    D3D11VADevicePriv       *priv = ctx->user_opaque;
>> +
>> +    if (device_hwctx->video_device)
>> +        ID3D11Device_Release(device_hwctx->video_device);
>> +
>> +    if (device_hwctx->video_context)
>> +        ID3D11VideoContext_Release(device_hwctx->video_context);
>> +
>> +    if (priv->d3dlib)
>> +        dlclose(priv->d3dlib);
>> +
>> +    av_freep(&ctx->user_opaque);
>> +}
>> +
>> +static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
>> +                                 AVDictionary *opts, int flags)
>> +{
>> +    AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
>> +    ID3D11Device           *d3d11_device;
>> +    ID3D11DeviceContext    *d3d11_context;
>> +    ID3D10Multithread      *pMultithread;
>> +    D3D11VADevicePriv      *priv;
>> +
>> +    HRESULT hr;
>> +    PFN_D3D11_CREATE_DEVICE createD3D;
>> +    IDXGIAdapter *pAdapter = NULL;
>> +    UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
>> +    /* if the DirectX SDK is installed creationFlags |= D3D11_CREATE_DEVICE_DEBUG; */
>> +
>> +    if (device) {
>> +        HMODULE dxgilib = dlopen("dxgi.dll", 0);
>> +        if (dxgilib) {
>> +            PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
>> +            mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) dlsym(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);
>> +                }
>> +            }
>> +            dlclose(dxgilib);
>> +        }
>> +    }
>> +
>> +    priv = av_mallocz(sizeof(*priv));
>> +    if (!priv)
>> +        return AVERROR(ENOMEM);
>> +
>> +    ctx->user_opaque = priv;
>> +    ctx->free        = d3d11va_device_free;
>> +
>> +    priv->d3dlib = dlopen("d3d11.dll", 0);
>> +    if (!priv->d3dlib) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library\n");
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    createD3D = (PFN_D3D11_CREATE_DEVICE) dlsym(priv->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, &d3d11_device, NULL, &d3d11_context);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to create Direct3D device %lx\n", hr);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    hr = ID3D11Device_QueryInterface(d3d11_device, &IID_ID3D10Multithread, (void **)&pMultithread);
>> +    if (SUCCEEDED(hr)) {
>> +        ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
>> +        ID3D10Multithread_Release(pMultithread);
>> +    }
>> +
>> +    hr = ID3D11DeviceContext_QueryInterface(d3d11_context, &IID_ID3D11VideoContext,
>> +                                            (void **)&device_hwctx->video_context);
>> +    ID3D11DeviceContext_Release(d3d11_context);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the Video Context %lx\n", hr);
>> +        ID3D11Device_Release(d3d11_device);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    hr = ID3D11Device_QueryInterface(d3d11_device, &IID_ID3D11VideoDevice,
>> +                                     (void **)&device_hwctx->video_device);
>> +    ID3D11Device_Release(d3d11_device);
>> +    if (FAILED(hr)) {
>> +        av_log(NULL, AV_LOG_ERROR, "Failed to get the Video Device %lx\n", hr);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    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,
>> +    .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_D3D11VA_VLD, AV_PIX_FMT_NONE },
>> +};
>> diff --git a/libavutil/hwcontext_d3d11va.h b/libavutil/hwcontext_d3d11va.h
>> new file mode 100644
>> index 0000000..e8d03f4
>> --- /dev/null
>> +++ b/libavutil/hwcontext_d3d11va.h
>> @@ -0,0 +1,81 @@
>> +/*
>> + * This file is part of FFmpeg.
>> + *
>> + * FFmpeg is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * FFmpeg is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with FFmpeg; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>> + */
>> +
>> +
>> +#ifndef AVUTIL_HWCONTEXT_D3D11VA_H
>> +#define AVUTIL_HWCONTEXT_D3D11VA_H
>> +
>> +/**
>> + * @file
>> + * An API-specific header for AV_HWDEVICE_TYPE_D3D11VA.
>> + *
>> + * Only fixed-size pools are supported.
>> + *
>> + * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
>> + * with the data pointer set to a pointer to ID3D11VideoDecoderOutputView.
>> + */
>> +
>> +#include <d3d11.h>
>> +
>> +/**
>> + * This struct is allocated as AVHWDeviceContext.hwctx
>> + */
>> +typedef struct AVD3D11VADeviceContext {
>> +    ID3D11VideoDevice   *video_device;
>> +    ID3D11VideoContext  *video_context;
>> +} AVD3D11VADeviceContext;
>> +
>> +/**
>> + * This struct is allocated as AVHWFramesContext.hwctx
>> + */
>> +typedef struct AVD3D11VAFramesContext {
>> +    /**
>> +     * The surface pool. When an external pool is not provided by the caller,
>> +     * this will be managed (allocated and filled on init, freed on uninit) by
>> +     * libavutil.
>> +     * When it is provided the allocation/deallocation is up to the caller.
>> +     */
>> +    ID3D11VideoDecoderOutputView **surfaces;
>> +    int                         nb_surfaces;
>> +
>> +    /**
>> +     * Certain drivers require the decoder to be destroyed before the surfaces.
>> +     * To allow internally managed pools to work properly in such cases, this
>> +     * field is provided.
>> +     *
>> +     * The decoder must be created before the surface pool.
>> +     *
>> +     * If it is non-NULL, libavutil will call ID3D11VideoDecoder_Release() on
>> +     * it just before the internal surface pool is freed.
>> +     */
>> +    ID3D11VideoDecoder  *decoder_to_release;
>> +
>> +    /**
>> +     * Internal texture to get access to the decoded pixels from the CPU as the
>> +     * decoder texture needs D3D11_BIND_DECODER which forbdis CPU access.
>
> Typo: forbids
>
>> +     */
>> +    ID3D11Texture2D     *staging_texture;
>> +
>> +    /**
>> +     * Internal Mutex to avoid accessing the ID3D11DeviceContext from multiple
>> +     * threads simultaneously.
>> +     */
>> +    HANDLE               dev_ctx_mutex;
>> +} AVD3D11VAFramesContext;
>
> If this is internal, then why is it exposed here? Same could probably
> be said about the staging texture.
>
> Also, I believe using AVMutex from libavutil/thread.h would be better,
> because it can use fast userspace mutexes on Vista+. But probably
> doesn't matter in this case.
>

IMHO this mutex should just generally go away, as you can simply lock
externally if you need to, it feels rather ugly and hackish to add it
here. Add to that Antons work to make hwaccel with threads fully
synchronized to the calling thread, and everything can be achieved
with external locking if you want multiple threads using the same
device.

- Hendrik
wm4 Dec. 17, 2016, 4:19 p.m. UTC | #3
On Fri, 16 Dec 2016 19:04:33 +0100
Hendrik Leppkes <h.leppkes@gmail.com> wrote:

> 
> IMHO this mutex should just generally go away, as you can simply lock
> externally if you need to, it feels rather ugly and hackish to add it
> here. Add to that Antons work to make hwaccel with threads fully
> synchronized to the calling thread, and everything can be achieved
> with external locking if you want multiple threads using the same
> device.

Where is the thread-safety or unsafety of these things defined?

MIO it's completely insane not to let at least some AVHWFramesContext
functions be thread-safe.
Steve Lhomme Jan. 4, 2017, 7:52 a.m. UTC | #4
On Fri, Dec 16, 2016 at 5:32 PM, wm4 <nfxjfg@googlemail.com> wrote:
> On Fri, 16 Dec 2016 11:12:02 +0100
> Steve Lhomme <robux4@gmail.com> wrote:
>
>> From: Steve Lhomme <robux4@gmail.com>
>>
>> ---
>>  libavutil/Makefile             |   3 +
>>  libavutil/hwcontext.c          |   3 +
>>  libavutil/hwcontext.h          |   1 +
>>  libavutil/hwcontext_d3d11va.c  | 457 +++++++++++++++++++++++++++++++++++++++++
>>  libavutil/hwcontext_d3d11va.h  |  81 ++++++++
>>  libavutil/hwcontext_internal.h |   1 +
>>  libavutil/version.h            |   2 +-
>>  7 files changed, 547 insertions(+), 1 deletion(-)
>>  create mode 100644 libavutil/hwcontext_d3d11va.c
>>  create mode 100644 libavutil/hwcontext_d3d11va.h
>>
>> diff --git a/libavutil/Makefile b/libavutil/Makefile
>> index 9841645..9b8ce22 100644
>> --- a/libavutil/Makefile
>> +++ b/libavutil/Makefile
>> @@ -34,6 +34,7 @@ HEADERS = adler32.h                                                     \
>>            hmac.h                                                        \
>>            hwcontext.h                                                   \
>>            hwcontext_cuda.h                                              \
>> +          hwcontext_d3d11va.h                                           \
>>            hwcontext_dxva2.h                                             \
>>            hwcontext_qsv.h                                               \
>>            hwcontext_vaapi.h                                             \
>> @@ -156,6 +157,7 @@ OBJS = adler32.o                                                        \
>>  OBJS-$(!HAVE_ATOMICS_NATIVE)            += atomic.o                     \
>>
>>  OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
>> +OBJS-$(CONFIG_D3D11VA)                  += hwcontext_d3d11va.o
>>  OBJS-$(CONFIG_DXVA2)                    += hwcontext_dxva2.o
>>  OBJS-$(CONFIG_QSV)                   += hwcontext_qsv.o
>>  OBJS-$(CONFIG_LZO)                      += lzo.o
>> @@ -170,6 +172,7 @@ SLIBOBJS-$(HAVE_GNU_WINDRES)            += avutilres.o
>>
>>  SKIPHEADERS-$(HAVE_CUDA_H)             += hwcontext_cuda.h
>>  SKIPHEADERS-$(CONFIG_CUDA)             += hwcontext_cuda_internal.h
>> +SKIPHEADERS-$(CONFIG_D3D11VA)          += hwcontext_d3d11va.h
>>  SKIPHEADERS-$(CONFIG_DXVA2)            += hwcontext_dxva2.h
>>  SKIPHEADERS-$(CONFIG_QSV)           += hwcontext_qsv.h
>>  SKIPHEADERS-$(CONFIG_VAAPI)            += hwcontext_vaapi.h
>> diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
>> index 615f1f7..a9db84b 100644
>> --- a/libavutil/hwcontext.c
>> +++ b/libavutil/hwcontext.c
>> @@ -32,6 +32,9 @@ static const HWContextType *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
>> diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
>> index 785da09..1325917 100644
>> --- a/libavutil/hwcontext.h
>> +++ b/libavutil/hwcontext.h
>> @@ -30,6 +30,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 0000000..0618da3
>> --- /dev/null
>> +++ b/libavutil/hwcontext_d3d11va.c
>> @@ -0,0 +1,457 @@
>> +/*
>> + * This file is part of FFmpeg.
>> + *
>> + * FFmpeg is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * FFmpeg is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with FFmpeg; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>> + */
>> +
>> +#include <windows.h>
>> +
>> +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600
>> +#undef _WIN32_WINNT
>> +#define _WIN32_WINNT 0x0600
>> +#endif
>> +#define COBJMACROS
>> +
>> +#include <initguid.h>
>> +#include <d3d11.h>
>> +#include <dxgi1_2.h>
>> +
>> +#include "avassert.h"
>> +#include "common.h"
>> +#include "hwcontext.h"
>> +#include "hwcontext_d3d11va.h"
>> +#include "hwcontext_internal.h"
>> +#include "imgutils.h"
>> +#include "pixdesc.h"
>> +#include "pixfmt.h"
>> +#include "compat/w32dlfcn.h"
>> +
>> +typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
>> +
>> +typedef struct D3D11VAFramesContext {
>> +    ID3D11VideoDecoderOutputView **surfaces_internal;
>> +    int              nb_surfaces_used;
>> +
>> +    ID3D11DeviceContext *d3d11_context;
>> +
>> +    DXGI_FORMAT format;
>> +} D3D11VAFramesContext;
>> +
>> +typedef struct D3D11VADevicePriv {
>> +    HMODULE d3dlib;
>> +} D3D11VADevicePriv;
>> +
>> +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_frames_uninit(AVHWFramesContext *ctx)
>> +{
>> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
>> +    D3D11VAFramesContext *s = ctx->internal->priv;
>> +
>> +    if (frames_hwctx->decoder_to_release)
>> +        ID3D11VideoDecoder_Release(frames_hwctx->decoder_to_release);
>> +
>> +    av_freep(&s->surfaces_internal);
>> +
>> +    if (frames_hwctx->staging_texture)
>> +        ID3D11Texture2D_Release(frames_hwctx->staging_texture);
>> +
>> +    if (s->d3d11_context) {
>> +        ID3D11DeviceContext_Release(s->d3d11_context);
>> +        s->d3d11_context = NULL;
>> +    }
>> +
>> +    if (frames_hwctx->dev_ctx_mutex != INVALID_HANDLE_VALUE)
>> +        ReleaseMutex(frames_hwctx->dev_ctx_mutex);
>> +}
>> +
>> +static void free_surface(void *opaque, uint8_t *data)
>> +{
>> +    ID3D11VideoDecoderOutputView_Release((ID3D11VideoDecoderOutputView*)data);
>> +}
>> +
>> +static AVBufferRef *d3d11va_pool_alloc(void *opaque, int size)
>> +{
>> +    AVHWFramesContext        *ctx = (AVHWFramesContext*)opaque;
>> +    D3D11VAFramesContext       *s = ctx->internal->priv;
>> +    AVD3D11VAFramesContext *hwctx = ctx->hwctx;
>> +
>> +    if (s->nb_surfaces_used < hwctx->nb_surfaces) {
>> +        s->nb_surfaces_used++;
>> +        return av_buffer_create((uint8_t*)s->surfaces_internal[s->nb_surfaces_used - 1],
>> +                                sizeof(*hwctx->surfaces), free_surface, 0, 0);
>> +    }
>> +
>> +    return NULL;
>> +}
>> +
>> +static int d3d11va_init_pool(AVHWFramesContext *ctx)
>> +{
>> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
>> +    AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
>> +    D3D11VAFramesContext              *s = ctx->internal->priv;
>> +
>> +    int i;
>> +    HRESULT hr;
>> +    D3D11_TEXTURE2D_DESC texDesc;
>> +    ID3D11Texture2D *p_texture;
>> +    D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
>> +    D3D11_VIDEO_DECODER_DESC decoderDesc;
>> +    D3D11_VIDEO_DECODER_CONFIG decoderConfig;
>> +    ID3D11Device *d3d11_device;
>> +
>> +    if (ctx->initial_pool_size <= 0)
>> +        return 0;
>> +
>> +    hr = ID3D11VideoContext_QueryInterface(device_hwctx->video_context, &IID_ID3D11DeviceContext,
>> +                                           (void **)&s->d3d11_context);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the device context %lx\n", hr);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    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);
>> +    }
>> +
>> +    s->surfaces_internal = av_mallocz_array(ctx->initial_pool_size,
>> +                                            sizeof(*s->surfaces_internal));
>> +    if (!s->surfaces_internal)
>> +        return AVERROR(ENOMEM);
>> +
>> +    ZeroMemory(&texDesc, sizeof(texDesc));
>> +    texDesc.Width = ctx->width;
>> +    texDesc.Height = ctx->height;
>> +    texDesc.MipLevels = 1;
>> +    texDesc.Format = s->format;
>> +    texDesc.SampleDesc.Count = 1;
>> +    texDesc.ArraySize = ctx->initial_pool_size;
>> +    texDesc.Usage = D3D11_USAGE_DEFAULT;
>> +    texDesc.BindFlags = D3D11_BIND_DECODER;
>> +
>> +    frames_hwctx->dev_ctx_mutex = CreateMutex(NULL, 0, NULL);
>> +    if (frames_hwctx->dev_ctx_mutex == INVALID_HANDLE_VALUE) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to get a mutex for the D3D11VA decoder\n");
>> +        return AVERROR(EINVAL);
>> +    }
>> +
>> +    hr = ID3D11VideoDevice_QueryInterface(device_hwctx->video_device, &IID_ID3D11Device, (void **)&d3d11_device);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the device %lx\n", hr);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    hr = ID3D11Device_CreateTexture2D(d3d11_device, &texDesc, NULL, &p_texture);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Could not create the texture %lx\n", hr);
>> +        ID3D11Device_Release(d3d11_device);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    hr = ID3D11VideoDecoder_GetCreationParameters(frames_hwctx->decoder_to_release, &decoderDesc, &decoderConfig);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Could not get the decoder config %lx\n", hr);
>> +        ID3D11Texture2D_Release(p_texture);
>> +        ID3D11Device_Release(d3d11_device);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    ZeroMemory(&viewDesc, sizeof(viewDesc));
>> +    viewDesc.DecodeProfile = decoderDesc.Guid;
>> +    viewDesc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
>> +    for (i=0; i<ctx->initial_pool_size; i++)
>> +    {
>> +        hr = ID3D11VideoDevice_CreateVideoDecoderOutputView(device_hwctx->video_device,
>> +                                                            (ID3D11Resource*) p_texture,
>> +                                                            &viewDesc,
>> +                                                            (ID3D11VideoDecoderOutputView**) &s->surfaces_internal[i]);
>> +        if (FAILED(hr)) {
>> +            av_log(ctx, AV_LOG_ERROR, "Could not create the decoder output %d\n", i);
>> +            while (--i >= 0) {
>> +                ID3D11VideoDecoderOutputView_Release(s->surfaces_internal[i]);
>> +                s->surfaces_internal[i] = NULL;
>> +            }
>> +            ID3D11Texture2D_Release(p_texture);
>> +            ID3D11Device_Release(d3d11_device);
>> +            return AVERROR_UNKNOWN;
>> +        }
>> +    }
>> +    ID3D11Texture2D_Release(p_texture);
>> +
>> +    texDesc.ArraySize = 1;
>> +    texDesc.Usage = D3D11_USAGE_STAGING;
>> +    texDesc.BindFlags = 0;
>> +    texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
>> +    hr = ID3D11Device_CreateTexture2D(d3d11_device, &texDesc, NULL, &frames_hwctx->staging_texture);
>> +    ID3D11Device_Release(d3d11_device);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Could not create the staging texture %lx\n", hr);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    ctx->internal->pool_internal = av_buffer_pool_init2(sizeof(*s->surfaces_internal),
>> +                                                        ctx, d3d11va_pool_alloc, NULL);
>> +    if (!ctx->internal->pool_internal)
>> +        return AVERROR(ENOMEM);
>> +
>> +    frames_hwctx->surfaces    = s->surfaces_internal;
>> +    frames_hwctx->nb_surfaces = ctx->initial_pool_size;
>> +
>> +    return 0;
>> +}
>> +
>> +static int d3d11va_frames_init(AVHWFramesContext *ctx)
>> +{
>> +    int ret;
>> +
>> +    /* init the frame pool if the caller didn't provide one */
>> +    if (!ctx->pool) {
>> +        ret = d3d11va_init_pool(ctx);
>> +        if (ret < 0) {
>> +            av_log(ctx, AV_LOG_ERROR, "Error creating an internal frame pool\n");
>> +            return ret;
>> +        }
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
>> +{
>> +    frame->buf[0] = av_buffer_pool_get(ctx->pool);
>> +    if (!frame->buf[0])
>> +        return AVERROR(ENOMEM);
>> +
>> +    frame->data[3] = frame->buf[0]->data;
>> +    frame->format  = AV_PIX_FMT_D3D11VA_VLD;
>> +    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 int d3d11va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
>> +                                 const AVFrame *src)
>> +{
>> +    ID3D11VideoDecoderOutputView *surface;
>> +    D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC    surfaceDesc;
>> +    D3D11_TEXTURE2D_DESC dstDesc;
>> +    D3D11_MAPPED_SUBRESOURCE     LockedRect;
>> +    ID3D11Resource *pTexture;
>> +    HRESULT            hr;
>> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
>> +    D3D11VAFramesContext              *s = ctx->internal->priv;
>> +
>> +    uint8_t *surf_data[4]     = { NULL };
>> +    int      surf_linesize[4] = { 0 };
>> +    int i;
>> +
>> +    int download = !!src->hw_frames_ctx;
>> +
>> +    surface = (ID3D11VideoDecoderOutputView*)(download ? src->data[3] : dst->data[3]);
>> +
>> +    ID3D11VideoDecoderOutputView_GetDesc(surface, &surfaceDesc);
>> +    ID3D11VideoDecoderOutputView_GetResource(surface, &pTexture);
>> +    ID3D11Texture2D_GetDesc(frames_hwctx->staging_texture, &dstDesc);
>> +
>> +    WaitForSingleObjectEx(frames_hwctx->dev_ctx_mutex, INFINITE, FALSE);
>> +
>> +    ID3D11DeviceContext_CopySubresourceRegion(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture,
>> +                                              0, 0, 0, 0,
>> +                                              (ID3D11Resource*)pTexture, surfaceDesc.Texture2D.ArraySlice,
>> +                                              NULL);
>> +    ID3D11Resource_Release(pTexture);
>> +
>> +    hr = ID3D11DeviceContext_Map(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture,
>> +                                 0, download ? D3D11_MAP_READ : D3D11_MAP_WRITE, 0, &LockedRect);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface %lx\n", hr);
>> +        ReleaseMutex(frames_hwctx->dev_ctx_mutex);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    for (i = 0; download ? dst->data[i] : src->data[i]; i++)
>> +        surf_linesize[i] = LockedRect.RowPitch;
>> +
>> +    av_image_fill_pointers(surf_data, ctx->sw_format, dstDesc.Height,
>> +                           (uint8_t*)LockedRect.pData, surf_linesize);
>> +
>> +    if (download) {
>> +        av_image_copy(dst->data, dst->linesize, surf_data, surf_linesize,
>> +                      ctx->sw_format, src->width, src->height);
>> +    } else {
>> +        av_image_copy(surf_data, surf_linesize, src->data, src->linesize,
>> +                      ctx->sw_format, src->width, src->height);
>> +    }
>> +
>> +    ID3D11DeviceContext_Unmap(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture, 0);
>> +    ReleaseMutex(frames_hwctx->dev_ctx_mutex);
>> +
>> +    return 0;
>> +}
>> +
>> +static void d3d11va_device_free(AVHWDeviceContext *ctx)
>> +{
>> +    AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
>> +    D3D11VADevicePriv       *priv = ctx->user_opaque;
>> +
>> +    if (device_hwctx->video_device)
>> +        ID3D11Device_Release(device_hwctx->video_device);
>> +
>> +    if (device_hwctx->video_context)
>> +        ID3D11VideoContext_Release(device_hwctx->video_context);
>> +
>> +    if (priv->d3dlib)
>> +        dlclose(priv->d3dlib);
>> +
>> +    av_freep(&ctx->user_opaque);
>> +}
>> +
>> +static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
>> +                                 AVDictionary *opts, int flags)
>> +{
>> +    AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
>> +    ID3D11Device           *d3d11_device;
>> +    ID3D11DeviceContext    *d3d11_context;
>> +    ID3D10Multithread      *pMultithread;
>> +    D3D11VADevicePriv      *priv;
>> +
>> +    HRESULT hr;
>> +    PFN_D3D11_CREATE_DEVICE createD3D;
>> +    IDXGIAdapter *pAdapter = NULL;
>> +    UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
>> +    /* if the DirectX SDK is installed creationFlags |= D3D11_CREATE_DEVICE_DEBUG; */
>> +
>> +    if (device) {
>> +        HMODULE dxgilib = dlopen("dxgi.dll", 0);
>> +        if (dxgilib) {
>> +            PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
>> +            mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) dlsym(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);
>> +                }
>> +            }
>> +            dlclose(dxgilib);
>> +        }
>> +    }
>> +
>> +    priv = av_mallocz(sizeof(*priv));
>> +    if (!priv)
>> +        return AVERROR(ENOMEM);
>> +
>> +    ctx->user_opaque = priv;
>> +    ctx->free        = d3d11va_device_free;
>> +
>> +    priv->d3dlib = dlopen("d3d11.dll", 0);
>> +    if (!priv->d3dlib) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library\n");
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    createD3D = (PFN_D3D11_CREATE_DEVICE) dlsym(priv->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, &d3d11_device, NULL, &d3d11_context);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to create Direct3D device %lx\n", hr);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    hr = ID3D11Device_QueryInterface(d3d11_device, &IID_ID3D10Multithread, (void **)&pMultithread);
>> +    if (SUCCEEDED(hr)) {
>> +        ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
>> +        ID3D10Multithread_Release(pMultithread);
>> +    }
>> +
>> +    hr = ID3D11DeviceContext_QueryInterface(d3d11_context, &IID_ID3D11VideoContext,
>> +                                            (void **)&device_hwctx->video_context);
>> +    ID3D11DeviceContext_Release(d3d11_context);
>> +    if (FAILED(hr)) {
>> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the Video Context %lx\n", hr);
>> +        ID3D11Device_Release(d3d11_device);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    hr = ID3D11Device_QueryInterface(d3d11_device, &IID_ID3D11VideoDevice,
>> +                                     (void **)&device_hwctx->video_device);
>> +    ID3D11Device_Release(d3d11_device);
>> +    if (FAILED(hr)) {
>> +        av_log(NULL, AV_LOG_ERROR, "Failed to get the Video Device %lx\n", hr);
>> +        return AVERROR_UNKNOWN;
>> +    }
>> +
>> +    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,
>> +    .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_D3D11VA_VLD, AV_PIX_FMT_NONE },
>> +};
>> diff --git a/libavutil/hwcontext_d3d11va.h b/libavutil/hwcontext_d3d11va.h
>> new file mode 100644
>> index 0000000..e8d03f4
>> --- /dev/null
>> +++ b/libavutil/hwcontext_d3d11va.h
>> @@ -0,0 +1,81 @@
>> +/*
>> + * This file is part of FFmpeg.
>> + *
>> + * FFmpeg is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * FFmpeg is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with FFmpeg; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>> + */
>> +
>> +
>> +#ifndef AVUTIL_HWCONTEXT_D3D11VA_H
>> +#define AVUTIL_HWCONTEXT_D3D11VA_H
>> +
>> +/**
>> + * @file
>> + * An API-specific header for AV_HWDEVICE_TYPE_D3D11VA.
>> + *
>> + * Only fixed-size pools are supported.
>> + *
>> + * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
>> + * with the data pointer set to a pointer to ID3D11VideoDecoderOutputView.
>> + */
>> +
>> +#include <d3d11.h>
>> +
>> +/**
>> + * This struct is allocated as AVHWDeviceContext.hwctx
>> + */
>> +typedef struct AVD3D11VADeviceContext {
>> +    ID3D11VideoDevice   *video_device;
>> +    ID3D11VideoContext  *video_context;
>> +} AVD3D11VADeviceContext;
>> +
>> +/**
>> + * This struct is allocated as AVHWFramesContext.hwctx
>> + */
>> +typedef struct AVD3D11VAFramesContext {
>> +    /**
>> +     * The surface pool. When an external pool is not provided by the caller,
>> +     * this will be managed (allocated and filled on init, freed on uninit) by
>> +     * libavutil.
>> +     * When it is provided the allocation/deallocation is up to the caller.
>> +     */
>> +    ID3D11VideoDecoderOutputView **surfaces;
>> +    int                         nb_surfaces;
>> +
>> +    /**
>> +     * Certain drivers require the decoder to be destroyed before the surfaces.
>> +     * To allow internally managed pools to work properly in such cases, this
>> +     * field is provided.
>> +     *
>> +     * The decoder must be created before the surface pool.
>> +     *
>> +     * If it is non-NULL, libavutil will call ID3D11VideoDecoder_Release() on
>> +     * it just before the internal surface pool is freed.
>> +     */
>> +    ID3D11VideoDecoder  *decoder_to_release;
>> +
>> +    /**
>> +     * Internal texture to get access to the decoded pixels from the CPU as the
>> +     * decoder texture needs D3D11_BIND_DECODER which forbdis CPU access.
>
> Typo: forbids
>
>> +     */
>> +    ID3D11Texture2D     *staging_texture;
>> +
>> +    /**
>> +     * Internal Mutex to avoid accessing the ID3D11DeviceContext from multiple
>> +     * threads simultaneously.
>> +     */
>> +    HANDLE               dev_ctx_mutex;
>> +} AVD3D11VAFramesContext;
>
> If this is internal, then why is it exposed here? Same could probably
> be said about the staging texture.
>
> Also, I believe using AVMutex from libavutil/thread.h would be better,
> because it can use fast userspace mutexes on Vista+. But probably
> doesn't matter in this case.

No because this mutex also needs to be used by the dxva decoder when
using the D3D11 device/video context. And this is a public variable.
In VLC we also have better things than a Mutex but since it's shared
code with libavcodec we can't really have some advanced common ground.

>> +
>> +#endif /* AVUTIL_HWCONTEXT_D3D11VA_H */
>> diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h
>> index 079e42b..aaa6286 100644
>> --- a/libavutil/hwcontext_internal.h
>> +++ b/libavutil/hwcontext_internal.h
>> @@ -100,6 +100,7 @@ struct AVHWFramesInternal {
>>  };
>>
>>  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/version.h b/libavutil/version.h
>> index 9f8c4c2..6dac240 100644
>> --- a/libavutil/version.h
>> +++ b/libavutil/version.h
>> @@ -80,7 +80,7 @@
>>
>>  #define LIBAVUTIL_VERSION_MAJOR  55
>>  #define LIBAVUTIL_VERSION_MINOR  43
>> -#define LIBAVUTIL_VERSION_MICRO 100
>> +#define LIBAVUTIL_VERSION_MICRO 101
>>
>>  #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
>>                                                 LIBAVUTIL_VERSION_MINOR, \
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
wm4 Jan. 9, 2017, 9:48 a.m. UTC | #5
On Wed, 4 Jan 2017 08:52:26 +0100
Steve Lhomme <robux4@gmail.com> wrote:

> On Fri, Dec 16, 2016 at 5:32 PM, wm4 <nfxjfg@googlemail.com> wrote:
> > On Fri, 16 Dec 2016 11:12:02 +0100
> > Steve Lhomme <robux4@gmail.com> wrote:
> >  
> >> From: Steve Lhomme <robux4@gmail.com>
> >>
> >> ---
> >>  libavutil/Makefile             |   3 +
> >>  libavutil/hwcontext.c          |   3 +
> >>  libavutil/hwcontext.h          |   1 +
> >>  libavutil/hwcontext_d3d11va.c  | 457 +++++++++++++++++++++++++++++++++++++++++
> >>  libavutil/hwcontext_d3d11va.h  |  81 ++++++++
> >>  libavutil/hwcontext_internal.h |   1 +
> >>  libavutil/version.h            |   2 +-
> >>  7 files changed, 547 insertions(+), 1 deletion(-)
> >>  create mode 100644 libavutil/hwcontext_d3d11va.c
> >>  create mode 100644 libavutil/hwcontext_d3d11va.h
> >>
> >> diff --git a/libavutil/Makefile b/libavutil/Makefile
> >> index 9841645..9b8ce22 100644
> >> --- a/libavutil/Makefile
> >> +++ b/libavutil/Makefile
> >> @@ -34,6 +34,7 @@ HEADERS = adler32.h                                                     \
> >>            hmac.h                                                        \
> >>            hwcontext.h                                                   \
> >>            hwcontext_cuda.h                                              \
> >> +          hwcontext_d3d11va.h                                           \
> >>            hwcontext_dxva2.h                                             \
> >>            hwcontext_qsv.h                                               \
> >>            hwcontext_vaapi.h                                             \
> >> @@ -156,6 +157,7 @@ OBJS = adler32.o                                                        \
> >>  OBJS-$(!HAVE_ATOMICS_NATIVE)            += atomic.o                     \
> >>
> >>  OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
> >> +OBJS-$(CONFIG_D3D11VA)                  += hwcontext_d3d11va.o
> >>  OBJS-$(CONFIG_DXVA2)                    += hwcontext_dxva2.o
> >>  OBJS-$(CONFIG_QSV)                   += hwcontext_qsv.o
> >>  OBJS-$(CONFIG_LZO)                      += lzo.o
> >> @@ -170,6 +172,7 @@ SLIBOBJS-$(HAVE_GNU_WINDRES)            += avutilres.o
> >>
> >>  SKIPHEADERS-$(HAVE_CUDA_H)             += hwcontext_cuda.h
> >>  SKIPHEADERS-$(CONFIG_CUDA)             += hwcontext_cuda_internal.h
> >> +SKIPHEADERS-$(CONFIG_D3D11VA)          += hwcontext_d3d11va.h
> >>  SKIPHEADERS-$(CONFIG_DXVA2)            += hwcontext_dxva2.h
> >>  SKIPHEADERS-$(CONFIG_QSV)           += hwcontext_qsv.h
> >>  SKIPHEADERS-$(CONFIG_VAAPI)            += hwcontext_vaapi.h
> >> diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
> >> index 615f1f7..a9db84b 100644
> >> --- a/libavutil/hwcontext.c
> >> +++ b/libavutil/hwcontext.c
> >> @@ -32,6 +32,9 @@ static const HWContextType *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
> >> diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
> >> index 785da09..1325917 100644
> >> --- a/libavutil/hwcontext.h
> >> +++ b/libavutil/hwcontext.h
> >> @@ -30,6 +30,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 0000000..0618da3
> >> --- /dev/null
> >> +++ b/libavutil/hwcontext_d3d11va.c
> >> @@ -0,0 +1,457 @@
> >> +/*
> >> + * This file is part of FFmpeg.
> >> + *
> >> + * FFmpeg is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU Lesser General Public
> >> + * License as published by the Free Software Foundation; either
> >> + * version 2.1 of the License, or (at your option) any later version.
> >> + *
> >> + * FFmpeg is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >> + * Lesser General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU Lesser General Public
> >> + * License along with FFmpeg; if not, write to the Free Software
> >> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> >> + */
> >> +
> >> +#include <windows.h>
> >> +
> >> +#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600
> >> +#undef _WIN32_WINNT
> >> +#define _WIN32_WINNT 0x0600
> >> +#endif
> >> +#define COBJMACROS
> >> +
> >> +#include <initguid.h>
> >> +#include <d3d11.h>
> >> +#include <dxgi1_2.h>
> >> +
> >> +#include "avassert.h"
> >> +#include "common.h"
> >> +#include "hwcontext.h"
> >> +#include "hwcontext_d3d11va.h"
> >> +#include "hwcontext_internal.h"
> >> +#include "imgutils.h"
> >> +#include "pixdesc.h"
> >> +#include "pixfmt.h"
> >> +#include "compat/w32dlfcn.h"
> >> +
> >> +typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
> >> +
> >> +typedef struct D3D11VAFramesContext {
> >> +    ID3D11VideoDecoderOutputView **surfaces_internal;
> >> +    int              nb_surfaces_used;
> >> +
> >> +    ID3D11DeviceContext *d3d11_context;
> >> +
> >> +    DXGI_FORMAT format;
> >> +} D3D11VAFramesContext;
> >> +
> >> +typedef struct D3D11VADevicePriv {
> >> +    HMODULE d3dlib;
> >> +} D3D11VADevicePriv;
> >> +
> >> +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_frames_uninit(AVHWFramesContext *ctx)
> >> +{
> >> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
> >> +    D3D11VAFramesContext *s = ctx->internal->priv;
> >> +
> >> +    if (frames_hwctx->decoder_to_release)
> >> +        ID3D11VideoDecoder_Release(frames_hwctx->decoder_to_release);
> >> +
> >> +    av_freep(&s->surfaces_internal);
> >> +
> >> +    if (frames_hwctx->staging_texture)
> >> +        ID3D11Texture2D_Release(frames_hwctx->staging_texture);
> >> +
> >> +    if (s->d3d11_context) {
> >> +        ID3D11DeviceContext_Release(s->d3d11_context);
> >> +        s->d3d11_context = NULL;
> >> +    }
> >> +
> >> +    if (frames_hwctx->dev_ctx_mutex != INVALID_HANDLE_VALUE)
> >> +        ReleaseMutex(frames_hwctx->dev_ctx_mutex);
> >> +}
> >> +
> >> +static void free_surface(void *opaque, uint8_t *data)
> >> +{
> >> +    ID3D11VideoDecoderOutputView_Release((ID3D11VideoDecoderOutputView*)data);
> >> +}
> >> +
> >> +static AVBufferRef *d3d11va_pool_alloc(void *opaque, int size)
> >> +{
> >> +    AVHWFramesContext        *ctx = (AVHWFramesContext*)opaque;
> >> +    D3D11VAFramesContext       *s = ctx->internal->priv;
> >> +    AVD3D11VAFramesContext *hwctx = ctx->hwctx;
> >> +
> >> +    if (s->nb_surfaces_used < hwctx->nb_surfaces) {
> >> +        s->nb_surfaces_used++;
> >> +        return av_buffer_create((uint8_t*)s->surfaces_internal[s->nb_surfaces_used - 1],
> >> +                                sizeof(*hwctx->surfaces), free_surface, 0, 0);
> >> +    }
> >> +
> >> +    return NULL;
> >> +}
> >> +
> >> +static int d3d11va_init_pool(AVHWFramesContext *ctx)
> >> +{
> >> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
> >> +    AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
> >> +    D3D11VAFramesContext              *s = ctx->internal->priv;
> >> +
> >> +    int i;
> >> +    HRESULT hr;
> >> +    D3D11_TEXTURE2D_DESC texDesc;
> >> +    ID3D11Texture2D *p_texture;
> >> +    D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
> >> +    D3D11_VIDEO_DECODER_DESC decoderDesc;
> >> +    D3D11_VIDEO_DECODER_CONFIG decoderConfig;
> >> +    ID3D11Device *d3d11_device;
> >> +
> >> +    if (ctx->initial_pool_size <= 0)
> >> +        return 0;
> >> +
> >> +    hr = ID3D11VideoContext_QueryInterface(device_hwctx->video_context, &IID_ID3D11DeviceContext,
> >> +                                           (void **)&s->d3d11_context);
> >> +    if (FAILED(hr)) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the device context %lx\n", hr);
> >> +        return AVERROR_UNKNOWN;
> >> +    }
> >> +
> >> +    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);
> >> +    }
> >> +
> >> +    s->surfaces_internal = av_mallocz_array(ctx->initial_pool_size,
> >> +                                            sizeof(*s->surfaces_internal));
> >> +    if (!s->surfaces_internal)
> >> +        return AVERROR(ENOMEM);
> >> +
> >> +    ZeroMemory(&texDesc, sizeof(texDesc));
> >> +    texDesc.Width = ctx->width;
> >> +    texDesc.Height = ctx->height;
> >> +    texDesc.MipLevels = 1;
> >> +    texDesc.Format = s->format;
> >> +    texDesc.SampleDesc.Count = 1;
> >> +    texDesc.ArraySize = ctx->initial_pool_size;
> >> +    texDesc.Usage = D3D11_USAGE_DEFAULT;
> >> +    texDesc.BindFlags = D3D11_BIND_DECODER;
> >> +
> >> +    frames_hwctx->dev_ctx_mutex = CreateMutex(NULL, 0, NULL);
> >> +    if (frames_hwctx->dev_ctx_mutex == INVALID_HANDLE_VALUE) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Failed to get a mutex for the D3D11VA decoder\n");
> >> +        return AVERROR(EINVAL);
> >> +    }
> >> +
> >> +    hr = ID3D11VideoDevice_QueryInterface(device_hwctx->video_device, &IID_ID3D11Device, (void **)&d3d11_device);
> >> +    if (FAILED(hr)) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the device %lx\n", hr);
> >> +        return AVERROR_UNKNOWN;
> >> +    }
> >> +
> >> +    hr = ID3D11Device_CreateTexture2D(d3d11_device, &texDesc, NULL, &p_texture);
> >> +    if (FAILED(hr)) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Could not create the texture %lx\n", hr);
> >> +        ID3D11Device_Release(d3d11_device);
> >> +        return AVERROR_UNKNOWN;
> >> +    }
> >> +
> >> +    hr = ID3D11VideoDecoder_GetCreationParameters(frames_hwctx->decoder_to_release, &decoderDesc, &decoderConfig);
> >> +    if (FAILED(hr)) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Could not get the decoder config %lx\n", hr);
> >> +        ID3D11Texture2D_Release(p_texture);
> >> +        ID3D11Device_Release(d3d11_device);
> >> +        return AVERROR_UNKNOWN;
> >> +    }
> >> +
> >> +    ZeroMemory(&viewDesc, sizeof(viewDesc));
> >> +    viewDesc.DecodeProfile = decoderDesc.Guid;
> >> +    viewDesc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
> >> +    for (i=0; i<ctx->initial_pool_size; i++)
> >> +    {
> >> +        hr = ID3D11VideoDevice_CreateVideoDecoderOutputView(device_hwctx->video_device,
> >> +                                                            (ID3D11Resource*) p_texture,
> >> +                                                            &viewDesc,
> >> +                                                            (ID3D11VideoDecoderOutputView**) &s->surfaces_internal[i]);
> >> +        if (FAILED(hr)) {
> >> +            av_log(ctx, AV_LOG_ERROR, "Could not create the decoder output %d\n", i);
> >> +            while (--i >= 0) {
> >> +                ID3D11VideoDecoderOutputView_Release(s->surfaces_internal[i]);
> >> +                s->surfaces_internal[i] = NULL;
> >> +            }
> >> +            ID3D11Texture2D_Release(p_texture);
> >> +            ID3D11Device_Release(d3d11_device);
> >> +            return AVERROR_UNKNOWN;
> >> +        }
> >> +    }
> >> +    ID3D11Texture2D_Release(p_texture);
> >> +
> >> +    texDesc.ArraySize = 1;
> >> +    texDesc.Usage = D3D11_USAGE_STAGING;
> >> +    texDesc.BindFlags = 0;
> >> +    texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
> >> +    hr = ID3D11Device_CreateTexture2D(d3d11_device, &texDesc, NULL, &frames_hwctx->staging_texture);
> >> +    ID3D11Device_Release(d3d11_device);
> >> +    if (FAILED(hr)) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Could not create the staging texture %lx\n", hr);
> >> +        return AVERROR_UNKNOWN;
> >> +    }
> >> +
> >> +    ctx->internal->pool_internal = av_buffer_pool_init2(sizeof(*s->surfaces_internal),
> >> +                                                        ctx, d3d11va_pool_alloc, NULL);
> >> +    if (!ctx->internal->pool_internal)
> >> +        return AVERROR(ENOMEM);
> >> +
> >> +    frames_hwctx->surfaces    = s->surfaces_internal;
> >> +    frames_hwctx->nb_surfaces = ctx->initial_pool_size;
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int d3d11va_frames_init(AVHWFramesContext *ctx)
> >> +{
> >> +    int ret;
> >> +
> >> +    /* init the frame pool if the caller didn't provide one */
> >> +    if (!ctx->pool) {
> >> +        ret = d3d11va_init_pool(ctx);
> >> +        if (ret < 0) {
> >> +            av_log(ctx, AV_LOG_ERROR, "Error creating an internal frame pool\n");
> >> +            return ret;
> >> +        }
> >> +    }
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
> >> +{
> >> +    frame->buf[0] = av_buffer_pool_get(ctx->pool);
> >> +    if (!frame->buf[0])
> >> +        return AVERROR(ENOMEM);
> >> +
> >> +    frame->data[3] = frame->buf[0]->data;
> >> +    frame->format  = AV_PIX_FMT_D3D11VA_VLD;
> >> +    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 int d3d11va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
> >> +                                 const AVFrame *src)
> >> +{
> >> +    ID3D11VideoDecoderOutputView *surface;
> >> +    D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC    surfaceDesc;
> >> +    D3D11_TEXTURE2D_DESC dstDesc;
> >> +    D3D11_MAPPED_SUBRESOURCE     LockedRect;
> >> +    ID3D11Resource *pTexture;
> >> +    HRESULT            hr;
> >> +    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
> >> +    D3D11VAFramesContext              *s = ctx->internal->priv;
> >> +
> >> +    uint8_t *surf_data[4]     = { NULL };
> >> +    int      surf_linesize[4] = { 0 };
> >> +    int i;
> >> +
> >> +    int download = !!src->hw_frames_ctx;
> >> +
> >> +    surface = (ID3D11VideoDecoderOutputView*)(download ? src->data[3] : dst->data[3]);
> >> +
> >> +    ID3D11VideoDecoderOutputView_GetDesc(surface, &surfaceDesc);
> >> +    ID3D11VideoDecoderOutputView_GetResource(surface, &pTexture);
> >> +    ID3D11Texture2D_GetDesc(frames_hwctx->staging_texture, &dstDesc);
> >> +
> >> +    WaitForSingleObjectEx(frames_hwctx->dev_ctx_mutex, INFINITE, FALSE);
> >> +
> >> +    ID3D11DeviceContext_CopySubresourceRegion(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture,
> >> +                                              0, 0, 0, 0,
> >> +                                              (ID3D11Resource*)pTexture, surfaceDesc.Texture2D.ArraySlice,
> >> +                                              NULL);
> >> +    ID3D11Resource_Release(pTexture);
> >> +
> >> +    hr = ID3D11DeviceContext_Map(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture,
> >> +                                 0, download ? D3D11_MAP_READ : D3D11_MAP_WRITE, 0, &LockedRect);
> >> +    if (FAILED(hr)) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface %lx\n", hr);
> >> +        ReleaseMutex(frames_hwctx->dev_ctx_mutex);
> >> +        return AVERROR_UNKNOWN;
> >> +    }
> >> +
> >> +    for (i = 0; download ? dst->data[i] : src->data[i]; i++)
> >> +        surf_linesize[i] = LockedRect.RowPitch;
> >> +
> >> +    av_image_fill_pointers(surf_data, ctx->sw_format, dstDesc.Height,
> >> +                           (uint8_t*)LockedRect.pData, surf_linesize);
> >> +
> >> +    if (download) {
> >> +        av_image_copy(dst->data, dst->linesize, surf_data, surf_linesize,
> >> +                      ctx->sw_format, src->width, src->height);
> >> +    } else {
> >> +        av_image_copy(surf_data, surf_linesize, src->data, src->linesize,
> >> +                      ctx->sw_format, src->width, src->height);
> >> +    }
> >> +
> >> +    ID3D11DeviceContext_Unmap(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture, 0);
> >> +    ReleaseMutex(frames_hwctx->dev_ctx_mutex);
> >> +
> >> +    return 0;
> >> +}
> >> +
> >> +static void d3d11va_device_free(AVHWDeviceContext *ctx)
> >> +{
> >> +    AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
> >> +    D3D11VADevicePriv       *priv = ctx->user_opaque;
> >> +
> >> +    if (device_hwctx->video_device)
> >> +        ID3D11Device_Release(device_hwctx->video_device);
> >> +
> >> +    if (device_hwctx->video_context)
> >> +        ID3D11VideoContext_Release(device_hwctx->video_context);
> >> +
> >> +    if (priv->d3dlib)
> >> +        dlclose(priv->d3dlib);
> >> +
> >> +    av_freep(&ctx->user_opaque);
> >> +}
> >> +
> >> +static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
> >> +                                 AVDictionary *opts, int flags)
> >> +{
> >> +    AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
> >> +    ID3D11Device           *d3d11_device;
> >> +    ID3D11DeviceContext    *d3d11_context;
> >> +    ID3D10Multithread      *pMultithread;
> >> +    D3D11VADevicePriv      *priv;
> >> +
> >> +    HRESULT hr;
> >> +    PFN_D3D11_CREATE_DEVICE createD3D;
> >> +    IDXGIAdapter *pAdapter = NULL;
> >> +    UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
> >> +    /* if the DirectX SDK is installed creationFlags |= D3D11_CREATE_DEVICE_DEBUG; */
> >> +
> >> +    if (device) {
> >> +        HMODULE dxgilib = dlopen("dxgi.dll", 0);
> >> +        if (dxgilib) {
> >> +            PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
> >> +            mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) dlsym(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);
> >> +                }
> >> +            }
> >> +            dlclose(dxgilib);
> >> +        }
> >> +    }
> >> +
> >> +    priv = av_mallocz(sizeof(*priv));
> >> +    if (!priv)
> >> +        return AVERROR(ENOMEM);
> >> +
> >> +    ctx->user_opaque = priv;
> >> +    ctx->free        = d3d11va_device_free;
> >> +
> >> +    priv->d3dlib = dlopen("d3d11.dll", 0);
> >> +    if (!priv->d3dlib) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library\n");
> >> +        return AVERROR_UNKNOWN;
> >> +    }
> >> +
> >> +    createD3D = (PFN_D3D11_CREATE_DEVICE) dlsym(priv->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, &d3d11_device, NULL, &d3d11_context);
> >> +    if (FAILED(hr)) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Failed to create Direct3D device %lx\n", hr);
> >> +        return AVERROR_UNKNOWN;
> >> +    }
> >> +
> >> +    hr = ID3D11Device_QueryInterface(d3d11_device, &IID_ID3D10Multithread, (void **)&pMultithread);
> >> +    if (SUCCEEDED(hr)) {
> >> +        ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
> >> +        ID3D10Multithread_Release(pMultithread);
> >> +    }
> >> +
> >> +    hr = ID3D11DeviceContext_QueryInterface(d3d11_context, &IID_ID3D11VideoContext,
> >> +                                            (void **)&device_hwctx->video_context);
> >> +    ID3D11DeviceContext_Release(d3d11_context);
> >> +    if (FAILED(hr)) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Failed to get the Video Context %lx\n", hr);
> >> +        ID3D11Device_Release(d3d11_device);
> >> +        return AVERROR_UNKNOWN;
> >> +    }
> >> +
> >> +    hr = ID3D11Device_QueryInterface(d3d11_device, &IID_ID3D11VideoDevice,
> >> +                                     (void **)&device_hwctx->video_device);
> >> +    ID3D11Device_Release(d3d11_device);
> >> +    if (FAILED(hr)) {
> >> +        av_log(NULL, AV_LOG_ERROR, "Failed to get the Video Device %lx\n", hr);
> >> +        return AVERROR_UNKNOWN;
> >> +    }
> >> +
> >> +    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,
> >> +    .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_D3D11VA_VLD, AV_PIX_FMT_NONE },
> >> +};
> >> diff --git a/libavutil/hwcontext_d3d11va.h b/libavutil/hwcontext_d3d11va.h
> >> new file mode 100644
> >> index 0000000..e8d03f4
> >> --- /dev/null
> >> +++ b/libavutil/hwcontext_d3d11va.h
> >> @@ -0,0 +1,81 @@
> >> +/*
> >> + * This file is part of FFmpeg.
> >> + *
> >> + * FFmpeg is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU Lesser General Public
> >> + * License as published by the Free Software Foundation; either
> >> + * version 2.1 of the License, or (at your option) any later version.
> >> + *
> >> + * FFmpeg is distributed in the hope that it will be useful,
> >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> >> + * Lesser General Public License for more details.
> >> + *
> >> + * You should have received a copy of the GNU Lesser General Public
> >> + * License along with FFmpeg; if not, write to the Free Software
> >> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> >> + */
> >> +
> >> +
> >> +#ifndef AVUTIL_HWCONTEXT_D3D11VA_H
> >> +#define AVUTIL_HWCONTEXT_D3D11VA_H
> >> +
> >> +/**
> >> + * @file
> >> + * An API-specific header for AV_HWDEVICE_TYPE_D3D11VA.
> >> + *
> >> + * Only fixed-size pools are supported.
> >> + *
> >> + * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
> >> + * with the data pointer set to a pointer to ID3D11VideoDecoderOutputView.
> >> + */
> >> +
> >> +#include <d3d11.h>
> >> +
> >> +/**
> >> + * This struct is allocated as AVHWDeviceContext.hwctx
> >> + */
> >> +typedef struct AVD3D11VADeviceContext {
> >> +    ID3D11VideoDevice   *video_device;
> >> +    ID3D11VideoContext  *video_context;
> >> +} AVD3D11VADeviceContext;
> >> +
> >> +/**
> >> + * This struct is allocated as AVHWFramesContext.hwctx
> >> + */
> >> +typedef struct AVD3D11VAFramesContext {
> >> +    /**
> >> +     * The surface pool. When an external pool is not provided by the caller,
> >> +     * this will be managed (allocated and filled on init, freed on uninit) by
> >> +     * libavutil.
> >> +     * When it is provided the allocation/deallocation is up to the caller.
> >> +     */
> >> +    ID3D11VideoDecoderOutputView **surfaces;
> >> +    int                         nb_surfaces;
> >> +
> >> +    /**
> >> +     * Certain drivers require the decoder to be destroyed before the surfaces.
> >> +     * To allow internally managed pools to work properly in such cases, this
> >> +     * field is provided.
> >> +     *
> >> +     * The decoder must be created before the surface pool.
> >> +     *
> >> +     * If it is non-NULL, libavutil will call ID3D11VideoDecoder_Release() on
> >> +     * it just before the internal surface pool is freed.
> >> +     */
> >> +    ID3D11VideoDecoder  *decoder_to_release;
> >> +
> >> +    /**
> >> +     * Internal texture to get access to the decoded pixels from the CPU as the
> >> +     * decoder texture needs D3D11_BIND_DECODER which forbdis CPU access.  
> >
> > Typo: forbids
> >  
> >> +     */
> >> +    ID3D11Texture2D     *staging_texture;
> >> +
> >> +    /**
> >> +     * Internal Mutex to avoid accessing the ID3D11DeviceContext from multiple
> >> +     * threads simultaneously.
> >> +     */
> >> +    HANDLE               dev_ctx_mutex;
> >> +} AVD3D11VAFramesContext;  
> >
> > If this is internal, then why is it exposed here? Same could probably
> > be said about the staging texture.
> >
> > Also, I believe using AVMutex from libavutil/thread.h would be better,
> > because it can use fast userspace mutexes on Vista+. But probably
> > doesn't matter in this case.  
> 
> No because this mutex also needs to be used by the dxva decoder when
> using the D3D11 device/video context. And this is a public variable.
> In VLC we also have better things than a Mutex but since it's shared
> code with libavcodec we can't really have some advanced common ground.

OK, if the lavc API also uses a HANDLE mutex, fine I guess.

The doxygen shouldn't call it "internal" then, though.
diff mbox

Patch

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 9841645..9b8ce22 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -34,6 +34,7 @@  HEADERS = adler32.h                                                     \
           hmac.h                                                        \
           hwcontext.h                                                   \
           hwcontext_cuda.h                                              \
+          hwcontext_d3d11va.h                                           \
           hwcontext_dxva2.h                                             \
           hwcontext_qsv.h                                               \
           hwcontext_vaapi.h                                             \
@@ -156,6 +157,7 @@  OBJS = adler32.o                                                        \
 OBJS-$(!HAVE_ATOMICS_NATIVE)            += atomic.o                     \
 
 OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
+OBJS-$(CONFIG_D3D11VA)                  += hwcontext_d3d11va.o
 OBJS-$(CONFIG_DXVA2)                    += hwcontext_dxva2.o
 OBJS-$(CONFIG_QSV)                   += hwcontext_qsv.o
 OBJS-$(CONFIG_LZO)                      += lzo.o
@@ -170,6 +172,7 @@  SLIBOBJS-$(HAVE_GNU_WINDRES)            += avutilres.o
 
 SKIPHEADERS-$(HAVE_CUDA_H)             += hwcontext_cuda.h
 SKIPHEADERS-$(CONFIG_CUDA)             += hwcontext_cuda_internal.h
+SKIPHEADERS-$(CONFIG_D3D11VA)          += hwcontext_d3d11va.h
 SKIPHEADERS-$(CONFIG_DXVA2)            += hwcontext_dxva2.h
 SKIPHEADERS-$(CONFIG_QSV)           += hwcontext_qsv.h
 SKIPHEADERS-$(CONFIG_VAAPI)            += hwcontext_vaapi.h
diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
index 615f1f7..a9db84b 100644
--- a/libavutil/hwcontext.c
+++ b/libavutil/hwcontext.c
@@ -32,6 +32,9 @@  static const HWContextType *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
diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
index 785da09..1325917 100644
--- a/libavutil/hwcontext.h
+++ b/libavutil/hwcontext.h
@@ -30,6 +30,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 0000000..0618da3
--- /dev/null
+++ b/libavutil/hwcontext_d3d11va.c
@@ -0,0 +1,457 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <windows.h>
+
+#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+#define COBJMACROS
+
+#include <initguid.h>
+#include <d3d11.h>
+#include <dxgi1_2.h>
+
+#include "avassert.h"
+#include "common.h"
+#include "hwcontext.h"
+#include "hwcontext_d3d11va.h"
+#include "hwcontext_internal.h"
+#include "imgutils.h"
+#include "pixdesc.h"
+#include "pixfmt.h"
+#include "compat/w32dlfcn.h"
+
+typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
+
+typedef struct D3D11VAFramesContext {
+    ID3D11VideoDecoderOutputView **surfaces_internal;
+    int              nb_surfaces_used;
+
+    ID3D11DeviceContext *d3d11_context;
+
+    DXGI_FORMAT format;
+} D3D11VAFramesContext;
+
+typedef struct D3D11VADevicePriv {
+    HMODULE d3dlib;
+} D3D11VADevicePriv;
+
+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_frames_uninit(AVHWFramesContext *ctx)
+{
+    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
+    D3D11VAFramesContext *s = ctx->internal->priv;
+
+    if (frames_hwctx->decoder_to_release)
+        ID3D11VideoDecoder_Release(frames_hwctx->decoder_to_release);
+
+    av_freep(&s->surfaces_internal);
+
+    if (frames_hwctx->staging_texture)
+        ID3D11Texture2D_Release(frames_hwctx->staging_texture);
+
+    if (s->d3d11_context) {
+        ID3D11DeviceContext_Release(s->d3d11_context);
+        s->d3d11_context = NULL;
+    }
+
+    if (frames_hwctx->dev_ctx_mutex != INVALID_HANDLE_VALUE)
+        ReleaseMutex(frames_hwctx->dev_ctx_mutex);
+}
+
+static void free_surface(void *opaque, uint8_t *data)
+{
+    ID3D11VideoDecoderOutputView_Release((ID3D11VideoDecoderOutputView*)data);
+}
+
+static AVBufferRef *d3d11va_pool_alloc(void *opaque, int size)
+{
+    AVHWFramesContext        *ctx = (AVHWFramesContext*)opaque;
+    D3D11VAFramesContext       *s = ctx->internal->priv;
+    AVD3D11VAFramesContext *hwctx = ctx->hwctx;
+
+    if (s->nb_surfaces_used < hwctx->nb_surfaces) {
+        s->nb_surfaces_used++;
+        return av_buffer_create((uint8_t*)s->surfaces_internal[s->nb_surfaces_used - 1],
+                                sizeof(*hwctx->surfaces), free_surface, 0, 0);
+    }
+
+    return NULL;
+}
+
+static int d3d11va_init_pool(AVHWFramesContext *ctx)
+{
+    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
+    AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
+    D3D11VAFramesContext              *s = ctx->internal->priv;
+
+    int i;
+    HRESULT hr;
+    D3D11_TEXTURE2D_DESC texDesc;
+    ID3D11Texture2D *p_texture;
+    D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
+    D3D11_VIDEO_DECODER_DESC decoderDesc;
+    D3D11_VIDEO_DECODER_CONFIG decoderConfig;
+    ID3D11Device *d3d11_device;
+
+    if (ctx->initial_pool_size <= 0)
+        return 0;
+
+    hr = ID3D11VideoContext_QueryInterface(device_hwctx->video_context, &IID_ID3D11DeviceContext,
+                                           (void **)&s->d3d11_context);
+    if (FAILED(hr)) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to get the device context %lx\n", hr);
+        return AVERROR_UNKNOWN;
+    }
+
+    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);
+    }
+
+    s->surfaces_internal = av_mallocz_array(ctx->initial_pool_size,
+                                            sizeof(*s->surfaces_internal));
+    if (!s->surfaces_internal)
+        return AVERROR(ENOMEM);
+
+    ZeroMemory(&texDesc, sizeof(texDesc));
+    texDesc.Width = ctx->width;
+    texDesc.Height = ctx->height;
+    texDesc.MipLevels = 1;
+    texDesc.Format = s->format;
+    texDesc.SampleDesc.Count = 1;
+    texDesc.ArraySize = ctx->initial_pool_size;
+    texDesc.Usage = D3D11_USAGE_DEFAULT;
+    texDesc.BindFlags = D3D11_BIND_DECODER;
+
+    frames_hwctx->dev_ctx_mutex = CreateMutex(NULL, 0, NULL);
+    if (frames_hwctx->dev_ctx_mutex == INVALID_HANDLE_VALUE) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to get a mutex for the D3D11VA decoder\n");
+        return AVERROR(EINVAL);
+    }
+
+    hr = ID3D11VideoDevice_QueryInterface(device_hwctx->video_device, &IID_ID3D11Device, (void **)&d3d11_device);
+    if (FAILED(hr)) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to get the device %lx\n", hr);
+        return AVERROR_UNKNOWN;
+    }
+
+    hr = ID3D11Device_CreateTexture2D(d3d11_device, &texDesc, NULL, &p_texture);
+    if (FAILED(hr)) {
+        av_log(ctx, AV_LOG_ERROR, "Could not create the texture %lx\n", hr);
+        ID3D11Device_Release(d3d11_device);
+        return AVERROR_UNKNOWN;
+    }
+
+    hr = ID3D11VideoDecoder_GetCreationParameters(frames_hwctx->decoder_to_release, &decoderDesc, &decoderConfig);
+    if (FAILED(hr)) {
+        av_log(ctx, AV_LOG_ERROR, "Could not get the decoder config %lx\n", hr);
+        ID3D11Texture2D_Release(p_texture);
+        ID3D11Device_Release(d3d11_device);
+        return AVERROR_UNKNOWN;
+    }
+
+    ZeroMemory(&viewDesc, sizeof(viewDesc));
+    viewDesc.DecodeProfile = decoderDesc.Guid;
+    viewDesc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
+    for (i=0; i<ctx->initial_pool_size; i++)
+    {
+        hr = ID3D11VideoDevice_CreateVideoDecoderOutputView(device_hwctx->video_device,
+                                                            (ID3D11Resource*) p_texture,
+                                                            &viewDesc,
+                                                            (ID3D11VideoDecoderOutputView**) &s->surfaces_internal[i]);
+        if (FAILED(hr)) {
+            av_log(ctx, AV_LOG_ERROR, "Could not create the decoder output %d\n", i);
+            while (--i >= 0) {
+                ID3D11VideoDecoderOutputView_Release(s->surfaces_internal[i]);
+                s->surfaces_internal[i] = NULL;
+            }
+            ID3D11Texture2D_Release(p_texture);
+            ID3D11Device_Release(d3d11_device);
+            return AVERROR_UNKNOWN;
+        }
+    }
+    ID3D11Texture2D_Release(p_texture);
+
+    texDesc.ArraySize = 1;
+    texDesc.Usage = D3D11_USAGE_STAGING;
+    texDesc.BindFlags = 0;
+    texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
+    hr = ID3D11Device_CreateTexture2D(d3d11_device, &texDesc, NULL, &frames_hwctx->staging_texture);
+    ID3D11Device_Release(d3d11_device);
+    if (FAILED(hr)) {
+        av_log(ctx, AV_LOG_ERROR, "Could not create the staging texture %lx\n", hr);
+        return AVERROR_UNKNOWN;
+    }
+
+    ctx->internal->pool_internal = av_buffer_pool_init2(sizeof(*s->surfaces_internal),
+                                                        ctx, d3d11va_pool_alloc, NULL);
+    if (!ctx->internal->pool_internal)
+        return AVERROR(ENOMEM);
+
+    frames_hwctx->surfaces    = s->surfaces_internal;
+    frames_hwctx->nb_surfaces = ctx->initial_pool_size;
+
+    return 0;
+}
+
+static int d3d11va_frames_init(AVHWFramesContext *ctx)
+{
+    int ret;
+
+    /* init the frame pool if the caller didn't provide one */
+    if (!ctx->pool) {
+        ret = d3d11va_init_pool(ctx);
+        if (ret < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Error creating an internal frame pool\n");
+            return ret;
+        }
+    }
+
+    return 0;
+}
+
+static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
+{
+    frame->buf[0] = av_buffer_pool_get(ctx->pool);
+    if (!frame->buf[0])
+        return AVERROR(ENOMEM);
+
+    frame->data[3] = frame->buf[0]->data;
+    frame->format  = AV_PIX_FMT_D3D11VA_VLD;
+    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 int d3d11va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
+                                 const AVFrame *src)
+{
+    ID3D11VideoDecoderOutputView *surface;
+    D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC    surfaceDesc;
+    D3D11_TEXTURE2D_DESC dstDesc;
+    D3D11_MAPPED_SUBRESOURCE     LockedRect;
+    ID3D11Resource *pTexture;
+    HRESULT            hr;
+    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
+    D3D11VAFramesContext              *s = ctx->internal->priv;
+
+    uint8_t *surf_data[4]     = { NULL };
+    int      surf_linesize[4] = { 0 };
+    int i;
+
+    int download = !!src->hw_frames_ctx;
+
+    surface = (ID3D11VideoDecoderOutputView*)(download ? src->data[3] : dst->data[3]);
+
+    ID3D11VideoDecoderOutputView_GetDesc(surface, &surfaceDesc);
+    ID3D11VideoDecoderOutputView_GetResource(surface, &pTexture);
+    ID3D11Texture2D_GetDesc(frames_hwctx->staging_texture, &dstDesc);
+
+    WaitForSingleObjectEx(frames_hwctx->dev_ctx_mutex, INFINITE, FALSE);
+
+    ID3D11DeviceContext_CopySubresourceRegion(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture,
+                                              0, 0, 0, 0,
+                                              (ID3D11Resource*)pTexture, surfaceDesc.Texture2D.ArraySlice,
+                                              NULL);
+    ID3D11Resource_Release(pTexture);
+
+    hr = ID3D11DeviceContext_Map(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture,
+                                 0, download ? D3D11_MAP_READ : D3D11_MAP_WRITE, 0, &LockedRect);
+    if (FAILED(hr)) {
+        av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface %lx\n", hr);
+        ReleaseMutex(frames_hwctx->dev_ctx_mutex);
+        return AVERROR_UNKNOWN;
+    }
+
+    for (i = 0; download ? dst->data[i] : src->data[i]; i++)
+        surf_linesize[i] = LockedRect.RowPitch;
+
+    av_image_fill_pointers(surf_data, ctx->sw_format, dstDesc.Height,
+                           (uint8_t*)LockedRect.pData, surf_linesize);
+
+    if (download) {
+        av_image_copy(dst->data, dst->linesize, surf_data, surf_linesize,
+                      ctx->sw_format, src->width, src->height);
+    } else {
+        av_image_copy(surf_data, surf_linesize, src->data, src->linesize,
+                      ctx->sw_format, src->width, src->height);
+    }
+
+    ID3D11DeviceContext_Unmap(s->d3d11_context, (ID3D11Resource*)frames_hwctx->staging_texture, 0);
+    ReleaseMutex(frames_hwctx->dev_ctx_mutex);
+
+    return 0;
+}
+
+static void d3d11va_device_free(AVHWDeviceContext *ctx)
+{
+    AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
+    D3D11VADevicePriv       *priv = ctx->user_opaque;
+
+    if (device_hwctx->video_device)
+        ID3D11Device_Release(device_hwctx->video_device);
+
+    if (device_hwctx->video_context)
+        ID3D11VideoContext_Release(device_hwctx->video_context);
+
+    if (priv->d3dlib)
+        dlclose(priv->d3dlib);
+
+    av_freep(&ctx->user_opaque);
+}
+
+static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
+                                 AVDictionary *opts, int flags)
+{
+    AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
+    ID3D11Device           *d3d11_device;
+    ID3D11DeviceContext    *d3d11_context;
+    ID3D10Multithread      *pMultithread;
+    D3D11VADevicePriv      *priv;
+
+    HRESULT hr;
+    PFN_D3D11_CREATE_DEVICE createD3D;
+    IDXGIAdapter *pAdapter = NULL;
+    UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
+    /* if the DirectX SDK is installed creationFlags |= D3D11_CREATE_DEVICE_DEBUG; */
+
+    if (device) {
+        HMODULE dxgilib = dlopen("dxgi.dll", 0);
+        if (dxgilib) {
+            PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
+            mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) dlsym(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);
+                }
+            }
+            dlclose(dxgilib);
+        }
+    }
+
+    priv = av_mallocz(sizeof(*priv));
+    if (!priv)
+        return AVERROR(ENOMEM);
+
+    ctx->user_opaque = priv;
+    ctx->free        = d3d11va_device_free;
+
+    priv->d3dlib = dlopen("d3d11.dll", 0);
+    if (!priv->d3dlib) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library\n");
+        return AVERROR_UNKNOWN;
+    }
+
+    createD3D = (PFN_D3D11_CREATE_DEVICE) dlsym(priv->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, &d3d11_device, NULL, &d3d11_context);
+    if (FAILED(hr)) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to create Direct3D device %lx\n", hr);
+        return AVERROR_UNKNOWN;
+    }
+
+    hr = ID3D11Device_QueryInterface(d3d11_device, &IID_ID3D10Multithread, (void **)&pMultithread);
+    if (SUCCEEDED(hr)) {
+        ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
+        ID3D10Multithread_Release(pMultithread);
+    }
+
+    hr = ID3D11DeviceContext_QueryInterface(d3d11_context, &IID_ID3D11VideoContext,
+                                            (void **)&device_hwctx->video_context);
+    ID3D11DeviceContext_Release(d3d11_context);
+    if (FAILED(hr)) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to get the Video Context %lx\n", hr);
+        ID3D11Device_Release(d3d11_device);
+        return AVERROR_UNKNOWN;
+    }
+
+    hr = ID3D11Device_QueryInterface(d3d11_device, &IID_ID3D11VideoDevice,
+                                     (void **)&device_hwctx->video_device);
+    ID3D11Device_Release(d3d11_device);
+    if (FAILED(hr)) {
+        av_log(NULL, AV_LOG_ERROR, "Failed to get the Video Device %lx\n", hr);
+        return AVERROR_UNKNOWN;
+    }
+
+    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,
+    .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_D3D11VA_VLD, AV_PIX_FMT_NONE },
+};
diff --git a/libavutil/hwcontext_d3d11va.h b/libavutil/hwcontext_d3d11va.h
new file mode 100644
index 0000000..e8d03f4
--- /dev/null
+++ b/libavutil/hwcontext_d3d11va.h
@@ -0,0 +1,81 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#ifndef AVUTIL_HWCONTEXT_D3D11VA_H
+#define AVUTIL_HWCONTEXT_D3D11VA_H
+
+/**
+ * @file
+ * An API-specific header for AV_HWDEVICE_TYPE_D3D11VA.
+ *
+ * Only fixed-size pools are supported.
+ *
+ * For user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
+ * with the data pointer set to a pointer to ID3D11VideoDecoderOutputView.
+ */
+
+#include <d3d11.h>
+
+/**
+ * This struct is allocated as AVHWDeviceContext.hwctx
+ */
+typedef struct AVD3D11VADeviceContext {
+    ID3D11VideoDevice   *video_device;
+    ID3D11VideoContext  *video_context;
+} AVD3D11VADeviceContext;
+
+/**
+ * This struct is allocated as AVHWFramesContext.hwctx
+ */
+typedef struct AVD3D11VAFramesContext {
+    /**
+     * The surface pool. When an external pool is not provided by the caller,
+     * this will be managed (allocated and filled on init, freed on uninit) by
+     * libavutil.
+     * When it is provided the allocation/deallocation is up to the caller.
+     */
+    ID3D11VideoDecoderOutputView **surfaces;
+    int                         nb_surfaces;
+
+    /**
+     * Certain drivers require the decoder to be destroyed before the surfaces.
+     * To allow internally managed pools to work properly in such cases, this
+     * field is provided.
+     *
+     * The decoder must be created before the surface pool.
+     *
+     * If it is non-NULL, libavutil will call ID3D11VideoDecoder_Release() on
+     * it just before the internal surface pool is freed.
+     */
+    ID3D11VideoDecoder  *decoder_to_release;
+    
+    /**
+     * Internal texture to get access to the decoded pixels from the CPU as the
+     * decoder texture needs D3D11_BIND_DECODER which forbdis CPU access.
+     */
+    ID3D11Texture2D     *staging_texture;
+
+    /**
+     * Internal Mutex to avoid accessing the ID3D11DeviceContext from multiple
+     * threads simultaneously.
+     */
+    HANDLE               dev_ctx_mutex;
+} AVD3D11VAFramesContext;
+
+#endif /* AVUTIL_HWCONTEXT_D3D11VA_H */
diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h
index 079e42b..aaa6286 100644
--- a/libavutil/hwcontext_internal.h
+++ b/libavutil/hwcontext_internal.h
@@ -100,6 +100,7 @@  struct AVHWFramesInternal {
 };
 
 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/version.h b/libavutil/version.h
index 9f8c4c2..6dac240 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -80,7 +80,7 @@ 
 
 #define LIBAVUTIL_VERSION_MAJOR  55
 #define LIBAVUTIL_VERSION_MINOR  43
-#define LIBAVUTIL_VERSION_MICRO 100
+#define LIBAVUTIL_VERSION_MICRO 101
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
                                                LIBAVUTIL_VERSION_MINOR, \