FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/hwcontext_vaapi.c
Date: 2026-09-24 20:08:24
Exec Total Coverage
Lines: 0 925 0.0%
Functions: 0 34 0.0%
Branches: 0 454 0.0%

Line Branch Exec Source
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 "config.h"
20
21 #if HAVE_VAAPI_WIN32
22 # include <windows.h>
23 #define COBJMACROS
24 # include <initguid.h>
25 # include <dxgi1_2.h>
26 # include "compat/w32dlfcn.h"
27 # include <va/va_win32.h>
28 typedef HRESULT (WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
29 #endif
30 #if HAVE_VAAPI_X11
31 # include <va/va_x11.h>
32 #endif
33 #if HAVE_VAAPI_DRM
34 # include <va/va_drm.h>
35 #endif
36
37 #if CONFIG_LIBDRM
38 # include <va/va_drmcommon.h>
39 # include <xf86drm.h>
40 # include <drm_fourcc.h>
41 # ifndef DRM_FORMAT_MOD_INVALID
42 # define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
43 # endif
44 #endif
45
46 #include <fcntl.h>
47 #if HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif
50
51
52 #include "avassert.h"
53 #include "buffer.h"
54 #include "common.h"
55 #include "hwcontext.h"
56 #include "hwcontext_drm.h"
57 #include "hwcontext_internal.h"
58 #include "hwcontext_vaapi.h"
59 #include "mem.h"
60 #include "pixdesc.h"
61 #include "pixfmt.h"
62
63
64 typedef struct VAAPIDevicePriv {
65 #if HAVE_VAAPI_X11
66 Display *x11_display;
67 #endif
68
69 int drm_fd;
70 } VAAPIDevicePriv;
71
72 typedef struct VAAPISurfaceFormat {
73 enum AVPixelFormat pix_fmt;
74 VAImageFormat image_format;
75 unsigned int fourcc;
76 } VAAPISurfaceFormat;
77
78 typedef struct VAAPIDeviceContext {
79 /**
80 * The public AVVAAPIDeviceContext. See hwcontext_vaapi.h for it.
81 */
82 AVVAAPIDeviceContext p;
83
84 // Surface formats which can be used with this device.
85 VAAPISurfaceFormat *formats;
86 int nb_formats;
87 } VAAPIDeviceContext;
88
89 typedef struct VAAPIFramesContext {
90 /**
91 * The public AVVAAPIFramesContext. See hwcontext_vaapi.h for it.
92 */
93 AVVAAPIFramesContext p;
94
95 // Surface attributes set at create time.
96 VASurfaceAttrib *attributes;
97 int nb_attributes;
98 // RT format of the underlying surface (Intel driver ignores this anyway).
99 unsigned int rt_format;
100 // Whether vaDeriveImage works.
101 int derive_works;
102 // Caches whether VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 is unsupported for
103 // surface imports.
104 int prime_2_import_unsupported;
105 } VAAPIFramesContext;
106
107 typedef struct VAAPIMapping {
108 // Handle to the derived or copied image which is mapped.
109 VAImage image;
110 // The mapping flags actually used.
111 int flags;
112 } VAAPIMapping;
113
114 typedef struct VAAPIFormat {
115 unsigned int fourcc;
116 unsigned int rt_format;
117 enum AVPixelFormat pix_fmt;
118 int chroma_planes_swapped;
119 } VAAPIFormatDescriptor;
120
121 #define MAP(va, rt, av, swap_uv) { \
122 VA_FOURCC_ ## va, \
123 VA_RT_FORMAT_ ## rt, \
124 AV_PIX_FMT_ ## av, \
125 swap_uv, \
126 }
127 // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
128 // plane swap cases. The frame handling below tries to hide these.
129 static const VAAPIFormatDescriptor vaapi_format_map[] = {
130 MAP(NV12, YUV420, NV12, 0),
131 #ifdef VA_FOURCC_I420
132 MAP(I420, YUV420, YUV420P, 0),
133 #endif
134 MAP(YV12, YUV420, YUV420P, 1),
135 MAP(IYUV, YUV420, YUV420P, 0),
136 MAP(422H, YUV422, YUV422P, 0),
137 #ifdef VA_FOURCC_YV16
138 MAP(YV16, YUV422, YUV422P, 1),
139 #endif
140 MAP(UYVY, YUV422, UYVY422, 0),
141 MAP(YUY2, YUV422, YUYV422, 0),
142 #ifdef VA_FOURCC_Y210
143 MAP(Y210, YUV422_10, Y210, 0),
144 #endif
145 #ifdef VA_FOURCC_Y212
146 MAP(Y212, YUV422_12, Y212, 0),
147 #endif
148 MAP(411P, YUV411, YUV411P, 0),
149 MAP(422V, YUV422, YUV440P, 0),
150 MAP(444P, YUV444, YUV444P, 0),
151 #ifdef VA_FOURCC_XYUV
152 MAP(XYUV, YUV444, VUYX, 0),
153 #endif
154 MAP(Y800, YUV400, GRAY8, 0),
155 #ifdef VA_FOURCC_P010
156 MAP(P010, YUV420_10BPP, P010, 0),
157 #endif
158 #ifdef VA_FOURCC_P012
159 MAP(P012, YUV420_12, P012, 0),
160 #endif
161 MAP(BGRA, RGB32, BGRA, 0),
162 MAP(BGRX, RGB32, BGR0, 0),
163 MAP(RGBA, RGB32, RGBA, 0),
164 MAP(RGBX, RGB32, RGB0, 0),
165 #ifdef VA_FOURCC_ABGR
166 MAP(ABGR, RGB32, ABGR, 0),
167 MAP(XBGR, RGB32, 0BGR, 0),
168 #endif
169 MAP(ARGB, RGB32, ARGB, 0),
170 MAP(XRGB, RGB32, 0RGB, 0),
171 #ifdef VA_FOURCC_X2R10G10B10
172 MAP(X2R10G10B10, RGB32_10, X2RGB10, 0),
173 #endif
174 #ifdef VA_FOURCC_Y410
175 // libva doesn't include a fourcc for XV30 and the driver only declares
176 // support for Y410, so we must fudge the mapping here.
177 MAP(Y410, YUV444_10, XV30, 0),
178 #endif
179 #ifdef VA_FOURCC_Y412
180 // libva doesn't include a fourcc for XV36 and the driver only declares
181 // support for Y412, so we must fudge the mapping here.
182 MAP(Y412, YUV444_12, XV36, 0),
183 #endif
184 };
185 #undef MAP
186
187 static const VAAPIFormatDescriptor *
188 ✗ vaapi_format_from_fourcc(unsigned int fourcc)
189 {
190 int i;
191 ✗ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
192 ✗ if (vaapi_format_map[i].fourcc == fourcc)
193 ✗ return &vaapi_format_map[i];
194 ✗ return NULL;
195 }
196
197 static const VAAPIFormatDescriptor *
198 ✗ vaapi_format_from_pix_fmt(enum AVPixelFormat pix_fmt, const VAAPIFormatDescriptor *prev)
199 {
200 ✗ const VAAPIFormatDescriptor *end = &vaapi_format_map[FF_ARRAY_ELEMS(vaapi_format_map)];
201 ✗ if (!prev)
202 ✗ prev = vaapi_format_map;
203 else
204 ✗ prev++;
205
206 ✗ for (; prev < end; prev++)
207 ✗ if (prev->pix_fmt == pix_fmt)
208 ✗ return prev;
209 ✗ return NULL;
210 }
211
212 ✗ static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
213 {
214 const VAAPIFormatDescriptor *desc;
215 ✗ desc = vaapi_format_from_fourcc(fourcc);
216 ✗ if (desc)
217 ✗ return desc->pix_fmt;
218 else
219 ✗ return AV_PIX_FMT_NONE;
220 }
221
222 ✗ static int vaapi_get_img_desc_and_format(AVHWDeviceContext *hwdev,
223 enum AVPixelFormat pix_fmt,
224 const VAAPIFormatDescriptor **_desc,
225 VAImageFormat **image_format)
226 {
227 ✗ VAAPIDeviceContext *ctx = hwdev->hwctx;
228 ✗ const VAAPIFormatDescriptor *desc = NULL;
229 int i;
230
231 ✗ while ((desc = vaapi_format_from_pix_fmt(pix_fmt, desc))) {
232 ✗ for (i = 0; i < ctx->nb_formats; i++) {
233 ✗ if (ctx->formats[i].fourcc == desc->fourcc) {
234 ✗ if (_desc)
235 ✗ *_desc = desc;
236 ✗ if (image_format)
237 ✗ *image_format = &ctx->formats[i].image_format;
238 ✗ return 0;
239 }
240 }
241 }
242
243 ✗ return AVERROR(ENOSYS);
244 }
245
246 ✗ static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
247 enum AVPixelFormat pix_fmt,
248 VAImageFormat **image_format)
249 {
250 ✗ if (!image_format)
251 ✗ return AVERROR(EINVAL);
252 ✗ return vaapi_get_img_desc_and_format(hwdev, pix_fmt, NULL, image_format);
253 }
254
255 ✗ static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
256 const void *hwconfig,
257 AVHWFramesConstraints *constraints)
258 {
259 ✗ VAAPIDeviceContext *ctx = hwdev->hwctx;
260 ✗ AVVAAPIDeviceContext *hwctx = &ctx->p;
261 ✗ const AVVAAPIHWConfig *config = hwconfig;
262 ✗ VASurfaceAttrib *attr_list = NULL;
263 VAStatus vas;
264 enum AVPixelFormat pix_fmt;
265 unsigned int fourcc;
266 int err, i, j, attr_count, pix_fmt_count;
267
268 ✗ if (config &&
269 ✗ !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
270 ✗ attr_count = 0;
271 ✗ vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
272 0, &attr_count);
273 ✗ if (vas != VA_STATUS_SUCCESS) {
274 ✗ av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
275 "%d (%s).\n", vas, vaErrorStr(vas));
276 ✗ err = AVERROR(ENOSYS);
277 ✗ goto fail;
278 }
279
280 ✗ attr_list = av_malloc(attr_count * sizeof(*attr_list));
281 ✗ if (!attr_list) {
282 ✗ err = AVERROR(ENOMEM);
283 ✗ goto fail;
284 }
285
286 ✗ vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
287 attr_list, &attr_count);
288 ✗ if (vas != VA_STATUS_SUCCESS) {
289 ✗ av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
290 "%d (%s).\n", vas, vaErrorStr(vas));
291 ✗ err = AVERROR(ENOSYS);
292 ✗ goto fail;
293 }
294
295 ✗ pix_fmt_count = 0;
296 ✗ for (i = 0; i < attr_count; i++) {
297 ✗ switch (attr_list[i].type) {
298 ✗ case VASurfaceAttribPixelFormat:
299 ✗ fourcc = attr_list[i].value.value.i;
300 ✗ pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
301 ✗ if (pix_fmt != AV_PIX_FMT_NONE) {
302 ✗ ++pix_fmt_count;
303 } else {
304 // Something unsupported - ignore.
305 }
306 ✗ break;
307 ✗ case VASurfaceAttribMinWidth:
308 ✗ constraints->min_width = attr_list[i].value.value.i;
309 ✗ break;
310 ✗ case VASurfaceAttribMinHeight:
311 ✗ constraints->min_height = attr_list[i].value.value.i;
312 ✗ break;
313 ✗ case VASurfaceAttribMaxWidth:
314 ✗ constraints->max_width = attr_list[i].value.value.i;
315 ✗ break;
316 ✗ case VASurfaceAttribMaxHeight:
317 ✗ constraints->max_height = attr_list[i].value.value.i;
318 ✗ break;
319 }
320 }
321 ✗ if (pix_fmt_count == 0) {
322 // Nothing usable found. Presumably there exists something which
323 // works, so leave the set null to indicate unknown.
324 ✗ constraints->valid_sw_formats = NULL;
325 } else {
326 ✗ constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
327 sizeof(pix_fmt));
328 ✗ if (!constraints->valid_sw_formats) {
329 ✗ err = AVERROR(ENOMEM);
330 ✗ goto fail;
331 }
332
333 ✗ for (i = j = 0; i < attr_count; i++) {
334 int k;
335
336 ✗ if (attr_list[i].type != VASurfaceAttribPixelFormat)
337 ✗ continue;
338 ✗ fourcc = attr_list[i].value.value.i;
339 ✗ pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
340
341 ✗ if (pix_fmt == AV_PIX_FMT_NONE)
342 ✗ continue;
343
344 ✗ for (k = 0; k < j; k++) {
345 ✗ if (constraints->valid_sw_formats[k] == pix_fmt)
346 ✗ break;
347 }
348
349 ✗ if (k == j)
350 ✗ constraints->valid_sw_formats[j++] = pix_fmt;
351 }
352 ✗ constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
353 }
354 } else {
355 // No configuration supplied.
356 // Return the full set of image formats known by the implementation.
357 ✗ constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
358 sizeof(pix_fmt));
359 ✗ if (!constraints->valid_sw_formats) {
360 ✗ err = AVERROR(ENOMEM);
361 ✗ goto fail;
362 }
363 ✗ for (i = j = 0; i < ctx->nb_formats; i++) {
364 int k;
365
366 ✗ for (k = 0; k < j; k++) {
367 ✗ if (constraints->valid_sw_formats[k] == ctx->formats[i].pix_fmt)
368 ✗ break;
369 }
370
371 ✗ if (k == j)
372 ✗ constraints->valid_sw_formats[j++] = ctx->formats[i].pix_fmt;
373 }
374
375 ✗ constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
376 }
377
378 ✗ constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
379 ✗ if (!constraints->valid_hw_formats) {
380 ✗ err = AVERROR(ENOMEM);
381 ✗ goto fail;
382 }
383 ✗ constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
384 ✗ constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
385
386 ✗ err = 0;
387 ✗ fail:
388 ✗ av_freep(&attr_list);
389 ✗ return err;
390 }
391
392 static const struct {
393 const char *friendly_name;
394 const char *match_string;
395 unsigned int quirks;
396 } vaapi_driver_quirks_table[] = {
397 #if !VA_CHECK_VERSION(1, 0, 0)
398 // The i965 driver did not conform before version 2.0.
399 {
400 "Intel i965 (Quick Sync)",
401 "i965",
402 AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
403 },
404 #endif
405 {
406 "Intel iHD",
407 "ubit",
408 AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
409 },
410 {
411 "VDPAU wrapper",
412 "Splitted-Desktop Systems VDPAU backend for VA-API",
413 AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES,
414 },
415 };
416
417 ✗ static int vaapi_device_init(AVHWDeviceContext *hwdev)
418 {
419 ✗ VAAPIDeviceContext *ctx = hwdev->hwctx;
420 ✗ AVVAAPIDeviceContext *hwctx = &ctx->p;
421 ✗ VAImageFormat *image_list = NULL;
422 VAStatus vas;
423 const char *vendor_string;
424 int err, i, image_count;
425 enum AVPixelFormat pix_fmt;
426 unsigned int fourcc;
427
428 ✗ image_count = vaMaxNumImageFormats(hwctx->display);
429 ✗ if (image_count <= 0) {
430 ✗ err = AVERROR(EIO);
431 ✗ goto fail;
432 }
433 ✗ image_list = av_malloc(image_count * sizeof(*image_list));
434 ✗ if (!image_list) {
435 ✗ err = AVERROR(ENOMEM);
436 ✗ goto fail;
437 }
438 ✗ vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
439 ✗ if (vas != VA_STATUS_SUCCESS) {
440 ✗ err = AVERROR(EIO);
441 ✗ goto fail;
442 }
443
444 ✗ ctx->formats = av_malloc(image_count * sizeof(*ctx->formats));
445 ✗ if (!ctx->formats) {
446 ✗ err = AVERROR(ENOMEM);
447 ✗ goto fail;
448 }
449 ✗ ctx->nb_formats = 0;
450 ✗ for (i = 0; i < image_count; i++) {
451 ✗ fourcc = image_list[i].fourcc;
452 ✗ pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
453 ✗ if (pix_fmt == AV_PIX_FMT_NONE) {
454 ✗ av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
455 fourcc);
456 } else {
457 ✗ av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
458 fourcc, av_get_pix_fmt_name(pix_fmt));
459 ✗ ctx->formats[ctx->nb_formats].pix_fmt = pix_fmt;
460 ✗ ctx->formats[ctx->nb_formats].fourcc = fourcc;
461 ✗ ctx->formats[ctx->nb_formats].image_format = image_list[i];
462 ✗ ++ctx->nb_formats;
463 }
464 }
465
466 ✗ vendor_string = vaQueryVendorString(hwctx->display);
467 ✗ if (vendor_string)
468 ✗ av_log(hwdev, AV_LOG_VERBOSE, "VAAPI driver: %s.\n", vendor_string);
469
470 ✗ if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
471 ✗ av_log(hwdev, AV_LOG_VERBOSE, "Using quirks set by user (%#x).\n",
472 hwctx->driver_quirks);
473 } else {
474 // Detect the driver in use and set quirk flags if necessary.
475 ✗ hwctx->driver_quirks = 0;
476 ✗ if (vendor_string) {
477 ✗ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
478 ✗ if (strstr(vendor_string,
479 ✗ vaapi_driver_quirks_table[i].match_string)) {
480 ✗ av_log(hwdev, AV_LOG_VERBOSE, "Matched driver string "
481 "as known nonstandard driver \"%s\", setting "
482 "quirks (%#x).\n",
483 ✗ vaapi_driver_quirks_table[i].friendly_name,
484 ✗ vaapi_driver_quirks_table[i].quirks);
485 ✗ hwctx->driver_quirks |=
486 ✗ vaapi_driver_quirks_table[i].quirks;
487 ✗ break;
488 }
489 }
490 ✗ if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
491 ✗ av_log(hwdev, AV_LOG_VERBOSE, "Driver not found in known "
492 "nonstandard list, using standard behaviour.\n");
493 }
494 } else {
495 ✗ av_log(hwdev, AV_LOG_VERBOSE, "Driver has no vendor string, "
496 "assuming standard behaviour.\n");
497 }
498 }
499
500 ✗ av_free(image_list);
501 ✗ return 0;
502 ✗ fail:
503 ✗ av_freep(&ctx->formats);
504 ✗ av_free(image_list);
505 ✗ return err;
506 }
507
508 ✗ static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
509 {
510 ✗ VAAPIDeviceContext *ctx = hwdev->hwctx;
511
512 ✗ av_freep(&ctx->formats);
513 ✗ }
514
515 ✗ static void vaapi_buffer_free(void *opaque, uint8_t *data)
516 {
517 ✗ AVHWFramesContext *hwfc = opaque;
518 ✗ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
519 VASurfaceID surface_id;
520 VAStatus vas;
521
522 ✗ surface_id = (VASurfaceID)(uintptr_t)data;
523
524 ✗ vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
525 ✗ if (vas != VA_STATUS_SUCCESS) {
526 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
527 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
528 }
529 ✗ }
530
531 ✗ static AVBufferRef *vaapi_pool_alloc(void *opaque, size_t size)
532 {
533 ✗ AVHWFramesContext *hwfc = opaque;
534 ✗ VAAPIFramesContext *ctx = hwfc->hwctx;
535 ✗ AVVAAPIFramesContext *avfc = &ctx->p;
536 ✗ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
537 VASurfaceID surface_id;
538 VAStatus vas;
539 AVBufferRef *ref;
540
541 ✗ if (hwfc->initial_pool_size > 0 &&
542 ✗ avfc->nb_surfaces >= hwfc->initial_pool_size)
543 ✗ return NULL;
544
545 ✗ vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
546 ✗ hwfc->width, hwfc->height,
547 &surface_id, 1,
548 ✗ ctx->attributes, ctx->nb_attributes);
549 ✗ if (vas != VA_STATUS_SUCCESS) {
550 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
551 "%d (%s).\n", vas, vaErrorStr(vas));
552 ✗ return NULL;
553 }
554 ✗ av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
555
556 ✗ ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
557 sizeof(surface_id), &vaapi_buffer_free,
558 hwfc, AV_BUFFER_FLAG_READONLY);
559 ✗ if (!ref) {
560 ✗ vaDestroySurfaces(hwctx->display, &surface_id, 1);
561 ✗ return NULL;
562 }
563
564 ✗ if (hwfc->initial_pool_size > 0) {
565 // This is a fixed-size pool, so we must still be in the initial
566 // allocation sequence.
567 ✗ av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
568 ✗ avfc->surface_ids[avfc->nb_surfaces] = surface_id;
569 ✗ ++avfc->nb_surfaces;
570 }
571
572 ✗ return ref;
573 }
574
575 ✗ static int vaapi_frames_init(AVHWFramesContext *hwfc)
576 {
577 ✗ VAAPIFramesContext *ctx = hwfc->hwctx;
578 ✗ AVVAAPIFramesContext *avfc = &ctx->p;
579 ✗ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
580 ✗ const VAAPIFormatDescriptor *desc = NULL;
581 ✗ VAImageFormat *expected_format = NULL;
582 ✗ AVBufferRef *test_surface = NULL;
583 VASurfaceID test_surface_id;
584 VAImage test_image;
585 VAStatus vas;
586 int err, i;
587
588 ✗ err = vaapi_get_img_desc_and_format(hwfc->device_ctx, hwfc->sw_format,
589 &desc, &expected_format);
590 ✗ if (err < 0) {
591 // Use a relaxed check when pool exist. It can be an external pool.
592 ✗ if (!hwfc->pool || !vaapi_format_from_pix_fmt(hwfc->sw_format, NULL)) {
593 ✗ av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
594 av_get_pix_fmt_name(hwfc->sw_format));
595 ✗ return AVERROR(EINVAL);
596 }
597 }
598
599 ✗ if (!hwfc->pool) {
600 ✗ if (!(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
601 ✗ int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
602 ✗ int need_pixel_format = 1;
603 ✗ for (i = 0; i < avfc->nb_attributes; i++) {
604 ✗ if (avfc->attributes[i].type == VASurfaceAttribMemoryType)
605 ✗ need_memory_type = 0;
606 ✗ if (avfc->attributes[i].type == VASurfaceAttribPixelFormat)
607 ✗ need_pixel_format = 0;
608 }
609 ✗ ctx->nb_attributes =
610 ✗ avfc->nb_attributes + need_memory_type + need_pixel_format;
611
612 ✗ ctx->attributes = av_malloc(ctx->nb_attributes *
613 sizeof(*ctx->attributes));
614 ✗ if (!ctx->attributes) {
615 ✗ err = AVERROR(ENOMEM);
616 ✗ goto fail;
617 }
618
619 ✗ for (i = 0; i < avfc->nb_attributes; i++)
620 ✗ ctx->attributes[i] = avfc->attributes[i];
621 ✗ if (need_memory_type) {
622 ✗ ctx->attributes[i++] = (VASurfaceAttrib) {
623 .type = VASurfaceAttribMemoryType,
624 .flags = VA_SURFACE_ATTRIB_SETTABLE,
625 .value.type = VAGenericValueTypeInteger,
626 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
627 };
628 }
629 ✗ if (need_pixel_format) {
630 ✗ ctx->attributes[i++] = (VASurfaceAttrib) {
631 .type = VASurfaceAttribPixelFormat,
632 .flags = VA_SURFACE_ATTRIB_SETTABLE,
633 .value.type = VAGenericValueTypeInteger,
634 ✗ .value.value.i = desc->fourcc,
635 };
636 }
637 ✗ av_assert0(i == ctx->nb_attributes);
638 } else {
639 ✗ ctx->attributes = NULL;
640 ✗ ctx->nb_attributes = 0;
641 }
642
643 ✗ ctx->rt_format = desc->rt_format;
644
645 ✗ if (hwfc->initial_pool_size > 0) {
646 // This pool will be usable as a render target, so we need to store
647 // all of the surface IDs somewhere that vaCreateContext() calls
648 // will be able to access them.
649 ✗ avfc->nb_surfaces = 0;
650 ✗ avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
651 sizeof(*avfc->surface_ids));
652 ✗ if (!avfc->surface_ids) {
653 ✗ err = AVERROR(ENOMEM);
654 ✗ goto fail;
655 }
656 } else {
657 // This pool allows dynamic sizing, and will not be usable as a
658 // render target.
659 ✗ avfc->nb_surfaces = 0;
660 ✗ avfc->surface_ids = NULL;
661 }
662
663 ✗ ffhwframesctx(hwfc)->pool_internal =
664 ✗ av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
665 &vaapi_pool_alloc, NULL);
666 ✗ if (!ffhwframesctx(hwfc)->pool_internal) {
667 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
668 ✗ err = AVERROR(ENOMEM);
669 ✗ goto fail;
670 }
671 }
672
673 // Allocate a single surface to test whether vaDeriveImage() is going
674 // to work for the specific configuration.
675 ✗ if (hwfc->pool) {
676 ✗ test_surface = av_buffer_pool_get(hwfc->pool);
677 ✗ if (!test_surface) {
678 ✗ av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
679 "user-configured buffer pool.\n");
680 ✗ err = AVERROR(ENOMEM);
681 ✗ goto fail;
682 }
683 } else {
684 ✗ test_surface = av_buffer_pool_get(ffhwframesctx(hwfc)->pool_internal);
685 ✗ if (!test_surface) {
686 ✗ av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
687 "internal buffer pool.\n");
688 ✗ err = AVERROR(ENOMEM);
689 ✗ goto fail;
690 }
691 }
692 ✗ test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
693
694 ✗ ctx->derive_works = 0;
695 ✗ if (expected_format) {
696 ✗ vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
697 ✗ if (vas == VA_STATUS_SUCCESS) {
698 ✗ if (expected_format->fourcc == test_image.format.fourcc) {
699 ✗ av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
700 ✗ ctx->derive_works = 1;
701 } else {
702 ✗ av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
703 "derived image format %08x does not match "
704 "expected format %08x.\n",
705 ✗ expected_format->fourcc, test_image.format.fourcc);
706 }
707 ✗ vaDestroyImage(hwctx->display, test_image.image_id);
708 } else {
709 ✗ av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
710 "deriving image does not work: "
711 "%d (%s).\n", vas, vaErrorStr(vas));
712 }
713 } else {
714 ✗ av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
715 "image format is not supported.\n");
716 }
717
718 ✗ av_buffer_unref(&test_surface);
719 ✗ return 0;
720
721 ✗ fail:
722 ✗ av_buffer_unref(&test_surface);
723 ✗ av_freep(&avfc->surface_ids);
724 ✗ av_freep(&ctx->attributes);
725 ✗ return err;
726 }
727
728 ✗ static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
729 {
730 ✗ VAAPIFramesContext *ctx = hwfc->hwctx;
731 ✗ AVVAAPIFramesContext *avfc = &ctx->p;
732
733 ✗ av_freep(&avfc->surface_ids);
734 ✗ av_freep(&ctx->attributes);
735 ✗ }
736
737 ✗ static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
738 {
739 ✗ frame->buf[0] = av_buffer_pool_get(hwfc->pool);
740 ✗ if (!frame->buf[0])
741 ✗ return AVERROR(ENOMEM);
742
743 ✗ frame->data[3] = frame->buf[0]->data;
744 ✗ frame->format = AV_PIX_FMT_VAAPI;
745 ✗ frame->width = hwfc->width;
746 ✗ frame->height = hwfc->height;
747
748 ✗ return 0;
749 }
750
751 ✗ static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
752 enum AVHWFrameTransferDirection dir,
753 enum AVPixelFormat **formats)
754 {
755 ✗ VAAPIDeviceContext *ctx = hwfc->device_ctx->hwctx;
756 enum AVPixelFormat *pix_fmts;
757 int i, k, sw_format_available;
758
759 ✗ sw_format_available = 0;
760 ✗ for (i = 0; i < ctx->nb_formats; i++) {
761 ✗ if (ctx->formats[i].pix_fmt == hwfc->sw_format)
762 ✗ sw_format_available = 1;
763 }
764
765 ✗ pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
766 ✗ if (!pix_fmts)
767 ✗ return AVERROR(ENOMEM);
768
769 ✗ if (sw_format_available) {
770 ✗ pix_fmts[0] = hwfc->sw_format;
771 ✗ k = 1;
772 } else {
773 ✗ k = 0;
774 }
775 ✗ for (i = 0; i < ctx->nb_formats; i++) {
776 ✗ if (ctx->formats[i].pix_fmt == hwfc->sw_format)
777 ✗ continue;
778 ✗ av_assert0(k < ctx->nb_formats);
779 ✗ pix_fmts[k++] = ctx->formats[i].pix_fmt;
780 }
781 ✗ pix_fmts[k] = AV_PIX_FMT_NONE;
782
783 ✗ *formats = pix_fmts;
784 ✗ return 0;
785 }
786
787 ✗ static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
788 HWMapDescriptor *hwmap)
789 {
790 ✗ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
791 ✗ VAAPIMapping *map = hwmap->priv;
792 VASurfaceID surface_id;
793 VAStatus vas;
794
795 ✗ surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
796 ✗ av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
797
798 ✗ vas = vaUnmapBuffer(hwctx->display, map->image.buf);
799 ✗ if (vas != VA_STATUS_SUCCESS) {
800 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
801 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
802 }
803
804 ✗ if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
805 ✗ !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
806 ✗ vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
807 ✗ 0, 0, hwfc->width, hwfc->height,
808 ✗ 0, 0, hwfc->width, hwfc->height);
809 ✗ if (vas != VA_STATUS_SUCCESS) {
810 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
811 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
812 }
813 }
814
815 ✗ vas = vaDestroyImage(hwctx->display, map->image.image_id);
816 ✗ if (vas != VA_STATUS_SUCCESS) {
817 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
818 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
819 }
820
821 ✗ av_free(map);
822 ✗ }
823
824 ✗ static int vaapi_map_frame(AVHWFramesContext *hwfc,
825 AVFrame *dst, const AVFrame *src, int flags)
826 {
827 ✗ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
828 ✗ VAAPIFramesContext *ctx = hwfc->hwctx;
829 VASurfaceID surface_id;
830 const VAAPIFormatDescriptor *desc;
831 VAImageFormat *image_format;
832 VAAPIMapping *map;
833 VAStatus vas;
834 ✗ void *address = NULL;
835 int err, i;
836 #if VA_CHECK_VERSION(1, 21, 0)
837 ✗ uint32_t vaflags = 0;
838 #endif
839
840 ✗ surface_id = (VASurfaceID)(uintptr_t)src->data[3];
841 ✗ av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
842
843 ✗ if (!ctx->derive_works && (flags & AV_HWFRAME_MAP_DIRECT)) {
844 // Requested direct mapping but it is not possible.
845 ✗ return AVERROR(EINVAL);
846 }
847 ✗ if (dst->format == AV_PIX_FMT_NONE)
848 ✗ dst->format = hwfc->sw_format;
849 ✗ if (dst->format != hwfc->sw_format && (flags & AV_HWFRAME_MAP_DIRECT)) {
850 // Requested direct mapping but the formats do not match.
851 ✗ return AVERROR(EINVAL);
852 }
853
854 ✗ err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
855 ✗ if (err < 0) {
856 // Requested format is not a valid output format.
857 ✗ return err;
858 }
859
860 ✗ map = av_malloc(sizeof(*map));
861 ✗ if (!map)
862 ✗ return AVERROR(ENOMEM);
863 ✗ map->flags = flags;
864 ✗ map->image.image_id = VA_INVALID_ID;
865
866 ✗ vas = vaSyncSurface(hwctx->display, surface_id);
867 ✗ if (vas != VA_STATUS_SUCCESS) {
868 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
869 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
870 ✗ err = AVERROR(EIO);
871 ✗ goto fail;
872 }
873
874 // The memory which we map using derive need not be connected to the CPU
875 // in a way conducive to fast access. On Gen7-Gen9 Intel graphics, the
876 // memory is mappable but not cached, so normal memcpy()-like access is
877 // very slow to read it (but writing is ok). It is possible to read much
878 // faster with a copy routine which is aware of the limitation, but we
879 // assume for now that the user is not aware of that and would therefore
880 // prefer not to be given direct-mapped memory if they request read access.
881 ✗ if (ctx->derive_works && dst->format == hwfc->sw_format &&
882 ✗ ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
883 ✗ vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
884 ✗ if (vas != VA_STATUS_SUCCESS) {
885 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
886 "surface %#x: %d (%s).\n",
887 surface_id, vas, vaErrorStr(vas));
888 ✗ err = AVERROR(EIO);
889 ✗ goto fail;
890 }
891 ✗ if (map->image.format.fourcc != image_format->fourcc) {
892 ✗ av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
893 "is in wrong format: expected %#08x, got %#08x.\n",
894 ✗ surface_id, image_format->fourcc, map->image.format.fourcc);
895 ✗ err = AVERROR(EIO);
896 ✗ goto fail;
897 }
898 ✗ map->flags |= AV_HWFRAME_MAP_DIRECT;
899 } else {
900 ✗ vas = vaCreateImage(hwctx->display, image_format,
901 hwfc->width, hwfc->height, &map->image);
902 ✗ if (vas != VA_STATUS_SUCCESS) {
903 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
904 "surface %#x: %d (%s).\n",
905 surface_id, vas, vaErrorStr(vas));
906 ✗ err = AVERROR(EIO);
907 ✗ goto fail;
908 }
909 ✗ if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
910 ✗ vas = vaGetImage(hwctx->display, surface_id, 0, 0,
911 ✗ hwfc->width, hwfc->height, map->image.image_id);
912 ✗ if (vas != VA_STATUS_SUCCESS) {
913 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
914 "surface %#x: %d (%s).\n",
915 surface_id, vas, vaErrorStr(vas));
916 ✗ err = AVERROR(EIO);
917 ✗ goto fail;
918 }
919 }
920 }
921
922 #if VA_CHECK_VERSION(1, 21, 0)
923 ✗ if (flags & AV_HWFRAME_MAP_READ)
924 ✗ vaflags |= VA_MAPBUFFER_FLAG_READ;
925 ✗ if (flags & AV_HWFRAME_MAP_WRITE)
926 ✗ vaflags |= VA_MAPBUFFER_FLAG_WRITE;
927 // On drivers not implementing vaMapBuffer2 libva calls vaMapBuffer instead.
928 ✗ vas = vaMapBuffer2(hwctx->display, map->image.buf, &address, vaflags);
929 #else
930 vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
931 #endif
932 ✗ if (vas != VA_STATUS_SUCCESS) {
933 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
934 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
935 ✗ err = AVERROR(EIO);
936 ✗ goto fail;
937 }
938
939 ✗ err = ff_hwframe_map_create(src->hw_frames_ctx,
940 dst, src, &vaapi_unmap_frame, map);
941 ✗ if (err < 0)
942 ✗ goto fail;
943
944 ✗ dst->width = src->width;
945 ✗ dst->height = src->height;
946
947 ✗ for (i = 0; i < map->image.num_planes; i++) {
948 ✗ dst->data[i] = (uint8_t*)address + map->image.offsets[i];
949 ✗ dst->linesize[i] = map->image.pitches[i];
950 }
951
952 ✗ desc = vaapi_format_from_fourcc(map->image.format.fourcc);
953 ✗ if (desc && desc->chroma_planes_swapped) {
954 // Chroma planes are YVU rather than YUV, so swap them.
955 ✗ FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
956 }
957
958 ✗ return 0;
959
960 ✗ fail:
961 ✗ if (map) {
962 ✗ if (address)
963 ✗ vaUnmapBuffer(hwctx->display, map->image.buf);
964 ✗ if (map->image.image_id != VA_INVALID_ID)
965 ✗ vaDestroyImage(hwctx->display, map->image.image_id);
966 ✗ av_free(map);
967 }
968 ✗ return err;
969 }
970
971 ✗ static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
972 AVFrame *dst, const AVFrame *src)
973 {
974 AVFrame *map;
975 int err;
976
977 ✗ if (dst->width > hwfc->width || dst->height > hwfc->height)
978 ✗ return AVERROR(EINVAL);
979
980 ✗ map = av_frame_alloc();
981 ✗ if (!map)
982 ✗ return AVERROR(ENOMEM);
983 ✗ map->format = dst->format;
984
985 ✗ err = vaapi_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
986 ✗ if (err)
987 ✗ goto fail;
988
989 ✗ map->width = dst->width;
990 ✗ map->height = dst->height;
991
992 ✗ err = av_frame_copy(dst, map);
993 ✗ if (err)
994 ✗ goto fail;
995
996 ✗ err = 0;
997 ✗ fail:
998 ✗ av_frame_free(&map);
999 ✗ return err;
1000 }
1001
1002 ✗ static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
1003 AVFrame *dst, const AVFrame *src)
1004 {
1005 AVFrame *map;
1006 int err;
1007
1008 ✗ if (src->width > hwfc->width || src->height > hwfc->height)
1009 ✗ return AVERROR(EINVAL);
1010
1011 ✗ map = av_frame_alloc();
1012 ✗ if (!map)
1013 ✗ return AVERROR(ENOMEM);
1014 ✗ map->format = src->format;
1015
1016 ✗ err = vaapi_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
1017 ✗ if (err)
1018 ✗ goto fail;
1019
1020 ✗ map->width = src->width;
1021 ✗ map->height = src->height;
1022
1023 ✗ err = av_frame_copy(map, src);
1024 ✗ if (err)
1025 ✗ goto fail;
1026
1027 ✗ err = 0;
1028 ✗ fail:
1029 ✗ av_frame_free(&map);
1030 ✗ return err;
1031 }
1032
1033 ✗ static int vaapi_map_to_memory(AVHWFramesContext *hwfc, AVFrame *dst,
1034 const AVFrame *src, int flags)
1035 {
1036 int err;
1037
1038 ✗ err = vaapi_map_frame(hwfc, dst, src, flags);
1039 ✗ if (err)
1040 ✗ return err;
1041
1042 ✗ err = av_frame_copy_props(dst, src);
1043 ✗ if (err)
1044 ✗ return err;
1045
1046 ✗ return 0;
1047 }
1048
1049 #if CONFIG_LIBDRM
1050
1051 #define DRM_MAP(va, layers, ...) { \
1052 VA_FOURCC_ ## va, \
1053 layers, \
1054 { __VA_ARGS__ } \
1055 }
1056 static const struct {
1057 uint32_t va_fourcc;
1058 int nb_layer_formats;
1059 uint32_t layer_formats[AV_DRM_MAX_PLANES];
1060 } vaapi_drm_format_map[] = {
1061 #ifdef DRM_FORMAT_R8
1062 DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88),
1063 DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_GR88),
1064 #endif
1065 DRM_MAP(NV12, 1, DRM_FORMAT_NV12),
1066 #if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16)
1067 DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
1068 #endif
1069 #if defined(VA_FOURCC_P012) && defined(DRM_FORMAT_R16)
1070 DRM_MAP(P012, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
1071 #endif
1072 DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888),
1073 DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888),
1074 DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888),
1075 DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888),
1076 #ifdef VA_FOURCC_ABGR
1077 DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888),
1078 DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888),
1079 #endif
1080 DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888),
1081 DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888),
1082 #if defined(VA_FOURCC_XYUV) && defined(DRM_FORMAT_XYUV8888)
1083 DRM_MAP(XYUV, 1, DRM_FORMAT_XYUV8888),
1084 #endif
1085 #if defined(VA_FOURCC_Y412) && defined(DRM_FORMAT_XVYU2101010)
1086 DRM_MAP(Y410, 1, DRM_FORMAT_XVYU2101010),
1087 #endif
1088 #if defined(VA_FOURCC_Y412) && defined(DRM_FORMAT_XVYU12_16161616)
1089 DRM_MAP(Y412, 1, DRM_FORMAT_XVYU12_16161616),
1090 #endif
1091 #if defined(VA_FOURCC_X2R10G10B10) && defined(DRM_FORMAT_XRGB2101010)
1092 DRM_MAP(X2R10G10B10, 1, DRM_FORMAT_XRGB2101010),
1093 #endif
1094 };
1095 #undef DRM_MAP
1096
1097 ✗ static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
1098 HWMapDescriptor *hwmap)
1099 {
1100 ✗ AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
1101
1102 ✗ VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
1103
1104 ✗ av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id);
1105
1106 ✗ vaDestroySurfaces(dst_dev->display, &surface_id, 1);
1107 ✗ }
1108
1109 ✗ static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
1110 const AVFrame *src, int flags)
1111 {
1112 #if VA_CHECK_VERSION(1, 1, 0)
1113 ✗ VAAPIFramesContext *src_vafc = src_fc->hwctx;
1114 int use_prime2;
1115 #else
1116 int k;
1117 #endif
1118 ✗ AVHWFramesContext *dst_fc =
1119 ✗ (AVHWFramesContext*)dst->hw_frames_ctx->data;
1120 ✗ AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
1121 const AVDRMFrameDescriptor *desc;
1122 const VAAPIFormatDescriptor *format_desc;
1123 VASurfaceID surface_id;
1124 ✗ VAStatus vas = VA_STATUS_SUCCESS;
1125 uint32_t va_fourcc;
1126 int err, i, j;
1127
1128 #if !VA_CHECK_VERSION(1, 1, 0)
1129 unsigned long buffer_handle;
1130 VASurfaceAttribExternalBuffers buffer_desc;
1131 VASurfaceAttrib attrs[2] = {
1132 {
1133 .type = VASurfaceAttribMemoryType,
1134 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1135 .value.type = VAGenericValueTypeInteger,
1136 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
1137 },
1138 {
1139 .type = VASurfaceAttribExternalBufferDescriptor,
1140 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1141 .value.type = VAGenericValueTypePointer,
1142 .value.value.p = &buffer_desc,
1143 }
1144 };
1145 #endif
1146
1147 ✗ desc = (AVDRMFrameDescriptor*)src->data[0];
1148
1149 ✗ if (desc->nb_objects != 1) {
1150 ✗ av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames "
1151 "made from a single DRM object.\n");
1152 ✗ return AVERROR(EINVAL);
1153 }
1154
1155 ✗ va_fourcc = 0;
1156 ✗ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1157 ✗ if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats)
1158 ✗ continue;
1159 ✗ for (j = 0; j < desc->nb_layers; j++) {
1160 ✗ if (desc->layers[j].format !=
1161 ✗ vaapi_drm_format_map[i].layer_formats[j])
1162 ✗ break;
1163 }
1164 ✗ if (j != desc->nb_layers)
1165 ✗ continue;
1166 ✗ va_fourcc = vaapi_drm_format_map[i].va_fourcc;
1167 ✗ break;
1168 }
1169 ✗ if (!va_fourcc) {
1170 ✗ av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported "
1171 "by VAAPI.\n");
1172 ✗ return AVERROR(EINVAL);
1173 }
1174
1175 ✗ av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
1176 ✗ "%08x.\n", desc->objects[0].fd, va_fourcc);
1177
1178 ✗ format_desc = vaapi_format_from_fourcc(va_fourcc);
1179 ✗ av_assert0(format_desc);
1180
1181 #if VA_CHECK_VERSION(1, 1, 0)
1182 ✗ use_prime2 = !src_vafc->prime_2_import_unsupported &&
1183 ✗ desc->objects[0].format_modifier != DRM_FORMAT_MOD_INVALID;
1184 ✗ if (use_prime2) {
1185 VADRMPRIMESurfaceDescriptor prime_desc;
1186 ✗ VASurfaceAttrib prime_attrs[2] = {
1187 {
1188 .type = VASurfaceAttribMemoryType,
1189 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1190 .value.type = VAGenericValueTypeInteger,
1191 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
1192 },
1193 {
1194 .type = VASurfaceAttribExternalBufferDescriptor,
1195 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1196 .value.type = VAGenericValueTypePointer,
1197 .value.value.p = &prime_desc,
1198 }
1199 };
1200 ✗ prime_desc.fourcc = va_fourcc;
1201 ✗ prime_desc.width = src_fc->width;
1202 ✗ prime_desc.height = src_fc->height;
1203 ✗ prime_desc.num_objects = desc->nb_objects;
1204 ✗ for (i = 0; i < desc->nb_objects; ++i) {
1205 ✗ prime_desc.objects[i].fd = desc->objects[i].fd;
1206 ✗ prime_desc.objects[i].size = desc->objects[i].size;
1207 ✗ prime_desc.objects[i].drm_format_modifier =
1208 ✗ desc->objects[i].format_modifier;
1209 }
1210
1211 ✗ prime_desc.num_layers = desc->nb_layers;
1212 ✗ for (i = 0; i < desc->nb_layers; ++i) {
1213 ✗ prime_desc.layers[i].drm_format = desc->layers[i].format;
1214 ✗ prime_desc.layers[i].num_planes = desc->layers[i].nb_planes;
1215 ✗ for (j = 0; j < desc->layers[i].nb_planes; ++j) {
1216 ✗ prime_desc.layers[i].object_index[j] =
1217 ✗ desc->layers[i].planes[j].object_index;
1218 ✗ prime_desc.layers[i].offset[j] = desc->layers[i].planes[j].offset;
1219 ✗ prime_desc.layers[i].pitch[j] = desc->layers[i].planes[j].pitch;
1220 }
1221
1222 ✗ if (format_desc->chroma_planes_swapped &&
1223 ✗ desc->layers[i].nb_planes == 3) {
1224 ✗ FFSWAP(uint32_t, prime_desc.layers[i].pitch[1],
1225 prime_desc.layers[i].pitch[2]);
1226 ✗ FFSWAP(uint32_t, prime_desc.layers[i].offset[1],
1227 prime_desc.layers[i].offset[2]);
1228 }
1229 }
1230
1231 /*
1232 * We can query for PRIME_2 support with vaQuerySurfaceAttributes, but that
1233 * that needs the config_id which we don't have here . Both Intel and
1234 * Gallium seem to do the correct error checks, so lets just try the
1235 * PRIME_2 import first.
1236 */
1237 ✗ vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
1238 ✗ src->width, src->height, &surface_id, 1,
1239 prime_attrs, FF_ARRAY_ELEMS(prime_attrs));
1240 ✗ if (vas != VA_STATUS_SUCCESS)
1241 ✗ src_vafc->prime_2_import_unsupported = 1;
1242 }
1243
1244 ✗ if (!use_prime2 || vas != VA_STATUS_SUCCESS) {
1245 int k;
1246 uintptr_t buffer_handle;
1247 VASurfaceAttribExternalBuffers buffer_desc;
1248 ✗ VASurfaceAttrib buffer_attrs[2] = {
1249 {
1250 .type = VASurfaceAttribMemoryType,
1251 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1252 .value.type = VAGenericValueTypeInteger,
1253 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
1254 },
1255 {
1256 .type = VASurfaceAttribExternalBufferDescriptor,
1257 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1258 .value.type = VAGenericValueTypePointer,
1259 .value.value.p = &buffer_desc,
1260 }
1261 };
1262
1263 ✗ buffer_handle = desc->objects[0].fd;
1264 ✗ buffer_desc.pixel_format = va_fourcc;
1265 ✗ buffer_desc.width = src_fc->width;
1266 ✗ buffer_desc.height = src_fc->height;
1267 ✗ buffer_desc.data_size = desc->objects[0].size;
1268 ✗ buffer_desc.buffers = &buffer_handle;
1269 ✗ buffer_desc.num_buffers = 1;
1270 ✗ buffer_desc.flags = 0;
1271
1272 ✗ k = 0;
1273 ✗ for (i = 0; i < desc->nb_layers; i++) {
1274 ✗ for (j = 0; j < desc->layers[i].nb_planes; j++) {
1275 ✗ buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
1276 ✗ buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
1277 ✗ ++k;
1278 }
1279 }
1280 ✗ buffer_desc.num_planes = k;
1281
1282 ✗ if (format_desc->chroma_planes_swapped &&
1283 ✗ buffer_desc.num_planes == 3) {
1284 ✗ FFSWAP(uint32_t, buffer_desc.pitches[1], buffer_desc.pitches[2]);
1285 ✗ FFSWAP(uint32_t, buffer_desc.offsets[1], buffer_desc.offsets[2]);
1286 }
1287
1288 ✗ vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
1289 ✗ src->width, src->height,
1290 &surface_id, 1,
1291 buffer_attrs, FF_ARRAY_ELEMS(buffer_attrs));
1292 }
1293 #else
1294 buffer_handle = desc->objects[0].fd;
1295 buffer_desc.pixel_format = va_fourcc;
1296 buffer_desc.width = src_fc->width;
1297 buffer_desc.height = src_fc->height;
1298 buffer_desc.data_size = desc->objects[0].size;
1299 buffer_desc.buffers = &buffer_handle;
1300 buffer_desc.num_buffers = 1;
1301 buffer_desc.flags = 0;
1302
1303 k = 0;
1304 for (i = 0; i < desc->nb_layers; i++) {
1305 for (j = 0; j < desc->layers[i].nb_planes; j++) {
1306 buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
1307 buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
1308 ++k;
1309 }
1310 }
1311 buffer_desc.num_planes = k;
1312
1313 if (format_desc->chroma_planes_swapped &&
1314 buffer_desc.num_planes == 3) {
1315 FFSWAP(uint32_t, buffer_desc.pitches[1], buffer_desc.pitches[2]);
1316 FFSWAP(uint32_t, buffer_desc.offsets[1], buffer_desc.offsets[2]);
1317 }
1318
1319 vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
1320 src->width, src->height,
1321 &surface_id, 1,
1322 attrs, FF_ARRAY_ELEMS(attrs));
1323 #endif
1324 ✗ if (vas != VA_STATUS_SUCCESS) {
1325 ✗ av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
1326 "object: %d (%s).\n", vas, vaErrorStr(vas));
1327 ✗ return AVERROR(EIO);
1328 }
1329 ✗ av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
1330
1331 ✗ err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
1332 &vaapi_unmap_from_drm,
1333 ✗ (void*)(uintptr_t)surface_id);
1334 ✗ if (err < 0)
1335 ✗ return err;
1336
1337 ✗ dst->width = src->width;
1338 ✗ dst->height = src->height;
1339 ✗ dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
1340
1341 ✗ av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
1342 ✗ "surface %#x.\n", desc->objects[0].fd, surface_id);
1343
1344 ✗ return 0;
1345 }
1346
1347 #if VA_CHECK_VERSION(1, 1, 0)
1348 ✗ static void vaapi_unmap_to_drm_esh(AVHWFramesContext *hwfc,
1349 HWMapDescriptor *hwmap)
1350 {
1351 ✗ AVDRMFrameDescriptor *drm_desc = hwmap->priv;
1352 int i;
1353
1354 ✗ for (i = 0; i < drm_desc->nb_objects; i++)
1355 ✗ close(drm_desc->objects[i].fd);
1356
1357 ✗ av_freep(&drm_desc);
1358 ✗ }
1359
1360 ✗ static int vaapi_map_to_drm_esh(AVHWFramesContext *hwfc, AVFrame *dst,
1361 const AVFrame *src, int flags)
1362 {
1363 ✗ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1364 VASurfaceID surface_id;
1365 VAStatus vas;
1366 VADRMPRIMESurfaceDescriptor va_desc;
1367 ✗ AVDRMFrameDescriptor *drm_desc = NULL;
1368 uint32_t export_flags;
1369 int err, i, j;
1370
1371 ✗ surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1372
1373 ✗ export_flags = VA_EXPORT_SURFACE_SEPARATE_LAYERS;
1374 ✗ if (flags & AV_HWFRAME_MAP_READ) {
1375 ✗ export_flags |= VA_EXPORT_SURFACE_READ_ONLY;
1376
1377 ✗ vas = vaSyncSurface(hwctx->display, surface_id);
1378 ✗ if (vas != VA_STATUS_SUCCESS) {
1379 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
1380 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
1381 ✗ return AVERROR(EIO);
1382 }
1383 }
1384
1385 ✗ if (flags & AV_HWFRAME_MAP_WRITE)
1386 ✗ export_flags |= VA_EXPORT_SURFACE_WRITE_ONLY;
1387
1388 ✗ vas = vaExportSurfaceHandle(hwctx->display, surface_id,
1389 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
1390 export_flags, &va_desc);
1391 ✗ if (vas != VA_STATUS_SUCCESS) {
1392 ✗ if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
1393 ✗ return AVERROR(ENOSYS);
1394 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
1395 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
1396 ✗ return AVERROR(EIO);
1397 }
1398
1399 ✗ drm_desc = av_mallocz(sizeof(*drm_desc));
1400 ✗ if (!drm_desc) {
1401 ✗ err = AVERROR(ENOMEM);
1402 ✗ goto fail;
1403 }
1404
1405 // By some bizarre coincidence, these structures are very similar...
1406 ✗ drm_desc->nb_objects = va_desc.num_objects;
1407 ✗ for (i = 0; i < va_desc.num_objects; i++) {
1408 ✗ drm_desc->objects[i].fd = va_desc.objects[i].fd;
1409 ✗ drm_desc->objects[i].size = va_desc.objects[i].size;
1410 ✗ drm_desc->objects[i].format_modifier =
1411 ✗ va_desc.objects[i].drm_format_modifier;
1412 }
1413 ✗ drm_desc->nb_layers = va_desc.num_layers;
1414 ✗ for (i = 0; i < va_desc.num_layers; i++) {
1415 ✗ drm_desc->layers[i].format = va_desc.layers[i].drm_format;
1416 ✗ drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes;
1417 ✗ for (j = 0; j < va_desc.layers[i].num_planes; j++) {
1418 ✗ drm_desc->layers[i].planes[j].object_index =
1419 ✗ va_desc.layers[i].object_index[j];
1420 ✗ drm_desc->layers[i].planes[j].offset =
1421 ✗ va_desc.layers[i].offset[j];
1422 ✗ drm_desc->layers[i].planes[j].pitch =
1423 ✗ va_desc.layers[i].pitch[j];
1424 }
1425 }
1426
1427 ✗ err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
1428 &vaapi_unmap_to_drm_esh, drm_desc);
1429 ✗ if (err < 0)
1430 ✗ goto fail;
1431
1432 ✗ dst->width = src->width;
1433 ✗ dst->height = src->height;
1434 ✗ dst->data[0] = (uint8_t*)drm_desc;
1435
1436 ✗ return 0;
1437
1438 ✗ fail:
1439 ✗ for (i = 0; i < va_desc.num_objects; i++)
1440 ✗ close(va_desc.objects[i].fd);
1441 ✗ av_freep(&drm_desc);
1442 ✗ return err;
1443 }
1444 #endif
1445
1446 #if VA_CHECK_VERSION(0, 36, 0)
1447 typedef struct VAAPIDRMImageBufferMapping {
1448 VAImage image;
1449 VABufferInfo buffer_info;
1450
1451 AVDRMFrameDescriptor drm_desc;
1452 } VAAPIDRMImageBufferMapping;
1453
1454 ✗ static void vaapi_unmap_to_drm_abh(AVHWFramesContext *hwfc,
1455 HWMapDescriptor *hwmap)
1456 {
1457 ✗ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1458 ✗ VAAPIDRMImageBufferMapping *mapping = hwmap->priv;
1459 VASurfaceID surface_id;
1460 VAStatus vas;
1461
1462 ✗ surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
1463 ✗ av_log(hwfc, AV_LOG_DEBUG, "Unmap VAAPI surface %#x from DRM.\n",
1464 surface_id);
1465
1466 // DRM PRIME file descriptors are closed by vaReleaseBufferHandle(),
1467 // so we shouldn't close them separately.
1468
1469 ✗ vas = vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1470 ✗ if (vas != VA_STATUS_SUCCESS) {
1471 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to release buffer "
1472 "handle of image %#x (derived from surface %#x): "
1473 "%d (%s).\n", mapping->image.buf, surface_id,
1474 vas, vaErrorStr(vas));
1475 }
1476
1477 ✗ vas = vaDestroyImage(hwctx->display, mapping->image.image_id);
1478 ✗ if (vas != VA_STATUS_SUCCESS) {
1479 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image "
1480 "derived from surface %#x: %d (%s).\n",
1481 surface_id, vas, vaErrorStr(vas));
1482 }
1483
1484 ✗ av_free(mapping);
1485 ✗ }
1486
1487 ✗ static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst,
1488 const AVFrame *src, int flags)
1489 {
1490 ✗ AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1491 ✗ VAAPIDRMImageBufferMapping *mapping = NULL;
1492 VASurfaceID surface_id;
1493 VAStatus vas;
1494 int err, i, p;
1495
1496 ✗ surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1497 ✗ av_log(hwfc, AV_LOG_DEBUG, "Map VAAPI surface %#x to DRM.\n",
1498 surface_id);
1499
1500 ✗ mapping = av_mallocz(sizeof(*mapping));
1501 ✗ if (!mapping)
1502 ✗ return AVERROR(ENOMEM);
1503
1504 ✗ vas = vaDeriveImage(hwctx->display, surface_id,
1505 ✗ &mapping->image);
1506 ✗ if (vas != VA_STATUS_SUCCESS) {
1507 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
1508 "surface %#x: %d (%s).\n",
1509 surface_id, vas, vaErrorStr(vas));
1510 ✗ err = AVERROR(EIO);
1511 ✗ goto fail;
1512 }
1513
1514 ✗ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1515 ✗ if (vaapi_drm_format_map[i].va_fourcc ==
1516 ✗ mapping->image.format.fourcc)
1517 ✗ break;
1518 }
1519 ✗ if (i >= FF_ARRAY_ELEMS(vaapi_drm_format_map)) {
1520 ✗ av_log(hwfc, AV_LOG_ERROR, "No matching DRM format for "
1521 ✗ "VAAPI format %#x.\n", mapping->image.format.fourcc);
1522 ✗ err = AVERROR(EINVAL);
1523 ✗ goto fail_derived;
1524 }
1525
1526 ✗ mapping->buffer_info.mem_type =
1527 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
1528
1529 ✗ mapping->drm_desc.nb_layers =
1530 ✗ vaapi_drm_format_map[i].nb_layer_formats;
1531 ✗ if (mapping->drm_desc.nb_layers > 1) {
1532 ✗ if (mapping->drm_desc.nb_layers != mapping->image.num_planes) {
1533 ✗ av_log(hwfc, AV_LOG_ERROR, "Image properties do not match "
1534 "expected format: got %d planes, but expected %d.\n",
1535 ✗ mapping->image.num_planes, mapping->drm_desc.nb_layers);
1536 ✗ err = AVERROR(EINVAL);
1537 ✗ goto fail_derived;
1538 }
1539
1540 ✗ for(p = 0; p < mapping->drm_desc.nb_layers; p++) {
1541 ✗ mapping->drm_desc.layers[p] = (AVDRMLayerDescriptor) {
1542 ✗ .format = vaapi_drm_format_map[i].layer_formats[p],
1543 .nb_planes = 1,
1544 .planes[0] = {
1545 .object_index = 0,
1546 ✗ .offset = mapping->image.offsets[p],
1547 ✗ .pitch = mapping->image.pitches[p],
1548 },
1549 };
1550 }
1551 } else {
1552 ✗ mapping->drm_desc.layers[0].format =
1553 ✗ vaapi_drm_format_map[i].layer_formats[0];
1554 ✗ mapping->drm_desc.layers[0].nb_planes = mapping->image.num_planes;
1555 ✗ for (p = 0; p < mapping->image.num_planes; p++) {
1556 ✗ mapping->drm_desc.layers[0].planes[p] = (AVDRMPlaneDescriptor) {
1557 .object_index = 0,
1558 ✗ .offset = mapping->image.offsets[p],
1559 ✗ .pitch = mapping->image.pitches[p],
1560 };
1561 }
1562 }
1563
1564 ✗ vas = vaAcquireBufferHandle(hwctx->display, mapping->image.buf,
1565 ✗ &mapping->buffer_info);
1566 ✗ if (vas != VA_STATUS_SUCCESS) {
1567 ✗ av_log(hwfc, AV_LOG_ERROR, "Failed to get buffer "
1568 "handle from image %#x (derived from surface %#x): "
1569 ✗ "%d (%s).\n", mapping->image.buf, surface_id,
1570 vas, vaErrorStr(vas));
1571 ✗ err = AVERROR(EIO);
1572 ✗ goto fail_derived;
1573 }
1574
1575 ✗ av_log(hwfc, AV_LOG_DEBUG, "DRM PRIME fd is %"PRIdPTR".\n",
1576 ✗ mapping->buffer_info.handle);
1577
1578 ✗ mapping->drm_desc.nb_objects = 1;
1579 ✗ mapping->drm_desc.objects[0] = (AVDRMObjectDescriptor) {
1580 ✗ .fd = mapping->buffer_info.handle,
1581 ✗ .size = mapping->image.data_size,
1582 // There is no way to get the format modifier with this API.
1583 .format_modifier = DRM_FORMAT_MOD_INVALID,
1584 };
1585
1586 ✗ err = ff_hwframe_map_create(src->hw_frames_ctx,
1587 dst, src, &vaapi_unmap_to_drm_abh,
1588 mapping);
1589 ✗ if (err < 0)
1590 ✗ goto fail_mapped;
1591
1592 ✗ dst->data[0] = (uint8_t*)&mapping->drm_desc;
1593 ✗ dst->width = src->width;
1594 ✗ dst->height = src->height;
1595
1596 ✗ return 0;
1597
1598 ✗ fail_mapped:
1599 ✗ vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1600 ✗ fail_derived:
1601 ✗ vaDestroyImage(hwctx->display, mapping->image.image_id);
1602 ✗ fail:
1603 ✗ av_freep(&mapping);
1604 ✗ return err;
1605 }
1606 #endif
1607
1608 ✗ static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
1609 const AVFrame *src, int flags)
1610 {
1611 #if VA_CHECK_VERSION(1, 1, 0)
1612 int err;
1613 ✗ err = vaapi_map_to_drm_esh(hwfc, dst, src, flags);
1614 ✗ if (err != AVERROR(ENOSYS))
1615 ✗ return err;
1616 #endif
1617 #if VA_CHECK_VERSION(0, 36, 0)
1618 ✗ return vaapi_map_to_drm_abh(hwfc, dst, src, flags);
1619 #endif
1620 return AVERROR(ENOSYS);
1621 }
1622
1623 #endif /* CONFIG_LIBDRM */
1624
1625 ✗ static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
1626 const AVFrame *src, int flags)
1627 {
1628 ✗ switch (src->format) {
1629 #if CONFIG_LIBDRM
1630 ✗ case AV_PIX_FMT_DRM_PRIME:
1631 ✗ return vaapi_map_from_drm(hwfc, dst, src, flags);
1632 #endif
1633 ✗ default:
1634 ✗ return AVERROR(ENOSYS);
1635 }
1636 }
1637
1638 ✗ static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
1639 const AVFrame *src, int flags)
1640 {
1641 ✗ switch (dst->format) {
1642 #if CONFIG_LIBDRM
1643 ✗ case AV_PIX_FMT_DRM_PRIME:
1644 ✗ return vaapi_map_to_drm(hwfc, dst, src, flags);
1645 #endif
1646 ✗ default:
1647 ✗ return vaapi_map_to_memory(hwfc, dst, src, flags);
1648 }
1649 }
1650
1651 ✗ static void vaapi_device_free(AVHWDeviceContext *ctx)
1652 {
1653 ✗ AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1654 ✗ VAAPIDevicePriv *priv = ctx->user_opaque;
1655
1656 ✗ if (hwctx->display)
1657 ✗ vaTerminate(hwctx->display);
1658
1659 #if HAVE_VAAPI_X11
1660 ✗ if (priv->x11_display)
1661 ✗ XCloseDisplay(priv->x11_display);
1662 #endif
1663
1664 #if HAVE_VAAPI_DRM
1665 ✗ if (priv->drm_fd >= 0)
1666 ✗ close(priv->drm_fd);
1667 #endif
1668
1669 ✗ av_freep(&priv);
1670 ✗ }
1671
1672 #if CONFIG_VAAPI_1
1673 ✗ static void vaapi_device_log_error(void *context, const char *message)
1674 {
1675 ✗ AVHWDeviceContext *ctx = context;
1676
1677 ✗ av_log(ctx, AV_LOG_ERROR, "libva: %s", message);
1678 ✗ }
1679
1680 ✗ static void vaapi_device_log_info(void *context, const char *message)
1681 {
1682 ✗ AVHWDeviceContext *ctx = context;
1683
1684 ✗ av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message);
1685 ✗ }
1686 #endif
1687
1688 ✗ static int vaapi_device_connect(AVHWDeviceContext *ctx,
1689 VADisplay display)
1690 {
1691 ✗ AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1692 int major, minor;
1693 VAStatus vas;
1694
1695 #if CONFIG_VAAPI_1
1696 ✗ vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
1697 ✗ vaSetInfoCallback (display, &vaapi_device_log_info, ctx);
1698 #endif
1699
1700 ✗ hwctx->display = display;
1701
1702 ✗ vas = vaInitialize(display, &major, &minor);
1703 ✗ if (vas != VA_STATUS_SUCCESS) {
1704 ✗ av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
1705 "connection: %d (%s).\n", vas, vaErrorStr(vas));
1706 ✗ return AVERROR(EIO);
1707 }
1708 ✗ av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
1709 "version %d.%d\n", major, minor);
1710
1711 ✗ return 0;
1712 }
1713
1714 ✗ static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
1715 AVDictionary *opts, int flags)
1716 {
1717 VAAPIDevicePriv *priv;
1718 ✗ VADisplay display = NULL;
1719 const AVDictionaryEntry *ent;
1720 int try_drm, try_x11, try_win32, try_all av_unused;
1721
1722 ✗ priv = av_mallocz(sizeof(*priv));
1723 ✗ if (!priv)
1724 ✗ return AVERROR(ENOMEM);
1725
1726 ✗ priv->drm_fd = -1;
1727
1728 ✗ ctx->user_opaque = priv;
1729 ✗ ctx->free = vaapi_device_free;
1730
1731 ✗ ent = av_dict_get(opts, "connection_type", NULL, 0);
1732 ✗ if (ent) {
1733 ✗ try_all = try_drm = try_x11 = try_win32 = 0;
1734 ✗ if (!strcmp(ent->value, "drm")) {
1735 ✗ try_drm = 1;
1736 ✗ } else if (!strcmp(ent->value, "x11")) {
1737 ✗ try_x11 = 1;
1738 ✗ } else if (!strcmp(ent->value, "win32")) {
1739 ✗ try_win32 = 1;
1740 } else {
1741 ✗ av_log(ctx, AV_LOG_ERROR, "Invalid connection type %s.\n",
1742 ✗ ent->value);
1743 ✗ return AVERROR(EINVAL);
1744 }
1745 } else {
1746 ✗ try_all = 1;
1747 ✗ try_drm = HAVE_VAAPI_DRM;
1748 ✗ try_x11 = HAVE_VAAPI_X11;
1749 ✗ try_win32 = HAVE_VAAPI_WIN32;
1750 }
1751
1752 #if HAVE_VAAPI_DRM
1753 ✗ while (!display && try_drm) {
1754 // If the device is specified, try to open it as a DRM device node.
1755 // If not, look for a usable render node, possibly restricted to those
1756 // using a specified kernel driver.
1757 ✗ int loglevel = try_all ? AV_LOG_VERBOSE : AV_LOG_ERROR;
1758 ✗ if (device) {
1759 ✗ priv->drm_fd = open(device, O_RDWR);
1760 ✗ if (priv->drm_fd < 0) {
1761 ✗ av_log(ctx, loglevel, "Failed to open %s as "
1762 "DRM device node.\n", device);
1763 ✗ break;
1764 }
1765 } else {
1766 char path[64];
1767 ✗ int n, max_devices = 8;
1768 #if CONFIG_LIBDRM
1769 drmVersion *info;
1770 const AVDictionaryEntry *kernel_driver;
1771 const AVDictionaryEntry *vendor_id;
1772 ✗ kernel_driver = av_dict_get(opts, "kernel_driver", NULL, 0);
1773 ✗ vendor_id = av_dict_get(opts, "vendor_id", NULL, 0);
1774 #endif
1775 ✗ for (n = 0; n < max_devices; n++) {
1776 ✗ snprintf(path, sizeof(path),
1777 "/dev/dri/renderD%d", 128 + n);
1778 ✗ priv->drm_fd = open(path, O_RDWR);
1779 ✗ if (priv->drm_fd < 0) {
1780 ✗ if (errno == ENOENT) {
1781 ✗ if (n != max_devices - 1) {
1782 ✗ av_log(ctx, AV_LOG_VERBOSE,
1783 "No render device %s, try next device for "
1784 "DRM render node.\n", path);
1785 ✗ continue;
1786 }
1787
1788 ✗ av_log(ctx, AV_LOG_VERBOSE, "No available render device "
1789 "for DRM render node.\n");
1790 } else
1791 ✗ av_log(ctx, AV_LOG_VERBOSE, "Cannot open "
1792 "DRM render node for device %d.\n", n);
1793 ✗ break;
1794 }
1795 #if CONFIG_LIBDRM
1796 ✗ info = drmGetVersion(priv->drm_fd);
1797 ✗ if (!info) {
1798 ✗ av_log(ctx, AV_LOG_VERBOSE,
1799 "Failed to get DRM version for device %d.\n", n);
1800 ✗ close(priv->drm_fd);
1801 ✗ priv->drm_fd = -1;
1802 ✗ continue;
1803 }
1804 ✗ if (kernel_driver) {
1805 ✗ if (strcmp(kernel_driver->value, info->name)) {
1806 ✗ av_log(ctx, AV_LOG_VERBOSE, "Ignoring device %d "
1807 "with non-matching kernel driver (%s).\n",
1808 n, info->name);
1809 ✗ drmFreeVersion(info);
1810 ✗ close(priv->drm_fd);
1811 ✗ priv->drm_fd = -1;
1812 ✗ continue;
1813 }
1814 ✗ av_log(ctx, AV_LOG_VERBOSE, "Trying to use "
1815 "DRM render node for device %d, "
1816 "with matching kernel driver (%s).\n",
1817 n, info->name);
1818 ✗ drmFreeVersion(info);
1819 ✗ break;
1820 // drmGetVersion() ensures |info->name| is 0-terminated.
1821 ✗ } else if (!strcmp(info->name, "vgem")) {
1822 ✗ av_log(ctx, AV_LOG_VERBOSE,
1823 "Skipping vgem node for device %d.\n", n);
1824 ✗ drmFreeVersion(info);
1825 ✗ close(priv->drm_fd);
1826 ✗ priv->drm_fd = -1;
1827 ✗ continue;
1828 ✗ } else if (vendor_id) {
1829 drmDevicePtr device;
1830 char drm_vendor[8];
1831 ✗ if (drmGetDevice(priv->drm_fd, &device)) {
1832 ✗ av_log(ctx, AV_LOG_VERBOSE,
1833 "Failed to get DRM device info for device %d.\n", n);
1834 ✗ close(priv->drm_fd);
1835 ✗ priv->drm_fd = -1;
1836 ✗ drmFreeVersion(info);
1837 ✗ continue;
1838 }
1839
1840 ✗ snprintf(drm_vendor, sizeof(drm_vendor), "0x%x", device->deviceinfo.pci->vendor_id);
1841 ✗ if (strcmp(vendor_id->value, drm_vendor)) {
1842 ✗ av_log(ctx, AV_LOG_VERBOSE, "Ignoring device %d "
1843 "with non-matching vendor id (%s).\n",
1844 ✗ n, vendor_id->value);
1845 ✗ drmFreeDevice(&device);
1846 ✗ close(priv->drm_fd);
1847 ✗ priv->drm_fd = -1;
1848 ✗ drmFreeVersion(info);
1849 ✗ continue;
1850 }
1851 ✗ av_log(ctx, AV_LOG_VERBOSE, "Trying to use "
1852 "DRM render node for device %d, "
1853 "with matching vendor id (%s).\n",
1854 ✗ n, vendor_id->value);
1855 ✗ drmFreeDevice(&device);
1856 ✗ drmFreeVersion(info);
1857 ✗ break;
1858 }
1859 ✗ drmFreeVersion(info);
1860 #endif
1861 ✗ av_log(ctx, AV_LOG_VERBOSE, "Trying to use "
1862 "DRM render node for device %d.\n", n);
1863 ✗ break;
1864 }
1865 ✗ if (n >= max_devices)
1866 ✗ break;
1867 }
1868
1869 ✗ display = vaGetDisplayDRM(priv->drm_fd);
1870 ✗ if (!display) {
1871 ✗ av_log(ctx, AV_LOG_VERBOSE, "Cannot open a VA display "
1872 "from DRM device %s.\n", device);
1873 ✗ return AVERROR_EXTERNAL;
1874 }
1875 ✗ break;
1876 }
1877 #endif
1878
1879 #if HAVE_VAAPI_X11
1880 ✗ if (!display && try_x11) {
1881 // Try to open the device as an X11 display.
1882 ✗ priv->x11_display = XOpenDisplay(device);
1883 ✗ if (!priv->x11_display) {
1884 ✗ av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
1885 "%s.\n", XDisplayName(device));
1886 } else {
1887 ✗ display = vaGetDisplay(priv->x11_display);
1888 ✗ if (!display) {
1889 ✗ av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1890 "from X11 display %s.\n", XDisplayName(device));
1891 ✗ return AVERROR_UNKNOWN;
1892 }
1893
1894 ✗ av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1895 "X11 display %s.\n", XDisplayName(device));
1896 }
1897 }
1898 #endif
1899
1900 #if HAVE_VAAPI_WIN32
1901 if (!display && try_win32) {
1902 // Try to create a display from the specified device, if any.
1903 if (!device) {
1904 display = vaGetDisplayWin32(NULL);
1905 } else {
1906 IDXGIFactory2 *pDXGIFactory = NULL;
1907 IDXGIAdapter *pAdapter = NULL;
1908 #if !HAVE_UWP
1909 HANDLE dxgi = dlopen("dxgi.dll", 0);
1910 if (!dxgi) {
1911 av_log(ctx, AV_LOG_ERROR, "Failed to load dxgi.dll\n");
1912 return AVERROR_UNKNOWN;
1913 }
1914 PFN_CREATE_DXGI_FACTORY pfnCreateDXGIFactory =
1915 (PFN_CREATE_DXGI_FACTORY)dlsym(dxgi, "CreateDXGIFactory");
1916 if (!pfnCreateDXGIFactory) {
1917 av_log(ctx, AV_LOG_ERROR, "CreateDXGIFactory load failed\n");
1918 dlclose(dxgi);
1919 return AVERROR_UNKNOWN;
1920 }
1921 #else
1922 // In UWP (which lacks LoadLibrary), CreateDXGIFactory isn't
1923 // available, only CreateDXGIFactory1
1924 PFN_CREATE_DXGI_FACTORY pfnCreateDXGIFactory =
1925 (PFN_CREATE_DXGI_FACTORY)CreateDXGIFactory1;
1926 #endif
1927 if (SUCCEEDED(pfnCreateDXGIFactory(&IID_IDXGIFactory2,
1928 (void **)&pDXGIFactory))) {
1929 int adapter = atoi(device);
1930 if (SUCCEEDED(IDXGIFactory2_EnumAdapters(pDXGIFactory,
1931 adapter,
1932 &pAdapter))) {
1933 DXGI_ADAPTER_DESC desc;
1934 if (SUCCEEDED(IDXGIAdapter2_GetDesc(pAdapter, &desc))) {
1935 av_log(ctx, AV_LOG_INFO,
1936 "Using device %04x:%04x (%ls) - LUID %lu %ld.\n",
1937 desc.VendorId, desc.DeviceId, desc.Description,
1938 desc.AdapterLuid.LowPart,
1939 desc.AdapterLuid.HighPart);
1940 display = vaGetDisplayWin32(&desc.AdapterLuid);
1941 }
1942 IDXGIAdapter_Release(pAdapter);
1943 }
1944 IDXGIFactory2_Release(pDXGIFactory);
1945 }
1946 #if !HAVE_UWP
1947 dlclose(dxgi);
1948 #endif
1949 }
1950
1951 if (!display) {
1952 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1953 "from Win32 display.\n");
1954 return AVERROR_UNKNOWN;
1955 }
1956
1957 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1958 "Win32 display.\n");
1959 }
1960 #endif
1961
1962 ✗ if (!display) {
1963 ✗ if (device)
1964 ✗ av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1965 "device %s.\n", device);
1966 else
1967 ✗ av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1968 "any default device.\n");
1969 ✗ return AVERROR(EINVAL);
1970 }
1971
1972 ✗ ent = av_dict_get(opts, "driver", NULL, 0);
1973 ✗ if (ent) {
1974 #if VA_CHECK_VERSION(0, 38, 0)
1975 VAStatus vas;
1976 ✗ vas = vaSetDriverName(display, ent->value);
1977 ✗ if (vas != VA_STATUS_SUCCESS) {
1978 ✗ av_log(ctx, AV_LOG_ERROR, "Failed to set driver name to "
1979 ✗ "%s: %d (%s).\n", ent->value, vas, vaErrorStr(vas));
1980 ✗ vaTerminate(display);
1981 ✗ return AVERROR_EXTERNAL;
1982 }
1983 #else
1984 av_log(ctx, AV_LOG_WARNING, "Driver name setting is not "
1985 "supported with this VAAPI version.\n");
1986 #endif
1987 }
1988
1989 ✗ return vaapi_device_connect(ctx, display);
1990 }
1991
1992 ✗ static int vaapi_device_derive(AVHWDeviceContext *ctx,
1993 AVHWDeviceContext *src_ctx,
1994 AVDictionary *opts, int flags)
1995 {
1996 #if HAVE_VAAPI_DRM
1997 ✗ if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
1998 ✗ AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
1999 VADisplay *display;
2000 VAAPIDevicePriv *priv;
2001 int fd;
2002
2003 ✗ if (src_hwctx->fd < 0) {
2004 ✗ av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
2005 "device to derive a VA display from.\n");
2006 ✗ return AVERROR(EINVAL);
2007 }
2008
2009 #if CONFIG_LIBDRM
2010 {
2011 ✗ int node_type = drmGetNodeTypeFromFd(src_hwctx->fd);
2012 char *render_node;
2013 ✗ if (node_type < 0) {
2014 ✗ av_log(ctx, AV_LOG_ERROR, "DRM instance fd does not appear "
2015 "to refer to a DRM device.\n");
2016 ✗ return AVERROR(EINVAL);
2017 }
2018 ✗ if (node_type == DRM_NODE_RENDER) {
2019 ✗ fd = src_hwctx->fd;
2020 } else {
2021 ✗ render_node = drmGetRenderDeviceNameFromFd(src_hwctx->fd);
2022 ✗ if (!render_node) {
2023 ✗ av_log(ctx, AV_LOG_VERBOSE, "Using non-render node "
2024 "because the device does not have an "
2025 "associated render node.\n");
2026 ✗ fd = src_hwctx->fd;
2027 } else {
2028 ✗ fd = open(render_node, O_RDWR);
2029 ✗ if (fd < 0) {
2030 ✗ av_log(ctx, AV_LOG_VERBOSE, "Using non-render node "
2031 "because the associated render node "
2032 "could not be opened.\n");
2033 ✗ fd = src_hwctx->fd;
2034 } else {
2035 ✗ av_log(ctx, AV_LOG_VERBOSE, "Using render node %s "
2036 "in place of non-render DRM device.\n",
2037 render_node);
2038 }
2039 ✗ free(render_node);
2040 }
2041 }
2042 }
2043 #else
2044 fd = src_hwctx->fd;
2045 #endif
2046
2047 ✗ priv = av_mallocz(sizeof(*priv));
2048 ✗ if (!priv) {
2049 ✗ if (fd != src_hwctx->fd) {
2050 // The fd was opened in this function.
2051 ✗ close(fd);
2052 }
2053 ✗ return AVERROR(ENOMEM);
2054 }
2055
2056 ✗ if (fd == src_hwctx->fd) {
2057 // The fd is inherited from the source context and we are holding
2058 // a reference to that, we don't want to close it from here.
2059 ✗ priv->drm_fd = -1;
2060 } else {
2061 ✗ priv->drm_fd = fd;
2062 }
2063
2064 ✗ ctx->user_opaque = priv;
2065 ✗ ctx->free = &vaapi_device_free;
2066
2067 ✗ display = vaGetDisplayDRM(fd);
2068 ✗ if (!display) {
2069 ✗ av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
2070 "DRM device.\n");
2071 ✗ return AVERROR(EIO);
2072 }
2073
2074 ✗ return vaapi_device_connect(ctx, display);
2075 }
2076 #endif
2077 ✗ return AVERROR(ENOSYS);
2078 }
2079
2080 const HWContextType ff_hwcontext_type_vaapi = {
2081 .type = AV_HWDEVICE_TYPE_VAAPI,
2082 .name = "VAAPI",
2083
2084 .device_hwctx_size = sizeof(VAAPIDeviceContext),
2085 .device_hwconfig_size = sizeof(AVVAAPIHWConfig),
2086 .frames_hwctx_size = sizeof(VAAPIFramesContext),
2087
2088 .device_create = &vaapi_device_create,
2089 .device_derive = &vaapi_device_derive,
2090 .device_init = &vaapi_device_init,
2091 .device_uninit = &vaapi_device_uninit,
2092 .frames_get_constraints = &vaapi_frames_get_constraints,
2093 .frames_init = &vaapi_frames_init,
2094 .frames_uninit = &vaapi_frames_uninit,
2095 .frames_get_buffer = &vaapi_get_buffer,
2096 .transfer_get_formats = &vaapi_transfer_get_formats,
2097 .transfer_data_to = &vaapi_transfer_data_to,
2098 .transfer_data_from = &vaapi_transfer_data_from,
2099 .map_to = &vaapi_map_to,
2100 .map_from = &vaapi_map_from,
2101
2102 .pix_fmts = (const enum AVPixelFormat[]) {
2103 AV_PIX_FMT_VAAPI,
2104 AV_PIX_FMT_NONE
2105 },
2106 };
2107