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