2 * This file is part of FFmpeg.
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 # include <va/va_x11.h>
25 # include <va/va_drm.h>
29 # include <va/va_drmcommon.h>
30 # include <drm_fourcc.h>
42 #include "hwcontext.h"
43 #include "hwcontext_internal.h"
44 #include "hwcontext_vaapi.h"
50 # include "hwcontext_drm.h"
53 typedef struct VAAPIDevicePriv {
61 typedef struct VAAPISurfaceFormat {
62 enum AVPixelFormat pix_fmt;
63 VAImageFormat image_format;
66 typedef struct VAAPIDeviceContext {
67 // Surface formats which can be used with this device.
68 VAAPISurfaceFormat *formats;
72 typedef struct VAAPIFramesContext {
73 // Surface attributes set at create time.
74 VASurfaceAttrib *attributes;
76 // RT format of the underlying surface (Intel driver ignores this anyway).
77 unsigned int rt_format;
78 // Whether vaDeriveImage works.
82 typedef struct VAAPIMapping {
83 // Handle to the derived or copied image which is mapped.
85 // The mapping flags actually used.
89 #define MAP(va, rt, av) { \
91 VA_RT_FORMAT_ ## rt, \
94 // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
95 // plane swap cases. The frame handling below tries to hide these.
98 unsigned int rt_format;
99 enum AVPixelFormat pix_fmt;
100 } vaapi_format_map[] = {
101 MAP(NV12, YUV420, NV12),
102 MAP(YV12, YUV420, YUV420P), // With U/V planes swapped.
103 MAP(IYUV, YUV420, YUV420P),
104 //MAP(I420, YUV420, YUV420P), // Not in libva but used by Intel driver.
105 #ifdef VA_FOURCC_YV16
106 MAP(YV16, YUV422, YUV422P), // With U/V planes swapped.
108 MAP(422H, YUV422, YUV422P),
109 MAP(UYVY, YUV422, UYVY422),
110 MAP(YUY2, YUV422, YUYV422),
111 MAP(Y800, YUV400, GRAY8),
112 #ifdef VA_FOURCC_P010
113 MAP(P010, YUV420_10BPP, P010),
115 MAP(BGRA, RGB32, BGRA),
116 MAP(BGRX, RGB32, BGR0),
117 MAP(RGBA, RGB32, RGBA),
118 MAP(RGBX, RGB32, RGB0),
119 #ifdef VA_FOURCC_ABGR
120 MAP(ABGR, RGB32, ABGR),
121 MAP(XBGR, RGB32, 0BGR),
123 MAP(ARGB, RGB32, ARGB),
124 MAP(XRGB, RGB32, 0RGB),
128 static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
131 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
132 if (vaapi_format_map[i].fourcc == fourcc)
133 return vaapi_format_map[i].pix_fmt;
134 return AV_PIX_FMT_NONE;
137 static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
138 enum AVPixelFormat pix_fmt,
139 VAImageFormat **image_format)
141 VAAPIDeviceContext *ctx = hwdev->internal->priv;
144 for (i = 0; i < ctx->nb_formats; i++) {
145 if (ctx->formats[i].pix_fmt == pix_fmt) {
147 *image_format = &ctx->formats[i].image_format;
151 return AVERROR(EINVAL);
154 static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
155 const void *hwconfig,
156 AVHWFramesConstraints *constraints)
158 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
159 const AVVAAPIHWConfig *config = hwconfig;
160 VAAPIDeviceContext *ctx = hwdev->internal->priv;
161 VASurfaceAttrib *attr_list = NULL;
163 enum AVPixelFormat pix_fmt;
165 int err, i, j, attr_count, pix_fmt_count;
168 !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
170 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
172 if (vas != VA_STATUS_SUCCESS) {
173 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
174 "%d (%s).\n", vas, vaErrorStr(vas));
175 err = AVERROR(ENOSYS);
179 attr_list = av_malloc(attr_count * sizeof(*attr_list));
181 err = AVERROR(ENOMEM);
185 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
186 attr_list, &attr_count);
187 if (vas != VA_STATUS_SUCCESS) {
188 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
189 "%d (%s).\n", vas, vaErrorStr(vas));
190 err = AVERROR(ENOSYS);
195 for (i = 0; i < attr_count; i++) {
196 switch (attr_list[i].type) {
197 case VASurfaceAttribPixelFormat:
198 fourcc = attr_list[i].value.value.i;
199 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
200 if (pix_fmt != AV_PIX_FMT_NONE) {
203 // Something unsupported - ignore.
206 case VASurfaceAttribMinWidth:
207 constraints->min_width = attr_list[i].value.value.i;
209 case VASurfaceAttribMinHeight:
210 constraints->min_height = attr_list[i].value.value.i;
212 case VASurfaceAttribMaxWidth:
213 constraints->max_width = attr_list[i].value.value.i;
215 case VASurfaceAttribMaxHeight:
216 constraints->max_height = attr_list[i].value.value.i;
220 if (pix_fmt_count == 0) {
221 // Nothing usable found. Presumably there exists something which
222 // works, so leave the set null to indicate unknown.
223 constraints->valid_sw_formats = NULL;
225 constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
227 if (!constraints->valid_sw_formats) {
228 err = AVERROR(ENOMEM);
232 for (i = j = 0; i < attr_count; i++) {
233 if (attr_list[i].type != VASurfaceAttribPixelFormat)
235 fourcc = attr_list[i].value.value.i;
236 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
237 if (pix_fmt != AV_PIX_FMT_NONE)
238 constraints->valid_sw_formats[j++] = pix_fmt;
240 av_assert0(j == pix_fmt_count);
241 constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
244 // No configuration supplied.
245 // Return the full set of image formats known by the implementation.
246 constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
248 if (!constraints->valid_sw_formats) {
249 err = AVERROR(ENOMEM);
252 for (i = 0; i < ctx->nb_formats; i++)
253 constraints->valid_sw_formats[i] = ctx->formats[i].pix_fmt;
254 constraints->valid_sw_formats[i] = AV_PIX_FMT_NONE;
257 constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
258 if (!constraints->valid_hw_formats) {
259 err = AVERROR(ENOMEM);
262 constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
263 constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
267 av_freep(&attr_list);
271 static const struct {
272 const char *friendly_name;
273 const char *match_string;
275 } vaapi_driver_quirks_table[] = {
277 "Intel i965 (Quick Sync)",
279 AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
284 AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
288 "Splitted-Desktop Systems VDPAU backend for VA-API",
289 AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES,
293 static int vaapi_device_init(AVHWDeviceContext *hwdev)
295 VAAPIDeviceContext *ctx = hwdev->internal->priv;
296 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
297 VAImageFormat *image_list = NULL;
299 const char *vendor_string;
300 int err, i, image_count;
301 enum AVPixelFormat pix_fmt;
304 image_count = vaMaxNumImageFormats(hwctx->display);
305 if (image_count <= 0) {
309 image_list = av_malloc(image_count * sizeof(*image_list));
311 err = AVERROR(ENOMEM);
314 vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
315 if (vas != VA_STATUS_SUCCESS) {
320 ctx->formats = av_malloc(image_count * sizeof(*ctx->formats));
322 err = AVERROR(ENOMEM);
326 for (i = 0; i < image_count; i++) {
327 fourcc = image_list[i].fourcc;
328 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
329 if (pix_fmt == AV_PIX_FMT_NONE) {
330 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
333 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
334 fourcc, av_get_pix_fmt_name(pix_fmt));
335 ctx->formats[ctx->nb_formats].pix_fmt = pix_fmt;
336 ctx->formats[ctx->nb_formats].image_format = image_list[i];
341 if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
342 av_log(hwdev, AV_LOG_VERBOSE, "Not detecting driver: "
343 "quirks set by user.\n");
345 // Detect the driver in use and set quirk flags if necessary.
346 vendor_string = vaQueryVendorString(hwctx->display);
347 hwctx->driver_quirks = 0;
349 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
350 if (strstr(vendor_string,
351 vaapi_driver_quirks_table[i].match_string)) {
352 av_log(hwdev, AV_LOG_VERBOSE, "Matched \"%s\" as known "
353 "driver \"%s\".\n", vendor_string,
354 vaapi_driver_quirks_table[i].friendly_name);
355 hwctx->driver_quirks |=
356 vaapi_driver_quirks_table[i].quirks;
360 if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
361 av_log(hwdev, AV_LOG_VERBOSE, "Unknown driver \"%s\", "
362 "assuming standard behaviour.\n", vendor_string);
370 av_freep(&ctx->formats);
375 static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
377 VAAPIDeviceContext *ctx = hwdev->internal->priv;
379 av_freep(&ctx->formats);
382 static void vaapi_buffer_free(void *opaque, uint8_t *data)
384 AVHWFramesContext *hwfc = opaque;
385 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
386 VASurfaceID surface_id;
389 surface_id = (VASurfaceID)(uintptr_t)data;
391 vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
392 if (vas != VA_STATUS_SUCCESS) {
393 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
394 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
398 static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
400 AVHWFramesContext *hwfc = opaque;
401 VAAPIFramesContext *ctx = hwfc->internal->priv;
402 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
403 AVVAAPIFramesContext *avfc = hwfc->hwctx;
404 VASurfaceID surface_id;
408 if (hwfc->initial_pool_size > 0 &&
409 avfc->nb_surfaces >= hwfc->initial_pool_size)
412 vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
413 hwfc->width, hwfc->height,
415 ctx->attributes, ctx->nb_attributes);
416 if (vas != VA_STATUS_SUCCESS) {
417 av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
418 "%d (%s).\n", vas, vaErrorStr(vas));
421 av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
423 ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
424 sizeof(surface_id), &vaapi_buffer_free,
425 hwfc, AV_BUFFER_FLAG_READONLY);
427 vaDestroySurfaces(hwctx->display, &surface_id, 1);
431 if (hwfc->initial_pool_size > 0) {
432 // This is a fixed-size pool, so we must still be in the initial
433 // allocation sequence.
434 av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
435 avfc->surface_ids[avfc->nb_surfaces] = surface_id;
442 static int vaapi_frames_init(AVHWFramesContext *hwfc)
444 AVVAAPIFramesContext *avfc = hwfc->hwctx;
445 VAAPIFramesContext *ctx = hwfc->internal->priv;
446 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
447 VAImageFormat *expected_format;
448 AVBufferRef *test_surface = NULL;
449 VASurfaceID test_surface_id;
453 unsigned int fourcc, rt_format;
455 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
456 if (vaapi_format_map[i].pix_fmt == hwfc->sw_format) {
457 fourcc = vaapi_format_map[i].fourcc;
458 rt_format = vaapi_format_map[i].rt_format;
462 if (i >= FF_ARRAY_ELEMS(vaapi_format_map)) {
463 av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
464 av_get_pix_fmt_name(hwfc->sw_format));
465 return AVERROR(EINVAL);
469 if (!(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
470 int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
471 int need_pixel_format = 1;
472 for (i = 0; i < avfc->nb_attributes; i++) {
473 if (ctx->attributes[i].type == VASurfaceAttribMemoryType)
474 need_memory_type = 0;
475 if (ctx->attributes[i].type == VASurfaceAttribPixelFormat)
476 need_pixel_format = 0;
479 avfc->nb_attributes + need_memory_type + need_pixel_format;
481 ctx->attributes = av_malloc(ctx->nb_attributes *
482 sizeof(*ctx->attributes));
483 if (!ctx->attributes) {
484 err = AVERROR(ENOMEM);
488 for (i = 0; i < avfc->nb_attributes; i++)
489 ctx->attributes[i] = avfc->attributes[i];
490 if (need_memory_type) {
491 ctx->attributes[i++] = (VASurfaceAttrib) {
492 .type = VASurfaceAttribMemoryType,
493 .flags = VA_SURFACE_ATTRIB_SETTABLE,
494 .value.type = VAGenericValueTypeInteger,
495 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
498 if (need_pixel_format) {
499 ctx->attributes[i++] = (VASurfaceAttrib) {
500 .type = VASurfaceAttribPixelFormat,
501 .flags = VA_SURFACE_ATTRIB_SETTABLE,
502 .value.type = VAGenericValueTypeInteger,
503 .value.value.i = fourcc,
506 av_assert0(i == ctx->nb_attributes);
508 ctx->attributes = NULL;
509 ctx->nb_attributes = 0;
512 ctx->rt_format = rt_format;
514 if (hwfc->initial_pool_size > 0) {
515 // This pool will be usable as a render target, so we need to store
516 // all of the surface IDs somewhere that vaCreateContext() calls
517 // will be able to access them.
518 avfc->nb_surfaces = 0;
519 avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
520 sizeof(*avfc->surface_ids));
521 if (!avfc->surface_ids) {
522 err = AVERROR(ENOMEM);
526 // This pool allows dynamic sizing, and will not be usable as a
528 avfc->nb_surfaces = 0;
529 avfc->surface_ids = NULL;
532 hwfc->internal->pool_internal =
533 av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
534 &vaapi_pool_alloc, NULL);
535 if (!hwfc->internal->pool_internal) {
536 av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
537 err = AVERROR(ENOMEM);
542 // Allocate a single surface to test whether vaDeriveImage() is going
543 // to work for the specific configuration.
545 test_surface = av_buffer_pool_get(hwfc->pool);
547 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
548 "user-configured buffer pool.\n");
549 err = AVERROR(ENOMEM);
553 test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
555 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
556 "internal buffer pool.\n");
557 err = AVERROR(ENOMEM);
561 test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
563 ctx->derive_works = 0;
565 err = vaapi_get_image_format(hwfc->device_ctx,
566 hwfc->sw_format, &expected_format);
568 vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
569 if (vas == VA_STATUS_SUCCESS) {
570 if (expected_format->fourcc == test_image.format.fourcc) {
571 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
572 ctx->derive_works = 1;
574 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
575 "derived image format %08x does not match "
576 "expected format %08x.\n",
577 expected_format->fourcc, test_image.format.fourcc);
579 vaDestroyImage(hwctx->display, test_image.image_id);
581 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
582 "deriving image does not work: "
583 "%d (%s).\n", vas, vaErrorStr(vas));
586 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
587 "image format is not supported.\n");
590 av_buffer_unref(&test_surface);
594 av_buffer_unref(&test_surface);
595 av_freep(&avfc->surface_ids);
596 av_freep(&ctx->attributes);
600 static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
602 AVVAAPIFramesContext *avfc = hwfc->hwctx;
603 VAAPIFramesContext *ctx = hwfc->internal->priv;
605 av_freep(&avfc->surface_ids);
606 av_freep(&ctx->attributes);
609 static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
611 frame->buf[0] = av_buffer_pool_get(hwfc->pool);
613 return AVERROR(ENOMEM);
615 frame->data[3] = frame->buf[0]->data;
616 frame->format = AV_PIX_FMT_VAAPI;
617 frame->width = hwfc->width;
618 frame->height = hwfc->height;
623 static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
624 enum AVHWFrameTransferDirection dir,
625 enum AVPixelFormat **formats)
627 VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
628 enum AVPixelFormat *pix_fmts, preferred_format;
631 preferred_format = hwfc->sw_format;
633 pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
635 return AVERROR(ENOMEM);
637 pix_fmts[0] = preferred_format;
639 for (i = 0; i < ctx->nb_formats; i++) {
640 if (ctx->formats[i].pix_fmt == preferred_format)
642 av_assert0(k < ctx->nb_formats);
643 pix_fmts[k++] = ctx->formats[i].pix_fmt;
645 av_assert0(k == ctx->nb_formats);
646 pix_fmts[k] = AV_PIX_FMT_NONE;
652 static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
653 HWMapDescriptor *hwmap)
655 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
656 VAAPIMapping *map = hwmap->priv;
657 VASurfaceID surface_id;
660 surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
661 av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
663 vas = vaUnmapBuffer(hwctx->display, map->image.buf);
664 if (vas != VA_STATUS_SUCCESS) {
665 av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
666 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
669 if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
670 !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
671 vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
672 0, 0, hwfc->width, hwfc->height,
673 0, 0, hwfc->width, hwfc->height);
674 if (vas != VA_STATUS_SUCCESS) {
675 av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
676 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
680 vas = vaDestroyImage(hwctx->display, map->image.image_id);
681 if (vas != VA_STATUS_SUCCESS) {
682 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
683 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
689 static int vaapi_map_frame(AVHWFramesContext *hwfc,
690 AVFrame *dst, const AVFrame *src, int flags)
692 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
693 VAAPIFramesContext *ctx = hwfc->internal->priv;
694 VASurfaceID surface_id;
695 VAImageFormat *image_format;
698 void *address = NULL;
701 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
702 av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
704 if (!ctx->derive_works && (flags & AV_HWFRAME_MAP_DIRECT)) {
705 // Requested direct mapping but it is not possible.
706 return AVERROR(EINVAL);
708 if (dst->format == AV_PIX_FMT_NONE)
709 dst->format = hwfc->sw_format;
710 if (dst->format != hwfc->sw_format && (flags & AV_HWFRAME_MAP_DIRECT)) {
711 // Requested direct mapping but the formats do not match.
712 return AVERROR(EINVAL);
715 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
717 // Requested format is not a valid output format.
718 return AVERROR(EINVAL);
721 map = av_malloc(sizeof(*map));
723 return AVERROR(ENOMEM);
725 map->image.image_id = VA_INVALID_ID;
727 vas = vaSyncSurface(hwctx->display, surface_id);
728 if (vas != VA_STATUS_SUCCESS) {
729 av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
730 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
735 // The memory which we map using derive need not be connected to the CPU
736 // in a way conducive to fast access. On Gen7-Gen9 Intel graphics, the
737 // memory is mappable but not cached, so normal memcpy()-like access is
738 // very slow to read it (but writing is ok). It is possible to read much
739 // faster with a copy routine which is aware of the limitation, but we
740 // assume for now that the user is not aware of that and would therefore
741 // prefer not to be given direct-mapped memory if they request read access.
742 if (ctx->derive_works && dst->format == hwfc->sw_format &&
743 ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
744 vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
745 if (vas != VA_STATUS_SUCCESS) {
746 av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
747 "surface %#x: %d (%s).\n",
748 surface_id, vas, vaErrorStr(vas));
752 if (map->image.format.fourcc != image_format->fourcc) {
753 av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
754 "is in wrong format: expected %#08x, got %#08x.\n",
755 surface_id, image_format->fourcc, map->image.format.fourcc);
759 map->flags |= AV_HWFRAME_MAP_DIRECT;
761 vas = vaCreateImage(hwctx->display, image_format,
762 hwfc->width, hwfc->height, &map->image);
763 if (vas != VA_STATUS_SUCCESS) {
764 av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
765 "surface %#x: %d (%s).\n",
766 surface_id, vas, vaErrorStr(vas));
770 if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
771 vas = vaGetImage(hwctx->display, surface_id, 0, 0,
772 hwfc->width, hwfc->height, map->image.image_id);
773 if (vas != VA_STATUS_SUCCESS) {
774 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
775 "surface %#x: %d (%s).\n",
776 surface_id, vas, vaErrorStr(vas));
783 vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
784 if (vas != VA_STATUS_SUCCESS) {
785 av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
786 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
791 err = ff_hwframe_map_create(src->hw_frames_ctx,
792 dst, src, &vaapi_unmap_frame, map);
796 dst->width = src->width;
797 dst->height = src->height;
799 for (i = 0; i < map->image.num_planes; i++) {
800 dst->data[i] = (uint8_t*)address + map->image.offsets[i];
801 dst->linesize[i] = map->image.pitches[i];
804 #ifdef VA_FOURCC_YV16
805 map->image.format.fourcc == VA_FOURCC_YV16 ||
807 map->image.format.fourcc == VA_FOURCC_YV12) {
808 // Chroma planes are YVU rather than YUV, so swap them.
809 FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
817 vaUnmapBuffer(hwctx->display, map->image.buf);
818 if (map->image.image_id != VA_INVALID_ID)
819 vaDestroyImage(hwctx->display, map->image.image_id);
825 static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
826 AVFrame *dst, const AVFrame *src)
831 if (dst->width > hwfc->width || dst->height > hwfc->height)
832 return AVERROR(EINVAL);
834 map = av_frame_alloc();
836 return AVERROR(ENOMEM);
837 map->format = dst->format;
839 err = vaapi_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
843 map->width = dst->width;
844 map->height = dst->height;
846 err = av_frame_copy(dst, map);
856 static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
857 AVFrame *dst, const AVFrame *src)
862 if (src->width > hwfc->width || src->height > hwfc->height)
863 return AVERROR(EINVAL);
865 map = av_frame_alloc();
867 return AVERROR(ENOMEM);
868 map->format = src->format;
870 err = vaapi_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
874 map->width = src->width;
875 map->height = src->height;
877 err = av_frame_copy(map, src);
887 static int vaapi_map_to_memory(AVHWFramesContext *hwfc, AVFrame *dst,
888 const AVFrame *src, int flags)
892 if (dst->format != AV_PIX_FMT_NONE) {
893 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, NULL);
895 return AVERROR(ENOSYS);
898 err = vaapi_map_frame(hwfc, dst, src, flags);
902 err = av_frame_copy_props(dst, src);
911 #define DRM_MAP(va, layers, ...) { \
916 static const struct {
918 int nb_layer_formats;
919 uint32_t layer_formats[AV_DRM_MAX_PLANES];
920 } vaapi_drm_format_map[] = {
922 DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88),
924 DRM_MAP(NV12, 1, DRM_FORMAT_NV12),
925 #if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16)
926 DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
928 DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888),
929 DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888),
930 DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888),
931 DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888),
932 #ifdef VA_FOURCC_ABGR
933 DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888),
934 DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888),
936 DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888),
937 DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888),
941 static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
942 HWMapDescriptor *hwmap)
944 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
946 VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
948 av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id);
950 vaDestroySurfaces(dst_dev->display, &surface_id, 1);
953 static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
954 const AVFrame *src, int flags)
956 AVHWFramesContext *dst_fc =
957 (AVHWFramesContext*)dst->hw_frames_ctx->data;
958 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
959 const AVDRMFrameDescriptor *desc;
960 VASurfaceID surface_id;
962 uint32_t va_fourcc, va_rt_format;
965 unsigned long buffer_handle;
966 VASurfaceAttribExternalBuffers buffer_desc;
967 VASurfaceAttrib attrs[2] = {
969 .type = VASurfaceAttribMemoryType,
970 .flags = VA_SURFACE_ATTRIB_SETTABLE,
971 .value.type = VAGenericValueTypeInteger,
972 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
975 .type = VASurfaceAttribExternalBufferDescriptor,
976 .flags = VA_SURFACE_ATTRIB_SETTABLE,
977 .value.type = VAGenericValueTypePointer,
978 .value.value.p = &buffer_desc,
982 desc = (AVDRMFrameDescriptor*)src->data[0];
984 if (desc->nb_objects != 1) {
985 av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames "
986 "made from a single DRM object.\n");
987 return AVERROR(EINVAL);
991 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
992 if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats)
994 for (j = 0; j < desc->nb_layers; j++) {
995 if (desc->layers[j].format !=
996 vaapi_drm_format_map[i].layer_formats[j])
999 if (j != desc->nb_layers)
1001 va_fourcc = vaapi_drm_format_map[i].va_fourcc;
1005 av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported "
1007 return AVERROR(EINVAL);
1010 av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
1011 "%08x.\n", desc->objects[0].fd, va_fourcc);
1013 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
1014 if (vaapi_format_map[i].fourcc == va_fourcc)
1015 va_rt_format = vaapi_format_map[i].rt_format;
1018 buffer_handle = desc->objects[0].fd;
1019 buffer_desc.pixel_format = va_fourcc;
1020 buffer_desc.width = src_fc->width;
1021 buffer_desc.height = src_fc->height;
1022 buffer_desc.data_size = desc->objects[0].size;
1023 buffer_desc.buffers = &buffer_handle;
1024 buffer_desc.num_buffers = 1;
1025 buffer_desc.flags = 0;
1028 for (i = 0; i < desc->nb_layers; i++) {
1029 for (j = 0; j < desc->layers[i].nb_planes; j++) {
1030 buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
1031 buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
1035 buffer_desc.num_planes = k;
1037 vas = vaCreateSurfaces(dst_dev->display, va_rt_format,
1038 src->width, src->height,
1040 attrs, FF_ARRAY_ELEMS(attrs));
1041 if (vas != VA_STATUS_SUCCESS) {
1042 av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
1043 "object: %d (%s).\n", vas, vaErrorStr(vas));
1044 return AVERROR(EIO);
1046 av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
1048 err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
1049 &vaapi_unmap_from_drm,
1050 (void*)(uintptr_t)surface_id);
1054 dst->width = src->width;
1055 dst->height = src->height;
1056 dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
1058 av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
1059 "surface %#x.\n", desc->objects[0].fd, surface_id);
1064 static void vaapi_unmap_to_drm(AVHWFramesContext *dst_fc,
1065 HWMapDescriptor *hwmap)
1067 AVDRMFrameDescriptor *drm_desc = hwmap->priv;
1070 for (i = 0; i < drm_desc->nb_objects; i++)
1071 close(drm_desc->objects[i].fd);
1073 av_freep(&drm_desc);
1076 static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
1077 const AVFrame *src, int flags)
1080 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1081 VASurfaceID surface_id;
1083 VADRMPRIMESurfaceDescriptor va_desc;
1084 AVDRMFrameDescriptor *drm_desc = NULL;
1087 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1089 vas = vaExportSurfaceHandle(hwctx->display, surface_id,
1090 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
1091 VA_EXPORT_SURFACE_READ_ONLY |
1092 VA_EXPORT_SURFACE_SEPARATE_LAYERS,
1094 if (vas != VA_STATUS_SUCCESS) {
1095 if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
1096 return AVERROR(ENOSYS);
1097 av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
1098 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
1099 return AVERROR(EIO);
1102 drm_desc = av_mallocz(sizeof(*drm_desc));
1104 err = AVERROR(ENOMEM);
1108 // By some bizarre coincidence, these structures are very similar...
1109 drm_desc->nb_objects = va_desc.num_objects;
1110 for (i = 0; i < va_desc.num_objects; i++) {
1111 drm_desc->objects[i].fd = va_desc.objects[i].fd;
1112 drm_desc->objects[i].size = va_desc.objects[i].size;
1113 drm_desc->objects[i].format_modifier =
1114 va_desc.objects[i].drm_format_modifier;
1116 drm_desc->nb_layers = va_desc.num_layers;
1117 for (i = 0; i < va_desc.num_layers; i++) {
1118 drm_desc->layers[i].format = va_desc.layers[i].drm_format;
1119 drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes;
1120 for (j = 0; j < va_desc.layers[i].num_planes; j++) {
1121 drm_desc->layers[i].planes[j].object_index =
1122 va_desc.layers[i].object_index[j];
1123 drm_desc->layers[i].planes[j].offset =
1124 va_desc.layers[i].offset[j];
1125 drm_desc->layers[i].planes[j].pitch =
1126 va_desc.layers[i].pitch[j];
1130 err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
1131 &vaapi_unmap_to_drm, drm_desc);
1135 dst->width = src->width;
1136 dst->height = src->height;
1137 dst->data[0] = (uint8_t*)drm_desc;
1142 for (i = 0; i < va_desc.num_objects; i++)
1143 close(va_desc.objects[i].fd);
1144 av_freep(&drm_desc);
1147 // Older versions without vaExportSurfaceHandle() are not supported -
1148 // in theory this is possible with a combination of vaDeriveImage()
1149 // and vaAcquireBufferHandle(), but it doesn't carry enough metadata
1150 // to actually use the result in a generic way.
1151 return AVERROR(ENOSYS);
1156 static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
1157 const AVFrame *src, int flags)
1159 switch (src->format) {
1161 case AV_PIX_FMT_DRM_PRIME:
1162 return vaapi_map_from_drm(hwfc, dst, src, flags);
1165 return AVERROR(ENOSYS);
1169 static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
1170 const AVFrame *src, int flags)
1172 switch (dst->format) {
1174 case AV_PIX_FMT_DRM_PRIME:
1175 return vaapi_map_to_drm(hwfc, dst, src, flags);
1178 return vaapi_map_to_memory(hwfc, dst, src, flags);
1182 static void vaapi_device_free(AVHWDeviceContext *ctx)
1184 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1185 VAAPIDevicePriv *priv = ctx->user_opaque;
1188 vaTerminate(hwctx->display);
1191 if (priv->x11_display)
1192 XCloseDisplay(priv->x11_display);
1195 if (priv->drm_fd >= 0)
1196 close(priv->drm_fd);
1202 static void vaapi_device_log_error(void *context, const char *message)
1204 AVHWDeviceContext *ctx = context;
1206 av_log(ctx, AV_LOG_ERROR, "libva: %s", message);
1209 static void vaapi_device_log_info(void *context, const char *message)
1211 AVHWDeviceContext *ctx = context;
1213 av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message);
1217 static int vaapi_device_connect(AVHWDeviceContext *ctx,
1220 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1225 vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
1226 vaSetInfoCallback (display, &vaapi_device_log_info, ctx);
1229 hwctx->display = display;
1231 vas = vaInitialize(display, &major, &minor);
1232 if (vas != VA_STATUS_SUCCESS) {
1233 av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
1234 "connection: %d (%s).\n", vas, vaErrorStr(vas));
1235 return AVERROR(EIO);
1237 av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
1238 "version %d.%d\n", major, minor);
1243 static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
1244 AVDictionary *opts, int flags)
1246 VAAPIDevicePriv *priv;
1247 VADisplay display = NULL;
1249 priv = av_mallocz(sizeof(*priv));
1251 return AVERROR(ENOMEM);
1255 ctx->user_opaque = priv;
1256 ctx->free = vaapi_device_free;
1259 if (!display && !(device && device[0] == '/')) {
1260 // Try to open the device as an X11 display.
1261 priv->x11_display = XOpenDisplay(device);
1262 if (!priv->x11_display) {
1263 av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
1264 "%s.\n", XDisplayName(device));
1266 display = vaGetDisplay(priv->x11_display);
1268 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1269 "from X11 display %s.\n", XDisplayName(device));
1270 return AVERROR_UNKNOWN;
1273 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1274 "X11 display %s.\n", XDisplayName(device));
1281 // Try to open the device as a DRM path.
1282 // Default to using the first render node if the user did not
1284 const char *path = device ? device : "/dev/dri/renderD128";
1285 priv->drm_fd = open(path, O_RDWR);
1286 if (priv->drm_fd < 0) {
1287 av_log(ctx, AV_LOG_VERBOSE, "Cannot open DRM device %s.\n",
1290 display = vaGetDisplayDRM(priv->drm_fd);
1292 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1293 "from DRM device %s.\n", path);
1294 return AVERROR_UNKNOWN;
1297 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1298 "DRM device %s.\n", path);
1304 av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1305 "device: %s.\n", device ? device : "");
1306 return AVERROR(EINVAL);
1309 return vaapi_device_connect(ctx, display);
1312 static int vaapi_device_derive(AVHWDeviceContext *ctx,
1313 AVHWDeviceContext *src_ctx, int flags)
1316 if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
1317 AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
1319 VAAPIDevicePriv *priv;
1321 if (src_hwctx->fd < 0) {
1322 av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
1323 "device to derive a VA display from.\n");
1324 return AVERROR(EINVAL);
1327 priv = av_mallocz(sizeof(*priv));
1329 return AVERROR(ENOMEM);
1331 // Inherits the fd from the source context, which will close it.
1334 ctx->user_opaque = priv;
1335 ctx->free = &vaapi_device_free;
1337 display = vaGetDisplayDRM(src_hwctx->fd);
1339 av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
1341 return AVERROR(EIO);
1344 return vaapi_device_connect(ctx, display);
1347 return AVERROR(ENOSYS);
1350 const HWContextType ff_hwcontext_type_vaapi = {
1351 .type = AV_HWDEVICE_TYPE_VAAPI,
1354 .device_hwctx_size = sizeof(AVVAAPIDeviceContext),
1355 .device_priv_size = sizeof(VAAPIDeviceContext),
1356 .device_hwconfig_size = sizeof(AVVAAPIHWConfig),
1357 .frames_hwctx_size = sizeof(AVVAAPIFramesContext),
1358 .frames_priv_size = sizeof(VAAPIFramesContext),
1360 .device_create = &vaapi_device_create,
1361 .device_derive = &vaapi_device_derive,
1362 .device_init = &vaapi_device_init,
1363 .device_uninit = &vaapi_device_uninit,
1364 .frames_get_constraints = &vaapi_frames_get_constraints,
1365 .frames_init = &vaapi_frames_init,
1366 .frames_uninit = &vaapi_frames_uninit,
1367 .frames_get_buffer = &vaapi_get_buffer,
1368 .transfer_get_formats = &vaapi_transfer_get_formats,
1369 .transfer_data_to = &vaapi_transfer_data_to,
1370 .transfer_data_from = &vaapi_transfer_data_from,
1371 .map_to = &vaapi_map_to,
1372 .map_from = &vaapi_map_from,
1374 .pix_fmts = (const enum AVPixelFormat[]) {