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>
31 # include <drm_fourcc.h>
32 # ifndef DRM_FORMAT_MOD_INVALID
33 # define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
46 #include "hwcontext.h"
47 #include "hwcontext_drm.h"
48 #include "hwcontext_internal.h"
49 #include "hwcontext_vaapi.h"
55 typedef struct VAAPIDevicePriv {
63 typedef struct VAAPISurfaceFormat {
64 enum AVPixelFormat pix_fmt;
65 VAImageFormat image_format;
68 typedef struct VAAPIDeviceContext {
69 // Surface formats which can be used with this device.
70 VAAPISurfaceFormat *formats;
74 typedef struct VAAPIFramesContext {
75 // Surface attributes set at create time.
76 VASurfaceAttrib *attributes;
78 // RT format of the underlying surface (Intel driver ignores this anyway).
79 unsigned int rt_format;
80 // Whether vaDeriveImage works.
84 typedef struct VAAPIMapping {
85 // Handle to the derived or copied image which is mapped.
87 // The mapping flags actually used.
91 typedef struct VAAPIFormat {
93 unsigned int rt_format;
94 enum AVPixelFormat pix_fmt;
95 int chroma_planes_swapped;
96 } VAAPIFormatDescriptor;
98 #define MAP(va, rt, av, swap_uv) { \
100 VA_RT_FORMAT_ ## rt, \
104 // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
105 // plane swap cases. The frame handling below tries to hide these.
106 static const VAAPIFormatDescriptor vaapi_format_map[] = {
107 MAP(NV12, YUV420, NV12, 0),
108 #ifdef VA_FOURCC_I420
109 MAP(I420, YUV420, YUV420P, 0),
111 MAP(YV12, YUV420, YUV420P, 1),
112 MAP(IYUV, YUV420, YUV420P, 0),
113 MAP(422H, YUV422, YUV422P, 0),
114 #ifdef VA_FOURCC_YV16
115 MAP(YV16, YUV422, YUV422P, 1),
117 MAP(UYVY, YUV422, UYVY422, 0),
118 MAP(YUY2, YUV422, YUYV422, 0),
119 MAP(411P, YUV411, YUV411P, 0),
120 MAP(422V, YUV422, YUV440P, 0),
121 MAP(444P, YUV444, YUV444P, 0),
122 MAP(Y800, YUV400, GRAY8, 0),
123 #ifdef VA_FOURCC_P010
124 MAP(P010, YUV420_10BPP, P010, 0),
126 MAP(BGRA, RGB32, BGRA, 0),
127 MAP(BGRX, RGB32, BGR0, 0),
128 MAP(RGBA, RGB32, RGBA, 0),
129 MAP(RGBX, RGB32, RGB0, 0),
130 #ifdef VA_FOURCC_ABGR
131 MAP(ABGR, RGB32, ABGR, 0),
132 MAP(XBGR, RGB32, 0BGR, 0),
134 MAP(ARGB, RGB32, ARGB, 0),
135 MAP(XRGB, RGB32, 0RGB, 0),
139 static const VAAPIFormatDescriptor *
140 vaapi_format_from_fourcc(unsigned int fourcc)
143 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
144 if (vaapi_format_map[i].fourcc == fourcc)
145 return &vaapi_format_map[i];
149 static const VAAPIFormatDescriptor *
150 vaapi_format_from_pix_fmt(enum AVPixelFormat pix_fmt)
153 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
154 if (vaapi_format_map[i].pix_fmt == pix_fmt)
155 return &vaapi_format_map[i];
159 static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
161 const VAAPIFormatDescriptor *desc;
162 desc = vaapi_format_from_fourcc(fourcc);
164 return desc->pix_fmt;
166 return AV_PIX_FMT_NONE;
169 static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
170 enum AVPixelFormat pix_fmt,
171 VAImageFormat **image_format)
173 VAAPIDeviceContext *ctx = hwdev->internal->priv;
176 for (i = 0; i < ctx->nb_formats; i++) {
177 if (ctx->formats[i].pix_fmt == pix_fmt) {
179 *image_format = &ctx->formats[i].image_format;
183 return AVERROR(EINVAL);
186 static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
187 const void *hwconfig,
188 AVHWFramesConstraints *constraints)
190 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
191 const AVVAAPIHWConfig *config = hwconfig;
192 VAAPIDeviceContext *ctx = hwdev->internal->priv;
193 VASurfaceAttrib *attr_list = NULL;
195 enum AVPixelFormat pix_fmt;
197 int err, i, j, attr_count, pix_fmt_count;
200 !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
202 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
204 if (vas != VA_STATUS_SUCCESS) {
205 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
206 "%d (%s).\n", vas, vaErrorStr(vas));
207 err = AVERROR(ENOSYS);
211 attr_list = av_malloc(attr_count * sizeof(*attr_list));
213 err = AVERROR(ENOMEM);
217 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
218 attr_list, &attr_count);
219 if (vas != VA_STATUS_SUCCESS) {
220 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
221 "%d (%s).\n", vas, vaErrorStr(vas));
222 err = AVERROR(ENOSYS);
227 for (i = 0; i < attr_count; i++) {
228 switch (attr_list[i].type) {
229 case VASurfaceAttribPixelFormat:
230 fourcc = attr_list[i].value.value.i;
231 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
232 if (pix_fmt != AV_PIX_FMT_NONE) {
235 // Something unsupported - ignore.
238 case VASurfaceAttribMinWidth:
239 constraints->min_width = attr_list[i].value.value.i;
241 case VASurfaceAttribMinHeight:
242 constraints->min_height = attr_list[i].value.value.i;
244 case VASurfaceAttribMaxWidth:
245 constraints->max_width = attr_list[i].value.value.i;
247 case VASurfaceAttribMaxHeight:
248 constraints->max_height = attr_list[i].value.value.i;
252 if (pix_fmt_count == 0) {
253 // Nothing usable found. Presumably there exists something which
254 // works, so leave the set null to indicate unknown.
255 constraints->valid_sw_formats = NULL;
257 constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
259 if (!constraints->valid_sw_formats) {
260 err = AVERROR(ENOMEM);
264 for (i = j = 0; i < attr_count; i++) {
265 if (attr_list[i].type != 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)
270 constraints->valid_sw_formats[j++] = pix_fmt;
272 av_assert0(j == pix_fmt_count);
273 constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
276 // No configuration supplied.
277 // Return the full set of image formats known by the implementation.
278 constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
280 if (!constraints->valid_sw_formats) {
281 err = AVERROR(ENOMEM);
284 for (i = 0; i < ctx->nb_formats; i++)
285 constraints->valid_sw_formats[i] = ctx->formats[i].pix_fmt;
286 constraints->valid_sw_formats[i] = AV_PIX_FMT_NONE;
289 constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
290 if (!constraints->valid_hw_formats) {
291 err = AVERROR(ENOMEM);
294 constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
295 constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
299 av_freep(&attr_list);
303 static const struct {
304 const char *friendly_name;
305 const char *match_string;
307 } vaapi_driver_quirks_table[] = {
308 #if !VA_CHECK_VERSION(1, 0, 0)
309 // The i965 driver did not conform before version 2.0.
311 "Intel i965 (Quick Sync)",
313 AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
319 AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
323 "Splitted-Desktop Systems VDPAU backend for VA-API",
324 AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES,
328 static int vaapi_device_init(AVHWDeviceContext *hwdev)
330 VAAPIDeviceContext *ctx = hwdev->internal->priv;
331 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
332 VAImageFormat *image_list = NULL;
334 const char *vendor_string;
335 int err, i, image_count;
336 enum AVPixelFormat pix_fmt;
339 image_count = vaMaxNumImageFormats(hwctx->display);
340 if (image_count <= 0) {
344 image_list = av_malloc(image_count * sizeof(*image_list));
346 err = AVERROR(ENOMEM);
349 vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
350 if (vas != VA_STATUS_SUCCESS) {
355 ctx->formats = av_malloc(image_count * sizeof(*ctx->formats));
357 err = AVERROR(ENOMEM);
361 for (i = 0; i < image_count; i++) {
362 fourcc = image_list[i].fourcc;
363 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
364 if (pix_fmt == AV_PIX_FMT_NONE) {
365 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
368 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
369 fourcc, av_get_pix_fmt_name(pix_fmt));
370 ctx->formats[ctx->nb_formats].pix_fmt = pix_fmt;
371 ctx->formats[ctx->nb_formats].image_format = image_list[i];
376 vendor_string = vaQueryVendorString(hwctx->display);
378 av_log(hwdev, AV_LOG_VERBOSE, "VAAPI driver: %s.\n", vendor_string);
380 if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
381 av_log(hwdev, AV_LOG_VERBOSE, "Using quirks set by user (%#x).\n",
382 hwctx->driver_quirks);
384 // Detect the driver in use and set quirk flags if necessary.
385 hwctx->driver_quirks = 0;
387 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
388 if (strstr(vendor_string,
389 vaapi_driver_quirks_table[i].match_string)) {
390 av_log(hwdev, AV_LOG_VERBOSE, "Matched driver string "
391 "as known nonstandard driver \"%s\", setting "
393 vaapi_driver_quirks_table[i].friendly_name,
394 vaapi_driver_quirks_table[i].quirks);
395 hwctx->driver_quirks |=
396 vaapi_driver_quirks_table[i].quirks;
400 if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
401 av_log(hwdev, AV_LOG_VERBOSE, "Driver not found in known "
402 "nonstandard list, using standard behaviour.\n");
405 av_log(hwdev, AV_LOG_VERBOSE, "Driver has no vendor string, "
406 "assuming standard behaviour.\n");
413 av_freep(&ctx->formats);
418 static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
420 VAAPIDeviceContext *ctx = hwdev->internal->priv;
422 av_freep(&ctx->formats);
425 static void vaapi_buffer_free(void *opaque, uint8_t *data)
427 AVHWFramesContext *hwfc = opaque;
428 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
429 VASurfaceID surface_id;
432 surface_id = (VASurfaceID)(uintptr_t)data;
434 vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
435 if (vas != VA_STATUS_SUCCESS) {
436 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
437 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
441 static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
443 AVHWFramesContext *hwfc = opaque;
444 VAAPIFramesContext *ctx = hwfc->internal->priv;
445 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
446 AVVAAPIFramesContext *avfc = hwfc->hwctx;
447 VASurfaceID surface_id;
451 if (hwfc->initial_pool_size > 0 &&
452 avfc->nb_surfaces >= hwfc->initial_pool_size)
455 vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
456 hwfc->width, hwfc->height,
458 ctx->attributes, ctx->nb_attributes);
459 if (vas != VA_STATUS_SUCCESS) {
460 av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
461 "%d (%s).\n", vas, vaErrorStr(vas));
464 av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
466 ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
467 sizeof(surface_id), &vaapi_buffer_free,
468 hwfc, AV_BUFFER_FLAG_READONLY);
470 vaDestroySurfaces(hwctx->display, &surface_id, 1);
474 if (hwfc->initial_pool_size > 0) {
475 // This is a fixed-size pool, so we must still be in the initial
476 // allocation sequence.
477 av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
478 avfc->surface_ids[avfc->nb_surfaces] = surface_id;
485 static int vaapi_frames_init(AVHWFramesContext *hwfc)
487 AVVAAPIFramesContext *avfc = hwfc->hwctx;
488 VAAPIFramesContext *ctx = hwfc->internal->priv;
489 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
490 const VAAPIFormatDescriptor *desc;
491 VAImageFormat *expected_format;
492 AVBufferRef *test_surface = NULL;
493 VASurfaceID test_surface_id;
498 desc = vaapi_format_from_pix_fmt(hwfc->sw_format);
500 av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
501 av_get_pix_fmt_name(hwfc->sw_format));
502 return AVERROR(EINVAL);
506 if (!(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
507 int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
508 int need_pixel_format = 1;
509 for (i = 0; i < avfc->nb_attributes; i++) {
510 if (avfc->attributes[i].type == VASurfaceAttribMemoryType)
511 need_memory_type = 0;
512 if (avfc->attributes[i].type == VASurfaceAttribPixelFormat)
513 need_pixel_format = 0;
516 avfc->nb_attributes + need_memory_type + need_pixel_format;
518 ctx->attributes = av_malloc(ctx->nb_attributes *
519 sizeof(*ctx->attributes));
520 if (!ctx->attributes) {
521 err = AVERROR(ENOMEM);
525 for (i = 0; i < avfc->nb_attributes; i++)
526 ctx->attributes[i] = avfc->attributes[i];
527 if (need_memory_type) {
528 ctx->attributes[i++] = (VASurfaceAttrib) {
529 .type = VASurfaceAttribMemoryType,
530 .flags = VA_SURFACE_ATTRIB_SETTABLE,
531 .value.type = VAGenericValueTypeInteger,
532 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
535 if (need_pixel_format) {
536 ctx->attributes[i++] = (VASurfaceAttrib) {
537 .type = VASurfaceAttribPixelFormat,
538 .flags = VA_SURFACE_ATTRIB_SETTABLE,
539 .value.type = VAGenericValueTypeInteger,
540 .value.value.i = desc->fourcc,
543 av_assert0(i == ctx->nb_attributes);
545 ctx->attributes = NULL;
546 ctx->nb_attributes = 0;
549 ctx->rt_format = desc->rt_format;
551 if (hwfc->initial_pool_size > 0) {
552 // This pool will be usable as a render target, so we need to store
553 // all of the surface IDs somewhere that vaCreateContext() calls
554 // will be able to access them.
555 avfc->nb_surfaces = 0;
556 avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
557 sizeof(*avfc->surface_ids));
558 if (!avfc->surface_ids) {
559 err = AVERROR(ENOMEM);
563 // This pool allows dynamic sizing, and will not be usable as a
565 avfc->nb_surfaces = 0;
566 avfc->surface_ids = NULL;
569 hwfc->internal->pool_internal =
570 av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
571 &vaapi_pool_alloc, NULL);
572 if (!hwfc->internal->pool_internal) {
573 av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
574 err = AVERROR(ENOMEM);
579 // Allocate a single surface to test whether vaDeriveImage() is going
580 // to work for the specific configuration.
582 test_surface = av_buffer_pool_get(hwfc->pool);
584 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
585 "user-configured buffer pool.\n");
586 err = AVERROR(ENOMEM);
590 test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
592 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
593 "internal buffer pool.\n");
594 err = AVERROR(ENOMEM);
598 test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
600 ctx->derive_works = 0;
602 err = vaapi_get_image_format(hwfc->device_ctx,
603 hwfc->sw_format, &expected_format);
605 vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
606 if (vas == VA_STATUS_SUCCESS) {
607 if (expected_format->fourcc == test_image.format.fourcc) {
608 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
609 ctx->derive_works = 1;
611 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
612 "derived image format %08x does not match "
613 "expected format %08x.\n",
614 expected_format->fourcc, test_image.format.fourcc);
616 vaDestroyImage(hwctx->display, test_image.image_id);
618 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
619 "deriving image does not work: "
620 "%d (%s).\n", vas, vaErrorStr(vas));
623 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
624 "image format is not supported.\n");
627 av_buffer_unref(&test_surface);
631 av_buffer_unref(&test_surface);
632 av_freep(&avfc->surface_ids);
633 av_freep(&ctx->attributes);
637 static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
639 AVVAAPIFramesContext *avfc = hwfc->hwctx;
640 VAAPIFramesContext *ctx = hwfc->internal->priv;
642 av_freep(&avfc->surface_ids);
643 av_freep(&ctx->attributes);
646 static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
648 frame->buf[0] = av_buffer_pool_get(hwfc->pool);
650 return AVERROR(ENOMEM);
652 frame->data[3] = frame->buf[0]->data;
653 frame->format = AV_PIX_FMT_VAAPI;
654 frame->width = hwfc->width;
655 frame->height = hwfc->height;
660 static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
661 enum AVHWFrameTransferDirection dir,
662 enum AVPixelFormat **formats)
664 VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
665 enum AVPixelFormat *pix_fmts;
666 int i, k, sw_format_available;
668 sw_format_available = 0;
669 for (i = 0; i < ctx->nb_formats; i++) {
670 if (ctx->formats[i].pix_fmt == hwfc->sw_format)
671 sw_format_available = 1;
674 pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
676 return AVERROR(ENOMEM);
678 if (sw_format_available) {
679 pix_fmts[0] = hwfc->sw_format;
684 for (i = 0; i < ctx->nb_formats; i++) {
685 if (ctx->formats[i].pix_fmt == hwfc->sw_format)
687 av_assert0(k < ctx->nb_formats);
688 pix_fmts[k++] = ctx->formats[i].pix_fmt;
690 pix_fmts[k] = AV_PIX_FMT_NONE;
696 static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
697 HWMapDescriptor *hwmap)
699 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
700 VAAPIMapping *map = hwmap->priv;
701 VASurfaceID surface_id;
704 surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
705 av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
707 vas = vaUnmapBuffer(hwctx->display, map->image.buf);
708 if (vas != VA_STATUS_SUCCESS) {
709 av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
710 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
713 if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
714 !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
715 vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
716 0, 0, hwfc->width, hwfc->height,
717 0, 0, hwfc->width, hwfc->height);
718 if (vas != VA_STATUS_SUCCESS) {
719 av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
720 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
724 vas = vaDestroyImage(hwctx->display, map->image.image_id);
725 if (vas != VA_STATUS_SUCCESS) {
726 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
727 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
733 static int vaapi_map_frame(AVHWFramesContext *hwfc,
734 AVFrame *dst, const AVFrame *src, int flags)
736 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
737 VAAPIFramesContext *ctx = hwfc->internal->priv;
738 VASurfaceID surface_id;
739 const VAAPIFormatDescriptor *desc;
740 VAImageFormat *image_format;
743 void *address = NULL;
746 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
747 av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
749 if (!ctx->derive_works && (flags & AV_HWFRAME_MAP_DIRECT)) {
750 // Requested direct mapping but it is not possible.
751 return AVERROR(EINVAL);
753 if (dst->format == AV_PIX_FMT_NONE)
754 dst->format = hwfc->sw_format;
755 if (dst->format != hwfc->sw_format && (flags & AV_HWFRAME_MAP_DIRECT)) {
756 // Requested direct mapping but the formats do not match.
757 return AVERROR(EINVAL);
760 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
762 // Requested format is not a valid output format.
763 return AVERROR(EINVAL);
766 map = av_malloc(sizeof(*map));
768 return AVERROR(ENOMEM);
770 map->image.image_id = VA_INVALID_ID;
772 vas = vaSyncSurface(hwctx->display, surface_id);
773 if (vas != VA_STATUS_SUCCESS) {
774 av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
775 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
780 // The memory which we map using derive need not be connected to the CPU
781 // in a way conducive to fast access. On Gen7-Gen9 Intel graphics, the
782 // memory is mappable but not cached, so normal memcpy()-like access is
783 // very slow to read it (but writing is ok). It is possible to read much
784 // faster with a copy routine which is aware of the limitation, but we
785 // assume for now that the user is not aware of that and would therefore
786 // prefer not to be given direct-mapped memory if they request read access.
787 if (ctx->derive_works && dst->format == hwfc->sw_format &&
788 ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
789 vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
790 if (vas != VA_STATUS_SUCCESS) {
791 av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
792 "surface %#x: %d (%s).\n",
793 surface_id, vas, vaErrorStr(vas));
797 if (map->image.format.fourcc != image_format->fourcc) {
798 av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
799 "is in wrong format: expected %#08x, got %#08x.\n",
800 surface_id, image_format->fourcc, map->image.format.fourcc);
804 map->flags |= AV_HWFRAME_MAP_DIRECT;
806 vas = vaCreateImage(hwctx->display, image_format,
807 hwfc->width, hwfc->height, &map->image);
808 if (vas != VA_STATUS_SUCCESS) {
809 av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
810 "surface %#x: %d (%s).\n",
811 surface_id, vas, vaErrorStr(vas));
815 if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
816 vas = vaGetImage(hwctx->display, surface_id, 0, 0,
817 hwfc->width, hwfc->height, map->image.image_id);
818 if (vas != VA_STATUS_SUCCESS) {
819 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
820 "surface %#x: %d (%s).\n",
821 surface_id, vas, vaErrorStr(vas));
828 vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
829 if (vas != VA_STATUS_SUCCESS) {
830 av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
831 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
836 err = ff_hwframe_map_create(src->hw_frames_ctx,
837 dst, src, &vaapi_unmap_frame, map);
841 dst->width = src->width;
842 dst->height = src->height;
844 for (i = 0; i < map->image.num_planes; i++) {
845 dst->data[i] = (uint8_t*)address + map->image.offsets[i];
846 dst->linesize[i] = map->image.pitches[i];
849 desc = vaapi_format_from_fourcc(map->image.format.fourcc);
850 if (desc && desc->chroma_planes_swapped) {
851 // Chroma planes are YVU rather than YUV, so swap them.
852 FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
860 vaUnmapBuffer(hwctx->display, map->image.buf);
861 if (map->image.image_id != VA_INVALID_ID)
862 vaDestroyImage(hwctx->display, map->image.image_id);
868 static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
869 AVFrame *dst, const AVFrame *src)
874 if (dst->width > hwfc->width || dst->height > hwfc->height)
875 return AVERROR(EINVAL);
877 map = av_frame_alloc();
879 return AVERROR(ENOMEM);
880 map->format = dst->format;
882 err = vaapi_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
886 map->width = dst->width;
887 map->height = dst->height;
889 err = av_frame_copy(dst, map);
899 static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
900 AVFrame *dst, const AVFrame *src)
905 if (src->width > hwfc->width || src->height > hwfc->height)
906 return AVERROR(EINVAL);
908 map = av_frame_alloc();
910 return AVERROR(ENOMEM);
911 map->format = src->format;
913 err = vaapi_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
917 map->width = src->width;
918 map->height = src->height;
920 err = av_frame_copy(map, src);
930 static int vaapi_map_to_memory(AVHWFramesContext *hwfc, AVFrame *dst,
931 const AVFrame *src, int flags)
935 if (dst->format != AV_PIX_FMT_NONE) {
936 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, NULL);
938 return AVERROR(ENOSYS);
941 err = vaapi_map_frame(hwfc, dst, src, flags);
945 err = av_frame_copy_props(dst, src);
954 #define DRM_MAP(va, layers, ...) { \
959 static const struct {
961 int nb_layer_formats;
962 uint32_t layer_formats[AV_DRM_MAX_PLANES];
963 } vaapi_drm_format_map[] = {
965 DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88),
967 DRM_MAP(NV12, 1, DRM_FORMAT_NV12),
968 #if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16)
969 DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
971 DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888),
972 DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888),
973 DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888),
974 DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888),
975 #ifdef VA_FOURCC_ABGR
976 DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888),
977 DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888),
979 DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888),
980 DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888),
984 static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
985 HWMapDescriptor *hwmap)
987 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
989 VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
991 av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id);
993 vaDestroySurfaces(dst_dev->display, &surface_id, 1);
996 static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
997 const AVFrame *src, int flags)
999 AVHWFramesContext *dst_fc =
1000 (AVHWFramesContext*)dst->hw_frames_ctx->data;
1001 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
1002 const AVDRMFrameDescriptor *desc;
1003 const VAAPIFormatDescriptor *format_desc;
1004 VASurfaceID surface_id;
1009 unsigned long buffer_handle;
1010 VASurfaceAttribExternalBuffers buffer_desc;
1011 VASurfaceAttrib attrs[2] = {
1013 .type = VASurfaceAttribMemoryType,
1014 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1015 .value.type = VAGenericValueTypeInteger,
1016 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
1019 .type = VASurfaceAttribExternalBufferDescriptor,
1020 .flags = VA_SURFACE_ATTRIB_SETTABLE,
1021 .value.type = VAGenericValueTypePointer,
1022 .value.value.p = &buffer_desc,
1026 desc = (AVDRMFrameDescriptor*)src->data[0];
1028 if (desc->nb_objects != 1) {
1029 av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames "
1030 "made from a single DRM object.\n");
1031 return AVERROR(EINVAL);
1035 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1036 if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats)
1038 for (j = 0; j < desc->nb_layers; j++) {
1039 if (desc->layers[j].format !=
1040 vaapi_drm_format_map[i].layer_formats[j])
1043 if (j != desc->nb_layers)
1045 va_fourcc = vaapi_drm_format_map[i].va_fourcc;
1049 av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported "
1051 return AVERROR(EINVAL);
1054 av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
1055 "%08x.\n", desc->objects[0].fd, va_fourcc);
1057 format_desc = vaapi_format_from_fourcc(va_fourcc);
1058 av_assert0(format_desc);
1060 buffer_handle = desc->objects[0].fd;
1061 buffer_desc.pixel_format = va_fourcc;
1062 buffer_desc.width = src_fc->width;
1063 buffer_desc.height = src_fc->height;
1064 buffer_desc.data_size = desc->objects[0].size;
1065 buffer_desc.buffers = &buffer_handle;
1066 buffer_desc.num_buffers = 1;
1067 buffer_desc.flags = 0;
1070 for (i = 0; i < desc->nb_layers; i++) {
1071 for (j = 0; j < desc->layers[i].nb_planes; j++) {
1072 buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
1073 buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
1077 buffer_desc.num_planes = k;
1079 if (format_desc->chroma_planes_swapped &&
1080 buffer_desc.num_planes == 3) {
1081 FFSWAP(uint32_t, buffer_desc.pitches[1], buffer_desc.pitches[2]);
1082 FFSWAP(uint32_t, buffer_desc.offsets[1], buffer_desc.offsets[2]);
1085 vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
1086 src->width, src->height,
1088 attrs, FF_ARRAY_ELEMS(attrs));
1089 if (vas != VA_STATUS_SUCCESS) {
1090 av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
1091 "object: %d (%s).\n", vas, vaErrorStr(vas));
1092 return AVERROR(EIO);
1094 av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
1096 err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
1097 &vaapi_unmap_from_drm,
1098 (void*)(uintptr_t)surface_id);
1102 dst->width = src->width;
1103 dst->height = src->height;
1104 dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
1106 av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
1107 "surface %#x.\n", desc->objects[0].fd, surface_id);
1112 #if VA_CHECK_VERSION(1, 1, 0)
1113 static void vaapi_unmap_to_drm_esh(AVHWFramesContext *hwfc,
1114 HWMapDescriptor *hwmap)
1116 AVDRMFrameDescriptor *drm_desc = hwmap->priv;
1119 for (i = 0; i < drm_desc->nb_objects; i++)
1120 close(drm_desc->objects[i].fd);
1122 av_freep(&drm_desc);
1125 static int vaapi_map_to_drm_esh(AVHWFramesContext *hwfc, AVFrame *dst,
1126 const AVFrame *src, int flags)
1128 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1129 VASurfaceID surface_id;
1131 VADRMPRIMESurfaceDescriptor va_desc;
1132 AVDRMFrameDescriptor *drm_desc = NULL;
1133 uint32_t export_flags;
1136 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1138 export_flags = VA_EXPORT_SURFACE_SEPARATE_LAYERS;
1139 if (flags & AV_HWFRAME_MAP_READ)
1140 export_flags |= VA_EXPORT_SURFACE_READ_ONLY;
1141 if (flags & AV_HWFRAME_MAP_WRITE)
1142 export_flags |= VA_EXPORT_SURFACE_WRITE_ONLY;
1144 vas = vaExportSurfaceHandle(hwctx->display, surface_id,
1145 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
1146 export_flags, &va_desc);
1147 if (vas != VA_STATUS_SUCCESS) {
1148 if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
1149 return AVERROR(ENOSYS);
1150 av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
1151 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
1152 return AVERROR(EIO);
1155 drm_desc = av_mallocz(sizeof(*drm_desc));
1157 err = AVERROR(ENOMEM);
1161 // By some bizarre coincidence, these structures are very similar...
1162 drm_desc->nb_objects = va_desc.num_objects;
1163 for (i = 0; i < va_desc.num_objects; i++) {
1164 drm_desc->objects[i].fd = va_desc.objects[i].fd;
1165 drm_desc->objects[i].size = va_desc.objects[i].size;
1166 drm_desc->objects[i].format_modifier =
1167 va_desc.objects[i].drm_format_modifier;
1169 drm_desc->nb_layers = va_desc.num_layers;
1170 for (i = 0; i < va_desc.num_layers; i++) {
1171 drm_desc->layers[i].format = va_desc.layers[i].drm_format;
1172 drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes;
1173 for (j = 0; j < va_desc.layers[i].num_planes; j++) {
1174 drm_desc->layers[i].planes[j].object_index =
1175 va_desc.layers[i].object_index[j];
1176 drm_desc->layers[i].planes[j].offset =
1177 va_desc.layers[i].offset[j];
1178 drm_desc->layers[i].planes[j].pitch =
1179 va_desc.layers[i].pitch[j];
1183 err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
1184 &vaapi_unmap_to_drm_esh, drm_desc);
1188 dst->width = src->width;
1189 dst->height = src->height;
1190 dst->data[0] = (uint8_t*)drm_desc;
1195 for (i = 0; i < va_desc.num_objects; i++)
1196 close(va_desc.objects[i].fd);
1197 av_freep(&drm_desc);
1202 #if VA_CHECK_VERSION(0, 36, 0)
1203 typedef struct VAAPIDRMImageBufferMapping {
1205 VABufferInfo buffer_info;
1207 AVDRMFrameDescriptor drm_desc;
1208 } VAAPIDRMImageBufferMapping;
1210 static void vaapi_unmap_to_drm_abh(AVHWFramesContext *hwfc,
1211 HWMapDescriptor *hwmap)
1213 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1214 VAAPIDRMImageBufferMapping *mapping = hwmap->priv;
1215 VASurfaceID surface_id;
1218 surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
1219 av_log(hwfc, AV_LOG_DEBUG, "Unmap VAAPI surface %#x from DRM.\n",
1222 // DRM PRIME file descriptors are closed by vaReleaseBufferHandle(),
1223 // so we shouldn't close them separately.
1225 vas = vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1226 if (vas != VA_STATUS_SUCCESS) {
1227 av_log(hwfc, AV_LOG_ERROR, "Failed to release buffer "
1228 "handle of image %#x (derived from surface %#x): "
1229 "%d (%s).\n", mapping->image.buf, surface_id,
1230 vas, vaErrorStr(vas));
1233 vas = vaDestroyImage(hwctx->display, mapping->image.image_id);
1234 if (vas != VA_STATUS_SUCCESS) {
1235 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image "
1236 "derived from surface %#x: %d (%s).\n",
1237 surface_id, vas, vaErrorStr(vas));
1243 static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst,
1244 const AVFrame *src, int flags)
1246 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1247 VAAPIDRMImageBufferMapping *mapping = NULL;
1248 VASurfaceID surface_id;
1252 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1253 av_log(hwfc, AV_LOG_DEBUG, "Map VAAPI surface %#x to DRM.\n",
1256 mapping = av_mallocz(sizeof(*mapping));
1258 return AVERROR(ENOMEM);
1260 vas = vaDeriveImage(hwctx->display, surface_id,
1262 if (vas != VA_STATUS_SUCCESS) {
1263 av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
1264 "surface %#x: %d (%s).\n",
1265 surface_id, vas, vaErrorStr(vas));
1270 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1271 if (vaapi_drm_format_map[i].va_fourcc ==
1272 mapping->image.format.fourcc)
1275 if (i >= FF_ARRAY_ELEMS(vaapi_drm_format_map)) {
1276 av_log(hwfc, AV_LOG_ERROR, "No matching DRM format for "
1277 "VAAPI format %#x.\n", mapping->image.format.fourcc);
1278 err = AVERROR(EINVAL);
1282 mapping->buffer_info.mem_type =
1283 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
1285 mapping->drm_desc.nb_layers =
1286 vaapi_drm_format_map[i].nb_layer_formats;
1287 if (mapping->drm_desc.nb_layers > 1) {
1288 if (mapping->drm_desc.nb_layers != mapping->image.num_planes) {
1289 av_log(hwfc, AV_LOG_ERROR, "Image properties do not match "
1290 "expected format: got %d planes, but expected %d.\n",
1291 mapping->image.num_planes, mapping->drm_desc.nb_layers);
1292 err = AVERROR(EINVAL);
1296 for(p = 0; p < mapping->drm_desc.nb_layers; p++) {
1297 mapping->drm_desc.layers[p] = (AVDRMLayerDescriptor) {
1298 .format = vaapi_drm_format_map[i].layer_formats[p],
1302 .offset = mapping->image.offsets[p],
1303 .pitch = mapping->image.pitches[p],
1308 mapping->drm_desc.layers[0].format =
1309 vaapi_drm_format_map[i].layer_formats[0];
1310 mapping->drm_desc.layers[0].nb_planes = mapping->image.num_planes;
1311 for (p = 0; p < mapping->image.num_planes; p++) {
1312 mapping->drm_desc.layers[0].planes[p] = (AVDRMPlaneDescriptor) {
1314 .offset = mapping->image.offsets[p],
1315 .pitch = mapping->image.pitches[p],
1320 vas = vaAcquireBufferHandle(hwctx->display, mapping->image.buf,
1321 &mapping->buffer_info);
1322 if (vas != VA_STATUS_SUCCESS) {
1323 av_log(hwfc, AV_LOG_ERROR, "Failed to get buffer "
1324 "handle from image %#x (derived from surface %#x): "
1325 "%d (%s).\n", mapping->image.buf, surface_id,
1326 vas, vaErrorStr(vas));
1331 av_log(hwfc, AV_LOG_DEBUG, "DRM PRIME fd is %ld.\n",
1332 mapping->buffer_info.handle);
1334 mapping->drm_desc.nb_objects = 1;
1335 mapping->drm_desc.objects[0] = (AVDRMObjectDescriptor) {
1336 .fd = mapping->buffer_info.handle,
1337 .size = mapping->image.data_size,
1338 // There is no way to get the format modifier with this API.
1339 .format_modifier = DRM_FORMAT_MOD_INVALID,
1342 err = ff_hwframe_map_create(src->hw_frames_ctx,
1343 dst, src, &vaapi_unmap_to_drm_abh,
1348 dst->data[0] = (uint8_t*)&mapping->drm_desc;
1349 dst->width = src->width;
1350 dst->height = src->height;
1355 vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1357 vaDestroyImage(hwctx->display, mapping->image.image_id);
1364 static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
1365 const AVFrame *src, int flags)
1367 #if VA_CHECK_VERSION(1, 1, 0)
1369 err = vaapi_map_to_drm_esh(hwfc, dst, src, flags);
1370 if (err != AVERROR(ENOSYS))
1373 #if VA_CHECK_VERSION(0, 36, 0)
1374 return vaapi_map_to_drm_abh(hwfc, dst, src, flags);
1376 return AVERROR(ENOSYS);
1379 #endif /* CONFIG_LIBDRM */
1381 static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
1382 const AVFrame *src, int flags)
1384 switch (src->format) {
1386 case AV_PIX_FMT_DRM_PRIME:
1387 return vaapi_map_from_drm(hwfc, dst, src, flags);
1390 return AVERROR(ENOSYS);
1394 static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
1395 const AVFrame *src, int flags)
1397 switch (dst->format) {
1399 case AV_PIX_FMT_DRM_PRIME:
1400 return vaapi_map_to_drm(hwfc, dst, src, flags);
1403 return vaapi_map_to_memory(hwfc, dst, src, flags);
1407 static void vaapi_device_free(AVHWDeviceContext *ctx)
1409 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1410 VAAPIDevicePriv *priv = ctx->user_opaque;
1413 vaTerminate(hwctx->display);
1416 if (priv->x11_display)
1417 XCloseDisplay(priv->x11_display);
1420 if (priv->drm_fd >= 0)
1421 close(priv->drm_fd);
1427 static void vaapi_device_log_error(void *context, const char *message)
1429 AVHWDeviceContext *ctx = context;
1431 av_log(ctx, AV_LOG_ERROR, "libva: %s", message);
1434 static void vaapi_device_log_info(void *context, const char *message)
1436 AVHWDeviceContext *ctx = context;
1438 av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message);
1442 static int vaapi_device_connect(AVHWDeviceContext *ctx,
1445 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1450 vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
1451 vaSetInfoCallback (display, &vaapi_device_log_info, ctx);
1454 hwctx->display = display;
1456 vas = vaInitialize(display, &major, &minor);
1457 if (vas != VA_STATUS_SUCCESS) {
1458 av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
1459 "connection: %d (%s).\n", vas, vaErrorStr(vas));
1460 return AVERROR(EIO);
1462 av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
1463 "version %d.%d\n", major, minor);
1468 static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
1469 AVDictionary *opts, int flags)
1471 VAAPIDevicePriv *priv;
1472 VADisplay display = NULL;
1473 const AVDictionaryEntry *ent;
1474 int try_drm, try_x11, try_all;
1476 priv = av_mallocz(sizeof(*priv));
1478 return AVERROR(ENOMEM);
1482 ctx->user_opaque = priv;
1483 ctx->free = vaapi_device_free;
1485 ent = av_dict_get(opts, "connection_type", NULL, 0);
1487 try_all = try_drm = try_x11 = 0;
1488 if (!strcmp(ent->value, "drm")) {
1490 } else if (!strcmp(ent->value, "x11")) {
1493 av_log(ctx, AV_LOG_ERROR, "Invalid connection type %s.\n",
1495 return AVERROR(EINVAL);
1499 try_drm = HAVE_VAAPI_DRM;
1500 try_x11 = HAVE_VAAPI_X11;
1504 if (!display && try_x11) {
1505 // Try to open the device as an X11 display.
1506 priv->x11_display = XOpenDisplay(device);
1507 if (!priv->x11_display) {
1508 av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
1509 "%s.\n", XDisplayName(device));
1511 display = vaGetDisplay(priv->x11_display);
1513 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1514 "from X11 display %s.\n", XDisplayName(device));
1515 return AVERROR_UNKNOWN;
1518 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1519 "X11 display %s.\n", XDisplayName(device));
1525 while (!display && try_drm) {
1526 // If the device is specified, try to open it as a DRM device node.
1527 // If not, look for a usable render node, possibly restricted to those
1528 // using a specified kernel driver.
1529 int loglevel = try_all ? AV_LOG_VERBOSE : AV_LOG_ERROR;
1531 priv->drm_fd = open(device, O_RDWR);
1532 if (priv->drm_fd < 0) {
1533 av_log(ctx, loglevel, "Failed to open %s as "
1534 "DRM device node.\n", device);
1538 const AVDictionaryEntry *kernel_driver;
1540 int n, max_devices = 8;
1541 kernel_driver = av_dict_get(opts, "kernel_driver", NULL, 0);
1542 for (n = 0; n < max_devices; n++) {
1543 snprintf(path, sizeof(path),
1544 "/dev/dri/renderD%d", 128 + n);
1545 priv->drm_fd = open(path, O_RDWR);
1546 if (priv->drm_fd < 0) {
1547 av_log(ctx, AV_LOG_VERBOSE, "Cannot open "
1548 "DRM render node for device %d.\n", n);
1552 if (kernel_driver) {
1554 info = drmGetVersion(priv->drm_fd);
1555 if (strcmp(kernel_driver->value, info->name)) {
1556 av_log(ctx, AV_LOG_VERBOSE, "Ignoring device %d "
1557 "with non-matching kernel driver (%s).\n",
1559 drmFreeVersion(info);
1560 close(priv->drm_fd);
1564 av_log(ctx, AV_LOG_VERBOSE, "Trying to use "
1565 "DRM render node for device %d, "
1566 "with matching kernel driver (%s).\n",
1568 drmFreeVersion(info);
1572 av_log(ctx, AV_LOG_VERBOSE, "Trying to use "
1573 "DRM render node for device %d.\n", n);
1577 if (n >= max_devices)
1581 display = vaGetDisplayDRM(priv->drm_fd);
1583 av_log(ctx, AV_LOG_VERBOSE, "Cannot open a VA display "
1584 "from DRM device %s.\n", device);
1585 return AVERROR_EXTERNAL;
1593 av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1594 "device %s.\n", device);
1596 av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1597 "any default device.\n");
1598 return AVERROR(EINVAL);
1601 ent = av_dict_get(opts, "driver", NULL, 0);
1603 #if VA_CHECK_VERSION(0, 38, 0)
1605 vas = vaSetDriverName(display, ent->value);
1606 if (vas != VA_STATUS_SUCCESS) {
1607 av_log(ctx, AV_LOG_ERROR, "Failed to set driver name to "
1608 "%s: %d (%s).\n", ent->value, vas, vaErrorStr(vas));
1609 vaTerminate(display);
1610 return AVERROR_EXTERNAL;
1613 av_log(ctx, AV_LOG_WARNING, "Driver name setting is not "
1614 "supported with this VAAPI version.\n");
1618 return vaapi_device_connect(ctx, display);
1621 static int vaapi_device_derive(AVHWDeviceContext *ctx,
1622 AVHWDeviceContext *src_ctx, int flags)
1625 if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
1626 AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
1628 VAAPIDevicePriv *priv;
1630 if (src_hwctx->fd < 0) {
1631 av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
1632 "device to derive a VA display from.\n");
1633 return AVERROR(EINVAL);
1636 priv = av_mallocz(sizeof(*priv));
1638 return AVERROR(ENOMEM);
1640 // Inherits the fd from the source context, which will close it.
1643 ctx->user_opaque = priv;
1644 ctx->free = &vaapi_device_free;
1646 display = vaGetDisplayDRM(src_hwctx->fd);
1648 av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
1650 return AVERROR(EIO);
1653 return vaapi_device_connect(ctx, display);
1656 return AVERROR(ENOSYS);
1659 const HWContextType ff_hwcontext_type_vaapi = {
1660 .type = AV_HWDEVICE_TYPE_VAAPI,
1663 .device_hwctx_size = sizeof(AVVAAPIDeviceContext),
1664 .device_priv_size = sizeof(VAAPIDeviceContext),
1665 .device_hwconfig_size = sizeof(AVVAAPIHWConfig),
1666 .frames_hwctx_size = sizeof(AVVAAPIFramesContext),
1667 .frames_priv_size = sizeof(VAAPIFramesContext),
1669 .device_create = &vaapi_device_create,
1670 .device_derive = &vaapi_device_derive,
1671 .device_init = &vaapi_device_init,
1672 .device_uninit = &vaapi_device_uninit,
1673 .frames_get_constraints = &vaapi_frames_get_constraints,
1674 .frames_init = &vaapi_frames_init,
1675 .frames_uninit = &vaapi_frames_uninit,
1676 .frames_get_buffer = &vaapi_get_buffer,
1677 .transfer_get_formats = &vaapi_transfer_get_formats,
1678 .transfer_data_to = &vaapi_transfer_data_to,
1679 .transfer_data_from = &vaapi_transfer_data_from,
1680 .map_to = &vaapi_map_to,
1681 .map_from = &vaapi_map_from,
1683 .pix_fmts = (const enum AVPixelFormat[]) {