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