diff mbox series

[FFmpeg-devel,2/5] lavu/hwcontext_amf: HWContext for AMF based components

Message ID 20201015001625.32832-2-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
From: Alexander Kravchenko <akravchenko188@gmail.com>

---
 libavutil/Makefile             |   2 +
 libavutil/hwcontext.c          |   4 +
 libavutil/hwcontext.h          |   1 +
 libavutil/hwcontext_amf.c      | 284 +++++++++++++++++++++++++++++++++
 libavutil/hwcontext_amf.h      |  54 +++++++
 libavutil/hwcontext_internal.h |   1 +
 6 files changed, 346 insertions(+)
 create mode 100644 libavutil/hwcontext_amf.c
 create mode 100644 libavutil/hwcontext_amf.h
diff mbox series

Patch

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 9b08372eb2..4160f5d972 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -172,6 +172,7 @@  OBJS = adler32.o                                                        \
        video_enc_params.o                                               \
 
 
+OBJS-$(CONFIG_AMF)                      += hwcontext_amf.o
 OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
 OBJS-$(CONFIG_D3D11VA)                  += hwcontext_d3d11va.o
 OBJS-$(CONFIG_DXVA2)                    += hwcontext_dxva2.o
@@ -190,6 +191,7 @@  OBJS += $(COMPAT_OBJS:%=../compat/%)
 # Windows resource file
 SLIBOBJS-$(HAVE_GNU_WINDRES)            += avutilres.o
 
+SKIPHEADERS-$(CONFIG_AMF)              += hwcontext_amf.h
 SKIPHEADERS-$(HAVE_CUDA_H)             += hwcontext_cuda.h
 SKIPHEADERS-$(CONFIG_CUDA)             += hwcontext_cuda_internal.h     \
                                           cuda_check.h
diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
index d13d0f7c9b..ff140f04cb 100644
--- a/libavutil/hwcontext.c
+++ b/libavutil/hwcontext.c
@@ -29,6 +29,9 @@ 
 #include "pixfmt.h"
 
 static const HWContextType * const hw_table[] = {
+#if CONFIG_AMF
+    &ff_hwcontext_type_amf,
+#endif
 #if CONFIG_CUDA
     &ff_hwcontext_type_cuda,
 #endif
@@ -66,6 +69,7 @@  static const HWContextType * const hw_table[] = {
 };
 
 static const char *const hw_type_names[] = {
+    [AV_HWDEVICE_TYPE_AMF]   = "amf",
     [AV_HWDEVICE_TYPE_CUDA]   = "cuda",
     [AV_HWDEVICE_TYPE_DRM]    = "drm",
     [AV_HWDEVICE_TYPE_DXVA2]  = "dxva2",
diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
index 04d19d89c2..9e1e76dee6 100644
--- a/libavutil/hwcontext.h
+++ b/libavutil/hwcontext.h
@@ -37,6 +37,7 @@  enum AVHWDeviceType {
     AV_HWDEVICE_TYPE_OPENCL,
     AV_HWDEVICE_TYPE_MEDIACODEC,
     AV_HWDEVICE_TYPE_VULKAN,
+    AV_HWDEVICE_TYPE_AMF,
 };
 
 typedef struct AVHWDeviceInternal AVHWDeviceInternal;
diff --git a/libavutil/hwcontext_amf.c b/libavutil/hwcontext_amf.c
new file mode 100644
index 0000000000..b70ee90d40
--- /dev/null
+++ b/libavutil/hwcontext_amf.c
@@ -0,0 +1,284 @@ 
+/*
+ * 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 <string.h>
+
+#include "config.h"
+
+#include "avassert.h"
+#include "avstring.h"
+#include "common.h"
+#include "hwcontext.h"
+#include "hwcontext_internal.h"
+#include "hwcontext_amf.h"
+
+#if CONFIG_D3D11VA
+#include "libavutil/hwcontext_d3d11va.h"
+#endif
+
+#if CONFIG_DXVA2
+#define COBJMACROS
+#include "libavutil/hwcontext_dxva2.h"
+#endif
+
+#ifdef _WIN32
+#include "compat/w32dlfcn.h"
+#else
+#include <dlfcn.h>
+#endif
+
+/**
+* Error handling helper
+*/
+#define AMFAV_RETURN_IF_FALSE(avctx, exp, ret_value, /*message,*/ ...) \
+    if (!(exp)) { \
+        av_log(avctx, AV_LOG_ERROR, __VA_ARGS__); \
+        return ret_value; \
+    }
+
+static const AVClass amflib_class = {
+    .class_name = "amf",
+    .item_name = av_default_item_name,
+    .version = LIBAVUTIL_VERSION_INT,
+};
+
+typedef struct AMFLibraryContext {
+    const AVClass      *avclass;
+} AMFLibraryContext;
+
+static AMFLibraryContext amflib_context =
+{
+    .avclass = &amflib_class,
+};
+
+typedef struct AmfTraceWriter {
+    const AMFTraceWriterVtbl    *vtbl;
+    void                        *avcl;
+} AmfTraceWriter;
+
+static void AMF_CDECL_CALL AMFTraceWriter_Write(AMFTraceWriter *pThis,
+    const wchar_t *scope, const wchar_t *message)
+{
+    AmfTraceWriter *tracer = (AmfTraceWriter*)pThis;
+    av_log(tracer->avcl, AV_LOG_DEBUG, "%ls: %ls", scope, message);
+}
+
+static void AMF_CDECL_CALL AMFTraceWriter_Flush(AMFTraceWriter *pThis)
+{
+}
+
+static const AMFTraceWriterVtbl tracer_vtbl =
+{
+    .Write = AMFTraceWriter_Write,
+    .Flush = AMFTraceWriter_Flush,
+};
+
+static const AmfTraceWriter amf_trace_writer =
+{
+    .vtbl = &tracer_vtbl,
+    .avcl = &amflib_context,
+};
+#define AMFAV_WRITER_ID L"avlog"
+
+typedef struct AMFDeviceContextPrivate {
+    amf_handle          library;
+    AMFDebug           *debug;
+    AMFTrace           *trace;
+    AmfTraceWriter      tracer;
+} AMFDeviceContextPrivate;
+
+static void amf_device_free(AVHWDeviceContext *ctx)
+{
+    AVAMFDeviceContext      *amf_ctx = ctx->hwctx;
+    AMFDeviceContextPrivate *priv = ctx->internal->priv;
+    if (amf_ctx->context) {
+        amf_ctx->context->pVtbl->Terminate(amf_ctx->context);
+        amf_ctx->context->pVtbl->Release(amf_ctx->context);
+        amf_ctx->context = NULL;
+    }
+    if(priv->library) {
+        dlclose(priv->library);
+    }
+}
+
+static int amf_init_device_ctx_object(AVHWDeviceContext *ctx)
+{
+    AVAMFDeviceContext         *hwctx = ctx->hwctx;
+    AMFDeviceContextPrivate    *priv = ctx->internal->priv;
+    AMF_RESULT                  res;
+    AMFInit_Fn                  init_fun;
+
+    ctx->free = amf_device_free;
+
+    priv->library = dlopen(AMF_DLL_NAMEA, RTLD_NOW | RTLD_LOCAL);
+    AMFAV_RETURN_IF_FALSE(ctx, priv->library != NULL, AVERROR_UNKNOWN, "DLL %s failed to open\n", AMF_DLL_NAMEA);
+
+    init_fun = (AMFInit_Fn)dlsym(priv->library, AMF_INIT_FUNCTION_NAME);
+    AMFAV_RETURN_IF_FALSE(ctx, init_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_INIT_FUNCTION_NAME);
+
+    res = init_fun(AMF_FULL_VERSION, &hwctx->factory);
+    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_INIT_FUNCTION_NAME, res);
+
+    res = hwctx->factory->pVtbl->GetTrace(hwctx->factory, &priv->trace);
+    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetTrace() failed with error %d\n", res);
+    res = hwctx->factory->pVtbl->GetDebug(hwctx->factory, &priv->debug);
+    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetDebug() failed with error %d\n", res);
+
+    priv->trace->pVtbl->EnableWriter(priv->trace, AMF_TRACE_WRITER_CONSOLE, 0);
+    priv->trace->pVtbl->SetGlobalLevel(priv->trace, AMF_TRACE_TRACE);
+
+    // connect AMF logger to av_log
+    priv->trace->pVtbl->RegisterWriter(priv->trace, AMFAV_WRITER_ID, (AMFTraceWriter*)&amf_trace_writer, 1);
+    priv->trace->pVtbl->SetWriterLevel(priv->trace, AMFAV_WRITER_ID, AMF_TRACE_TRACE);
+
+    res = hwctx->factory->pVtbl->CreateContext(hwctx->factory, &hwctx->context);
+    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext() failed with error %d\n", res);
+    return 0;
+}
+
+static int amf_device_create(AVHWDeviceContext *ctx, const char *device,
+                                AVDictionary *opts, int flags)
+{
+    AVAMFDeviceContext *amf_ctx = ctx->hwctx;
+    AMFContext1 *context1 = NULL;
+    AMF_RESULT res;
+    int err;
+
+    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);
+            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);
+            }
+            av_log(ctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via Vulkan.\n");
+        }
+    }
+    return 0;
+}
+
+static int amf_device_derive(AVHWDeviceContext *dst_ctx,
+                                AVHWDeviceContext *src_ctx,
+                                int flags)
+{
+    AVAMFDeviceContext *amf_ctx = dst_ctx->hwctx;
+    AMF_RESULT res;
+    int err;
+
+    err = amf_init_device_ctx_object(dst_ctx);
+    if(err < 0)
+        return err;
+
+    switch (src_ctx->type) {
+
+#if CONFIG_D3D11VA
+    case AV_HWDEVICE_TYPE_DXVA2:
+        {
+            AVDXVA2DeviceContext *dxva2_ctx = src_ctx->hwctx;
+            HANDLE device_handle;
+            IDirect3DDevice9 *device;
+            HRESULT hr;
+            AMF_RESULT res;
+            int ret;
+
+            hr = IDirect3DDeviceManager9_OpenDeviceHandle(dxva2_ctx->devmgr, &device_handle);
+            if (FAILED(hr)) {
+                av_log(dst_ctx, AV_LOG_ERROR, "Failed to open device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
+                return AVERROR_EXTERNAL;
+            }
+
+            hr = IDirect3DDeviceManager9_LockDevice(dxva2_ctx->devmgr, device_handle, &device, FALSE);
+            if (SUCCEEDED(hr)) {
+                IDirect3DDeviceManager9_UnlockDevice(dxva2_ctx->devmgr, device_handle, FALSE);
+                ret = 0;
+            } else {
+                av_log(dst_ctx, AV_LOG_ERROR, "Failed to lock device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
+                ret = AVERROR_EXTERNAL;
+            }
+
+            IDirect3DDeviceManager9_CloseDeviceHandle(dxva2_ctx->devmgr, device_handle);
+
+            if (ret < 0)
+                return ret;
+
+            res = amf_ctx->context->pVtbl->InitDX9(amf_ctx->context, device);
+
+            IDirect3DDevice9_Release(device);
+
+            if (res != AMF_OK) {
+                if (res == AMF_NOT_SUPPORTED)
+                    av_log(dst_ctx, AV_LOG_ERROR, "AMF via D3D9 is not supported on the given device.\n");
+                else
+                    av_log(dst_ctx, AV_LOG_ERROR, "AMF failed to initialise on given D3D9 device: %d.\n", res);
+                return AVERROR(ENODEV);
+            }
+        }
+        break;
+#endif
+
+#if CONFIG_D3D11VA
+    case AV_HWDEVICE_TYPE_D3D11VA:
+        {
+            AVD3D11VADeviceContext *d3d11_ctx = src_ctx->hwctx;
+            res = amf_ctx->context->pVtbl->InitDX11(amf_ctx->context, d3d11_ctx->device, AMF_DX11_1);
+            if (res != AMF_OK) {
+                if (res == AMF_NOT_SUPPORTED)
+                    av_log(dst_ctx, AV_LOG_ERROR, "AMF via D3D11 is not supported on the given device.\n");
+                else
+                    av_log(dst_ctx, AV_LOG_ERROR, "AMF failed to initialise on the given D3D11 device: %d.\n", res);
+                return AVERROR(ENODEV);
+            }
+        }
+        break;
+#endif
+    default:
+        av_log(dst_ctx, AV_LOG_ERROR, "AMF initialisation from a %s device is not supported.\n",
+                av_hwdevice_get_type_name(src_ctx->type));
+        return AVERROR(ENOSYS);
+    }
+    return 0;
+}
+
+const HWContextType ff_hwcontext_type_amf = {
+    .type                   = AV_HWDEVICE_TYPE_AMF,
+    .name                   = "AMF",
+
+    .device_hwctx_size      = sizeof(AVAMFDeviceContext),
+    .device_priv_size       = sizeof(AMFDeviceContextPrivate),
+
+    .device_create          = &amf_device_create,
+    .device_derive          = &amf_device_derive,
+};
diff --git a/libavutil/hwcontext_amf.h b/libavutil/hwcontext_amf.h
new file mode 100644
index 0000000000..ae2a37f425
--- /dev/null
+++ b/libavutil/hwcontext_amf.h
@@ -0,0 +1,54 @@ 
+/*
+ * 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_AMF_H
+#define AVUTIL_HWCONTEXT_AMF_H
+
+/**
+ * @file
+ * API-specific header for AV_HWDEVICE_TYPE_AMF.
+ *
+ */
+
+#include "frame.h"
+#include "AMF/core/Context.h"
+#include "AMF/core/Factory.h"
+
+
+/**
+ * This struct is allocated as AVHWDeviceContext.hwctx
+ */
+typedef struct AVAMFDeviceContext {
+    /**
+     * Context used for:
+     * texture and buffers allocation.
+     * Access to device objects (DX9, DX11, OpenCL, OpenGL) which are being used in the context
+     */
+    AMFContext *context;
+
+    /**
+     * Factory used for:
+     * AMF component creation such as encoder, decoder, converter...
+     * Access AMF Library settings such as trace/debug/cache
+     */
+    AMFFactory *factory;
+} AVAMFDeviceContext;
+
+
+#endif /* AVUTIL_HWCONTEXT_AMF_H */
diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h
index e6266494ac..7d9c6f0a68 100644
--- a/libavutil/hwcontext_internal.h
+++ b/libavutil/hwcontext_internal.h
@@ -163,6 +163,7 @@  int ff_hwframe_map_create(AVBufferRef *hwframe_ref,
  */
 int ff_hwframe_map_replace(AVFrame *dst, const AVFrame *src);
 
+extern const HWContextType ff_hwcontext_type_amf;
 extern const HWContextType ff_hwcontext_type_cuda;
 extern const HWContextType ff_hwcontext_type_d3d11va;
 extern const HWContextType ff_hwcontext_type_drm;