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_X11
22 # include <va/va_x11.h>
23 #endif
24 #if HAVE_VAAPI_DRM
25 # include <va/va_drm.h>
26 #endif
27
28 #if CONFIG_LIBDRM
29 # include <va/va_drmcommon.h>
30 # include <drm_fourcc.h>
31 # ifndef DRM_FORMAT_MOD_INVALID
32 # define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
33 # endif
34 #endif
35
36 #include <fcntl.h>
37 #if HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40
41
42 #include "avassert.h"
43 #include "buffer.h"
44 #include "common.h"
45 #include "hwcontext.h"
46 #include "hwcontext_drm.h"
47 #include "hwcontext_internal.h"
48 #include "hwcontext_vaapi.h"
49 #include "mem.h"
50 #include "pixdesc.h"
51 #include "pixfmt.h"
52
53
54 typedef struct VAAPIDevicePriv {
55 #if HAVE_VAAPI_X11
56 Display *x11_display;
57 #endif
58
59 int drm_fd;
60 } VAAPIDevicePriv;
61
62 typedef struct VAAPISurfaceFormat {
63 enum AVPixelFormat pix_fmt;
64 VAImageFormat image_format;
65 } VAAPISurfaceFormat;
66
67 typedef struct VAAPIDeviceContext {
68 // Surface formats which can be used with this device.
69 VAAPISurfaceFormat *formats;
70 int nb_formats;
71 } VAAPIDeviceContext;
72
73 typedef struct VAAPIFramesContext {
74 // Surface attributes set at create time.
75 VASurfaceAttrib *attributes;
76 int nb_attributes;
77 // RT format of the underlying surface (Intel driver ignores this anyway).
78 unsigned int rt_format;
79 // Whether vaDeriveImage works.
80 int derive_works;
81 } VAAPIFramesContext;
82
83 typedef struct VAAPIMapping {
84 // Handle to the derived or copied image which is mapped.
85 VAImage image;
86 // The mapping flags actually used.
87 int flags;
88 } VAAPIMapping;
89
90 typedef struct VAAPIFormat {
91 unsigned int fourcc;
92 unsigned int rt_format;
93 enum AVPixelFormat pix_fmt;
94 int chroma_planes_swapped;
95 } VAAPIFormatDescriptor;
96
97 #define MAP(va, rt, av, swap_uv) { \
98 VA_FOURCC_ ## va, \
99 VA_RT_FORMAT_ ## rt, \
100 AV_PIX_FMT_ ## av, \
101 swap_uv, \
102 }
103 // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
104 // plane swap cases. The frame handling below tries to hide these.
105 static const VAAPIFormatDescriptor vaapi_format_map[] = {
106 MAP(NV12, YUV420, NV12, 0),
107 #ifdef VA_FOURCC_I420
108 MAP(I420, YUV420, YUV420P, 0),
109 #endif
110 MAP(YV12, YUV420, YUV420P, 1),
111 MAP(IYUV, YUV420, YUV420P, 0),
112 MAP(422H, YUV422, YUV422P, 0),
113 #ifdef VA_FOURCC_YV16
114 MAP(YV16, YUV422, YUV422P, 1),
115 #endif
116 MAP(UYVY, YUV422, UYVY422, 0),
117 MAP(YUY2, YUV422, YUYV422, 0),
118 MAP(411P, YUV411, YUV411P, 0),
119 MAP(422V, YUV422, YUV440P, 0),
120 MAP(444P, YUV444, YUV444P, 0),
121 MAP(Y800, YUV400, GRAY8, 0),
122 #ifdef VA_FOURCC_P010
123 MAP(P010, YUV420_10BPP, P010, 0),
124 #endif
125 MAP(BGRA, RGB32, BGRA, 0),
126 MAP(BGRX, RGB32, BGR0, 0),
127 MAP(RGBA, RGB32, RGBA, 0),
128 MAP(RGBX, RGB32, RGB0, 0),
129 #ifdef VA_FOURCC_ABGR
130 MAP(ABGR, RGB32, ABGR, 0),
131 MAP(XBGR, RGB32, 0BGR, 0),
132 #endif
133 MAP(ARGB, RGB32, ARGB, 0),
134 MAP(XRGB, RGB32, 0RGB, 0),
135 };
136 #undef MAP
137
138 static const VAAPIFormatDescriptor *
vaapi_format_from_fourcc(unsigned int fourcc)139 vaapi_format_from_fourcc(unsigned int fourcc)
140 {
141 int i;
142 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
143 if (vaapi_format_map[i].fourcc == fourcc)
144 return &vaapi_format_map[i];
145 return NULL;
146 }
147
148 static const VAAPIFormatDescriptor *
vaapi_format_from_pix_fmt(enum AVPixelFormat pix_fmt)149 vaapi_format_from_pix_fmt(enum AVPixelFormat pix_fmt)
150 {
151 int i;
152 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
153 if (vaapi_format_map[i].pix_fmt == pix_fmt)
154 return &vaapi_format_map[i];
155 return NULL;
156 }
157
vaapi_pix_fmt_from_fourcc(unsigned int fourcc)158 static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
159 {
160 const VAAPIFormatDescriptor *desc;
161 desc = vaapi_format_from_fourcc(fourcc);
162 if (desc)
163 return desc->pix_fmt;
164 else
165 return AV_PIX_FMT_NONE;
166 }
167
vaapi_get_image_format(AVHWDeviceContext * hwdev,enum AVPixelFormat pix_fmt,VAImageFormat ** image_format)168 static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
169 enum AVPixelFormat pix_fmt,
170 VAImageFormat **image_format)
171 {
172 VAAPIDeviceContext *ctx = hwdev->internal->priv;
173 int i;
174
175 for (i = 0; i < ctx->nb_formats; i++) {
176 if (ctx->formats[i].pix_fmt == pix_fmt) {
177 if (image_format)
178 *image_format = &ctx->formats[i].image_format;
179 return 0;
180 }
181 }
182 return AVERROR(EINVAL);
183 }
184
vaapi_frames_get_constraints(AVHWDeviceContext * hwdev,const void * hwconfig,AVHWFramesConstraints * constraints)185 static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
186 const void *hwconfig,
187 AVHWFramesConstraints *constraints)
188 {
189 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
190 const AVVAAPIHWConfig *config = hwconfig;
191 VAAPIDeviceContext *ctx = hwdev->internal->priv;
192 VASurfaceAttrib *attr_list = NULL;
193 VAStatus vas;
194 enum AVPixelFormat pix_fmt;
195 unsigned int fourcc;
196 int err, i, j, attr_count, pix_fmt_count;
197
198 if (config &&
199 !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
200 attr_count = 0;
201 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
202 0, &attr_count);
203 if (vas != VA_STATUS_SUCCESS) {
204 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
205 "%d (%s).\n", vas, vaErrorStr(vas));
206 err = AVERROR(ENOSYS);
207 goto fail;
208 }
209
210 attr_list = av_malloc(attr_count * sizeof(*attr_list));
211 if (!attr_list) {
212 err = AVERROR(ENOMEM);
213 goto fail;
214 }
215
216 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
217 attr_list, &attr_count);
218 if (vas != VA_STATUS_SUCCESS) {
219 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
220 "%d (%s).\n", vas, vaErrorStr(vas));
221 err = AVERROR(ENOSYS);
222 goto fail;
223 }
224
225 pix_fmt_count = 0;
226 for (i = 0; i < attr_count; i++) {
227 switch (attr_list[i].type) {
228 case VASurfaceAttribPixelFormat:
229 fourcc = attr_list[i].value.value.i;
230 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
231 if (pix_fmt != AV_PIX_FMT_NONE) {
232 ++pix_fmt_count;
233 } else {
234 // Something unsupported - ignore.
235 }
236 break;
237 case VASurfaceAttribMinWidth:
238 constraints->min_width = attr_list[i].value.value.i;
239 break;
240 case VASurfaceAttribMinHeight:
241 constraints->min_height = attr_list[i].value.value.i;
242 break;
243 case VASurfaceAttribMaxWidth:
244 constraints->max_width = attr_list[i].value.value.i;
245 break;
246 case VASurfaceAttribMaxHeight:
247 constraints->max_height = attr_list[i].value.value.i;
248 break;
249 }
250 }
251 if (pix_fmt_count == 0) {
252 // Nothing usable found. Presumably there exists something which
253 // works, so leave the set null to indicate unknown.
254 constraints->valid_sw_formats = NULL;
255 } else {
256 constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
257 sizeof(pix_fmt));
258 if (!constraints->valid_sw_formats) {
259 err = AVERROR(ENOMEM);
260 goto fail;
261 }
262
263 for (i = j = 0; i < attr_count; i++) {
264 if (attr_list[i].type != VASurfaceAttribPixelFormat)
265 continue;
266 fourcc = attr_list[i].value.value.i;
267 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
268 if (pix_fmt != AV_PIX_FMT_NONE)
269 constraints->valid_sw_formats[j++] = pix_fmt;
270 }
271 av_assert0(j == pix_fmt_count);
272 constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
273 }
274 } else {
275 // No configuration supplied.
276 // Return the full set of image formats known by the implementation.
277 constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
278 sizeof(pix_fmt));
279 if (!constraints->valid_sw_formats) {
280 err = AVERROR(ENOMEM);
281 goto fail;
282 }
283 for (i = 0; i < ctx->nb_formats; i++)
284 constraints->valid_sw_formats[i] = ctx->formats[i].pix_fmt;
285 constraints->valid_sw_formats[i] = AV_PIX_FMT_NONE;
286 }
287
288 constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
289 if (!constraints->valid_hw_formats) {
290 err = AVERROR(ENOMEM);
291 goto fail;
292 }
293 constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
294 constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
295
296 err = 0;
297 fail:
298 av_freep(&attr_list);
299 return err;
300 }
301
302 static const struct {
303 const char *friendly_name;
304 const char *match_string;
305 unsigned int quirks;
306 } vaapi_driver_quirks_table[] = {
307 #if !VA_CHECK_VERSION(1, 0, 0)
308 // The i965 driver did not conform before version 2.0.
309 {
310 "Intel i965 (Quick Sync)",
311 "i965",
312 AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
313 },
314 #endif
315 {
316 "Intel iHD",
317 "ubit",
318 AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
319 },
320 {
321 "VDPAU wrapper",
322 "Splitted-Desktop Systems VDPAU backend for VA-API",
323 AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES,
324 },
325 };
326
vaapi_device_init(AVHWDeviceContext * hwdev)327 static int vaapi_device_init(AVHWDeviceContext *hwdev)
328 {
329 VAAPIDeviceContext *ctx = hwdev->internal->priv;
330 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
331 VAImageFormat *image_list = NULL;
332 VAStatus vas;
333 const char *vendor_string;
334 int err, i, image_count;
335 enum AVPixelFormat pix_fmt;
336 unsigned int fourcc;
337
338 image_count = vaMaxNumImageFormats(hwctx->display);
339 if (image_count <= 0) {
340 err = AVERROR(EIO);
341 goto fail;
342 }
343 image_list = av_malloc(image_count * sizeof(*image_list));
344 if (!image_list) {
345 err = AVERROR(ENOMEM);
346 goto fail;
347 }
348 vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
349 if (vas != VA_STATUS_SUCCESS) {
350 err = AVERROR(EIO);
351 goto fail;
352 }
353
354 ctx->formats = av_malloc(image_count * sizeof(*ctx->formats));
355 if (!ctx->formats) {
356 err = AVERROR(ENOMEM);
357 goto fail;
358 }
359 ctx->nb_formats = 0;
360 for (i = 0; i < image_count; i++) {
361 fourcc = image_list[i].fourcc;
362 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
363 if (pix_fmt == AV_PIX_FMT_NONE) {
364 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
365 fourcc);
366 } else {
367 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
368 fourcc, av_get_pix_fmt_name(pix_fmt));
369 ctx->formats[ctx->nb_formats].pix_fmt = pix_fmt;
370 ctx->formats[ctx->nb_formats].image_format = image_list[i];
371 ++ctx->nb_formats;
372 }
373 }
374
375 vendor_string = vaQueryVendorString(hwctx->display);
376 if (vendor_string)
377 av_log(hwdev, AV_LOG_VERBOSE, "VAAPI driver: %s.\n", vendor_string);
378
379 if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
380 av_log(hwdev, AV_LOG_VERBOSE, "Using quirks set by user (%#x).\n",
381 hwctx->driver_quirks);
382 } else {
383 // Detect the driver in use and set quirk flags if necessary.
384 hwctx->driver_quirks = 0;
385 if (vendor_string) {
386 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
387 if (strstr(vendor_string,
388 vaapi_driver_quirks_table[i].match_string)) {
389 av_log(hwdev, AV_LOG_VERBOSE, "Matched driver string "
390 "as known nonstandard driver \"%s\", setting "
391 "quirks (%#x).\n",
392 vaapi_driver_quirks_table[i].friendly_name,
393 vaapi_driver_quirks_table[i].quirks);
394 hwctx->driver_quirks |=
395 vaapi_driver_quirks_table[i].quirks;
396 break;
397 }
398 }
399 if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
400 av_log(hwdev, AV_LOG_VERBOSE, "Driver not found in known "
401 "nonstandard list, using standard behaviour.\n");
402 }
403 } else {
404 av_log(hwdev, AV_LOG_VERBOSE, "Driver has no vendor string, "
405 "assuming standard behaviour.\n");
406 }
407 }
408
409 av_free(image_list);
410 return 0;
411 fail:
412 av_freep(&ctx->formats);
413 av_free(image_list);
414 return err;
415 }
416
vaapi_device_uninit(AVHWDeviceContext * hwdev)417 static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
418 {
419 VAAPIDeviceContext *ctx = hwdev->internal->priv;
420
421 av_freep(&ctx->formats);
422 }
423
vaapi_buffer_free(void * opaque,uint8_t * data)424 static void vaapi_buffer_free(void *opaque, uint8_t *data)
425 {
426 AVHWFramesContext *hwfc = opaque;
427 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
428 VASurfaceID surface_id;
429 VAStatus vas;
430
431 surface_id = (VASurfaceID)(uintptr_t)data;
432
433 vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
434 if (vas != VA_STATUS_SUCCESS) {
435 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
436 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
437 }
438 }
439
vaapi_pool_alloc(void * opaque,int size)440 static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
441 {
442 AVHWFramesContext *hwfc = opaque;
443 VAAPIFramesContext *ctx = hwfc->internal->priv;
444 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
445 AVVAAPIFramesContext *avfc = hwfc->hwctx;
446 VASurfaceID surface_id;
447 VAStatus vas;
448 AVBufferRef *ref;
449
450 if (hwfc->initial_pool_size > 0 &&
451 avfc->nb_surfaces >= hwfc->initial_pool_size)
452 return NULL;
453
454 vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
455 hwfc->width, hwfc->height,
456 &surface_id, 1,
457 ctx->attributes, ctx->nb_attributes);
458 if (vas != VA_STATUS_SUCCESS) {
459 av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
460 "%d (%s).\n", vas, vaErrorStr(vas));
461 return NULL;
462 }
463 av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
464
465 ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
466 sizeof(surface_id), &vaapi_buffer_free,
467 hwfc, AV_BUFFER_FLAG_READONLY);
468 if (!ref) {
469 vaDestroySurfaces(hwctx->display, &surface_id, 1);
470 return NULL;
471 }
472
473 if (hwfc->initial_pool_size > 0) {
474 // This is a fixed-size pool, so we must still be in the initial
475 // allocation sequence.
476 av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
477 avfc->surface_ids[avfc->nb_surfaces] = surface_id;
478 ++avfc->nb_surfaces;
479 }
480
481 return ref;
482 }
483
vaapi_frames_init(AVHWFramesContext * hwfc)484 static int vaapi_frames_init(AVHWFramesContext *hwfc)
485 {
486 AVVAAPIFramesContext *avfc = hwfc->hwctx;
487 VAAPIFramesContext *ctx = hwfc->internal->priv;
488 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
489 const VAAPIFormatDescriptor *desc;
490 VAImageFormat *expected_format;
491 AVBufferRef *test_surface = NULL;
492 VASurfaceID test_surface_id;
493 VAImage test_image;
494 VAStatus vas;
495 int err, i;
496
497 desc = vaapi_format_from_pix_fmt(hwfc->sw_format);
498 if (!desc) {
499 av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
500 av_get_pix_fmt_name(hwfc->sw_format));
501 return AVERROR(EINVAL);
502 }
503
504 if (!hwfc->pool) {
505 if (!(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
506 int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
507 int need_pixel_format = 1;
508 for (i = 0; i < avfc->nb_attributes; i++) {
509 if (avfc->attributes[i].type == VASurfaceAttribMemoryType)
510 need_memory_type = 0;
511 if (avfc->attributes[i].type == VASurfaceAttribPixelFormat)
512 need_pixel_format = 0;
513 }
514 ctx->nb_attributes =
515 avfc->nb_attributes + need_memory_type + need_pixel_format;
516
517 ctx->attributes = av_malloc(ctx->nb_attributes *
518 sizeof(*ctx->attributes));
519 if (!ctx->attributes) {
520 err = AVERROR(ENOMEM);
521 goto fail;
522 }
523
524 for (i = 0; i < avfc->nb_attributes; i++)
525 ctx->attributes[i] = avfc->attributes[i];
526 if (need_memory_type) {
527 ctx->attributes[i++] = (VASurfaceAttrib) {
528 .type = VASurfaceAttribMemoryType,
529 .flags = VA_SURFACE_ATTRIB_SETTABLE,
530 .value.type = VAGenericValueTypeInteger,
531 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
532 };
533 }
534 if (need_pixel_format) {
535 ctx->attributes[i++] = (VASurfaceAttrib) {
536 .type = VASurfaceAttribPixelFormat,
537 .flags = VA_SURFACE_ATTRIB_SETTABLE,
538 .value.type = VAGenericValueTypeInteger,
539 .value.value.i = desc->fourcc,
540 };
541 }
542 av_assert0(i == ctx->nb_attributes);
543 } else {
544 ctx->attributes = NULL;
545 ctx->nb_attributes = 0;
546 }
547
548 ctx->rt_format = desc->rt_format;
549
550 if (hwfc->initial_pool_size > 0) {
551 // This pool will be usable as a render target, so we need to store
552 // all of the surface IDs somewhere that vaCreateContext() calls
553 // will be able to access them.
554 avfc->nb_surfaces = 0;
555 avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
556 sizeof(*avfc->surface_ids));
557 if (!avfc->surface_ids) {
558 err = AVERROR(ENOMEM);
559 goto fail;
560 }
561 } else {
562 // This pool allows dynamic sizing, and will not be usable as a
563 // render target.
564 avfc->nb_surfaces = 0;
565 avfc->surface_ids = NULL;
566 }
567
568 hwfc->internal->pool_internal =
569 av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
570 &vaapi_pool_alloc, NULL);
571 if (!hwfc->internal->pool_internal) {
572 av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
573 err = AVERROR(ENOMEM);
574 goto fail;
575 }
576 }
577
578 // Allocate a single surface to test whether vaDeriveImage() is going
579 // to work for the specific configuration.
580 if (hwfc->pool) {
581 test_surface = av_buffer_pool_get(hwfc->pool);
582 if (!test_surface) {
583 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
584 "user-configured buffer pool.\n");
585 err = AVERROR(ENOMEM);
586 goto fail;
587 }
588 } else {
589 test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
590 if (!test_surface) {
591 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
592 "internal buffer pool.\n");
593 err = AVERROR(ENOMEM);
594 goto fail;
595 }
596 }
597 test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
598
599 ctx->derive_works = 0;
600
601 err = vaapi_get_image_format(hwfc->device_ctx,
602 hwfc->sw_format, &expected_format);
603 if (err == 0) {
604 vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
605 if (vas == VA_STATUS_SUCCESS) {
606 if (expected_format->fourcc == test_image.format.fourcc) {
607 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
608 ctx->derive_works = 1;
609 } else {
610 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
611 "derived image format %08x does not match "
612 "expected format %08x.\n",
613 expected_format->fourcc, test_image.format.fourcc);
614 }
615 vaDestroyImage(hwctx->display, test_image.image_id);
616 } else {
617 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
618 "deriving image does not work: "
619 "%d (%s).\n", vas, vaErrorStr(vas));
620 }
621 } else {
622 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
623 "image format is not supported.\n");
624 }
625
626 av_buffer_unref(&test_surface);
627 return 0;
628
629 fail:
630 av_buffer_unref(&test_surface);
631 av_freep(&avfc->surface_ids);
632 av_freep(&ctx->attributes);
633 return err;
634 }
635
vaapi_frames_uninit(AVHWFramesContext * hwfc)636 static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
637 {
638 AVVAAPIFramesContext *avfc = hwfc->hwctx;
639 VAAPIFramesContext *ctx = hwfc->internal->priv;
640
641 av_freep(&avfc->surface_ids);
642 av_freep(&ctx->attributes);
643 }
644
vaapi_get_buffer(AVHWFramesContext * hwfc,AVFrame * frame)645 static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
646 {
647 frame->buf[0] = av_buffer_pool_get(hwfc->pool);
648 if (!frame->buf[0])
649 return AVERROR(ENOMEM);
650
651 frame->data[3] = frame->buf[0]->data;
652 frame->format = AV_PIX_FMT_VAAPI;
653 frame->width = hwfc->width;
654 frame->height = hwfc->height;
655
656 return 0;
657 }
658
vaapi_transfer_get_formats(AVHWFramesContext * hwfc,enum AVHWFrameTransferDirection dir,enum AVPixelFormat ** formats)659 static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
660 enum AVHWFrameTransferDirection dir,
661 enum AVPixelFormat **formats)
662 {
663 VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
664 enum AVPixelFormat *pix_fmts;
665 int i, k, sw_format_available;
666
667 sw_format_available = 0;
668 for (i = 0; i < ctx->nb_formats; i++) {
669 if (ctx->formats[i].pix_fmt == hwfc->sw_format)
670 sw_format_available = 1;
671 }
672
673 pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
674 if (!pix_fmts)
675 return AVERROR(ENOMEM);
676
677 if (sw_format_available) {
678 pix_fmts[0] = hwfc->sw_format;
679 k = 1;
680 } else {
681 k = 0;
682 }
683 for (i = 0; i < ctx->nb_formats; i++) {
684 if (ctx->formats[i].pix_fmt == hwfc->sw_format)
685 continue;
686 av_assert0(k < ctx->nb_formats);
687 pix_fmts[k++] = ctx->formats[i].pix_fmt;
688 }
689 pix_fmts[k] = AV_PIX_FMT_NONE;
690
691 *formats = pix_fmts;
692 return 0;
693 }
694
vaapi_unmap_frame(AVHWFramesContext * hwfc,HWMapDescriptor * hwmap)695 static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
696 HWMapDescriptor *hwmap)
697 {
698 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
699 VAAPIMapping *map = hwmap->priv;
700 VASurfaceID surface_id;
701 VAStatus vas;
702
703 surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
704 av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
705
706 vas = vaUnmapBuffer(hwctx->display, map->image.buf);
707 if (vas != VA_STATUS_SUCCESS) {
708 av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
709 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
710 }
711
712 if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
713 !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
714 vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
715 0, 0, hwfc->width, hwfc->height,
716 0, 0, hwfc->width, hwfc->height);
717 if (vas != VA_STATUS_SUCCESS) {
718 av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
719 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
720 }
721 }
722
723 vas = vaDestroyImage(hwctx->display, map->image.image_id);
724 if (vas != VA_STATUS_SUCCESS) {
725 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
726 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
727 }
728
729 av_free(map);
730 }
731
vaapi_map_frame(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)732 static int vaapi_map_frame(AVHWFramesContext *hwfc,
733 AVFrame *dst, const AVFrame *src, int flags)
734 {
735 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
736 VAAPIFramesContext *ctx = hwfc->internal->priv;
737 VASurfaceID surface_id;
738 const VAAPIFormatDescriptor *desc;
739 VAImageFormat *image_format;
740 VAAPIMapping *map;
741 VAStatus vas;
742 void *address = NULL;
743 int err, i;
744
745 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
746 av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
747
748 if (!ctx->derive_works && (flags & AV_HWFRAME_MAP_DIRECT)) {
749 // Requested direct mapping but it is not possible.
750 return AVERROR(EINVAL);
751 }
752 if (dst->format == AV_PIX_FMT_NONE)
753 dst->format = hwfc->sw_format;
754 if (dst->format != hwfc->sw_format && (flags & AV_HWFRAME_MAP_DIRECT)) {
755 // Requested direct mapping but the formats do not match.
756 return AVERROR(EINVAL);
757 }
758
759 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
760 if (err < 0) {
761 // Requested format is not a valid output format.
762 return AVERROR(EINVAL);
763 }
764
765 map = av_malloc(sizeof(*map));
766 if (!map)
767 return AVERROR(ENOMEM);
768 map->flags = flags;
769 map->image.image_id = VA_INVALID_ID;
770
771 vas = vaSyncSurface(hwctx->display, surface_id);
772 if (vas != VA_STATUS_SUCCESS) {
773 av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
774 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
775 err = AVERROR(EIO);
776 goto fail;
777 }
778
779 // The memory which we map using derive need not be connected to the CPU
780 // in a way conducive to fast access. On Gen7-Gen9 Intel graphics, the
781 // memory is mappable but not cached, so normal memcpy()-like access is
782 // very slow to read it (but writing is ok). It is possible to read much
783 // faster with a copy routine which is aware of the limitation, but we
784 // assume for now that the user is not aware of that and would therefore
785 // prefer not to be given direct-mapped memory if they request read access.
786 if (ctx->derive_works && dst->format == hwfc->sw_format &&
787 ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
788 vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
789 if (vas != VA_STATUS_SUCCESS) {
790 av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
791 "surface %#x: %d (%s).\n",
792 surface_id, vas, vaErrorStr(vas));
793 err = AVERROR(EIO);
794 goto fail;
795 }
796 if (map->image.format.fourcc != image_format->fourcc) {
797 av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
798 "is in wrong format: expected %#08x, got %#08x.\n",
799 surface_id, image_format->fourcc, map->image.format.fourcc);
800 err = AVERROR(EIO);
801 goto fail;
802 }
803 map->flags |= AV_HWFRAME_MAP_DIRECT;
804 } else {
805 vas = vaCreateImage(hwctx->display, image_format,
806 hwfc->width, hwfc->height, &map->image);
807 if (vas != VA_STATUS_SUCCESS) {
808 av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
809 "surface %#x: %d (%s).\n",
810 surface_id, vas, vaErrorStr(vas));
811 err = AVERROR(EIO);
812 goto fail;
813 }
814 if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
815 vas = vaGetImage(hwctx->display, surface_id, 0, 0,
816 hwfc->width, hwfc->height, map->image.image_id);
817 if (vas != VA_STATUS_SUCCESS) {
818 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
819 "surface %#x: %d (%s).\n",
820 surface_id, vas, vaErrorStr(vas));
821 err = AVERROR(EIO);
822 goto fail;
823 }
824 }
825 }
826
827 vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
828 if (vas != VA_STATUS_SUCCESS) {
829 av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
830 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
831 err = AVERROR(EIO);
832 goto fail;
833 }
834
835 err = ff_hwframe_map_create(src->hw_frames_ctx,
836 dst, src, &vaapi_unmap_frame, map);
837 if (err < 0)
838 goto fail;
839
840 dst->width = src->width;
841 dst->height = src->height;
842
843 for (i = 0; i < map->image.num_planes; i++) {
844 dst->data[i] = (uint8_t*)address + map->image.offsets[i];
845 dst->linesize[i] = map->image.pitches[i];
846 }
847
848 desc = vaapi_format_from_fourcc(map->image.format.fourcc);
849 if (desc && desc->chroma_planes_swapped) {
850 // Chroma planes are YVU rather than YUV, so swap them.
851 FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
852 }
853
854 return 0;
855
856 fail:
857 if (map) {
858 if (address)
859 vaUnmapBuffer(hwctx->display, map->image.buf);
860 if (map->image.image_id != VA_INVALID_ID)
861 vaDestroyImage(hwctx->display, map->image.image_id);
862 av_free(map);
863 }
864 return err;
865 }
866
vaapi_transfer_data_from(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src)867 static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
868 AVFrame *dst, const AVFrame *src)
869 {
870 AVFrame *map;
871 int err;
872
873 if (dst->width > hwfc->width || dst->height > hwfc->height)
874 return AVERROR(EINVAL);
875
876 map = av_frame_alloc();
877 if (!map)
878 return AVERROR(ENOMEM);
879 map->format = dst->format;
880
881 err = vaapi_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
882 if (err)
883 goto fail;
884
885 map->width = dst->width;
886 map->height = dst->height;
887
888 err = av_frame_copy(dst, map);
889 if (err)
890 goto fail;
891
892 err = 0;
893 fail:
894 av_frame_free(&map);
895 return err;
896 }
897
vaapi_transfer_data_to(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src)898 static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
899 AVFrame *dst, const AVFrame *src)
900 {
901 AVFrame *map;
902 int err;
903
904 if (src->width > hwfc->width || src->height > hwfc->height)
905 return AVERROR(EINVAL);
906
907 map = av_frame_alloc();
908 if (!map)
909 return AVERROR(ENOMEM);
910 map->format = src->format;
911
912 err = vaapi_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
913 if (err)
914 goto fail;
915
916 map->width = src->width;
917 map->height = src->height;
918
919 err = av_frame_copy(map, src);
920 if (err)
921 goto fail;
922
923 err = 0;
924 fail:
925 av_frame_free(&map);
926 return err;
927 }
928
vaapi_map_to_memory(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)929 static int vaapi_map_to_memory(AVHWFramesContext *hwfc, AVFrame *dst,
930 const AVFrame *src, int flags)
931 {
932 int err;
933
934 if (dst->format != AV_PIX_FMT_NONE) {
935 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, NULL);
936 if (err < 0)
937 return AVERROR(ENOSYS);
938 }
939
940 err = vaapi_map_frame(hwfc, dst, src, flags);
941 if (err)
942 return err;
943
944 err = av_frame_copy_props(dst, src);
945 if (err)
946 return err;
947
948 return 0;
949 }
950
951 #if CONFIG_LIBDRM
952
953 #define DRM_MAP(va, layers, ...) { \
954 VA_FOURCC_ ## va, \
955 layers, \
956 { __VA_ARGS__ } \
957 }
958 static const struct {
959 uint32_t va_fourcc;
960 int nb_layer_formats;
961 uint32_t layer_formats[AV_DRM_MAX_PLANES];
962 } vaapi_drm_format_map[] = {
963 #ifdef DRM_FORMAT_R8
964 DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88),
965 #endif
966 DRM_MAP(NV12, 1, DRM_FORMAT_NV12),
967 #if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16)
968 DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
969 #endif
970 DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888),
971 DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888),
972 DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888),
973 DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888),
974 #ifdef VA_FOURCC_ABGR
975 DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888),
976 DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888),
977 #endif
978 DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888),
979 DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888),
980 };
981 #undef DRM_MAP
982
vaapi_unmap_from_drm(AVHWFramesContext * dst_fc,HWMapDescriptor * hwmap)983 static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
984 HWMapDescriptor *hwmap)
985 {
986 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
987
988 VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
989
990 av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id);
991
992 vaDestroySurfaces(dst_dev->display, &surface_id, 1);
993 }
994
vaapi_map_from_drm(AVHWFramesContext * src_fc,AVFrame * dst,const AVFrame * src,int flags)995 static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
996 const AVFrame *src, int flags)
997 {
998 AVHWFramesContext *dst_fc =
999 (AVHWFramesContext*)dst->hw_frames_ctx->data;
1000 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
1001 const AVDRMFrameDescriptor *desc;
1002 const VAAPIFormatDescriptor *format_desc;
1003 VASurfaceID surface_id;
1004 VAStatus vas;
1005 uint32_t va_fourcc;
1006 int err, i, j, k;
1007
1008 unsigned long buffer_handle;
1009 VASurfaceAttribExternalBuffers buffer_desc;
1010 VASurfaceAttrib attrs[2] = {
1011 {
1012 .type = VASurfaceAttribMemoryType,
1013 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1014 .value.type = VAGenericValueTypeInteger,
1015 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
1016 },
1017 {
1018 .type = VASurfaceAttribExternalBufferDescriptor,
1019 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1020 .value.type = VAGenericValueTypePointer,
1021 .value.value.p = &buffer_desc,
1022 }
1023 };
1024
1025 desc = (AVDRMFrameDescriptor*)src->data[0];
1026
1027 if (desc->nb_objects != 1) {
1028 av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames "
1029 "made from a single DRM object.\n");
1030 return AVERROR(EINVAL);
1031 }
1032
1033 va_fourcc = 0;
1034 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1035 if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats)
1036 continue;
1037 for (j = 0; j < desc->nb_layers; j++) {
1038 if (desc->layers[j].format !=
1039 vaapi_drm_format_map[i].layer_formats[j])
1040 break;
1041 }
1042 if (j != desc->nb_layers)
1043 continue;
1044 va_fourcc = vaapi_drm_format_map[i].va_fourcc;
1045 break;
1046 }
1047 if (!va_fourcc) {
1048 av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported "
1049 "by VAAPI.\n");
1050 return AVERROR(EINVAL);
1051 }
1052
1053 av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
1054 "%08x.\n", desc->objects[0].fd, va_fourcc);
1055
1056 format_desc = vaapi_format_from_fourcc(va_fourcc);
1057 av_assert0(format_desc);
1058
1059 buffer_handle = desc->objects[0].fd;
1060 buffer_desc.pixel_format = va_fourcc;
1061 buffer_desc.width = src_fc->width;
1062 buffer_desc.height = src_fc->height;
1063 buffer_desc.data_size = desc->objects[0].size;
1064 buffer_desc.buffers = &buffer_handle;
1065 buffer_desc.num_buffers = 1;
1066 buffer_desc.flags = 0;
1067
1068 k = 0;
1069 for (i = 0; i < desc->nb_layers; i++) {
1070 for (j = 0; j < desc->layers[i].nb_planes; j++) {
1071 buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
1072 buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
1073 ++k;
1074 }
1075 }
1076 buffer_desc.num_planes = k;
1077
1078 if (format_desc->chroma_planes_swapped &&
1079 buffer_desc.num_planes == 3) {
1080 FFSWAP(uint32_t, buffer_desc.pitches[1], buffer_desc.pitches[2]);
1081 FFSWAP(uint32_t, buffer_desc.offsets[1], buffer_desc.offsets[2]);
1082 }
1083
1084 vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
1085 src->width, src->height,
1086 &surface_id, 1,
1087 attrs, FF_ARRAY_ELEMS(attrs));
1088 if (vas != VA_STATUS_SUCCESS) {
1089 av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
1090 "object: %d (%s).\n", vas, vaErrorStr(vas));
1091 return AVERROR(EIO);
1092 }
1093 av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
1094
1095 err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
1096 &vaapi_unmap_from_drm,
1097 (void*)(uintptr_t)surface_id);
1098 if (err < 0)
1099 return err;
1100
1101 dst->width = src->width;
1102 dst->height = src->height;
1103 dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
1104
1105 av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
1106 "surface %#x.\n", desc->objects[0].fd, surface_id);
1107
1108 return 0;
1109 }
1110
1111 #if VA_CHECK_VERSION(1, 1, 0)
vaapi_unmap_to_drm_esh(AVHWFramesContext * hwfc,HWMapDescriptor * hwmap)1112 static void vaapi_unmap_to_drm_esh(AVHWFramesContext *hwfc,
1113 HWMapDescriptor *hwmap)
1114 {
1115 AVDRMFrameDescriptor *drm_desc = hwmap->priv;
1116 int i;
1117
1118 for (i = 0; i < drm_desc->nb_objects; i++)
1119 close(drm_desc->objects[i].fd);
1120
1121 av_freep(&drm_desc);
1122 }
1123
vaapi_map_to_drm_esh(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)1124 static int vaapi_map_to_drm_esh(AVHWFramesContext *hwfc, AVFrame *dst,
1125 const AVFrame *src, int flags)
1126 {
1127 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1128 VASurfaceID surface_id;
1129 VAStatus vas;
1130 VADRMPRIMESurfaceDescriptor va_desc;
1131 AVDRMFrameDescriptor *drm_desc = NULL;
1132 uint32_t export_flags;
1133 int err, i, j;
1134
1135 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1136
1137 export_flags = VA_EXPORT_SURFACE_SEPARATE_LAYERS;
1138 if (flags & AV_HWFRAME_MAP_READ)
1139 export_flags |= VA_EXPORT_SURFACE_READ_ONLY;
1140 if (flags & AV_HWFRAME_MAP_WRITE)
1141 export_flags |= VA_EXPORT_SURFACE_WRITE_ONLY;
1142
1143 vas = vaExportSurfaceHandle(hwctx->display, surface_id,
1144 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
1145 export_flags, &va_desc);
1146 if (vas != VA_STATUS_SUCCESS) {
1147 if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
1148 return AVERROR(ENOSYS);
1149 av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
1150 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
1151 return AVERROR(EIO);
1152 }
1153
1154 drm_desc = av_mallocz(sizeof(*drm_desc));
1155 if (!drm_desc) {
1156 err = AVERROR(ENOMEM);
1157 goto fail;
1158 }
1159
1160 // By some bizarre coincidence, these structures are very similar...
1161 drm_desc->nb_objects = va_desc.num_objects;
1162 for (i = 0; i < va_desc.num_objects; i++) {
1163 drm_desc->objects[i].fd = va_desc.objects[i].fd;
1164 drm_desc->objects[i].size = va_desc.objects[i].size;
1165 drm_desc->objects[i].format_modifier =
1166 va_desc.objects[i].drm_format_modifier;
1167 }
1168 drm_desc->nb_layers = va_desc.num_layers;
1169 for (i = 0; i < va_desc.num_layers; i++) {
1170 drm_desc->layers[i].format = va_desc.layers[i].drm_format;
1171 drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes;
1172 for (j = 0; j < va_desc.layers[i].num_planes; j++) {
1173 drm_desc->layers[i].planes[j].object_index =
1174 va_desc.layers[i].object_index[j];
1175 drm_desc->layers[i].planes[j].offset =
1176 va_desc.layers[i].offset[j];
1177 drm_desc->layers[i].planes[j].pitch =
1178 va_desc.layers[i].pitch[j];
1179 }
1180 }
1181
1182 err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
1183 &vaapi_unmap_to_drm_esh, drm_desc);
1184 if (err < 0)
1185 goto fail;
1186
1187 dst->width = src->width;
1188 dst->height = src->height;
1189 dst->data[0] = (uint8_t*)drm_desc;
1190
1191 return 0;
1192
1193 fail:
1194 for (i = 0; i < va_desc.num_objects; i++)
1195 close(va_desc.objects[i].fd);
1196 av_freep(&drm_desc);
1197 return err;
1198 }
1199 #endif
1200
1201 #if VA_CHECK_VERSION(0, 36, 0)
1202 typedef struct VAAPIDRMImageBufferMapping {
1203 VAImage image;
1204 VABufferInfo buffer_info;
1205
1206 AVDRMFrameDescriptor drm_desc;
1207 } VAAPIDRMImageBufferMapping;
1208
vaapi_unmap_to_drm_abh(AVHWFramesContext * hwfc,HWMapDescriptor * hwmap)1209 static void vaapi_unmap_to_drm_abh(AVHWFramesContext *hwfc,
1210 HWMapDescriptor *hwmap)
1211 {
1212 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1213 VAAPIDRMImageBufferMapping *mapping = hwmap->priv;
1214 VASurfaceID surface_id;
1215 VAStatus vas;
1216
1217 surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
1218 av_log(hwfc, AV_LOG_DEBUG, "Unmap VAAPI surface %#x from DRM.\n",
1219 surface_id);
1220
1221 // DRM PRIME file descriptors are closed by vaReleaseBufferHandle(),
1222 // so we shouldn't close them separately.
1223
1224 vas = vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1225 if (vas != VA_STATUS_SUCCESS) {
1226 av_log(hwfc, AV_LOG_ERROR, "Failed to release buffer "
1227 "handle of image %#x (derived from surface %#x): "
1228 "%d (%s).\n", mapping->image.buf, surface_id,
1229 vas, vaErrorStr(vas));
1230 }
1231
1232 vas = vaDestroyImage(hwctx->display, mapping->image.image_id);
1233 if (vas != VA_STATUS_SUCCESS) {
1234 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image "
1235 "derived from surface %#x: %d (%s).\n",
1236 surface_id, vas, vaErrorStr(vas));
1237 }
1238
1239 av_free(mapping);
1240 }
1241
vaapi_map_to_drm_abh(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)1242 static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst,
1243 const AVFrame *src, int flags)
1244 {
1245 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1246 VAAPIDRMImageBufferMapping *mapping = NULL;
1247 VASurfaceID surface_id;
1248 VAStatus vas;
1249 int err, i, p;
1250
1251 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1252 av_log(hwfc, AV_LOG_DEBUG, "Map VAAPI surface %#x to DRM.\n",
1253 surface_id);
1254
1255 mapping = av_mallocz(sizeof(*mapping));
1256 if (!mapping)
1257 return AVERROR(ENOMEM);
1258
1259 vas = vaDeriveImage(hwctx->display, surface_id,
1260 &mapping->image);
1261 if (vas != VA_STATUS_SUCCESS) {
1262 av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
1263 "surface %#x: %d (%s).\n",
1264 surface_id, vas, vaErrorStr(vas));
1265 err = AVERROR(EIO);
1266 goto fail;
1267 }
1268
1269 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1270 if (vaapi_drm_format_map[i].va_fourcc ==
1271 mapping->image.format.fourcc)
1272 break;
1273 }
1274 if (i >= FF_ARRAY_ELEMS(vaapi_drm_format_map)) {
1275 av_log(hwfc, AV_LOG_ERROR, "No matching DRM format for "
1276 "VAAPI format %#x.\n", mapping->image.format.fourcc);
1277 err = AVERROR(EINVAL);
1278 goto fail_derived;
1279 }
1280
1281 mapping->buffer_info.mem_type =
1282 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
1283
1284 mapping->drm_desc.nb_layers =
1285 vaapi_drm_format_map[i].nb_layer_formats;
1286 if (mapping->drm_desc.nb_layers > 1) {
1287 if (mapping->drm_desc.nb_layers != mapping->image.num_planes) {
1288 av_log(hwfc, AV_LOG_ERROR, "Image properties do not match "
1289 "expected format: got %d planes, but expected %d.\n",
1290 mapping->image.num_planes, mapping->drm_desc.nb_layers);
1291 err = AVERROR(EINVAL);
1292 goto fail_derived;
1293 }
1294
1295 for(p = 0; p < mapping->drm_desc.nb_layers; p++) {
1296 mapping->drm_desc.layers[p] = (AVDRMLayerDescriptor) {
1297 .format = vaapi_drm_format_map[i].layer_formats[p],
1298 .nb_planes = 1,
1299 .planes[0] = {
1300 .object_index = 0,
1301 .offset = mapping->image.offsets[p],
1302 .pitch = mapping->image.pitches[p],
1303 },
1304 };
1305 }
1306 } else {
1307 mapping->drm_desc.layers[0].format =
1308 vaapi_drm_format_map[i].layer_formats[0];
1309 mapping->drm_desc.layers[0].nb_planes = mapping->image.num_planes;
1310 for (p = 0; p < mapping->image.num_planes; p++) {
1311 mapping->drm_desc.layers[0].planes[p] = (AVDRMPlaneDescriptor) {
1312 .object_index = 0,
1313 .offset = mapping->image.offsets[p],
1314 .pitch = mapping->image.pitches[p],
1315 };
1316 }
1317 }
1318
1319 vas = vaAcquireBufferHandle(hwctx->display, mapping->image.buf,
1320 &mapping->buffer_info);
1321 if (vas != VA_STATUS_SUCCESS) {
1322 av_log(hwfc, AV_LOG_ERROR, "Failed to get buffer "
1323 "handle from image %#x (derived from surface %#x): "
1324 "%d (%s).\n", mapping->image.buf, surface_id,
1325 vas, vaErrorStr(vas));
1326 err = AVERROR(EIO);
1327 goto fail_derived;
1328 }
1329
1330 av_log(hwfc, AV_LOG_DEBUG, "DRM PRIME fd is %ld.\n",
1331 mapping->buffer_info.handle);
1332
1333 mapping->drm_desc.nb_objects = 1;
1334 mapping->drm_desc.objects[0] = (AVDRMObjectDescriptor) {
1335 .fd = mapping->buffer_info.handle,
1336 .size = mapping->image.data_size,
1337 // There is no way to get the format modifier with this API.
1338 .format_modifier = DRM_FORMAT_MOD_INVALID,
1339 };
1340
1341 err = ff_hwframe_map_create(src->hw_frames_ctx,
1342 dst, src, &vaapi_unmap_to_drm_abh,
1343 mapping);
1344 if (err < 0)
1345 goto fail_mapped;
1346
1347 dst->data[0] = (uint8_t*)&mapping->drm_desc;
1348 dst->width = src->width;
1349 dst->height = src->height;
1350
1351 return 0;
1352
1353 fail_mapped:
1354 vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1355 fail_derived:
1356 vaDestroyImage(hwctx->display, mapping->image.image_id);
1357 fail:
1358 av_freep(&mapping);
1359 return err;
1360 }
1361 #endif
1362
vaapi_map_to_drm(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)1363 static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
1364 const AVFrame *src, int flags)
1365 {
1366 #if VA_CHECK_VERSION(1, 1, 0)
1367 int err;
1368 err = vaapi_map_to_drm_esh(hwfc, dst, src, flags);
1369 if (err != AVERROR(ENOSYS))
1370 return err;
1371 #endif
1372 #if VA_CHECK_VERSION(0, 36, 0)
1373 return vaapi_map_to_drm_abh(hwfc, dst, src, flags);
1374 #endif
1375 return AVERROR(ENOSYS);
1376 }
1377
1378 #endif /* CONFIG_LIBDRM */
1379
vaapi_map_to(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)1380 static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
1381 const AVFrame *src, int flags)
1382 {
1383 switch (src->format) {
1384 #if CONFIG_LIBDRM
1385 case AV_PIX_FMT_DRM_PRIME:
1386 return vaapi_map_from_drm(hwfc, dst, src, flags);
1387 #endif
1388 default:
1389 return AVERROR(ENOSYS);
1390 }
1391 }
1392
vaapi_map_from(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)1393 static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
1394 const AVFrame *src, int flags)
1395 {
1396 switch (dst->format) {
1397 #if CONFIG_LIBDRM
1398 case AV_PIX_FMT_DRM_PRIME:
1399 return vaapi_map_to_drm(hwfc, dst, src, flags);
1400 #endif
1401 default:
1402 return vaapi_map_to_memory(hwfc, dst, src, flags);
1403 }
1404 }
1405
vaapi_device_free(AVHWDeviceContext * ctx)1406 static void vaapi_device_free(AVHWDeviceContext *ctx)
1407 {
1408 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1409 VAAPIDevicePriv *priv = ctx->user_opaque;
1410
1411 if (hwctx->display)
1412 vaTerminate(hwctx->display);
1413
1414 #if HAVE_VAAPI_X11
1415 if (priv->x11_display)
1416 XCloseDisplay(priv->x11_display);
1417 #endif
1418
1419 if (priv->drm_fd >= 0)
1420 close(priv->drm_fd);
1421
1422 av_freep(&priv);
1423 }
1424
1425 #if CONFIG_VAAPI_1
vaapi_device_log_error(void * context,const char * message)1426 static void vaapi_device_log_error(void *context, const char *message)
1427 {
1428 AVHWDeviceContext *ctx = context;
1429
1430 av_log(ctx, AV_LOG_ERROR, "libva: %s", message);
1431 }
1432
vaapi_device_log_info(void * context,const char * message)1433 static void vaapi_device_log_info(void *context, const char *message)
1434 {
1435 AVHWDeviceContext *ctx = context;
1436
1437 av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message);
1438 }
1439 #endif
1440
vaapi_device_connect(AVHWDeviceContext * ctx,VADisplay display)1441 static int vaapi_device_connect(AVHWDeviceContext *ctx,
1442 VADisplay display)
1443 {
1444 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1445 int major, minor;
1446 VAStatus vas;
1447
1448 #if CONFIG_VAAPI_1
1449 vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
1450 vaSetInfoCallback (display, &vaapi_device_log_info, ctx);
1451 #endif
1452
1453 hwctx->display = display;
1454
1455 vas = vaInitialize(display, &major, &minor);
1456 if (vas != VA_STATUS_SUCCESS) {
1457 av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
1458 "connection: %d (%s).\n", vas, vaErrorStr(vas));
1459 return AVERROR(EIO);
1460 }
1461 av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
1462 "version %d.%d\n", major, minor);
1463
1464 return 0;
1465 }
1466
vaapi_device_create(AVHWDeviceContext * ctx,const char * device,AVDictionary * opts,int flags)1467 static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
1468 AVDictionary *opts, int flags)
1469 {
1470 VAAPIDevicePriv *priv;
1471 VADisplay display = NULL;
1472
1473 priv = av_mallocz(sizeof(*priv));
1474 if (!priv)
1475 return AVERROR(ENOMEM);
1476
1477 priv->drm_fd = -1;
1478
1479 ctx->user_opaque = priv;
1480 ctx->free = vaapi_device_free;
1481
1482 #if HAVE_VAAPI_X11
1483 if (!display && !(device && device[0] == '/')) {
1484 // Try to open the device as an X11 display.
1485 priv->x11_display = XOpenDisplay(device);
1486 if (!priv->x11_display) {
1487 av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
1488 "%s.\n", XDisplayName(device));
1489 } else {
1490 display = vaGetDisplay(priv->x11_display);
1491 if (!display) {
1492 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1493 "from X11 display %s.\n", XDisplayName(device));
1494 return AVERROR_UNKNOWN;
1495 }
1496
1497 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1498 "X11 display %s.\n", XDisplayName(device));
1499 }
1500 }
1501 #endif
1502
1503 #if HAVE_VAAPI_DRM
1504 if (!display) {
1505 // Try to open the device as a DRM path.
1506 // Default to using the first render node if the user did not
1507 // supply a path.
1508 const char *path = device ? device : "/dev/dri/renderD128";
1509 priv->drm_fd = open(path, O_RDWR);
1510 if (priv->drm_fd < 0) {
1511 av_log(ctx, AV_LOG_VERBOSE, "Cannot open DRM device %s.\n",
1512 path);
1513 } else {
1514 display = vaGetDisplayDRM(priv->drm_fd);
1515 if (!display) {
1516 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1517 "from DRM device %s.\n", path);
1518 return AVERROR_UNKNOWN;
1519 }
1520
1521 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1522 "DRM device %s.\n", path);
1523 }
1524 }
1525 #endif
1526
1527 if (!display) {
1528 av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1529 "device: %s.\n", device ? device : "");
1530 return AVERROR(EINVAL);
1531 }
1532
1533 return vaapi_device_connect(ctx, display);
1534 }
1535
vaapi_device_derive(AVHWDeviceContext * ctx,AVHWDeviceContext * src_ctx,int flags)1536 static int vaapi_device_derive(AVHWDeviceContext *ctx,
1537 AVHWDeviceContext *src_ctx, int flags)
1538 {
1539 #if HAVE_VAAPI_DRM
1540 if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
1541 AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
1542 VADisplay *display;
1543 VAAPIDevicePriv *priv;
1544
1545 if (src_hwctx->fd < 0) {
1546 av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
1547 "device to derive a VA display from.\n");
1548 return AVERROR(EINVAL);
1549 }
1550
1551 priv = av_mallocz(sizeof(*priv));
1552 if (!priv)
1553 return AVERROR(ENOMEM);
1554
1555 // Inherits the fd from the source context, which will close it.
1556 priv->drm_fd = -1;
1557
1558 ctx->user_opaque = priv;
1559 ctx->free = &vaapi_device_free;
1560
1561 display = vaGetDisplayDRM(src_hwctx->fd);
1562 if (!display) {
1563 av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
1564 "DRM device.\n");
1565 return AVERROR(EIO);
1566 }
1567
1568 return vaapi_device_connect(ctx, display);
1569 }
1570 #endif
1571 return AVERROR(ENOSYS);
1572 }
1573
1574 const HWContextType ff_hwcontext_type_vaapi = {
1575 .type = AV_HWDEVICE_TYPE_VAAPI,
1576 .name = "VAAPI",
1577
1578 .device_hwctx_size = sizeof(AVVAAPIDeviceContext),
1579 .device_priv_size = sizeof(VAAPIDeviceContext),
1580 .device_hwconfig_size = sizeof(AVVAAPIHWConfig),
1581 .frames_hwctx_size = sizeof(AVVAAPIFramesContext),
1582 .frames_priv_size = sizeof(VAAPIFramesContext),
1583
1584 .device_create = &vaapi_device_create,
1585 .device_derive = &vaapi_device_derive,
1586 .device_init = &vaapi_device_init,
1587 .device_uninit = &vaapi_device_uninit,
1588 .frames_get_constraints = &vaapi_frames_get_constraints,
1589 .frames_init = &vaapi_frames_init,
1590 .frames_uninit = &vaapi_frames_uninit,
1591 .frames_get_buffer = &vaapi_get_buffer,
1592 .transfer_get_formats = &vaapi_transfer_get_formats,
1593 .transfer_data_to = &vaapi_transfer_data_to,
1594 .transfer_data_from = &vaapi_transfer_data_from,
1595 .map_to = &vaapi_map_to,
1596 .map_from = &vaapi_map_from,
1597
1598 .pix_fmts = (const enum AVPixelFormat[]) {
1599 AV_PIX_FMT_VAAPI,
1600 AV_PIX_FMT_NONE
1601 },
1602 };
1603