From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTP id 46E8249558 for ; Wed, 14 Feb 2024 01:55:41 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 2B2BB68D184; Wed, 14 Feb 2024 03:55:38 +0200 (EET) Received: from mail-ej1-f54.google.com (mail-ej1-f54.google.com [209.85.218.54]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 7925368D098 for ; Wed, 14 Feb 2024 03:55:31 +0200 (EET) Received: by mail-ej1-f54.google.com with SMTP id a640c23a62f3a-a29c4bbb2f4so658603066b.1 for ; Tue, 13 Feb 2024 17:55:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1707875730; x=1708480530; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=Ho4/GocOSY0UQlB9iSFl9hxAaJ//24POI+zE7nLkxts=; b=mXTuGXYyWjkZydyczTfKMEev/61rJqPcnDMFC//Obv4/lxIVn+Qgj/oVvB1qBd79aS 1bZYsgRWaKPsub2+LdlCy2/I7bJsOVMoyJOTQnSuOC3f0uepivU8R6c3bQxjsWFdkBAg e+6kaK3nYJK0UP3aBHkOOLWgsKBUnsW0fTbGuQpqQXd8ukfSXAAxUF+Xds5Swzdun8OU KUSoBqZDo8a4+isfkYT74wgqxC3otxAR1YpuqfxhaQmOw89arGgH8yAMr6grvd53FxMP hEY5ZQPk/MdMffFMKl+6vCuPa7R8BHXbATK749dkbY+uE1EoksPiEJKjxsgm9mGZwdjC SGgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1707875730; x=1708480530; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=Ho4/GocOSY0UQlB9iSFl9hxAaJ//24POI+zE7nLkxts=; b=c3bpHDYAheQ2urPbojYX9BBTEFweiRJJULIax09+X+7iiOdbkRBQla3L/fTpqHGbUg KuUrasUvUAj68Pg5M+jpWuUZnfSSaUZNIdvWurHZOHb43rgvwBjtZ+GewogyxGld8R81 BFsIxsRl9Gv/cC7UX0DlV0DwvYlGrDIQtphWYbiVzuVgpqzeboHohxoBqKFG/ux4t3f7 QnblwIppFb3Kt3BHN4ceEuqf+1fJFNM+zSmpIMbP5PEgMaJKr2PE5iN8WEkooL2sjYMf XbaJryTfxl4GPTgCOQrpc3gP+nCnhgK021x0tA1Nisjue6jnbreKxIFyVZxh0bNjeIIJ XYNw== X-Gm-Message-State: AOJu0Yxy3nCjaC4QuAk3vLPEaCIif2S+Z2l3N3dcaoZZ6FG7/II9zN5K 30JWsgfxrF/7qT0hzXo0X5BXLyceVUNcBvGfswkATUMll7ahFdhtq/xF14Gd X-Google-Smtp-Source: AGHT+IHFA+LxJr+ep8SVu/TwZ/y2Uup/TSqxQwreGogv581roXYd1fuWwX/+DkMw+0rC8tVmQUzQ3w== X-Received: by 2002:a17:906:6d0b:b0:a38:a2f:c131 with SMTP id m11-20020a1709066d0b00b00a380a2fc131mr702615ejr.43.1707875729869; Tue, 13 Feb 2024 17:55:29 -0800 (PST) Received: from dovchinn.amd.com (cable-178-148-16-149.dynamic.sbb.rs. [178.148.16.149]) by smtp.gmail.com with ESMTPSA id vi16-20020a170907d41000b00a3ce31d3ffdsm1663230ejc.93.2024.02.13.17.55.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 13 Feb 2024 17:55:29 -0800 (PST) From: Dmitrii Ovchinnikov To: ffmpeg-devel@ffmpeg.org Date: Wed, 14 Feb 2024 02:55:07 +0100 Message-Id: <20240214015515.1027-1-ovchinnikov.dmitrii@gmail.com> X-Mailer: git-send-email 2.38.1.windows.1 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 1/9] libavutil: add hwcontext_amf. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Dmitrii Ovchinnikov Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: Adds hwcontext_amf, which allows to use shared AMF context for the encoder, decoder and AMF-based filters, without copy to the host memory. It will also allow you to use some optimizations in the interaction of components (for example, SAV) and make a more manageable and optimal setup for using GPU devices with AMF in the case of a fully AMF pipeline. It will be a significant performance uplift when full AMF pipeline with filters is used. We also plan to add Compression artefact removal filter in near feature. --- libavutil/Makefile | 3 + libavutil/hwcontext.c | 4 + libavutil/hwcontext.h | 1 + libavutil/hwcontext_amf.c | 580 +++++++++++++++++++++++++++++++++ libavutil/hwcontext_amf.h | 105 ++++++ libavutil/hwcontext_internal.h | 1 + libavutil/pixdesc.c | 4 + libavutil/pixfmt.h | 4 + 8 files changed, 702 insertions(+) create mode 100644 libavutil/hwcontext_amf.c create mode 100644 libavutil/hwcontext_amf.h diff --git a/libavutil/Makefile b/libavutil/Makefile index e7709b97d0..785af8e926 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -45,6 +45,7 @@ HEADERS = adler32.h \ hwcontext_d3d12va.h \ hwcontext_drm.h \ hwcontext_dxva2.h \ + hwcontext_amf.h \ hwcontext_qsv.h \ hwcontext_mediacodec.h \ hwcontext_opencl.h \ @@ -195,6 +196,7 @@ OBJS-$(CONFIG_CUDA) += hwcontext_cuda.o OBJS-$(CONFIG_D3D11VA) += hwcontext_d3d11va.o OBJS-$(CONFIG_D3D12VA) += hwcontext_d3d12va.o OBJS-$(CONFIG_DXVA2) += hwcontext_dxva2.o +OBJS-$(CONFIG_AMF) += hwcontext_amf.o OBJS-$(CONFIG_LIBDRM) += hwcontext_drm.o OBJS-$(CONFIG_MACOS_KPERF) += macos_kperf.o OBJS-$(CONFIG_MEDIACODEC) += hwcontext_mediacodec.o @@ -219,6 +221,7 @@ SKIPHEADERS-$(CONFIG_CUDA) += hwcontext_cuda_internal.h \ SKIPHEADERS-$(CONFIG_D3D11VA) += hwcontext_d3d11va.h SKIPHEADERS-$(CONFIG_D3D12VA) += hwcontext_d3d12va.h SKIPHEADERS-$(CONFIG_DXVA2) += hwcontext_dxva2.h +SKIPHEADERS-$(CONFIG_AMF) += hwcontext_amf.h SKIPHEADERS-$(CONFIG_QSV) += hwcontext_qsv.h SKIPHEADERS-$(CONFIG_OPENCL) += hwcontext_opencl.h SKIPHEADERS-$(CONFIG_VAAPI) += hwcontext_vaapi.h diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c index e23bad230f..8c53fc1862 100644 --- a/libavutil/hwcontext.c +++ b/libavutil/hwcontext.c @@ -65,6 +65,9 @@ static const HWContextType * const hw_table[] = { #endif #if CONFIG_VULKAN &ff_hwcontext_type_vulkan, +#endif +#if CONFIG_AMF + &ff_hwcontext_type_amf, #endif NULL, }; @@ -82,6 +85,7 @@ static const char *const hw_type_names[] = { [AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox", [AV_HWDEVICE_TYPE_MEDIACODEC] = "mediacodec", [AV_HWDEVICE_TYPE_VULKAN] = "vulkan", + [AV_HWDEVICE_TYPE_AMF] = "amf", }; enum AVHWDeviceType av_hwdevice_find_type_by_name(const char *name) diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h index 2b33721a97..53292ea67f 100644 --- a/libavutil/hwcontext.h +++ b/libavutil/hwcontext.h @@ -38,6 +38,7 @@ enum AVHWDeviceType { AV_HWDEVICE_TYPE_MEDIACODEC, AV_HWDEVICE_TYPE_VULKAN, AV_HWDEVICE_TYPE_D3D12VA, + 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..6f43780dc5 --- /dev/null +++ b/libavutil/hwcontext_amf.c @@ -0,0 +1,580 @@ +/* + * 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 "buffer.h" +#include "common.h" +#include "hwcontext.h" +#include "hwcontext_amf.h" +#include "hwcontext_internal.h" +#if CONFIG_VULKAN +#include "hwcontext_vulkan.h" +#endif +#if CONFIG_D3D11VA +#include "libavutil/hwcontext_d3d11va.h" +#endif +#if CONFIG_DXVA2 +#define COBJMACROS +#include "libavutil/hwcontext_dxva2.h" +#endif +#include "mem.h" +#include "pixdesc.h" +#include "pixfmt.h" +#include "imgutils.h" +#include "libavutil/avassert.h" +#include +#ifdef _WIN32 +#include "compat/w32dlfcn.h" +#else +#include +#endif + +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, +}; + + +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); // \n is provided from AMF +} + +static void AMF_CDECL_CALL AMFTraceWriter_Flush(AMFTraceWriter *pThis) +{ +} + +static AMFTraceWriterVtbl tracer_vtbl = +{ + .Write = AMFTraceWriter_Write, + .Flush = AMFTraceWriter_Flush, +}; + +AmfTraceWriter av_amf_trace_writer = +{ + .vtbl = &tracer_vtbl, + .avcl = &amflib_context, + .avctx = NULL +}; + +const FormatMap format_map[] = +{ + { AV_PIX_FMT_NONE, AMF_SURFACE_UNKNOWN }, + { AV_PIX_FMT_NV12, AMF_SURFACE_NV12 }, + { AV_PIX_FMT_BGR0, AMF_SURFACE_BGRA }, + { AV_PIX_FMT_RGB0, AMF_SURFACE_RGBA }, + { AV_PIX_FMT_BGRA, AMF_SURFACE_BGRA }, + { AV_PIX_FMT_ARGB, AMF_SURFACE_ARGB }, + { AV_PIX_FMT_RGBA, AMF_SURFACE_RGBA }, + { AV_PIX_FMT_GRAY8, AMF_SURFACE_GRAY8 }, + { AV_PIX_FMT_YUV420P, AMF_SURFACE_YUV420P }, + { AV_PIX_FMT_YUYV422, AMF_SURFACE_YUY2 }, + { AV_PIX_FMT_P010, AMF_SURFACE_P010 }, + { AV_PIX_FMT_YUV420P10, AMF_SURFACE_P010 }, + { AV_PIX_FMT_YUV420P12, AMF_SURFACE_P012 }, + { AV_PIX_FMT_YUV420P12, AMF_SURFACE_P012 }, + { AV_PIX_FMT_YUV420P16, AMF_SURFACE_P016 }, + { AV_PIX_FMT_YUV422P10LE, AMF_SURFACE_Y210 }, + { AV_PIX_FMT_YUV444P10LE, AMF_SURFACE_Y416 }, +}; + +enum AMF_SURFACE_FORMAT av_amf_av_to_amf_format(enum AVPixelFormat fmt) +{ + int i; + for (i = 0; i < amf_countof(format_map); i++) { + if (format_map[i].av_format == fmt) { + return format_map[i].amf_format; + } + } + return AMF_SURFACE_UNKNOWN; +} + +enum AVPixelFormat av_amf_to_av_format(enum AMF_SURFACE_FORMAT fmt) +{ + int i; + for (i = 0; i < amf_countof(format_map); i++) { + if (format_map[i].amf_format == fmt) { + return format_map[i].av_format; + } + } + return AMF_SURFACE_UNKNOWN; +} + +static const enum AVPixelFormat supported_formats[] = { + AV_PIX_FMT_NV12, + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_BGRA, + AV_PIX_FMT_P010, + AV_PIX_FMT_YUV420P10, +#if CONFIG_D3D11VA + AV_PIX_FMT_D3D11, +#endif +#if CONFIG_DXVA2 + AV_PIX_FMT_DXVA2_VLD, +#endif + AV_PIX_FMT_AMF +}; + +static int amf_frames_get_constraints(AVHWDeviceContext *ctx, + const void *hwconfig, + AVHWFramesConstraints *constraints) +{ + int i; + + constraints->valid_sw_formats = av_malloc_array(FF_ARRAY_ELEMS(supported_formats) + 1, + sizeof(*constraints->valid_sw_formats)); + if (!constraints->valid_sw_formats) + return AVERROR(ENOMEM); + + for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) + constraints->valid_sw_formats[i] = supported_formats[i]; + constraints->valid_sw_formats[FF_ARRAY_ELEMS(supported_formats)] = AV_PIX_FMT_NONE; + + constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats)); + if (!constraints->valid_hw_formats) + return AVERROR(ENOMEM); + + constraints->valid_hw_formats[0] = AV_PIX_FMT_AMF; + constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE; + + return 0; +} + +static void amf_dummy_free(void *opaque, uint8_t *data) +{ + +} + +static AVBufferRef *amf_pool_alloc(void *opaque, size_t size) +{ + AVHWFramesContext *hwfc = (AVHWFramesContext *)opaque; + AVBufferRef *buf; + + buf = av_buffer_create(NULL, NULL, amf_dummy_free, hwfc, AV_BUFFER_FLAG_READONLY); + if (!buf) { + av_log(hwfc, AV_LOG_ERROR, "Failed to create buffer for AMF context.\n"); + return NULL; + } + return buf; +} + +static int amf_frames_init(AVHWFramesContext *ctx) +{ + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) { + if (ctx->sw_format == supported_formats[i]) + break; + } + if (i == FF_ARRAY_ELEMS(supported_formats)) { + av_log(ctx, AV_LOG_ERROR, "Pixel format '%s' is not supported\n", + av_get_pix_fmt_name(ctx->sw_format)); + return AVERROR(ENOSYS); + } + + ctx->internal->pool_internal = + av_buffer_pool_init2(sizeof(AMFSurface), ctx, + &amf_pool_alloc, NULL); + + + return 0; +} + +static int amf_get_buffer(AVHWFramesContext *ctx, AVFrame *frame) +{ + frame->buf[0] = av_buffer_pool_get(ctx->pool); + if (!frame->buf[0]) + return AVERROR(ENOMEM); + + frame->data[3] = frame->buf[0]->data; + frame->format = AV_PIX_FMT_AMF; + frame->width = ctx->width; + frame->height = ctx->height; + return 0; +} + +static int amf_transfer_get_formats(AVHWFramesContext *ctx, + enum AVHWFrameTransferDirection dir, + enum AVPixelFormat **formats) +{ + enum AVPixelFormat *fmts; + + fmts = av_malloc_array(2, sizeof(*fmts)); + if (!fmts) + return AVERROR(ENOMEM); + + fmts[0] = ctx->sw_format; + fmts[1] = AV_PIX_FMT_NONE; + + *formats = fmts; + + return 0; +} + +static int amf_transfer_data_to(AVHWFramesContext *ctx, AVFrame *dst, + const AVFrame *src) +{ + AMFSurface* surface = (AMFSurface*)dst->data[3]; + AMFPlane *plane; + uint8_t *dst_data[4]; + int dst_linesize[4]; + int planes; + int i; + int w = FFMIN(dst->width, src->width); + int h = FFMIN(dst->height, src->height); + + planes = (int)surface->pVtbl->GetPlanesCount(surface); + av_assert0(planes < FF_ARRAY_ELEMS(dst_data)); + + for (i = 0; i < planes; i++) { + plane = surface->pVtbl->GetPlaneAt(surface, i); + dst_data[i] = plane->pVtbl->GetNative(plane); + dst_linesize[i] = plane->pVtbl->GetHPitch(plane); + } + av_image_copy(dst_data, dst_linesize, + (const uint8_t**)src->data, src->linesize, src->format, + w, h); + + return 0; +} +static int amf_transfer_data_from(AVHWFramesContext *ctx, AVFrame *dst, + const AVFrame *src) +{ + AMFSurface* surface = (AMFSurface*)src->data[3]; + AMFPlane *plane; + uint8_t *src_data[4]; + int src_linesize[4]; + int planes; + int i; + int w = FFMIN(dst->width, src->width); + int h = FFMIN(dst->height, src->height); + int ret; + + ret = surface->pVtbl->Convert(surface, AMF_MEMORY_HOST); + AMF_RETURN_IF_FALSE(ctx, ret == AMF_OK, AVERROR_UNKNOWN, "Convert(amf::AMF_MEMORY_HOST) failed with error %d\n", AVERROR_UNKNOWN); + + planes = (int)surface->pVtbl->GetPlanesCount(surface); + av_assert0(planes < FF_ARRAY_ELEMS(src_data)); + + for (i = 0; i < planes; i++) { + plane = surface->pVtbl->GetPlaneAt(surface, i); + src_data[i] = plane->pVtbl->GetNative(plane); + src_linesize[i] = plane->pVtbl->GetHPitch(plane); + } + av_image_copy(dst->data, dst->linesize, + (const uint8_t **)src_data, src_linesize, dst->format, + w, h); + surface->pVtbl->Release(surface); + return 0; +} + + +static void amf_device_uninit(AVHWDeviceContext *device_ctx) +{ + AVAMFDeviceContext *amf_ctx = device_ctx->hwctx; + av_buffer_unref(&amf_ctx->internal); +} + +static int amf_device_init(AVHWDeviceContext *ctx) +{ + AVAMFDeviceContext *amf_ctx = ctx->hwctx; + return av_amf_context_init((AVAMFDeviceContextInternal * )amf_ctx->internal->data, ctx); +} + +static int amf_device_create(AVHWDeviceContext *device_ctx, + const char *device, + AVDictionary *opts, int flags) +{ + AVAMFDeviceContext *ctx = device_ctx->hwctx; + AVAMFDeviceContextInternal *wrapped = av_mallocz(sizeof(*wrapped)); + ctx->internal = av_buffer_create((uint8_t *)wrapped, sizeof(*wrapped), + av_amf_context_internal_free, NULL, 0); + AVAMFDeviceContextInternal * internal = (AVAMFDeviceContextInternal * )ctx->internal->data; + int ret; + if ((ret = av_amf_load_library(internal, device_ctx)) == 0) { + if ((ret = av_amf_create_context(internal, device_ctx, "", opts, flags)) == 0){ + return 0; + } + } + amf_device_uninit(device_ctx); + return ret; +} + +static int amf_device_derive(AVHWDeviceContext *device_ctx, + AVHWDeviceContext *child_device_ctx, AVDictionary *opts, + int flags) +{ + AVAMFDeviceContext *ctx = device_ctx->hwctx; + AVAMFDeviceContextInternal * internal = (AVAMFDeviceContextInternal * )ctx->internal->data; + int ret; + + ret = amf_device_create(device_ctx, "", opts, flags); + if(ret < 0) + return ret; + + return av_amf_context_derive(internal, child_device_ctx, opts, flags); +} + +#if CONFIG_DXVA2 +static int amf_init_from_dxva2_device(AVAMFDeviceContextInternal * internal, AVDXVA2DeviceContext *hwctx) +{ + IDirect3DDevice9 *device; + HANDLE device_handle; + HRESULT hr; + AMF_RESULT res; + int ret; + + hr = IDirect3DDeviceManager9_OpenDeviceHandle(hwctx->devmgr, &device_handle); + if (FAILED(hr)) { + av_log(hwctx, AV_LOG_ERROR, "Failed to open device handle for Direct3D9 device: %lx.\n", (unsigned long)hr); + return AVERROR_EXTERNAL; + } + + hr = IDirect3DDeviceManager9_LockDevice(hwctx->devmgr, device_handle, &device, FALSE); + if (SUCCEEDED(hr)) { + IDirect3DDeviceManager9_UnlockDevice(hwctx->devmgr, device_handle, FALSE); + ret = 0; + } else { + av_log(hwctx, AV_LOG_ERROR, "Failed to lock device handle for Direct3D9 device: %lx.\n", (unsigned long)hr); + ret = AVERROR_EXTERNAL; + } + + + IDirect3DDeviceManager9_CloseDeviceHandle(hwctx->devmgr, device_handle); + + if (ret < 0) + return ret; + + res = internal->context->pVtbl->InitDX9(internal->context, device); + + IDirect3DDevice9_Release(device); + + if (res != AMF_OK) { + if (res == AMF_NOT_SUPPORTED) + av_log(hwctx, AV_LOG_ERROR, "AMF via D3D9 is not supported on the given device.\n"); + else + av_log(hwctx, AV_LOG_ERROR, "AMF failed to initialise on given D3D9 device: %d.\n", res); + return AVERROR(ENODEV); + } + internal->mem_type = AMF_MEMORY_DX9; + return 0; +} +#endif + +#if CONFIG_D3D11VA +static int amf_init_from_d3d11_device(AVAMFDeviceContextInternal* internal, AVD3D11VADeviceContext *hwctx) +{ + AMF_RESULT res; + res = internal->context->pVtbl->InitDX11(internal->context, hwctx->device, AMF_DX11_1); + if (res != AMF_OK) { + if (res == AMF_NOT_SUPPORTED) + av_log(hwctx, AV_LOG_ERROR, "AMF via D3D11 is not supported on the given device.\n"); + else + av_log(hwctx, AV_LOG_ERROR, "AMF failed to initialise on the given D3D11 device: %d.\n", res); + return AVERROR(ENODEV); + } + internal->mem_type = AMF_MEMORY_DX11; + return 0; +} +#endif + +int av_amf_context_init(AVAMFDeviceContextInternal* internal, void* avcl) +{ + AMFContext1 *context1 = NULL; + AMF_RESULT res; + + res = internal->context->pVtbl->InitDX11(internal->context, NULL, AMF_DX11_1); + if (res == AMF_OK) { + internal->mem_type = AMF_MEMORY_DX11; + av_log(avcl, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D11.\n"); + } else { + res = internal->context->pVtbl->InitDX9(internal->context, NULL); + if (res == AMF_OK) { + internal->mem_type = AMF_MEMORY_DX9; + av_log(avcl, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D9.\n"); + } else { + AMFGuid guid = IID_AMFContext1(); + res = internal->context->pVtbl->QueryInterface(internal->context, &guid, (void**)&context1); + AMF_RETURN_IF_FALSE(avcl, 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(avcl, AV_LOG_ERROR, "AMF via Vulkan is not supported on the given device.\n"); + else + av_log(avcl, AV_LOG_ERROR, "AMF failed to initialise on the given Vulkan device: %d.\n", res); + return AVERROR(ENOSYS); + } + internal->mem_type = AMF_MEMORY_VULKAN; + av_log(avcl, AV_LOG_VERBOSE, "AMF initialisation succeeded via Vulkan.\n"); + } + } + return 0; +} +int av_amf_load_library(AVAMFDeviceContextInternal* internal, void* avcl) +{ + AMFInit_Fn init_fun; + AMFQueryVersion_Fn version_fun; + AMF_RESULT res; + + internal->library = dlopen(AMF_DLL_NAMEA, RTLD_NOW | RTLD_LOCAL); + AMF_RETURN_IF_FALSE(avcl, internal->library != NULL, + AVERROR_UNKNOWN, "DLL %s failed to open\n", AMF_DLL_NAMEA); + + init_fun = (AMFInit_Fn)dlsym(internal->library, AMF_INIT_FUNCTION_NAME); + AMF_RETURN_IF_FALSE(avcl, init_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_INIT_FUNCTION_NAME); + + version_fun = (AMFQueryVersion_Fn)dlsym(internal->library, AMF_QUERY_VERSION_FUNCTION_NAME); + AMF_RETURN_IF_FALSE(avcl, version_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_QUERY_VERSION_FUNCTION_NAME); + + res = version_fun(&internal->version); + AMF_RETURN_IF_FALSE(avcl, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_QUERY_VERSION_FUNCTION_NAME, res); + res = init_fun(AMF_FULL_VERSION, &internal->factory); + AMF_RETURN_IF_FALSE(avcl, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_INIT_FUNCTION_NAME, res); + res = internal->factory->pVtbl->GetTrace(internal->factory, &internal->trace); + AMF_RETURN_IF_FALSE(avcl, res == AMF_OK, AVERROR_UNKNOWN, "GetTrace() failed with error %d\n", res); + res = internal->factory->pVtbl->GetDebug(internal->factory, &internal->debug); + AMF_RETURN_IF_FALSE(avcl, res == AMF_OK, AVERROR_UNKNOWN, "GetDebug() failed with error %d\n", res); + return 0; +} + +int av_amf_create_context( AVAMFDeviceContextInternal * internal, + void* avcl, + const char *device, + AVDictionary *opts, int flags) +{ + AMF_RESULT res; + + internal->trace->pVtbl->EnableWriter(internal->trace, AMF_TRACE_WRITER_CONSOLE, 0); + internal->trace->pVtbl->SetGlobalLevel(internal->trace, AMF_TRACE_TRACE); + + // connect AMF logger to av_log + av_amf_trace_writer.avctx = avcl; + internal->trace->pVtbl->RegisterWriter(internal->trace, FFMPEG_AMF_WRITER_ID, (AMFTraceWriter*)&av_amf_trace_writer, 1); + internal->trace->pVtbl->SetWriterLevel(internal->trace, FFMPEG_AMF_WRITER_ID, AMF_TRACE_TRACE); + + res = internal->factory->pVtbl->CreateContext(internal->factory, &internal->context); + AMF_RETURN_IF_FALSE(avcl, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext() failed with error %d\n", res); + + return 0; +} + +int av_amf_context_internal_create(AVAMFDeviceContextInternal * internal, + void* avcl, + const char *device, + AVDictionary *opts, int flags) +{ + int ret; + if ((ret = av_amf_load_library(internal, avcl)) == 0) { + if ((ret = av_amf_create_context(internal, avcl, "", opts, flags)) == 0){ + return 0; + } + } + av_amf_context_internal_free(0, (uint8_t *)internal); + return ret; +} + +void av_amf_context_internal_free(void *opaque, uint8_t *data) +{ + AVAMFDeviceContextInternal *amf_ctx = (AVAMFDeviceContextInternal *)data; + 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 (amf_ctx->trace) { + amf_ctx->trace->pVtbl->UnregisterWriter(amf_ctx->trace, FFMPEG_AMF_WRITER_ID); + } + + if(amf_ctx->library) { + dlclose(amf_ctx->library); + amf_ctx->library = NULL; + } + + amf_ctx->debug = NULL; + amf_ctx->version = 0; + av_free(amf_ctx); +} + +int av_amf_context_derive(AVAMFDeviceContextInternal * internal, + AVHWDeviceContext *child_device_ctx, AVDictionary *opts, + int flags) +{ + + switch (child_device_ctx->type) { + +#if CONFIG_DXVA2 + case AV_HWDEVICE_TYPE_DXVA2: + { + AVDXVA2DeviceContext *child_device_hwctx = child_device_ctx->hwctx; + return amf_init_from_dxva2_device(internal, child_device_hwctx); + } + break; +#endif + +#if CONFIG_D3D11VA + case AV_HWDEVICE_TYPE_D3D11VA: + { + AVD3D11VADeviceContext *child_device_hwctx = child_device_ctx->hwctx; + return amf_init_from_d3d11_device(internal, child_device_hwctx); + } + break; +#endif + default: + { + av_log(child_device_ctx, AV_LOG_ERROR, "AMF initialisation from a %s device is not supported.\n", + av_hwdevice_get_type_name(child_device_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), + .frames_priv_size = sizeof(AMFFramesContext), + + .device_create = amf_device_create, + .device_derive = amf_device_derive, + .device_init = amf_device_init, + .device_uninit = amf_device_uninit, + .frames_get_constraints = amf_frames_get_constraints, + .frames_init = amf_frames_init, + .frames_get_buffer = amf_get_buffer, + .transfer_get_formats = amf_transfer_get_formats, + .transfer_data_to = amf_transfer_data_to, + .transfer_data_from = amf_transfer_data_from, + + .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_AMF, AV_PIX_FMT_NONE }, +}; diff --git a/libavutil/hwcontext_amf.h b/libavutil/hwcontext_amf.h new file mode 100644 index 0000000000..0161b9a29c --- /dev/null +++ b/libavutil/hwcontext_amf.h @@ -0,0 +1,105 @@ +/* + * 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 +#include +#include +#include +#include "pixfmt.h" + +#include "libavformat/avformat.h" +#include "libavutil/hwcontext.h" + +#define FFMPEG_AMF_WRITER_ID L"ffmpeg_amf" + +typedef struct AmfTraceWriter { + AMFTraceWriterVtbl *vtbl; + void *avctx; + void *avcl; +} AmfTraceWriter; + +typedef struct AVAMFDeviceContextInternal { + amf_handle library; ///< handle to DLL library + AMFFactory *factory; ///< pointer to AMF factory + AMFDebug *debug; ///< pointer to AMF debug interface + AMFTrace *trace; ///< pointer to AMF trace interface + + amf_uint64 version; ///< version of AMF runtime + AMFContext *context; ///< AMF context + AMF_MEMORY_TYPE mem_type; +} AVAMFDeviceContextInternal; + +/** + * This struct is allocated as AVHWDeviceContext.hwctx + */ + +typedef struct AVAMFDeviceContext { + AVBufferRef *internal; +} AVAMFDeviceContext; + +typedef struct AMFFramesContext { + AMFSurface * surfaces; + int nb_surfaces; +} AMFFramesContext; + +/** +* Error handling helper +*/ +#define AMF_RETURN_IF_FALSE(avctx, exp, ret_value, /*message,*/ ...) \ + if (!(exp)) { \ + av_log(avctx, AV_LOG_ERROR, __VA_ARGS__); \ + return ret_value; \ + } + +#define AMF_GOTO_FAIL_IF_FALSE(avctx, exp, ret_value, /*message,*/ ...) \ + if (!(exp)) { \ + av_log(avctx, AV_LOG_ERROR, __VA_ARGS__); \ + ret = ret_value; \ + goto fail; \ + } + +#define AMF_TIME_BASE_Q (AVRational){1, AMF_SECOND} + +typedef struct FormatMap { + enum AVPixelFormat av_format; + enum AMF_SURFACE_FORMAT amf_format; +} FormatMap; + +extern const FormatMap format_map[]; +enum AMF_SURFACE_FORMAT av_amf_av_to_amf_format(enum AVPixelFormat fmt); +enum AVPixelFormat av_amf_to_av_format(enum AMF_SURFACE_FORMAT fmt); +extern AmfTraceWriter av_amf_trace_writer; + +int av_amf_context_init(AVAMFDeviceContextInternal* internal, void* avcl); +int av_amf_load_library(AVAMFDeviceContextInternal* internal, void* avcl); +int av_amf_create_context( AVAMFDeviceContextInternal * internal, + void* avcl, + const char *device, + AVDictionary *opts, int flags); +int av_amf_context_internal_create(AVAMFDeviceContextInternal * internal, + void* avcl, + const char *device, + AVDictionary *opts, int flags); +void av_amf_context_internal_free(void *opaque, uint8_t *data); +int av_amf_context_derive(AVAMFDeviceContextInternal * internal, + AVHWDeviceContext *child_device_ctx, AVDictionary *opts, + int flags); + +#endif /* AVUTIL_HWCONTEXT_AMF_H */ diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h index 4df516ee6a..48d2dc012c 100644 --- a/libavutil/hwcontext_internal.h +++ b/libavutil/hwcontext_internal.h @@ -175,5 +175,6 @@ extern const HWContextType ff_hwcontext_type_vdpau; extern const HWContextType ff_hwcontext_type_videotoolbox; extern const HWContextType ff_hwcontext_type_mediacodec; extern const HWContextType ff_hwcontext_type_vulkan; +extern const HWContextType ff_hwcontext_type_amf; #endif /* AVUTIL_HWCONTEXT_INTERNAL_H */ diff --git a/libavutil/pixdesc.c b/libavutil/pixdesc.c index f6d4d01460..ebc79b0c74 100644 --- a/libavutil/pixdesc.c +++ b/libavutil/pixdesc.c @@ -2125,6 +2125,10 @@ static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = { .name = "cuda", .flags = AV_PIX_FMT_FLAG_HWACCEL, }, + [AV_PIX_FMT_AMF] = { + .name = "amf", + .flags = AV_PIX_FMT_FLAG_HWACCEL, + }, [AV_PIX_FMT_AYUV64LE] = { .name = "ayuv64le", .nb_components = 4, diff --git a/libavutil/pixfmt.h b/libavutil/pixfmt.h index 9c87571f49..06459be62c 100644 --- a/libavutil/pixfmt.h +++ b/libavutil/pixfmt.h @@ -251,6 +251,10 @@ enum AVPixelFormat { * exactly as for system memory frames. */ AV_PIX_FMT_CUDA, + /** + * HW acceleration through AMF. data[3] contain AMFSurface pointer + */ + AV_PIX_FMT_AMF, AV_PIX_FMT_0RGB, ///< packed RGB 8:8:8, 32bpp, XRGBXRGB... X=unused/undefined AV_PIX_FMT_RGB0, ///< packed RGB 8:8:8, 32bpp, RGBXRGBX... X=unused/undefined -- 2.38.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-request@ffmpeg.org with subject "unsubscribe".