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
24 # include <initguid.h>
26 # include "compat/w32dlfcn.h"
27 # include <va/va_win32.h>
28 typedef HRESULT (WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
31 # include <va/va_x11.h>
34 # include <va/va_drm.h>
38 # include <va/va_drmcommon.h>
40 # include <drm_fourcc.h>
41 # ifndef DRM_FORMAT_MOD_INVALID
42 # define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
55 #include "hwcontext.h"
56 #include "hwcontext_drm.h"
57 #include "hwcontext_internal.h"
58 #include "hwcontext_vaapi.h"
64 typedef struct VAAPIDevicePriv {
72 typedef struct VAAPISurfaceFormat {
73 enum AVPixelFormat pix_fmt;
74 VAImageFormat image_format;
77 typedef struct VAAPIDeviceContext {
78 // Surface formats which can be used with this device.
79 VAAPISurfaceFormat *formats;
83 typedef struct VAAPIFramesContext {
84 // Surface attributes set at create time.
85 VASurfaceAttrib *attributes;
87 // RT format of the underlying surface (Intel driver ignores this anyway).
88 unsigned int rt_format;
89 // Whether vaDeriveImage works.
91 // Caches whether VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 is unsupported for
93 int prime_2_import_unsupported;
96 typedef struct VAAPIMapping {
97 // Handle to the derived or copied image which is mapped.
99 // The mapping flags actually used.
103 typedef struct VAAPIFormat {
105 unsigned int rt_format;
106 enum AVPixelFormat pix_fmt;
107 int chroma_planes_swapped;
108 } VAAPIFormatDescriptor;
110 #define MAP(va, rt, av, swap_uv) { \
112 VA_RT_FORMAT_ ## rt, \
116 // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
117 // plane swap cases. The frame handling below tries to hide these.
118 static const VAAPIFormatDescriptor vaapi_format_map[] = {
119 MAP(NV12, YUV420, NV12, 0),
120 #ifdef VA_FOURCC_I420
121 MAP(I420, YUV420, YUV420P, 0),
123 MAP(YV12, YUV420, YUV420P, 1),
124 MAP(IYUV, YUV420, YUV420P, 0),
125 MAP(422H, YUV422, YUV422P, 0),
126 #ifdef VA_FOURCC_YV16
127 MAP(YV16, YUV422, YUV422P, 1),
129 MAP(UYVY, YUV422, UYVY422, 0),
130 MAP(YUY2, YUV422, YUYV422, 0),
131 #ifdef VA_FOURCC_Y210
132 MAP(Y210, YUV422_10, Y210, 0),
134 #ifdef VA_FOURCC_Y212
135 MAP(Y212, YUV422_12, Y212, 0),
137 MAP(411P, YUV411, YUV411P, 0),
138 MAP(422V, YUV422, YUV440P, 0),
139 MAP(444P, YUV444, YUV444P, 0),
140 #ifdef VA_FOURCC_XYUV
141 MAP(XYUV, YUV444, VUYX, 0),
143 MAP(Y800, YUV400, GRAY8, 0),
144 #ifdef VA_FOURCC_P010
145 MAP(P010, YUV420_10BPP, P010, 0),
147 #ifdef VA_FOURCC_P012
148 MAP(P012, YUV420_12, P012, 0),
150 MAP(BGRA, RGB32, BGRA, 0),
151 MAP(BGRX, RGB32, BGR0, 0),
152 MAP(RGBA, RGB32, RGBA, 0),
153 MAP(RGBX, RGB32, RGB0, 0),
154 #ifdef VA_FOURCC_ABGR
155 MAP(ABGR, RGB32, ABGR, 0),
156 MAP(XBGR, RGB32, 0BGR, 0),
158 MAP(ARGB, RGB32, ARGB, 0),
159 MAP(XRGB, RGB32, 0RGB, 0),
160 #ifdef VA_FOURCC_X2R10G10B10
161 MAP(X2R10G10B10, RGB32_10, X2RGB10, 0),
163 #ifdef VA_FOURCC_Y410
164 // libva doesn't include a fourcc for XV30 and the driver only declares
165 // support for Y410, so we must fudge the mapping here.
166 MAP(Y410, YUV444_10, XV30, 0),
168 #ifdef VA_FOURCC_Y412
169 // libva doesn't include a fourcc for XV36 and the driver only declares
170 // support for Y412, so we must fudge the mapping here.
171 MAP(Y412, YUV444_12, XV36, 0),
176 static const VAAPIFormatDescriptor *
177 vaapi_format_from_fourcc(unsigned int fourcc)
180 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
181 if (vaapi_format_map[i].fourcc == fourcc)
182 return &vaapi_format_map[i];
186 static const VAAPIFormatDescriptor *
187 vaapi_format_from_pix_fmt(enum AVPixelFormat pix_fmt)
190 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
191 if (vaapi_format_map[i].pix_fmt == pix_fmt)
192 return &vaapi_format_map[i];
196 static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
198 const VAAPIFormatDescriptor *desc;
199 desc = vaapi_format_from_fourcc(fourcc);
201 return desc->pix_fmt;
203 return AV_PIX_FMT_NONE;
206 static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
207 enum AVPixelFormat pix_fmt,
208 VAImageFormat **image_format)
210 VAAPIDeviceContext *ctx = hwdev->internal->priv;
213 for (i = 0; i < ctx->nb_formats; i++) {
214 if (ctx->formats[i].pix_fmt == pix_fmt) {
216 *image_format = &ctx->formats[i].image_format;
220 return AVERROR(ENOSYS);
223 static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
224 const void *hwconfig,
225 AVHWFramesConstraints *constraints)
227 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
228 const AVVAAPIHWConfig *config = hwconfig;
229 VAAPIDeviceContext *ctx = hwdev->internal->priv;
230 VASurfaceAttrib *attr_list = NULL;
232 enum AVPixelFormat pix_fmt;
234 int err, i, j, attr_count, pix_fmt_count;
237 !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
239 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
241 if (vas != VA_STATUS_SUCCESS) {
242 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
243 "%d (%s).\n", vas, vaErrorStr(vas));
244 err = AVERROR(ENOSYS);
248 attr_list = av_malloc(attr_count * sizeof(*attr_list));
250 err = AVERROR(ENOMEM);
254 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
255 attr_list, &attr_count);
256 if (vas != VA_STATUS_SUCCESS) {
257 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
258 "%d (%s).\n", vas, vaErrorStr(vas));
259 err = AVERROR(ENOSYS);
264 for (i = 0; i < attr_count; i++) {
265 switch (attr_list[i].type) {
266 case VASurfaceAttribPixelFormat:
267 fourcc = attr_list[i].value.value.i;
268 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
269 if (pix_fmt != AV_PIX_FMT_NONE) {
272 // Something unsupported - ignore.
275 case VASurfaceAttribMinWidth:
276 constraints->min_width = attr_list[i].value.value.i;
278 case VASurfaceAttribMinHeight:
279 constraints->min_height = attr_list[i].value.value.i;
281 case VASurfaceAttribMaxWidth:
282 constraints->max_width = attr_list[i].value.value.i;
284 case VASurfaceAttribMaxHeight:
285 constraints->max_height = attr_list[i].value.value.i;
289 if (pix_fmt_count == 0) {
290 // Nothing usable found. Presumably there exists something which
291 // works, so leave the set null to indicate unknown.
292 constraints->valid_sw_formats = NULL;
294 constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
296 if (!constraints->valid_sw_formats) {
297 err = AVERROR(ENOMEM);
301 for (i = j = 0; i < attr_count; i++) {
304 if (attr_list[i].type != VASurfaceAttribPixelFormat)
306 fourcc = attr_list[i].value.value.i;
307 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
309 if (pix_fmt == AV_PIX_FMT_NONE)
312 for (k = 0; k < j; k++) {
313 if (constraints->valid_sw_formats[k] == pix_fmt)
318 constraints->valid_sw_formats[j++] = pix_fmt;
320 constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
323 // No configuration supplied.
324 // Return the full set of image formats known by the implementation.
325 constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
327 if (!constraints->valid_sw_formats) {
328 err = AVERROR(ENOMEM);
331 for (i = j = 0; i < ctx->nb_formats; i++) {
334 for (k = 0; k < j; k++) {
335 if (constraints->valid_sw_formats[k] == ctx->formats[i].pix_fmt)
340 constraints->valid_sw_formats[j++] = ctx->formats[i].pix_fmt;
343 constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
346 constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
347 if (!constraints->valid_hw_formats) {
348 err = AVERROR(ENOMEM);
351 constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
352 constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
356 av_freep(&attr_list);
360 static const struct {
361 const char *friendly_name;
362 const char *match_string;
364 } vaapi_driver_quirks_table[] = {
365 #if !VA_CHECK_VERSION(1, 0, 0)
366 // The i965 driver did not conform before version 2.0.
368 "Intel i965 (Quick Sync)",
370 AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
376 AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
380 "Splitted-Desktop Systems VDPAU backend for VA-API",
381 AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES,
385 static int vaapi_device_init(AVHWDeviceContext *hwdev)
387 VAAPIDeviceContext *ctx = hwdev->internal->priv;
388 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
389 VAImageFormat *image_list = NULL;
391 const char *vendor_string;
392 int err, i, image_count;
393 enum AVPixelFormat pix_fmt;
396 image_count = vaMaxNumImageFormats(hwctx->display);
397 if (image_count <= 0) {
401 image_list = av_malloc(image_count * sizeof(*image_list));
403 err = AVERROR(ENOMEM);
406 vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
407 if (vas != VA_STATUS_SUCCESS) {
412 ctx->formats = av_malloc(image_count * sizeof(*ctx->formats));
414 err = AVERROR(ENOMEM);
418 for (i = 0; i < image_count; i++) {
419 fourcc = image_list[i].fourcc;
420 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
421 if (pix_fmt == AV_PIX_FMT_NONE) {
422 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
425 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
426 fourcc, av_get_pix_fmt_name(pix_fmt));
427 ctx->formats[ctx->nb_formats].pix_fmt = pix_fmt;
428 ctx->formats[ctx->nb_formats].image_format = image_list[i];
433 vendor_string = vaQueryVendorString(hwctx->display);
435 av_log(hwdev, AV_LOG_VERBOSE, "VAAPI driver: %s.\n", vendor_string);
437 if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
438 av_log(hwdev, AV_LOG_VERBOSE, "Using quirks set by user (%#x).\n",
439 hwctx->driver_quirks);
441 // Detect the driver in use and set quirk flags if necessary.
442 hwctx->driver_quirks = 0;
444 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
445 if (strstr(vendor_string,
446 vaapi_driver_quirks_table[i].match_string)) {
447 av_log(hwdev, AV_LOG_VERBOSE, "Matched driver string "
448 "as known nonstandard driver \"%s\", setting "
450 vaapi_driver_quirks_table[i].friendly_name,
451 vaapi_driver_quirks_table[i].quirks);
452 hwctx->driver_quirks |=
453 vaapi_driver_quirks_table[i].quirks;
457 if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
458 av_log(hwdev, AV_LOG_VERBOSE, "Driver not found in known "
459 "nonstandard list, using standard behaviour.\n");
462 av_log(hwdev, AV_LOG_VERBOSE, "Driver has no vendor string, "
463 "assuming standard behaviour.\n");
470 av_freep(&ctx->formats);
475 static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
477 VAAPIDeviceContext *ctx = hwdev->internal->priv;
479 av_freep(&ctx->formats);
482 static void vaapi_buffer_free(void *opaque, uint8_t *data)
484 AVHWFramesContext *hwfc = opaque;
485 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
486 VASurfaceID surface_id;
489 surface_id = (VASurfaceID)(uintptr_t)data;
491 vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
492 if (vas != VA_STATUS_SUCCESS) {
493 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
494 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
498 static AVBufferRef *vaapi_pool_alloc(void *opaque, size_t size)
500 AVHWFramesContext *hwfc = opaque;
501 VAAPIFramesContext *ctx = hwfc->internal->priv;
502 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
503 AVVAAPIFramesContext *avfc = hwfc->hwctx;
504 VASurfaceID surface_id;
508 if (hwfc->initial_pool_size > 0 &&
509 avfc->nb_surfaces >= hwfc->initial_pool_size)
512 vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
513 hwfc->width, hwfc->height,
515 ctx->attributes, ctx->nb_attributes);
516 if (vas != VA_STATUS_SUCCESS) {
517 av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
518 "%d (%s).\n", vas, vaErrorStr(vas));
521 av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
523 ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
524 sizeof(surface_id), &vaapi_buffer_free,
525 hwfc, AV_BUFFER_FLAG_READONLY);
527 vaDestroySurfaces(hwctx->display, &surface_id, 1);
531 if (hwfc->initial_pool_size > 0) {
532 // This is a fixed-size pool, so we must still be in the initial
533 // allocation sequence.
534 av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
535 avfc->surface_ids[avfc->nb_surfaces] = surface_id;
542 static int vaapi_frames_init(AVHWFramesContext *hwfc)
544 AVVAAPIFramesContext *avfc = hwfc->hwctx;
545 VAAPIFramesContext *ctx = hwfc->internal->priv;
546 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
547 const VAAPIFormatDescriptor *desc;
548 VAImageFormat *expected_format;
549 AVBufferRef *test_surface = NULL;
550 VASurfaceID test_surface_id;
555 desc = vaapi_format_from_pix_fmt(hwfc->sw_format);
557 av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
558 av_get_pix_fmt_name(hwfc->sw_format));
559 return AVERROR(EINVAL);
563 if (!(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
564 int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
565 int need_pixel_format = 1;
566 for (i = 0; i < avfc->nb_attributes; i++) {
567 if (avfc->attributes[i].type == VASurfaceAttribMemoryType)
568 need_memory_type = 0;
569 if (avfc->attributes[i].type == VASurfaceAttribPixelFormat)
570 need_pixel_format = 0;
573 avfc->nb_attributes + need_memory_type + need_pixel_format;
575 ctx->attributes = av_malloc(ctx->nb_attributes *
576 sizeof(*ctx->attributes));
577 if (!ctx->attributes) {
578 err = AVERROR(ENOMEM);
582 for (i = 0; i < avfc->nb_attributes; i++)
583 ctx->attributes[i] = avfc->attributes[i];
584 if (need_memory_type) {
585 ctx->attributes[i++] = (VASurfaceAttrib) {
586 .type = VASurfaceAttribMemoryType,
587 .flags = VA_SURFACE_ATTRIB_SETTABLE,
588 .value.type = VAGenericValueTypeInteger,
589 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
592 if (need_pixel_format) {
593 ctx->attributes[i++] = (VASurfaceAttrib) {
594 .type = VASurfaceAttribPixelFormat,
595 .flags = VA_SURFACE_ATTRIB_SETTABLE,
596 .value.type = VAGenericValueTypeInteger,
597 .value.value.i = desc->fourcc,
600 av_assert0(i == ctx->nb_attributes);
602 ctx->attributes = NULL;
603 ctx->nb_attributes = 0;
606 ctx->rt_format = desc->rt_format;
608 if (hwfc->initial_pool_size > 0) {
609 // This pool will be usable as a render target, so we need to store
610 // all of the surface IDs somewhere that vaCreateContext() calls
611 // will be able to access them.
612 avfc->nb_surfaces = 0;
613 avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
614 sizeof(*avfc->surface_ids));
615 if (!avfc->surface_ids) {
616 err = AVERROR(ENOMEM);
620 // This pool allows dynamic sizing, and will not be usable as a
622 avfc->nb_surfaces = 0;
623 avfc->surface_ids = NULL;
626 hwfc->internal->pool_internal =
627 av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
628 &vaapi_pool_alloc, NULL);
629 if (!hwfc->internal->pool_internal) {
630 av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
631 err = AVERROR(ENOMEM);
636 // Allocate a single surface to test whether vaDeriveImage() is going
637 // to work for the specific configuration.
639 test_surface = av_buffer_pool_get(hwfc->pool);
641 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
642 "user-configured buffer pool.\n");
643 err = AVERROR(ENOMEM);
647 test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
649 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
650 "internal buffer pool.\n");
651 err = AVERROR(ENOMEM);
655 test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
657 ctx->derive_works = 0;
659 err = vaapi_get_image_format(hwfc->device_ctx,
660 hwfc->sw_format, &expected_format);
662 vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
663 if (vas == VA_STATUS_SUCCESS) {
664 if (expected_format->fourcc == test_image.format.fourcc) {
665 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
666 ctx->derive_works = 1;
668 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
669 "derived image format %08x does not match "
670 "expected format %08x.\n",
671 expected_format->fourcc, test_image.format.fourcc);
673 vaDestroyImage(hwctx->display, test_image.image_id);
675 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
676 "deriving image does not work: "
677 "%d (%s).\n", vas, vaErrorStr(vas));
680 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
681 "image format is not supported.\n");
684 av_buffer_unref(&test_surface);
688 av_buffer_unref(&test_surface);
689 av_freep(&avfc->surface_ids);
690 av_freep(&ctx->attributes);
694 static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
696 AVVAAPIFramesContext *avfc = hwfc->hwctx;
697 VAAPIFramesContext *ctx = hwfc->internal->priv;
699 av_freep(&avfc->surface_ids);
700 av_freep(&ctx->attributes);
703 static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
705 frame->buf[0] = av_buffer_pool_get(hwfc->pool);
707 return AVERROR(ENOMEM);
709 frame->data[3] = frame->buf[0]->data;
710 frame->format = AV_PIX_FMT_VAAPI;
711 frame->width = hwfc->width;
712 frame->height = hwfc->height;
717 static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
718 enum AVHWFrameTransferDirection dir,
719 enum AVPixelFormat **formats)
721 VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
722 enum AVPixelFormat *pix_fmts;
723 int i, k, sw_format_available;
725 sw_format_available = 0;
726 for (i = 0; i < ctx->nb_formats; i++) {
727 if (ctx->formats[i].pix_fmt == hwfc->sw_format)
728 sw_format_available = 1;
731 pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
733 return AVERROR(ENOMEM);
735 if (sw_format_available) {
736 pix_fmts[0] = hwfc->sw_format;
741 for (i = 0; i < ctx->nb_formats; i++) {
742 if (ctx->formats[i].pix_fmt == hwfc->sw_format)
744 av_assert0(k < ctx->nb_formats);
745 pix_fmts[k++] = ctx->formats[i].pix_fmt;
747 pix_fmts[k] = AV_PIX_FMT_NONE;
753 static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
754 HWMapDescriptor *hwmap)
756 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
757 VAAPIMapping *map = hwmap->priv;
758 VASurfaceID surface_id;
761 surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
762 av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
764 vas = vaUnmapBuffer(hwctx->display, map->image.buf);
765 if (vas != VA_STATUS_SUCCESS) {
766 av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
767 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
770 if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
771 !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
772 vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
773 0, 0, hwfc->width, hwfc->height,
774 0, 0, hwfc->width, hwfc->height);
775 if (vas != VA_STATUS_SUCCESS) {
776 av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
777 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
781 vas = vaDestroyImage(hwctx->display, map->image.image_id);
782 if (vas != VA_STATUS_SUCCESS) {
783 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
784 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
790 static int vaapi_map_frame(AVHWFramesContext *hwfc,
791 AVFrame *dst, const AVFrame *src, int flags)
793 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
794 VAAPIFramesContext *ctx = hwfc->internal->priv;
795 VASurfaceID surface_id;
796 const VAAPIFormatDescriptor *desc;
797 VAImageFormat *image_format;
800 void *address = NULL;
803 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
804 av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
806 if (!ctx->derive_works && (flags & AV_HWFRAME_MAP_DIRECT)) {
807 // Requested direct mapping but it is not possible.
808 return AVERROR(EINVAL);
810 if (dst->format == AV_PIX_FMT_NONE)
811 dst->format = hwfc->sw_format;
812 if (dst->format != hwfc->sw_format && (flags & AV_HWFRAME_MAP_DIRECT)) {
813 // Requested direct mapping but the formats do not match.
814 return AVERROR(EINVAL);
817 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
819 // Requested format is not a valid output format.
823 map = av_malloc(sizeof(*map));
825 return AVERROR(ENOMEM);
827 map->image.image_id = VA_INVALID_ID;
829 vas = vaSyncSurface(hwctx->display, surface_id);
830 if (vas != VA_STATUS_SUCCESS) {
831 av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
832 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
837 // The memory which we map using derive need not be connected to the CPU
838 // in a way conducive to fast access. On Gen7-Gen9 Intel graphics, the
839 // memory is mappable but not cached, so normal memcpy()-like access is
840 // very slow to read it (but writing is ok). It is possible to read much
841 // faster with a copy routine which is aware of the limitation, but we
842 // assume for now that the user is not aware of that and would therefore
843 // prefer not to be given direct-mapped memory if they request read access.
844 if (ctx->derive_works && dst->format == hwfc->sw_format &&
845 ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
846 vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
847 if (vas != VA_STATUS_SUCCESS) {
848 av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
849 "surface %#x: %d (%s).\n",
850 surface_id, vas, vaErrorStr(vas));
854 if (map->image.format.fourcc != image_format->fourcc) {
855 av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
856 "is in wrong format: expected %#08x, got %#08x.\n",
857 surface_id, image_format->fourcc, map->image.format.fourcc);
861 map->flags |= AV_HWFRAME_MAP_DIRECT;
863 vas = vaCreateImage(hwctx->display, image_format,
864 hwfc->width, hwfc->height, &map->image);
865 if (vas != VA_STATUS_SUCCESS) {
866 av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
867 "surface %#x: %d (%s).\n",
868 surface_id, vas, vaErrorStr(vas));
872 if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
873 vas = vaGetImage(hwctx->display, surface_id, 0, 0,
874 hwfc->width, hwfc->height, map->image.image_id);
875 if (vas != VA_STATUS_SUCCESS) {
876 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
877 "surface %#x: %d (%s).\n",
878 surface_id, vas, vaErrorStr(vas));
885 vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
886 if (vas != VA_STATUS_SUCCESS) {
887 av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
888 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
893 err = ff_hwframe_map_create(src->hw_frames_ctx,
894 dst, src, &vaapi_unmap_frame, map);
898 dst->width = src->width;
899 dst->height = src->height;
901 for (i = 0; i < map->image.num_planes; i++) {
902 dst->data[i] = (uint8_t*)address + map->image.offsets[i];
903 dst->linesize[i] = map->image.pitches[i];
906 desc = vaapi_format_from_fourcc(map->image.format.fourcc);
907 if (desc && desc->chroma_planes_swapped) {
908 // Chroma planes are YVU rather than YUV, so swap them.
909 FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
917 vaUnmapBuffer(hwctx->display, map->image.buf);
918 if (map->image.image_id != VA_INVALID_ID)
919 vaDestroyImage(hwctx->display, map->image.image_id);
925 static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
926 AVFrame *dst, const AVFrame *src)
931 if (dst->width > hwfc->width || dst->height > hwfc->height)
932 return AVERROR(EINVAL);
934 map = av_frame_alloc();
936 return AVERROR(ENOMEM);
937 map->format = dst->format;
939 err = vaapi_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
943 map->width = dst->width;
944 map->height = dst->height;
946 err = av_frame_copy(dst, map);
956 static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
957 AVFrame *dst, const AVFrame *src)
962 if (src->width > hwfc->width || src->height > hwfc->height)
963 return AVERROR(EINVAL);
965 map = av_frame_alloc();
967 return AVERROR(ENOMEM);
968 map->format = src->format;
970 err = vaapi_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
974 map->width = src->width;
975 map->height = src->height;
977 err = av_frame_copy(map, src);
987 static int vaapi_map_to_memory(AVHWFramesContext *hwfc, AVFrame *dst,
988 const AVFrame *src, int flags)
992 if (dst->format != AV_PIX_FMT_NONE) {
993 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, NULL);
998 err = vaapi_map_frame(hwfc, dst, src, flags);
1002 err = av_frame_copy_props(dst, src);
1011 #define DRM_MAP(va, layers, ...) { \
1016 static const struct {
1018 int nb_layer_formats;
1019 uint32_t layer_formats[AV_DRM_MAX_PLANES];
1020 } vaapi_drm_format_map[] = {
1021 #ifdef DRM_FORMAT_R8
1022 DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88),
1023 DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_GR88),
1025 DRM_MAP(NV12, 1, DRM_FORMAT_NV12),
1026 #if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16)
1027 DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
1029 #if defined(VA_FOURCC_P012) && defined(DRM_FORMAT_R16)
1030 DRM_MAP(P012, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
1032 DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888),
1033 DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888),
1034 DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888),
1035 DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888),
1036 #ifdef VA_FOURCC_ABGR
1037 DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888),
1038 DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888),
1040 DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888),
1041 DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888),
1042 #if defined(VA_FOURCC_XYUV) && defined(DRM_FORMAT_XYUV8888)
1043 DRM_MAP(XYUV, 1, DRM_FORMAT_XYUV8888),
1045 #if defined(VA_FOURCC_Y412) && defined(DRM_FORMAT_XVYU2101010)
1046 DRM_MAP(Y410, 1, DRM_FORMAT_XVYU2101010),
1048 #if defined(VA_FOURCC_Y412) && defined(DRM_FORMAT_XVYU12_16161616)
1049 DRM_MAP(Y412, 1, DRM_FORMAT_XVYU12_16161616),
1051 #if defined(VA_FOURCC_X2R10G10B10) && defined(DRM_FORMAT_XRGB2101010)
1052 DRM_MAP(X2R10G10B10, 1, DRM_FORMAT_XRGB2101010),
1057 static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
1058 HWMapDescriptor *hwmap)
1060 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
1062 VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
1064 av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id);
1066 vaDestroySurfaces(dst_dev->display, &surface_id, 1);
1069 static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
1070 const AVFrame *src, int flags)
1072 #if VA_CHECK_VERSION(1, 1, 0)
1073 VAAPIFramesContext *src_vafc = src_fc->internal->priv;
1078 AVHWFramesContext *dst_fc =
1079 (AVHWFramesContext*)dst->hw_frames_ctx->data;
1080 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
1081 const AVDRMFrameDescriptor *desc;
1082 const VAAPIFormatDescriptor *format_desc;
1083 VASurfaceID surface_id;
1084 VAStatus vas = VA_STATUS_SUCCESS;
1088 #if !VA_CHECK_VERSION(1, 1, 0)
1089 unsigned long buffer_handle;
1090 VASurfaceAttribExternalBuffers buffer_desc;
1091 VASurfaceAttrib attrs[2] = {
1093 .type = VASurfaceAttribMemoryType,
1094 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1095 .value.type = VAGenericValueTypeInteger,
1096 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
1099 .type = VASurfaceAttribExternalBufferDescriptor,
1100 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1101 .value.type = VAGenericValueTypePointer,
1102 .value.value.p = &buffer_desc,
1107 desc = (AVDRMFrameDescriptor*)src->data[0];
1109 if (desc->nb_objects != 1) {
1110 av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames "
1111 "made from a single DRM object.\n");
1112 return AVERROR(EINVAL);
1116 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1117 if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats)
1119 for (j = 0; j < desc->nb_layers; j++) {
1120 if (desc->layers[j].format !=
1121 vaapi_drm_format_map[i].layer_formats[j])
1124 if (j != desc->nb_layers)
1126 va_fourcc = vaapi_drm_format_map[i].va_fourcc;
1130 av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported "
1132 return AVERROR(EINVAL);
1135 av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
1136 "%08x.\n", desc->objects[0].fd, va_fourcc);
1138 format_desc = vaapi_format_from_fourcc(va_fourcc);
1139 av_assert0(format_desc);
1141 #if VA_CHECK_VERSION(1, 1, 0)
1142 use_prime2 = !src_vafc->prime_2_import_unsupported &&
1143 desc->objects[0].format_modifier != DRM_FORMAT_MOD_INVALID;
1145 VADRMPRIMESurfaceDescriptor prime_desc;
1146 VASurfaceAttrib prime_attrs[2] = {
1148 .type = VASurfaceAttribMemoryType,
1149 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1150 .value.type = VAGenericValueTypeInteger,
1151 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
1154 .type = VASurfaceAttribExternalBufferDescriptor,
1155 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1156 .value.type = VAGenericValueTypePointer,
1157 .value.value.p = &prime_desc,
1160 prime_desc.fourcc = va_fourcc;
1161 prime_desc.width = src_fc->width;
1162 prime_desc.height = src_fc->height;
1163 prime_desc.num_objects = desc->nb_objects;
1164 for (i = 0; i < desc->nb_objects; ++i) {
1165 prime_desc.objects[i].fd = desc->objects[i].fd;
1166 prime_desc.objects[i].size = desc->objects[i].size;
1167 prime_desc.objects[i].drm_format_modifier =
1168 desc->objects[i].format_modifier;
1171 prime_desc.num_layers = desc->nb_layers;
1172 for (i = 0; i < desc->nb_layers; ++i) {
1173 prime_desc.layers[i].drm_format = desc->layers[i].format;
1174 prime_desc.layers[i].num_planes = desc->layers[i].nb_planes;
1175 for (j = 0; j < desc->layers[i].nb_planes; ++j) {
1176 prime_desc.layers[i].object_index[j] =
1177 desc->layers[i].planes[j].object_index;
1178 prime_desc.layers[i].offset[j] = desc->layers[i].planes[j].offset;
1179 prime_desc.layers[i].pitch[j] = desc->layers[i].planes[j].pitch;
1182 if (format_desc->chroma_planes_swapped &&
1183 desc->layers[i].nb_planes == 3) {
1184 FFSWAP(uint32_t, prime_desc.layers[i].pitch[1],
1185 prime_desc.layers[i].pitch[2]);
1186 FFSWAP(uint32_t, prime_desc.layers[i].offset[1],
1187 prime_desc.layers[i].offset[2]);
1192 * We can query for PRIME_2 support with vaQuerySurfaceAttributes, but that
1193 * that needs the config_id which we don't have here . Both Intel and
1194 * Gallium seem to do the correct error checks, so lets just try the
1195 * PRIME_2 import first.
1197 vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
1198 src->width, src->height, &surface_id, 1,
1199 prime_attrs, FF_ARRAY_ELEMS(prime_attrs));
1200 if (vas != VA_STATUS_SUCCESS)
1201 src_vafc->prime_2_import_unsupported = 1;
1204 if (!use_prime2 || vas != VA_STATUS_SUCCESS) {
1206 unsigned long buffer_handle;
1207 VASurfaceAttribExternalBuffers buffer_desc;
1208 VASurfaceAttrib buffer_attrs[2] = {
1210 .type = VASurfaceAttribMemoryType,
1211 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1212 .value.type = VAGenericValueTypeInteger,
1213 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
1216 .type = VASurfaceAttribExternalBufferDescriptor,
1217 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1218 .value.type = VAGenericValueTypePointer,
1219 .value.value.p = &buffer_desc,
1223 buffer_handle = desc->objects[0].fd;
1224 buffer_desc.pixel_format = va_fourcc;
1225 buffer_desc.width = src_fc->width;
1226 buffer_desc.height = src_fc->height;
1227 buffer_desc.data_size = desc->objects[0].size;
1228 buffer_desc.buffers = &buffer_handle;
1229 buffer_desc.num_buffers = 1;
1230 buffer_desc.flags = 0;
1233 for (i = 0; i < desc->nb_layers; i++) {
1234 for (j = 0; j < desc->layers[i].nb_planes; j++) {
1235 buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
1236 buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
1240 buffer_desc.num_planes = k;
1242 if (format_desc->chroma_planes_swapped &&
1243 buffer_desc.num_planes == 3) {
1244 FFSWAP(uint32_t, buffer_desc.pitches[1], buffer_desc.pitches[2]);
1245 FFSWAP(uint32_t, buffer_desc.offsets[1], buffer_desc.offsets[2]);
1248 vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
1249 src->width, src->height,
1251 buffer_attrs, FF_ARRAY_ELEMS(buffer_attrs));
1254 buffer_handle = desc->objects[0].fd;
1255 buffer_desc.pixel_format = va_fourcc;
1256 buffer_desc.width = src_fc->width;
1257 buffer_desc.height = src_fc->height;
1258 buffer_desc.data_size = desc->objects[0].size;
1259 buffer_desc.buffers = &buffer_handle;
1260 buffer_desc.num_buffers = 1;
1261 buffer_desc.flags = 0;
1264 for (i = 0; i < desc->nb_layers; i++) {
1265 for (j = 0; j < desc->layers[i].nb_planes; j++) {
1266 buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
1267 buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
1271 buffer_desc.num_planes = k;
1273 if (format_desc->chroma_planes_swapped &&
1274 buffer_desc.num_planes == 3) {
1275 FFSWAP(uint32_t, buffer_desc.pitches[1], buffer_desc.pitches[2]);
1276 FFSWAP(uint32_t, buffer_desc.offsets[1], buffer_desc.offsets[2]);
1279 vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
1280 src->width, src->height,
1282 attrs, FF_ARRAY_ELEMS(attrs));
1284 if (vas != VA_STATUS_SUCCESS) {
1285 av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
1286 "object: %d (%s).\n", vas, vaErrorStr(vas));
1287 return AVERROR(EIO);
1289 av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
1291 err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
1292 &vaapi_unmap_from_drm,
1293 (void*)(uintptr_t)surface_id);
1297 dst->width = src->width;
1298 dst->height = src->height;
1299 dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
1301 av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
1302 "surface %#x.\n", desc->objects[0].fd, surface_id);
1307 #if VA_CHECK_VERSION(1, 1, 0)
1308 static void vaapi_unmap_to_drm_esh(AVHWFramesContext *hwfc,
1309 HWMapDescriptor *hwmap)
1311 AVDRMFrameDescriptor *drm_desc = hwmap->priv;
1314 for (i = 0; i < drm_desc->nb_objects; i++)
1315 close(drm_desc->objects[i].fd);
1317 av_freep(&drm_desc);
1320 static int vaapi_map_to_drm_esh(AVHWFramesContext *hwfc, AVFrame *dst,
1321 const AVFrame *src, int flags)
1323 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1324 VASurfaceID surface_id;
1326 VADRMPRIMESurfaceDescriptor va_desc;
1327 AVDRMFrameDescriptor *drm_desc = NULL;
1328 uint32_t export_flags;
1331 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1333 export_flags = VA_EXPORT_SURFACE_SEPARATE_LAYERS;
1334 if (flags & AV_HWFRAME_MAP_READ) {
1335 export_flags |= VA_EXPORT_SURFACE_READ_ONLY;
1337 vas = vaSyncSurface(hwctx->display, surface_id);
1338 if (vas != VA_STATUS_SUCCESS) {
1339 av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
1340 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
1341 return AVERROR(EIO);
1345 if (flags & AV_HWFRAME_MAP_WRITE)
1346 export_flags |= VA_EXPORT_SURFACE_WRITE_ONLY;
1348 vas = vaExportSurfaceHandle(hwctx->display, surface_id,
1349 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
1350 export_flags, &va_desc);
1351 if (vas != VA_STATUS_SUCCESS) {
1352 if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
1353 return AVERROR(ENOSYS);
1354 av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
1355 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
1356 return AVERROR(EIO);
1359 drm_desc = av_mallocz(sizeof(*drm_desc));
1361 err = AVERROR(ENOMEM);
1365 // By some bizarre coincidence, these structures are very similar...
1366 drm_desc->nb_objects = va_desc.num_objects;
1367 for (i = 0; i < va_desc.num_objects; i++) {
1368 drm_desc->objects[i].fd = va_desc.objects[i].fd;
1369 drm_desc->objects[i].size = va_desc.objects[i].size;
1370 drm_desc->objects[i].format_modifier =
1371 va_desc.objects[i].drm_format_modifier;
1373 drm_desc->nb_layers = va_desc.num_layers;
1374 for (i = 0; i < va_desc.num_layers; i++) {
1375 drm_desc->layers[i].format = va_desc.layers[i].drm_format;
1376 drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes;
1377 for (j = 0; j < va_desc.layers[i].num_planes; j++) {
1378 drm_desc->layers[i].planes[j].object_index =
1379 va_desc.layers[i].object_index[j];
1380 drm_desc->layers[i].planes[j].offset =
1381 va_desc.layers[i].offset[j];
1382 drm_desc->layers[i].planes[j].pitch =
1383 va_desc.layers[i].pitch[j];
1387 err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
1388 &vaapi_unmap_to_drm_esh, drm_desc);
1392 dst->width = src->width;
1393 dst->height = src->height;
1394 dst->data[0] = (uint8_t*)drm_desc;
1399 for (i = 0; i < va_desc.num_objects; i++)
1400 close(va_desc.objects[i].fd);
1401 av_freep(&drm_desc);
1406 #if VA_CHECK_VERSION(0, 36, 0)
1407 typedef struct VAAPIDRMImageBufferMapping {
1409 VABufferInfo buffer_info;
1411 AVDRMFrameDescriptor drm_desc;
1412 } VAAPIDRMImageBufferMapping;
1414 static void vaapi_unmap_to_drm_abh(AVHWFramesContext *hwfc,
1415 HWMapDescriptor *hwmap)
1417 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1418 VAAPIDRMImageBufferMapping *mapping = hwmap->priv;
1419 VASurfaceID surface_id;
1422 surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
1423 av_log(hwfc, AV_LOG_DEBUG, "Unmap VAAPI surface %#x from DRM.\n",
1426 // DRM PRIME file descriptors are closed by vaReleaseBufferHandle(),
1427 // so we shouldn't close them separately.
1429 vas = vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1430 if (vas != VA_STATUS_SUCCESS) {
1431 av_log(hwfc, AV_LOG_ERROR, "Failed to release buffer "
1432 "handle of image %#x (derived from surface %#x): "
1433 "%d (%s).\n", mapping->image.buf, surface_id,
1434 vas, vaErrorStr(vas));
1437 vas = vaDestroyImage(hwctx->display, mapping->image.image_id);
1438 if (vas != VA_STATUS_SUCCESS) {
1439 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image "
1440 "derived from surface %#x: %d (%s).\n",
1441 surface_id, vas, vaErrorStr(vas));
1447 static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst,
1448 const AVFrame *src, int flags)
1450 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1451 VAAPIDRMImageBufferMapping *mapping = NULL;
1452 VASurfaceID surface_id;
1456 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1457 av_log(hwfc, AV_LOG_DEBUG, "Map VAAPI surface %#x to DRM.\n",
1460 mapping = av_mallocz(sizeof(*mapping));
1462 return AVERROR(ENOMEM);
1464 vas = vaDeriveImage(hwctx->display, surface_id,
1466 if (vas != VA_STATUS_SUCCESS) {
1467 av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
1468 "surface %#x: %d (%s).\n",
1469 surface_id, vas, vaErrorStr(vas));
1474 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1475 if (vaapi_drm_format_map[i].va_fourcc ==
1476 mapping->image.format.fourcc)
1479 if (i >= FF_ARRAY_ELEMS(vaapi_drm_format_map)) {
1480 av_log(hwfc, AV_LOG_ERROR, "No matching DRM format for "
1481 "VAAPI format %#x.\n", mapping->image.format.fourcc);
1482 err = AVERROR(EINVAL);
1486 mapping->buffer_info.mem_type =
1487 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
1489 mapping->drm_desc.nb_layers =
1490 vaapi_drm_format_map[i].nb_layer_formats;
1491 if (mapping->drm_desc.nb_layers > 1) {
1492 if (mapping->drm_desc.nb_layers != mapping->image.num_planes) {
1493 av_log(hwfc, AV_LOG_ERROR, "Image properties do not match "
1494 "expected format: got %d planes, but expected %d.\n",
1495 mapping->image.num_planes, mapping->drm_desc.nb_layers);
1496 err = AVERROR(EINVAL);
1500 for(p = 0; p < mapping->drm_desc.nb_layers; p++) {
1501 mapping->drm_desc.layers[p] = (AVDRMLayerDescriptor) {
1502 .format = vaapi_drm_format_map[i].layer_formats[p],
1506 .offset = mapping->image.offsets[p],
1507 .pitch = mapping->image.pitches[p],
1512 mapping->drm_desc.layers[0].format =
1513 vaapi_drm_format_map[i].layer_formats[0];
1514 mapping->drm_desc.layers[0].nb_planes = mapping->image.num_planes;
1515 for (p = 0; p < mapping->image.num_planes; p++) {
1516 mapping->drm_desc.layers[0].planes[p] = (AVDRMPlaneDescriptor) {
1518 .offset = mapping->image.offsets[p],
1519 .pitch = mapping->image.pitches[p],
1524 vas = vaAcquireBufferHandle(hwctx->display, mapping->image.buf,
1525 &mapping->buffer_info);
1526 if (vas != VA_STATUS_SUCCESS) {
1527 av_log(hwfc, AV_LOG_ERROR, "Failed to get buffer "
1528 "handle from image %#x (derived from surface %#x): "
1529 "%d (%s).\n", mapping->image.buf, surface_id,
1530 vas, vaErrorStr(vas));
1535 av_log(hwfc, AV_LOG_DEBUG, "DRM PRIME fd is %"PRIdPTR".\n",
1536 mapping->buffer_info.handle);
1538 mapping->drm_desc.nb_objects = 1;
1539 mapping->drm_desc.objects[0] = (AVDRMObjectDescriptor) {
1540 .fd = mapping->buffer_info.handle,
1541 .size = mapping->image.data_size,
1542 // There is no way to get the format modifier with this API.
1543 .format_modifier = DRM_FORMAT_MOD_INVALID,
1546 err = ff_hwframe_map_create(src->hw_frames_ctx,
1547 dst, src, &vaapi_unmap_to_drm_abh,
1552 dst->data[0] = (uint8_t*)&mapping->drm_desc;
1553 dst->width = src->width;
1554 dst->height = src->height;
1559 vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1561 vaDestroyImage(hwctx->display, mapping->image.image_id);
1568 static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
1569 const AVFrame *src, int flags)
1571 #if VA_CHECK_VERSION(1, 1, 0)
1573 err = vaapi_map_to_drm_esh(hwfc, dst, src, flags);
1574 if (err != AVERROR(ENOSYS))
1577 #if VA_CHECK_VERSION(0, 36, 0)
1578 return vaapi_map_to_drm_abh(hwfc, dst, src, flags);
1580 return AVERROR(ENOSYS);
1583 #endif /* CONFIG_LIBDRM */
1585 static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
1586 const AVFrame *src, int flags)
1588 switch (src->format) {
1590 case AV_PIX_FMT_DRM_PRIME:
1591 return vaapi_map_from_drm(hwfc, dst, src, flags);
1594 return AVERROR(ENOSYS);
1598 static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
1599 const AVFrame *src, int flags)
1601 switch (dst->format) {
1603 case AV_PIX_FMT_DRM_PRIME:
1604 return vaapi_map_to_drm(hwfc, dst, src, flags);
1607 return vaapi_map_to_memory(hwfc, dst, src, flags);
1611 static void vaapi_device_free(AVHWDeviceContext *ctx)
1613 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1614 VAAPIDevicePriv *priv = ctx->user_opaque;
1617 vaTerminate(hwctx->display);
1620 if (priv->x11_display)
1621 XCloseDisplay(priv->x11_display);
1624 if (priv->drm_fd >= 0)
1625 close(priv->drm_fd);
1631 static void vaapi_device_log_error(void *context, const char *message)
1633 AVHWDeviceContext *ctx = context;
1635 av_log(ctx, AV_LOG_ERROR, "libva: %s", message);
1638 static void vaapi_device_log_info(void *context, const char *message)
1640 AVHWDeviceContext *ctx = context;
1642 av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message);
1646 static int vaapi_device_connect(AVHWDeviceContext *ctx,
1649 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1654 vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
1655 vaSetInfoCallback (display, &vaapi_device_log_info, ctx);
1658 hwctx->display = display;
1660 vas = vaInitialize(display, &major, &minor);
1661 if (vas != VA_STATUS_SUCCESS) {
1662 av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
1663 "connection: %d (%s).\n", vas, vaErrorStr(vas));
1664 return AVERROR(EIO);
1666 av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
1667 "version %d.%d\n", major, minor);
1672 static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
1673 AVDictionary *opts, int flags)
1675 VAAPIDevicePriv *priv;
1676 VADisplay display = NULL;
1677 const AVDictionaryEntry *ent;
1678 int try_drm, try_x11, try_win32, try_all;
1680 priv = av_mallocz(sizeof(*priv));
1682 return AVERROR(ENOMEM);
1686 ctx->user_opaque = priv;
1687 ctx->free = vaapi_device_free;
1689 ent = av_dict_get(opts, "connection_type", NULL, 0);
1691 try_all = try_drm = try_x11 = try_win32 = 0;
1692 if (!strcmp(ent->value, "drm")) {
1694 } else if (!strcmp(ent->value, "x11")) {
1696 } else if (!strcmp(ent->value, "win32")) {
1699 av_log(ctx, AV_LOG_ERROR, "Invalid connection type %s.\n",
1701 return AVERROR(EINVAL);
1705 try_drm = HAVE_VAAPI_DRM;
1706 try_x11 = HAVE_VAAPI_X11;
1707 try_win32 = HAVE_VAAPI_WIN32;
1711 while (!display && try_drm) {
1712 // If the device is specified, try to open it as a DRM device node.
1713 // If not, look for a usable render node, possibly restricted to those
1714 // using a specified kernel driver.
1715 int loglevel = try_all ? AV_LOG_VERBOSE : AV_LOG_ERROR;
1717 priv->drm_fd = open(device, O_RDWR);
1718 if (priv->drm_fd < 0) {
1719 av_log(ctx, loglevel, "Failed to open %s as "
1720 "DRM device node.\n", device);
1725 int n, max_devices = 8;
1728 const AVDictionaryEntry *kernel_driver;
1729 kernel_driver = av_dict_get(opts, "kernel_driver", NULL, 0);
1731 for (n = 0; n < max_devices; n++) {
1732 snprintf(path, sizeof(path),
1733 "/dev/dri/renderD%d", 128 + n);
1734 priv->drm_fd = open(path, O_RDWR);
1735 if (priv->drm_fd < 0) {
1736 av_log(ctx, AV_LOG_VERBOSE, "Cannot open "
1737 "DRM render node for device %d.\n", n);
1741 info = drmGetVersion(priv->drm_fd);
1743 av_log(ctx, AV_LOG_VERBOSE,
1744 "Failed to get DRM version for device %d.\n", n);
1745 close(priv->drm_fd);
1749 if (kernel_driver) {
1750 if (strcmp(kernel_driver->value, info->name)) {
1751 av_log(ctx, AV_LOG_VERBOSE, "Ignoring device %d "
1752 "with non-matching kernel driver (%s).\n",
1754 drmFreeVersion(info);
1755 close(priv->drm_fd);
1759 av_log(ctx, AV_LOG_VERBOSE, "Trying to use "
1760 "DRM render node for device %d, "
1761 "with matching kernel driver (%s).\n",
1763 drmFreeVersion(info);
1765 // drmGetVersion() ensures |info->name| is 0-terminated.
1766 } else if (!strcmp(info->name, "vgem")) {
1767 av_log(ctx, AV_LOG_VERBOSE,
1768 "Skipping vgem node for device %d.\n", n);
1769 drmFreeVersion(info);
1770 close(priv->drm_fd);
1774 drmFreeVersion(info);
1776 av_log(ctx, AV_LOG_VERBOSE, "Trying to use "
1777 "DRM render node for device %d.\n", n);
1780 if (n >= max_devices)
1784 display = vaGetDisplayDRM(priv->drm_fd);
1786 av_log(ctx, AV_LOG_VERBOSE, "Cannot open a VA display "
1787 "from DRM device %s.\n", device);
1788 return AVERROR_EXTERNAL;
1795 if (!display && try_x11) {
1796 // Try to open the device as an X11 display.
1797 priv->x11_display = XOpenDisplay(device);
1798 if (!priv->x11_display) {
1799 av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
1800 "%s.\n", XDisplayName(device));
1802 display = vaGetDisplay(priv->x11_display);
1804 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1805 "from X11 display %s.\n", XDisplayName(device));
1806 return AVERROR_UNKNOWN;
1809 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1810 "X11 display %s.\n", XDisplayName(device));
1815 #if HAVE_VAAPI_WIN32
1816 if (!display && try_win32) {
1817 // Try to create a display from the specified device, if any.
1819 display = vaGetDisplayWin32(NULL);
1821 IDXGIFactory2 *pDXGIFactory = NULL;
1822 IDXGIAdapter *pAdapter = NULL;
1824 HANDLE dxgi = dlopen("dxgi.dll", 0);
1826 av_log(ctx, AV_LOG_ERROR, "Failed to load dxgi.dll\n");
1827 return AVERROR_UNKNOWN;
1829 PFN_CREATE_DXGI_FACTORY pfnCreateDXGIFactory =
1830 (PFN_CREATE_DXGI_FACTORY)dlsym(dxgi, "CreateDXGIFactory");
1831 if (!pfnCreateDXGIFactory) {
1832 av_log(ctx, AV_LOG_ERROR, "CreateDXGIFactory load failed\n");
1834 return AVERROR_UNKNOWN;
1837 // In UWP (which lacks LoadLibrary), CreateDXGIFactory isn't
1838 // available, only CreateDXGIFactory1
1839 PFN_CREATE_DXGI_FACTORY pfnCreateDXGIFactory =
1840 (PFN_CREATE_DXGI_FACTORY)CreateDXGIFactory1;
1842 if (SUCCEEDED(pfnCreateDXGIFactory(&IID_IDXGIFactory2,
1843 (void **)&pDXGIFactory))) {
1844 int adapter = atoi(device);
1845 if (SUCCEEDED(IDXGIFactory2_EnumAdapters(pDXGIFactory,
1848 DXGI_ADAPTER_DESC desc;
1849 if (SUCCEEDED(IDXGIAdapter2_GetDesc(pAdapter, &desc))) {
1850 av_log(ctx, AV_LOG_INFO,
1851 "Using device %04x:%04x (%ls) - LUID %lu %ld.\n",
1852 desc.VendorId, desc.DeviceId, desc.Description,
1853 desc.AdapterLuid.LowPart,
1854 desc.AdapterLuid.HighPart);
1855 display = vaGetDisplayWin32(&desc.AdapterLuid);
1857 IDXGIAdapter_Release(pAdapter);
1859 IDXGIFactory2_Release(pDXGIFactory);
1867 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1868 "from Win32 display.\n");
1869 return AVERROR_UNKNOWN;
1872 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1873 "Win32 display.\n");
1879 av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1880 "device %s.\n", device);
1882 av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1883 "any default device.\n");
1884 return AVERROR(EINVAL);
1887 ent = av_dict_get(opts, "driver", NULL, 0);
1889 #if VA_CHECK_VERSION(0, 38, 0)
1891 vas = vaSetDriverName(display, ent->value);
1892 if (vas != VA_STATUS_SUCCESS) {
1893 av_log(ctx, AV_LOG_ERROR, "Failed to set driver name to "
1894 "%s: %d (%s).\n", ent->value, vas, vaErrorStr(vas));
1895 vaTerminate(display);
1896 return AVERROR_EXTERNAL;
1899 av_log(ctx, AV_LOG_WARNING, "Driver name setting is not "
1900 "supported with this VAAPI version.\n");
1904 return vaapi_device_connect(ctx, display);
1907 static int vaapi_device_derive(AVHWDeviceContext *ctx,
1908 AVHWDeviceContext *src_ctx,
1909 AVDictionary *opts, int flags)
1912 if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
1913 AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
1915 VAAPIDevicePriv *priv;
1918 if (src_hwctx->fd < 0) {
1919 av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
1920 "device to derive a VA display from.\n");
1921 return AVERROR(EINVAL);
1926 int node_type = drmGetNodeTypeFromFd(src_hwctx->fd);
1928 if (node_type < 0) {
1929 av_log(ctx, AV_LOG_ERROR, "DRM instance fd does not appear "
1930 "to refer to a DRM device.\n");
1931 return AVERROR(EINVAL);
1933 if (node_type == DRM_NODE_RENDER) {
1936 render_node = drmGetRenderDeviceNameFromFd(src_hwctx->fd);
1938 av_log(ctx, AV_LOG_VERBOSE, "Using non-render node "
1939 "because the device does not have an "
1940 "associated render node.\n");
1943 fd = open(render_node, O_RDWR);
1945 av_log(ctx, AV_LOG_VERBOSE, "Using non-render node "
1946 "because the associated render node "
1947 "could not be opened.\n");
1950 av_log(ctx, AV_LOG_VERBOSE, "Using render node %s "
1951 "in place of non-render DRM device.\n",
1962 priv = av_mallocz(sizeof(*priv));
1964 if (fd != src_hwctx->fd) {
1965 // The fd was opened in this function.
1968 return AVERROR(ENOMEM);
1971 if (fd == src_hwctx->fd) {
1972 // The fd is inherited from the source context and we are holding
1973 // a reference to that, we don't want to close it from here.
1979 ctx->user_opaque = priv;
1980 ctx->free = &vaapi_device_free;
1982 display = vaGetDisplayDRM(fd);
1984 av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
1986 return AVERROR(EIO);
1989 return vaapi_device_connect(ctx, display);
1992 return AVERROR(ENOSYS);
1995 const HWContextType ff_hwcontext_type_vaapi = {
1996 .type = AV_HWDEVICE_TYPE_VAAPI,
1999 .device_hwctx_size = sizeof(AVVAAPIDeviceContext),
2000 .device_priv_size = sizeof(VAAPIDeviceContext),
2001 .device_hwconfig_size = sizeof(AVVAAPIHWConfig),
2002 .frames_hwctx_size = sizeof(AVVAAPIFramesContext),
2003 .frames_priv_size = sizeof(VAAPIFramesContext),
2005 .device_create = &vaapi_device_create,
2006 .device_derive = &vaapi_device_derive,
2007 .device_init = &vaapi_device_init,
2008 .device_uninit = &vaapi_device_uninit,
2009 .frames_get_constraints = &vaapi_frames_get_constraints,
2010 .frames_init = &vaapi_frames_init,
2011 .frames_uninit = &vaapi_frames_uninit,
2012 .frames_get_buffer = &vaapi_get_buffer,
2013 .transfer_get_formats = &vaapi_transfer_get_formats,
2014 .transfer_data_to = &vaapi_transfer_data_to,
2015 .transfer_data_from = &vaapi_transfer_data_from,
2016 .map_to = &vaapi_map_to,
2017 .map_from = &vaapi_map_from,
2019 .pix_fmts = (const enum AVPixelFormat[]) {