summaryrefslogtreecommitdiff
path: root/libavutil/hwcontext_vaapi.c (plain)
blob: 3b50e95615a436d22fa5eb3af0c8119cda7afa32
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#include <fcntl.h>
29#if HAVE_UNISTD_H
30# include <unistd.h>
31#endif
32
33
34#include "avassert.h"
35#include "buffer.h"
36#include "common.h"
37#include "hwcontext.h"
38#include "hwcontext_internal.h"
39#include "hwcontext_vaapi.h"
40#include "mem.h"
41#include "pixdesc.h"
42#include "pixfmt.h"
43
44typedef struct VAAPIDevicePriv {
45#if HAVE_VAAPI_X11
46 Display *x11_display;
47#endif
48
49 int drm_fd;
50} VAAPIDevicePriv;
51
52typedef struct VAAPISurfaceFormat {
53 enum AVPixelFormat pix_fmt;
54 VAImageFormat image_format;
55} VAAPISurfaceFormat;
56
57typedef struct VAAPIDeviceContext {
58 // Surface formats which can be used with this device.
59 VAAPISurfaceFormat *formats;
60 int nb_formats;
61} VAAPIDeviceContext;
62
63typedef struct VAAPIFramesContext {
64 // Surface attributes set at create time.
65 VASurfaceAttrib *attributes;
66 int nb_attributes;
67 // RT format of the underlying surface (Intel driver ignores this anyway).
68 unsigned int rt_format;
69 // Whether vaDeriveImage works.
70 int derive_works;
71} VAAPIFramesContext;
72
73typedef struct VAAPIMapping {
74 // Handle to the derived or copied image which is mapped.
75 VAImage image;
76 // The mapping flags actually used.
77 int flags;
78} VAAPIMapping;
79
80#define MAP(va, rt, av) { \
81 VA_FOURCC_ ## va, \
82 VA_RT_FORMAT_ ## rt, \
83 AV_PIX_FMT_ ## av \
84 }
85// The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
86// plane swap cases. The frame handling below tries to hide these.
87static struct {
88 unsigned int fourcc;
89 unsigned int rt_format;
90 enum AVPixelFormat pix_fmt;
91} vaapi_format_map[] = {
92 MAP(NV12, YUV420, NV12),
93 MAP(YV12, YUV420, YUV420P), // With U/V planes swapped.
94 MAP(IYUV, YUV420, YUV420P),
95 //MAP(I420, YUV420, YUV420P), // Not in libva but used by Intel driver.
96#ifdef VA_FOURCC_YV16
97 MAP(YV16, YUV422, YUV422P), // With U/V planes swapped.
98#endif
99 MAP(422H, YUV422, YUV422P),
100 MAP(UYVY, YUV422, UYVY422),
101 MAP(YUY2, YUV422, YUYV422),
102 MAP(Y800, YUV400, GRAY8),
103#ifdef VA_FOURCC_P010
104 MAP(P010, YUV420_10BPP, P010),
105#endif
106 MAP(BGRA, RGB32, BGRA),
107 MAP(BGRX, RGB32, BGR0),
108 MAP(RGBA, RGB32, RGBA),
109 MAP(RGBX, RGB32, RGB0),
110#ifdef VA_FOURCC_ABGR
111 MAP(ABGR, RGB32, ABGR),
112 MAP(XBGR, RGB32, 0BGR),
113#endif
114 MAP(ARGB, RGB32, ARGB),
115 MAP(XRGB, RGB32, 0RGB),
116};
117#undef MAP
118
119static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
120{
121 int i;
122 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
123 if (vaapi_format_map[i].fourcc == fourcc)
124 return vaapi_format_map[i].pix_fmt;
125 return AV_PIX_FMT_NONE;
126}
127
128static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
129 enum AVPixelFormat pix_fmt,
130 VAImageFormat **image_format)
131{
132 VAAPIDeviceContext *ctx = hwdev->internal->priv;
133 int i;
134
135 for (i = 0; i < ctx->nb_formats; i++) {
136 if (ctx->formats[i].pix_fmt == pix_fmt) {
137 if (image_format)
138 *image_format = &ctx->formats[i].image_format;
139 return 0;
140 }
141 }
142 return AVERROR(EINVAL);
143}
144
145static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
146 const void *hwconfig,
147 AVHWFramesConstraints *constraints)
148{
149 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
150 const AVVAAPIHWConfig *config = hwconfig;
151 VAAPIDeviceContext *ctx = hwdev->internal->priv;
152 VASurfaceAttrib *attr_list = NULL;
153 VAStatus vas;
154 enum AVPixelFormat pix_fmt;
155 unsigned int fourcc;
156 int err, i, j, attr_count, pix_fmt_count;
157
158 if (config) {
159 attr_count = 0;
160 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
161 0, &attr_count);
162 if (vas != VA_STATUS_SUCCESS) {
163 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
164 "%d (%s).\n", vas, vaErrorStr(vas));
165 err = AVERROR(ENOSYS);
166 goto fail;
167 }
168
169 attr_list = av_malloc(attr_count * sizeof(*attr_list));
170 if (!attr_list) {
171 err = AVERROR(ENOMEM);
172 goto fail;
173 }
174
175 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
176 attr_list, &attr_count);
177 if (vas != VA_STATUS_SUCCESS) {
178 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
179 "%d (%s).\n", vas, vaErrorStr(vas));
180 err = AVERROR(ENOSYS);
181 goto fail;
182 }
183
184 pix_fmt_count = 0;
185 for (i = 0; i < attr_count; i++) {
186 switch (attr_list[i].type) {
187 case VASurfaceAttribPixelFormat:
188 fourcc = attr_list[i].value.value.i;
189 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
190 if (pix_fmt != AV_PIX_FMT_NONE) {
191 ++pix_fmt_count;
192 } else {
193 // Something unsupported - ignore.
194 }
195 break;
196 case VASurfaceAttribMinWidth:
197 constraints->min_width = attr_list[i].value.value.i;
198 break;
199 case VASurfaceAttribMinHeight:
200 constraints->min_height = attr_list[i].value.value.i;
201 break;
202 case VASurfaceAttribMaxWidth:
203 constraints->max_width = attr_list[i].value.value.i;
204 break;
205 case VASurfaceAttribMaxHeight:
206 constraints->max_height = attr_list[i].value.value.i;
207 break;
208 }
209 }
210 if (pix_fmt_count == 0) {
211 // Nothing usable found. Presumably there exists something which
212 // works, so leave the set null to indicate unknown.
213 constraints->valid_sw_formats = NULL;
214 } else {
215 constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
216 sizeof(pix_fmt));
217 if (!constraints->valid_sw_formats) {
218 err = AVERROR(ENOMEM);
219 goto fail;
220 }
221
222 for (i = j = 0; i < attr_count; i++) {
223 if (attr_list[i].type != VASurfaceAttribPixelFormat)
224 continue;
225 fourcc = attr_list[i].value.value.i;
226 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
227 if (pix_fmt != AV_PIX_FMT_NONE)
228 constraints->valid_sw_formats[j++] = pix_fmt;
229 }
230 av_assert0(j == pix_fmt_count);
231 constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
232 }
233 } else {
234 // No configuration supplied.
235 // Return the full set of image formats known by the implementation.
236 constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
237 sizeof(pix_fmt));
238 if (!constraints->valid_sw_formats) {
239 err = AVERROR(ENOMEM);
240 goto fail;
241 }
242 for (i = 0; i < ctx->nb_formats; i++)
243 constraints->valid_sw_formats[i] = ctx->formats[i].pix_fmt;
244 constraints->valid_sw_formats[i] = AV_PIX_FMT_NONE;
245 }
246
247 constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
248 if (!constraints->valid_hw_formats) {
249 err = AVERROR(ENOMEM);
250 goto fail;
251 }
252 constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
253 constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
254
255 err = 0;
256fail:
257 av_freep(&attr_list);
258 return err;
259}
260
261static const struct {
262 const char *friendly_name;
263 const char *match_string;
264 unsigned int quirks;
265} vaapi_driver_quirks_table[] = {
266 {
267 "Intel i965 (Quick Sync)",
268 "i965",
269 AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
270 },
271 {
272 "Intel iHD",
273 "ubit",
274 AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
275 },
276};
277
278static int vaapi_device_init(AVHWDeviceContext *hwdev)
279{
280 VAAPIDeviceContext *ctx = hwdev->internal->priv;
281 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
282 VAImageFormat *image_list = NULL;
283 VAStatus vas;
284 const char *vendor_string;
285 int err, i, image_count;
286 enum AVPixelFormat pix_fmt;
287 unsigned int fourcc;
288
289 image_count = vaMaxNumImageFormats(hwctx->display);
290 if (image_count <= 0) {
291 err = AVERROR(EIO);
292 goto fail;
293 }
294 image_list = av_malloc(image_count * sizeof(*image_list));
295 if (!image_list) {
296 err = AVERROR(ENOMEM);
297 goto fail;
298 }
299 vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
300 if (vas != VA_STATUS_SUCCESS) {
301 err = AVERROR(EIO);
302 goto fail;
303 }
304
305 ctx->formats = av_malloc(image_count * sizeof(*ctx->formats));
306 if (!ctx->formats) {
307 err = AVERROR(ENOMEM);
308 goto fail;
309 }
310 ctx->nb_formats = 0;
311 for (i = 0; i < image_count; i++) {
312 fourcc = image_list[i].fourcc;
313 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
314 if (pix_fmt == AV_PIX_FMT_NONE) {
315 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
316 fourcc);
317 } else {
318 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
319 fourcc, av_get_pix_fmt_name(pix_fmt));
320 ctx->formats[ctx->nb_formats].pix_fmt = pix_fmt;
321 ctx->formats[ctx->nb_formats].image_format = image_list[i];
322 ++ctx->nb_formats;
323 }
324 }
325
326 if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
327 av_log(hwdev, AV_LOG_VERBOSE, "Not detecting driver: "
328 "quirks set by user.\n");
329 } else {
330 // Detect the driver in use and set quirk flags if necessary.
331 vendor_string = vaQueryVendorString(hwctx->display);
332 hwctx->driver_quirks = 0;
333 if (vendor_string) {
334 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
335 if (strstr(vendor_string,
336 vaapi_driver_quirks_table[i].match_string)) {
337 av_log(hwdev, AV_LOG_VERBOSE, "Matched \"%s\" as known "
338 "driver \"%s\".\n", vendor_string,
339 vaapi_driver_quirks_table[i].friendly_name);
340 hwctx->driver_quirks |=
341 vaapi_driver_quirks_table[i].quirks;
342 break;
343 }
344 }
345 if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
346 av_log(hwdev, AV_LOG_VERBOSE, "Unknown driver \"%s\", "
347 "assuming standard behaviour.\n", vendor_string);
348 }
349 }
350 }
351
352 av_free(image_list);
353 return 0;
354fail:
355 av_freep(&ctx->formats);
356 av_free(image_list);
357 return err;
358}
359
360static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
361{
362 VAAPIDeviceContext *ctx = hwdev->internal->priv;
363
364 av_freep(&ctx->formats);
365}
366
367static void vaapi_buffer_free(void *opaque, uint8_t *data)
368{
369 AVHWFramesContext *hwfc = opaque;
370 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
371 VASurfaceID surface_id;
372 VAStatus vas;
373
374 surface_id = (VASurfaceID)(uintptr_t)data;
375
376 vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
377 if (vas != VA_STATUS_SUCCESS) {
378 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
379 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
380 }
381}
382
383static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
384{
385 AVHWFramesContext *hwfc = opaque;
386 VAAPIFramesContext *ctx = hwfc->internal->priv;
387 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
388 AVVAAPIFramesContext *avfc = hwfc->hwctx;
389 VASurfaceID surface_id;
390 VAStatus vas;
391 AVBufferRef *ref;
392
393 if (hwfc->initial_pool_size > 0 &&
394 avfc->nb_surfaces >= hwfc->initial_pool_size)
395 return NULL;
396
397 vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
398 hwfc->width, hwfc->height,
399 &surface_id, 1,
400 ctx->attributes, ctx->nb_attributes);
401 if (vas != VA_STATUS_SUCCESS) {
402 av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
403 "%d (%s).\n", vas, vaErrorStr(vas));
404 return NULL;
405 }
406 av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
407
408 ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
409 sizeof(surface_id), &vaapi_buffer_free,
410 hwfc, AV_BUFFER_FLAG_READONLY);
411 if (!ref) {
412 vaDestroySurfaces(hwctx->display, &surface_id, 1);
413 return NULL;
414 }
415
416 if (hwfc->initial_pool_size > 0) {
417 // This is a fixed-size pool, so we must still be in the initial
418 // allocation sequence.
419 av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
420 avfc->surface_ids[avfc->nb_surfaces] = surface_id;
421 ++avfc->nb_surfaces;
422 }
423
424 return ref;
425}
426
427static int vaapi_frames_init(AVHWFramesContext *hwfc)
428{
429 AVVAAPIFramesContext *avfc = hwfc->hwctx;
430 VAAPIFramesContext *ctx = hwfc->internal->priv;
431 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
432 VAImageFormat *expected_format;
433 AVBufferRef *test_surface = NULL;
434 VASurfaceID test_surface_id;
435 VAImage test_image;
436 VAStatus vas;
437 int err, i;
438 unsigned int fourcc, rt_format;
439
440 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
441 if (vaapi_format_map[i].pix_fmt == hwfc->sw_format) {
442 fourcc = vaapi_format_map[i].fourcc;
443 rt_format = vaapi_format_map[i].rt_format;
444 break;
445 }
446 }
447 if (i >= FF_ARRAY_ELEMS(vaapi_format_map)) {
448 av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
449 av_get_pix_fmt_name(hwfc->sw_format));
450 return AVERROR(EINVAL);
451 }
452
453 if (!hwfc->pool) {
454 int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
455 int need_pixel_format = 1;
456 for (i = 0; i < avfc->nb_attributes; i++) {
457 if (ctx->attributes[i].type == VASurfaceAttribMemoryType)
458 need_memory_type = 0;
459 if (ctx->attributes[i].type == VASurfaceAttribPixelFormat)
460 need_pixel_format = 0;
461 }
462 ctx->nb_attributes =
463 avfc->nb_attributes + need_memory_type + need_pixel_format;
464
465 ctx->attributes = av_malloc(ctx->nb_attributes *
466 sizeof(*ctx->attributes));
467 if (!ctx->attributes) {
468 err = AVERROR(ENOMEM);
469 goto fail;
470 }
471
472 for (i = 0; i < avfc->nb_attributes; i++)
473 ctx->attributes[i] = avfc->attributes[i];
474 if (need_memory_type) {
475 ctx->attributes[i++] = (VASurfaceAttrib) {
476 .type = VASurfaceAttribMemoryType,
477 .flags = VA_SURFACE_ATTRIB_SETTABLE,
478 .value.type = VAGenericValueTypeInteger,
479 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
480 };
481 }
482 if (need_pixel_format) {
483 ctx->attributes[i++] = (VASurfaceAttrib) {
484 .type = VASurfaceAttribPixelFormat,
485 .flags = VA_SURFACE_ATTRIB_SETTABLE,
486 .value.type = VAGenericValueTypeInteger,
487 .value.value.i = fourcc,
488 };
489 }
490 av_assert0(i == ctx->nb_attributes);
491
492 ctx->rt_format = rt_format;
493
494 if (hwfc->initial_pool_size > 0) {
495 // This pool will be usable as a render target, so we need to store
496 // all of the surface IDs somewhere that vaCreateContext() calls
497 // will be able to access them.
498 avfc->nb_surfaces = 0;
499 avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
500 sizeof(*avfc->surface_ids));
501 if (!avfc->surface_ids) {
502 err = AVERROR(ENOMEM);
503 goto fail;
504 }
505 } else {
506 // This pool allows dynamic sizing, and will not be usable as a
507 // render target.
508 avfc->nb_surfaces = 0;
509 avfc->surface_ids = NULL;
510 }
511
512 hwfc->internal->pool_internal =
513 av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
514 &vaapi_pool_alloc, NULL);
515 if (!hwfc->internal->pool_internal) {
516 av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
517 err = AVERROR(ENOMEM);
518 goto fail;
519 }
520 }
521
522 // Allocate a single surface to test whether vaDeriveImage() is going
523 // to work for the specific configuration.
524 if (hwfc->pool) {
525 test_surface = av_buffer_pool_get(hwfc->pool);
526 if (!test_surface) {
527 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
528 "user-configured buffer pool.\n");
529 err = AVERROR(ENOMEM);
530 goto fail;
531 }
532 } else {
533 test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
534 if (!test_surface) {
535 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
536 "internal buffer pool.\n");
537 err = AVERROR(ENOMEM);
538 goto fail;
539 }
540 }
541 test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
542
543 ctx->derive_works = 0;
544
545 err = vaapi_get_image_format(hwfc->device_ctx,
546 hwfc->sw_format, &expected_format);
547 if (err == 0) {
548 vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
549 if (vas == VA_STATUS_SUCCESS) {
550 if (expected_format->fourcc == test_image.format.fourcc) {
551 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
552 ctx->derive_works = 1;
553 } else {
554 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
555 "derived image format %08x does not match "
556 "expected format %08x.\n",
557 expected_format->fourcc, test_image.format.fourcc);
558 }
559 vaDestroyImage(hwctx->display, test_image.image_id);
560 } else {
561 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
562 "deriving image does not work: "
563 "%d (%s).\n", vas, vaErrorStr(vas));
564 }
565 } else {
566 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
567 "image format is not supported.\n");
568 }
569
570 av_buffer_unref(&test_surface);
571 return 0;
572
573fail:
574 av_buffer_unref(&test_surface);
575 av_freep(&avfc->surface_ids);
576 av_freep(&ctx->attributes);
577 return err;
578}
579
580static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
581{
582 AVVAAPIFramesContext *avfc = hwfc->hwctx;
583 VAAPIFramesContext *ctx = hwfc->internal->priv;
584
585 av_freep(&avfc->surface_ids);
586 av_freep(&ctx->attributes);
587}
588
589static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
590{
591 frame->buf[0] = av_buffer_pool_get(hwfc->pool);
592 if (!frame->buf[0])
593 return AVERROR(ENOMEM);
594
595 frame->data[3] = frame->buf[0]->data;
596 frame->format = AV_PIX_FMT_VAAPI;
597 frame->width = hwfc->width;
598 frame->height = hwfc->height;
599
600 return 0;
601}
602
603static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
604 enum AVHWFrameTransferDirection dir,
605 enum AVPixelFormat **formats)
606{
607 VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
608 enum AVPixelFormat *pix_fmts, preferred_format;
609 int i, k;
610
611 preferred_format = hwfc->sw_format;
612
613 pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
614 if (!pix_fmts)
615 return AVERROR(ENOMEM);
616
617 pix_fmts[0] = preferred_format;
618 k = 1;
619 for (i = 0; i < ctx->nb_formats; i++) {
620 if (ctx->formats[i].pix_fmt == preferred_format)
621 continue;
622 av_assert0(k < ctx->nb_formats);
623 pix_fmts[k++] = ctx->formats[i].pix_fmt;
624 }
625 av_assert0(k == ctx->nb_formats);
626 pix_fmts[k] = AV_PIX_FMT_NONE;
627
628 *formats = pix_fmts;
629 return 0;
630}
631
632static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
633 HWMapDescriptor *hwmap)
634{
635 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
636 VAAPIMapping *map = hwmap->priv;
637 VASurfaceID surface_id;
638 VAStatus vas;
639
640 surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
641 av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
642
643 vas = vaUnmapBuffer(hwctx->display, map->image.buf);
644 if (vas != VA_STATUS_SUCCESS) {
645 av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
646 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
647 }
648
649 if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
650 !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
651 vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
652 0, 0, hwfc->width, hwfc->height,
653 0, 0, hwfc->width, hwfc->height);
654 if (vas != VA_STATUS_SUCCESS) {
655 av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
656 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
657 }
658 }
659
660 vas = vaDestroyImage(hwctx->display, map->image.image_id);
661 if (vas != VA_STATUS_SUCCESS) {
662 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
663 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
664 }
665
666 av_free(map);
667}
668
669static int vaapi_map_frame(AVHWFramesContext *hwfc,
670 AVFrame *dst, const AVFrame *src, int flags)
671{
672 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
673 VAAPIFramesContext *ctx = hwfc->internal->priv;
674 VASurfaceID surface_id;
675 VAImageFormat *image_format;
676 VAAPIMapping *map;
677 VAStatus vas;
678 void *address = NULL;
679 int err, i;
680
681 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
682 av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
683
684 if (!ctx->derive_works && (flags & AV_HWFRAME_MAP_DIRECT)) {
685 // Requested direct mapping but it is not possible.
686 return AVERROR(EINVAL);
687 }
688 if (dst->format == AV_PIX_FMT_NONE)
689 dst->format = hwfc->sw_format;
690 if (dst->format != hwfc->sw_format && (flags & AV_HWFRAME_MAP_DIRECT)) {
691 // Requested direct mapping but the formats do not match.
692 return AVERROR(EINVAL);
693 }
694
695 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
696 if (err < 0) {
697 // Requested format is not a valid output format.
698 return AVERROR(EINVAL);
699 }
700
701 map = av_malloc(sizeof(*map));
702 if (!map)
703 return AVERROR(ENOMEM);
704 map->flags = flags;
705 map->image.image_id = VA_INVALID_ID;
706
707 vas = vaSyncSurface(hwctx->display, surface_id);
708 if (vas != VA_STATUS_SUCCESS) {
709 av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
710 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
711 err = AVERROR(EIO);
712 goto fail;
713 }
714
715 // The memory which we map using derive need not be connected to the CPU
716 // in a way conducive to fast access. On Gen7-Gen9 Intel graphics, the
717 // memory is mappable but not cached, so normal memcpy()-like access is
718 // very slow to read it (but writing is ok). It is possible to read much
719 // faster with a copy routine which is aware of the limitation, but we
720 // assume for now that the user is not aware of that and would therefore
721 // prefer not to be given direct-mapped memory if they request read access.
722 if (ctx->derive_works && dst->format == hwfc->sw_format &&
723 ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
724 vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
725 if (vas != VA_STATUS_SUCCESS) {
726 av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
727 "surface %#x: %d (%s).\n",
728 surface_id, vas, vaErrorStr(vas));
729 err = AVERROR(EIO);
730 goto fail;
731 }
732 if (map->image.format.fourcc != image_format->fourcc) {
733 av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
734 "is in wrong format: expected %#08x, got %#08x.\n",
735 surface_id, image_format->fourcc, map->image.format.fourcc);
736 err = AVERROR(EIO);
737 goto fail;
738 }
739 map->flags |= AV_HWFRAME_MAP_DIRECT;
740 } else {
741 vas = vaCreateImage(hwctx->display, image_format,
742 hwfc->width, hwfc->height, &map->image);
743 if (vas != VA_STATUS_SUCCESS) {
744 av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
745 "surface %#x: %d (%s).\n",
746 surface_id, vas, vaErrorStr(vas));
747 err = AVERROR(EIO);
748 goto fail;
749 }
750 if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
751 vas = vaGetImage(hwctx->display, surface_id, 0, 0,
752 hwfc->width, hwfc->height, map->image.image_id);
753 if (vas != VA_STATUS_SUCCESS) {
754 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
755 "surface %#x: %d (%s).\n",
756 surface_id, vas, vaErrorStr(vas));
757 err = AVERROR(EIO);
758 goto fail;
759 }
760 }
761 }
762
763 vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
764 if (vas != VA_STATUS_SUCCESS) {
765 av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
766 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
767 err = AVERROR(EIO);
768 goto fail;
769 }
770
771 err = ff_hwframe_map_create(src->hw_frames_ctx,
772 dst, src, &vaapi_unmap_frame, map);
773 if (err < 0)
774 goto fail;
775
776 dst->width = src->width;
777 dst->height = src->height;
778
779 for (i = 0; i < map->image.num_planes; i++) {
780 dst->data[i] = (uint8_t*)address + map->image.offsets[i];
781 dst->linesize[i] = map->image.pitches[i];
782 }
783 if (
784#ifdef VA_FOURCC_YV16
785 map->image.format.fourcc == VA_FOURCC_YV16 ||
786#endif
787 map->image.format.fourcc == VA_FOURCC_YV12) {
788 // Chroma planes are YVU rather than YUV, so swap them.
789 FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
790 }
791
792 return 0;
793
794fail:
795 if (map) {
796 if (address)
797 vaUnmapBuffer(hwctx->display, map->image.buf);
798 if (map->image.image_id != VA_INVALID_ID)
799 vaDestroyImage(hwctx->display, map->image.image_id);
800 av_free(map);
801 }
802 return err;
803}
804
805static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
806 AVFrame *dst, const AVFrame *src)
807{
808 AVFrame *map;
809 int err;
810
811 if (dst->width > hwfc->width || dst->height > hwfc->height)
812 return AVERROR(EINVAL);
813
814 map = av_frame_alloc();
815 if (!map)
816 return AVERROR(ENOMEM);
817 map->format = dst->format;
818
819 err = vaapi_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
820 if (err)
821 goto fail;
822
823 map->width = dst->width;
824 map->height = dst->height;
825
826 err = av_frame_copy(dst, map);
827 if (err)
828 goto fail;
829
830 err = 0;
831fail:
832 av_frame_free(&map);
833 return err;
834}
835
836static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
837 AVFrame *dst, const AVFrame *src)
838{
839 AVFrame *map;
840 int err;
841
842 if (src->width > hwfc->width || src->height > hwfc->height)
843 return AVERROR(EINVAL);
844
845 map = av_frame_alloc();
846 if (!map)
847 return AVERROR(ENOMEM);
848 map->format = src->format;
849
850 err = vaapi_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
851 if (err)
852 goto fail;
853
854 map->width = src->width;
855 map->height = src->height;
856
857 err = av_frame_copy(map, src);
858 if (err)
859 goto fail;
860
861 err = 0;
862fail:
863 av_frame_free(&map);
864 return err;
865}
866
867static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
868 const AVFrame *src, int flags)
869{
870 int err;
871
872 if (dst->format != AV_PIX_FMT_NONE) {
873 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, NULL);
874 if (err < 0)
875 return AVERROR(ENOSYS);
876 }
877
878 err = vaapi_map_frame(hwfc, dst, src, flags);
879 if (err)
880 return err;
881
882 err = av_frame_copy_props(dst, src);
883 if (err)
884 return err;
885
886 return 0;
887}
888
889static void vaapi_device_free(AVHWDeviceContext *ctx)
890{
891 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
892 VAAPIDevicePriv *priv = ctx->user_opaque;
893
894 if (hwctx->display)
895 vaTerminate(hwctx->display);
896
897#if HAVE_VAAPI_X11
898 if (priv->x11_display)
899 XCloseDisplay(priv->x11_display);
900#endif
901
902 if (priv->drm_fd >= 0)
903 close(priv->drm_fd);
904
905 av_freep(&priv);
906}
907
908static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
909 AVDictionary *opts, int flags)
910{
911 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
912 VAAPIDevicePriv *priv;
913 VADisplay display = 0;
914 VAStatus vas;
915 int major, minor;
916
917 priv = av_mallocz(sizeof(*priv));
918 if (!priv)
919 return AVERROR(ENOMEM);
920
921 priv->drm_fd = -1;
922
923 ctx->user_opaque = priv;
924 ctx->free = vaapi_device_free;
925
926#if HAVE_VAAPI_X11
927 if (!display && !(device && device[0] == '/')) {
928 // Try to open the device as an X11 display.
929 priv->x11_display = XOpenDisplay(device);
930 if (!priv->x11_display) {
931 av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
932 "%s.\n", XDisplayName(device));
933 } else {
934 display = vaGetDisplay(priv->x11_display);
935 if (!display) {
936 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
937 "from X11 display %s.\n", XDisplayName(device));
938 return AVERROR_UNKNOWN;
939 }
940
941 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
942 "X11 display %s.\n", XDisplayName(device));
943 }
944 }
945#endif
946
947#if HAVE_VAAPI_DRM
948 if (!display) {
949 // Try to open the device as a DRM path.
950 // Default to using the first render node if the user did not
951 // supply a path.
952 const char *path = device ? device : "/dev/dri/renderD128";
953 priv->drm_fd = open(path, O_RDWR);
954 if (priv->drm_fd < 0) {
955 av_log(ctx, AV_LOG_VERBOSE, "Cannot open DRM device %s.\n",
956 path);
957 } else {
958 display = vaGetDisplayDRM(priv->drm_fd);
959 if (!display) {
960 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
961 "from DRM device %s.\n", path);
962 return AVERROR_UNKNOWN;
963 }
964
965 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
966 "DRM device %s.\n", path);
967 }
968 }
969#endif
970
971 if (!display) {
972 av_log(ctx, AV_LOG_ERROR, "No VA display found for "
973 "device: %s.\n", device ? device : "");
974 return AVERROR(EINVAL);
975 }
976
977 hwctx->display = display;
978
979 vas = vaInitialize(display, &major, &minor);
980 if (vas != VA_STATUS_SUCCESS) {
981 av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
982 "connection: %d (%s).\n", vas, vaErrorStr(vas));
983 return AVERROR(EIO);
984 }
985 av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
986 "version %d.%d\n", major, minor);
987
988 return 0;
989}
990
991const HWContextType ff_hwcontext_type_vaapi = {
992 .type = AV_HWDEVICE_TYPE_VAAPI,
993 .name = "VAAPI",
994
995 .device_hwctx_size = sizeof(AVVAAPIDeviceContext),
996 .device_priv_size = sizeof(VAAPIDeviceContext),
997 .device_hwconfig_size = sizeof(AVVAAPIHWConfig),
998 .frames_hwctx_size = sizeof(AVVAAPIFramesContext),
999 .frames_priv_size = sizeof(VAAPIFramesContext),
1000
1001 .device_create = &vaapi_device_create,
1002 .device_init = &vaapi_device_init,
1003 .device_uninit = &vaapi_device_uninit,
1004 .frames_get_constraints = &vaapi_frames_get_constraints,
1005 .frames_init = &vaapi_frames_init,
1006 .frames_uninit = &vaapi_frames_uninit,
1007 .frames_get_buffer = &vaapi_get_buffer,
1008 .transfer_get_formats = &vaapi_transfer_get_formats,
1009 .transfer_data_to = &vaapi_transfer_data_to,
1010 .transfer_data_from = &vaapi_transfer_data_from,
1011 .map_to = NULL,
1012 .map_from = &vaapi_map_from,
1013
1014 .pix_fmts = (const enum AVPixelFormat[]) {
1015 AV_PIX_FMT_VAAPI,
1016 AV_PIX_FMT_NONE
1017 },
1018};
1019