]> git.sesse.net Git - ffmpeg/blob - libavutil/hwcontext_vaapi.c
Merge commit '7e42d5f0ab2aeac811fd01e122627c9198b13f01'
[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 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 *
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 *
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
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
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
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
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
417 static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
418 {
419     VAAPIDeviceContext *ctx = hwdev->internal->priv;
420
421     av_freep(&ctx->formats);
422 }
423
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
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
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
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
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
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
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
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
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
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
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
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
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)
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
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
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
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
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
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
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
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
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
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
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
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
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 };