| @@ -0,0 +1,610 @@ |
| 1 | +/* |
| 2 | + * This file is part of FFmpeg. |
| 3 | + * |
| 4 | + * FFmpeg is free software; you can redistribute it and/or |
| 5 | + * modify it under the terms of the GNU Lesser General Public |
| 6 | + * License as published by the Free Software Foundation; either |
| 7 | + * version 2.1 of the License, or (at your option) any later version. |
| 8 | + * |
| 9 | + * FFmpeg is distributed in the hope that it will be useful, |
| 10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | + * Lesser General Public License for more details. |
| 13 | + * |
| 14 | + * You should have received a copy of the GNU Lesser General Public |
| 15 | + * License along with FFmpeg; if not, write to the Free Software |
| 16 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 17 | + */ |
| 18 | + |
| 19 | +#include "buffer.h" |
| 20 | +#include "common.h" |
| 21 | +#include "hwcontext.h" |
| 22 | +#include "hwcontext_amf.h" |
| 23 | +#include "hwcontext_internal.h" |
| 24 | +#include "hwcontext_amf_internal.h" |
| 25 | +#if CONFIG_VULKAN |
| 26 | +#include "hwcontext_vulkan.h" |
| 27 | +#endif |
| 28 | +#if CONFIG_D3D11VA |
| 29 | +#include "libavutil/hwcontext_d3d11va.h" |
| 30 | +#endif |
| 31 | +#if CONFIG_DXVA2 |
| 32 | +#define COBJMACROS |
| 33 | +#include "libavutil/hwcontext_dxva2.h" |
| 34 | +#endif |
| 35 | +#include "mem.h" |
| 36 | +#include "pixdesc.h" |
| 37 | +#include "pixfmt.h" |
| 38 | +#include "imgutils.h" |
| 39 | +#include "libavutil/avassert.h" |
| 40 | +#include <AMF/core/Surface.h> |
| 41 | +#include <AMF/core/Trace.h> |
| 42 | +#ifdef _WIN32 |
| 43 | +#include "compat/w32dlfcn.h" |
| 44 | +#else |
| 45 | +#include <dlfcn.h> |
| 46 | +#endif |
| 47 | +#define FFMPEG_AMF_WRITER_ID L"ffmpeg_amf" |
| 48 | + |
| 49 | + |
| 50 | +typedef struct AmfTraceWriter { |
| 51 | + AMFTraceWriterVtbl *vtblp; |
| 52 | + void *avctx; |
| 53 | + AMFTraceWriterVtbl vtbl; |
| 54 | +} AmfTraceWriter; |
| 55 | + |
| 56 | +static void AMF_CDECL_CALL AMFTraceWriter_Write(AMFTraceWriter *pThis, |
| 57 | + const wchar_t *scope, const wchar_t *message) |
| 58 | +{ |
| 59 | + AmfTraceWriter *tracer = (AmfTraceWriter*)pThis; |
| 60 | + av_log(tracer->avctx, AV_LOG_DEBUG, "%ls: %ls", scope, message); // \n is provided from AMF |
| 61 | +} |
| 62 | + |
| 63 | +static void AMF_CDECL_CALL AMFTraceWriter_Flush(AMFTraceWriter *pThis) |
| 64 | +{ |
| 65 | +} |
| 66 | + |
| 67 | +static AmfTraceWriter * amf_writer_alloc(void *avctx) |
| 68 | +{ |
| 69 | + AmfTraceWriter * writer = av_mallocz(sizeof(AmfTraceWriter)); |
| 70 | + if (!writer) |
| 71 | + return NULL; |
| 72 | + |
| 73 | + writer->vtblp = &writer->vtbl; |
| 74 | + writer->vtblp->Write = AMFTraceWriter_Write; |
| 75 | + writer->vtblp->Flush = AMFTraceWriter_Flush; |
| 76 | + writer->avctx = avctx; |
| 77 | + |
| 78 | + return writer; |
| 79 | +} |
| 80 | + |
| 81 | +static void amf_writer_free(void *opaque) |
| 82 | +{ |
| 83 | + AmfTraceWriter *writer = (AmfTraceWriter *)opaque; |
| 84 | + av_freep(&writer); |
| 85 | +} |
| 86 | + |
| 87 | +/** |
| 88 | + * We still need AVHWFramesContext to utilize our hardware memory |
| 89 | + * otherwise, we will receive the error "HW format requires hw_frames_ctx to be non-NULL". |
| 90 | + * (libavfilter\buffersrc.c function query_formats) |
| 91 | +*/ |
| 92 | +typedef struct { |
| 93 | + void *dummy; |
| 94 | +} AMFFramesContext; |
| 95 | + |
| 96 | +typedef struct AVAMFFormatMap { |
| 97 | + enum AVPixelFormat av_format; |
| 98 | + enum AMF_SURFACE_FORMAT amf_format; |
| 99 | +} FormatMap; |
| 100 | + |
| 101 | +const FormatMap format_map[] = |
| 102 | +{ |
| 103 | + { AV_PIX_FMT_NONE, AMF_SURFACE_UNKNOWN }, |
| 104 | + { AV_PIX_FMT_NV12, AMF_SURFACE_NV12 }, |
| 105 | + { AV_PIX_FMT_BGR0, AMF_SURFACE_BGRA }, |
| 106 | + { AV_PIX_FMT_RGB0, AMF_SURFACE_RGBA }, |
| 107 | + { AV_PIX_FMT_BGRA, AMF_SURFACE_BGRA }, |
| 108 | + { AV_PIX_FMT_ARGB, AMF_SURFACE_ARGB }, |
| 109 | + { AV_PIX_FMT_RGBA, AMF_SURFACE_RGBA }, |
| 110 | + { AV_PIX_FMT_GRAY8, AMF_SURFACE_GRAY8 }, |
| 111 | + { AV_PIX_FMT_YUV420P, AMF_SURFACE_YUV420P }, |
| 112 | + { AV_PIX_FMT_YUYV422, AMF_SURFACE_YUY2 }, |
| 113 | + { AV_PIX_FMT_P010, AMF_SURFACE_P010 }, |
| 114 | +}; |
| 115 | + |
| 116 | +enum AMF_SURFACE_FORMAT av_av_to_amf_format(enum AVPixelFormat fmt) |
| 117 | +{ |
| 118 | + int i; |
| 119 | + for (i = 0; i < amf_countof(format_map); i++) { |
| 120 | + if (format_map[i].av_format == fmt) { |
| 121 | + return format_map[i].amf_format; |
| 122 | + } |
| 123 | + } |
| 124 | + return AMF_SURFACE_UNKNOWN; |
| 125 | +} |
| 126 | + |
| 127 | +enum AVPixelFormat av_amf_to_av_format(enum AMF_SURFACE_FORMAT fmt) |
| 128 | +{ |
| 129 | + int i; |
| 130 | + for (i = 0; i < amf_countof(format_map); i++) { |
| 131 | + if (format_map[i].amf_format == fmt) { |
| 132 | + return format_map[i].av_format; |
| 133 | + } |
| 134 | + } |
| 135 | + return AMF_SURFACE_UNKNOWN; |
| 136 | +} |
| 137 | + |
| 138 | +static const enum AVPixelFormat supported_formats[] = { |
| 139 | + AV_PIX_FMT_NV12, |
| 140 | + AV_PIX_FMT_YUV420P, |
| 141 | + AV_PIX_FMT_BGRA, |
| 142 | + AV_PIX_FMT_RGBA, |
| 143 | + AV_PIX_FMT_P010, |
| 144 | +#if CONFIG_D3D11VA |
| 145 | + AV_PIX_FMT_D3D11, |
| 146 | +#endif |
| 147 | +#if CONFIG_DXVA2 |
| 148 | + AV_PIX_FMT_DXVA2_VLD, |
| 149 | +#endif |
| 150 | +}; |
| 151 | + |
| 152 | +static const enum AVPixelFormat supported_transfer_formats[] = { |
| 153 | + AV_PIX_FMT_NV12, |
| 154 | + AV_PIX_FMT_YUV420P, |
| 155 | + AV_PIX_FMT_BGRA, |
| 156 | + AV_PIX_FMT_RGBA, |
| 157 | + AV_PIX_FMT_P010, |
| 158 | + AV_PIX_FMT_NONE, |
| 159 | +}; |
| 160 | + |
| 161 | +static int amf_frames_get_constraints(AVHWDeviceContext *ctx, |
| 162 | + const void *hwconfig, |
| 163 | + AVHWFramesConstraints *constraints) |
| 164 | +{ |
| 165 | + int i; |
| 166 | + |
| 167 | + constraints->valid_sw_formats = av_malloc_array(FF_ARRAY_ELEMS(supported_formats) + 1, |
| 168 | + sizeof(*constraints->valid_sw_formats)); |
| 169 | + if (!constraints->valid_sw_formats) |
| 170 | + return AVERROR(ENOMEM); |
| 171 | + |
| 172 | + for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) |
| 173 | + constraints->valid_sw_formats[i] = supported_formats[i]; |
| 174 | + constraints->valid_sw_formats[FF_ARRAY_ELEMS(supported_formats)] = AV_PIX_FMT_NONE; |
| 175 | + |
| 176 | + constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats)); |
| 177 | + if (!constraints->valid_hw_formats) |
| 178 | + return AVERROR(ENOMEM); |
| 179 | + |
| 180 | + constraints->valid_hw_formats[0] = AV_PIX_FMT_AMF_SURFACE; |
| 181 | + constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE; |
| 182 | + |
| 183 | + return 0; |
| 184 | +} |
| 185 | + |
| 186 | +static void amf_dummy_free(void *opaque, uint8_t *data) |
| 187 | +{ |
| 188 | + |
| 189 | +} |
| 190 | + |
| 191 | +static AVBufferRef *amf_pool_alloc(void *opaque, size_t size) |
| 192 | +{ |
| 193 | + AVHWFramesContext *hwfc = (AVHWFramesContext *)opaque; |
| 194 | + AVBufferRef *buf; |
| 195 | + |
| 196 | + buf = av_buffer_create(NULL, 0, amf_dummy_free, hwfc, AV_BUFFER_FLAG_READONLY); |
| 197 | + if (!buf) { |
| 198 | + av_log(hwfc, AV_LOG_ERROR, "Failed to create buffer for AMF context.\n"); |
| 199 | + return NULL; |
| 200 | + } |
| 201 | + return buf; |
| 202 | +} |
| 203 | + |
| 204 | +static int amf_frames_init(AVHWFramesContext *ctx) |
| 205 | +{ |
| 206 | + int i; |
| 207 | + |
| 208 | + for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) { |
| 209 | + if (ctx->sw_format == supported_formats[i]) |
| 210 | + break; |
| 211 | + } |
| 212 | + if (i == FF_ARRAY_ELEMS(supported_formats)) { |
| 213 | + av_log(ctx, AV_LOG_ERROR, "Pixel format '%s' is not supported\n", |
| 214 | + av_get_pix_fmt_name(ctx->sw_format)); |
| 215 | + return AVERROR(ENOSYS); |
| 216 | + } |
| 217 | + |
| 218 | + ffhwframesctx(ctx)->pool_internal = |
| 219 | + av_buffer_pool_init2(sizeof(AMFSurface), ctx, |
| 220 | + &amf_pool_alloc, NULL); |
| 221 | + |
| 222 | + return 0; |
| 223 | +} |
| 224 | + |
| 225 | + |
| 226 | +static int amf_get_buffer(AVHWFramesContext *ctx, AVFrame *frame) |
| 227 | +{ |
| 228 | + frame->buf[0] = av_buffer_pool_get(ctx->pool); |
| 229 | + if (!frame->buf[0]) |
| 230 | + return AVERROR(ENOMEM); |
| 231 | + |
| 232 | + frame->data[0] = frame->buf[0]->data; |
| 233 | + frame->format = AV_PIX_FMT_AMF_SURFACE; |
| 234 | + frame->width = ctx->width; |
| 235 | + frame->height = ctx->height; |
| 236 | + return 0; |
| 237 | +} |
| 238 | + |
| 239 | +static int amf_transfer_get_formats(AVHWFramesContext *ctx, |
| 240 | + enum AVHWFrameTransferDirection dir, |
| 241 | + enum AVPixelFormat **formats) |
| 242 | +{ |
| 243 | + enum AVPixelFormat *fmts; |
| 244 | + int i; |
| 245 | + |
| 246 | + fmts = av_malloc_array(FF_ARRAY_ELEMS(supported_transfer_formats), sizeof(*fmts)); |
| 247 | + if (!fmts) |
| 248 | + return AVERROR(ENOMEM); |
| 249 | + for (i = 0; i < FF_ARRAY_ELEMS(supported_transfer_formats); i++) |
| 250 | + fmts[i] = supported_transfer_formats[i]; |
| 251 | + |
| 252 | + *formats = fmts; |
| 253 | + |
| 254 | + return 0; |
| 255 | +} |
| 256 | + |
| 257 | +static void amf_free_amfsurface(void *opaque, uint8_t *data) |
| 258 | +{ |
| 259 | + if(!!data){ |
| 260 | + AMFSurface *surface = (AMFSurface*)(data); |
| 261 | + surface->pVtbl->Release(surface); |
| 262 | + } |
| 263 | +} |
| 264 | + |
| 265 | +static int amf_transfer_data_to(AVHWFramesContext *ctx, AVFrame *dst, |
| 266 | + const AVFrame *src) |
| 267 | +{ |
| 268 | + AMFSurface* surface = (AMFSurface*)dst->data[0]; |
| 269 | + AMFPlane *plane; |
| 270 | + uint8_t *dst_data[4]; |
| 271 | + int dst_linesize[4]; |
| 272 | + int planes; |
| 273 | + int i; |
| 274 | + int res; |
| 275 | + int w = FFMIN(dst->width, src->width); |
| 276 | + int h = FFMIN(dst->height, src->height); |
| 277 | + |
| 278 | + if (!surface) { |
| 279 | + AVHWDeviceContext *hwdev_ctx = ctx->device_ctx; |
| 280 | + AVAMFDeviceContext *amf_device_ctx = (AVAMFDeviceContext *)hwdev_ctx->hwctx; |
| 281 | + AMF_SURFACE_FORMAT format = av_av_to_amf_format(ctx->sw_format); |
| 282 | + res = amf_device_ctx->context->pVtbl->AllocSurface(amf_device_ctx->context, AMF_MEMORY_HOST, format, dst->width, dst->height, &surface); |
| 283 | + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM), "AllocSurface() failed with error %d\n", res); |
| 284 | + dst->data[0] = (uint8_t *)surface; |
| 285 | + dst->buf[1] = av_buffer_create((uint8_t *)surface, sizeof(surface), |
| 286 | + amf_free_amfsurface, |
| 287 | + NULL, |
| 288 | + AV_BUFFER_FLAG_READONLY); |
| 289 | + AMF_RETURN_IF_FALSE(ctx, !!dst->buf[1], AVERROR(ENOMEM), "av_buffer_create for amf surface failed."); |
| 290 | + } |
| 291 | + |
| 292 | + planes = (int)surface->pVtbl->GetPlanesCount(surface); |
| 293 | + av_assert0(planes < FF_ARRAY_ELEMS(dst_data)); |
| 294 | + |
| 295 | + for (i = 0; i < planes; i++) { |
| 296 | + plane = surface->pVtbl->GetPlaneAt(surface, i); |
| 297 | + dst_data[i] = plane->pVtbl->GetNative(plane); |
| 298 | + dst_linesize[i] = plane->pVtbl->GetHPitch(plane); |
| 299 | + } |
| 300 | + av_image_copy2(dst_data, dst_linesize, |
| 301 | + src->data, src->linesize, src->format, |
| 302 | + w, h); |
| 303 | + |
| 304 | + return 0; |
| 305 | +} |
| 306 | + |
| 307 | +static int amf_transfer_data_from(AVHWFramesContext *ctx, AVFrame *dst, |
| 308 | + const AVFrame *src) |
| 309 | +{ |
| 310 | + AMFSurface* surface = (AMFSurface*)src->data[0]; |
| 311 | + AMFPlane *plane; |
| 312 | + uint8_t *src_data[4]; |
| 313 | + int src_linesize[4]; |
| 314 | + int planes; |
| 315 | + int i; |
| 316 | + int w = FFMIN(dst->width, src->width); |
| 317 | + int h = FFMIN(dst->height, src->height); |
| 318 | + int ret; |
| 319 | + |
| 320 | + ret = surface->pVtbl->Convert(surface, AMF_MEMORY_HOST); |
| 321 | + AMF_RETURN_IF_FALSE(ctx, ret == AMF_OK, AVERROR_UNKNOWN, "Convert(amf::AMF_MEMORY_HOST) failed with error %d\n", AVERROR_UNKNOWN); |
| 322 | + |
| 323 | + planes = (int)surface->pVtbl->GetPlanesCount(surface); |
| 324 | + av_assert0(planes < FF_ARRAY_ELEMS(src_data)); |
| 325 | + |
| 326 | + for (i = 0; i < planes; i++) { |
| 327 | + plane = surface->pVtbl->GetPlaneAt(surface, i); |
| 328 | + src_data[i] = plane->pVtbl->GetNative(plane); |
| 329 | + src_linesize[i] = plane->pVtbl->GetHPitch(plane); |
| 330 | + } |
| 331 | + av_image_copy2(dst->data, dst->linesize, |
| 332 | + src_data, src_linesize, dst->format, |
| 333 | + w, h); |
| 334 | + return 0; |
| 335 | +} |
| 336 | + |
| 337 | + |
| 338 | + |
| 339 | +static void amf_device_uninit(AVHWDeviceContext *device_ctx) |
| 340 | +{ |
| 341 | + AVAMFDeviceContext *amf_ctx = device_ctx->hwctx; |
| 342 | + AMF_RESULT res; |
| 343 | + AMFTrace *trace; |
| 344 | + |
| 345 | + if (amf_ctx->context) { |
| 346 | + amf_ctx->context->pVtbl->Terminate(amf_ctx->context); |
| 347 | + amf_ctx->context->pVtbl->Release(amf_ctx->context); |
| 348 | + amf_ctx->context = NULL; |
| 349 | + } |
| 350 | + |
| 351 | + res = amf_ctx->factory->pVtbl->GetTrace(amf_ctx->factory, &trace); |
| 352 | + if (res == AMF_OK) { |
| 353 | + trace->pVtbl->UnregisterWriter(trace, FFMPEG_AMF_WRITER_ID); |
| 354 | + } |
| 355 | + |
| 356 | + if(amf_ctx->library) { |
| 357 | + dlclose(amf_ctx->library); |
| 358 | + amf_ctx->library = NULL; |
| 359 | + } |
| 360 | + if (amf_ctx->trace_writer) { |
| 361 | + amf_writer_free(amf_ctx->trace_writer); |
| 362 | + } |
| 363 | + |
| 364 | + amf_ctx->version = 0; |
| 365 | +} |
| 366 | + |
| 367 | +static int amf_device_init(AVHWDeviceContext *ctx) |
| 368 | +{ |
| 369 | + AVAMFDeviceContext *amf_ctx = ctx->hwctx; |
| 370 | + AMFContext1 *context1 = NULL; |
| 371 | + AMF_RESULT res; |
| 372 | + |
| 373 | +#ifdef _WIN32 |
| 374 | + res = amf_ctx->context->pVtbl->InitDX11(amf_ctx->context, NULL, AMF_DX11_1); |
| 375 | + if (res == AMF_OK || res == AMF_ALREADY_INITIALIZED) { |
| 376 | + av_log(ctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D11.\n"); |
| 377 | + } else { |
| 378 | + res = amf_ctx->context->pVtbl->InitDX9(amf_ctx->context, NULL); |
| 379 | + if (res == AMF_OK) { |
| 380 | + av_log(ctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D9.\n"); |
| 381 | + } else { |
| 382 | +#endif |
| 383 | + AMFGuid guid = IID_AMFContext1(); |
| 384 | + res = amf_ctx->context->pVtbl->QueryInterface(amf_ctx->context, &guid, (void**)&context1); |
| 385 | + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext1() failed with error %d\n", res); |
| 386 | + |
| 387 | + res = context1->pVtbl->InitVulkan(context1, NULL); |
| 388 | + context1->pVtbl->Release(context1); |
| 389 | + if (res != AMF_OK && res != AMF_ALREADY_INITIALIZED) { |
| 390 | + if (res == AMF_NOT_SUPPORTED) |
| 391 | + av_log(ctx, AV_LOG_ERROR, "AMF via Vulkan is not supported on the given device.\n"); |
| 392 | + else |
| 393 | + av_log(ctx, AV_LOG_ERROR, "AMF failed to initialise on the given Vulkan device: %d.\n", res); |
| 394 | + return AVERROR(ENOSYS); |
| 395 | + } |
| 396 | + av_log(ctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via Vulkan.\n"); |
| 397 | +#ifdef _WIN32 |
| 398 | + } |
| 399 | + } |
| 400 | +#endif |
| 401 | + return 0; |
| 402 | +} |
| 403 | + |
| 404 | +static int amf_load_library(AVAMFDeviceContext* amf_ctx, void* avcl) |
| 405 | +{ |
| 406 | + AMFInit_Fn init_fun; |
| 407 | + AMFQueryVersion_Fn version_fun; |
| 408 | + AMF_RESULT res; |
| 409 | + |
| 410 | + amf_ctx->library = dlopen(AMF_DLL_NAMEA, RTLD_NOW | RTLD_LOCAL); |
| 411 | + AMF_RETURN_IF_FALSE(avcl, amf_ctx->library != NULL, |
| 412 | + AVERROR_UNKNOWN, "DLL %s failed to open\n", AMF_DLL_NAMEA); |
| 413 | + |
| 414 | + init_fun = (AMFInit_Fn)dlsym(amf_ctx->library, AMF_INIT_FUNCTION_NAME); |
| 415 | + 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); |
| 416 | + |
| 417 | + version_fun = (AMFQueryVersion_Fn)dlsym(amf_ctx->library, AMF_QUERY_VERSION_FUNCTION_NAME); |
| 418 | + 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); |
| 419 | + |
| 420 | + res = version_fun(&amf_ctx->version); |
| 421 | + AMF_RETURN_IF_FALSE(avcl, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_QUERY_VERSION_FUNCTION_NAME, res); |
| 422 | + res = init_fun(AMF_FULL_VERSION, &amf_ctx->factory); |
| 423 | + AMF_RETURN_IF_FALSE(avcl, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_INIT_FUNCTION_NAME, res); |
| 424 | + return 0; |
| 425 | +} |
| 426 | + |
| 427 | +static int amf_device_create(AVHWDeviceContext *device_ctx, |
| 428 | + const char *device, |
| 429 | + AVDictionary *opts, int flags) |
| 430 | +{ |
| 431 | + AVAMFDeviceContext *ctx = device_ctx->hwctx; |
| 432 | + AMFTrace *trace; |
| 433 | + int ret; |
| 434 | + if ((ret = amf_load_library(ctx, device_ctx)) == 0) { |
| 435 | + ret = ctx->factory->pVtbl->GetTrace(ctx->factory, &trace); |
| 436 | + if (ret == AMF_OK) { |
| 437 | + int level_ff = av_log_get_level(); |
| 438 | + int level_amf = AMF_TRACE_TRACE; |
| 439 | + amf_bool enable_log = true; |
| 440 | + switch(level_ff) |
| 441 | + { |
| 442 | + case AV_LOG_QUIET: |
| 443 | + level_amf = AMF_TRACE_ERROR; |
| 444 | + enable_log = false; |
| 445 | + break; |
| 446 | + case AV_LOG_PANIC: |
| 447 | + case AV_LOG_FATAL: |
| 448 | + case AV_LOG_ERROR: |
| 449 | + level_amf = AMF_TRACE_ERROR; |
| 450 | + break; |
| 451 | + case AV_LOG_WARNING: |
| 452 | + case AV_LOG_INFO: |
| 453 | + level_amf = AMF_TRACE_WARNING; |
| 454 | + break; |
| 455 | + case AV_LOG_VERBOSE: |
| 456 | + level_amf = AMF_TRACE_INFO; |
| 457 | + break; |
| 458 | + case AV_LOG_DEBUG: |
| 459 | + level_amf = AMF_TRACE_DEBUG; |
| 460 | + break; |
| 461 | + case AV_LOG_TRACE: |
| 462 | + level_amf = AMF_TRACE_TRACE; |
| 463 | + break; |
| 464 | + } |
| 465 | + if(ctx->version == AMF_MAKE_FULL_VERSION(1, 4, 35, 0)){// get around a bug in trace in AMF runtime driver 24.20 |
| 466 | + level_amf = AMF_TRACE_WARNING; |
| 467 | + } |
| 468 | + |
| 469 | + trace->pVtbl->EnableWriter(trace, AMF_TRACE_WRITER_CONSOLE, 0); |
| 470 | + trace->pVtbl->SetGlobalLevel(trace, level_amf); |
| 471 | + |
| 472 | + // connect AMF logger to av_log |
| 473 | + ctx->trace_writer = amf_writer_alloc(device_ctx); |
| 474 | + trace->pVtbl->RegisterWriter(trace, FFMPEG_AMF_WRITER_ID, (AMFTraceWriter*)ctx->trace_writer, 1); |
| 475 | + trace->pVtbl->SetWriterLevel(trace, FFMPEG_AMF_WRITER_ID, level_amf); |
| 476 | + trace->pVtbl->EnableWriter(trace, FFMPEG_AMF_WRITER_ID, enable_log); |
| 477 | + trace->pVtbl->SetWriterLevel(trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, level_amf); |
| 478 | + trace->pVtbl->EnableWriter(trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, enable_log); |
| 479 | + } |
| 480 | + |
| 481 | + |
| 482 | + ret = ctx->factory->pVtbl->CreateContext(ctx->factory, &ctx->context); |
| 483 | + if (ret == AMF_OK) |
| 484 | + return 0; |
| 485 | + av_log(device_ctx, AV_LOG_ERROR, "CreateContext() failed with error %d.\n", ret); |
| 486 | + } |
| 487 | + amf_device_uninit(device_ctx); |
| 488 | + return ret; |
| 489 | +} |
| 490 | + |
| 491 | +#if CONFIG_DXVA2 |
| 492 | +static int amf_init_from_dxva2_device(AVAMFDeviceContext * amf_ctx, AVDXVA2DeviceContext *hwctx) |
| 493 | +{ |
| 494 | + IDirect3DDevice9 *device; |
| 495 | + HANDLE device_handle; |
| 496 | + HRESULT hr; |
| 497 | + AMF_RESULT res; |
| 498 | + int ret; |
| 499 | + |
| 500 | + hr = IDirect3DDeviceManager9_OpenDeviceHandle(hwctx->devmgr, &device_handle); |
| 501 | + if (FAILED(hr)) { |
| 502 | + av_log(hwctx, AV_LOG_ERROR, "Failed to open device handle for Direct3D9 device: %lx.\n", (unsigned long)hr); |
| 503 | + return AVERROR_EXTERNAL; |
| 504 | + } |
| 505 | + |
| 506 | + hr = IDirect3DDeviceManager9_LockDevice(hwctx->devmgr, device_handle, &device, FALSE); |
| 507 | + if (SUCCEEDED(hr)) { |
| 508 | + IDirect3DDeviceManager9_UnlockDevice(hwctx->devmgr, device_handle, FALSE); |
| 509 | + ret = 0; |
| 510 | + } else { |
| 511 | + av_log(hwctx, AV_LOG_ERROR, "Failed to lock device handle for Direct3D9 device: %lx.\n", (unsigned long)hr); |
| 512 | + ret = AVERROR_EXTERNAL; |
| 513 | + } |
| 514 | + |
| 515 | + |
| 516 | + IDirect3DDeviceManager9_CloseDeviceHandle(hwctx->devmgr, device_handle); |
| 517 | + |
| 518 | + if (ret < 0) |
| 519 | + return ret; |
| 520 | + |
| 521 | + res = amf_ctx->context->pVtbl->InitDX9(amf_ctx->context, device); |
| 522 | + |
| 523 | + IDirect3DDevice9_Release(device); |
| 524 | + |
| 525 | + if (res != AMF_OK && res != AMF_ALREADY_INITIALIZED) { |
| 526 | + if (res == AMF_NOT_SUPPORTED) |
| 527 | + av_log(hwctx, AV_LOG_ERROR, "AMF via D3D9 is not supported on the given device.\n"); |
| 528 | + else |
| 529 | + av_log(hwctx, AV_LOG_ERROR, "AMF failed to initialise on given D3D9 device: %d.\n", res); |
| 530 | + return AVERROR(ENODEV); |
| 531 | + } |
| 532 | + return 0; |
| 533 | +} |
| 534 | +#endif |
| 535 | + |
| 536 | +#if CONFIG_D3D11VA |
| 537 | +static int amf_init_from_d3d11_device(AVAMFDeviceContext* amf_ctx, AVD3D11VADeviceContext *hwctx) |
| 538 | +{ |
| 539 | + AMF_RESULT res; |
| 540 | + res = amf_ctx->context->pVtbl->InitDX11(amf_ctx->context, hwctx->device, AMF_DX11_1); |
| 541 | + if (res != AMF_OK && res != AMF_ALREADY_INITIALIZED) { |
| 542 | + if (res == AMF_NOT_SUPPORTED) |
| 543 | + av_log(hwctx, AV_LOG_ERROR, "AMF via D3D11 is not supported on the given device.\n"); |
| 544 | + else |
| 545 | + av_log(hwctx, AV_LOG_ERROR, "AMF failed to initialise on the given D3D11 device: %d.\n", res); |
| 546 | + return AVERROR(ENODEV); |
| 547 | + } |
| 548 | + return 0; |
| 549 | +} |
| 550 | +#endif |
| 551 | + |
| 552 | +static int amf_device_derive(AVHWDeviceContext *device_ctx, |
| 553 | + AVHWDeviceContext *child_device_ctx, AVDictionary *opts, |
| 554 | + int flags) |
| 555 | +{ |
| 556 | +#if CONFIG_DXVA2 || CONFIG_D3D11VA |
| 557 | + AVAMFDeviceContext *amf_ctx = device_ctx->hwctx; |
| 558 | +#endif |
| 559 | + int ret; |
| 560 | + |
| 561 | + ret = amf_device_create(device_ctx, "", opts, flags); |
| 562 | + if(ret < 0) |
| 563 | + return ret; |
| 564 | + |
| 565 | + switch (child_device_ctx->type) { |
| 566 | + |
| 567 | +#if CONFIG_DXVA2 |
| 568 | + case AV_HWDEVICE_TYPE_DXVA2: { |
| 569 | + AVDXVA2DeviceContext *child_device_hwctx = child_device_ctx->hwctx; |
| 570 | + return amf_init_from_dxva2_device(amf_ctx, child_device_hwctx); |
| 571 | + } |
| 572 | + break; |
| 573 | +#endif |
| 574 | + |
| 575 | +#if CONFIG_D3D11VA |
| 576 | + case AV_HWDEVICE_TYPE_D3D11VA: { |
| 577 | + AVD3D11VADeviceContext *child_device_hwctx = child_device_ctx->hwctx; |
| 578 | + return amf_init_from_d3d11_device(amf_ctx, child_device_hwctx); |
| 579 | + } |
| 580 | + break; |
| 581 | +#endif |
| 582 | + default: { |
| 583 | + av_log(child_device_ctx, AV_LOG_ERROR, "AMF initialisation from a %s device is not supported.\n", |
| 584 | + av_hwdevice_get_type_name(child_device_ctx->type)); |
| 585 | + return AVERROR(ENOSYS); |
| 586 | + } |
| 587 | + } |
| 588 | + return 0; |
| 589 | +} |
| 590 | + |
| 591 | +const HWContextType ff_hwcontext_type_amf = { |
| 592 | + .type = AV_HWDEVICE_TYPE_AMF, |
| 593 | + .name = "AMF", |
| 594 | + |
| 595 | + .device_hwctx_size = sizeof(AVAMFDeviceContext), |
| 596 | + .frames_hwctx_size = sizeof(AMFFramesContext), |
| 597 | + |
| 598 | + .device_create = amf_device_create, |
| 599 | + .device_derive = amf_device_derive, |
| 600 | + .device_init = amf_device_init, |
| 601 | + .device_uninit = amf_device_uninit, |
| 602 | + .frames_get_constraints = amf_frames_get_constraints, |
| 603 | + .frames_init = amf_frames_init, |
| 604 | + .frames_get_buffer = amf_get_buffer, |
| 605 | + .transfer_get_formats = amf_transfer_get_formats, |
| 606 | + .transfer_data_to = amf_transfer_data_to, |
| 607 | + .transfer_data_from = amf_transfer_data_from, |
| 608 | + |
| 609 | + .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_AMF_SURFACE, AV_PIX_FMT_NONE }, |
| 610 | +}; |