From: Alexander Kravchenko <akravchenko...@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 --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; -- 2.19.1.windows.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".