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

Submitted by Alexander Kravchenko on Nov. 1, 2018, 10:03 p.m.

Details

Message ID 20181101220341.11644-1-akravchenko188@gmail.com
State New
Headers show

Commit Message

Alexander Kravchenko Nov. 1, 2018, 10:03 p.m.
From: Alexander Kravchenko <akravchenko188@gmail.com>

---
 libavutil/Makefile             |   2 +
 libavutil/hwcontext.c          |   4 +
 libavutil/hwcontext.h          |   1 +
 libavutil/hwcontext_amf.c      | 290 +++++++++++++++++++++++++++++++++
 libavutil/hwcontext_amf.h      |  53 ++++++
 libavutil/hwcontext_internal.h |   1 +
 6 files changed, 351 insertions(+)
 create mode 100644 libavutil/hwcontext_amf.c
 create mode 100644 libavutil/hwcontext_amf.h

Patch hide | download patch | download mbox

diff --git a/libavutil/Makefile b/libavutil/Makefile
index 9ed24cfc82..d8b9f03d43 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -157,6 +157,7 @@  OBJS = adler32.o                                                        \
        xtea.o                                                           \
        tea.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
@@ -174,6 +175,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
 SKIPHEADERS-$(CONFIG_D3D11VA)          += hwcontext_d3d11va.h
diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
index f1e404ab20..642f3ca5d0 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
@@ -63,6 +66,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 f5a4b62387..b18591205a 100644
--- a/libavutil/hwcontext.h
+++ b/libavutil/hwcontext.h
@@ -36,6 +36,7 @@  enum AVHWDeviceType {
     AV_HWDEVICE_TYPE_DRM,
     AV_HWDEVICE_TYPE_OPENCL,
     AV_HWDEVICE_TYPE_MEDIACODEC,
+    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..b993c76a7e
--- /dev/null
+++ b/libavutil/hwcontext_amf.c
@@ -0,0 +1,290 @@ 
+/*
+ * 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 "hwcontext_d3d11va.h"
+#endif
+
+#if CONFIG_DXVA2
+#define COBJMACROS
+#include "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; \
+    }
+
+/**
+* AMF library context: amflib_class, amflib_context, amf_trace_writer
+* AMF library has global tracer settings. It is set global library context
+* to path AMF logs to av_log with library class
+*
+*/
+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;
+} 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;
+    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 {
+            av_log(ctx, AV_LOG_ERROR, "AMF initialisation failed via D3D9: error %d.\n", res);
+            return AVERROR(ENOSYS);
+        }
+    }
+    return 0;
+}
+
+#if CONFIG_DXVA2
+static int amf_device_derive_dxva2(AVHWDeviceContext *dst_ctx, AVHWDeviceContext *src_ctx)
+{
+    AVAMFDeviceContext *amf_ctx = dst_ctx->hwctx;
+    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);
+    }
+    return 0;
+}
+#endif
+
+#if CONFIG_D3D11VA
+static int amf_device_derive_d3d11(AVHWDeviceContext *dst_ctx, AVHWDeviceContext *src_ctx)
+{
+    AVAMFDeviceContext *amf_ctx = dst_ctx->hwctx;
+    AVD3D11VADeviceContext *d3d11_ctx = src_ctx->hwctx;
+    AMF_RESULT res;
+    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);
+    }
+    return 0;
+}
+#endif
+
+static int amf_device_derive(AVHWDeviceContext *dst_ctx,
+    AVHWDeviceContext *src_ctx,
+    int flags)
+{
+    int err;
+
+    err = amf_init_device_ctx_object(dst_ctx);
+    if (err < 0)
+        return err;
+
+    switch (src_ctx->type) {
+
+#if CONFIG_DXVA2
+    case AV_HWDEVICE_TYPE_DXVA2:
+        return amf_device_derive_dxva2(dst_ctx, src_ctx);
+#endif
+
+#if CONFIG_D3D11VA
+    case AV_HWDEVICE_TYPE_D3D11VA:
+        return amf_device_derive_d3d11(dst_ctx, src_ctx);
+#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..691a217c2e
--- /dev/null
+++ b/libavutil/hwcontext_amf.h
@@ -0,0 +1,53 @@ 
+/*
+ * 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 "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 77dc47ddd6..cb76e04866 100644
--- a/libavutil/hwcontext_internal.h
+++ b/libavutil/hwcontext_internal.h
@@ -162,6 +162,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;