diff mbox series

[FFmpeg-devel,4/5] lavu/hwcontext_amf: Engine selection support for AMF context

Message ID 20201015001625.32832-4-ovchinnikov.dmitrii@gmail.com
State New
Headers show
Series [FFmpeg-devel,1/5] lavc/amfenc: HWConfig for amf encoder.
Related show

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished

Commit Message

OvchinnikovDmitrii Oct. 15, 2020, 12:16 a.m. UTC
---
 libavcodec/amfenc.c       |   9 +++-
 libavcodec/amfenc.h       |   1 +
 libavcodec/amfenc_h264.c  |   8 +++
 libavcodec/amfenc_hevc.c  |   8 +++
 libavutil/hwcontext_amf.c | 104 ++++++++++++++++++++++++++++++--------
 libavutil/hwcontext_amf.h |   7 +++
 6 files changed, 116 insertions(+), 21 deletions(-)
diff mbox series

Patch

diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
index 767eab3d56..b7eb74811b 100644
--- a/libavcodec/amfenc.c
+++ b/libavcodec/amfenc.c
@@ -82,6 +82,7 @@  static int amf_init_context(AVCodecContext *avctx)
 {
     AmfContext *ctx = avctx->priv_data;
     AVAMFDeviceContext *amf_ctx;
+    AVDictionary *dict = NULL;
     int ret;
 
     ctx->delayed_frame = av_frame_alloc();
@@ -123,10 +124,16 @@  static int amf_init_context(AVCodecContext *avctx)
         if (ret < 0)
             return ret;
     } else {
-        ret = av_hwdevice_ctx_create(&ctx->amf_device_ctx, AV_HWDEVICE_TYPE_AMF, NULL, NULL, 0);
+#if defined(_WIN32)
+    if (ctx->engine) {
+        ret = av_dict_set_int(&dict, "engine", ctx->engine, 0);
         if (ret < 0)
             return ret;
     }
+#endif
+        ret = av_hwdevice_ctx_create(&ctx->amf_device_ctx, AV_HWDEVICE_TYPE_AMF, NULL, dict, 0);if (ret < 0)
+            return ret;
+    }
     amf_ctx = ((AVHWDeviceContext*)ctx->amf_device_ctx->data)->hwctx;
     ctx->context = amf_ctx->context;
     ctx->factory = amf_ctx->factory;
diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h
index 96d1942a37..f4897c9b2a 100644
--- a/libavcodec/amfenc.h
+++ b/libavcodec/amfenc.h
@@ -73,6 +73,7 @@  typedef struct AmfContext {
     int                 quality;
     int                 b_frame_delta_qp;
     int                 ref_b_frame_delta_qp;
+    int                 engine;
 
     // Dynamic options, can be set after Init() call
 
diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c
index 622ee85946..9fb42323d8 100644
--- a/libavcodec/amfenc_h264.c
+++ b/libavcodec/amfenc_h264.c
@@ -71,6 +71,14 @@  static const AVOption options[] = {
     { "balanced",       "Balanced",                             0,                  AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED },    0, 0, VE, "quality" },
     { "quality",        "Prefer Quality",                       0,                  AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY  },     0, 0, VE, "quality" },
 
+#if defined(_WIN32)
+//preffered engine
+    { "engine",         "Preffered engine",             OFFSET(engine), AV_OPT_TYPE_INT,   { .i64 = AMF_VIDEO_ENCODER_ENGINE_DEFAULT }, AMF_VIDEO_ENCODER_ENGINE_DEFAULT, AMF_VIDEO_ENCODER_ENGINE_VULKAN, VE, "engine" },
+    { "dxva2",          "DirectX Video Acceleration 2", 0,              AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_ENGINE_DXVA2   }, 0, 0, VE, "engine" },
+    { "d3d11",          "Direct2D11",                   0,              AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_ENGINE_D3D11   }, 0, 0, VE, "engine" },
+    { "vulkan",         "Vulkan",                       0,              AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_ENGINE_VULKAN  }, 0, 0, VE, "engine" },
+#endif
+
     // Dynamic
     /// Rate Control Method
     { "rc",             "Rate Control Method",                  OFFSET(rate_control_mode), AV_OPT_TYPE_INT,   { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR, VE, "rc" },
diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c
index bb224c5fec..fc2c40a6ed 100644
--- a/libavcodec/amfenc_hevc.c
+++ b/libavcodec/amfenc_hevc.c
@@ -58,6 +58,14 @@  static const AVOption options[] = {
     { "speed",          "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED    }, 0, 0, VE, "quality" },
     { "quality",        "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY  }, 0, 0, VE, "quality" },
 
+#if defined(_WIN32)
+//preffered engine
+    { "engine",         "Preffered engine",             OFFSET(engine), AV_OPT_TYPE_INT,   { .i64 = AMF_VIDEO_ENCODER_ENGINE_DEFAULT }, AMF_VIDEO_ENCODER_ENGINE_DEFAULT, AMF_VIDEO_ENCODER_ENGINE_VULKAN, VE, "engine" },
+    { "dxva2",          "DirectX Video Acceleration 2", 0,              AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_ENGINE_DXVA2   }, 0, 0, VE, "engine" },
+    { "d3d11",          "Direct2D11",                   0,              AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_ENGINE_D3D11   }, 0, 0, VE, "engine" },
+    { "vulkan",         "Vulkan",                       0,              AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_ENGINE_VULKAN  }, 0, 0, VE, "engine" },
+#endif
+
     { "rc",             "Set the rate control mode",            OFFSET(rate_control_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR, VE, "rc" },
     { "cqp",            "Constant Quantization Parameter",      0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP             }, 0, 0, VE, "rc" },
     { "cbr",            "Constant Bitrate",                     0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR                     }, 0, 0, VE, "rc" },
diff --git a/libavutil/hwcontext_amf.c b/libavutil/hwcontext_amf.c
index b70ee90d40..faae57dda3 100644
--- a/libavutil/hwcontext_amf.c
+++ b/libavutil/hwcontext_amf.c
@@ -41,6 +41,7 @@ 
 #else
 #include <dlfcn.h>
 #endif
+#include "libavutil/opt.h"
 
 /**
 * Error handling helper
@@ -151,6 +152,51 @@  static int amf_init_device_ctx_object(AVHWDeviceContext *ctx)
     return 0;
 }
 
+static AMF_RESULT amf_context_init_d3d11(AVHWDeviceContext * avctx)
+{
+    AMF_RESULT res;
+    AVAMFDeviceContext * ctx = avctx->hwctx;
+    res = ctx->context->pVtbl->InitDX11(ctx->context, NULL, AMF_DX11_1);
+    if (res == AMF_OK){
+        av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation successed via D3D11.\n");
+    }
+    return res;
+}
+
+static AMF_RESULT amf_context_init_dxva2(AVHWDeviceContext * avctx)
+{
+    AMF_RESULT res;
+    AVAMFDeviceContext * ctx = avctx->hwctx;
+    res = ctx->context->pVtbl->InitDX9(ctx->context, NULL);
+    if (res == AMF_OK){
+        av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation successed via DXVA2.\n");
+    }
+    return res;
+}
+
+static AMF_RESULT amf_context_init_vulkan(AVHWDeviceContext * avctx)
+{
+    AMF_RESULT res;
+    AVAMFDeviceContext * ctx = avctx->hwctx;
+    AMFContext1 *context1 = NULL;
+    AMFGuid guid = IID_AMFContext1();
+
+    res = ctx->context->pVtbl->QueryInterface(ctx->context, &guid, (void**)&context1);
+    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext1() fialed with error %d\n", res);
+
+    res = context1->pVtbl->InitVulkan(context1, NULL);
+    context1->pVtbl->Release(context1);
+    if (res != AMF_OK){
+        if (res == AMF_NOT_SUPPORTED)
+            av_log(avctx, AV_LOG_VERBOSE, "AMF via vulkan is not supported on the given device.\n");
+        else
+            av_log(avctx, AV_LOG_VERBOSE, "AMF failed to initialise on the given vulkan device. %d.\n", res);
+        return AMF_FAIL;
+    }
+    av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation successed via vulkan.\n");
+    return res;
+}
+
 static int amf_device_create(AVHWDeviceContext *ctx, const char *device,
                                 AVDictionary *opts, int flags)
 {
@@ -158,35 +204,53 @@  static int amf_device_create(AVHWDeviceContext *ctx, const char *device,
     AMFContext1 *context1 = NULL;
     AMF_RESULT res;
     int err;
+    AVDictionaryEntry *e;
+    int preffered_engine = AMF_VIDEO_ENCODER_ENGINE_DEFAULT;
 
     err = amf_init_device_ctx_object(ctx);
     if(err < 0)
         return err;
 
-    res = amf_ctx->context->pVtbl->InitDX11(amf_ctx->context, NULL, AMF_DX11_1);
-    if (res == AMF_OK) {
-        av_log(ctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D11.\n");
-    } else {
-        res = amf_ctx->context->pVtbl->InitDX9(amf_ctx->context, NULL);
-        if (res == AMF_OK) {
-            av_log(ctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D9.\n");
-        } else {
-            AMFGuid guid = IID_AMFContext1();
-            res = amf_ctx->context->pVtbl->QueryInterface(amf_ctx->context, &guid, (void**)&context1);
-            AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext1() failed with error %d\n", res);
-
-            res = context1->pVtbl->InitVulkan(context1, NULL);
-            context1->pVtbl->Release(context1);
+    e = av_dict_get(opts, "engine", NULL, 0);
+    if (!!e) {
+        preffered_engine = atoi(e->value);
+    }
+
+    res = AMF_FAIL;
+    switch(preffered_engine) {
+        case AMF_VIDEO_ENCODER_ENGINE_D3D11:
+            res = amf_context_init_d3d11(ctx);
+            break;
+        case AMF_VIDEO_ENCODER_ENGINE_DXVA2:
+            res = amf_context_init_dxva2(ctx);
+            break;
+        case AMF_VIDEO_ENCODER_ENGINE_VULKAN:
+            res = amf_context_init_vulkan(ctx);
+            break;
+        default:
+            break;
+    }
+    if (res != AMF_OK) {
+        if (preffered_engine != AMF_VIDEO_ENCODER_ENGINE_DEFAULT)
+            av_log(ctx, AV_LOG_ERROR, "AMF failed to initialise via preffered engine.\n");
+
+        if (preffered_engine != AMF_VIDEO_ENCODER_ENGINE_D3D11)//already tried in switch case
+            res = amf_context_init_d3d11(ctx);
+
+        if (res != AMF_OK) {
+            if (preffered_engine != AMF_VIDEO_ENCODER_ENGINE_DXVA2)
+            res = amf_context_init_dxva2(ctx);
+
             if (res != AMF_OK) {
-                if (res == AMF_NOT_SUPPORTED)
-                    av_log(ctx, AV_LOG_ERROR, "AMF via Vulkan is not supported on the given device.\n");
-                else
-                    av_log(ctx, AV_LOG_ERROR, "AMF failed to initialise on the given Vulkan device: %d.\n", res);
-                return AVERROR(ENOSYS);
+                if (preffered_engine != AMF_VIDEO_ENCODER_ENGINE_VULKAN)
+                    res = amf_context_init_vulkan(ctx);
             }
-            av_log(ctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via Vulkan.\n");
         }
     }
+
+    if (res != AMF_OK) {
+        return AVERROR(ENOSYS);
+    }
     return 0;
 }
 
diff --git a/libavutil/hwcontext_amf.h b/libavutil/hwcontext_amf.h
index ae2a37f425..82e2ce7abb 100644
--- a/libavutil/hwcontext_amf.h
+++ b/libavutil/hwcontext_amf.h
@@ -30,6 +30,13 @@ 
 #include "AMF/core/Context.h"
 #include "AMF/core/Factory.h"
 
+enum AMF_VIDEO_ENCODER_PREFFERED_ENGINE
+{
+    AMF_VIDEO_ENCODER_ENGINE_DEFAULT = 0,
+    AMF_VIDEO_ENCODER_ENGINE_DXVA2,
+    AMF_VIDEO_ENCODER_ENGINE_D3D11,
+    AMF_VIDEO_ENCODER_ENGINE_VULKAN
+};
 
 /**
  * This struct is allocated as AVHWDeviceContext.hwctx