2 * This file is part of FFmpeg.
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 # include <va/va_x11.h>
25 # include <va/va_drm.h>
29 # include <va/va_drmcommon.h>
30 # include <drm_fourcc.h>
42 #include "hwcontext.h"
43 #include "hwcontext_internal.h"
44 #include "hwcontext_vaapi.h"
50 # include "hwcontext_drm.h"
53 typedef struct VAAPIDevicePriv {
61 typedef struct VAAPISurfaceFormat {
62 enum AVPixelFormat pix_fmt;
63 VAImageFormat image_format;
66 typedef struct VAAPIDeviceContext {
67 // Surface formats which can be used with this device.
68 VAAPISurfaceFormat *formats;
72 typedef struct VAAPIFramesContext {
73 // Surface attributes set at create time.
74 VASurfaceAttrib *attributes;
76 // RT format of the underlying surface (Intel driver ignores this anyway).
77 unsigned int rt_format;
78 // Whether vaDeriveImage works.
82 typedef struct VAAPIMapping {
83 // Handle to the derived or copied image which is mapped.
85 // The mapping flags actually used.
89 #define MAP(va, rt, av) { \
91 VA_RT_FORMAT_ ## rt, \
94 // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
95 // plane swap cases. The frame handling below tries to hide these.
98 unsigned int rt_format;
99 enum AVPixelFormat pix_fmt;
100 } vaapi_format_map[] = {
101 MAP(NV12, YUV420, NV12),
102 MAP(YV12, YUV420, YUV420P), // With U/V planes swapped.
103 MAP(IYUV, YUV420, YUV420P),
104 #ifdef VA_FOURCC_I420
105 MAP(I420, YUV420, YUV420P),
107 #ifdef VA_FOURCC_YV16
108 MAP(YV16, YUV422, YUV422P), // With U/V planes swapped.
110 MAP(422H, YUV422, YUV422P),
111 MAP(UYVY, YUV422, UYVY422),
112 MAP(YUY2, YUV422, YUYV422),
113 MAP(Y800, YUV400, GRAY8),
114 #ifdef VA_FOURCC_P010
115 MAP(P010, YUV420_10BPP, P010),
117 MAP(BGRA, RGB32, BGRA),
118 MAP(BGRX, RGB32, BGR0),
119 MAP(RGBA, RGB32, RGBA),
120 MAP(RGBX, RGB32, RGB0),
121 #ifdef VA_FOURCC_ABGR
122 MAP(ABGR, RGB32, ABGR),
123 MAP(XBGR, RGB32, 0BGR),
125 MAP(ARGB, RGB32, ARGB),
126 MAP(XRGB, RGB32, 0RGB),
130 static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
133 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
134 if (vaapi_format_map[i].fourcc == fourcc)
135 return vaapi_format_map[i].pix_fmt;
136 return AV_PIX_FMT_NONE;
139 static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
140 enum AVPixelFormat pix_fmt,
141 VAImageFormat **image_format)
143 VAAPIDeviceContext *ctx = hwdev->internal->priv;
146 for (i = 0; i < ctx->nb_formats; i++) {
147 if (ctx->formats[i].pix_fmt == pix_fmt) {
149 *image_format = &ctx->formats[i].image_format;
153 return AVERROR(EINVAL);
156 static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
157 const void *hwconfig,
158 AVHWFramesConstraints *constraints)
160 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
161 const AVVAAPIHWConfig *config = hwconfig;
162 VAAPIDeviceContext *ctx = hwdev->internal->priv;
163 VASurfaceAttrib *attr_list = NULL;
165 enum AVPixelFormat pix_fmt;
167 int err, i, j, attr_count, pix_fmt_count;
170 !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
172 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
174 if (vas != VA_STATUS_SUCCESS) {
175 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
176 "%d (%s).\n", vas, vaErrorStr(vas));
177 err = AVERROR(ENOSYS);
181 attr_list = av_malloc(attr_count * sizeof(*attr_list));
183 err = AVERROR(ENOMEM);
187 vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
188 attr_list, &attr_count);
189 if (vas != VA_STATUS_SUCCESS) {
190 av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
191 "%d (%s).\n", vas, vaErrorStr(vas));
192 err = AVERROR(ENOSYS);
197 for (i = 0; i < attr_count; i++) {
198 switch (attr_list[i].type) {
199 case VASurfaceAttribPixelFormat:
200 fourcc = attr_list[i].value.value.i;
201 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
202 if (pix_fmt != AV_PIX_FMT_NONE) {
205 // Something unsupported - ignore.
208 case VASurfaceAttribMinWidth:
209 constraints->min_width = attr_list[i].value.value.i;
211 case VASurfaceAttribMinHeight:
212 constraints->min_height = attr_list[i].value.value.i;
214 case VASurfaceAttribMaxWidth:
215 constraints->max_width = attr_list[i].value.value.i;
217 case VASurfaceAttribMaxHeight:
218 constraints->max_height = attr_list[i].value.value.i;
222 if (pix_fmt_count == 0) {
223 // Nothing usable found. Presumably there exists something which
224 // works, so leave the set null to indicate unknown.
225 constraints->valid_sw_formats = NULL;
227 constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
229 if (!constraints->valid_sw_formats) {
230 err = AVERROR(ENOMEM);
234 for (i = j = 0; i < attr_count; i++) {
235 if (attr_list[i].type != VASurfaceAttribPixelFormat)
237 fourcc = attr_list[i].value.value.i;
238 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
239 if (pix_fmt != AV_PIX_FMT_NONE)
240 constraints->valid_sw_formats[j++] = pix_fmt;
242 av_assert0(j == pix_fmt_count);
243 constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
246 // No configuration supplied.
247 // Return the full set of image formats known by the implementation.
248 constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
250 if (!constraints->valid_sw_formats) {
251 err = AVERROR(ENOMEM);
254 for (i = 0; i < ctx->nb_formats; i++)
255 constraints->valid_sw_formats[i] = ctx->formats[i].pix_fmt;
256 constraints->valid_sw_formats[i] = AV_PIX_FMT_NONE;
259 constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
260 if (!constraints->valid_hw_formats) {
261 err = AVERROR(ENOMEM);
264 constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
265 constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
269 av_freep(&attr_list);
273 static const struct {
274 const char *friendly_name;
275 const char *match_string;
277 } vaapi_driver_quirks_table[] = {
279 "Intel i965 (Quick Sync)",
281 AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
286 AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
290 "Splitted-Desktop Systems VDPAU backend for VA-API",
291 AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES,
295 static int vaapi_device_init(AVHWDeviceContext *hwdev)
297 VAAPIDeviceContext *ctx = hwdev->internal->priv;
298 AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
299 VAImageFormat *image_list = NULL;
301 const char *vendor_string;
302 int err, i, image_count;
303 enum AVPixelFormat pix_fmt;
306 image_count = vaMaxNumImageFormats(hwctx->display);
307 if (image_count <= 0) {
311 image_list = av_malloc(image_count * sizeof(*image_list));
313 err = AVERROR(ENOMEM);
316 vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
317 if (vas != VA_STATUS_SUCCESS) {
322 ctx->formats = av_malloc(image_count * sizeof(*ctx->formats));
324 err = AVERROR(ENOMEM);
328 for (i = 0; i < image_count; i++) {
329 fourcc = image_list[i].fourcc;
330 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
331 if (pix_fmt == AV_PIX_FMT_NONE) {
332 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
335 av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
336 fourcc, av_get_pix_fmt_name(pix_fmt));
337 ctx->formats[ctx->nb_formats].pix_fmt = pix_fmt;
338 ctx->formats[ctx->nb_formats].image_format = image_list[i];
343 if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
344 av_log(hwdev, AV_LOG_VERBOSE, "Not detecting driver: "
345 "quirks set by user.\n");
347 // Detect the driver in use and set quirk flags if necessary.
348 vendor_string = vaQueryVendorString(hwctx->display);
349 hwctx->driver_quirks = 0;
351 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
352 if (strstr(vendor_string,
353 vaapi_driver_quirks_table[i].match_string)) {
354 av_log(hwdev, AV_LOG_VERBOSE, "Matched \"%s\" as known "
355 "driver \"%s\".\n", vendor_string,
356 vaapi_driver_quirks_table[i].friendly_name);
357 hwctx->driver_quirks |=
358 vaapi_driver_quirks_table[i].quirks;
362 if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
363 av_log(hwdev, AV_LOG_VERBOSE, "Unknown driver \"%s\", "
364 "assuming standard behaviour.\n", vendor_string);
372 av_freep(&ctx->formats);
377 static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
379 VAAPIDeviceContext *ctx = hwdev->internal->priv;
381 av_freep(&ctx->formats);
384 static void vaapi_buffer_free(void *opaque, uint8_t *data)
386 AVHWFramesContext *hwfc = opaque;
387 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
388 VASurfaceID surface_id;
391 surface_id = (VASurfaceID)(uintptr_t)data;
393 vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
394 if (vas != VA_STATUS_SUCCESS) {
395 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
396 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
400 static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
402 AVHWFramesContext *hwfc = opaque;
403 VAAPIFramesContext *ctx = hwfc->internal->priv;
404 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
405 AVVAAPIFramesContext *avfc = hwfc->hwctx;
406 VASurfaceID surface_id;
410 if (hwfc->initial_pool_size > 0 &&
411 avfc->nb_surfaces >= hwfc->initial_pool_size)
414 vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
415 hwfc->width, hwfc->height,
417 ctx->attributes, ctx->nb_attributes);
418 if (vas != VA_STATUS_SUCCESS) {
419 av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
420 "%d (%s).\n", vas, vaErrorStr(vas));
423 av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
425 ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
426 sizeof(surface_id), &vaapi_buffer_free,
427 hwfc, AV_BUFFER_FLAG_READONLY);
429 vaDestroySurfaces(hwctx->display, &surface_id, 1);
433 if (hwfc->initial_pool_size > 0) {
434 // This is a fixed-size pool, so we must still be in the initial
435 // allocation sequence.
436 av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
437 avfc->surface_ids[avfc->nb_surfaces] = surface_id;
444 static int vaapi_frames_init(AVHWFramesContext *hwfc)
446 AVVAAPIFramesContext *avfc = hwfc->hwctx;
447 VAAPIFramesContext *ctx = hwfc->internal->priv;
448 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
449 VAImageFormat *expected_format;
450 AVBufferRef *test_surface = NULL;
451 VASurfaceID test_surface_id;
455 unsigned int fourcc, rt_format;
457 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
458 if (vaapi_format_map[i].pix_fmt == hwfc->sw_format) {
459 fourcc = vaapi_format_map[i].fourcc;
460 rt_format = vaapi_format_map[i].rt_format;
464 if (i >= FF_ARRAY_ELEMS(vaapi_format_map)) {
465 av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
466 av_get_pix_fmt_name(hwfc->sw_format));
467 return AVERROR(EINVAL);
471 if (!(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
472 int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
473 int need_pixel_format = 1;
474 for (i = 0; i < avfc->nb_attributes; i++) {
475 if (ctx->attributes[i].type == VASurfaceAttribMemoryType)
476 need_memory_type = 0;
477 if (ctx->attributes[i].type == VASurfaceAttribPixelFormat)
478 need_pixel_format = 0;
481 avfc->nb_attributes + need_memory_type + need_pixel_format;
483 ctx->attributes = av_malloc(ctx->nb_attributes *
484 sizeof(*ctx->attributes));
485 if (!ctx->attributes) {
486 err = AVERROR(ENOMEM);
490 for (i = 0; i < avfc->nb_attributes; i++)
491 ctx->attributes[i] = avfc->attributes[i];
492 if (need_memory_type) {
493 ctx->attributes[i++] = (VASurfaceAttrib) {
494 .type = VASurfaceAttribMemoryType,
495 .flags = VA_SURFACE_ATTRIB_SETTABLE,
496 .value.type = VAGenericValueTypeInteger,
497 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
500 if (need_pixel_format) {
501 ctx->attributes[i++] = (VASurfaceAttrib) {
502 .type = VASurfaceAttribPixelFormat,
503 .flags = VA_SURFACE_ATTRIB_SETTABLE,
504 .value.type = VAGenericValueTypeInteger,
505 .value.value.i = fourcc,
508 av_assert0(i == ctx->nb_attributes);
510 ctx->attributes = NULL;
511 ctx->nb_attributes = 0;
514 ctx->rt_format = rt_format;
516 if (hwfc->initial_pool_size > 0) {
517 // This pool will be usable as a render target, so we need to store
518 // all of the surface IDs somewhere that vaCreateContext() calls
519 // will be able to access them.
520 avfc->nb_surfaces = 0;
521 avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
522 sizeof(*avfc->surface_ids));
523 if (!avfc->surface_ids) {
524 err = AVERROR(ENOMEM);
528 // This pool allows dynamic sizing, and will not be usable as a
530 avfc->nb_surfaces = 0;
531 avfc->surface_ids = NULL;
534 hwfc->internal->pool_internal =
535 av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
536 &vaapi_pool_alloc, NULL);
537 if (!hwfc->internal->pool_internal) {
538 av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
539 err = AVERROR(ENOMEM);
544 // Allocate a single surface to test whether vaDeriveImage() is going
545 // to work for the specific configuration.
547 test_surface = av_buffer_pool_get(hwfc->pool);
549 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
550 "user-configured buffer pool.\n");
551 err = AVERROR(ENOMEM);
555 test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
557 av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
558 "internal buffer pool.\n");
559 err = AVERROR(ENOMEM);
563 test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
565 ctx->derive_works = 0;
567 err = vaapi_get_image_format(hwfc->device_ctx,
568 hwfc->sw_format, &expected_format);
570 vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
571 if (vas == VA_STATUS_SUCCESS) {
572 if (expected_format->fourcc == test_image.format.fourcc) {
573 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
574 ctx->derive_works = 1;
576 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
577 "derived image format %08x does not match "
578 "expected format %08x.\n",
579 expected_format->fourcc, test_image.format.fourcc);
581 vaDestroyImage(hwctx->display, test_image.image_id);
583 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
584 "deriving image does not work: "
585 "%d (%s).\n", vas, vaErrorStr(vas));
588 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
589 "image format is not supported.\n");
592 av_buffer_unref(&test_surface);
596 av_buffer_unref(&test_surface);
597 av_freep(&avfc->surface_ids);
598 av_freep(&ctx->attributes);
602 static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
604 AVVAAPIFramesContext *avfc = hwfc->hwctx;
605 VAAPIFramesContext *ctx = hwfc->internal->priv;
607 av_freep(&avfc->surface_ids);
608 av_freep(&ctx->attributes);
611 static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
613 frame->buf[0] = av_buffer_pool_get(hwfc->pool);
615 return AVERROR(ENOMEM);
617 frame->data[3] = frame->buf[0]->data;
618 frame->format = AV_PIX_FMT_VAAPI;
619 frame->width = hwfc->width;
620 frame->height = hwfc->height;
625 static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
626 enum AVHWFrameTransferDirection dir,
627 enum AVPixelFormat **formats)
629 VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
630 enum AVPixelFormat *pix_fmts;
631 int i, k, sw_format_available;
633 sw_format_available = 0;
634 for (i = 0; i < ctx->nb_formats; i++) {
635 if (ctx->formats[i].pix_fmt == hwfc->sw_format)
636 sw_format_available = 1;
639 pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
641 return AVERROR(ENOMEM);
643 if (sw_format_available) {
644 pix_fmts[0] = hwfc->sw_format;
649 for (i = 0; i < ctx->nb_formats; i++) {
650 if (ctx->formats[i].pix_fmt == hwfc->sw_format)
652 av_assert0(k < ctx->nb_formats);
653 pix_fmts[k++] = ctx->formats[i].pix_fmt;
655 pix_fmts[k] = AV_PIX_FMT_NONE;
661 static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
662 HWMapDescriptor *hwmap)
664 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
665 VAAPIMapping *map = hwmap->priv;
666 VASurfaceID surface_id;
669 surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
670 av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
672 vas = vaUnmapBuffer(hwctx->display, map->image.buf);
673 if (vas != VA_STATUS_SUCCESS) {
674 av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
675 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
678 if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
679 !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
680 vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
681 0, 0, hwfc->width, hwfc->height,
682 0, 0, hwfc->width, hwfc->height);
683 if (vas != VA_STATUS_SUCCESS) {
684 av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
685 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
689 vas = vaDestroyImage(hwctx->display, map->image.image_id);
690 if (vas != VA_STATUS_SUCCESS) {
691 av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
692 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
698 static int vaapi_map_frame(AVHWFramesContext *hwfc,
699 AVFrame *dst, const AVFrame *src, int flags)
701 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
702 VAAPIFramesContext *ctx = hwfc->internal->priv;
703 VASurfaceID surface_id;
704 VAImageFormat *image_format;
707 void *address = NULL;
710 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
711 av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
713 if (!ctx->derive_works && (flags & AV_HWFRAME_MAP_DIRECT)) {
714 // Requested direct mapping but it is not possible.
715 return AVERROR(EINVAL);
717 if (dst->format == AV_PIX_FMT_NONE)
718 dst->format = hwfc->sw_format;
719 if (dst->format != hwfc->sw_format && (flags & AV_HWFRAME_MAP_DIRECT)) {
720 // Requested direct mapping but the formats do not match.
721 return AVERROR(EINVAL);
724 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
726 // Requested format is not a valid output format.
727 return AVERROR(EINVAL);
730 map = av_malloc(sizeof(*map));
732 return AVERROR(ENOMEM);
734 map->image.image_id = VA_INVALID_ID;
736 vas = vaSyncSurface(hwctx->display, surface_id);
737 if (vas != VA_STATUS_SUCCESS) {
738 av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
739 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
744 // The memory which we map using derive need not be connected to the CPU
745 // in a way conducive to fast access. On Gen7-Gen9 Intel graphics, the
746 // memory is mappable but not cached, so normal memcpy()-like access is
747 // very slow to read it (but writing is ok). It is possible to read much
748 // faster with a copy routine which is aware of the limitation, but we
749 // assume for now that the user is not aware of that and would therefore
750 // prefer not to be given direct-mapped memory if they request read access.
751 if (ctx->derive_works && dst->format == hwfc->sw_format &&
752 ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
753 vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
754 if (vas != VA_STATUS_SUCCESS) {
755 av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
756 "surface %#x: %d (%s).\n",
757 surface_id, vas, vaErrorStr(vas));
761 if (map->image.format.fourcc != image_format->fourcc) {
762 av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
763 "is in wrong format: expected %#08x, got %#08x.\n",
764 surface_id, image_format->fourcc, map->image.format.fourcc);
768 map->flags |= AV_HWFRAME_MAP_DIRECT;
770 vas = vaCreateImage(hwctx->display, image_format,
771 hwfc->width, hwfc->height, &map->image);
772 if (vas != VA_STATUS_SUCCESS) {
773 av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
774 "surface %#x: %d (%s).\n",
775 surface_id, vas, vaErrorStr(vas));
779 if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
780 vas = vaGetImage(hwctx->display, surface_id, 0, 0,
781 hwfc->width, hwfc->height, map->image.image_id);
782 if (vas != VA_STATUS_SUCCESS) {
783 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
784 "surface %#x: %d (%s).\n",
785 surface_id, vas, vaErrorStr(vas));
792 vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
793 if (vas != VA_STATUS_SUCCESS) {
794 av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
795 "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
800 err = ff_hwframe_map_create(src->hw_frames_ctx,
801 dst, src, &vaapi_unmap_frame, map);
805 dst->width = src->width;
806 dst->height = src->height;
808 for (i = 0; i < map->image.num_planes; i++) {
809 dst->data[i] = (uint8_t*)address + map->image.offsets[i];
810 dst->linesize[i] = map->image.pitches[i];
813 #ifdef VA_FOURCC_YV16
814 map->image.format.fourcc == VA_FOURCC_YV16 ||
816 map->image.format.fourcc == VA_FOURCC_YV12) {
817 // Chroma planes are YVU rather than YUV, so swap them.
818 FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
826 vaUnmapBuffer(hwctx->display, map->image.buf);
827 if (map->image.image_id != VA_INVALID_ID)
828 vaDestroyImage(hwctx->display, map->image.image_id);
834 static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
835 AVFrame *dst, const AVFrame *src)
840 if (dst->width > hwfc->width || dst->height > hwfc->height)
841 return AVERROR(EINVAL);
843 map = av_frame_alloc();
845 return AVERROR(ENOMEM);
846 map->format = dst->format;
848 err = vaapi_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
852 map->width = dst->width;
853 map->height = dst->height;
855 err = av_frame_copy(dst, map);
865 static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
866 AVFrame *dst, const AVFrame *src)
871 if (src->width > hwfc->width || src->height > hwfc->height)
872 return AVERROR(EINVAL);
874 map = av_frame_alloc();
876 return AVERROR(ENOMEM);
877 map->format = src->format;
879 err = vaapi_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
883 map->width = src->width;
884 map->height = src->height;
886 err = av_frame_copy(map, src);
896 static int vaapi_map_to_memory(AVHWFramesContext *hwfc, AVFrame *dst,
897 const AVFrame *src, int flags)
901 if (dst->format != AV_PIX_FMT_NONE) {
902 err = vaapi_get_image_format(hwfc->device_ctx, dst->format, NULL);
904 return AVERROR(ENOSYS);
907 err = vaapi_map_frame(hwfc, dst, src, flags);
911 err = av_frame_copy_props(dst, src);
920 #define DRM_MAP(va, layers, ...) { \
925 static const struct {
927 int nb_layer_formats;
928 uint32_t layer_formats[AV_DRM_MAX_PLANES];
929 } vaapi_drm_format_map[] = {
931 DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88),
933 DRM_MAP(NV12, 1, DRM_FORMAT_NV12),
934 #if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16)
935 DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
937 DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888),
938 DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888),
939 DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888),
940 DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888),
941 #ifdef VA_FOURCC_ABGR
942 DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888),
943 DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888),
945 DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888),
946 DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888),
950 static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
951 HWMapDescriptor *hwmap)
953 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
955 VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
957 av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id);
959 vaDestroySurfaces(dst_dev->display, &surface_id, 1);
962 static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
963 const AVFrame *src, int flags)
965 AVHWFramesContext *dst_fc =
966 (AVHWFramesContext*)dst->hw_frames_ctx->data;
967 AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
968 const AVDRMFrameDescriptor *desc;
969 VASurfaceID surface_id;
971 uint32_t va_fourcc, va_rt_format;
974 unsigned long buffer_handle;
975 VASurfaceAttribExternalBuffers buffer_desc;
976 VASurfaceAttrib attrs[2] = {
978 .type = VASurfaceAttribMemoryType,
979 .flags = VA_SURFACE_ATTRIB_SETTABLE,
980 .value.type = VAGenericValueTypeInteger,
981 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
984 .type = VASurfaceAttribExternalBufferDescriptor,
985 .flags = VA_SURFACE_ATTRIB_SETTABLE,
986 .value.type = VAGenericValueTypePointer,
987 .value.value.p = &buffer_desc,
991 desc = (AVDRMFrameDescriptor*)src->data[0];
993 if (desc->nb_objects != 1) {
994 av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames "
995 "made from a single DRM object.\n");
996 return AVERROR(EINVAL);
1000 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1001 if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats)
1003 for (j = 0; j < desc->nb_layers; j++) {
1004 if (desc->layers[j].format !=
1005 vaapi_drm_format_map[i].layer_formats[j])
1008 if (j != desc->nb_layers)
1010 va_fourcc = vaapi_drm_format_map[i].va_fourcc;
1014 av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported "
1016 return AVERROR(EINVAL);
1019 av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
1020 "%08x.\n", desc->objects[0].fd, va_fourcc);
1022 for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
1023 if (vaapi_format_map[i].fourcc == va_fourcc)
1024 va_rt_format = vaapi_format_map[i].rt_format;
1027 buffer_handle = desc->objects[0].fd;
1028 buffer_desc.pixel_format = va_fourcc;
1029 buffer_desc.width = src_fc->width;
1030 buffer_desc.height = src_fc->height;
1031 buffer_desc.data_size = desc->objects[0].size;
1032 buffer_desc.buffers = &buffer_handle;
1033 buffer_desc.num_buffers = 1;
1034 buffer_desc.flags = 0;
1037 for (i = 0; i < desc->nb_layers; i++) {
1038 for (j = 0; j < desc->layers[i].nb_planes; j++) {
1039 buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
1040 buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
1044 buffer_desc.num_planes = k;
1046 vas = vaCreateSurfaces(dst_dev->display, va_rt_format,
1047 src->width, src->height,
1049 attrs, FF_ARRAY_ELEMS(attrs));
1050 if (vas != VA_STATUS_SUCCESS) {
1051 av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
1052 "object: %d (%s).\n", vas, vaErrorStr(vas));
1053 return AVERROR(EIO);
1055 av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
1057 err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
1058 &vaapi_unmap_from_drm,
1059 (void*)(uintptr_t)surface_id);
1063 dst->width = src->width;
1064 dst->height = src->height;
1065 dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
1067 av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
1068 "surface %#x.\n", desc->objects[0].fd, surface_id);
1073 static void vaapi_unmap_to_drm(AVHWFramesContext *dst_fc,
1074 HWMapDescriptor *hwmap)
1076 AVDRMFrameDescriptor *drm_desc = hwmap->priv;
1079 for (i = 0; i < drm_desc->nb_objects; i++)
1080 close(drm_desc->objects[i].fd);
1082 av_freep(&drm_desc);
1085 static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
1086 const AVFrame *src, int flags)
1088 #if VA_CHECK_VERSION(1, 1, 0)
1089 AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1090 VASurfaceID surface_id;
1092 VADRMPRIMESurfaceDescriptor va_desc;
1093 AVDRMFrameDescriptor *drm_desc = NULL;
1096 surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1098 vas = vaExportSurfaceHandle(hwctx->display, surface_id,
1099 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
1100 VA_EXPORT_SURFACE_READ_ONLY |
1101 VA_EXPORT_SURFACE_SEPARATE_LAYERS,
1103 if (vas != VA_STATUS_SUCCESS) {
1104 if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
1105 return AVERROR(ENOSYS);
1106 av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
1107 "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
1108 return AVERROR(EIO);
1111 drm_desc = av_mallocz(sizeof(*drm_desc));
1113 err = AVERROR(ENOMEM);
1117 // By some bizarre coincidence, these structures are very similar...
1118 drm_desc->nb_objects = va_desc.num_objects;
1119 for (i = 0; i < va_desc.num_objects; i++) {
1120 drm_desc->objects[i].fd = va_desc.objects[i].fd;
1121 drm_desc->objects[i].size = va_desc.objects[i].size;
1122 drm_desc->objects[i].format_modifier =
1123 va_desc.objects[i].drm_format_modifier;
1125 drm_desc->nb_layers = va_desc.num_layers;
1126 for (i = 0; i < va_desc.num_layers; i++) {
1127 drm_desc->layers[i].format = va_desc.layers[i].drm_format;
1128 drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes;
1129 for (j = 0; j < va_desc.layers[i].num_planes; j++) {
1130 drm_desc->layers[i].planes[j].object_index =
1131 va_desc.layers[i].object_index[j];
1132 drm_desc->layers[i].planes[j].offset =
1133 va_desc.layers[i].offset[j];
1134 drm_desc->layers[i].planes[j].pitch =
1135 va_desc.layers[i].pitch[j];
1139 err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
1140 &vaapi_unmap_to_drm, drm_desc);
1144 dst->width = src->width;
1145 dst->height = src->height;
1146 dst->data[0] = (uint8_t*)drm_desc;
1151 for (i = 0; i < va_desc.num_objects; i++)
1152 close(va_desc.objects[i].fd);
1153 av_freep(&drm_desc);
1156 // Older versions without vaExportSurfaceHandle() are not supported -
1157 // in theory this is possible with a combination of vaDeriveImage()
1158 // and vaAcquireBufferHandle(), but it doesn't carry enough metadata
1159 // to actually use the result in a generic way.
1160 return AVERROR(ENOSYS);
1165 static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
1166 const AVFrame *src, int flags)
1168 switch (src->format) {
1170 case AV_PIX_FMT_DRM_PRIME:
1171 return vaapi_map_from_drm(hwfc, dst, src, flags);
1174 return AVERROR(ENOSYS);
1178 static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
1179 const AVFrame *src, int flags)
1181 switch (dst->format) {
1183 case AV_PIX_FMT_DRM_PRIME:
1184 return vaapi_map_to_drm(hwfc, dst, src, flags);
1187 return vaapi_map_to_memory(hwfc, dst, src, flags);
1191 static void vaapi_device_free(AVHWDeviceContext *ctx)
1193 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1194 VAAPIDevicePriv *priv = ctx->user_opaque;
1197 vaTerminate(hwctx->display);
1200 if (priv->x11_display)
1201 XCloseDisplay(priv->x11_display);
1204 if (priv->drm_fd >= 0)
1205 close(priv->drm_fd);
1211 static void vaapi_device_log_error(void *context, const char *message)
1213 AVHWDeviceContext *ctx = context;
1215 av_log(ctx, AV_LOG_ERROR, "libva: %s", message);
1218 static void vaapi_device_log_info(void *context, const char *message)
1220 AVHWDeviceContext *ctx = context;
1222 av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message);
1226 static int vaapi_device_connect(AVHWDeviceContext *ctx,
1229 AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1234 vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
1235 vaSetInfoCallback (display, &vaapi_device_log_info, ctx);
1238 hwctx->display = display;
1240 vas = vaInitialize(display, &major, &minor);
1241 if (vas != VA_STATUS_SUCCESS) {
1242 av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
1243 "connection: %d (%s).\n", vas, vaErrorStr(vas));
1244 return AVERROR(EIO);
1246 av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
1247 "version %d.%d\n", major, minor);
1252 static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
1253 AVDictionary *opts, int flags)
1255 VAAPIDevicePriv *priv;
1256 VADisplay display = NULL;
1258 priv = av_mallocz(sizeof(*priv));
1260 return AVERROR(ENOMEM);
1264 ctx->user_opaque = priv;
1265 ctx->free = vaapi_device_free;
1268 if (!display && !(device && device[0] == '/')) {
1269 // Try to open the device as an X11 display.
1270 priv->x11_display = XOpenDisplay(device);
1271 if (!priv->x11_display) {
1272 av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
1273 "%s.\n", XDisplayName(device));
1275 display = vaGetDisplay(priv->x11_display);
1277 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1278 "from X11 display %s.\n", XDisplayName(device));
1279 return AVERROR_UNKNOWN;
1282 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1283 "X11 display %s.\n", XDisplayName(device));
1290 // Try to open the device as a DRM path.
1291 // Default to using the first render node if the user did not
1293 const char *path = device ? device : "/dev/dri/renderD128";
1294 priv->drm_fd = open(path, O_RDWR);
1295 if (priv->drm_fd < 0) {
1296 av_log(ctx, AV_LOG_VERBOSE, "Cannot open DRM device %s.\n",
1299 display = vaGetDisplayDRM(priv->drm_fd);
1301 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1302 "from DRM device %s.\n", path);
1303 return AVERROR_UNKNOWN;
1306 av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1307 "DRM device %s.\n", path);
1313 av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1314 "device: %s.\n", device ? device : "");
1315 return AVERROR(EINVAL);
1318 return vaapi_device_connect(ctx, display);
1321 static int vaapi_device_derive(AVHWDeviceContext *ctx,
1322 AVHWDeviceContext *src_ctx, int flags)
1325 if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
1326 AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
1328 VAAPIDevicePriv *priv;
1330 if (src_hwctx->fd < 0) {
1331 av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
1332 "device to derive a VA display from.\n");
1333 return AVERROR(EINVAL);
1336 priv = av_mallocz(sizeof(*priv));
1338 return AVERROR(ENOMEM);
1340 // Inherits the fd from the source context, which will close it.
1343 ctx->user_opaque = priv;
1344 ctx->free = &vaapi_device_free;
1346 display = vaGetDisplayDRM(src_hwctx->fd);
1348 av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
1350 return AVERROR(EIO);
1353 return vaapi_device_connect(ctx, display);
1356 return AVERROR(ENOSYS);
1359 const HWContextType ff_hwcontext_type_vaapi = {
1360 .type = AV_HWDEVICE_TYPE_VAAPI,
1363 .device_hwctx_size = sizeof(AVVAAPIDeviceContext),
1364 .device_priv_size = sizeof(VAAPIDeviceContext),
1365 .device_hwconfig_size = sizeof(AVVAAPIHWConfig),
1366 .frames_hwctx_size = sizeof(AVVAAPIFramesContext),
1367 .frames_priv_size = sizeof(VAAPIFramesContext),
1369 .device_create = &vaapi_device_create,
1370 .device_derive = &vaapi_device_derive,
1371 .device_init = &vaapi_device_init,
1372 .device_uninit = &vaapi_device_uninit,
1373 .frames_get_constraints = &vaapi_frames_get_constraints,
1374 .frames_init = &vaapi_frames_init,
1375 .frames_uninit = &vaapi_frames_uninit,
1376 .frames_get_buffer = &vaapi_get_buffer,
1377 .transfer_get_formats = &vaapi_transfer_get_formats,
1378 .transfer_data_to = &vaapi_transfer_data_to,
1379 .transfer_data_from = &vaapi_transfer_data_from,
1380 .map_to = &vaapi_map_to,
1381 .map_from = &vaapi_map_from,
1383 .pix_fmts = (const enum AVPixelFormat[]) {