2 * This file is part of FFmpeg.
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.
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.
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
22 # include <va/va_x11.h>
25 # include <va/va_drm.h>
29 # include <va/va_drmcommon.h>
30 # include <drm_fourcc.h>
42 #include "hwcontext.h"
43 #include "hwcontext_internal.h"
44 #include "hwcontext_vaapi.h"
50 # include "hwcontext_drm.h"
53 typedef struct VAAPIDevicePriv {
61 typedef struct VAAPISurfaceFormat {
62 enum AVPixelFormat pix_fmt;
63 VAImageFormat image_format;
66 typedef struct VAAPIDeviceContext {
67 // Surface formats which can be used with this device.
68 VAAPISurfaceFormat *formats;
72 typedef struct VAAPIFramesContext {
73 // Surface attributes set at create time.
74 VASurfaceAttrib *attributes;
76 // RT format of the underlying surface (Intel driver ignores this anyway).
77 unsigned int rt_format;
78 // Whether vaDeriveImage works.
82 typedef struct VAAPIMapping {
83 // Handle to the derived or copied image which is mapped.
85 // The mapping flags actually used.
89 #define MAP(va, rt, av) { \
91 VA_RT_FORMAT_ ## rt, \
94 // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
95 // plane swap cases. The frame handling below tries to hide these.
98 unsigned int rt_format;
99 enum AVPixelFormat pix_fmt;
100 } vaapi_format_map[] = {
101 MAP(NV12, YUV420, NV12),
102 MAP(YV12, YUV420, YUV420P), // With U/V planes swapped.
103 MAP(IYUV, YUV420, YUV420P),
104 #ifdef VA_FOURCC_I420
105 MAP(I420, YUV420, YUV420P),
107 #ifdef VA_FOURCC_YV16
108 MAP(YV16, YUV422, YUV422P), // With U/V planes swapped.
110 MAP(422H, YUV422, YUV422P),
111 MAP(UYVY, YUV422, UYVY422),
112 MAP(YUY2, YUV422, YUYV422),
113 MAP(411P, YUV411, YUV411P),
114 MAP(422V, YUV422, YUV440P),
115 MAP(444P, YUV444, YUV444P),
116 MAP(Y800, YUV400, GRAY8),
117 #ifdef VA_FOURCC_P010
118 MAP(P010, YUV420_10BPP, P010),
120 MAP(BGRA, RGB32, BGRA),
121 MAP(BGRX, RGB32, BGR0),
122 MAP(RGBA, RGB32, RGBA),
123 MAP(RGBX, RGB32, RGB0),
124 #ifdef VA_FOURCC_ABGR
125 MAP(ABGR, RGB32, ABGR),
126 MAP(XBGR, RGB32, 0BGR),
128 MAP(ARGB, RGB32, ARGB),
129 MAP(XRGB, RGB32, 0RGB),
133 static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
136 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
137 if (vaapi_format_map[i].fourcc == fourcc)
138 return vaapi_format_map[i].pix_fmt;
139 return AV_PIX_FMT_NONE;
142 static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
143 enum AVPixelFormat pix_fmt,
144 VAImageFormat **image_format)
146 VAAPIDeviceContext *ctx = hwdev->internal->priv;
149 for (i = 0; i < ctx->nb_formats; i++) {
150 if (ctx->formats[i].pix_fmt == pix_fmt) {
152 *image_format = &ctx->formats[i].image_format;
156 return AVERROR(EINVAL);
159 static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
160 const void *hwconfig,
161 AVHWFramesConstraints *constraints)
163 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
164 const AVVAAPIHWConfig *config = hwconfig;
165 VAAPIDeviceContext *ctx = hwdev->internal->priv;
166 VASurfaceAttrib *attr_list = NULL;
168 enum AVPixelFormat pix_fmt;
170 int err, i, j, attr_count, pix_fmt_count;
173 !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
175 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
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);
184 attr_list = av_malloc(attr_count * sizeof(*attr_list));
186 err = AVERROR(ENOMEM);
190 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
191 attr_list, &attr_count);
192 if (vas != VA_STATUS_SUCCESS) {
193 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
194 "%d (%s).\n", vas, vaErrorStr(vas));
195 err = AVERROR(ENOSYS);
200 for (i = 0; i < attr_count; i++) {
201 switch (attr_list[i].type) {
202 case VASurfaceAttribPixelFormat:
203 fourcc = attr_list[i].value.value.i;
204 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
205 if (pix_fmt != AV_PIX_FMT_NONE) {
208 // Something unsupported - ignore.
211 case VASurfaceAttribMinWidth:
212 constraints->min_width = attr_list[i].value.value.i;
214 case VASurfaceAttribMinHeight:
215 constraints->min_height = attr_list[i].value.value.i;
217 case VASurfaceAttribMaxWidth:
218 constraints->max_width = attr_list[i].value.value.i;
220 case VASurfaceAttribMaxHeight:
221 constraints->max_height = attr_list[i].value.value.i;
225 if (pix_fmt_count == 0) {
226 // Nothing usable found. Presumably there exists something which
227 // works, so leave the set null to indicate unknown.
228 constraints->valid_sw_formats = NULL;
230 constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
232 if (!constraints->valid_sw_formats) {
233 err = AVERROR(ENOMEM);
237 for (i = j = 0; i < attr_count; i++) {
238 if (attr_list[i].type != VASurfaceAttribPixelFormat)
240 fourcc = attr_list[i].value.value.i;
241 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
242 if (pix_fmt != AV_PIX_FMT_NONE)
243 constraints->valid_sw_formats[j++] = pix_fmt;
245 av_assert0(j == pix_fmt_count);
246 constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
249 // No configuration supplied.
250 // Return the full set of image formats known by the implementation.
251 constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
253 if (!constraints->valid_sw_formats) {
254 err = AVERROR(ENOMEM);
257 for (i = 0; i < ctx->nb_formats; i++)
258 constraints->valid_sw_formats[i] = ctx->formats[i].pix_fmt;
259 constraints->valid_sw_formats[i] = AV_PIX_FMT_NONE;
262 constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
263 if (!constraints->valid_hw_formats) {
264 err = AVERROR(ENOMEM);
267 constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
268 constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
272 av_freep(&attr_list);
276 static const struct {
277 const char *friendly_name;
278 const char *match_string;
280 } vaapi_driver_quirks_table[] = {
282 "Intel i965 (Quick Sync)",
284 AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
289 AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
293 "Splitted-Desktop Systems VDPAU backend for VA-API",
294 AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES,
298 static int vaapi_device_init(AVHWDeviceContext *hwdev)
300 VAAPIDeviceContext *ctx = hwdev->internal->priv;
301 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
302 VAImageFormat *image_list = NULL;
304 const char *vendor_string;
305 int err, i, image_count;
306 enum AVPixelFormat pix_fmt;
309 image_count = vaMaxNumImageFormats(hwctx->display);
310 if (image_count <= 0) {
314 image_list = av_malloc(image_count * sizeof(*image_list));
316 err = AVERROR(ENOMEM);
319 vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
320 if (vas != VA_STATUS_SUCCESS) {
325 ctx->formats = av_malloc(image_count * sizeof(*ctx->formats));
327 err = AVERROR(ENOMEM);
331 for (i = 0; i < image_count; i++) {
332 fourcc = image_list[i].fourcc;
333 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
334 if (pix_fmt == AV_PIX_FMT_NONE) {
335 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
338 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
339 fourcc, av_get_pix_fmt_name(pix_fmt));
340 ctx->formats[ctx->nb_formats].pix_fmt = pix_fmt;
341 ctx->formats[ctx->nb_formats].image_format = image_list[i];
346 if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
347 av_log(hwdev, AV_LOG_VERBOSE, "Not detecting driver: "
348 "quirks set by user.\n");
350 // Detect the driver in use and set quirk flags if necessary.
351 vendor_string = vaQueryVendorString(hwctx->display);
352 hwctx->driver_quirks = 0;
354 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
355 if (strstr(vendor_string,
356 vaapi_driver_quirks_table[i].match_string)) {
357 av_log(hwdev, AV_LOG_VERBOSE, "Matched \"%s\" as known "
358 "driver \"%s\".\n", vendor_string,
359 vaapi_driver_quirks_table[i].friendly_name);
360 hwctx->driver_quirks |=
361 vaapi_driver_quirks_table[i].quirks;
365 if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
366 av_log(hwdev, AV_LOG_VERBOSE, "Unknown driver \"%s\", "
367 "assuming standard behaviour.\n", vendor_string);
375 av_freep(&ctx->formats);
380 static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
382 VAAPIDeviceContext *ctx = hwdev->internal->priv;
384 av_freep(&ctx->formats);
387 static void vaapi_buffer_free(void *opaque, uint8_t *data)
389 AVHWFramesContext *hwfc = opaque;
390 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
391 VASurfaceID surface_id;
394 surface_id = (VASurfaceID)(uintptr_t)data;
396 vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
397 if (vas != VA_STATUS_SUCCESS) {
398 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
399 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
403 static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
405 AVHWFramesContext *hwfc = opaque;
406 VAAPIFramesContext *ctx = hwfc->internal->priv;
407 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
408 AVVAAPIFramesContext *avfc = hwfc->hwctx;
409 VASurfaceID surface_id;
413 if (hwfc->initial_pool_size > 0 &&
414 avfc->nb_surfaces >= hwfc->initial_pool_size)
417 vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
418 hwfc->width, hwfc->height,
420 ctx->attributes, ctx->nb_attributes);
421 if (vas != VA_STATUS_SUCCESS) {
422 av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
423 "%d (%s).\n", vas, vaErrorStr(vas));
426 av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
428 ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
429 sizeof(surface_id), &vaapi_buffer_free,
430 hwfc, AV_BUFFER_FLAG_READONLY);
432 vaDestroySurfaces(hwctx->display, &surface_id, 1);
436 if (hwfc->initial_pool_size > 0) {
437 // This is a fixed-size pool, so we must still be in the initial
438 // allocation sequence.
439 av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
440 avfc->surface_ids[avfc->nb_surfaces] = surface_id;
447 static int vaapi_frames_init(AVHWFramesContext *hwfc)
449 AVVAAPIFramesContext *avfc = hwfc->hwctx;
450 VAAPIFramesContext *ctx = hwfc->internal->priv;
451 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
452 VAImageFormat *expected_format;
453 AVBufferRef *test_surface = NULL;
454 VASurfaceID test_surface_id;
458 unsigned int fourcc, rt_format;
460 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
461 if (vaapi_format_map[i].pix_fmt == hwfc->sw_format) {
462 fourcc = vaapi_format_map[i].fourcc;
463 rt_format = vaapi_format_map[i].rt_format;
467 if (i >= FF_ARRAY_ELEMS(vaapi_format_map)) {
468 av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
469 av_get_pix_fmt_name(hwfc->sw_format));
470 return AVERROR(EINVAL);
474 if (!(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
475 int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
476 int need_pixel_format = 1;
477 for (i = 0; i < avfc->nb_attributes; i++) {
478 if (avfc->attributes[i].type == VASurfaceAttribMemoryType)
479 need_memory_type = 0;
480 if (avfc->attributes[i].type == VASurfaceAttribPixelFormat)
481 need_pixel_format = 0;
484 avfc->nb_attributes + need_memory_type + need_pixel_format;
486 ctx->attributes = av_malloc(ctx->nb_attributes *
487 sizeof(*ctx->attributes));
488 if (!ctx->attributes) {
489 err = AVERROR(ENOMEM);
493 for (i = 0; i < avfc->nb_attributes; i++)
494 ctx->attributes[i] = avfc->attributes[i];
495 if (need_memory_type) {
496 ctx->attributes[i++] = (VASurfaceAttrib) {
497 .type = VASurfaceAttribMemoryType,
498 .flags = VA_SURFACE_ATTRIB_SETTABLE,
499 .value.type = VAGenericValueTypeInteger,
500 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
503 if (need_pixel_format) {
504 ctx->attributes[i++] = (VASurfaceAttrib) {
505 .type = VASurfaceAttribPixelFormat,
506 .flags = VA_SURFACE_ATTRIB_SETTABLE,
507 .value.type = VAGenericValueTypeInteger,
508 .value.value.i = fourcc,
511 av_assert0(i == ctx->nb_attributes);
513 ctx->attributes = NULL;
514 ctx->nb_attributes = 0;
517 ctx->rt_format = rt_format;
519 if (hwfc->initial_pool_size > 0) {
520 // This pool will be usable as a render target, so we need to store
521 // all of the surface IDs somewhere that vaCreateContext() calls
522 // will be able to access them.
523 avfc->nb_surfaces = 0;
524 avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
525 sizeof(*avfc->surface_ids));
526 if (!avfc->surface_ids) {
527 err = AVERROR(ENOMEM);
531 // This pool allows dynamic sizing, and will not be usable as a
533 avfc->nb_surfaces = 0;
534 avfc->surface_ids = NULL;
537 hwfc->internal->pool_internal =
538 av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
539 &vaapi_pool_alloc, NULL);
540 if (!hwfc->internal->pool_internal) {
541 av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
542 err = AVERROR(ENOMEM);
547 // Allocate a single surface to test whether vaDeriveImage() is going
548 // to work for the specific configuration.
550 test_surface = av_buffer_pool_get(hwfc->pool);
552 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
553 "user-configured buffer pool.\n");
554 err = AVERROR(ENOMEM);
558 test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
560 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
561 "internal buffer pool.\n");
562 err = AVERROR(ENOMEM);
566 test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
568 ctx->derive_works = 0;
570 err = vaapi_get_image_format(hwfc->device_ctx,
571 hwfc->sw_format, &expected_format);
573 vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
574 if (vas == VA_STATUS_SUCCESS) {
575 if (expected_format->fourcc == test_image.format.fourcc) {
576 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
577 ctx->derive_works = 1;
579 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
580 "derived image format %08x does not match "
581 "expected format %08x.\n",
582 expected_format->fourcc, test_image.format.fourcc);
584 vaDestroyImage(hwctx->display, test_image.image_id);
586 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
587 "deriving image does not work: "
588 "%d (%s).\n", vas, vaErrorStr(vas));
591 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
592 "image format is not supported.\n");
595 av_buffer_unref(&test_surface);
599 av_buffer_unref(&test_surface);
600 av_freep(&avfc->surface_ids);
601 av_freep(&ctx->attributes);
605 static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
607 AVVAAPIFramesContext *avfc = hwfc->hwctx;
608 VAAPIFramesContext *ctx = hwfc->internal->priv;
610 av_freep(&avfc->surface_ids);
611 av_freep(&ctx->attributes);
614 static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
616 frame->buf[0] = av_buffer_pool_get(hwfc->pool);
618 return AVERROR(ENOMEM);
620 frame->data[3] = frame->buf[0]->data;
621 frame->format = AV_PIX_FMT_VAAPI;
622 frame->width = hwfc->width;
623 frame->height = hwfc->height;
628 static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
629 enum AVHWFrameTransferDirection dir,
630 enum AVPixelFormat **formats)
632 VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
633 enum AVPixelFormat *pix_fmts;
634 int i, k, sw_format_available;
636 sw_format_available = 0;
637 for (i = 0; i < ctx->nb_formats; i++) {
638 if (ctx->formats[i].pix_fmt == hwfc->sw_format)
639 sw_format_available = 1;
642 pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
644 return AVERROR(ENOMEM);
646 if (sw_format_available) {
647 pix_fmts[0] = hwfc->sw_format;
652 for (i = 0; i < ctx->nb_formats; i++) {
653 if (ctx->formats[i].pix_fmt == hwfc->sw_format)
655 av_assert0(k < ctx->nb_formats);
656 pix_fmts[k++] = ctx->formats[i].pix_fmt;
658 pix_fmts[k] = AV_PIX_FMT_NONE;
664 static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
665 HWMapDescriptor *hwmap)
667 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
668 VAAPIMapping *map = hwmap->priv;
669 VASurfaceID surface_id;
672 surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
673 av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
675 vas = vaUnmapBuffer(hwctx->display, map->image.buf);
676 if (vas != VA_STATUS_SUCCESS) {
677 av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
678 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
681 if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
682 !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
683 vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
684 0, 0, hwfc->width, hwfc->height,
685 0, 0, hwfc->width, hwfc->height);
686 if (vas != VA_STATUS_SUCCESS) {
687 av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
688 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
692 vas = vaDestroyImage(hwctx->display, map->image.image_id);
693 if (vas != VA_STATUS_SUCCESS) {
694 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
695 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
701 static int vaapi_map_frame(AVHWFramesContext *hwfc,
702 AVFrame *dst, const AVFrame *src, int flags)
704 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
705 VAAPIFramesContext *ctx = hwfc->internal->priv;
706 VASurfaceID surface_id;
707 VAImageFormat *image_format;
710 void *address = NULL;
713 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
714 av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
716 if (!ctx->derive_works && (flags & AV_HWFRAME_MAP_DIRECT)) {
717 // Requested direct mapping but it is not possible.
718 return AVERROR(EINVAL);
720 if (dst->format == AV_PIX_FMT_NONE)
721 dst->format = hwfc->sw_format;
722 if (dst->format != hwfc->sw_format && (flags & AV_HWFRAME_MAP_DIRECT)) {
723 // Requested direct mapping but the formats do not match.
724 return AVERROR(EINVAL);
727 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
729 // Requested format is not a valid output format.
730 return AVERROR(EINVAL);
733 map = av_malloc(sizeof(*map));
735 return AVERROR(ENOMEM);
737 map->image.image_id = VA_INVALID_ID;
739 vas = vaSyncSurface(hwctx->display, surface_id);
740 if (vas != VA_STATUS_SUCCESS) {
741 av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
742 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
747 // The memory which we map using derive need not be connected to the CPU
748 // in a way conducive to fast access. On Gen7-Gen9 Intel graphics, the
749 // memory is mappable but not cached, so normal memcpy()-like access is
750 // very slow to read it (but writing is ok). It is possible to read much
751 // faster with a copy routine which is aware of the limitation, but we
752 // assume for now that the user is not aware of that and would therefore
753 // prefer not to be given direct-mapped memory if they request read access.
754 if (ctx->derive_works && dst->format == hwfc->sw_format &&
755 ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
756 vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
757 if (vas != VA_STATUS_SUCCESS) {
758 av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
759 "surface %#x: %d (%s).\n",
760 surface_id, vas, vaErrorStr(vas));
764 if (map->image.format.fourcc != image_format->fourcc) {
765 av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
766 "is in wrong format: expected %#08x, got %#08x.\n",
767 surface_id, image_format->fourcc, map->image.format.fourcc);
771 map->flags |= AV_HWFRAME_MAP_DIRECT;
773 vas = vaCreateImage(hwctx->display, image_format,
774 hwfc->width, hwfc->height, &map->image);
775 if (vas != VA_STATUS_SUCCESS) {
776 av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
777 "surface %#x: %d (%s).\n",
778 surface_id, vas, vaErrorStr(vas));
782 if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
783 vas = vaGetImage(hwctx->display, surface_id, 0, 0,
784 hwfc->width, hwfc->height, map->image.image_id);
785 if (vas != VA_STATUS_SUCCESS) {
786 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
787 "surface %#x: %d (%s).\n",
788 surface_id, vas, vaErrorStr(vas));
795 vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
796 if (vas != VA_STATUS_SUCCESS) {
797 av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
798 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
803 err = ff_hwframe_map_create(src->hw_frames_ctx,
804 dst, src, &vaapi_unmap_frame, map);
808 dst->width = src->width;
809 dst->height = src->height;
811 for (i = 0; i < map->image.num_planes; i++) {
812 dst->data[i] = (uint8_t*)address + map->image.offsets[i];
813 dst->linesize[i] = map->image.pitches[i];
816 #ifdef VA_FOURCC_YV16
817 map->image.format.fourcc == VA_FOURCC_YV16 ||
819 map->image.format.fourcc == VA_FOURCC_YV12) {
820 // Chroma planes are YVU rather than YUV, so swap them.
821 FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
829 vaUnmapBuffer(hwctx->display, map->image.buf);
830 if (map->image.image_id != VA_INVALID_ID)
831 vaDestroyImage(hwctx->display, map->image.image_id);
837 static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
838 AVFrame *dst, const AVFrame *src)
843 if (dst->width > hwfc->width || dst->height > hwfc->height)
844 return AVERROR(EINVAL);
846 map = av_frame_alloc();
848 return AVERROR(ENOMEM);
849 map->format = dst->format;
851 err = vaapi_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
855 map->width = dst->width;
856 map->height = dst->height;
858 err = av_frame_copy(dst, map);
868 static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
869 AVFrame *dst, const AVFrame *src)
874 if (src->width > hwfc->width || src->height > hwfc->height)
875 return AVERROR(EINVAL);
877 map = av_frame_alloc();
879 return AVERROR(ENOMEM);
880 map->format = src->format;
882 err = vaapi_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
886 map->width = src->width;
887 map->height = src->height;
889 err = av_frame_copy(map, src);
899 static int vaapi_map_to_memory(AVHWFramesContext *hwfc, AVFrame *dst,
900 const AVFrame *src, int flags)
904 if (dst->format != AV_PIX_FMT_NONE) {
905 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, NULL);
907 return AVERROR(ENOSYS);
910 err = vaapi_map_frame(hwfc, dst, src, flags);
914 err = av_frame_copy_props(dst, src);
923 #define DRM_MAP(va, layers, ...) { \
928 static const struct {
930 int nb_layer_formats;
931 uint32_t layer_formats[AV_DRM_MAX_PLANES];
932 } vaapi_drm_format_map[] = {
934 DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88),
936 DRM_MAP(NV12, 1, DRM_FORMAT_NV12),
937 #if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16)
938 DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
940 DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888),
941 DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888),
942 DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888),
943 DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888),
944 #ifdef VA_FOURCC_ABGR
945 DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888),
946 DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888),
948 DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888),
949 DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888),
953 static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
954 HWMapDescriptor *hwmap)
956 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
958 VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
960 av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id);
962 vaDestroySurfaces(dst_dev->display, &surface_id, 1);
965 static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
966 const AVFrame *src, int flags)
968 AVHWFramesContext *dst_fc =
969 (AVHWFramesContext*)dst->hw_frames_ctx->data;
970 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
971 const AVDRMFrameDescriptor *desc;
972 VASurfaceID surface_id;
974 uint32_t va_fourcc, va_rt_format;
977 unsigned long buffer_handle;
978 VASurfaceAttribExternalBuffers buffer_desc;
979 VASurfaceAttrib attrs[2] = {
981 .type = VASurfaceAttribMemoryType,
982 .flags = VA_SURFACE_ATTRIB_SETTABLE,
983 .value.type = VAGenericValueTypeInteger,
984 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
987 .type = VASurfaceAttribExternalBufferDescriptor,
988 .flags = VA_SURFACE_ATTRIB_SETTABLE,
989 .value.type = VAGenericValueTypePointer,
990 .value.value.p = &buffer_desc,
994 desc = (AVDRMFrameDescriptor*)src->data[0];
996 if (desc->nb_objects != 1) {
997 av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames "
998 "made from a single DRM object.\n");
999 return AVERROR(EINVAL);
1003 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1004 if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats)
1006 for (j = 0; j < desc->nb_layers; j++) {
1007 if (desc->layers[j].format !=
1008 vaapi_drm_format_map[i].layer_formats[j])
1011 if (j != desc->nb_layers)
1013 va_fourcc = vaapi_drm_format_map[i].va_fourcc;
1017 av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported "
1019 return AVERROR(EINVAL);
1022 av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
1023 "%08x.\n", desc->objects[0].fd, va_fourcc);
1025 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
1026 if (vaapi_format_map[i].fourcc == va_fourcc)
1027 va_rt_format = vaapi_format_map[i].rt_format;
1030 buffer_handle = desc->objects[0].fd;
1031 buffer_desc.pixel_format = va_fourcc;
1032 buffer_desc.width = src_fc->width;
1033 buffer_desc.height = src_fc->height;
1034 buffer_desc.data_size = desc->objects[0].size;
1035 buffer_desc.buffers = &buffer_handle;
1036 buffer_desc.num_buffers = 1;
1037 buffer_desc.flags = 0;
1040 for (i = 0; i < desc->nb_layers; i++) {
1041 for (j = 0; j < desc->layers[i].nb_planes; j++) {
1042 buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
1043 buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
1047 buffer_desc.num_planes = k;
1049 vas = vaCreateSurfaces(dst_dev->display, va_rt_format,
1050 src->width, src->height,
1052 attrs, FF_ARRAY_ELEMS(attrs));
1053 if (vas != VA_STATUS_SUCCESS) {
1054 av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
1055 "object: %d (%s).\n", vas, vaErrorStr(vas));
1056 return AVERROR(EIO);
1058 av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
1060 err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
1061 &vaapi_unmap_from_drm,
1062 (void*)(uintptr_t)surface_id);
1066 dst->width = src->width;
1067 dst->height = src->height;
1068 dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
1070 av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
1071 "surface %#x.\n", desc->objects[0].fd, surface_id);
1076 static void vaapi_unmap_to_drm(AVHWFramesContext *dst_fc,
1077 HWMapDescriptor *hwmap)
1079 AVDRMFrameDescriptor *drm_desc = hwmap->priv;
1082 for (i = 0; i < drm_desc->nb_objects; i++)
1083 close(drm_desc->objects[i].fd);
1085 av_freep(&drm_desc);
1088 static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
1089 const AVFrame *src, int flags)
1091 #if VA_CHECK_VERSION(1, 1, 0)
1092 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1093 VASurfaceID surface_id;
1095 VADRMPRIMESurfaceDescriptor va_desc;
1096 AVDRMFrameDescriptor *drm_desc = NULL;
1099 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1101 vas = vaExportSurfaceHandle(hwctx->display, surface_id,
1102 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
1103 VA_EXPORT_SURFACE_READ_ONLY |
1104 VA_EXPORT_SURFACE_SEPARATE_LAYERS,
1106 if (vas != VA_STATUS_SUCCESS) {
1107 if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
1108 return AVERROR(ENOSYS);
1109 av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
1110 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
1111 return AVERROR(EIO);
1114 drm_desc = av_mallocz(sizeof(*drm_desc));
1116 err = AVERROR(ENOMEM);
1120 // By some bizarre coincidence, these structures are very similar...
1121 drm_desc->nb_objects = va_desc.num_objects;
1122 for (i = 0; i < va_desc.num_objects; i++) {
1123 drm_desc->objects[i].fd = va_desc.objects[i].fd;
1124 drm_desc->objects[i].size = va_desc.objects[i].size;
1125 drm_desc->objects[i].format_modifier =
1126 va_desc.objects[i].drm_format_modifier;
1128 drm_desc->nb_layers = va_desc.num_layers;
1129 for (i = 0; i < va_desc.num_layers; i++) {
1130 drm_desc->layers[i].format = va_desc.layers[i].drm_format;
1131 drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes;
1132 for (j = 0; j < va_desc.layers[i].num_planes; j++) {
1133 drm_desc->layers[i].planes[j].object_index =
1134 va_desc.layers[i].object_index[j];
1135 drm_desc->layers[i].planes[j].offset =
1136 va_desc.layers[i].offset[j];
1137 drm_desc->layers[i].planes[j].pitch =
1138 va_desc.layers[i].pitch[j];
1142 err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
1143 &vaapi_unmap_to_drm, drm_desc);
1147 dst->width = src->width;
1148 dst->height = src->height;
1149 dst->data[0] = (uint8_t*)drm_desc;
1154 for (i = 0; i < va_desc.num_objects; i++)
1155 close(va_desc.objects[i].fd);
1156 av_freep(&drm_desc);
1159 // Older versions without vaExportSurfaceHandle() are not supported -
1160 // in theory this is possible with a combination of vaDeriveImage()
1161 // and vaAcquireBufferHandle(), but it doesn't carry enough metadata
1162 // to actually use the result in a generic way.
1163 return AVERROR(ENOSYS);
1168 static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
1169 const AVFrame *src, int flags)
1171 switch (src->format) {
1173 case AV_PIX_FMT_DRM_PRIME:
1174 return vaapi_map_from_drm(hwfc, dst, src, flags);
1177 return AVERROR(ENOSYS);
1181 static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
1182 const AVFrame *src, int flags)
1184 switch (dst->format) {
1186 case AV_PIX_FMT_DRM_PRIME:
1187 return vaapi_map_to_drm(hwfc, dst, src, flags);
1190 return vaapi_map_to_memory(hwfc, dst, src, flags);
1194 static void vaapi_device_free(AVHWDeviceContext *ctx)
1196 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1197 VAAPIDevicePriv *priv = ctx->user_opaque;
1200 vaTerminate(hwctx->display);
1203 if (priv->x11_display)
1204 XCloseDisplay(priv->x11_display);
1207 if (priv->drm_fd >= 0)
1208 close(priv->drm_fd);
1214 static void vaapi_device_log_error(void *context, const char *message)
1216 AVHWDeviceContext *ctx = context;
1218 av_log(ctx, AV_LOG_ERROR, "libva: %s", message);
1221 static void vaapi_device_log_info(void *context, const char *message)
1223 AVHWDeviceContext *ctx = context;
1225 av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message);
1229 static int vaapi_device_connect(AVHWDeviceContext *ctx,
1232 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1237 vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
1238 vaSetInfoCallback (display, &vaapi_device_log_info, ctx);
1241 hwctx->display = display;
1243 vas = vaInitialize(display, &major, &minor);
1244 if (vas != VA_STATUS_SUCCESS) {
1245 av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
1246 "connection: %d (%s).\n", vas, vaErrorStr(vas));
1247 return AVERROR(EIO);
1249 av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
1250 "version %d.%d\n", major, minor);
1255 static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
1256 AVDictionary *opts, int flags)
1258 VAAPIDevicePriv *priv;
1259 VADisplay display = NULL;
1261 priv = av_mallocz(sizeof(*priv));
1263 return AVERROR(ENOMEM);
1267 ctx->user_opaque = priv;
1268 ctx->free = vaapi_device_free;
1271 if (!display && !(device && device[0] == '/')) {
1272 // Try to open the device as an X11 display.
1273 priv->x11_display = XOpenDisplay(device);
1274 if (!priv->x11_display) {
1275 av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
1276 "%s.\n", XDisplayName(device));
1278 display = vaGetDisplay(priv->x11_display);
1280 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1281 "from X11 display %s.\n", XDisplayName(device));
1282 return AVERROR_UNKNOWN;
1285 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1286 "X11 display %s.\n", XDisplayName(device));
1293 // Try to open the device as a DRM path.
1294 // Default to using the first render node if the user did not
1296 const char *path = device ? device : "/dev/dri/renderD128";
1297 priv->drm_fd = open(path, O_RDWR);
1298 if (priv->drm_fd < 0) {
1299 av_log(ctx, AV_LOG_VERBOSE, "Cannot open DRM device %s.\n",
1302 display = vaGetDisplayDRM(priv->drm_fd);
1304 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1305 "from DRM device %s.\n", path);
1306 return AVERROR_UNKNOWN;
1309 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1310 "DRM device %s.\n", path);
1316 av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1317 "device: %s.\n", device ? device : "");
1318 return AVERROR(EINVAL);
1321 return vaapi_device_connect(ctx, display);
1324 static int vaapi_device_derive(AVHWDeviceContext *ctx,
1325 AVHWDeviceContext *src_ctx, int flags)
1328 if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
1329 AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
1331 VAAPIDevicePriv *priv;
1333 if (src_hwctx->fd < 0) {
1334 av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
1335 "device to derive a VA display from.\n");
1336 return AVERROR(EINVAL);
1339 priv = av_mallocz(sizeof(*priv));
1341 return AVERROR(ENOMEM);
1343 // Inherits the fd from the source context, which will close it.
1346 ctx->user_opaque = priv;
1347 ctx->free = &vaapi_device_free;
1349 display = vaGetDisplayDRM(src_hwctx->fd);
1351 av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
1353 return AVERROR(EIO);
1356 return vaapi_device_connect(ctx, display);
1359 return AVERROR(ENOSYS);
1362 const HWContextType ff_hwcontext_type_vaapi = {
1363 .type = AV_HWDEVICE_TYPE_VAAPI,
1366 .device_hwctx_size = sizeof(AVVAAPIDeviceContext),
1367 .device_priv_size = sizeof(VAAPIDeviceContext),
1368 .device_hwconfig_size = sizeof(AVVAAPIHWConfig),
1369 .frames_hwctx_size = sizeof(AVVAAPIFramesContext),
1370 .frames_priv_size = sizeof(VAAPIFramesContext),
1372 .device_create = &vaapi_device_create,
1373 .device_derive = &vaapi_device_derive,
1374 .device_init = &vaapi_device_init,
1375 .device_uninit = &vaapi_device_uninit,
1376 .frames_get_constraints = &vaapi_frames_get_constraints,
1377 .frames_init = &vaapi_frames_init,
1378 .frames_uninit = &vaapi_frames_uninit,
1379 .frames_get_buffer = &vaapi_get_buffer,
1380 .transfer_get_formats = &vaapi_transfer_get_formats,
1381 .transfer_data_to = &vaapi_transfer_data_to,
1382 .transfer_data_from = &vaapi_transfer_data_from,
1383 .map_to = &vaapi_map_to,
1384 .map_from = &vaapi_map_from,
1386 .pix_fmts = (const enum AVPixelFormat[]) {