diff mbox

[FFmpeg-devel] ffmpeg: add ffmpeg_d3d11va

Message ID 20161213131935.17052-1-robUx4@gmail.com
State Superseded
Headers show

Commit Message

Steve Lhomme Dec. 13, 2016, 1:19 p.m. UTC
From: Steve Lhomme <robux4@videolabs.io>

The code is similar to ffmpeg_dxva2. The decoded output needs to be copied into
a staging texture that can be accessed by the CPU as the decoder texture can't
be accessed by the CPU.
---
 Makefile                       |   1 +
 configure                      |  14 ++
 ffmpeg.h                       |   2 +
 ffmpeg_d3d11va.c               | 416 +++++++++++++++++++++++++++++++++++++++
 ffmpeg_opt.c                   |   3 +
 libavutil/Makefile             |   3 +
 libavutil/hwcontext.c          |   3 +
 libavutil/hwcontext.h          |   1 +
 libavutil/hwcontext_d3d11va.c  | 436 +++++++++++++++++++++++++++++++++++++++++
 libavutil/hwcontext_d3d11va.h  |  74 +++++++
 libavutil/hwcontext_internal.h |   1 +
 libavutil/version.h            |   2 +-
 12 files changed, 955 insertions(+), 1 deletion(-)
 create mode 100644 ffmpeg_d3d11va.c
 create mode 100644 libavutil/hwcontext_d3d11va.c
 create mode 100644 libavutil/hwcontext_d3d11va.h

Comments

wm4 Dec. 13, 2016, 1:35 p.m. UTC | #1
On Tue, 13 Dec 2016 14:19:35 +0100
Steve Lhomme <robux4@gmail.com> wrote:

> From: Steve Lhomme <robux4@videolabs.io>
> 
> The code is similar to ffmpeg_dxva2. The decoded output needs to be copied into
> a staging texture that can be accessed by the CPU as the decoder texture can't
> be accessed by the CPU.
> ---
>  Makefile                       |   1 +
>  configure                      |  14 ++
>  ffmpeg.h                       |   2 +
>  ffmpeg_d3d11va.c               | 416 +++++++++++++++++++++++++++++++++++++++
>  ffmpeg_opt.c                   |   3 +
>  libavutil/Makefile             |   3 +
>  libavutil/hwcontext.c          |   3 +
>  libavutil/hwcontext.h          |   1 +
>  libavutil/hwcontext_d3d11va.c  | 436 +++++++++++++++++++++++++++++++++++++++++
>  libavutil/hwcontext_d3d11va.h  |  74 +++++++
>  libavutil/hwcontext_internal.h |   1 +
>  libavutil/version.h            |   2 +-
>  12 files changed, 955 insertions(+), 1 deletion(-)
>  create mode 100644 ffmpeg_d3d11va.c
>  create mode 100644 libavutil/hwcontext_d3d11va.c
>  create mode 100644 libavutil/hwcontext_d3d11va.h
> 
> diff --git a/Makefile b/Makefile
> index 8aa72fd..a48d471 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -38,6 +38,7 @@ OBJS-ffmpeg-$(CONFIG_VDA)     += ffmpeg_videotoolbox.o
>  endif
>  OBJS-ffmpeg-$(CONFIG_CUVID)   += ffmpeg_cuvid.o
>  OBJS-ffmpeg-$(HAVE_DXVA2_LIB) += ffmpeg_dxva2.o
> +OBJS-ffmpeg-$(HAVE_D3D11VA_LIB) += ffmpeg_d3d11va.o
>  OBJS-ffmpeg-$(HAVE_VDPAU_X11) += ffmpeg_vdpau.o
>  OBJS-ffserver                 += ffserver_config.o
>  
> diff --git a/configure b/configure
> index 9dfd006..dea8339 100755
> --- a/configure
> +++ b/configure
> @@ -2050,6 +2050,8 @@ HAVE_LIST="
>      $TYPES_LIST
>      atomics_native
>      dos_paths
> +    d3d11va_lib
> +    d3d11vaapi_cobj
>      dxva2_lib
>      dxva2api_cobj
>      libc_msvcrt
> @@ -6037,6 +6039,15 @@ enabled dxva2api_h &&
>  int main(void) { IDirectXVideoDecoder *o = NULL; IDirectXVideoDecoder_Release(o); return 0; }
>  EOF
>  
> +enabled d3d11_h &&
> +    check_cc <<EOF && enable d3d11vaapi_cobj
> +#define _WIN32_WINNT 0x0600
> +#define COBJMACROS
> +#include <windows.h>
> +#include <d3d11.h>
> +int main(void) { ID3D11VideoDevice *o = NULL; ID3D11VideoDevice_Release(o); return 0; }
> +EOF
> +
>  enabled vaapi &&
>      check_lib va/va.h vaInitialize -lva ||
>      disable vaapi
> @@ -6368,6 +6379,9 @@ if test $target_os = "haiku"; then
>      disable posix_memalign
>  fi
>  
> +enabled_all d3d11va d3d11vaapi_cobj &&
> +    enable d3d11va_lib
> +
>  enabled_all dxva2 dxva2api_cobj CoTaskMemFree &&
>      prepend ffmpeg_libs $($ldflags_filter "-lole32") &&
>      enable dxva2_lib
> diff --git a/ffmpeg.h b/ffmpeg.h
> index ebe5bf0..a12701e 100644
> --- a/ffmpeg.h
> +++ b/ffmpeg.h
> @@ -61,6 +61,7 @@ enum HWAccelID {
>      HWACCEL_NONE = 0,
>      HWACCEL_AUTO,
>      HWACCEL_VDPAU,
> +    HWACCEL_D3D11VA,
>      HWACCEL_DXVA2,
>      HWACCEL_VDA,
>      HWACCEL_VIDEOTOOLBOX,
> @@ -633,6 +634,7 @@ int ifilter_parameters_from_decoder(InputFilter *ifilter, const AVCodecContext *
>  int ffmpeg_parse_options(int argc, char **argv);
>  
>  int vdpau_init(AVCodecContext *s);
> +int d3d11va_init(AVCodecContext *s);
>  int dxva2_init(AVCodecContext *s);
>  int vda_init(AVCodecContext *s);
>  int videotoolbox_init(AVCodecContext *s);
> diff --git a/ffmpeg_d3d11va.c b/ffmpeg_d3d11va.c
> new file mode 100644
> index 0000000..f6f8186
> --- /dev/null
> +++ b/ffmpeg_d3d11va.c
> @@ -0,0 +1,416 @@
> +/*
> + * 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 <stdint.h>
> +
> +#include <d3d11.h>
> +
> +#include "ffmpeg.h"
> +
> +#include "libavcodec/d3d11va.h"
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/buffer.h"
> +#include "libavutil/frame.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/pixfmt.h"
> +
> +#include "libavutil/hwcontext.h"
> +#include "libavutil/hwcontext_d3d11va.h"
> +
> +/* define all the GUIDs used directly here,
> +   to avoid problems with inconsistent dxva2api.h versions in mingw-w64 and different MSVC version */
> +#include <initguid.h>
> +DEFINE_GUID(DXVA2_ModeMPEG2_VLD,      0xee27417f, 0x5e28,0x4e65,0xbe,0xea,0x1d,0x26,0xb5,0x08,0xad,0xc9);
> +DEFINE_GUID(DXVA2_ModeMPEG2and1_VLD,  0x86695f12, 0x340e,0x4f04,0x9f,0xd3,0x92,0x53,0xdd,0x32,0x74,0x60);
> +DEFINE_GUID(DXVA2_ModeH264_E,         0x1b81be68, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
> +DEFINE_GUID(DXVA2_ModeH264_F,         0x1b81be69, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
> +DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68, 0x4951,0x4C54,0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6);
> +DEFINE_GUID(DXVA2_ModeVC1_D,          0x1b81beA3, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
> +DEFINE_GUID(DXVA2_ModeVC1_D2010,      0x1b81beA4, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
> +DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main,  0x5b11d51b, 0x2f4c,0x4452,0xbc,0xc3,0x09,0xf2,0xa1,0x16,0x0c,0xc0);
> +DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main10,0x107af0e0, 0xef1a,0x4d19,0xab,0xa8,0x67,0xa1,0x63,0x07,0x3d,0x13);
> +DEFINE_GUID(DXVA2_ModeVP9_VLD_Profile0, 0x463707f8, 0xa1d0,0x4585,0x87,0x6d,0x83,0xaa,0x6d,0x60,0xb8,0x9e);
> +DEFINE_GUID(DXVA2_NoEncrypt,          0x1b81beD0, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
> +
> +typedef struct d3d11va_mode {
> +  const GUID     *guid;
> +  enum AVCodecID codec;
> +} d3d11va_mode;
> +
> +static const d3d11va_mode d3d11va_modes[] = {
> +    /* MPEG-2 */
> +    { &DXVA2_ModeMPEG2_VLD,      AV_CODEC_ID_MPEG2VIDEO },
> +    { &DXVA2_ModeMPEG2and1_VLD,  AV_CODEC_ID_MPEG2VIDEO },
> +
> +    /* H.264 */
> +    { &DXVA2_ModeH264_F,         AV_CODEC_ID_H264 },
> +    { &DXVA2_ModeH264_E,         AV_CODEC_ID_H264 },
> +    /* Intel specific H.264 mode */
> +    { &DXVADDI_Intel_ModeH264_E, AV_CODEC_ID_H264 },
> +
> +    /* VC-1 / WMV3 */
> +    { &DXVA2_ModeVC1_D2010,      AV_CODEC_ID_VC1  },
> +    { &DXVA2_ModeVC1_D2010,      AV_CODEC_ID_WMV3 },
> +    { &DXVA2_ModeVC1_D,          AV_CODEC_ID_VC1  },
> +    { &DXVA2_ModeVC1_D,          AV_CODEC_ID_WMV3 },
> +
> +    /* HEVC/H.265 */
> +    { &DXVA2_ModeHEVC_VLD_Main,  AV_CODEC_ID_HEVC },
> +    { &DXVA2_ModeHEVC_VLD_Main10,AV_CODEC_ID_HEVC },
> +
> +    /* VP8/9 */
> +    { &DXVA2_ModeVP9_VLD_Profile0, AV_CODEC_ID_VP9 },
> +
> +    { NULL,                      0 },
> +};
> +
> +typedef struct D3D11VAContext {
> +    D3D11_VIDEO_DECODER_CONFIG   decoder_config;
> +
> +    AVFrame                     *tmp_frame;
> +
> +    AVBufferRef                 *hw_device_ctx;
> +    AVBufferRef                 *hw_frames_ctx;
> +} D3D11VAContext;
> +
> +static void d3d11va_uninit(AVCodecContext *s)
> +{
> +    InputStream  *ist = s->opaque;
> +    D3D11VAContext *ctx = ist->hwaccel_ctx;
> +
> +    ist->hwaccel_uninit        = NULL;
> +    ist->hwaccel_get_buffer    = NULL;
> +    ist->hwaccel_retrieve_data = NULL;
> +
> +    av_buffer_unref(&ctx->hw_frames_ctx);
> +    av_buffer_unref(&ctx->hw_device_ctx);
> +
> +    av_frame_free(&ctx->tmp_frame);
> +
> +    av_freep(&ist->hwaccel_ctx);
> +    av_freep(&s->hwaccel_context);
> +}
> +
> +static int d3d11va_get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
> +{
> +    InputStream  *ist = s->opaque;
> +    D3D11VAContext *ctx = ist->hwaccel_ctx;
> +
> +    return av_hwframe_get_buffer(ctx->hw_frames_ctx, frame, 0);
> +}
> +
> +static int d3d11va_retrieve_data(AVCodecContext *s, AVFrame *frame)
> +{
> +    InputStream        *ist = s->opaque;
> +    D3D11VAContext     *ctx = ist->hwaccel_ctx;
> +    int                ret;
> +
> +    ret = av_hwframe_transfer_data(ctx->tmp_frame, frame, 0);
> +    if (ret < 0)
> +        return ret;
> +
> +    ret = av_frame_copy_props(ctx->tmp_frame, frame);
> +    if (ret < 0) {
> +        av_frame_unref(ctx->tmp_frame);
> +        return ret;
> +    }
> +
> +    av_frame_unref(frame);
> +    av_frame_move_ref(frame, ctx->tmp_frame);
> +
> +    return 0;
> +}
> +
> +static int d3d11va_alloc(AVCodecContext *s)
> +{
> +    InputStream  *ist = s->opaque;
> +    D3D11VAContext *ctx;
> +
> +    int ret;
> +
> +    ctx = av_mallocz(sizeof(*ctx));
> +    if (!ctx)
> +        return AVERROR(ENOMEM);
> +
> +    ist->hwaccel_ctx           = ctx;
> +    ist->hwaccel_uninit        = d3d11va_uninit;
> +    ist->hwaccel_get_buffer    = d3d11va_get_buffer;
> +    ist->hwaccel_retrieve_data = d3d11va_retrieve_data;
> +
> +    ret = av_hwdevice_ctx_create(&ctx->hw_device_ctx, AV_HWDEVICE_TYPE_D3D11VA,
> +                                 ist->hwaccel_device, NULL, 0);
> +    if (ret < 0)
> +        goto fail;
> +
> +    ctx->tmp_frame = av_frame_alloc();
> +    if (!ctx->tmp_frame)
> +        goto fail;
> +
> +    s->hwaccel_context = av_mallocz(sizeof(AVD3D11VAContext));
> +    if (!s->hwaccel_context)
> +        goto fail;
> +
> +    return 0;
> +fail:
> +    d3d11va_uninit(s);
> +    return AVERROR(EINVAL);
> +}
> +
> +static int d3d11va_get_decoder_configuration(AVCodecContext *s,
> +                                           const D3D11_VIDEO_DECODER_DESC *desc,
> +                                           D3D11_VIDEO_DECODER_CONFIG *config)
> +{
> +    InputStream  *ist = s->opaque;
> +    int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
> +    D3D11VAContext *ctx = ist->hwaccel_ctx;
> +    unsigned cfg_count = 0, best_score = 0;
> +    D3D11_VIDEO_DECODER_CONFIG best_cfg = {{0}};
> +    HRESULT hr;
> +    int i;
> +    AVHWDeviceContext    *device_ctx = (AVHWDeviceContext*)ctx->hw_device_ctx->data;
> +    AVD3D11VADeviceContext *device_hwctx = device_ctx->hwctx;
> +
> +    hr = ID3D11VideoDevice_GetVideoDecoderConfigCount(device_hwctx->video_device, desc, &cfg_count);
> +    if (FAILED(hr)) {
> +        av_log(NULL, loglevel, "Unable to retrieve decoder configurations\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    for (i = 0; i < cfg_count; i++) {
> +        unsigned score;
> +        D3D11_VIDEO_DECODER_CONFIG cfg;
> +        hr = ID3D11VideoDevice_GetVideoDecoderConfig(device_hwctx->video_device, desc, i, &cfg );
> +        if (FAILED(hr)) {
> +            av_log(NULL, loglevel, "Unable to retrieve decoder configurations. (hr=0x%lX)\n", hr);
> +            return AVERROR(EINVAL);
> +        }
> +
> +        if (cfg.ConfigBitstreamRaw == 1)
> +            score = 1;
> +        else if (s->codec_id == AV_CODEC_ID_H264 && cfg.ConfigBitstreamRaw == 2)
> +            score = 2;
> +        else
> +            continue;
> +        if (IsEqualGUID(&cfg.guidConfigBitstreamEncryption, &DXVA2_NoEncrypt))
> +            score += 16;
> +        if (score > best_score) {
> +            best_score = score;
> +            best_cfg   = cfg;
> +        }
> +    }
> +
> +    if (!best_score) {
> +        av_log(NULL, loglevel, "No valid decoder configuration available\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    *config = best_cfg;
> +    return 0;
> +}
> +
> +static int d3d11va_create_decoder(AVCodecContext *s)
> +{
> +    InputStream  *ist = s->opaque;
> +    int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
> +    D3D11VAContext *ctx = ist->hwaccel_ctx;
> +    AVD3D11VAContext *dxva_ctx = s->hwaccel_context;
> +    GUID *guid_list = NULL;
> +    unsigned guid_count = 0, i, j;
> +    GUID device_guid = GUID_NULL;
> +    const DXGI_FORMAT surface_format = (s->sw_pix_fmt == AV_PIX_FMT_YUV420P10) ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12;
> +    DXGI_FORMAT target_format = DXGI_FORMAT_UNKNOWN;
> +    D3D11_VIDEO_DECODER_DESC desc = { 0 };
> +    D3D11_VIDEO_DECODER_CONFIG config;
> +    HRESULT hr;
> +    int surface_alignment, num_surfaces;
> +    int ret;
> +
> +    AVD3D11VAFramesContext *frames_hwctx;
> +    AVHWFramesContext *frames_ctx;
> +
> +    AVHWDeviceContext    *device_ctx;
> +    AVD3D11VADeviceContext *device_hwctx;
> +    device_ctx = (AVHWDeviceContext*)ctx->hw_device_ctx->data;
> +    device_hwctx = device_ctx->hwctx;
> +
> +    guid_count = ID3D11VideoDevice_GetVideoDecoderProfileCount(device_hwctx->video_device);
> +    guid_list = av_malloc(sizeof(*guid_list) * guid_count);
> +    if (guid_list==NULL) {
> +        av_log(NULL, loglevel, "Failed to allocate decoder device GUIDs\n");
> +        goto fail;
> +    }
> +    for (i=0; i<guid_count; i++) {
> +        hr = ID3D11VideoDevice_GetVideoDecoderProfile(device_hwctx->video_device, i, &guid_list[i]);
> +        if (FAILED(hr)) {
> +            av_log(NULL, loglevel, "Failed to retrieve decoder GUID %d\n", i);
> +            goto fail;
> +        }
> +    }
> +
> +    for (i = 0; d3d11va_modes[i].guid; i++) {
> +        BOOL is_supported = FALSE;
> +        const d3d11va_mode *mode = &d3d11va_modes[i];
> +        if (mode->codec != s->codec_id)
> +            continue;
> +
> +        for (j = 0; j < guid_count; j++) {
> +            if (IsEqualGUID(mode->guid, &guid_list[j]))
> +                break;
> +        }
> +        if (j == guid_count)
> +            continue;
> +
> +        hr = ID3D11VideoDevice_CheckVideoDecoderFormat(device_hwctx->video_device, mode->guid,
> +                                                       surface_format, &is_supported);
> +        if (SUCCEEDED(hr) && is_supported) {
> +            target_format = surface_format;
> +            device_guid = *mode->guid;
> +            break;
> +        }
> +    }
> +
> +    if (IsEqualGUID(&device_guid, &GUID_NULL)) {
> +        av_log(NULL, loglevel, "No decoder device for codec found\n");
> +        goto fail;
> +    }
> +
> +    desc.SampleWidth  = s->coded_width;
> +    desc.SampleHeight = s->coded_height;
> +    desc.OutputFormat = target_format;
> +    desc.Guid         = device_guid;
> +
> +    ret = d3d11va_get_decoder_configuration(s, &desc, &config);
> +    if (ret < 0) {
> +        goto fail;
> +    }
> +
> +    /* decoding MPEG-2 requires additional alignment on some Intel GPUs,
> +       but it causes issues for H.264 on certain AMD GPUs..... */
> +    if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO)
> +        surface_alignment = 32;
> +    /* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure
> +       all coding features have enough room to work with */
> +    else if  (s->codec_id == AV_CODEC_ID_HEVC)
> +        surface_alignment = 128;
> +    else
> +        surface_alignment = 16;
> +
> +    /* 4 base work surfaces */
> +    num_surfaces = 4;
> +
> +    /* add surfaces based on number of possible refs */
> +    if (s->codec_id == AV_CODEC_ID_H264 || s->codec_id == AV_CODEC_ID_HEVC)
> +        num_surfaces += 16;
> +    else if (s->codec_id == AV_CODEC_ID_VP9)
> +        num_surfaces += 8;
> +    else
> +        num_surfaces += 2;
> +
> +    /* add extra surfaces for frame threading */
> +    if (s->active_thread_type & FF_THREAD_FRAME)
> +        num_surfaces += s->thread_count;
> +
> +    ctx->hw_frames_ctx = av_hwframe_ctx_alloc(ctx->hw_device_ctx);
> +    if (!ctx->hw_frames_ctx)
> +        goto fail;
> +    frames_ctx   = (AVHWFramesContext*)ctx->hw_frames_ctx->data;
> +    frames_hwctx = frames_ctx->hwctx;
> +    
> +    frames_ctx->format            = AV_PIX_FMT_D3D11VA_VLD;
> +    frames_ctx->sw_format         = (target_format == DXGI_FORMAT_P010 ? AV_PIX_FMT_P010 : AV_PIX_FMT_NV12);
> +    frames_ctx->width             = FFALIGN(s->coded_width, surface_alignment);
> +    frames_ctx->height            = FFALIGN(s->coded_height, surface_alignment);
> +    frames_ctx->initial_pool_size = num_surfaces;
> +
> +    hr = ID3D11VideoDevice_CreateVideoDecoder(device_hwctx->video_device, &desc,
> +                                              &config, &frames_hwctx->decoder_to_release);
> +    if (FAILED(hr)) {
> +        av_log(NULL, loglevel, "Failed to create D3D11VA video decoder\n");
> +        goto fail;
> +    }
> +
> +    ret = av_hwframe_ctx_init(ctx->hw_frames_ctx);
> +    if (ret < 0) {
> +        av_log(NULL, loglevel, "Failed to initialize the HW frames context\n");
> +        goto fail;
> +    }
> +
> +    ctx->decoder_config = config;
> +
> +    dxva_ctx->cfg           = &ctx->decoder_config;
> +    dxva_ctx->decoder       = frames_hwctx->decoder_to_release;
> +    dxva_ctx->surface       = frames_hwctx->surfaces;
> +    dxva_ctx->surface_count = frames_hwctx->nb_surfaces;
> +    dxva_ctx->video_context = device_hwctx->video_context;
> +
> +    if (IsEqualGUID(&device_guid, &DXVADDI_Intel_ModeH264_E))
> +        dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO;
> +
> +    av_free(guid_list);
> +    return 0;
> +fail:
> +    av_free(guid_list);
> +    av_buffer_unref(&ctx->hw_frames_ctx);
> +    return AVERROR(EINVAL);
> +}
> +
> +int d3d11va_init(AVCodecContext *s)
> +{
> +    InputStream *ist = s->opaque;
> +    int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
> +    D3D11VAContext *ctx;
> +    int ret;
> +
> +    if (!ist->hwaccel_ctx) {
> +        ret = d3d11va_alloc(s);
> +        if (ret < 0)
> +            return ret;
> +    }
> +    ctx = ist->hwaccel_ctx;
> +
> +    if (s->codec_id == AV_CODEC_ID_H264 &&
> +        (s->profile & ~FF_PROFILE_H264_CONSTRAINED) > FF_PROFILE_H264_HIGH) {
> +        av_log(NULL, loglevel, "Unsupported H.264 profile for D3D11VA HWAccel: %d\n", s->profile);
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (s->codec_id == AV_CODEC_ID_HEVC &&
> +        s->profile != FF_PROFILE_HEVC_MAIN && s->profile != FF_PROFILE_HEVC_MAIN_10) {
> +        av_log(NULL, loglevel, "Unsupported HEVC profile for D3D11VA HWAccel: %d\n", s->profile);
> +        return AVERROR(EINVAL);
> +    }
> +
> +    av_buffer_unref(&ctx->hw_frames_ctx);
> +
> +    ret = d3d11va_create_decoder(s);
> +    if (ret < 0) {
> +        av_log(NULL, loglevel, "Error creating the D3D11VA decoder\n");
> +        return ret;
> +    }
> +
> +    return 0;
> +}
> diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c
> index 6862456..9cc1a55 100644
> --- a/ffmpeg_opt.c
> +++ b/ffmpeg_opt.c
> @@ -69,6 +69,9 @@ const HWAccel hwaccels[] = {
>  #if HAVE_VDPAU_X11
>      { "vdpau", vdpau_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU },
>  #endif
> +#if HAVE_D3D11VA_LIB
> +    { "d3d11va", d3d11va_init, HWACCEL_D3D11VA, AV_PIX_FMT_D3D11VA_VLD },
> +#endif
>  #if HAVE_DXVA2_LIB
>      { "dxva2", dxva2_init, HWACCEL_DXVA2, AV_PIX_FMT_DXVA2_VLD },
>  #endif

I'm pretty much against this, since it duplicates the profile selection
code all over again, even if it could be shared. (And this code should
be in libavcodec in the first place.) Someone adding such code would
have to refactor ffmpeg_d3d11va too.

> 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..e29dc67 100644
> --- a/libavutil/hwcontext.h
> +++ b/libavutil/hwcontext.h
> @@ -28,6 +28,7 @@ enum AVHWDeviceType {
>      AV_HWDEVICE_TYPE_VDPAU,
>      AV_HWDEVICE_TYPE_CUDA,
>      AV_HWDEVICE_TYPE_VAAPI,
> +    AV_HWDEVICE_TYPE_D3D11VA,
>      AV_HWDEVICE_TYPE_DXVA2,
>      AV_HWDEVICE_TYPE_QSV,
>  };
> diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
> new file mode 100644
> index 0000000..6ac1019
> --- /dev/null
> +++ b/libavutil/hwcontext_d3d11va.c
> @@ -0,0 +1,436 @@
> +/*
> + * 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;
> +    }
> +}
> +
> +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;
> +
> +    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;
> +
> +    ID3D11Device *d3d11_device;
> +    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;

Doesn't this exclude upload?

> +    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);
> +
> +    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, D3D11_MAP_READ, 0, &LockedRect);
> +    if (FAILED(hr)) {
> +        av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface %lx\n", hr);
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    for (i = 0; download ? dst->data[i] : src->data[i]; i++)
> +        surf_linesize[i] = LockedRect.RowPitch;
> +
> +    ID3D11Texture2D_GetDesc(frames_hwctx->staging_texture, &dstDesc);
> +    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);
> +
> +    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;
> +    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);

Using dlopen instead of LoadLibrary is just odd.

> +        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);

I know the dxva2 code uses this too, but please no lazy skipping of
error checking. What does atoi even return if the string is not a
number.

> +                    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;
> +    }
> +
> +    ID3D11Device        *d3d11_device;
> +    ID3D11DeviceContext *d3d11_context;
> +    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;
> +    }

No call to ID3D10Multithread_SetMultithreadProtected()?

> +
> +    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..c3e4437
> --- /dev/null
> +++ b/libavutil/hwcontext_d3d11va.h
> @@ -0,0 +1,74 @@
> +/*
> + * 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.

Doesn't say whether it also deallocates this stuff if the user allocates
it?

> +     */
> +    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;
> +} 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, \
Stève Lhomme Dec. 14, 2016, 9:30 a.m. UTC | #2
On Tue, Dec 13, 2016 at 2:35 PM, wm4 <nfxjfg@googlemail.com> wrote:
> On Tue, 13 Dec 2016 14:19:35 +0100

> Steve Lhomme <robux4@gmail.com> wrote:

>

>> From: Steve Lhomme <robux4@videolabs.io>

>>

>> The code is similar to ffmpeg_dxva2. The decoded output needs to be copied into

>> a staging texture that can be accessed by the CPU as the decoder texture can't

>> be accessed by the CPU.

>> ---

>>  Makefile                       |   1 +

>>  configure                      |  14 ++

>>  ffmpeg.h                       |   2 +

>>  ffmpeg_d3d11va.c               | 416 +++++++++++++++++++++++++++++++++++++++

>>  ffmpeg_opt.c                   |   3 +

>>  libavutil/Makefile             |   3 +

>>  libavutil/hwcontext.c          |   3 +

>>  libavutil/hwcontext.h          |   1 +

>>  libavutil/hwcontext_d3d11va.c  | 436 +++++++++++++++++++++++++++++++++++++++++

>>  libavutil/hwcontext_d3d11va.h  |  74 +++++++

>>  libavutil/hwcontext_internal.h |   1 +

>>  libavutil/version.h            |   2 +-

>>  12 files changed, 955 insertions(+), 1 deletion(-)

>>  create mode 100644 ffmpeg_d3d11va.c

>>  create mode 100644 libavutil/hwcontext_d3d11va.c

>>  create mode 100644 libavutil/hwcontext_d3d11va.h

>>

>> diff --git a/Makefile b/Makefile

>> index 8aa72fd..a48d471 100644

>> --- a/Makefile

>> +++ b/Makefile

>> @@ -38,6 +38,7 @@ OBJS-ffmpeg-$(CONFIG_VDA)     += ffmpeg_videotoolbox.o

>>  endif

>>  OBJS-ffmpeg-$(CONFIG_CUVID)   += ffmpeg_cuvid.o

>>  OBJS-ffmpeg-$(HAVE_DXVA2_LIB) += ffmpeg_dxva2.o

>> +OBJS-ffmpeg-$(HAVE_D3D11VA_LIB) += ffmpeg_d3d11va.o

>>  OBJS-ffmpeg-$(HAVE_VDPAU_X11) += ffmpeg_vdpau.o

>>  OBJS-ffserver                 += ffserver_config.o

>>

>> diff --git a/configure b/configure

>> index 9dfd006..dea8339 100755

>> --- a/configure

>> +++ b/configure

>> @@ -2050,6 +2050,8 @@ HAVE_LIST="

>>      $TYPES_LIST

>>      atomics_native

>>      dos_paths

>> +    d3d11va_lib

>> +    d3d11vaapi_cobj

>>      dxva2_lib

>>      dxva2api_cobj

>>      libc_msvcrt

>> @@ -6037,6 +6039,15 @@ enabled dxva2api_h &&

>>  int main(void) { IDirectXVideoDecoder *o = NULL; IDirectXVideoDecoder_Release(o); return 0; }

>>  EOF

>>

>> +enabled d3d11_h &&

>> +    check_cc <<EOF && enable d3d11vaapi_cobj

>> +#define _WIN32_WINNT 0x0600

>> +#define COBJMACROS

>> +#include <windows.h>

>> +#include <d3d11.h>

>> +int main(void) { ID3D11VideoDevice *o = NULL; ID3D11VideoDevice_Release(o); return 0; }

>> +EOF

>> +

>>  enabled vaapi &&

>>      check_lib va/va.h vaInitialize -lva ||

>>      disable vaapi

>> @@ -6368,6 +6379,9 @@ if test $target_os = "haiku"; then

>>      disable posix_memalign

>>  fi

>>

>> +enabled_all d3d11va d3d11vaapi_cobj &&

>> +    enable d3d11va_lib

>> +

>>  enabled_all dxva2 dxva2api_cobj CoTaskMemFree &&

>>      prepend ffmpeg_libs $($ldflags_filter "-lole32") &&

>>      enable dxva2_lib

>> diff --git a/ffmpeg.h b/ffmpeg.h

>> index ebe5bf0..a12701e 100644

>> --- a/ffmpeg.h

>> +++ b/ffmpeg.h

>> @@ -61,6 +61,7 @@ enum HWAccelID {

>>      HWACCEL_NONE = 0,

>>      HWACCEL_AUTO,

>>      HWACCEL_VDPAU,

>> +    HWACCEL_D3D11VA,

>>      HWACCEL_DXVA2,

>>      HWACCEL_VDA,

>>      HWACCEL_VIDEOTOOLBOX,

>> @@ -633,6 +634,7 @@ int ifilter_parameters_from_decoder(InputFilter *ifilter, const AVCodecContext *

>>  int ffmpeg_parse_options(int argc, char **argv);

>>

>>  int vdpau_init(AVCodecContext *s);

>> +int d3d11va_init(AVCodecContext *s);

>>  int dxva2_init(AVCodecContext *s);

>>  int vda_init(AVCodecContext *s);

>>  int videotoolbox_init(AVCodecContext *s);

>> diff --git a/ffmpeg_d3d11va.c b/ffmpeg_d3d11va.c

>> new file mode 100644

>> index 0000000..f6f8186

>> --- /dev/null

>> +++ b/ffmpeg_d3d11va.c

>> @@ -0,0 +1,416 @@

>> +/*

>> + * 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 <stdint.h>

>> +

>> +#include <d3d11.h>

>> +

>> +#include "ffmpeg.h"

>> +

>> +#include "libavcodec/d3d11va.h"

>> +

>> +#include "libavutil/avassert.h"

>> +#include "libavutil/buffer.h"

>> +#include "libavutil/frame.h"

>> +#include "libavutil/imgutils.h"

>> +#include "libavutil/pixfmt.h"

>> +

>> +#include "libavutil/hwcontext.h"

>> +#include "libavutil/hwcontext_d3d11va.h"

>> +

>> +/* define all the GUIDs used directly here,

>> +   to avoid problems with inconsistent dxva2api.h versions in mingw-w64 and different MSVC version */

>> +#include <initguid.h>

>> +DEFINE_GUID(DXVA2_ModeMPEG2_VLD,      0xee27417f, 0x5e28,0x4e65,0xbe,0xea,0x1d,0x26,0xb5,0x08,0xad,0xc9);

>> +DEFINE_GUID(DXVA2_ModeMPEG2and1_VLD,  0x86695f12, 0x340e,0x4f04,0x9f,0xd3,0x92,0x53,0xdd,0x32,0x74,0x60);

>> +DEFINE_GUID(DXVA2_ModeH264_E,         0x1b81be68, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);

>> +DEFINE_GUID(DXVA2_ModeH264_F,         0x1b81be69, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);

>> +DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68, 0x4951,0x4C54,0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6);

>> +DEFINE_GUID(DXVA2_ModeVC1_D,          0x1b81beA3, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);

>> +DEFINE_GUID(DXVA2_ModeVC1_D2010,      0x1b81beA4, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);

>> +DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main,  0x5b11d51b, 0x2f4c,0x4452,0xbc,0xc3,0x09,0xf2,0xa1,0x16,0x0c,0xc0);

>> +DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main10,0x107af0e0, 0xef1a,0x4d19,0xab,0xa8,0x67,0xa1,0x63,0x07,0x3d,0x13);

>> +DEFINE_GUID(DXVA2_ModeVP9_VLD_Profile0, 0x463707f8, 0xa1d0,0x4585,0x87,0x6d,0x83,0xaa,0x6d,0x60,0xb8,0x9e);

>> +DEFINE_GUID(DXVA2_NoEncrypt,          0x1b81beD0, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);

>> +

>> +typedef struct d3d11va_mode {

>> +  const GUID     *guid;

>> +  enum AVCodecID codec;

>> +} d3d11va_mode;

>> +

>> +static const d3d11va_mode d3d11va_modes[] = {

>> +    /* MPEG-2 */

>> +    { &DXVA2_ModeMPEG2_VLD,      AV_CODEC_ID_MPEG2VIDEO },

>> +    { &DXVA2_ModeMPEG2and1_VLD,  AV_CODEC_ID_MPEG2VIDEO },

>> +

>> +    /* H.264 */

>> +    { &DXVA2_ModeH264_F,         AV_CODEC_ID_H264 },

>> +    { &DXVA2_ModeH264_E,         AV_CODEC_ID_H264 },

>> +    /* Intel specific H.264 mode */

>> +    { &DXVADDI_Intel_ModeH264_E, AV_CODEC_ID_H264 },

>> +

>> +    /* VC-1 / WMV3 */

>> +    { &DXVA2_ModeVC1_D2010,      AV_CODEC_ID_VC1  },

>> +    { &DXVA2_ModeVC1_D2010,      AV_CODEC_ID_WMV3 },

>> +    { &DXVA2_ModeVC1_D,          AV_CODEC_ID_VC1  },

>> +    { &DXVA2_ModeVC1_D,          AV_CODEC_ID_WMV3 },

>> +

>> +    /* HEVC/H.265 */

>> +    { &DXVA2_ModeHEVC_VLD_Main,  AV_CODEC_ID_HEVC },

>> +    { &DXVA2_ModeHEVC_VLD_Main10,AV_CODEC_ID_HEVC },

>> +

>> +    /* VP8/9 */

>> +    { &DXVA2_ModeVP9_VLD_Profile0, AV_CODEC_ID_VP9 },

>> +

>> +    { NULL,                      0 },

>> +};

>> +

>> +typedef struct D3D11VAContext {

>> +    D3D11_VIDEO_DECODER_CONFIG   decoder_config;

>> +

>> +    AVFrame                     *tmp_frame;

>> +

>> +    AVBufferRef                 *hw_device_ctx;

>> +    AVBufferRef                 *hw_frames_ctx;

>> +} D3D11VAContext;

>> +

>> +static void d3d11va_uninit(AVCodecContext *s)

>> +{

>> +    InputStream  *ist = s->opaque;

>> +    D3D11VAContext *ctx = ist->hwaccel_ctx;

>> +

>> +    ist->hwaccel_uninit        = NULL;

>> +    ist->hwaccel_get_buffer    = NULL;

>> +    ist->hwaccel_retrieve_data = NULL;

>> +

>> +    av_buffer_unref(&ctx->hw_frames_ctx);

>> +    av_buffer_unref(&ctx->hw_device_ctx);

>> +

>> +    av_frame_free(&ctx->tmp_frame);

>> +

>> +    av_freep(&ist->hwaccel_ctx);

>> +    av_freep(&s->hwaccel_context);

>> +}

>> +

>> +static int d3d11va_get_buffer(AVCodecContext *s, AVFrame *frame, int flags)

>> +{

>> +    InputStream  *ist = s->opaque;

>> +    D3D11VAContext *ctx = ist->hwaccel_ctx;

>> +

>> +    return av_hwframe_get_buffer(ctx->hw_frames_ctx, frame, 0);

>> +}

>> +

>> +static int d3d11va_retrieve_data(AVCodecContext *s, AVFrame *frame)

>> +{

>> +    InputStream        *ist = s->opaque;

>> +    D3D11VAContext     *ctx = ist->hwaccel_ctx;

>> +    int                ret;

>> +

>> +    ret = av_hwframe_transfer_data(ctx->tmp_frame, frame, 0);

>> +    if (ret < 0)

>> +        return ret;

>> +

>> +    ret = av_frame_copy_props(ctx->tmp_frame, frame);

>> +    if (ret < 0) {

>> +        av_frame_unref(ctx->tmp_frame);

>> +        return ret;

>> +    }

>> +

>> +    av_frame_unref(frame);

>> +    av_frame_move_ref(frame, ctx->tmp_frame);

>> +

>> +    return 0;

>> +}

>> +

>> +static int d3d11va_alloc(AVCodecContext *s)

>> +{

>> +    InputStream  *ist = s->opaque;

>> +    D3D11VAContext *ctx;

>> +

>> +    int ret;

>> +

>> +    ctx = av_mallocz(sizeof(*ctx));

>> +    if (!ctx)

>> +        return AVERROR(ENOMEM);

>> +

>> +    ist->hwaccel_ctx           = ctx;

>> +    ist->hwaccel_uninit        = d3d11va_uninit;

>> +    ist->hwaccel_get_buffer    = d3d11va_get_buffer;

>> +    ist->hwaccel_retrieve_data = d3d11va_retrieve_data;

>> +

>> +    ret = av_hwdevice_ctx_create(&ctx->hw_device_ctx, AV_HWDEVICE_TYPE_D3D11VA,

>> +                                 ist->hwaccel_device, NULL, 0);

>> +    if (ret < 0)

>> +        goto fail;

>> +

>> +    ctx->tmp_frame = av_frame_alloc();

>> +    if (!ctx->tmp_frame)

>> +        goto fail;

>> +

>> +    s->hwaccel_context = av_mallocz(sizeof(AVD3D11VAContext));

>> +    if (!s->hwaccel_context)

>> +        goto fail;

>> +

>> +    return 0;

>> +fail:

>> +    d3d11va_uninit(s);

>> +    return AVERROR(EINVAL);

>> +}

>> +

>> +static int d3d11va_get_decoder_configuration(AVCodecContext *s,

>> +                                           const D3D11_VIDEO_DECODER_DESC *desc,

>> +                                           D3D11_VIDEO_DECODER_CONFIG *config)

>> +{

>> +    InputStream  *ist = s->opaque;

>> +    int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;

>> +    D3D11VAContext *ctx = ist->hwaccel_ctx;

>> +    unsigned cfg_count = 0, best_score = 0;

>> +    D3D11_VIDEO_DECODER_CONFIG best_cfg = {{0}};

>> +    HRESULT hr;

>> +    int i;

>> +    AVHWDeviceContext    *device_ctx = (AVHWDeviceContext*)ctx->hw_device_ctx->data;

>> +    AVD3D11VADeviceContext *device_hwctx = device_ctx->hwctx;

>> +

>> +    hr = ID3D11VideoDevice_GetVideoDecoderConfigCount(device_hwctx->video_device, desc, &cfg_count);

>> +    if (FAILED(hr)) {

>> +        av_log(NULL, loglevel, "Unable to retrieve decoder configurations\n");

>> +        return AVERROR(EINVAL);

>> +    }

>> +

>> +    for (i = 0; i < cfg_count; i++) {

>> +        unsigned score;

>> +        D3D11_VIDEO_DECODER_CONFIG cfg;

>> +        hr = ID3D11VideoDevice_GetVideoDecoderConfig(device_hwctx->video_device, desc, i, &cfg );

>> +        if (FAILED(hr)) {

>> +            av_log(NULL, loglevel, "Unable to retrieve decoder configurations. (hr=0x%lX)\n", hr);

>> +            return AVERROR(EINVAL);

>> +        }

>> +

>> +        if (cfg.ConfigBitstreamRaw == 1)

>> +            score = 1;

>> +        else if (s->codec_id == AV_CODEC_ID_H264 && cfg.ConfigBitstreamRaw == 2)

>> +            score = 2;

>> +        else

>> +            continue;

>> +        if (IsEqualGUID(&cfg.guidConfigBitstreamEncryption, &DXVA2_NoEncrypt))

>> +            score += 16;

>> +        if (score > best_score) {

>> +            best_score = score;

>> +            best_cfg   = cfg;

>> +        }

>> +    }

>> +

>> +    if (!best_score) {

>> +        av_log(NULL, loglevel, "No valid decoder configuration available\n");

>> +        return AVERROR(EINVAL);

>> +    }

>> +

>> +    *config = best_cfg;

>> +    return 0;

>> +}

>> +

>> +static int d3d11va_create_decoder(AVCodecContext *s)

>> +{

>> +    InputStream  *ist = s->opaque;

>> +    int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;

>> +    D3D11VAContext *ctx = ist->hwaccel_ctx;

>> +    AVD3D11VAContext *dxva_ctx = s->hwaccel_context;

>> +    GUID *guid_list = NULL;

>> +    unsigned guid_count = 0, i, j;

>> +    GUID device_guid = GUID_NULL;

>> +    const DXGI_FORMAT surface_format = (s->sw_pix_fmt == AV_PIX_FMT_YUV420P10) ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12;

>> +    DXGI_FORMAT target_format = DXGI_FORMAT_UNKNOWN;

>> +    D3D11_VIDEO_DECODER_DESC desc = { 0 };

>> +    D3D11_VIDEO_DECODER_CONFIG config;

>> +    HRESULT hr;

>> +    int surface_alignment, num_surfaces;

>> +    int ret;

>> +

>> +    AVD3D11VAFramesContext *frames_hwctx;

>> +    AVHWFramesContext *frames_ctx;

>> +

>> +    AVHWDeviceContext    *device_ctx;

>> +    AVD3D11VADeviceContext *device_hwctx;

>> +    device_ctx = (AVHWDeviceContext*)ctx->hw_device_ctx->data;

>> +    device_hwctx = device_ctx->hwctx;

>> +

>> +    guid_count = ID3D11VideoDevice_GetVideoDecoderProfileCount(device_hwctx->video_device);

>> +    guid_list = av_malloc(sizeof(*guid_list) * guid_count);

>> +    if (guid_list==NULL) {

>> +        av_log(NULL, loglevel, "Failed to allocate decoder device GUIDs\n");

>> +        goto fail;

>> +    }

>> +    for (i=0; i<guid_count; i++) {

>> +        hr = ID3D11VideoDevice_GetVideoDecoderProfile(device_hwctx->video_device, i, &guid_list[i]);

>> +        if (FAILED(hr)) {

>> +            av_log(NULL, loglevel, "Failed to retrieve decoder GUID %d\n", i);

>> +            goto fail;

>> +        }

>> +    }

>> +

>> +    for (i = 0; d3d11va_modes[i].guid; i++) {

>> +        BOOL is_supported = FALSE;

>> +        const d3d11va_mode *mode = &d3d11va_modes[i];

>> +        if (mode->codec != s->codec_id)

>> +            continue;

>> +

>> +        for (j = 0; j < guid_count; j++) {

>> +            if (IsEqualGUID(mode->guid, &guid_list[j]))

>> +                break;

>> +        }

>> +        if (j == guid_count)

>> +            continue;

>> +

>> +        hr = ID3D11VideoDevice_CheckVideoDecoderFormat(device_hwctx->video_device, mode->guid,

>> +                                                       surface_format, &is_supported);

>> +        if (SUCCEEDED(hr) && is_supported) {

>> +            target_format = surface_format;

>> +            device_guid = *mode->guid;

>> +            break;

>> +        }

>> +    }

>> +

>> +    if (IsEqualGUID(&device_guid, &GUID_NULL)) {

>> +        av_log(NULL, loglevel, "No decoder device for codec found\n");

>> +        goto fail;

>> +    }

>> +

>> +    desc.SampleWidth  = s->coded_width;

>> +    desc.SampleHeight = s->coded_height;

>> +    desc.OutputFormat = target_format;

>> +    desc.Guid         = device_guid;

>> +

>> +    ret = d3d11va_get_decoder_configuration(s, &desc, &config);

>> +    if (ret < 0) {

>> +        goto fail;

>> +    }

>> +

>> +    /* decoding MPEG-2 requires additional alignment on some Intel GPUs,

>> +       but it causes issues for H.264 on certain AMD GPUs..... */

>> +    if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO)

>> +        surface_alignment = 32;

>> +    /* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure

>> +       all coding features have enough room to work with */

>> +    else if  (s->codec_id == AV_CODEC_ID_HEVC)

>> +        surface_alignment = 128;

>> +    else

>> +        surface_alignment = 16;

>> +

>> +    /* 4 base work surfaces */

>> +    num_surfaces = 4;

>> +

>> +    /* add surfaces based on number of possible refs */

>> +    if (s->codec_id == AV_CODEC_ID_H264 || s->codec_id == AV_CODEC_ID_HEVC)

>> +        num_surfaces += 16;

>> +    else if (s->codec_id == AV_CODEC_ID_VP9)

>> +        num_surfaces += 8;

>> +    else

>> +        num_surfaces += 2;

>> +

>> +    /* add extra surfaces for frame threading */

>> +    if (s->active_thread_type & FF_THREAD_FRAME)

>> +        num_surfaces += s->thread_count;

>> +

>> +    ctx->hw_frames_ctx = av_hwframe_ctx_alloc(ctx->hw_device_ctx);

>> +    if (!ctx->hw_frames_ctx)

>> +        goto fail;

>> +    frames_ctx   = (AVHWFramesContext*)ctx->hw_frames_ctx->data;

>> +    frames_hwctx = frames_ctx->hwctx;

>> +

>> +    frames_ctx->format            = AV_PIX_FMT_D3D11VA_VLD;

>> +    frames_ctx->sw_format         = (target_format == DXGI_FORMAT_P010 ? AV_PIX_FMT_P010 : AV_PIX_FMT_NV12);

>> +    frames_ctx->width             = FFALIGN(s->coded_width, surface_alignment);

>> +    frames_ctx->height            = FFALIGN(s->coded_height, surface_alignment);

>> +    frames_ctx->initial_pool_size = num_surfaces;

>> +

>> +    hr = ID3D11VideoDevice_CreateVideoDecoder(device_hwctx->video_device, &desc,

>> +                                              &config, &frames_hwctx->decoder_to_release);

>> +    if (FAILED(hr)) {

>> +        av_log(NULL, loglevel, "Failed to create D3D11VA video decoder\n");

>> +        goto fail;

>> +    }

>> +

>> +    ret = av_hwframe_ctx_init(ctx->hw_frames_ctx);

>> +    if (ret < 0) {

>> +        av_log(NULL, loglevel, "Failed to initialize the HW frames context\n");

>> +        goto fail;

>> +    }

>> +

>> +    ctx->decoder_config = config;

>> +

>> +    dxva_ctx->cfg           = &ctx->decoder_config;

>> +    dxva_ctx->decoder       = frames_hwctx->decoder_to_release;

>> +    dxva_ctx->surface       = frames_hwctx->surfaces;

>> +    dxva_ctx->surface_count = frames_hwctx->nb_surfaces;

>> +    dxva_ctx->video_context = device_hwctx->video_context;

>> +

>> +    if (IsEqualGUID(&device_guid, &DXVADDI_Intel_ModeH264_E))

>> +        dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO;

>> +

>> +    av_free(guid_list);

>> +    return 0;

>> +fail:

>> +    av_free(guid_list);

>> +    av_buffer_unref(&ctx->hw_frames_ctx);

>> +    return AVERROR(EINVAL);

>> +}

>> +

>> +int d3d11va_init(AVCodecContext *s)

>> +{

>> +    InputStream *ist = s->opaque;

>> +    int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;

>> +    D3D11VAContext *ctx;

>> +    int ret;

>> +

>> +    if (!ist->hwaccel_ctx) {

>> +        ret = d3d11va_alloc(s);

>> +        if (ret < 0)

>> +            return ret;

>> +    }

>> +    ctx = ist->hwaccel_ctx;

>> +

>> +    if (s->codec_id == AV_CODEC_ID_H264 &&

>> +        (s->profile & ~FF_PROFILE_H264_CONSTRAINED) > FF_PROFILE_H264_HIGH) {

>> +        av_log(NULL, loglevel, "Unsupported H.264 profile for D3D11VA HWAccel: %d\n", s->profile);

>> +        return AVERROR(EINVAL);

>> +    }

>> +

>> +    if (s->codec_id == AV_CODEC_ID_HEVC &&

>> +        s->profile != FF_PROFILE_HEVC_MAIN && s->profile != FF_PROFILE_HEVC_MAIN_10) {

>> +        av_log(NULL, loglevel, "Unsupported HEVC profile for D3D11VA HWAccel: %d\n", s->profile);

>> +        return AVERROR(EINVAL);

>> +    }

>> +

>> +    av_buffer_unref(&ctx->hw_frames_ctx);

>> +

>> +    ret = d3d11va_create_decoder(s);

>> +    if (ret < 0) {

>> +        av_log(NULL, loglevel, "Error creating the D3D11VA decoder\n");

>> +        return ret;

>> +    }

>> +

>> +    return 0;

>> +}

>> diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c

>> index 6862456..9cc1a55 100644

>> --- a/ffmpeg_opt.c

>> +++ b/ffmpeg_opt.c

>> @@ -69,6 +69,9 @@ const HWAccel hwaccels[] = {

>>  #if HAVE_VDPAU_X11

>>      { "vdpau", vdpau_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU },

>>  #endif

>> +#if HAVE_D3D11VA_LIB

>> +    { "d3d11va", d3d11va_init, HWACCEL_D3D11VA, AV_PIX_FMT_D3D11VA_VLD },

>> +#endif

>>  #if HAVE_DXVA2_LIB

>>      { "dxva2", dxva2_init, HWACCEL_DXVA2, AV_PIX_FMT_DXVA2_VLD },

>>  #endif

>

> I'm pretty much against this, since it duplicates the profile selection

> code all over again, even if it could be shared. (And this code should

> be in libavcodec in the first place.) Someone adding such code would

> have to refactor ffmpeg_d3d11va too.


I'm not sure how this relates to the line above. d3d11va and dxva2 are
definitely not the same thing. On mobile you do not have DXVA2 at all
but D3D11(VA) is present. There must be a way to have one without the
other.

>> 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..e29dc67 100644

>> --- a/libavutil/hwcontext.h

>> +++ b/libavutil/hwcontext.h

>> @@ -28,6 +28,7 @@ enum AVHWDeviceType {

>>      AV_HWDEVICE_TYPE_VDPAU,

>>      AV_HWDEVICE_TYPE_CUDA,

>>      AV_HWDEVICE_TYPE_VAAPI,

>> +    AV_HWDEVICE_TYPE_D3D11VA,

>>      AV_HWDEVICE_TYPE_DXVA2,

>>      AV_HWDEVICE_TYPE_QSV,

>>  };

>> diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c

>> new file mode 100644

>> index 0000000..6ac1019

>> --- /dev/null

>> +++ b/libavutil/hwcontext_d3d11va.c

>> @@ -0,0 +1,436 @@

>> +/*

>> + * 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;

>> +    }

>> +}

>> +

>> +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;

>> +

>> +    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;

>> +

>> +    ID3D11Device *d3d11_device;

>> +    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;

>

> Doesn't this exclude upload?


I only too decoding in consideration. Since it's a staging texture,
adding ACCESS_WRITE is possible. When mapping the surface to the CPU
I'll need to adjust the flags.

>> +    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);

>> +

>> +    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, D3D11_MAP_READ, 0, &LockedRect);

>> +    if (FAILED(hr)) {

>> +        av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface %lx\n", hr);

>> +        return AVERROR_UNKNOWN;

>> +    }

>> +

>> +    for (i = 0; download ? dst->data[i] : src->data[i]; i++)

>> +        surf_linesize[i] = LockedRect.RowPitch;

>> +

>> +    ID3D11Texture2D_GetDesc(frames_hwctx->staging_texture, &dstDesc);

>> +    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);

>> +

>> +    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;

>> +    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);

>

> Using dlopen instead of LoadLibrary is just odd.


I used exactly what is used in other parts of the DXVA2 code. If it
exists I suppose there's a reason for that.

>> +        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);

>

> I know the dxva2 code uses this too, but please no lazy skipping of

> error checking. What does atoi even return if the string is not a

> number.


It returns 0 and so we'll use the first adapter.

>> +                    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;

>> +    }

>> +

>> +    ID3D11Device        *d3d11_device;

>> +    ID3D11DeviceContext *d3d11_context;

>> +    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;

>> +    }

>

> No call to ID3D10Multithread_SetMultithreadProtected()?


No, it worked without even with multiple threads.

>> +

>> +    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..c3e4437

>> --- /dev/null

>> +++ b/libavutil/hwcontext_d3d11va.h

>> @@ -0,0 +1,74 @@

>> +/*

>> + * 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.

>

> Doesn't say whether it also deallocates this stuff if the user allocates

> it?


I'll update the documentation. It was copy/pasted...

>> +     */

>> +    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;

>> +} 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, \

> _______________________________________________

> ffmpeg-devel mailing list

> ffmpeg-devel@ffmpeg.org

> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Hendrik Leppkes Dec. 14, 2016, 9:52 a.m. UTC | #3
On Wed, Dec 14, 2016 at 10:30 AM, Stève Lhomme <robux4@videolabs.io> wrote:
>>
>> I'm pretty much against this, since it duplicates the profile selection
>> code all over again, even if it could be shared. (And this code should
>> be in libavcodec in the first place.) Someone adding such code would
>> have to refactor ffmpeg_d3d11va too.
>
> I'm not sure how this relates to the line above. d3d11va and dxva2 are
> definitely not the same thing. On mobile you do not have DXVA2 at all
> but D3D11(VA) is present. There must be a way to have one without the
> other.
>

The point wm4 was making is that it should share a bunch of the code
with dxva2 (ie. the guid selection, for example, and whatever is
appropriate), instead of just duplicating it.
And on top of that, maybe even have it in avcodec since every
implementation needs a GUID -> codec mapping somewhere.

>>> +    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;
>>
>> Doesn't this exclude upload?
>
> I only too decoding in consideration. Since it's a staging texture,
> adding ACCESS_WRITE is possible. When mapping the surface to the CPU
> I'll need to adjust the flags.

The hwcontext should indeed be universal for both download and upload,
even if its only used for download in the first step.

>>> +static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
>>> +                                 AVDictionary *opts, int flags)
>>> +{
>>> +    AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
>>> +    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);
>>
>> Using dlopen instead of LoadLibrary is just odd.
>
> I used exactly what is used in other parts of the DXVA2 code. If it
> exists I suppose there's a reason for that.

ffmpeg has wrappers around LoadLibrary with some extra security
checks, and they mimick the dlopen API, IIRC.
wm4 Dec. 14, 2016, 10:44 a.m. UTC | #4
On Wed, 14 Dec 2016 09:30:35 +0000
Stève Lhomme <robux4@videolabs.io> wrote:

> On Tue, Dec 13, 2016 at 2:35 PM, wm4 <nfxjfg@googlemail.com> wrote:
> > On Tue, 13 Dec 2016 14:19:35 +0100
> > Steve Lhomme <robux4@gmail.com> wrote:

> >
> > I'm pretty much against this, since it duplicates the profile selection
> > code all over again, even if it could be shared. (And this code should
> > be in libavcodec in the first place.) Someone adding such code would
> > have to refactor ffmpeg_d3d11va too.  
> 
> I'm not sure how this relates to the line above. d3d11va and dxva2 are
> definitely not the same thing. On mobile you do not have DXVA2 at all
> but D3D11(VA) is present. There must be a way to have one without the
> other.

Yeah, but selection of the decoder GUIDs is very similar between both
APIs. It can definitely be shared (we do this in mpv). There's
absolutely no reason to copy-paste that stuff from ffmpeg_dxva2.c into
ffmpeg_d3d11va.c.

As for how libavcodec can do provide this part, see the Libav/avconv
vaapi support (none of that is in ffmpeg yet).

> >> 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..e29dc67 100644
> >> --- a/libavutil/hwcontext.h
> >> +++ b/libavutil/hwcontext.h
> >> @@ -28,6 +28,7 @@ enum AVHWDeviceType {
> >>      AV_HWDEVICE_TYPE_VDPAU,
> >>      AV_HWDEVICE_TYPE_CUDA,
> >>      AV_HWDEVICE_TYPE_VAAPI,
> >> +    AV_HWDEVICE_TYPE_D3D11VA,
> >>      AV_HWDEVICE_TYPE_DXVA2,
> >>      AV_HWDEVICE_TYPE_QSV,
> >>  };
> >> diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
> >> new file mode 100644
> >> index 0000000..6ac1019
> >> --- /dev/null
> >> +++ b/libavutil/hwcontext_d3d11va.c
> >> @@ -0,0 +1,436 @@
> >> +/*
> >> + * 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;
> >> +    }
> >> +}
> >> +
> >> +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;
> >> +
> >> +    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;
> >> +
> >> +    ID3D11Device *d3d11_device;
> >> +    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;  
> >
> > Doesn't this exclude upload?  
> 
> I only too decoding in consideration. Since it's a staging texture,
> adding ACCESS_WRITE is possible. When mapping the surface to the CPU
> I'll need to adjust the flags.
> 
> >> +    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);
> >> +
> >> +    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, D3D11_MAP_READ, 0, &LockedRect);
> >> +    if (FAILED(hr)) {
> >> +        av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface %lx\n", hr);
> >> +        return AVERROR_UNKNOWN;
> >> +    }
> >> +
> >> +    for (i = 0; download ? dst->data[i] : src->data[i]; i++)
> >> +        surf_linesize[i] = LockedRect.RowPitch;
> >> +
> >> +    ID3D11Texture2D_GetDesc(frames_hwctx->staging_texture, &dstDesc);
> >> +    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);
> >> +
> >> +    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;
> >> +    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);  
> >
> > Using dlopen instead of LoadLibrary is just odd.  
> 
> I used exactly what is used in other parts of the DXVA2 code. If it
> exists I suppose there's a reason for that.

Huh? libavutil/hwcontext_dxva2.c uses LoadLibrary.

I mean, using dlopen() works, but I just can't see any reason for it.
But it obfuscates the code somewhat.

> >> +        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);  
> >
> > I know the dxva2 code uses this too, but please no lazy skipping of
> > error checking. What does atoi even return if the string is not a
> > number.  
> 
> It returns 0 and so we'll use the first adapter.
> 

Quoting C99:

> The  functions atof, atoi, atol, and atoll need  not  affect  the
> value  of  the  integer expression errno on  an  error.   If  the
> value  of  the  result  cannot  be  represented,  the behavior is
> undefined.

Doesn't sound very promising.

> >> +                    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;
> >> +    }
> >> +
> >> +    ID3D11Device        *d3d11_device;
> >> +    ID3D11DeviceContext *d3d11_context;
> >> +    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;
> >> +    }  
> >
> > No call to ID3D10Multithread_SetMultithreadProtected()?  
> 
> No, it worked without even with multiple threads.

Not sure if that really means it's correct. We all know that even
disregarding the thread-safety of the D3D device, thread-safety of the
dxva2 code is shaky at best.

Anyway, this might also be needed in other contexts (although most API
users which need this will most likely provide their own device). If
you don't want to enable it unconditionally for whatever reasons,
maybe a flag?

> >> +
> >> +    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..c3e4437
> >> --- /dev/null
> >> +++ b/libavutil/hwcontext_d3d11va.h
> >> @@ -0,0 +1,74 @@
> >> +/*
> >> + * 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.  
> >
> > Doesn't say whether it also deallocates this stuff if the user allocates
> > it?  
> 
> I'll update the documentation. It was copy/pasted...

OK. The doxygen on the decoder_to_release field below is much more
explicit, for example.

> >> +     */
> >> +    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;
> >> +} 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, \  
> > _______________________________________________
> > ffmpeg-devel mailing list
> > ffmpeg-devel@ffmpeg.org
> > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel  
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
wm4 Dec. 14, 2016, 10:51 a.m. UTC | #5
On Tue, 13 Dec 2016 14:19:35 +0100
Steve Lhomme <robux4@gmail.com> wrote:


> +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);
> +
> +    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, D3D11_MAP_READ, 0, &LockedRect);
> +    if (FAILED(hr)) {
> +        av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface %lx\n", hr);
> +        return AVERROR_UNKNOWN;
> +    }
> +
> +    for (i = 0; download ? dst->data[i] : src->data[i]; i++)
> +        surf_linesize[i] = LockedRect.RowPitch;
> +
> +    ID3D11Texture2D_GetDesc(frames_hwctx->staging_texture, &dstDesc);
> +    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);
> +
> +    return 0;
> +}

Missed this part during the first review, but: the staging_texture use
should be either mutex-protected, or there should be a pool of staging
textures.
compn Dec. 15, 2016, 12:23 a.m. UTC | #6
On Wed, 14 Dec 2016 11:44:13 +0100
wm4 <nfxjfg@googlemail.com> wrote:

> On Wed, 14 Dec 2016 09:30:35 +0000
> Stève Lhomme <robux4@videolabs.io> wrote:
> 
> > On Tue, Dec 13, 2016 at 2:35 PM, wm4 <nfxjfg@googlemail.com> wrote:
> > > On Tue, 13 Dec 2016 14:19:35 +0100
> > > Steve Lhomme <robux4@gmail.com> wrote:
> 
> > >
> > > I'm pretty much against this, since it duplicates the profile
> > > selection code all over again, even if it could be shared. (And
> > > this code should be in libavcodec in the first place.) Someone
> > > adding such code would have to refactor ffmpeg_d3d11va too.  
> > 
> > I'm not sure how this relates to the line above. d3d11va and dxva2
> > are definitely not the same thing. On mobile you do not have DXVA2
> > at all but D3D11(VA) is present. There must be a way to have one
> > without the other.
> 
> Yeah, but selection of the decoder GUIDs is very similar between both
> APIs. It can definitely be shared (we do this in mpv). There's
> absolutely no reason to copy-paste that stuff from ffmpeg_dxva2.c into
> ffmpeg_d3d11va.c.
> 

is having this duplication a blocker to this patch would you say?

> As for how libavcodec can do provide this part, see the Libav/avconv
> vaapi support (none of that is in ffmpeg yet).

same question here.

i am not arguing one way or the other, just asking.

-compn
diff mbox

Patch

diff --git a/Makefile b/Makefile
index 8aa72fd..a48d471 100644
--- a/Makefile
+++ b/Makefile
@@ -38,6 +38,7 @@  OBJS-ffmpeg-$(CONFIG_VDA)     += ffmpeg_videotoolbox.o
 endif
 OBJS-ffmpeg-$(CONFIG_CUVID)   += ffmpeg_cuvid.o
 OBJS-ffmpeg-$(HAVE_DXVA2_LIB) += ffmpeg_dxva2.o
+OBJS-ffmpeg-$(HAVE_D3D11VA_LIB) += ffmpeg_d3d11va.o
 OBJS-ffmpeg-$(HAVE_VDPAU_X11) += ffmpeg_vdpau.o
 OBJS-ffserver                 += ffserver_config.o
 
diff --git a/configure b/configure
index 9dfd006..dea8339 100755
--- a/configure
+++ b/configure
@@ -2050,6 +2050,8 @@  HAVE_LIST="
     $TYPES_LIST
     atomics_native
     dos_paths
+    d3d11va_lib
+    d3d11vaapi_cobj
     dxva2_lib
     dxva2api_cobj
     libc_msvcrt
@@ -6037,6 +6039,15 @@  enabled dxva2api_h &&
 int main(void) { IDirectXVideoDecoder *o = NULL; IDirectXVideoDecoder_Release(o); return 0; }
 EOF
 
+enabled d3d11_h &&
+    check_cc <<EOF && enable d3d11vaapi_cobj
+#define _WIN32_WINNT 0x0600
+#define COBJMACROS
+#include <windows.h>
+#include <d3d11.h>
+int main(void) { ID3D11VideoDevice *o = NULL; ID3D11VideoDevice_Release(o); return 0; }
+EOF
+
 enabled vaapi &&
     check_lib va/va.h vaInitialize -lva ||
     disable vaapi
@@ -6368,6 +6379,9 @@  if test $target_os = "haiku"; then
     disable posix_memalign
 fi
 
+enabled_all d3d11va d3d11vaapi_cobj &&
+    enable d3d11va_lib
+
 enabled_all dxva2 dxva2api_cobj CoTaskMemFree &&
     prepend ffmpeg_libs $($ldflags_filter "-lole32") &&
     enable dxva2_lib
diff --git a/ffmpeg.h b/ffmpeg.h
index ebe5bf0..a12701e 100644
--- a/ffmpeg.h
+++ b/ffmpeg.h
@@ -61,6 +61,7 @@  enum HWAccelID {
     HWACCEL_NONE = 0,
     HWACCEL_AUTO,
     HWACCEL_VDPAU,
+    HWACCEL_D3D11VA,
     HWACCEL_DXVA2,
     HWACCEL_VDA,
     HWACCEL_VIDEOTOOLBOX,
@@ -633,6 +634,7 @@  int ifilter_parameters_from_decoder(InputFilter *ifilter, const AVCodecContext *
 int ffmpeg_parse_options(int argc, char **argv);
 
 int vdpau_init(AVCodecContext *s);
+int d3d11va_init(AVCodecContext *s);
 int dxva2_init(AVCodecContext *s);
 int vda_init(AVCodecContext *s);
 int videotoolbox_init(AVCodecContext *s);
diff --git a/ffmpeg_d3d11va.c b/ffmpeg_d3d11va.c
new file mode 100644
index 0000000..f6f8186
--- /dev/null
+++ b/ffmpeg_d3d11va.c
@@ -0,0 +1,416 @@ 
+/*
+ * 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 <stdint.h>
+
+#include <d3d11.h>
+
+#include "ffmpeg.h"
+
+#include "libavcodec/d3d11va.h"
+
+#include "libavutil/avassert.h"
+#include "libavutil/buffer.h"
+#include "libavutil/frame.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/pixfmt.h"
+
+#include "libavutil/hwcontext.h"
+#include "libavutil/hwcontext_d3d11va.h"
+
+/* define all the GUIDs used directly here,
+   to avoid problems with inconsistent dxva2api.h versions in mingw-w64 and different MSVC version */
+#include <initguid.h>
+DEFINE_GUID(DXVA2_ModeMPEG2_VLD,      0xee27417f, 0x5e28,0x4e65,0xbe,0xea,0x1d,0x26,0xb5,0x08,0xad,0xc9);
+DEFINE_GUID(DXVA2_ModeMPEG2and1_VLD,  0x86695f12, 0x340e,0x4f04,0x9f,0xd3,0x92,0x53,0xdd,0x32,0x74,0x60);
+DEFINE_GUID(DXVA2_ModeH264_E,         0x1b81be68, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
+DEFINE_GUID(DXVA2_ModeH264_F,         0x1b81be69, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
+DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68, 0x4951,0x4C54,0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6);
+DEFINE_GUID(DXVA2_ModeVC1_D,          0x1b81beA3, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
+DEFINE_GUID(DXVA2_ModeVC1_D2010,      0x1b81beA4, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
+DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main,  0x5b11d51b, 0x2f4c,0x4452,0xbc,0xc3,0x09,0xf2,0xa1,0x16,0x0c,0xc0);
+DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main10,0x107af0e0, 0xef1a,0x4d19,0xab,0xa8,0x67,0xa1,0x63,0x07,0x3d,0x13);
+DEFINE_GUID(DXVA2_ModeVP9_VLD_Profile0, 0x463707f8, 0xa1d0,0x4585,0x87,0x6d,0x83,0xaa,0x6d,0x60,0xb8,0x9e);
+DEFINE_GUID(DXVA2_NoEncrypt,          0x1b81beD0, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5);
+
+typedef struct d3d11va_mode {
+  const GUID     *guid;
+  enum AVCodecID codec;
+} d3d11va_mode;
+
+static const d3d11va_mode d3d11va_modes[] = {
+    /* MPEG-2 */
+    { &DXVA2_ModeMPEG2_VLD,      AV_CODEC_ID_MPEG2VIDEO },
+    { &DXVA2_ModeMPEG2and1_VLD,  AV_CODEC_ID_MPEG2VIDEO },
+
+    /* H.264 */
+    { &DXVA2_ModeH264_F,         AV_CODEC_ID_H264 },
+    { &DXVA2_ModeH264_E,         AV_CODEC_ID_H264 },
+    /* Intel specific H.264 mode */
+    { &DXVADDI_Intel_ModeH264_E, AV_CODEC_ID_H264 },
+
+    /* VC-1 / WMV3 */
+    { &DXVA2_ModeVC1_D2010,      AV_CODEC_ID_VC1  },
+    { &DXVA2_ModeVC1_D2010,      AV_CODEC_ID_WMV3 },
+    { &DXVA2_ModeVC1_D,          AV_CODEC_ID_VC1  },
+    { &DXVA2_ModeVC1_D,          AV_CODEC_ID_WMV3 },
+
+    /* HEVC/H.265 */
+    { &DXVA2_ModeHEVC_VLD_Main,  AV_CODEC_ID_HEVC },
+    { &DXVA2_ModeHEVC_VLD_Main10,AV_CODEC_ID_HEVC },
+
+    /* VP8/9 */
+    { &DXVA2_ModeVP9_VLD_Profile0, AV_CODEC_ID_VP9 },
+
+    { NULL,                      0 },
+};
+
+typedef struct D3D11VAContext {
+    D3D11_VIDEO_DECODER_CONFIG   decoder_config;
+
+    AVFrame                     *tmp_frame;
+
+    AVBufferRef                 *hw_device_ctx;
+    AVBufferRef                 *hw_frames_ctx;
+} D3D11VAContext;
+
+static void d3d11va_uninit(AVCodecContext *s)
+{
+    InputStream  *ist = s->opaque;
+    D3D11VAContext *ctx = ist->hwaccel_ctx;
+
+    ist->hwaccel_uninit        = NULL;
+    ist->hwaccel_get_buffer    = NULL;
+    ist->hwaccel_retrieve_data = NULL;
+
+    av_buffer_unref(&ctx->hw_frames_ctx);
+    av_buffer_unref(&ctx->hw_device_ctx);
+
+    av_frame_free(&ctx->tmp_frame);
+
+    av_freep(&ist->hwaccel_ctx);
+    av_freep(&s->hwaccel_context);
+}
+
+static int d3d11va_get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
+{
+    InputStream  *ist = s->opaque;
+    D3D11VAContext *ctx = ist->hwaccel_ctx;
+
+    return av_hwframe_get_buffer(ctx->hw_frames_ctx, frame, 0);
+}
+
+static int d3d11va_retrieve_data(AVCodecContext *s, AVFrame *frame)
+{
+    InputStream        *ist = s->opaque;
+    D3D11VAContext     *ctx = ist->hwaccel_ctx;
+    int                ret;
+
+    ret = av_hwframe_transfer_data(ctx->tmp_frame, frame, 0);
+    if (ret < 0)
+        return ret;
+
+    ret = av_frame_copy_props(ctx->tmp_frame, frame);
+    if (ret < 0) {
+        av_frame_unref(ctx->tmp_frame);
+        return ret;
+    }
+
+    av_frame_unref(frame);
+    av_frame_move_ref(frame, ctx->tmp_frame);
+
+    return 0;
+}
+
+static int d3d11va_alloc(AVCodecContext *s)
+{
+    InputStream  *ist = s->opaque;
+    D3D11VAContext *ctx;
+
+    int ret;
+
+    ctx = av_mallocz(sizeof(*ctx));
+    if (!ctx)
+        return AVERROR(ENOMEM);
+
+    ist->hwaccel_ctx           = ctx;
+    ist->hwaccel_uninit        = d3d11va_uninit;
+    ist->hwaccel_get_buffer    = d3d11va_get_buffer;
+    ist->hwaccel_retrieve_data = d3d11va_retrieve_data;
+
+    ret = av_hwdevice_ctx_create(&ctx->hw_device_ctx, AV_HWDEVICE_TYPE_D3D11VA,
+                                 ist->hwaccel_device, NULL, 0);
+    if (ret < 0)
+        goto fail;
+
+    ctx->tmp_frame = av_frame_alloc();
+    if (!ctx->tmp_frame)
+        goto fail;
+
+    s->hwaccel_context = av_mallocz(sizeof(AVD3D11VAContext));
+    if (!s->hwaccel_context)
+        goto fail;
+
+    return 0;
+fail:
+    d3d11va_uninit(s);
+    return AVERROR(EINVAL);
+}
+
+static int d3d11va_get_decoder_configuration(AVCodecContext *s,
+                                           const D3D11_VIDEO_DECODER_DESC *desc,
+                                           D3D11_VIDEO_DECODER_CONFIG *config)
+{
+    InputStream  *ist = s->opaque;
+    int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
+    D3D11VAContext *ctx = ist->hwaccel_ctx;
+    unsigned cfg_count = 0, best_score = 0;
+    D3D11_VIDEO_DECODER_CONFIG best_cfg = {{0}};
+    HRESULT hr;
+    int i;
+    AVHWDeviceContext    *device_ctx = (AVHWDeviceContext*)ctx->hw_device_ctx->data;
+    AVD3D11VADeviceContext *device_hwctx = device_ctx->hwctx;
+
+    hr = ID3D11VideoDevice_GetVideoDecoderConfigCount(device_hwctx->video_device, desc, &cfg_count);
+    if (FAILED(hr)) {
+        av_log(NULL, loglevel, "Unable to retrieve decoder configurations\n");
+        return AVERROR(EINVAL);
+    }
+
+    for (i = 0; i < cfg_count; i++) {
+        unsigned score;
+        D3D11_VIDEO_DECODER_CONFIG cfg;
+        hr = ID3D11VideoDevice_GetVideoDecoderConfig(device_hwctx->video_device, desc, i, &cfg );
+        if (FAILED(hr)) {
+            av_log(NULL, loglevel, "Unable to retrieve decoder configurations. (hr=0x%lX)\n", hr);
+            return AVERROR(EINVAL);
+        }
+
+        if (cfg.ConfigBitstreamRaw == 1)
+            score = 1;
+        else if (s->codec_id == AV_CODEC_ID_H264 && cfg.ConfigBitstreamRaw == 2)
+            score = 2;
+        else
+            continue;
+        if (IsEqualGUID(&cfg.guidConfigBitstreamEncryption, &DXVA2_NoEncrypt))
+            score += 16;
+        if (score > best_score) {
+            best_score = score;
+            best_cfg   = cfg;
+        }
+    }
+
+    if (!best_score) {
+        av_log(NULL, loglevel, "No valid decoder configuration available\n");
+        return AVERROR(EINVAL);
+    }
+
+    *config = best_cfg;
+    return 0;
+}
+
+static int d3d11va_create_decoder(AVCodecContext *s)
+{
+    InputStream  *ist = s->opaque;
+    int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
+    D3D11VAContext *ctx = ist->hwaccel_ctx;
+    AVD3D11VAContext *dxva_ctx = s->hwaccel_context;
+    GUID *guid_list = NULL;
+    unsigned guid_count = 0, i, j;
+    GUID device_guid = GUID_NULL;
+    const DXGI_FORMAT surface_format = (s->sw_pix_fmt == AV_PIX_FMT_YUV420P10) ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12;
+    DXGI_FORMAT target_format = DXGI_FORMAT_UNKNOWN;
+    D3D11_VIDEO_DECODER_DESC desc = { 0 };
+    D3D11_VIDEO_DECODER_CONFIG config;
+    HRESULT hr;
+    int surface_alignment, num_surfaces;
+    int ret;
+
+    AVD3D11VAFramesContext *frames_hwctx;
+    AVHWFramesContext *frames_ctx;
+
+    AVHWDeviceContext    *device_ctx;
+    AVD3D11VADeviceContext *device_hwctx;
+    device_ctx = (AVHWDeviceContext*)ctx->hw_device_ctx->data;
+    device_hwctx = device_ctx->hwctx;
+
+    guid_count = ID3D11VideoDevice_GetVideoDecoderProfileCount(device_hwctx->video_device);
+    guid_list = av_malloc(sizeof(*guid_list) * guid_count);
+    if (guid_list==NULL) {
+        av_log(NULL, loglevel, "Failed to allocate decoder device GUIDs\n");
+        goto fail;
+    }
+    for (i=0; i<guid_count; i++) {
+        hr = ID3D11VideoDevice_GetVideoDecoderProfile(device_hwctx->video_device, i, &guid_list[i]);
+        if (FAILED(hr)) {
+            av_log(NULL, loglevel, "Failed to retrieve decoder GUID %d\n", i);
+            goto fail;
+        }
+    }
+
+    for (i = 0; d3d11va_modes[i].guid; i++) {
+        BOOL is_supported = FALSE;
+        const d3d11va_mode *mode = &d3d11va_modes[i];
+        if (mode->codec != s->codec_id)
+            continue;
+
+        for (j = 0; j < guid_count; j++) {
+            if (IsEqualGUID(mode->guid, &guid_list[j]))
+                break;
+        }
+        if (j == guid_count)
+            continue;
+
+        hr = ID3D11VideoDevice_CheckVideoDecoderFormat(device_hwctx->video_device, mode->guid,
+                                                       surface_format, &is_supported);
+        if (SUCCEEDED(hr) && is_supported) {
+            target_format = surface_format;
+            device_guid = *mode->guid;
+            break;
+        }
+    }
+
+    if (IsEqualGUID(&device_guid, &GUID_NULL)) {
+        av_log(NULL, loglevel, "No decoder device for codec found\n");
+        goto fail;
+    }
+
+    desc.SampleWidth  = s->coded_width;
+    desc.SampleHeight = s->coded_height;
+    desc.OutputFormat = target_format;
+    desc.Guid         = device_guid;
+
+    ret = d3d11va_get_decoder_configuration(s, &desc, &config);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    /* decoding MPEG-2 requires additional alignment on some Intel GPUs,
+       but it causes issues for H.264 on certain AMD GPUs..... */
+    if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO)
+        surface_alignment = 32;
+    /* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure
+       all coding features have enough room to work with */
+    else if  (s->codec_id == AV_CODEC_ID_HEVC)
+        surface_alignment = 128;
+    else
+        surface_alignment = 16;
+
+    /* 4 base work surfaces */
+    num_surfaces = 4;
+
+    /* add surfaces based on number of possible refs */
+    if (s->codec_id == AV_CODEC_ID_H264 || s->codec_id == AV_CODEC_ID_HEVC)
+        num_surfaces += 16;
+    else if (s->codec_id == AV_CODEC_ID_VP9)
+        num_surfaces += 8;
+    else
+        num_surfaces += 2;
+
+    /* add extra surfaces for frame threading */
+    if (s->active_thread_type & FF_THREAD_FRAME)
+        num_surfaces += s->thread_count;
+
+    ctx->hw_frames_ctx = av_hwframe_ctx_alloc(ctx->hw_device_ctx);
+    if (!ctx->hw_frames_ctx)
+        goto fail;
+    frames_ctx   = (AVHWFramesContext*)ctx->hw_frames_ctx->data;
+    frames_hwctx = frames_ctx->hwctx;
+    
+    frames_ctx->format            = AV_PIX_FMT_D3D11VA_VLD;
+    frames_ctx->sw_format         = (target_format == DXGI_FORMAT_P010 ? AV_PIX_FMT_P010 : AV_PIX_FMT_NV12);
+    frames_ctx->width             = FFALIGN(s->coded_width, surface_alignment);
+    frames_ctx->height            = FFALIGN(s->coded_height, surface_alignment);
+    frames_ctx->initial_pool_size = num_surfaces;
+
+    hr = ID3D11VideoDevice_CreateVideoDecoder(device_hwctx->video_device, &desc,
+                                              &config, &frames_hwctx->decoder_to_release);
+    if (FAILED(hr)) {
+        av_log(NULL, loglevel, "Failed to create D3D11VA video decoder\n");
+        goto fail;
+    }
+
+    ret = av_hwframe_ctx_init(ctx->hw_frames_ctx);
+    if (ret < 0) {
+        av_log(NULL, loglevel, "Failed to initialize the HW frames context\n");
+        goto fail;
+    }
+
+    ctx->decoder_config = config;
+
+    dxva_ctx->cfg           = &ctx->decoder_config;
+    dxva_ctx->decoder       = frames_hwctx->decoder_to_release;
+    dxva_ctx->surface       = frames_hwctx->surfaces;
+    dxva_ctx->surface_count = frames_hwctx->nb_surfaces;
+    dxva_ctx->video_context = device_hwctx->video_context;
+
+    if (IsEqualGUID(&device_guid, &DXVADDI_Intel_ModeH264_E))
+        dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO;
+
+    av_free(guid_list);
+    return 0;
+fail:
+    av_free(guid_list);
+    av_buffer_unref(&ctx->hw_frames_ctx);
+    return AVERROR(EINVAL);
+}
+
+int d3d11va_init(AVCodecContext *s)
+{
+    InputStream *ist = s->opaque;
+    int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
+    D3D11VAContext *ctx;
+    int ret;
+
+    if (!ist->hwaccel_ctx) {
+        ret = d3d11va_alloc(s);
+        if (ret < 0)
+            return ret;
+    }
+    ctx = ist->hwaccel_ctx;
+
+    if (s->codec_id == AV_CODEC_ID_H264 &&
+        (s->profile & ~FF_PROFILE_H264_CONSTRAINED) > FF_PROFILE_H264_HIGH) {
+        av_log(NULL, loglevel, "Unsupported H.264 profile for D3D11VA HWAccel: %d\n", s->profile);
+        return AVERROR(EINVAL);
+    }
+
+    if (s->codec_id == AV_CODEC_ID_HEVC &&
+        s->profile != FF_PROFILE_HEVC_MAIN && s->profile != FF_PROFILE_HEVC_MAIN_10) {
+        av_log(NULL, loglevel, "Unsupported HEVC profile for D3D11VA HWAccel: %d\n", s->profile);
+        return AVERROR(EINVAL);
+    }
+
+    av_buffer_unref(&ctx->hw_frames_ctx);
+
+    ret = d3d11va_create_decoder(s);
+    if (ret < 0) {
+        av_log(NULL, loglevel, "Error creating the D3D11VA decoder\n");
+        return ret;
+    }
+
+    return 0;
+}
diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c
index 6862456..9cc1a55 100644
--- a/ffmpeg_opt.c
+++ b/ffmpeg_opt.c
@@ -69,6 +69,9 @@  const HWAccel hwaccels[] = {
 #if HAVE_VDPAU_X11
     { "vdpau", vdpau_init, HWACCEL_VDPAU, AV_PIX_FMT_VDPAU },
 #endif
+#if HAVE_D3D11VA_LIB
+    { "d3d11va", d3d11va_init, HWACCEL_D3D11VA, AV_PIX_FMT_D3D11VA_VLD },
+#endif
 #if HAVE_DXVA2_LIB
     { "dxva2", dxva2_init, HWACCEL_DXVA2, AV_PIX_FMT_DXVA2_VLD },
 #endif
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..e29dc67 100644
--- a/libavutil/hwcontext.h
+++ b/libavutil/hwcontext.h
@@ -28,6 +28,7 @@  enum AVHWDeviceType {
     AV_HWDEVICE_TYPE_VDPAU,
     AV_HWDEVICE_TYPE_CUDA,
     AV_HWDEVICE_TYPE_VAAPI,
+    AV_HWDEVICE_TYPE_D3D11VA,
     AV_HWDEVICE_TYPE_DXVA2,
     AV_HWDEVICE_TYPE_QSV,
 };
diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
new file mode 100644
index 0000000..6ac1019
--- /dev/null
+++ b/libavutil/hwcontext_d3d11va.c
@@ -0,0 +1,436 @@ 
+/*
+ * 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;
+    }
+}
+
+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;
+
+    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;
+
+    ID3D11Device *d3d11_device;
+    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;
+    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);
+
+    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, D3D11_MAP_READ, 0, &LockedRect);
+    if (FAILED(hr)) {
+        av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface %lx\n", hr);
+        return AVERROR_UNKNOWN;
+    }
+
+    for (i = 0; download ? dst->data[i] : src->data[i]; i++)
+        surf_linesize[i] = LockedRect.RowPitch;
+
+    ID3D11Texture2D_GetDesc(frames_hwctx->staging_texture, &dstDesc);
+    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);
+
+    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;
+    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;
+    }
+
+    ID3D11Device        *d3d11_device;
+    ID3D11DeviceContext *d3d11_context;
+    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 = 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..c3e4437
--- /dev/null
+++ b/libavutil/hwcontext_d3d11va.h
@@ -0,0 +1,74 @@ 
+/*
+ * 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.
+     */
+    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;
+} 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, \