| @@ -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 | }; |