[FFmpeg-devel] [PATCH] ffmpeg: add ffmpeg_d3d11va

Stève Lhomme robux4 at videolabs.io
Wed Dec 14 11:30:35 EET 2016


On Tue, Dec 13, 2016 at 2:35 PM, wm4 <nfxjfg at googlemail.com> wrote:
> On Tue, 13 Dec 2016 14:19:35 +0100
> Steve Lhomme <robux4 at gmail.com> wrote:
>
>> From: Steve Lhomme <robux4 at 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 at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel


More information about the ffmpeg-devel mailing list