diff mbox series

[FFmpeg-devel,4/4] libavutil/qsv: enabling d3d11va support

Message ID 20200409175054.23468-4-artem.galin@gmail.com
State Superseded
Headers show
Series [FFmpeg-devel,1/4] fftools/qsv: enabling d3d11va/dxva2 device selection | expand

Checks

Context Check Description
andriy/ffmpeg-patchwork success Make fate finished

Commit Message

galinart April 9, 2020, 5:50 p.m. UTC
From: Artem Galin <artem.galin@intel.com>

Makes selection of d3d11va device type by default and over DirectX 9,
which is still supported but requires explicit selection.
This enables usage of non-powered/headless GPU, better HDR support.
Pool of resources is allocated as one texture with array of slices.

Added d3d11va device selection by vendor id.
Example: --init_hw_device d3d11va:,vendor=0x8086

DirectX 9 usage.
Example: --init_hw_device qsv:hw,child_device_type=0x8086

Signed-off-by: Artem Galin <artem.galin@intel.com>
---
 libavutil/hwcontext_d3d11va.c |  82 +++++++-
 libavutil/hwcontext_d3d11va.h |   8 +
 libavutil/hwcontext_qsv.c     | 339 +++++++++++++++++++++++++++++-----
 3 files changed, 371 insertions(+), 58 deletions(-)

Comments

Soft Works April 10, 2020, 6:29 a.m. UTC | #1
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> artem.galin@gmail.com
> Sent: Thursday, April 9, 2020 7:51 PM
> To: ffmpeg-devel@ffmpeg.org
> Cc: Artem Galin <artem.galin@intel.com>
> Subject: [FFmpeg-devel] [PATCH 4/4] libavutil/qsv: enabling d3d11va support
> 
> From: Artem Galin <artem.galin@intel.com>
> 
> Makes selection of d3d11va device type by default and over DirectX 9, which
> is still supported but requires explicit selection.
> This enables usage of non-powered/headless GPU, better HDR support.
> Pool of resources is allocated as one texture with array of slices.
> 
> Added d3d11va device selection by vendor id.
> Example: --init_hw_device d3d11va:,vendor=0x8086
> 
> DirectX 9 usage.
> Example: --init_hw_device qsv:hw,child_device_type=0x8086

For transparency: I had developed a similar change some months ago which is
a bit more complete with regards to filtering and compatibility with older
CPU models, though I hadn't found the time to polish and submit the code yet.
Now I'm sharing my variant with the Intel guys allowing them to cherry-pick those
things they are missing.

Nonetheless there are things open for discussion. This is my point of view:

1. The QSV codecs are implemented as standalone hw encoders and decoders in
ffmpeg, and as such, there should be a way to switch between DX9 and DX11
that doesn't require to explicitly work with hw contexts.

https://github.com/softworkz/ffmpeg_dx11/issues/3

2. With the -qsv_device parameter it has always been possible to specify
an individual GPU adapter id. It doesn't make sense to me to drop this
behavior and it makes even less sense to me to introduce a 'vendor' 
parameter, which is in no way suitable to select a specific device.

https://github.com/softworkz/ffmpeg_dx11/issues/4

3. I am skeptical about making DX11 the default. There are many cases 
where DX11 doesn't work with QuickSync. Changing the default would
be a breaking change. More details here:

https://github.com/softworkz/ffmpeg_dx11/issues/2

Note: For privacy, I have copied only my part of the discussion to a 
public GitHub repo.

softworkz
diff mbox series

Patch

diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
index c8ae58f908..9d7615eb55 100644
--- a/libavutil/hwcontext_d3d11va.c
+++ b/libavutil/hwcontext_d3d11va.c
@@ -72,7 +72,7 @@  static av_cold void load_functions(void)
 }
 
 typedef struct D3D11VAFramesContext {
-    int nb_surfaces_used;
+    int nb_surfaces;
 
     DXGI_FORMAT format;
 
@@ -112,6 +112,8 @@  static void d3d11va_frames_uninit(AVHWFramesContext *ctx)
     if (s->staging_texture)
         ID3D11Texture2D_Release(s->staging_texture);
     s->staging_texture = NULL;
+
+    av_freep(&frames_hwctx->texture_infos);
 }
 
 static int d3d11va_frames_get_constraints(AVHWDeviceContext *ctx,
@@ -152,8 +154,9 @@  static void free_texture(void *opaque, uint8_t *data)
     av_free(data);
 }
 
-static AVBufferRef *wrap_texture_buf(ID3D11Texture2D *tex, int index)
+static AVBufferRef *wrap_texture_buf(AVHWFramesContext *ctx, ID3D11Texture2D *tex, int index)
 {
+    AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
     AVBufferRef *buf;
     AVD3D11FrameDescriptor *desc = av_mallocz(sizeof(*desc));
     if (!desc) {
@@ -161,6 +164,10 @@  static AVBufferRef *wrap_texture_buf(ID3D11Texture2D *tex, int index)
         return NULL;
     }
 
+    frames_hwctx->texture_infos[frames_hwctx->nb_surfaces_used].texture = tex;
+    frames_hwctx->texture_infos[frames_hwctx->nb_surfaces_used].index = index;
+    frames_hwctx->nb_surfaces_used++;
+
     desc->texture = tex;
     desc->index   = index;
 
@@ -199,13 +206,12 @@  static AVBufferRef *d3d11va_alloc_single(AVHWFramesContext *ctx)
         return NULL;
     }
 
-    return wrap_texture_buf(tex, 0);
+    return wrap_texture_buf(ctx, tex, 0);
 }
 
 static AVBufferRef *d3d11va_pool_alloc(void *opaque, int size)
 {
     AVHWFramesContext        *ctx = (AVHWFramesContext*)opaque;
-    D3D11VAFramesContext       *s = ctx->internal->priv;
     AVD3D11VAFramesContext *hwctx = ctx->hwctx;
     D3D11_TEXTURE2D_DESC  texDesc;
 
@@ -214,13 +220,13 @@  static AVBufferRef *d3d11va_pool_alloc(void *opaque, int size)
 
     ID3D11Texture2D_GetDesc(hwctx->texture, &texDesc);
 
-    if (s->nb_surfaces_used >= texDesc.ArraySize) {
+    if (hwctx->nb_surfaces_used >= texDesc.ArraySize) {
         av_log(ctx, AV_LOG_ERROR, "Static surface pool size exceeded.\n");
         return NULL;
     }
 
     ID3D11Texture2D_AddRef(hwctx->texture);
-    return wrap_texture_buf(hwctx->texture, s->nb_surfaces_used++);
+    return wrap_texture_buf(ctx, hwctx->texture, hwctx->nb_surfaces_used);
 }
 
 static int d3d11va_frames_init(AVHWFramesContext *ctx)
@@ -267,7 +273,7 @@  static int d3d11va_frames_init(AVHWFramesContext *ctx)
             av_log(ctx, AV_LOG_ERROR, "User-provided texture has mismatching parameters\n");
             return AVERROR(EINVAL);
         }
-    } else if (texDesc.ArraySize > 0) {
+    } else if (!(texDesc.BindFlags & D3D11_BIND_RENDER_TARGET) && texDesc.ArraySize > 0) {
         hr = ID3D11Device_CreateTexture2D(device_hwctx->device, &texDesc, NULL, &hwctx->texture);
         if (FAILED(hr)) {
             av_log(ctx, AV_LOG_ERROR, "Could not create the texture (%lx)\n", (long)hr);
@@ -275,6 +281,12 @@  static int d3d11va_frames_init(AVHWFramesContext *ctx)
         }
     }
 
+    hwctx->texture_infos = av_mallocz_array(ctx->initial_pool_size, sizeof(*hwctx->texture_infos));
+    if (!hwctx->texture_infos)
+        return AVERROR(ENOMEM);
+
+    s->nb_surfaces = ctx->initial_pool_size;
+
     ctx->internal->pool_internal = av_buffer_pool_init2(sizeof(AVD3D11FrameDescriptor),
                                                         ctx, d3d11va_pool_alloc, NULL);
     if (!ctx->internal->pool_internal)
@@ -511,15 +523,56 @@  static void d3d11va_device_uninit(AVHWDeviceContext *hwdev)
     }
 }
 
+static int d3d11va_device_find_adapter_by_vendor_id(AVHWDeviceContext *ctx, UINT creationFlags, char *vendor)
+{
+    HRESULT hr;
+    IDXGIAdapter *adapter = NULL;
+    int adapter_id = 0;
+    IDXGIFactory2 *factory;
+    long int vendor_id = strtol(vendor, NULL, 0);
+    hr = mCreateDXGIFactory(&IID_IDXGIFactory2, (void **)&factory);
+    while (IDXGIFactory2_EnumAdapters(factory, adapter_id++, &adapter) != DXGI_ERROR_NOT_FOUND) {
+        ID3D11Device* device = NULL;
+        DXGI_ADAPTER_DESC adapter_desc;
+
+        hr = mD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, creationFlags, NULL, 0, D3D11_SDK_VERSION, &device, NULL, NULL);
+        if (FAILED(hr)) {
+            av_log(ctx, AV_LOG_ERROR, "D3D11CreateDevice returned error\n");
+            continue;
+        }
+
+        hr = IDXGIAdapter2_GetDesc(adapter, &adapter_desc);
+        if (FAILED(hr)) {
+            av_log(ctx, AV_LOG_ERROR, "IDXGIAdapter2_GetDesc returned error\n");
+            continue;
+        }
+
+        if (device)
+            ID3D11Device_Release(device);
+
+        if (adapter)
+            IDXGIAdapter_Release(adapter);
+
+        if (adapter_desc.VendorId == vendor_id) {
+            IDXGIFactory2_Release(factory);
+            return adapter_id - 1;
+        }
+    }
+    IDXGIFactory2_Release(factory);
+    return -1;
+}
+
 static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
                                  AVDictionary *opts, int flags)
 {
     AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
 
     HRESULT hr;
+    AVDictionaryEntry *e;
     IDXGIAdapter           *pAdapter = NULL;
     ID3D10Multithread      *pMultithread;
     UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
+    int adapter = -1;
     int is_debug       = !!av_dict_get(opts, "debug", NULL, 0);
     int ret;
 
@@ -539,11 +592,23 @@  static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
         return AVERROR_UNKNOWN;
     }
 
+    e = av_dict_get(opts, "vendor", NULL, 0);
+    if (e) {
+        adapter = d3d11va_device_find_adapter_by_vendor_id(ctx, creationFlags, e ? e->value : NULL);
+        if (adapter < 0) {
+            av_log(ctx, AV_LOG_ERROR, "Failed to find d3d11va adapter by vendor id %s\n", e ? e->value : NULL);
+            return AVERROR_UNKNOWN;
+        }
+    }
+
     if (device) {
+        adapter = atoi(device);
+    }
+
+    if (adapter >= 0) {
         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);
@@ -568,6 +633,7 @@  static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
         return AVERROR_UNKNOWN;
     }
 
+    av_log(ctx, AV_LOG_VERBOSE, "Using D3D11 device.\n");
     hr = ID3D11Device_QueryInterface(device_hwctx->device, &IID_ID3D10Multithread, (void **)&pMultithread);
     if (SUCCEEDED(hr)) {
         ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
diff --git a/libavutil/hwcontext_d3d11va.h b/libavutil/hwcontext_d3d11va.h
index 9f91e9b1b6..4312fe0b62 100644
--- a/libavutil/hwcontext_d3d11va.h
+++ b/libavutil/hwcontext_d3d11va.h
@@ -39,6 +39,11 @@ 
 #include <d3d11.h>
 #include <stdint.h>
 
+typedef struct D3D11TextureInfo {
+    ID3D11Texture2D *texture;
+    int index;
+} D3D11TextureInfo;
+
 /**
  * This struct is allocated as AVHWDeviceContext.hwctx
  */
@@ -164,6 +169,9 @@  typedef struct AVD3D11VAFramesContext {
      * This field is ignored/invalid if a user-allocated texture is provided.
      */
     UINT MiscFlags;
+
+    D3D11TextureInfo *texture_infos;
+    int nb_surfaces_used;
 } AVD3D11VAFramesContext;
 
 #endif /* AVUTIL_HWCONTEXT_D3D11VA_H */
diff --git a/libavutil/hwcontext_qsv.c b/libavutil/hwcontext_qsv.c
index b1b67400de..1bc02c41eb 100644
--- a/libavutil/hwcontext_qsv.c
+++ b/libavutil/hwcontext_qsv.c
@@ -27,9 +27,13 @@ 
 #include <pthread.h>
 #endif
 
+#define COBJMACROS
 #if CONFIG_VAAPI
 #include "hwcontext_vaapi.h"
 #endif
+#if CONFIG_D3D11VA
+#include "hwcontext_d3d11va.h"
+#endif
 #if CONFIG_DXVA2
 #include "hwcontext_dxva2.h"
 #endif
@@ -44,6 +48,8 @@ 
 #include "pixdesc.h"
 #include "time.h"
 
+#define MFX_IMPL_VIA_MASK(impl) (0x0f00 & (impl))
+
 typedef struct QSVDevicePriv {
     AVBufferRef *child_device_ctx;
 } QSVDevicePriv;
@@ -70,6 +76,7 @@  typedef struct QSVFramesContext {
 
     AVBufferRef *child_frames_ref;
     mfxFrameSurface1 *surfaces_internal;
+    mfxHDLPair *handle_pairs_internal;
     int             nb_surfaces_used;
 
     // used in the frame allocator for non-opaque surfaces
@@ -89,6 +96,9 @@  static const struct {
 #if CONFIG_VAAPI
     { MFX_HANDLE_VA_DISPLAY,          AV_HWDEVICE_TYPE_VAAPI, AV_PIX_FMT_VAAPI },
 #endif
+#if CONFIG_D3D11VA
+    { MFX_HANDLE_D3D11_DEVICE,        AV_HWDEVICE_TYPE_D3D11VA, AV_PIX_FMT_D3D11 },
+#endif
 #if CONFIG_DXVA2
     { MFX_HANDLE_D3D9_DEVICE_MANAGER, AV_HWDEVICE_TYPE_DXVA2, AV_PIX_FMT_DXVA2_VLD },
 #endif
@@ -115,29 +125,32 @@  static uint32_t qsv_fourcc_from_pix_fmt(enum AVPixelFormat pix_fmt)
     return 0;
 }
 
+#if CONFIG_D3D11VA
+static uint32_t qsv_get_d3d11va_bind_flags(int mem_type)
+{
+    uint32_t bind_flags = 0;
+
+    if ((mem_type & MFX_MEMTYPE_VIDEO_MEMORY_ENCODER_TARGET) && (mem_type & MFX_MEMTYPE_INTERNAL_FRAME))
+        bind_flags = D3D11_BIND_DECODER | D3D11_BIND_VIDEO_ENCODER;
+    else
+        bind_flags = D3D11_BIND_DECODER;
+
+    if ((MFX_MEMTYPE_FROM_VPPOUT & mem_type) || (MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET & mem_type))
+        bind_flags = D3D11_BIND_RENDER_TARGET;
+
+    return bind_flags;
+}
+#endif
+
 static int qsv_device_init(AVHWDeviceContext *ctx)
 {
     AVQSVDeviceContext *hwctx = ctx->hwctx;
     QSVDeviceContext       *s = ctx->internal->priv;
+    mfxHandleType handle_type = MFX_HANDLE_D3D11_DEVICE;
 
     mfxStatus err;
     int i;
 
-    for (i = 0; supported_handle_types[i].handle_type; i++) {
-        err = MFXVideoCORE_GetHandle(hwctx->session, supported_handle_types[i].handle_type,
-                                     &s->handle);
-        if (err == MFX_ERR_NONE) {
-            s->handle_type       = supported_handle_types[i].handle_type;
-            s->child_device_type = supported_handle_types[i].device_type;
-            s->child_pix_fmt     = supported_handle_types[i].pix_fmt;
-            break;
-        }
-    }
-    if (!s->handle) {
-        av_log(ctx, AV_LOG_VERBOSE, "No supported hw handle could be retrieved "
-               "from the session\n");
-    }
-
     err = MFXQueryIMPL(hwctx->session, &s->impl);
     if (err == MFX_ERR_NONE)
         err = MFXQueryVersion(hwctx->session, &s->ver);
@@ -146,6 +159,31 @@  static int qsv_device_init(AVHWDeviceContext *ctx)
         return AVERROR_UNKNOWN;
     }
 
+    if (MFX_IMPL_VIA_D3D11 == MFX_IMPL_VIA_MASK(s->impl)) {
+        handle_type = MFX_HANDLE_D3D11_DEVICE;
+    } else if (MFX_IMPL_VIA_D3D9 == MFX_IMPL_VIA_MASK(s->impl)) {
+        handle_type = MFX_HANDLE_D3D9_DEVICE_MANAGER;
+    } else if (MFX_IMPL_VIA_VAAPI == MFX_IMPL_VIA_MASK(s->impl)) {
+        handle_type = MFX_HANDLE_VA_DISPLAY;
+    }
+
+    for (i = 0; supported_handle_types[i].handle_type; i++) {
+        if (supported_handle_types[i].handle_type == handle_type) {
+            err = MFXVideoCORE_GetHandle(hwctx->session, supported_handle_types[i].handle_type,
+                                        &s->handle);
+            if (err == MFX_ERR_NONE) {
+                s->handle_type       = supported_handle_types[i].handle_type;
+                s->child_device_type = supported_handle_types[i].device_type;
+                s->child_pix_fmt     = supported_handle_types[i].pix_fmt;
+                break;
+            }
+        }
+    }
+    if (!s->handle) {
+        av_log(ctx, AV_LOG_VERBOSE, "No supported hw handle could be retrieved "
+               "from the session\n");
+    }
+
     return 0;
 }
 
@@ -175,6 +213,7 @@  static void qsv_frames_uninit(AVHWFramesContext *ctx)
     av_freep(&s->mem_ids);
     av_freep(&s->surface_ptrs);
     av_freep(&s->surfaces_internal);
+    av_freep(&s->handle_pairs_internal);
     av_buffer_unref(&s->child_frames_ref);
 }
 
@@ -190,6 +229,8 @@  static AVBufferRef *qsv_pool_alloc(void *opaque, int size)
 
     if (s->nb_surfaces_used < hwctx->nb_surfaces) {
         s->nb_surfaces_used++;
+        av_buffer_create((uint8_t*)(s->handle_pairs_internal + s->nb_surfaces_used - 1),
+                                sizeof(*s->handle_pairs_internal), qsv_pool_release_dummy, NULL, 0);
         return av_buffer_create((uint8_t*)(s->surfaces_internal + s->nb_surfaces_used - 1),
                                 sizeof(*hwctx->surfaces), qsv_pool_release_dummy, NULL, 0);
     }
@@ -229,6 +270,13 @@  static int qsv_init_child_ctx(AVHWFramesContext *ctx)
         child_device_hwctx->display = (VADisplay)device_priv->handle;
     }
 #endif
+#if CONFIG_D3D11VA
+    if (child_device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) {
+        AVD3D11VADeviceContext *child_device_hwctx = child_device_ctx->hwctx;
+        ID3D11Device_AddRef((ID3D11Device*)device_priv->handle);
+        child_device_hwctx->device = (ID3D11Device*)device_priv->handle;
+    }
+#endif
 #if CONFIG_DXVA2
     if (child_device_ctx->type == AV_HWDEVICE_TYPE_DXVA2) {
         AVDXVA2DeviceContext *child_device_hwctx = child_device_ctx->hwctx;
@@ -255,6 +303,16 @@  static int qsv_init_child_ctx(AVHWFramesContext *ctx)
     child_frames_ctx->width             = FFALIGN(ctx->width, 16);
     child_frames_ctx->height            = FFALIGN(ctx->height, 16);
 
+#if CONFIG_D3D11VA
+    if (child_device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) {
+        AVD3D11VAFramesContext *child_frames_hwctx = child_frames_ctx->hwctx;
+        if (hwctx->frame_type == 0)
+            hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET;
+        if (hwctx->frame_type & MFX_MEMTYPE_SHARED_RESOURCE)
+            child_frames_hwctx->MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+        child_frames_hwctx->BindFlags = qsv_get_d3d11va_bind_flags(hwctx->frame_type);
+    }
+#endif
 #if CONFIG_DXVA2
     if (child_device_ctx->type == AV_HWDEVICE_TYPE_DXVA2) {
         AVDXVA2FramesContext *child_frames_hwctx = child_frames_ctx->hwctx;
@@ -279,11 +337,33 @@  static int qsv_init_child_ctx(AVHWFramesContext *ctx)
         hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET;
     }
 #endif
+#if CONFIG_D3D11VA
+    if (child_device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) {
+        AVD3D11VAFramesContext *child_frames_hwctx = child_frames_ctx->hwctx;
+        for (i = 0; i < ctx->initial_pool_size; i++) {
+            s->handle_pairs_internal[i].first = (mfxMemId)child_frames_hwctx->texture_infos[i].texture;
+            if(child_frames_hwctx->BindFlags & D3D11_BIND_RENDER_TARGET) {
+                s->handle_pairs_internal[i].second = (mfxMemId)MFX_INFINITE;
+            } else {
+                s->handle_pairs_internal[i].second = (mfxMemId)child_frames_hwctx->texture_infos[i].index;
+            }
+            s->surfaces_internal[i].Data.MemId = (mfxMemId)&s->handle_pairs_internal[i];
+        }
+        if (child_frames_hwctx->BindFlags & D3D11_BIND_RENDER_TARGET) {
+            hwctx->frame_type |= MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET;
+        } else {
+            hwctx->frame_type |= MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET;
+        }
+    }
+#endif
 #if CONFIG_DXVA2
     if (child_device_ctx->type == AV_HWDEVICE_TYPE_DXVA2) {
         AVDXVA2FramesContext *child_frames_hwctx = child_frames_ctx->hwctx;
-        for (i = 0; i < ctx->initial_pool_size; i++)
-            s->surfaces_internal[i].Data.MemId = (mfxMemId)child_frames_hwctx->surfaces[i];
+        for (i = 0; i < ctx->initial_pool_size; i++) {
+            s->handle_pairs_internal[i].first = (mfxMemId)child_frames_hwctx->surfaces[i];
+            s->handle_pairs_internal[i].second = (mfxMemId)MFX_INFINITE;
+            s->surfaces_internal[i].Data.MemId = (mfxMemId)&s->handle_pairs_internal[i];
+        }
         if (child_frames_hwctx->surface_type == DXVA2_VideoProcessorRenderTarget)
             hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET;
         else
@@ -348,6 +428,11 @@  static int qsv_init_pool(AVHWFramesContext *ctx, uint32_t fourcc)
         return AVERROR(EINVAL);
     }
 
+    s->handle_pairs_internal = av_mallocz_array(ctx->initial_pool_size,
+                                            sizeof(*s->handle_pairs_internal));
+    if (!s->handle_pairs_internal)
+        return AVERROR(ENOMEM);
+
     s->surfaces_internal = av_mallocz_array(ctx->initial_pool_size,
                                             sizeof(*s->surfaces_internal));
     if (!s->surfaces_internal)
@@ -421,7 +506,17 @@  static mfxStatus frame_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr)
 
 static mfxStatus frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl)
 {
+#if CONFIG_VAAPI
     *hdl = mid;
+#else
+    mfxHDLPair *pair_dst = (mfxHDLPair*)hdl;
+    mfxHDLPair *pair_src = (mfxHDLPair*)mid;
+
+    pair_dst->first = pair_src->first;
+
+    if (pair_src->second != (mfxMemId)MFX_INFINITE)
+        pair_dst->second = pair_src->second;
+#endif
     return MFX_ERR_NONE;
 }
 
@@ -621,6 +716,18 @@  static int qsv_frames_derive_from(AVHWFramesContext *dst_ctx,
         }
         break;
 #endif
+#if CONFIG_D3D11VA
+    case AV_HWDEVICE_TYPE_D3D11VA:
+        {
+            AVD3D11VAFramesContext *dst_hwctx = dst_ctx->hwctx;
+            mfxHDLPair *pair = (mfxHDLPair*)src_hwctx->surfaces[i].Data.MemId;
+            dst_hwctx->texture = (ID3D11Texture2D*)pair->first;
+            if (src_hwctx->frame_type & MFX_MEMTYPE_SHARED_RESOURCE)
+                dst_hwctx->MiscFlags = D3D11_RESOURCE_MISC_SHARED;
+            dst_hwctx->BindFlags = qsv_get_d3d11va_bind_flags(src_hwctx->frame_type);
+        }
+        break;
+#endif
 #if CONFIG_DXVA2
     case AV_HWDEVICE_TYPE_DXVA2:
         {
@@ -629,9 +736,10 @@  static int qsv_frames_derive_from(AVHWFramesContext *dst_ctx,
                                                    sizeof(*dst_hwctx->surfaces));
             if (!dst_hwctx->surfaces)
                 return AVERROR(ENOMEM);
-            for (i = 0; i < src_hwctx->nb_surfaces; i++)
-                dst_hwctx->surfaces[i] =
-                    (IDirect3DSurface9*)src_hwctx->surfaces[i].Data.MemId;
+            for (i = 0; i < src_hwctx->nb_surfaces; i++) {
+                mfxHDLPair *pair = (mfxHDLPair*)src_hwctx->surfaces[i].Data.MemId;
+                dst_hwctx->surfaces[i] = (IDirect3DSurface9*)pair->first;
+            }
             dst_hwctx->nb_surfaces = src_hwctx->nb_surfaces;
             if (src_hwctx->frame_type == MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET)
                 dst_hwctx->surface_type = DXVA2_VideoDecoderRenderTarget;
@@ -668,10 +776,21 @@  static int qsv_map_from(AVHWFramesContext *ctx,
         child_data = (uint8_t*)(intptr_t)*(VASurfaceID*)surf->Data.MemId;
         break;
 #endif
+#if CONFIG_D3D11VA
+    case AV_HWDEVICE_TYPE_D3D11VA:
+    {
+        mfxHDLPair *pair = (mfxHDLPair*)surf->Data.MemId;
+        child_data = pair->first;
+        break;
+    }
+#endif
 #if CONFIG_DXVA2
     case AV_HWDEVICE_TYPE_DXVA2:
-        child_data = surf->Data.MemId;
+    {
+        mfxHDLPair *pair = (mfxHDLPair*)surf->Data.MemId;
+        child_data = pair->first;
         break;
+    }
 #endif
     default:
         return AVERROR(ENOSYS);
@@ -685,7 +804,14 @@  static int qsv_map_from(AVHWFramesContext *ctx,
 
         dst->width   = src->width;
         dst->height  = src->height;
-        dst->data[3] = child_data;
+
+       if (child_frames_ctx->device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) {
+            mfxHDLPair *pair = (mfxHDLPair*)surf->Data.MemId;
+            dst->data[0] = pair->first;
+            dst->data[1] = pair->second;
+        } else {
+            dst->data[3] = child_data;
+        }
 
         return 0;
     }
@@ -708,7 +834,14 @@  static int qsv_map_from(AVHWFramesContext *ctx,
     dummy->format        = child_frames_ctx->format;
     dummy->width         = src->width;
     dummy->height        = src->height;
-    dummy->data[3]       = child_data;
+
+    if (child_frames_ctx->device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) {
+        mfxHDLPair *pair = (mfxHDLPair*)surf->Data.MemId;
+        dummy->data[0] = pair->first;
+        dummy->data[1] = pair->second;
+    } else {
+        dummy->data[3] = child_data;
+    }
 
     ret = av_hwframe_map(dst, dummy, flags);
 
@@ -954,6 +1087,12 @@  static int qsv_frames_derive_to(AVHWFramesContext *dst_ctx,
     AVQSVFramesContext *dst_hwctx = dst_ctx->hwctx;
     int i;
 
+    if (src_ctx->initial_pool_size == 0) {
+        av_log(dst_ctx, AV_LOG_ERROR, "Only fixed-size pools can be "
+            "mapped to QSV frames.\n");
+        return AVERROR(EINVAL);
+    }
+
     switch (src_ctx->device_ctx->type) {
 #if CONFIG_VAAPI
     case AV_HWDEVICE_TYPE_VAAPI:
@@ -972,6 +1111,36 @@  static int qsv_frames_derive_to(AVHWFramesContext *dst_ctx,
         }
         break;
 #endif
+#if CONFIG_D3D11VA
+    case AV_HWDEVICE_TYPE_D3D11VA:
+        {
+            AVD3D11VAFramesContext *src_hwctx = src_ctx->hwctx;
+            s->handle_pairs_internal = av_mallocz_array(src_ctx->initial_pool_size, sizeof(*s->handle_pairs_internal));
+            if (!s->handle_pairs_internal)
+                return AVERROR(ENOMEM);
+            s->surfaces_internal = av_mallocz_array(src_ctx->initial_pool_size,
+                                                    sizeof(*s->surfaces_internal));
+            if (!s->surfaces_internal)
+                return AVERROR(ENOMEM);
+            for (i = 0; i < src_ctx->initial_pool_size; i++) {
+                qsv_init_surface(dst_ctx, &s->surfaces_internal[i]);
+                s->handle_pairs_internal[i].first = (mfxMemId)src_hwctx->texture_infos[i].texture;
+                if (src_hwctx->BindFlags & D3D11_BIND_RENDER_TARGET) {
+                    s->handle_pairs_internal[i].second = (mfxMemId)MFX_INFINITE;
+                } else {
+                    s->handle_pairs_internal[i].second = (mfxMemId)src_hwctx->texture_infos[i].index;
+                }
+                s->surfaces_internal[i].Data.MemId = (mfxMemId)&s->handle_pairs_internal[i];
+            }
+            dst_hwctx->nb_surfaces = src_ctx->initial_pool_size;
+            if (src_hwctx->BindFlags & D3D11_BIND_RENDER_TARGET) {
+                dst_hwctx->frame_type |= MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET;
+            } else {
+                dst_hwctx->frame_type |= MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET;
+            }
+        }
+        break;
+#endif
 #if CONFIG_DXVA2
     case AV_HWDEVICE_TYPE_DXVA2:
         {
@@ -982,7 +1151,9 @@  static int qsv_frames_derive_to(AVHWFramesContext *dst_ctx,
                 return AVERROR(ENOMEM);
             for (i = 0; i < src_hwctx->nb_surfaces; i++) {
                 qsv_init_surface(dst_ctx, &s->surfaces_internal[i]);
-                s->surfaces_internal[i].Data.MemId = (mfxMemId)src_hwctx->surfaces[i];
+                s->handle_pairs_internal[i].first = (mfxMemId)src_hwctx->surfaces[i];
+                s->handle_pairs_internal[i].second = (mfxMemId)MFX_INFINITE;
+                s->surfaces_internal[i].Data.MemId = (mfxMemId)&s->handle_pairs_internal[i];
             }
             dst_hwctx->nb_surfaces = src_hwctx->nb_surfaces;
             if (src_hwctx->surface_type == DXVA2_VideoProcessorRenderTarget)
@@ -1005,21 +1176,43 @@  static int qsv_map_to(AVHWFramesContext *dst_ctx,
                       AVFrame *dst, const AVFrame *src, int flags)
 {
     AVQSVFramesContext *hwctx = dst_ctx->hwctx;
-    int i, err;
+    int i, err, index = -1;
 
-    for (i = 0; i < hwctx->nb_surfaces; i++) {
+    for (i = 0; i < hwctx->nb_surfaces && index < 0; i++) {
+        switch(src->format) {
 #if CONFIG_VAAPI
-        if (*(VASurfaceID*)hwctx->surfaces[i].Data.MemId ==
-            (VASurfaceID)(uintptr_t)src->data[3])
+        case AV_PIX_FMT_VAAPI:
+            if (*(VASurfaceID*)hwctx->surfaces[i].Data.MemId ==
+                (VASurfaceID)(uintptr_t)src->data[3])
+                index = i;
             break;
 #endif
+#if CONFIG_D3D11VA
+        case AV_PIX_FMT_D3D11:
+        {
+            mfxHDLPair *pair = (mfxHDLPair*)hwctx->surfaces[i].Data.MemId;
+            if ((ID3D11Texture2D*)pair->first ==
+                (ID3D11Texture2D*)(uintptr_t)src->data[0]
+                && pair->second == (mfxMemId)src->data[1]) {
+                index = i;
+                break;
+            }
+        }
+#endif
 #if CONFIG_DXVA2
-        if ((IDirect3DSurface9*)hwctx->surfaces[i].Data.MemId ==
-            (IDirect3DSurface9*)(uintptr_t)src->data[3])
-            break;
+        case AV_PIX_FMT_DXVA2_VLD:
+        {
+            mfxHDLPair *pair = (mfxHDLPair*)hwctx->surfaces[i].Data.MemId;
+            if ((IDirect3DSurface9*)pair->first ==
+                (IDirect3DSurface9*)(uintptr_t)src->data[3]) {
+                index = i;
+                break;
+            }
+        }
 #endif
+        }
     }
-    if (i >= hwctx->nb_surfaces) {
+    if (index < 0) {
         av_log(dst_ctx, AV_LOG_ERROR, "Trying to map from a surface which "
                "is not in the mapped frames context.\n");
         return AVERROR(EINVAL);
@@ -1032,7 +1225,7 @@  static int qsv_map_to(AVHWFramesContext *dst_ctx,
 
     dst->width   = src->width;
     dst->height  = src->height;
-    dst->data[3] = (uint8_t*)&hwctx->surfaces[i];
+    dst->data[3] = (uint8_t*)&hwctx->surfaces[index];
 
     return 0;
 }
@@ -1074,7 +1267,7 @@  static void qsv_device_free(AVHWDeviceContext *ctx)
     av_freep(&priv);
 }
 
-static mfxIMPL choose_implementation(const char *device)
+static mfxIMPL choose_implementation(const char *device, enum AVHWDeviceType child_device_type)
 {
     static const struct {
         const char *name;
@@ -1103,6 +1296,10 @@  static mfxIMPL choose_implementation(const char *device)
             impl = strtol(device, NULL, 0);
     }
 
+    if ( (child_device_type == AV_HWDEVICE_TYPE_D3D11VA) && (impl != MFX_IMPL_SOFTWARE) ) {
+        impl |= MFX_IMPL_VIA_D3D11;
+    }
+
     return impl;
 }
 
@@ -1129,6 +1326,15 @@  static int qsv_device_derive_from_child(AVHWDeviceContext *ctx,
         }
         break;
 #endif
+#if CONFIG_D3D11VA
+    case AV_HWDEVICE_TYPE_D3D11VA:
+        {
+            AVD3D11VADeviceContext *child_device_hwctx = child_device_ctx->hwctx;
+            handle_type = MFX_HANDLE_D3D11_DEVICE;
+            handle = (mfxHDL)child_device_hwctx->device;
+        }
+        break;
+#endif
 #if CONFIG_DXVA2
     case AV_HWDEVICE_TYPE_DXVA2:
         {
@@ -1191,7 +1397,9 @@  fail:
 static int qsv_device_derive(AVHWDeviceContext *ctx,
                              AVHWDeviceContext *child_device_ctx, int flags)
 {
-    return qsv_device_derive_from_child(ctx, MFX_IMPL_HARDWARE_ANY,
+    mfxIMPL impl;
+    impl = choose_implementation("hw_any", child_device_ctx->type);
+    return qsv_device_derive_from_child(ctx, impl,
                                         child_device_ctx, flags);
 }
 
@@ -1214,35 +1422,66 @@  static int qsv_device_create(AVHWDeviceContext *ctx, const char *device,
     ctx->user_opaque = priv;
     ctx->free        = qsv_device_free;
 
-    e = av_dict_get(opts, "child_device", NULL, 0);
-
-    child_device_opts = NULL;
-    if (CONFIG_VAAPI) {
+    e = av_dict_get(opts, "child_device_type", NULL, 0);
+    if (e) {
+        child_device_type = av_hwdevice_find_type_by_name(e ? e->value : NULL);
+        if (child_device_type == AV_HWDEVICE_TYPE_NONE) {
+            av_log(ctx, AV_LOG_ERROR, "Unknown child device type "
+                   "\"%s\".\n", e ? e->value : NULL);
+            return AVERROR(EINVAL);
+        }
+    } else if (CONFIG_VAAPI) {
         child_device_type = AV_HWDEVICE_TYPE_VAAPI;
-        // libmfx does not actually implement VAAPI properly, rather it
-        // depends on the specific behaviour of a matching iHD driver when
-        // used on recent Intel hardware.  Set options to the VAAPI device
-        // creation so that we should pick a usable setup by default if
-        // possible, even when multiple devices and drivers are available.
-        av_dict_set(&child_device_opts, "kernel_driver", "i915", 0);
-        av_dict_set(&child_device_opts, "driver",        "iHD",  0);
-    } else if (CONFIG_DXVA2)
+    } else if (CONFIG_D3D11VA) {
+        child_device_type = AV_HWDEVICE_TYPE_D3D11VA;
+    } else if (CONFIG_DXVA2) {
         child_device_type = AV_HWDEVICE_TYPE_DXVA2;
-    else {
+    } else {
         av_log(ctx, AV_LOG_ERROR, "No supported child device type is enabled\n");
         return AVERROR(ENOSYS);
     }
 
+    child_device_opts = NULL;
+    switch (child_device_type) {
+#if CONFIG_VAAPI
+    case AV_HWDEVICE_TYPE_VAAPI:
+        {
+            // libmfx does not actually implement VAAPI properly, rather it
+            // depends on the specific behaviour of a matching iHD driver when
+            // used on recent Intel hardware.  Set options to the VAAPI device
+            // creation so that we should pick a usable setup by default if
+            // possible, even when multiple devices and drivers are available.
+            av_dict_set(&child_device_opts, "kernel_driver", "i915", 0);
+            av_dict_set(&child_device_opts, "driver",        "iHD",  0);
+        }
+        break;
+#endif
+#if CONFIG_D3D11VA
+    case AV_HWDEVICE_TYPE_D3D11VA:
+        break;
+#endif
+#if CONFIG_DXVA2
+    case AV_HWDEVICE_TYPE_DXVA2:
+        break;
+#endif
+    default:
+        {
+            av_log(ctx, AV_LOG_ERROR, "No supported child device type is enabled\n");
+            return AVERROR(ENOSYS);
+        }
+        break;
+    }
+
+    e = av_dict_get(opts, "child_device", NULL, 0);
     ret = av_hwdevice_ctx_create(&priv->child_device_ctx, child_device_type,
                                  e ? e->value : NULL, child_device_opts, 0);
-
     av_dict_free(&child_device_opts);
     if (ret < 0)
         return ret;
 
     child_device = (AVHWDeviceContext*)priv->child_device_ctx->data;
 
-    impl = choose_implementation(device);
+    impl = choose_implementation(device, child_device_type);
 
     return qsv_device_derive_from_child(ctx, impl, child_device, 0);
 }