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