/* * 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" #include "hwcontext_amf_internal.h" #if CONFIG_VULKAN #include "hwcontext_vulkan.h" #endif #if CONFIG_D3D11VA #include "libavutil/hwcontext_d3d11va.h" #endif #if CONFIG_D3D12VA #include "libavutil/hwcontext_d3d12va.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 #include #ifdef _WIN32 #include "compat/w32dlfcn.h" #else #include #endif #define FFMPEG_AMF_WRITER_ID L"ffmpeg_amf" typedef struct AmfTraceWriter { AMFTraceWriterVtbl *vtblp; void *avctx; AMFTraceWriterVtbl vtbl; } 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->avctx, AV_LOG_DEBUG, "%ls: %ls", scope, message); // \n is provided from AMF } static void AMF_CDECL_CALL AMFTraceWriter_Flush(AMFTraceWriter *pThis) { } static AmfTraceWriter * amf_writer_alloc(void *avctx) { AmfTraceWriter * writer = av_mallocz(sizeof(AmfTraceWriter)); if (!writer) return NULL; writer->vtblp = &writer->vtbl; writer->vtblp->Write = AMFTraceWriter_Write; writer->vtblp->Flush = AMFTraceWriter_Flush; writer->avctx = avctx; return writer; } static void amf_writer_free(void *opaque) { AmfTraceWriter *writer = (AmfTraceWriter *)opaque; av_freep(&writer); } /** * We still need AVHWFramesContext to utilize our hardware memory * otherwise, we will receive the error "HW format requires hw_frames_ctx to be non-NULL". * (libavfilter\buffersrc.c function query_formats) */ typedef struct { void *dummy; } AMFFramesContext; typedef struct AVAMFFormatMap { enum AVPixelFormat av_format; enum AMF_SURFACE_FORMAT amf_format; } FormatMap; 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_X2BGR10, AMF_SURFACE_R10G10B10A2 }, { AV_PIX_FMT_RGBAF16, AMF_SURFACE_RGBA_F16}, }; enum AMF_SURFACE_FORMAT av_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_RGBA, AV_PIX_FMT_P010, #if CONFIG_D3D11VA AV_PIX_FMT_D3D11, #endif #if CONFIG_D3D12VA AV_PIX_FMT_D3D12, #endif #if CONFIG_DXVA2 AV_PIX_FMT_DXVA2_VLD, #endif }; static const enum AVPixelFormat supported_transfer_formats[] = { AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P, AV_PIX_FMT_BGRA, AV_PIX_FMT_RGBA, AV_PIX_FMT_P010, AV_PIX_FMT_NONE, }; 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_SURFACE; 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, 0, 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); } ffhwframesctx(ctx)->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[0] = frame->buf[0]->data; frame->format = AV_PIX_FMT_AMF_SURFACE; 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; int i; fmts = av_malloc_array(FF_ARRAY_ELEMS(supported_transfer_formats), sizeof(*fmts)); if (!fmts) return AVERROR(ENOMEM); for (i = 0; i < FF_ARRAY_ELEMS(supported_transfer_formats); i++) fmts[i] = supported_transfer_formats[i]; *formats = fmts; return 0; } static void amf_free_amfsurface(void *opaque, uint8_t *data) { if(!!data){ AMFSurface *surface = (AMFSurface*)(data); surface->pVtbl->Release(surface); } } static int amf_transfer_data_to(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src) { AMFSurface* surface = (AMFSurface*)dst->data[0]; AMFPlane *plane; uint8_t *dst_data[4]; int dst_linesize[4]; int planes; int i; int res; int w = FFMIN(dst->width, src->width); int h = FFMIN(dst->height, src->height); if (dst->hw_frames_ctx->data != (uint8_t *)ctx || src->format != ctx->sw_format) return AVERROR(EINVAL); if (!surface) { AVHWDeviceContext *hwdev_ctx = ctx->device_ctx; AVAMFDeviceContext *amf_device_ctx = (AVAMFDeviceContext *)hwdev_ctx->hwctx; AMF_SURFACE_FORMAT format = av_av_to_amf_format(ctx->sw_format); res = amf_device_ctx->context->pVtbl->AllocSurface(amf_device_ctx->context, AMF_MEMORY_HOST, format, dst->width, dst->height, &surface); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM), "AllocSurface() failed with error %d\n", res); dst->data[0] = (uint8_t *)surface; dst->buf[1] = av_buffer_create((uint8_t *)surface, sizeof(surface), amf_free_amfsurface, NULL, AV_BUFFER_FLAG_READONLY); AMF_RETURN_IF_FALSE(ctx, !!dst->buf[1], AVERROR(ENOMEM), "av_buffer_create for amf surface failed."); } 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_copy2(dst_data, dst_linesize, 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[0]; 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; if (src->hw_frames_ctx->data != (uint8_t *)ctx || dst->format != ctx->sw_format) return AVERROR(EINVAL); 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_copy2(dst->data, dst->linesize, src_data, src_linesize, dst->format, w, h); return 0; } static void amf_device_uninit(AVHWDeviceContext *device_ctx) { AVAMFDeviceContext *amf_ctx = device_ctx->hwctx; AMF_RESULT res = AMF_NOT_INITIALIZED; AMFTrace *trace; 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->factory) res = amf_ctx->factory->pVtbl->GetTrace(amf_ctx->factory, &trace); if (res == AMF_OK) { trace->pVtbl->UnregisterWriter(trace, FFMPEG_AMF_WRITER_ID); } if(amf_ctx->library) { dlclose(amf_ctx->library); amf_ctx->library = NULL; } if (amf_ctx->trace_writer) { amf_writer_free(amf_ctx->trace_writer); } amf_ctx->version = 0; } static int amf_device_init(AVHWDeviceContext *ctx) { AVAMFDeviceContext *amf_ctx = ctx->hwctx; AMFContext1 *context1 = NULL; AMF_RESULT res; #ifdef _WIN32 res = amf_ctx->context->pVtbl->InitDX11(amf_ctx->context, NULL, AMF_DX11_1); if (res == AMF_OK || res == AMF_ALREADY_INITIALIZED) { 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 { #endif AMFGuid guid = IID_AMFContext1(); res = amf_ctx->context->pVtbl->QueryInterface(amf_ctx->context, &guid, (void**)&context1); AMF_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 && res != AMF_ALREADY_INITIALIZED) { 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"); #ifdef _WIN32 } } #endif return 0; } static int amf_load_library(AVAMFDeviceContext* amf_ctx, void* avcl) { AMFInit_Fn init_fun; AMFQueryVersion_Fn version_fun; AMF_RESULT res; amf_ctx->library = dlopen(AMF_DLL_NAMEA, RTLD_NOW | RTLD_LOCAL); AMF_RETURN_IF_FALSE(avcl, amf_ctx->library != NULL, AVERROR_UNKNOWN, "DLL %s failed to open\n", AMF_DLL_NAMEA); init_fun = (AMFInit_Fn)dlsym(amf_ctx->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(amf_ctx->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(&amf_ctx->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, &amf_ctx->factory); AMF_RETURN_IF_FALSE(avcl, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_INIT_FUNCTION_NAME, res); return 0; } static int amf_device_create(AVHWDeviceContext *device_ctx, const char *device, AVDictionary *opts, int flags) { AVAMFDeviceContext *ctx = device_ctx->hwctx; AMFTrace *trace; int ret; if ((ret = amf_load_library(ctx, device_ctx)) == 0) { ret = ctx->factory->pVtbl->GetTrace(ctx->factory, &trace); if (ret == AMF_OK) { int level_ff = av_log_get_level(); int level_amf = AMF_TRACE_TRACE; amf_bool enable_log = true; switch(level_ff) { case AV_LOG_QUIET: level_amf = AMF_TRACE_ERROR; enable_log = false; break; case AV_LOG_PANIC: case AV_LOG_FATAL: case AV_LOG_ERROR: level_amf = AMF_TRACE_ERROR; break; case AV_LOG_WARNING: case AV_LOG_INFO: level_amf = AMF_TRACE_WARNING; break; case AV_LOG_VERBOSE: level_amf = AMF_TRACE_INFO; break; case AV_LOG_DEBUG: level_amf = AMF_TRACE_DEBUG; break; case AV_LOG_TRACE: level_amf = AMF_TRACE_TRACE; break; } if(ctx->version == AMF_MAKE_FULL_VERSION(1, 4, 35, 0)){// get around a bug in trace in AMF runtime driver 24.20 level_amf = AMF_TRACE_WARNING; } trace->pVtbl->EnableWriter(trace, AMF_TRACE_WRITER_CONSOLE, 0); trace->pVtbl->SetGlobalLevel(trace, level_amf); // connect AMF logger to av_log ctx->trace_writer = amf_writer_alloc(device_ctx); trace->pVtbl->RegisterWriter(trace, FFMPEG_AMF_WRITER_ID, (AMFTraceWriter*)ctx->trace_writer, 1); trace->pVtbl->SetWriterLevel(trace, FFMPEG_AMF_WRITER_ID, level_amf); trace->pVtbl->EnableWriter(trace, FFMPEG_AMF_WRITER_ID, enable_log); trace->pVtbl->SetWriterLevel(trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, level_amf); trace->pVtbl->EnableWriter(trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, enable_log); } ret = ctx->factory->pVtbl->CreateContext(ctx->factory, &ctx->context); if (ret == AMF_OK) { AMF_ASSIGN_PROPERTY_INT64(ret, ctx->context, L"DeviceSurfaceCacheSize", 50 ); return 0; } av_log(device_ctx, AV_LOG_ERROR, "CreateContext() failed with error %d.\n", ret); } amf_device_uninit(device_ctx); return ret; } #if CONFIG_DXVA2 static int amf_init_from_dxva2_device(AVAMFDeviceContext * amf_ctx, AVHWDeviceContext *child_device_ctx) { AVDXVA2DeviceContext *hwctx = child_device_ctx->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(child_device_ctx, 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(child_device_ctx, 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 = amf_ctx->context->pVtbl->InitDX9(amf_ctx->context, device); IDirect3DDevice9_Release(device); if (res != AMF_OK && res != AMF_ALREADY_INITIALIZED) { if (res == AMF_NOT_SUPPORTED) av_log(child_device_ctx, AV_LOG_ERROR, "AMF via D3D9 is not supported on the given device.\n"); else av_log(child_device_ctx, AV_LOG_ERROR, "AMF failed to initialise on given D3D9 device: %d.\n", res); return AVERROR(ENODEV); } av_log(child_device_ctx, AV_LOG_INFO, "AMF via DXVA2.\n"); return 0; } #endif #if CONFIG_D3D11VA static int amf_init_from_d3d11_device(AVAMFDeviceContext* amf_ctx, AVHWDeviceContext *child_device_ctx) { AMF_RESULT res; AVD3D11VADeviceContext *hwctx = child_device_ctx->hwctx; res = amf_ctx->context->pVtbl->InitDX11(amf_ctx->context, hwctx->device, AMF_DX11_1); if (res != AMF_OK && res != AMF_ALREADY_INITIALIZED) { if (res == AMF_NOT_SUPPORTED) av_log(child_device_ctx, AV_LOG_ERROR, "AMF via D3D11 is not supported on the given device.\n"); else av_log(child_device_ctx, AV_LOG_ERROR, "AMF failed to initialise on the given D3D11 device: %d.\n", res); return AVERROR(ENODEV); } av_log(child_device_ctx, AV_LOG_INFO, "AMF via D3D11.\n"); return 0; } #endif #if CONFIG_D3D12VA static int amf_init_from_d3d12_device(AVAMFDeviceContext* amf_ctx, AVHWDeviceContext *child_device_ctx) { AVD3D12VADeviceContext *hwctx = child_device_ctx->hwctx; AMF_RESULT res; AMFContext2 *context2 = NULL; AMFGuid guid = IID_AMFContext2(); res = amf_ctx->context->pVtbl->QueryInterface(amf_ctx->context, &guid, (void**)&context2); AMF_RETURN_IF_FALSE(child_device_ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext2() failed with error %d\n", res); res = context2->pVtbl->InitDX12(context2, hwctx->device, AMF_DX12); context2->pVtbl->Release(context2); if (res != AMF_OK && res != AMF_ALREADY_INITIALIZED) { if (res == AMF_NOT_SUPPORTED) av_log(child_device_ctx, AV_LOG_ERROR, "AMF via D3D12 is not supported on the given device.\n"); else av_log(child_device_ctx, AV_LOG_ERROR, "AMF failed to initialise on the given D3D12 device: %d.\n", res); return AVERROR(ENODEV); } av_log(child_device_ctx, AV_LOG_INFO, "AMF via D3D12.\n"); return 0; } #endif static int amf_device_derive(AVHWDeviceContext *device_ctx, AVHWDeviceContext *child_device_ctx, AVDictionary *opts, int flags) { #if CONFIG_DXVA2 || CONFIG_D3D11VA AVAMFDeviceContext *amf_ctx = device_ctx->hwctx; #endif int ret; ret = amf_device_create(device_ctx, "", opts, flags); if(ret < 0) return ret; switch (child_device_ctx->type) { #if CONFIG_DXVA2 case AV_HWDEVICE_TYPE_DXVA2: { return amf_init_from_dxva2_device(amf_ctx, child_device_ctx); } break; #endif #if CONFIG_D3D11VA case AV_HWDEVICE_TYPE_D3D11VA: { return amf_init_from_d3d11_device(amf_ctx, child_device_ctx); } break; #endif #if CONFIG_D3D12VA case AV_HWDEVICE_TYPE_D3D12VA: { return amf_init_from_d3d12_device(amf_ctx, child_device_ctx); } 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_hwctx_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_SURFACE, AV_PIX_FMT_NONE }, };