1 /*
2  * This file is part of FFmpeg.
3  *
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.
8  *
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.
13  *
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
17  */
18 
19 #include "config.h"
20 
21 #if HAVE_VAAPI_X11
22 #   include <va/va_x11.h>
23 #endif
24 #if HAVE_VAAPI_DRM
25 #   include <va/va_drm.h>
26 #endif
27 
28 #if CONFIG_LIBDRM
29 #   include <va/va_drmcommon.h>
30 #   include <drm_fourcc.h>
31 #   ifndef DRM_FORMAT_MOD_INVALID
32 #       define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
33 #   endif
34 #endif
35 
36 #include <fcntl.h>
37 #if HAVE_UNISTD_H
38 #   include <unistd.h>
39 #endif
40 
41 
42 #include "avassert.h"
43 #include "buffer.h"
44 #include "common.h"
45 #include "hwcontext.h"
46 #include "hwcontext_drm.h"
47 #include "hwcontext_internal.h"
48 #include "hwcontext_vaapi.h"
49 #include "mem.h"
50 #include "pixdesc.h"
51 #include "pixfmt.h"
52 
53 
54 typedef struct VAAPIDevicePriv {
55 #if HAVE_VAAPI_X11
56     Display *x11_display;
57 #endif
58 
59     int drm_fd;
60 } VAAPIDevicePriv;
61 
62 typedef struct VAAPISurfaceFormat {
63     enum AVPixelFormat pix_fmt;
64     VAImageFormat image_format;
65 } VAAPISurfaceFormat;
66 
67 typedef struct VAAPIDeviceContext {
68     // Surface formats which can be used with this device.
69     VAAPISurfaceFormat *formats;
70     int              nb_formats;
71 } VAAPIDeviceContext;
72 
73 typedef struct VAAPIFramesContext {
74     // Surface attributes set at create time.
75     VASurfaceAttrib *attributes;
76     int           nb_attributes;
77     // RT format of the underlying surface (Intel driver ignores this anyway).
78     unsigned int rt_format;
79     // Whether vaDeriveImage works.
80     int derive_works;
81 } VAAPIFramesContext;
82 
83 typedef struct VAAPIMapping {
84     // Handle to the derived or copied image which is mapped.
85     VAImage image;
86     // The mapping flags actually used.
87     int flags;
88 } VAAPIMapping;
89 
90 typedef struct VAAPIFormat {
91     unsigned int fourcc;
92     unsigned int rt_format;
93     enum AVPixelFormat pix_fmt;
94     int chroma_planes_swapped;
95 } VAAPIFormatDescriptor;
96 
97 #define MAP(va, rt, av, swap_uv) { \
98         VA_FOURCC_ ## va, \
99         VA_RT_FORMAT_ ## rt, \
100         AV_PIX_FMT_ ## av, \
101         swap_uv, \
102     }
103 // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
104 // plane swap cases.  The frame handling below tries to hide these.
105 static const VAAPIFormatDescriptor vaapi_format_map[] = {
106     MAP(NV12, YUV420,  NV12,    0),
107 #ifdef VA_FOURCC_I420
108     MAP(I420, YUV420,  YUV420P, 0),
109 #endif
110     MAP(YV12, YUV420,  YUV420P, 1),
111     MAP(IYUV, YUV420,  YUV420P, 0),
112     MAP(422H, YUV422,  YUV422P, 0),
113 #ifdef VA_FOURCC_YV16
114     MAP(YV16, YUV422,  YUV422P, 1),
115 #endif
116     MAP(UYVY, YUV422,  UYVY422, 0),
117     MAP(YUY2, YUV422,  YUYV422, 0),
118     MAP(411P, YUV411,  YUV411P, 0),
119     MAP(422V, YUV422,  YUV440P, 0),
120     MAP(444P, YUV444,  YUV444P, 0),
121     MAP(Y800, YUV400,  GRAY8,   0),
122 #ifdef VA_FOURCC_P010
123     MAP(P010, YUV420_10BPP, P010, 0),
124 #endif
125     MAP(BGRA, RGB32,   BGRA, 0),
126     MAP(BGRX, RGB32,   BGR0, 0),
127     MAP(RGBA, RGB32,   RGBA, 0),
128     MAP(RGBX, RGB32,   RGB0, 0),
129 #ifdef VA_FOURCC_ABGR
130     MAP(ABGR, RGB32,   ABGR, 0),
131     MAP(XBGR, RGB32,   0BGR, 0),
132 #endif
133     MAP(ARGB, RGB32,   ARGB, 0),
134     MAP(XRGB, RGB32,   0RGB, 0),
135 };
136 #undef MAP
137 
138 static const VAAPIFormatDescriptor *
vaapi_format_from_fourcc(unsigned int fourcc)139     vaapi_format_from_fourcc(unsigned int fourcc)
140 {
141     int i;
142     for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
143         if (vaapi_format_map[i].fourcc == fourcc)
144             return &vaapi_format_map[i];
145     return NULL;
146 }
147 
148 static const VAAPIFormatDescriptor *
vaapi_format_from_pix_fmt(enum AVPixelFormat pix_fmt)149     vaapi_format_from_pix_fmt(enum AVPixelFormat pix_fmt)
150 {
151     int i;
152     for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
153         if (vaapi_format_map[i].pix_fmt == pix_fmt)
154             return &vaapi_format_map[i];
155     return NULL;
156 }
157 
vaapi_pix_fmt_from_fourcc(unsigned int fourcc)158 static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
159 {
160     const VAAPIFormatDescriptor *desc;
161     desc = vaapi_format_from_fourcc(fourcc);
162     if (desc)
163         return desc->pix_fmt;
164     else
165         return AV_PIX_FMT_NONE;
166 }
167 
vaapi_get_image_format(AVHWDeviceContext * hwdev,enum AVPixelFormat pix_fmt,VAImageFormat ** image_format)168 static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
169                                   enum AVPixelFormat pix_fmt,
170                                   VAImageFormat **image_format)
171 {
172     VAAPIDeviceContext *ctx = hwdev->internal->priv;
173     int i;
174 
175     for (i = 0; i < ctx->nb_formats; i++) {
176         if (ctx->formats[i].pix_fmt == pix_fmt) {
177             if (image_format)
178                 *image_format = &ctx->formats[i].image_format;
179             return 0;
180         }
181     }
182     return AVERROR(EINVAL);
183 }
184 
vaapi_frames_get_constraints(AVHWDeviceContext * hwdev,const void * hwconfig,AVHWFramesConstraints * constraints)185 static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
186                                         const void *hwconfig,
187                                         AVHWFramesConstraints *constraints)
188 {
189     AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
190     const AVVAAPIHWConfig *config = hwconfig;
191     VAAPIDeviceContext *ctx = hwdev->internal->priv;
192     VASurfaceAttrib *attr_list = NULL;
193     VAStatus vas;
194     enum AVPixelFormat pix_fmt;
195     unsigned int fourcc;
196     int err, i, j, attr_count, pix_fmt_count;
197 
198     if (config &&
199         !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
200         attr_count = 0;
201         vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
202                                        0, &attr_count);
203         if (vas != VA_STATUS_SUCCESS) {
204             av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
205                    "%d (%s).\n", vas, vaErrorStr(vas));
206             err = AVERROR(ENOSYS);
207             goto fail;
208         }
209 
210         attr_list = av_malloc(attr_count * sizeof(*attr_list));
211         if (!attr_list) {
212             err = AVERROR(ENOMEM);
213             goto fail;
214         }
215 
216         vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
217                                        attr_list, &attr_count);
218         if (vas != VA_STATUS_SUCCESS) {
219             av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
220                    "%d (%s).\n", vas, vaErrorStr(vas));
221             err = AVERROR(ENOSYS);
222             goto fail;
223         }
224 
225         pix_fmt_count = 0;
226         for (i = 0; i < attr_count; i++) {
227             switch (attr_list[i].type) {
228             case VASurfaceAttribPixelFormat:
229                 fourcc = attr_list[i].value.value.i;
230                 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
231                 if (pix_fmt != AV_PIX_FMT_NONE) {
232                     ++pix_fmt_count;
233                 } else {
234                     // Something unsupported - ignore.
235                 }
236                 break;
237             case VASurfaceAttribMinWidth:
238                 constraints->min_width  = attr_list[i].value.value.i;
239                 break;
240             case VASurfaceAttribMinHeight:
241                 constraints->min_height = attr_list[i].value.value.i;
242                 break;
243             case VASurfaceAttribMaxWidth:
244                 constraints->max_width  = attr_list[i].value.value.i;
245                 break;
246             case VASurfaceAttribMaxHeight:
247                 constraints->max_height = attr_list[i].value.value.i;
248                 break;
249             }
250         }
251         if (pix_fmt_count == 0) {
252             // Nothing usable found.  Presumably there exists something which
253             // works, so leave the set null to indicate unknown.
254             constraints->valid_sw_formats = NULL;
255         } else {
256             constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
257                                                             sizeof(pix_fmt));
258             if (!constraints->valid_sw_formats) {
259                 err = AVERROR(ENOMEM);
260                 goto fail;
261             }
262 
263             for (i = j = 0; i < attr_count; i++) {
264                 if (attr_list[i].type != VASurfaceAttribPixelFormat)
265                     continue;
266                 fourcc = attr_list[i].value.value.i;
267                 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
268                 if (pix_fmt != AV_PIX_FMT_NONE)
269                     constraints->valid_sw_formats[j++] = pix_fmt;
270             }
271             av_assert0(j == pix_fmt_count);
272             constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
273         }
274     } else {
275         // No configuration supplied.
276         // Return the full set of image formats known by the implementation.
277         constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
278                                                         sizeof(pix_fmt));
279         if (!constraints->valid_sw_formats) {
280             err = AVERROR(ENOMEM);
281             goto fail;
282         }
283         for (i = 0; i < ctx->nb_formats; i++)
284             constraints->valid_sw_formats[i] = ctx->formats[i].pix_fmt;
285         constraints->valid_sw_formats[i] = AV_PIX_FMT_NONE;
286     }
287 
288     constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
289     if (!constraints->valid_hw_formats) {
290         err = AVERROR(ENOMEM);
291         goto fail;
292     }
293     constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
294     constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
295 
296     err = 0;
297 fail:
298     av_freep(&attr_list);
299     return err;
300 }
301 
302 static const struct {
303     const char *friendly_name;
304     const char *match_string;
305     unsigned int quirks;
306 } vaapi_driver_quirks_table[] = {
307 #if !VA_CHECK_VERSION(1, 0, 0)
308     // The i965 driver did not conform before version 2.0.
309     {
310         "Intel i965 (Quick Sync)",
311         "i965",
312         AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
313     },
314 #endif
315     {
316         "Intel iHD",
317         "ubit",
318         AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
319     },
320     {
321         "VDPAU wrapper",
322         "Splitted-Desktop Systems VDPAU backend for VA-API",
323         AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES,
324     },
325 };
326 
vaapi_device_init(AVHWDeviceContext * hwdev)327 static int vaapi_device_init(AVHWDeviceContext *hwdev)
328 {
329     VAAPIDeviceContext *ctx = hwdev->internal->priv;
330     AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
331     VAImageFormat *image_list = NULL;
332     VAStatus vas;
333     const char *vendor_string;
334     int err, i, image_count;
335     enum AVPixelFormat pix_fmt;
336     unsigned int fourcc;
337 
338     image_count = vaMaxNumImageFormats(hwctx->display);
339     if (image_count <= 0) {
340         err = AVERROR(EIO);
341         goto fail;
342     }
343     image_list = av_malloc(image_count * sizeof(*image_list));
344     if (!image_list) {
345         err = AVERROR(ENOMEM);
346         goto fail;
347     }
348     vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
349     if (vas != VA_STATUS_SUCCESS) {
350         err = AVERROR(EIO);
351         goto fail;
352     }
353 
354     ctx->formats  = av_malloc(image_count * sizeof(*ctx->formats));
355     if (!ctx->formats) {
356         err = AVERROR(ENOMEM);
357         goto fail;
358     }
359     ctx->nb_formats = 0;
360     for (i = 0; i < image_count; i++) {
361         fourcc  = image_list[i].fourcc;
362         pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
363         if (pix_fmt == AV_PIX_FMT_NONE) {
364             av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
365                    fourcc);
366         } else {
367             av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
368                    fourcc, av_get_pix_fmt_name(pix_fmt));
369             ctx->formats[ctx->nb_formats].pix_fmt      = pix_fmt;
370             ctx->formats[ctx->nb_formats].image_format = image_list[i];
371             ++ctx->nb_formats;
372         }
373     }
374 
375     vendor_string = vaQueryVendorString(hwctx->display);
376     if (vendor_string)
377         av_log(hwdev, AV_LOG_VERBOSE, "VAAPI driver: %s.\n", vendor_string);
378 
379     if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
380         av_log(hwdev, AV_LOG_VERBOSE, "Using quirks set by user (%#x).\n",
381                hwctx->driver_quirks);
382     } else {
383         // Detect the driver in use and set quirk flags if necessary.
384         hwctx->driver_quirks = 0;
385         if (vendor_string) {
386             for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
387                 if (strstr(vendor_string,
388                            vaapi_driver_quirks_table[i].match_string)) {
389                     av_log(hwdev, AV_LOG_VERBOSE, "Matched driver string "
390                            "as known nonstandard driver \"%s\", setting "
391                            "quirks (%#x).\n",
392                            vaapi_driver_quirks_table[i].friendly_name,
393                            vaapi_driver_quirks_table[i].quirks);
394                     hwctx->driver_quirks |=
395                         vaapi_driver_quirks_table[i].quirks;
396                     break;
397                 }
398             }
399             if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
400                 av_log(hwdev, AV_LOG_VERBOSE, "Driver not found in known "
401                        "nonstandard list, using standard behaviour.\n");
402             }
403         } else {
404             av_log(hwdev, AV_LOG_VERBOSE, "Driver has no vendor string, "
405                    "assuming standard behaviour.\n");
406         }
407     }
408 
409     av_free(image_list);
410     return 0;
411 fail:
412     av_freep(&ctx->formats);
413     av_free(image_list);
414     return err;
415 }
416 
vaapi_device_uninit(AVHWDeviceContext * hwdev)417 static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
418 {
419     VAAPIDeviceContext *ctx = hwdev->internal->priv;
420 
421     av_freep(&ctx->formats);
422 }
423 
vaapi_buffer_free(void * opaque,uint8_t * data)424 static void vaapi_buffer_free(void *opaque, uint8_t *data)
425 {
426     AVHWFramesContext     *hwfc = opaque;
427     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
428     VASurfaceID surface_id;
429     VAStatus vas;
430 
431     surface_id = (VASurfaceID)(uintptr_t)data;
432 
433     vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
434     if (vas != VA_STATUS_SUCCESS) {
435         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
436                "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
437     }
438 }
439 
vaapi_pool_alloc(void * opaque,int size)440 static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
441 {
442     AVHWFramesContext     *hwfc = opaque;
443     VAAPIFramesContext     *ctx = hwfc->internal->priv;
444     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
445     AVVAAPIFramesContext  *avfc = hwfc->hwctx;
446     VASurfaceID surface_id;
447     VAStatus vas;
448     AVBufferRef *ref;
449 
450     if (hwfc->initial_pool_size > 0 &&
451         avfc->nb_surfaces >= hwfc->initial_pool_size)
452         return NULL;
453 
454     vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
455                            hwfc->width, hwfc->height,
456                            &surface_id, 1,
457                            ctx->attributes, ctx->nb_attributes);
458     if (vas != VA_STATUS_SUCCESS) {
459         av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
460                "%d (%s).\n", vas, vaErrorStr(vas));
461         return NULL;
462     }
463     av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
464 
465     ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
466                            sizeof(surface_id), &vaapi_buffer_free,
467                            hwfc, AV_BUFFER_FLAG_READONLY);
468     if (!ref) {
469         vaDestroySurfaces(hwctx->display, &surface_id, 1);
470         return NULL;
471     }
472 
473     if (hwfc->initial_pool_size > 0) {
474         // This is a fixed-size pool, so we must still be in the initial
475         // allocation sequence.
476         av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
477         avfc->surface_ids[avfc->nb_surfaces] = surface_id;
478         ++avfc->nb_surfaces;
479     }
480 
481     return ref;
482 }
483 
vaapi_frames_init(AVHWFramesContext * hwfc)484 static int vaapi_frames_init(AVHWFramesContext *hwfc)
485 {
486     AVVAAPIFramesContext  *avfc = hwfc->hwctx;
487     VAAPIFramesContext     *ctx = hwfc->internal->priv;
488     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
489     const VAAPIFormatDescriptor *desc;
490     VAImageFormat *expected_format;
491     AVBufferRef *test_surface = NULL;
492     VASurfaceID test_surface_id;
493     VAImage test_image;
494     VAStatus vas;
495     int err, i;
496 
497     desc = vaapi_format_from_pix_fmt(hwfc->sw_format);
498     if (!desc) {
499         av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
500                av_get_pix_fmt_name(hwfc->sw_format));
501         return AVERROR(EINVAL);
502     }
503 
504     if (!hwfc->pool) {
505         if (!(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
506             int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
507             int need_pixel_format = 1;
508             for (i = 0; i < avfc->nb_attributes; i++) {
509                 if (avfc->attributes[i].type == VASurfaceAttribMemoryType)
510                     need_memory_type  = 0;
511                 if (avfc->attributes[i].type == VASurfaceAttribPixelFormat)
512                     need_pixel_format = 0;
513             }
514             ctx->nb_attributes =
515                 avfc->nb_attributes + need_memory_type + need_pixel_format;
516 
517             ctx->attributes = av_malloc(ctx->nb_attributes *
518                                         sizeof(*ctx->attributes));
519             if (!ctx->attributes) {
520                 err = AVERROR(ENOMEM);
521                 goto fail;
522             }
523 
524             for (i = 0; i < avfc->nb_attributes; i++)
525                 ctx->attributes[i] = avfc->attributes[i];
526             if (need_memory_type) {
527                 ctx->attributes[i++] = (VASurfaceAttrib) {
528                     .type          = VASurfaceAttribMemoryType,
529                     .flags         = VA_SURFACE_ATTRIB_SETTABLE,
530                     .value.type    = VAGenericValueTypeInteger,
531                     .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
532                 };
533             }
534             if (need_pixel_format) {
535                 ctx->attributes[i++] = (VASurfaceAttrib) {
536                     .type          = VASurfaceAttribPixelFormat,
537                     .flags         = VA_SURFACE_ATTRIB_SETTABLE,
538                     .value.type    = VAGenericValueTypeInteger,
539                     .value.value.i = desc->fourcc,
540                 };
541             }
542             av_assert0(i == ctx->nb_attributes);
543         } else {
544             ctx->attributes = NULL;
545             ctx->nb_attributes = 0;
546         }
547 
548         ctx->rt_format = desc->rt_format;
549 
550         if (hwfc->initial_pool_size > 0) {
551             // This pool will be usable as a render target, so we need to store
552             // all of the surface IDs somewhere that vaCreateContext() calls
553             // will be able to access them.
554             avfc->nb_surfaces = 0;
555             avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
556                                           sizeof(*avfc->surface_ids));
557             if (!avfc->surface_ids) {
558                 err = AVERROR(ENOMEM);
559                 goto fail;
560             }
561         } else {
562             // This pool allows dynamic sizing, and will not be usable as a
563             // render target.
564             avfc->nb_surfaces = 0;
565             avfc->surface_ids = NULL;
566         }
567 
568         hwfc->internal->pool_internal =
569             av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
570                                  &vaapi_pool_alloc, NULL);
571         if (!hwfc->internal->pool_internal) {
572             av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
573             err = AVERROR(ENOMEM);
574             goto fail;
575         }
576     }
577 
578     // Allocate a single surface to test whether vaDeriveImage() is going
579     // to work for the specific configuration.
580     if (hwfc->pool) {
581         test_surface = av_buffer_pool_get(hwfc->pool);
582         if (!test_surface) {
583             av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
584                    "user-configured buffer pool.\n");
585             err = AVERROR(ENOMEM);
586             goto fail;
587         }
588     } else {
589         test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
590         if (!test_surface) {
591             av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
592                    "internal buffer pool.\n");
593             err = AVERROR(ENOMEM);
594             goto fail;
595         }
596     }
597     test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
598 
599     ctx->derive_works = 0;
600 
601     err = vaapi_get_image_format(hwfc->device_ctx,
602                                  hwfc->sw_format, &expected_format);
603     if (err == 0) {
604         vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
605         if (vas == VA_STATUS_SUCCESS) {
606             if (expected_format->fourcc == test_image.format.fourcc) {
607                 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
608                 ctx->derive_works = 1;
609             } else {
610                 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
611                        "derived image format %08x does not match "
612                        "expected format %08x.\n",
613                        expected_format->fourcc, test_image.format.fourcc);
614             }
615             vaDestroyImage(hwctx->display, test_image.image_id);
616         } else {
617             av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
618                    "deriving image does not work: "
619                    "%d (%s).\n", vas, vaErrorStr(vas));
620         }
621     } else {
622         av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
623                "image format is not supported.\n");
624     }
625 
626     av_buffer_unref(&test_surface);
627     return 0;
628 
629 fail:
630     av_buffer_unref(&test_surface);
631     av_freep(&avfc->surface_ids);
632     av_freep(&ctx->attributes);
633     return err;
634 }
635 
vaapi_frames_uninit(AVHWFramesContext * hwfc)636 static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
637 {
638     AVVAAPIFramesContext *avfc = hwfc->hwctx;
639     VAAPIFramesContext    *ctx = hwfc->internal->priv;
640 
641     av_freep(&avfc->surface_ids);
642     av_freep(&ctx->attributes);
643 }
644 
vaapi_get_buffer(AVHWFramesContext * hwfc,AVFrame * frame)645 static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
646 {
647     frame->buf[0] = av_buffer_pool_get(hwfc->pool);
648     if (!frame->buf[0])
649         return AVERROR(ENOMEM);
650 
651     frame->data[3] = frame->buf[0]->data;
652     frame->format  = AV_PIX_FMT_VAAPI;
653     frame->width   = hwfc->width;
654     frame->height  = hwfc->height;
655 
656     return 0;
657 }
658 
vaapi_transfer_get_formats(AVHWFramesContext * hwfc,enum AVHWFrameTransferDirection dir,enum AVPixelFormat ** formats)659 static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
660                                       enum AVHWFrameTransferDirection dir,
661                                       enum AVPixelFormat **formats)
662 {
663     VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
664     enum AVPixelFormat *pix_fmts;
665     int i, k, sw_format_available;
666 
667     sw_format_available = 0;
668     for (i = 0; i < ctx->nb_formats; i++) {
669         if (ctx->formats[i].pix_fmt == hwfc->sw_format)
670             sw_format_available = 1;
671     }
672 
673     pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
674     if (!pix_fmts)
675         return AVERROR(ENOMEM);
676 
677     if (sw_format_available) {
678         pix_fmts[0] = hwfc->sw_format;
679         k = 1;
680     } else {
681         k = 0;
682     }
683     for (i = 0; i < ctx->nb_formats; i++) {
684         if (ctx->formats[i].pix_fmt == hwfc->sw_format)
685             continue;
686         av_assert0(k < ctx->nb_formats);
687         pix_fmts[k++] = ctx->formats[i].pix_fmt;
688     }
689     pix_fmts[k] = AV_PIX_FMT_NONE;
690 
691     *formats = pix_fmts;
692     return 0;
693 }
694 
vaapi_unmap_frame(AVHWFramesContext * hwfc,HWMapDescriptor * hwmap)695 static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
696                               HWMapDescriptor *hwmap)
697 {
698     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
699     VAAPIMapping           *map = hwmap->priv;
700     VASurfaceID surface_id;
701     VAStatus vas;
702 
703     surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
704     av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
705 
706     vas = vaUnmapBuffer(hwctx->display, map->image.buf);
707     if (vas != VA_STATUS_SUCCESS) {
708         av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
709                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
710     }
711 
712     if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
713         !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
714         vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
715                          0, 0, hwfc->width, hwfc->height,
716                          0, 0, hwfc->width, hwfc->height);
717         if (vas != VA_STATUS_SUCCESS) {
718             av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
719                    "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
720         }
721     }
722 
723     vas = vaDestroyImage(hwctx->display, map->image.image_id);
724     if (vas != VA_STATUS_SUCCESS) {
725         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
726                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
727     }
728 
729     av_free(map);
730 }
731 
vaapi_map_frame(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)732 static int vaapi_map_frame(AVHWFramesContext *hwfc,
733                            AVFrame *dst, const AVFrame *src, int flags)
734 {
735     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
736     VAAPIFramesContext *ctx = hwfc->internal->priv;
737     VASurfaceID surface_id;
738     const VAAPIFormatDescriptor *desc;
739     VAImageFormat *image_format;
740     VAAPIMapping *map;
741     VAStatus vas;
742     void *address = NULL;
743     int err, i;
744 
745     surface_id = (VASurfaceID)(uintptr_t)src->data[3];
746     av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
747 
748     if (!ctx->derive_works && (flags & AV_HWFRAME_MAP_DIRECT)) {
749         // Requested direct mapping but it is not possible.
750         return AVERROR(EINVAL);
751     }
752     if (dst->format == AV_PIX_FMT_NONE)
753         dst->format = hwfc->sw_format;
754     if (dst->format != hwfc->sw_format && (flags & AV_HWFRAME_MAP_DIRECT)) {
755         // Requested direct mapping but the formats do not match.
756         return AVERROR(EINVAL);
757     }
758 
759     err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
760     if (err < 0) {
761         // Requested format is not a valid output format.
762         return AVERROR(EINVAL);
763     }
764 
765     map = av_malloc(sizeof(*map));
766     if (!map)
767         return AVERROR(ENOMEM);
768     map->flags = flags;
769     map->image.image_id = VA_INVALID_ID;
770 
771     vas = vaSyncSurface(hwctx->display, surface_id);
772     if (vas != VA_STATUS_SUCCESS) {
773         av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
774                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
775         err = AVERROR(EIO);
776         goto fail;
777     }
778 
779     // The memory which we map using derive need not be connected to the CPU
780     // in a way conducive to fast access.  On Gen7-Gen9 Intel graphics, the
781     // memory is mappable but not cached, so normal memcpy()-like access is
782     // very slow to read it (but writing is ok).  It is possible to read much
783     // faster with a copy routine which is aware of the limitation, but we
784     // assume for now that the user is not aware of that and would therefore
785     // prefer not to be given direct-mapped memory if they request read access.
786     if (ctx->derive_works && dst->format == hwfc->sw_format &&
787         ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
788         vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
789         if (vas != VA_STATUS_SUCCESS) {
790             av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
791                    "surface %#x: %d (%s).\n",
792                    surface_id, vas, vaErrorStr(vas));
793             err = AVERROR(EIO);
794             goto fail;
795         }
796         if (map->image.format.fourcc != image_format->fourcc) {
797             av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
798                    "is in wrong format: expected %#08x, got %#08x.\n",
799                    surface_id, image_format->fourcc, map->image.format.fourcc);
800             err = AVERROR(EIO);
801             goto fail;
802         }
803         map->flags |= AV_HWFRAME_MAP_DIRECT;
804     } else {
805         vas = vaCreateImage(hwctx->display, image_format,
806                             hwfc->width, hwfc->height, &map->image);
807         if (vas != VA_STATUS_SUCCESS) {
808             av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
809                    "surface %#x: %d (%s).\n",
810                    surface_id, vas, vaErrorStr(vas));
811             err = AVERROR(EIO);
812             goto fail;
813         }
814         if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
815             vas = vaGetImage(hwctx->display, surface_id, 0, 0,
816                              hwfc->width, hwfc->height, map->image.image_id);
817             if (vas != VA_STATUS_SUCCESS) {
818                 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
819                        "surface %#x: %d (%s).\n",
820                        surface_id, vas, vaErrorStr(vas));
821                 err = AVERROR(EIO);
822                 goto fail;
823             }
824         }
825     }
826 
827     vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
828     if (vas != VA_STATUS_SUCCESS) {
829         av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
830                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
831         err = AVERROR(EIO);
832         goto fail;
833     }
834 
835     err = ff_hwframe_map_create(src->hw_frames_ctx,
836                                 dst, src, &vaapi_unmap_frame, map);
837     if (err < 0)
838         goto fail;
839 
840     dst->width  = src->width;
841     dst->height = src->height;
842 
843     for (i = 0; i < map->image.num_planes; i++) {
844         dst->data[i] = (uint8_t*)address + map->image.offsets[i];
845         dst->linesize[i] = map->image.pitches[i];
846     }
847 
848     desc = vaapi_format_from_fourcc(map->image.format.fourcc);
849     if (desc && desc->chroma_planes_swapped) {
850         // Chroma planes are YVU rather than YUV, so swap them.
851         FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
852     }
853 
854     return 0;
855 
856 fail:
857     if (map) {
858         if (address)
859             vaUnmapBuffer(hwctx->display, map->image.buf);
860         if (map->image.image_id != VA_INVALID_ID)
861             vaDestroyImage(hwctx->display, map->image.image_id);
862         av_free(map);
863     }
864     return err;
865 }
866 
vaapi_transfer_data_from(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src)867 static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
868                                     AVFrame *dst, const AVFrame *src)
869 {
870     AVFrame *map;
871     int err;
872 
873     if (dst->width > hwfc->width || dst->height > hwfc->height)
874         return AVERROR(EINVAL);
875 
876     map = av_frame_alloc();
877     if (!map)
878         return AVERROR(ENOMEM);
879     map->format = dst->format;
880 
881     err = vaapi_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
882     if (err)
883         goto fail;
884 
885     map->width  = dst->width;
886     map->height = dst->height;
887 
888     err = av_frame_copy(dst, map);
889     if (err)
890         goto fail;
891 
892     err = 0;
893 fail:
894     av_frame_free(&map);
895     return err;
896 }
897 
vaapi_transfer_data_to(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src)898 static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
899                                   AVFrame *dst, const AVFrame *src)
900 {
901     AVFrame *map;
902     int err;
903 
904     if (src->width > hwfc->width || src->height > hwfc->height)
905         return AVERROR(EINVAL);
906 
907     map = av_frame_alloc();
908     if (!map)
909         return AVERROR(ENOMEM);
910     map->format = src->format;
911 
912     err = vaapi_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
913     if (err)
914         goto fail;
915 
916     map->width  = src->width;
917     map->height = src->height;
918 
919     err = av_frame_copy(map, src);
920     if (err)
921         goto fail;
922 
923     err = 0;
924 fail:
925     av_frame_free(&map);
926     return err;
927 }
928 
vaapi_map_to_memory(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)929 static int vaapi_map_to_memory(AVHWFramesContext *hwfc, AVFrame *dst,
930                                const AVFrame *src, int flags)
931 {
932     int err;
933 
934     if (dst->format != AV_PIX_FMT_NONE) {
935         err = vaapi_get_image_format(hwfc->device_ctx, dst->format, NULL);
936         if (err < 0)
937             return AVERROR(ENOSYS);
938     }
939 
940     err = vaapi_map_frame(hwfc, dst, src, flags);
941     if (err)
942         return err;
943 
944     err = av_frame_copy_props(dst, src);
945     if (err)
946         return err;
947 
948     return 0;
949 }
950 
951 #if CONFIG_LIBDRM
952 
953 #define DRM_MAP(va, layers, ...) { \
954         VA_FOURCC_ ## va, \
955         layers, \
956         { __VA_ARGS__ } \
957     }
958 static const struct {
959     uint32_t va_fourcc;
960     int   nb_layer_formats;
961     uint32_t layer_formats[AV_DRM_MAX_PLANES];
962 } vaapi_drm_format_map[] = {
963 #ifdef DRM_FORMAT_R8
964     DRM_MAP(NV12, 2, DRM_FORMAT_R8,  DRM_FORMAT_RG88),
965 #endif
966     DRM_MAP(NV12, 1, DRM_FORMAT_NV12),
967 #if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16)
968     DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
969 #endif
970     DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888),
971     DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888),
972     DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888),
973     DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888),
974 #ifdef VA_FOURCC_ABGR
975     DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888),
976     DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888),
977 #endif
978     DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888),
979     DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888),
980 };
981 #undef DRM_MAP
982 
vaapi_unmap_from_drm(AVHWFramesContext * dst_fc,HWMapDescriptor * hwmap)983 static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
984                                  HWMapDescriptor *hwmap)
985 {
986     AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
987 
988     VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
989 
990     av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id);
991 
992     vaDestroySurfaces(dst_dev->display, &surface_id, 1);
993 }
994 
vaapi_map_from_drm(AVHWFramesContext * src_fc,AVFrame * dst,const AVFrame * src,int flags)995 static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
996                               const AVFrame *src, int flags)
997 {
998     AVHWFramesContext      *dst_fc =
999         (AVHWFramesContext*)dst->hw_frames_ctx->data;
1000     AVVAAPIDeviceContext  *dst_dev = dst_fc->device_ctx->hwctx;
1001     const AVDRMFrameDescriptor *desc;
1002     const VAAPIFormatDescriptor *format_desc;
1003     VASurfaceID surface_id;
1004     VAStatus vas;
1005     uint32_t va_fourcc;
1006     int err, i, j, k;
1007 
1008     unsigned long buffer_handle;
1009     VASurfaceAttribExternalBuffers buffer_desc;
1010     VASurfaceAttrib attrs[2] = {
1011         {
1012             .type  = VASurfaceAttribMemoryType,
1013             .flags = VA_SURFACE_ATTRIB_SETTABLE,
1014             .value.type    = VAGenericValueTypeInteger,
1015             .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
1016         },
1017         {
1018             .type  = VASurfaceAttribExternalBufferDescriptor,
1019             .flags = VA_SURFACE_ATTRIB_SETTABLE,
1020             .value.type    = VAGenericValueTypePointer,
1021             .value.value.p = &buffer_desc,
1022         }
1023     };
1024 
1025     desc = (AVDRMFrameDescriptor*)src->data[0];
1026 
1027     if (desc->nb_objects != 1) {
1028         av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames "
1029                "made from a single DRM object.\n");
1030         return AVERROR(EINVAL);
1031     }
1032 
1033     va_fourcc = 0;
1034     for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1035         if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats)
1036             continue;
1037         for (j = 0; j < desc->nb_layers; j++) {
1038             if (desc->layers[j].format !=
1039                 vaapi_drm_format_map[i].layer_formats[j])
1040                 break;
1041         }
1042         if (j != desc->nb_layers)
1043             continue;
1044         va_fourcc = vaapi_drm_format_map[i].va_fourcc;
1045         break;
1046     }
1047     if (!va_fourcc) {
1048         av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported "
1049                "by VAAPI.\n");
1050         return AVERROR(EINVAL);
1051     }
1052 
1053     av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
1054            "%08x.\n", desc->objects[0].fd, va_fourcc);
1055 
1056     format_desc = vaapi_format_from_fourcc(va_fourcc);
1057     av_assert0(format_desc);
1058 
1059     buffer_handle = desc->objects[0].fd;
1060     buffer_desc.pixel_format = va_fourcc;
1061     buffer_desc.width        = src_fc->width;
1062     buffer_desc.height       = src_fc->height;
1063     buffer_desc.data_size    = desc->objects[0].size;
1064     buffer_desc.buffers      = &buffer_handle;
1065     buffer_desc.num_buffers  = 1;
1066     buffer_desc.flags        = 0;
1067 
1068     k = 0;
1069     for (i = 0; i < desc->nb_layers; i++) {
1070         for (j = 0; j < desc->layers[i].nb_planes; j++) {
1071             buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
1072             buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
1073             ++k;
1074         }
1075     }
1076     buffer_desc.num_planes = k;
1077 
1078     if (format_desc->chroma_planes_swapped &&
1079         buffer_desc.num_planes == 3) {
1080         FFSWAP(uint32_t, buffer_desc.pitches[1], buffer_desc.pitches[2]);
1081         FFSWAP(uint32_t, buffer_desc.offsets[1], buffer_desc.offsets[2]);
1082     }
1083 
1084     vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
1085                            src->width, src->height,
1086                            &surface_id, 1,
1087                            attrs, FF_ARRAY_ELEMS(attrs));
1088     if (vas != VA_STATUS_SUCCESS) {
1089         av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
1090                "object: %d (%s).\n", vas, vaErrorStr(vas));
1091         return AVERROR(EIO);
1092     }
1093     av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
1094 
1095     err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
1096                                 &vaapi_unmap_from_drm,
1097                                 (void*)(uintptr_t)surface_id);
1098     if (err < 0)
1099         return err;
1100 
1101     dst->width   = src->width;
1102     dst->height  = src->height;
1103     dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
1104 
1105     av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
1106            "surface %#x.\n", desc->objects[0].fd, surface_id);
1107 
1108     return 0;
1109 }
1110 
1111 #if VA_CHECK_VERSION(1, 1, 0)
vaapi_unmap_to_drm_esh(AVHWFramesContext * hwfc,HWMapDescriptor * hwmap)1112 static void vaapi_unmap_to_drm_esh(AVHWFramesContext *hwfc,
1113                                    HWMapDescriptor *hwmap)
1114 {
1115     AVDRMFrameDescriptor *drm_desc = hwmap->priv;
1116     int i;
1117 
1118     for (i = 0; i < drm_desc->nb_objects; i++)
1119         close(drm_desc->objects[i].fd);
1120 
1121     av_freep(&drm_desc);
1122 }
1123 
vaapi_map_to_drm_esh(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)1124 static int vaapi_map_to_drm_esh(AVHWFramesContext *hwfc, AVFrame *dst,
1125                                 const AVFrame *src, int flags)
1126 {
1127     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1128     VASurfaceID surface_id;
1129     VAStatus vas;
1130     VADRMPRIMESurfaceDescriptor va_desc;
1131     AVDRMFrameDescriptor *drm_desc = NULL;
1132     uint32_t export_flags;
1133     int err, i, j;
1134 
1135     surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1136 
1137     export_flags = VA_EXPORT_SURFACE_SEPARATE_LAYERS;
1138     if (flags & AV_HWFRAME_MAP_READ)
1139         export_flags |= VA_EXPORT_SURFACE_READ_ONLY;
1140     if (flags & AV_HWFRAME_MAP_WRITE)
1141         export_flags |= VA_EXPORT_SURFACE_WRITE_ONLY;
1142 
1143     vas = vaExportSurfaceHandle(hwctx->display, surface_id,
1144                                 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
1145                                 export_flags, &va_desc);
1146     if (vas != VA_STATUS_SUCCESS) {
1147         if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
1148             return AVERROR(ENOSYS);
1149         av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
1150                "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
1151         return AVERROR(EIO);
1152     }
1153 
1154     drm_desc = av_mallocz(sizeof(*drm_desc));
1155     if (!drm_desc) {
1156         err = AVERROR(ENOMEM);
1157         goto fail;
1158     }
1159 
1160     // By some bizarre coincidence, these structures are very similar...
1161     drm_desc->nb_objects = va_desc.num_objects;
1162     for (i = 0; i < va_desc.num_objects; i++) {
1163         drm_desc->objects[i].fd   = va_desc.objects[i].fd;
1164         drm_desc->objects[i].size = va_desc.objects[i].size;
1165         drm_desc->objects[i].format_modifier =
1166             va_desc.objects[i].drm_format_modifier;
1167     }
1168     drm_desc->nb_layers = va_desc.num_layers;
1169     for (i = 0; i < va_desc.num_layers; i++) {
1170         drm_desc->layers[i].format    = va_desc.layers[i].drm_format;
1171         drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes;
1172         for (j = 0; j < va_desc.layers[i].num_planes; j++) {
1173             drm_desc->layers[i].planes[j].object_index =
1174                 va_desc.layers[i].object_index[j];
1175             drm_desc->layers[i].planes[j].offset =
1176                 va_desc.layers[i].offset[j];
1177             drm_desc->layers[i].planes[j].pitch =
1178                 va_desc.layers[i].pitch[j];
1179         }
1180     }
1181 
1182     err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
1183                                 &vaapi_unmap_to_drm_esh, drm_desc);
1184     if (err < 0)
1185         goto fail;
1186 
1187     dst->width   = src->width;
1188     dst->height  = src->height;
1189     dst->data[0] = (uint8_t*)drm_desc;
1190 
1191     return 0;
1192 
1193 fail:
1194     for (i = 0; i < va_desc.num_objects; i++)
1195         close(va_desc.objects[i].fd);
1196     av_freep(&drm_desc);
1197     return err;
1198 }
1199 #endif
1200 
1201 #if VA_CHECK_VERSION(0, 36, 0)
1202 typedef struct VAAPIDRMImageBufferMapping {
1203     VAImage      image;
1204     VABufferInfo buffer_info;
1205 
1206     AVDRMFrameDescriptor drm_desc;
1207 } VAAPIDRMImageBufferMapping;
1208 
vaapi_unmap_to_drm_abh(AVHWFramesContext * hwfc,HWMapDescriptor * hwmap)1209 static void vaapi_unmap_to_drm_abh(AVHWFramesContext *hwfc,
1210                                   HWMapDescriptor *hwmap)
1211 {
1212     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1213     VAAPIDRMImageBufferMapping *mapping = hwmap->priv;
1214     VASurfaceID surface_id;
1215     VAStatus vas;
1216 
1217     surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
1218     av_log(hwfc, AV_LOG_DEBUG, "Unmap VAAPI surface %#x from DRM.\n",
1219            surface_id);
1220 
1221     // DRM PRIME file descriptors are closed by vaReleaseBufferHandle(),
1222     // so we shouldn't close them separately.
1223 
1224     vas = vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1225     if (vas != VA_STATUS_SUCCESS) {
1226         av_log(hwfc, AV_LOG_ERROR, "Failed to release buffer "
1227                "handle of image %#x (derived from surface %#x): "
1228                "%d (%s).\n", mapping->image.buf, surface_id,
1229                vas, vaErrorStr(vas));
1230     }
1231 
1232     vas = vaDestroyImage(hwctx->display, mapping->image.image_id);
1233     if (vas != VA_STATUS_SUCCESS) {
1234         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image "
1235                "derived from surface %#x: %d (%s).\n",
1236                surface_id, vas, vaErrorStr(vas));
1237     }
1238 
1239     av_free(mapping);
1240 }
1241 
vaapi_map_to_drm_abh(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)1242 static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst,
1243                                 const AVFrame *src, int flags)
1244 {
1245     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1246     VAAPIDRMImageBufferMapping *mapping = NULL;
1247     VASurfaceID surface_id;
1248     VAStatus vas;
1249     int err, i, p;
1250 
1251     surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1252     av_log(hwfc, AV_LOG_DEBUG, "Map VAAPI surface %#x to DRM.\n",
1253            surface_id);
1254 
1255     mapping = av_mallocz(sizeof(*mapping));
1256     if (!mapping)
1257         return AVERROR(ENOMEM);
1258 
1259     vas = vaDeriveImage(hwctx->display, surface_id,
1260                         &mapping->image);
1261     if (vas != VA_STATUS_SUCCESS) {
1262         av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
1263                "surface %#x: %d (%s).\n",
1264                surface_id, vas, vaErrorStr(vas));
1265         err = AVERROR(EIO);
1266         goto fail;
1267     }
1268 
1269     for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1270         if (vaapi_drm_format_map[i].va_fourcc ==
1271             mapping->image.format.fourcc)
1272             break;
1273     }
1274     if (i >= FF_ARRAY_ELEMS(vaapi_drm_format_map)) {
1275         av_log(hwfc, AV_LOG_ERROR, "No matching DRM format for "
1276                "VAAPI format %#x.\n", mapping->image.format.fourcc);
1277         err = AVERROR(EINVAL);
1278         goto fail_derived;
1279     }
1280 
1281     mapping->buffer_info.mem_type =
1282         VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
1283 
1284     mapping->drm_desc.nb_layers =
1285         vaapi_drm_format_map[i].nb_layer_formats;
1286     if (mapping->drm_desc.nb_layers > 1) {
1287         if (mapping->drm_desc.nb_layers != mapping->image.num_planes) {
1288             av_log(hwfc, AV_LOG_ERROR, "Image properties do not match "
1289                    "expected format: got %d planes, but expected %d.\n",
1290                    mapping->image.num_planes, mapping->drm_desc.nb_layers);
1291             err = AVERROR(EINVAL);
1292             goto fail_derived;
1293         }
1294 
1295         for(p = 0; p < mapping->drm_desc.nb_layers; p++) {
1296             mapping->drm_desc.layers[p] = (AVDRMLayerDescriptor) {
1297                 .format    = vaapi_drm_format_map[i].layer_formats[p],
1298                 .nb_planes = 1,
1299                 .planes[0] = {
1300                     .object_index = 0,
1301                     .offset       = mapping->image.offsets[p],
1302                     .pitch        = mapping->image.pitches[p],
1303                 },
1304             };
1305         }
1306     } else {
1307         mapping->drm_desc.layers[0].format =
1308             vaapi_drm_format_map[i].layer_formats[0];
1309         mapping->drm_desc.layers[0].nb_planes = mapping->image.num_planes;
1310         for (p = 0; p < mapping->image.num_planes; p++) {
1311             mapping->drm_desc.layers[0].planes[p] = (AVDRMPlaneDescriptor) {
1312                 .object_index = 0,
1313                 .offset       = mapping->image.offsets[p],
1314                 .pitch        = mapping->image.pitches[p],
1315             };
1316         }
1317     }
1318 
1319     vas = vaAcquireBufferHandle(hwctx->display, mapping->image.buf,
1320                                 &mapping->buffer_info);
1321     if (vas != VA_STATUS_SUCCESS) {
1322         av_log(hwfc, AV_LOG_ERROR, "Failed to get buffer "
1323                "handle from image %#x (derived from surface %#x): "
1324                "%d (%s).\n", mapping->image.buf, surface_id,
1325                vas, vaErrorStr(vas));
1326         err = AVERROR(EIO);
1327         goto fail_derived;
1328     }
1329 
1330     av_log(hwfc, AV_LOG_DEBUG, "DRM PRIME fd is %ld.\n",
1331            mapping->buffer_info.handle);
1332 
1333     mapping->drm_desc.nb_objects = 1;
1334     mapping->drm_desc.objects[0] = (AVDRMObjectDescriptor) {
1335         .fd   = mapping->buffer_info.handle,
1336         .size = mapping->image.data_size,
1337         // There is no way to get the format modifier with this API.
1338         .format_modifier = DRM_FORMAT_MOD_INVALID,
1339     };
1340 
1341     err = ff_hwframe_map_create(src->hw_frames_ctx,
1342                                 dst, src, &vaapi_unmap_to_drm_abh,
1343                                 mapping);
1344     if (err < 0)
1345         goto fail_mapped;
1346 
1347     dst->data[0] = (uint8_t*)&mapping->drm_desc;
1348     dst->width   = src->width;
1349     dst->height  = src->height;
1350 
1351     return 0;
1352 
1353 fail_mapped:
1354     vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1355 fail_derived:
1356     vaDestroyImage(hwctx->display, mapping->image.image_id);
1357 fail:
1358     av_freep(&mapping);
1359     return err;
1360 }
1361 #endif
1362 
vaapi_map_to_drm(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)1363 static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
1364                             const AVFrame *src, int flags)
1365 {
1366 #if VA_CHECK_VERSION(1, 1, 0)
1367     int err;
1368     err = vaapi_map_to_drm_esh(hwfc, dst, src, flags);
1369     if (err != AVERROR(ENOSYS))
1370         return err;
1371 #endif
1372 #if VA_CHECK_VERSION(0, 36, 0)
1373     return vaapi_map_to_drm_abh(hwfc, dst, src, flags);
1374 #endif
1375     return AVERROR(ENOSYS);
1376 }
1377 
1378 #endif /* CONFIG_LIBDRM */
1379 
vaapi_map_to(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)1380 static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
1381                         const AVFrame *src, int flags)
1382 {
1383     switch (src->format) {
1384 #if CONFIG_LIBDRM
1385     case AV_PIX_FMT_DRM_PRIME:
1386         return vaapi_map_from_drm(hwfc, dst, src, flags);
1387 #endif
1388     default:
1389         return AVERROR(ENOSYS);
1390     }
1391 }
1392 
vaapi_map_from(AVHWFramesContext * hwfc,AVFrame * dst,const AVFrame * src,int flags)1393 static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
1394                           const AVFrame *src, int flags)
1395 {
1396     switch (dst->format) {
1397 #if CONFIG_LIBDRM
1398     case AV_PIX_FMT_DRM_PRIME:
1399         return vaapi_map_to_drm(hwfc, dst, src, flags);
1400 #endif
1401     default:
1402         return vaapi_map_to_memory(hwfc, dst, src, flags);
1403     }
1404 }
1405 
vaapi_device_free(AVHWDeviceContext * ctx)1406 static void vaapi_device_free(AVHWDeviceContext *ctx)
1407 {
1408     AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1409     VAAPIDevicePriv      *priv  = ctx->user_opaque;
1410 
1411     if (hwctx->display)
1412         vaTerminate(hwctx->display);
1413 
1414 #if HAVE_VAAPI_X11
1415     if (priv->x11_display)
1416         XCloseDisplay(priv->x11_display);
1417 #endif
1418 
1419     if (priv->drm_fd >= 0)
1420         close(priv->drm_fd);
1421 
1422     av_freep(&priv);
1423 }
1424 
1425 #if CONFIG_VAAPI_1
vaapi_device_log_error(void * context,const char * message)1426 static void vaapi_device_log_error(void *context, const char *message)
1427 {
1428     AVHWDeviceContext *ctx = context;
1429 
1430     av_log(ctx, AV_LOG_ERROR, "libva: %s", message);
1431 }
1432 
vaapi_device_log_info(void * context,const char * message)1433 static void vaapi_device_log_info(void *context, const char *message)
1434 {
1435     AVHWDeviceContext *ctx = context;
1436 
1437     av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message);
1438 }
1439 #endif
1440 
vaapi_device_connect(AVHWDeviceContext * ctx,VADisplay display)1441 static int vaapi_device_connect(AVHWDeviceContext *ctx,
1442                                 VADisplay display)
1443 {
1444     AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1445     int major, minor;
1446     VAStatus vas;
1447 
1448 #if CONFIG_VAAPI_1
1449     vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
1450     vaSetInfoCallback (display, &vaapi_device_log_info,  ctx);
1451 #endif
1452 
1453     hwctx->display = display;
1454 
1455     vas = vaInitialize(display, &major, &minor);
1456     if (vas != VA_STATUS_SUCCESS) {
1457         av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
1458                "connection: %d (%s).\n", vas, vaErrorStr(vas));
1459         return AVERROR(EIO);
1460     }
1461     av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
1462            "version %d.%d\n", major, minor);
1463 
1464     return 0;
1465 }
1466 
vaapi_device_create(AVHWDeviceContext * ctx,const char * device,AVDictionary * opts,int flags)1467 static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
1468                                AVDictionary *opts, int flags)
1469 {
1470     VAAPIDevicePriv *priv;
1471     VADisplay display = NULL;
1472 
1473     priv = av_mallocz(sizeof(*priv));
1474     if (!priv)
1475         return AVERROR(ENOMEM);
1476 
1477     priv->drm_fd = -1;
1478 
1479     ctx->user_opaque = priv;
1480     ctx->free        = vaapi_device_free;
1481 
1482 #if HAVE_VAAPI_X11
1483     if (!display && !(device && device[0] == '/')) {
1484         // Try to open the device as an X11 display.
1485         priv->x11_display = XOpenDisplay(device);
1486         if (!priv->x11_display) {
1487             av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
1488                    "%s.\n", XDisplayName(device));
1489         } else {
1490             display = vaGetDisplay(priv->x11_display);
1491             if (!display) {
1492                 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1493                        "from X11 display %s.\n", XDisplayName(device));
1494                 return AVERROR_UNKNOWN;
1495             }
1496 
1497             av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1498                    "X11 display %s.\n", XDisplayName(device));
1499         }
1500     }
1501 #endif
1502 
1503 #if HAVE_VAAPI_DRM
1504     if (!display) {
1505         // Try to open the device as a DRM path.
1506         // Default to using the first render node if the user did not
1507         // supply a path.
1508         const char *path = device ? device : "/dev/dri/renderD128";
1509         priv->drm_fd = open(path, O_RDWR);
1510         if (priv->drm_fd < 0) {
1511             av_log(ctx, AV_LOG_VERBOSE, "Cannot open DRM device %s.\n",
1512                    path);
1513         } else {
1514             display = vaGetDisplayDRM(priv->drm_fd);
1515             if (!display) {
1516                 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1517                        "from DRM device %s.\n", path);
1518                 return AVERROR_UNKNOWN;
1519             }
1520 
1521             av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1522                    "DRM device %s.\n", path);
1523         }
1524     }
1525 #endif
1526 
1527     if (!display) {
1528         av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1529                "device: %s.\n", device ? device : "");
1530         return AVERROR(EINVAL);
1531     }
1532 
1533     return vaapi_device_connect(ctx, display);
1534 }
1535 
vaapi_device_derive(AVHWDeviceContext * ctx,AVHWDeviceContext * src_ctx,int flags)1536 static int vaapi_device_derive(AVHWDeviceContext *ctx,
1537                                AVHWDeviceContext *src_ctx, int flags)
1538 {
1539 #if HAVE_VAAPI_DRM
1540     if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
1541         AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
1542         VADisplay *display;
1543         VAAPIDevicePriv *priv;
1544 
1545         if (src_hwctx->fd < 0) {
1546             av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
1547                    "device to derive a VA display from.\n");
1548             return AVERROR(EINVAL);
1549         }
1550 
1551         priv = av_mallocz(sizeof(*priv));
1552         if (!priv)
1553             return AVERROR(ENOMEM);
1554 
1555         // Inherits the fd from the source context, which will close it.
1556         priv->drm_fd = -1;
1557 
1558         ctx->user_opaque = priv;
1559         ctx->free        = &vaapi_device_free;
1560 
1561         display = vaGetDisplayDRM(src_hwctx->fd);
1562         if (!display) {
1563             av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
1564                    "DRM device.\n");
1565             return AVERROR(EIO);
1566         }
1567 
1568         return vaapi_device_connect(ctx, display);
1569     }
1570 #endif
1571     return AVERROR(ENOSYS);
1572 }
1573 
1574 const HWContextType ff_hwcontext_type_vaapi = {
1575     .type                   = AV_HWDEVICE_TYPE_VAAPI,
1576     .name                   = "VAAPI",
1577 
1578     .device_hwctx_size      = sizeof(AVVAAPIDeviceContext),
1579     .device_priv_size       = sizeof(VAAPIDeviceContext),
1580     .device_hwconfig_size   = sizeof(AVVAAPIHWConfig),
1581     .frames_hwctx_size      = sizeof(AVVAAPIFramesContext),
1582     .frames_priv_size       = sizeof(VAAPIFramesContext),
1583 
1584     .device_create          = &vaapi_device_create,
1585     .device_derive          = &vaapi_device_derive,
1586     .device_init            = &vaapi_device_init,
1587     .device_uninit          = &vaapi_device_uninit,
1588     .frames_get_constraints = &vaapi_frames_get_constraints,
1589     .frames_init            = &vaapi_frames_init,
1590     .frames_uninit          = &vaapi_frames_uninit,
1591     .frames_get_buffer      = &vaapi_get_buffer,
1592     .transfer_get_formats   = &vaapi_transfer_get_formats,
1593     .transfer_data_to       = &vaapi_transfer_data_to,
1594     .transfer_data_from     = &vaapi_transfer_data_from,
1595     .map_to                 = &vaapi_map_to,
1596     .map_from               = &vaapi_map_from,
1597 
1598     .pix_fmts = (const enum AVPixelFormat[]) {
1599         AV_PIX_FMT_VAAPI,
1600         AV_PIX_FMT_NONE
1601     },
1602 };
1603