]> git.sesse.net Git - ffmpeg/blob - libavutil/hwcontext_vaapi.c
Merge commit 'e1e3a12242347dd11174b2fb9ddac8dc8df16224'
[ffmpeg] / libavutil / hwcontext_vaapi.c
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include "config.h"
20
21 #if HAVE_VAAPI_X11
22 #   include <va/va_x11.h>
23 #endif
24 #if HAVE_VAAPI_DRM
25 #   include <va/va_drm.h>
26 #endif
27
28 #if CONFIG_LIBDRM
29 #   include <va/va_drmcommon.h>
30 #   include <drm_fourcc.h>
31 #   ifndef DRM_FORMAT_MOD_INVALID
32 #       define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
33 #   endif
34 #endif
35
36 #include <fcntl.h>
37 #if HAVE_UNISTD_H
38 #   include <unistd.h>
39 #endif
40
41
42 #include "avassert.h"
43 #include "buffer.h"
44 #include "common.h"
45 #include "hwcontext.h"
46 #include "hwcontext_drm.h"
47 #include "hwcontext_internal.h"
48 #include "hwcontext_vaapi.h"
49 #include "mem.h"
50 #include "pixdesc.h"
51 #include "pixfmt.h"
52
53
54 typedef struct VAAPIDevicePriv {
55 #if HAVE_VAAPI_X11
56     Display *x11_display;
57 #endif
58
59     int drm_fd;
60 } VAAPIDevicePriv;
61
62 typedef struct VAAPISurfaceFormat {
63     enum AVPixelFormat pix_fmt;
64     VAImageFormat image_format;
65 } VAAPISurfaceFormat;
66
67 typedef struct VAAPIDeviceContext {
68     // Surface formats which can be used with this device.
69     VAAPISurfaceFormat *formats;
70     int              nb_formats;
71 } VAAPIDeviceContext;
72
73 typedef struct VAAPIFramesContext {
74     // Surface attributes set at create time.
75     VASurfaceAttrib *attributes;
76     int           nb_attributes;
77     // RT format of the underlying surface (Intel driver ignores this anyway).
78     unsigned int rt_format;
79     // Whether vaDeriveImage works.
80     int derive_works;
81 } VAAPIFramesContext;
82
83 typedef struct VAAPIMapping {
84     // Handle to the derived or copied image which is mapped.
85     VAImage image;
86     // The mapping flags actually used.
87     int flags;
88 } VAAPIMapping;
89
90 #define MAP(va, rt, av) { \
91         VA_FOURCC_ ## va, \
92         VA_RT_FORMAT_ ## rt, \
93         AV_PIX_FMT_ ## av \
94     }
95 // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
96 // plane swap cases.  The frame handling below tries to hide these.
97 static const struct {
98     unsigned int fourcc;
99     unsigned int rt_format;
100     enum AVPixelFormat pix_fmt;
101 } vaapi_format_map[] = {
102     MAP(NV12, YUV420,  NV12),
103     MAP(YV12, YUV420,  YUV420P), // With U/V planes swapped.
104     MAP(IYUV, YUV420,  YUV420P),
105 #ifdef VA_FOURCC_I420
106     MAP(I420, YUV420,  YUV420P),
107 #endif
108 #ifdef VA_FOURCC_YV16
109     MAP(YV16, YUV422,  YUV422P), // With U/V planes swapped.
110 #endif
111     MAP(422H, YUV422,  YUV422P),
112     MAP(UYVY, YUV422,  UYVY422),
113     MAP(YUY2, YUV422,  YUYV422),
114     MAP(411P, YUV411,  YUV411P),
115     MAP(422V, YUV422,  YUV440P),
116     MAP(444P, YUV444,  YUV444P),
117     MAP(Y800, YUV400,  GRAY8),
118 #ifdef VA_FOURCC_P010
119     MAP(P010, YUV420_10BPP, P010),
120 #endif
121     MAP(BGRA, RGB32,   BGRA),
122     MAP(BGRX, RGB32,   BGR0),
123     MAP(RGBA, RGB32,   RGBA),
124     MAP(RGBX, RGB32,   RGB0),
125 #ifdef VA_FOURCC_ABGR
126     MAP(ABGR, RGB32,   ABGR),
127     MAP(XBGR, RGB32,   0BGR),
128 #endif
129     MAP(ARGB, RGB32,   ARGB),
130     MAP(XRGB, RGB32,   0RGB),
131 };
132 #undef MAP
133
134 static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
135 {
136     int i;
137     for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
138         if (vaapi_format_map[i].fourcc == fourcc)
139             return vaapi_format_map[i].pix_fmt;
140     return AV_PIX_FMT_NONE;
141 }
142
143 static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
144                                   enum AVPixelFormat pix_fmt,
145                                   VAImageFormat **image_format)
146 {
147     VAAPIDeviceContext *ctx = hwdev->internal->priv;
148     int i;
149
150     for (i = 0; i < ctx->nb_formats; i++) {
151         if (ctx->formats[i].pix_fmt == pix_fmt) {
152             if (image_format)
153                 *image_format = &ctx->formats[i].image_format;
154             return 0;
155         }
156     }
157     return AVERROR(EINVAL);
158 }
159
160 static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
161                                         const void *hwconfig,
162                                         AVHWFramesConstraints *constraints)
163 {
164     AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
165     const AVVAAPIHWConfig *config = hwconfig;
166     VAAPIDeviceContext *ctx = hwdev->internal->priv;
167     VASurfaceAttrib *attr_list = NULL;
168     VAStatus vas;
169     enum AVPixelFormat pix_fmt;
170     unsigned int fourcc;
171     int err, i, j, attr_count, pix_fmt_count;
172
173     if (config &&
174         !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES)) {
175         attr_count = 0;
176         vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
177                                        0, &attr_count);
178         if (vas != VA_STATUS_SUCCESS) {
179             av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
180                    "%d (%s).\n", vas, vaErrorStr(vas));
181             err = AVERROR(ENOSYS);
182             goto fail;
183         }
184
185         attr_list = av_malloc(attr_count * sizeof(*attr_list));
186         if (!attr_list) {
187             err = AVERROR(ENOMEM);
188             goto fail;
189         }
190
191         vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
192                                        attr_list, &attr_count);
193         if (vas != VA_STATUS_SUCCESS) {
194             av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
195                    "%d (%s).\n", vas, vaErrorStr(vas));
196             err = AVERROR(ENOSYS);
197             goto fail;
198         }
199
200         pix_fmt_count = 0;
201         for (i = 0; i < attr_count; i++) {
202             switch (attr_list[i].type) {
203             case VASurfaceAttribPixelFormat:
204                 fourcc = attr_list[i].value.value.i;
205                 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
206                 if (pix_fmt != AV_PIX_FMT_NONE) {
207                     ++pix_fmt_count;
208                 } else {
209                     // Something unsupported - ignore.
210                 }
211                 break;
212             case VASurfaceAttribMinWidth:
213                 constraints->min_width  = attr_list[i].value.value.i;
214                 break;
215             case VASurfaceAttribMinHeight:
216                 constraints->min_height = attr_list[i].value.value.i;
217                 break;
218             case VASurfaceAttribMaxWidth:
219                 constraints->max_width  = attr_list[i].value.value.i;
220                 break;
221             case VASurfaceAttribMaxHeight:
222                 constraints->max_height = attr_list[i].value.value.i;
223                 break;
224             }
225         }
226         if (pix_fmt_count == 0) {
227             // Nothing usable found.  Presumably there exists something which
228             // works, so leave the set null to indicate unknown.
229             constraints->valid_sw_formats = NULL;
230         } else {
231             constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
232                                                             sizeof(pix_fmt));
233             if (!constraints->valid_sw_formats) {
234                 err = AVERROR(ENOMEM);
235                 goto fail;
236             }
237
238             for (i = j = 0; i < attr_count; i++) {
239                 if (attr_list[i].type != VASurfaceAttribPixelFormat)
240                     continue;
241                 fourcc = attr_list[i].value.value.i;
242                 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
243                 if (pix_fmt != AV_PIX_FMT_NONE)
244                     constraints->valid_sw_formats[j++] = pix_fmt;
245             }
246             av_assert0(j == pix_fmt_count);
247             constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
248         }
249     } else {
250         // No configuration supplied.
251         // Return the full set of image formats known by the implementation.
252         constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
253                                                         sizeof(pix_fmt));
254         if (!constraints->valid_sw_formats) {
255             err = AVERROR(ENOMEM);
256             goto fail;
257         }
258         for (i = 0; i < ctx->nb_formats; i++)
259             constraints->valid_sw_formats[i] = ctx->formats[i].pix_fmt;
260         constraints->valid_sw_formats[i] = AV_PIX_FMT_NONE;
261     }
262
263     constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
264     if (!constraints->valid_hw_formats) {
265         err = AVERROR(ENOMEM);
266         goto fail;
267     }
268     constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
269     constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
270
271     err = 0;
272 fail:
273     av_freep(&attr_list);
274     return err;
275 }
276
277 static const struct {
278     const char *friendly_name;
279     const char *match_string;
280     unsigned int quirks;
281 } vaapi_driver_quirks_table[] = {
282     {
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             break;
1030         }
1031     }
1032
1033     av_assert0(i < FF_ARRAY_ELEMS(vaapi_format_map));
1034
1035     buffer_handle = desc->objects[0].fd;
1036     buffer_desc.pixel_format = va_fourcc;
1037     buffer_desc.width        = src_fc->width;
1038     buffer_desc.height       = src_fc->height;
1039     buffer_desc.data_size    = desc->objects[0].size;
1040     buffer_desc.buffers      = &buffer_handle;
1041     buffer_desc.num_buffers  = 1;
1042     buffer_desc.flags        = 0;
1043
1044     k = 0;
1045     for (i = 0; i < desc->nb_layers; i++) {
1046         for (j = 0; j < desc->layers[i].nb_planes; j++) {
1047             buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
1048             buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
1049             ++k;
1050         }
1051     }
1052     buffer_desc.num_planes = k;
1053
1054     vas = vaCreateSurfaces(dst_dev->display, va_rt_format,
1055                            src->width, src->height,
1056                            &surface_id, 1,
1057                            attrs, FF_ARRAY_ELEMS(attrs));
1058     if (vas != VA_STATUS_SUCCESS) {
1059         av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
1060                "object: %d (%s).\n", vas, vaErrorStr(vas));
1061         return AVERROR(EIO);
1062     }
1063     av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id);
1064
1065     err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
1066                                 &vaapi_unmap_from_drm,
1067                                 (void*)(uintptr_t)surface_id);
1068     if (err < 0)
1069         return err;
1070
1071     dst->width   = src->width;
1072     dst->height  = src->height;
1073     dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
1074
1075     av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
1076            "surface %#x.\n", desc->objects[0].fd, surface_id);
1077
1078     return 0;
1079 }
1080
1081 #if VA_CHECK_VERSION(1, 1, 0)
1082 static void vaapi_unmap_to_drm_esh(AVHWFramesContext *hwfc,
1083                                    HWMapDescriptor *hwmap)
1084 {
1085     AVDRMFrameDescriptor *drm_desc = hwmap->priv;
1086     int i;
1087
1088     for (i = 0; i < drm_desc->nb_objects; i++)
1089         close(drm_desc->objects[i].fd);
1090
1091     av_freep(&drm_desc);
1092 }
1093
1094 static int vaapi_map_to_drm_esh(AVHWFramesContext *hwfc, AVFrame *dst,
1095                                 const AVFrame *src, int flags)
1096 {
1097     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1098     VASurfaceID surface_id;
1099     VAStatus vas;
1100     VADRMPRIMESurfaceDescriptor va_desc;
1101     AVDRMFrameDescriptor *drm_desc = NULL;
1102     uint32_t export_flags;
1103     int err, i, j;
1104
1105     surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1106
1107     export_flags = VA_EXPORT_SURFACE_SEPARATE_LAYERS;
1108     if (flags & AV_HWFRAME_MAP_READ)
1109         export_flags |= VA_EXPORT_SURFACE_READ_ONLY;
1110     if (flags & AV_HWFRAME_MAP_WRITE)
1111         export_flags |= VA_EXPORT_SURFACE_WRITE_ONLY;
1112
1113     vas = vaExportSurfaceHandle(hwctx->display, surface_id,
1114                                 VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
1115                                 export_flags, &va_desc);
1116     if (vas != VA_STATUS_SUCCESS) {
1117         if (vas == VA_STATUS_ERROR_UNIMPLEMENTED)
1118             return AVERROR(ENOSYS);
1119         av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: "
1120                "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
1121         return AVERROR(EIO);
1122     }
1123
1124     drm_desc = av_mallocz(sizeof(*drm_desc));
1125     if (!drm_desc) {
1126         err = AVERROR(ENOMEM);
1127         goto fail;
1128     }
1129
1130     // By some bizarre coincidence, these structures are very similar...
1131     drm_desc->nb_objects = va_desc.num_objects;
1132     for (i = 0; i < va_desc.num_objects; i++) {
1133         drm_desc->objects[i].fd   = va_desc.objects[i].fd;
1134         drm_desc->objects[i].size = va_desc.objects[i].size;
1135         drm_desc->objects[i].format_modifier =
1136             va_desc.objects[i].drm_format_modifier;
1137     }
1138     drm_desc->nb_layers = va_desc.num_layers;
1139     for (i = 0; i < va_desc.num_layers; i++) {
1140         drm_desc->layers[i].format    = va_desc.layers[i].drm_format;
1141         drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes;
1142         for (j = 0; j < va_desc.layers[i].num_planes; j++) {
1143             drm_desc->layers[i].planes[j].object_index =
1144                 va_desc.layers[i].object_index[j];
1145             drm_desc->layers[i].planes[j].offset =
1146                 va_desc.layers[i].offset[j];
1147             drm_desc->layers[i].planes[j].pitch =
1148                 va_desc.layers[i].pitch[j];
1149         }
1150     }
1151
1152     err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
1153                                 &vaapi_unmap_to_drm_esh, drm_desc);
1154     if (err < 0)
1155         goto fail;
1156
1157     dst->width   = src->width;
1158     dst->height  = src->height;
1159     dst->data[0] = (uint8_t*)drm_desc;
1160
1161     return 0;
1162
1163 fail:
1164     for (i = 0; i < va_desc.num_objects; i++)
1165         close(va_desc.objects[i].fd);
1166     av_freep(&drm_desc);
1167     return err;
1168 }
1169 #endif
1170
1171 #if VA_CHECK_VERSION(0, 36, 0)
1172 typedef struct VAAPIDRMImageBufferMapping {
1173     VAImage      image;
1174     VABufferInfo buffer_info;
1175
1176     AVDRMFrameDescriptor drm_desc;
1177 } VAAPIDRMImageBufferMapping;
1178
1179 static void vaapi_unmap_to_drm_abh(AVHWFramesContext *hwfc,
1180                                   HWMapDescriptor *hwmap)
1181 {
1182     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1183     VAAPIDRMImageBufferMapping *mapping = hwmap->priv;
1184     VASurfaceID surface_id;
1185     VAStatus vas;
1186
1187     surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
1188     av_log(hwfc, AV_LOG_DEBUG, "Unmap VAAPI surface %#x from DRM.\n",
1189            surface_id);
1190
1191     // DRM PRIME file descriptors are closed by vaReleaseBufferHandle(),
1192     // so we shouldn't close them separately.
1193
1194     vas = vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1195     if (vas != VA_STATUS_SUCCESS) {
1196         av_log(hwfc, AV_LOG_ERROR, "Failed to release buffer "
1197                "handle of image %#x (derived from surface %#x): "
1198                "%d (%s).\n", mapping->image.buf, surface_id,
1199                vas, vaErrorStr(vas));
1200     }
1201
1202     vas = vaDestroyImage(hwctx->display, mapping->image.image_id);
1203     if (vas != VA_STATUS_SUCCESS) {
1204         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image "
1205                "derived from surface %#x: %d (%s).\n",
1206                surface_id, vas, vaErrorStr(vas));
1207     }
1208
1209     av_free(mapping);
1210 }
1211
1212 static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst,
1213                                 const AVFrame *src, int flags)
1214 {
1215     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
1216     VAAPIDRMImageBufferMapping *mapping = NULL;
1217     VASurfaceID surface_id;
1218     VAStatus vas;
1219     int err, i, p;
1220
1221     surface_id = (VASurfaceID)(uintptr_t)src->data[3];
1222     av_log(hwfc, AV_LOG_DEBUG, "Map VAAPI surface %#x to DRM.\n",
1223            surface_id);
1224
1225     mapping = av_mallocz(sizeof(*mapping));
1226     if (!mapping)
1227         return AVERROR(ENOMEM);
1228
1229     vas = vaDeriveImage(hwctx->display, surface_id,
1230                         &mapping->image);
1231     if (vas != VA_STATUS_SUCCESS) {
1232         av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
1233                "surface %#x: %d (%s).\n",
1234                surface_id, vas, vaErrorStr(vas));
1235         err = AVERROR(EIO);
1236         goto fail;
1237     }
1238
1239     for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
1240         if (vaapi_drm_format_map[i].va_fourcc ==
1241             mapping->image.format.fourcc)
1242             break;
1243     }
1244     if (i >= FF_ARRAY_ELEMS(vaapi_drm_format_map)) {
1245         av_log(hwfc, AV_LOG_ERROR, "No matching DRM format for "
1246                "VAAPI format %#x.\n", mapping->image.format.fourcc);
1247         err = AVERROR(EINVAL);
1248         goto fail_derived;
1249     }
1250
1251     mapping->buffer_info.mem_type =
1252         VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME;
1253
1254     mapping->drm_desc.nb_layers =
1255         vaapi_drm_format_map[i].nb_layer_formats;
1256     if (mapping->drm_desc.nb_layers > 1) {
1257         if (mapping->drm_desc.nb_layers != mapping->image.num_planes) {
1258             av_log(hwfc, AV_LOG_ERROR, "Image properties do not match "
1259                    "expected format: got %d planes, but expected %d.\n",
1260                    mapping->image.num_planes, mapping->drm_desc.nb_layers);
1261             err = AVERROR(EINVAL);
1262             goto fail_derived;
1263         }
1264
1265         for(p = 0; p < mapping->drm_desc.nb_layers; p++) {
1266             mapping->drm_desc.layers[p] = (AVDRMLayerDescriptor) {
1267                 .format    = vaapi_drm_format_map[i].layer_formats[p],
1268                 .nb_planes = 1,
1269                 .planes[0] = {
1270                     .object_index = 0,
1271                     .offset       = mapping->image.offsets[p],
1272                     .pitch        = mapping->image.pitches[p],
1273                 },
1274             };
1275         }
1276     } else {
1277         mapping->drm_desc.layers[0].format =
1278             vaapi_drm_format_map[i].layer_formats[0];
1279         mapping->drm_desc.layers[0].nb_planes = mapping->image.num_planes;
1280         for (p = 0; p < mapping->image.num_planes; p++) {
1281             mapping->drm_desc.layers[0].planes[p] = (AVDRMPlaneDescriptor) {
1282                 .object_index = 0,
1283                 .offset       = mapping->image.offsets[p],
1284                 .pitch        = mapping->image.pitches[p],
1285             };
1286         }
1287     }
1288
1289     vas = vaAcquireBufferHandle(hwctx->display, mapping->image.buf,
1290                                 &mapping->buffer_info);
1291     if (vas != VA_STATUS_SUCCESS) {
1292         av_log(hwfc, AV_LOG_ERROR, "Failed to get buffer "
1293                "handle from image %#x (derived from surface %#x): "
1294                "%d (%s).\n", mapping->image.buf, surface_id,
1295                vas, vaErrorStr(vas));
1296         err = AVERROR(EIO);
1297         goto fail_derived;
1298     }
1299
1300     av_log(hwfc, AV_LOG_DEBUG, "DRM PRIME fd is %ld.\n",
1301            mapping->buffer_info.handle);
1302
1303     mapping->drm_desc.nb_objects = 1;
1304     mapping->drm_desc.objects[0] = (AVDRMObjectDescriptor) {
1305         .fd   = mapping->buffer_info.handle,
1306         .size = mapping->image.data_size,
1307         // There is no way to get the format modifier with this API.
1308         .format_modifier = DRM_FORMAT_MOD_INVALID,
1309     };
1310
1311     err = ff_hwframe_map_create(src->hw_frames_ctx,
1312                                 dst, src, &vaapi_unmap_to_drm_abh,
1313                                 mapping);
1314     if (err < 0)
1315         goto fail_mapped;
1316
1317     dst->data[0] = (uint8_t*)&mapping->drm_desc;
1318     dst->width   = src->width;
1319     dst->height  = src->height;
1320
1321     return 0;
1322
1323 fail_mapped:
1324     vaReleaseBufferHandle(hwctx->display, mapping->image.buf);
1325 fail_derived:
1326     vaDestroyImage(hwctx->display, mapping->image.image_id);
1327 fail:
1328     av_freep(&mapping);
1329     return err;
1330 }
1331 #endif
1332
1333 static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
1334                             const AVFrame *src, int flags)
1335 {
1336 #if VA_CHECK_VERSION(1, 1, 0)
1337     int err;
1338     err = vaapi_map_to_drm_esh(hwfc, dst, src, flags);
1339     if (err != AVERROR(ENOSYS))
1340         return err;
1341 #endif
1342 #if VA_CHECK_VERSION(0, 36, 0)
1343     return vaapi_map_to_drm_abh(hwfc, dst, src, flags);
1344 #endif
1345     return AVERROR(ENOSYS);
1346 }
1347
1348 #endif /* CONFIG_LIBDRM */
1349
1350 static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
1351                         const AVFrame *src, int flags)
1352 {
1353     switch (src->format) {
1354 #if CONFIG_LIBDRM
1355     case AV_PIX_FMT_DRM_PRIME:
1356         return vaapi_map_from_drm(hwfc, dst, src, flags);
1357 #endif
1358     default:
1359         return AVERROR(ENOSYS);
1360     }
1361 }
1362
1363 static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
1364                           const AVFrame *src, int flags)
1365 {
1366     switch (dst->format) {
1367 #if CONFIG_LIBDRM
1368     case AV_PIX_FMT_DRM_PRIME:
1369         return vaapi_map_to_drm(hwfc, dst, src, flags);
1370 #endif
1371     default:
1372         return vaapi_map_to_memory(hwfc, dst, src, flags);
1373     }
1374 }
1375
1376 static void vaapi_device_free(AVHWDeviceContext *ctx)
1377 {
1378     AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1379     VAAPIDevicePriv      *priv  = ctx->user_opaque;
1380
1381     if (hwctx->display)
1382         vaTerminate(hwctx->display);
1383
1384 #if HAVE_VAAPI_X11
1385     if (priv->x11_display)
1386         XCloseDisplay(priv->x11_display);
1387 #endif
1388
1389     if (priv->drm_fd >= 0)
1390         close(priv->drm_fd);
1391
1392     av_freep(&priv);
1393 }
1394
1395 #if CONFIG_VAAPI_1
1396 static void vaapi_device_log_error(void *context, const char *message)
1397 {
1398     AVHWDeviceContext *ctx = context;
1399
1400     av_log(ctx, AV_LOG_ERROR, "libva: %s", message);
1401 }
1402
1403 static void vaapi_device_log_info(void *context, const char *message)
1404 {
1405     AVHWDeviceContext *ctx = context;
1406
1407     av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message);
1408 }
1409 #endif
1410
1411 static int vaapi_device_connect(AVHWDeviceContext *ctx,
1412                                 VADisplay display)
1413 {
1414     AVVAAPIDeviceContext *hwctx = ctx->hwctx;
1415     int major, minor;
1416     VAStatus vas;
1417
1418 #if CONFIG_VAAPI_1
1419     vaSetErrorCallback(display, &vaapi_device_log_error, ctx);
1420     vaSetInfoCallback (display, &vaapi_device_log_info,  ctx);
1421 #endif
1422
1423     hwctx->display = display;
1424
1425     vas = vaInitialize(display, &major, &minor);
1426     if (vas != VA_STATUS_SUCCESS) {
1427         av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
1428                "connection: %d (%s).\n", vas, vaErrorStr(vas));
1429         return AVERROR(EIO);
1430     }
1431     av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
1432            "version %d.%d\n", major, minor);
1433
1434     return 0;
1435 }
1436
1437 static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
1438                                AVDictionary *opts, int flags)
1439 {
1440     VAAPIDevicePriv *priv;
1441     VADisplay display = NULL;
1442
1443     priv = av_mallocz(sizeof(*priv));
1444     if (!priv)
1445         return AVERROR(ENOMEM);
1446
1447     priv->drm_fd = -1;
1448
1449     ctx->user_opaque = priv;
1450     ctx->free        = vaapi_device_free;
1451
1452 #if HAVE_VAAPI_X11
1453     if (!display && !(device && device[0] == '/')) {
1454         // Try to open the device as an X11 display.
1455         priv->x11_display = XOpenDisplay(device);
1456         if (!priv->x11_display) {
1457             av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
1458                    "%s.\n", XDisplayName(device));
1459         } else {
1460             display = vaGetDisplay(priv->x11_display);
1461             if (!display) {
1462                 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1463                        "from X11 display %s.\n", XDisplayName(device));
1464                 return AVERROR_UNKNOWN;
1465             }
1466
1467             av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1468                    "X11 display %s.\n", XDisplayName(device));
1469         }
1470     }
1471 #endif
1472
1473 #if HAVE_VAAPI_DRM
1474     if (!display) {
1475         // Try to open the device as a DRM path.
1476         // Default to using the first render node if the user did not
1477         // supply a path.
1478         const char *path = device ? device : "/dev/dri/renderD128";
1479         priv->drm_fd = open(path, O_RDWR);
1480         if (priv->drm_fd < 0) {
1481             av_log(ctx, AV_LOG_VERBOSE, "Cannot open DRM device %s.\n",
1482                    path);
1483         } else {
1484             display = vaGetDisplayDRM(priv->drm_fd);
1485             if (!display) {
1486                 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
1487                        "from DRM device %s.\n", path);
1488                 return AVERROR_UNKNOWN;
1489             }
1490
1491             av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
1492                    "DRM device %s.\n", path);
1493         }
1494     }
1495 #endif
1496
1497     if (!display) {
1498         av_log(ctx, AV_LOG_ERROR, "No VA display found for "
1499                "device: %s.\n", device ? device : "");
1500         return AVERROR(EINVAL);
1501     }
1502
1503     return vaapi_device_connect(ctx, display);
1504 }
1505
1506 static int vaapi_device_derive(AVHWDeviceContext *ctx,
1507                                AVHWDeviceContext *src_ctx, int flags)
1508 {
1509 #if HAVE_VAAPI_DRM
1510     if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
1511         AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
1512         VADisplay *display;
1513         VAAPIDevicePriv *priv;
1514
1515         if (src_hwctx->fd < 0) {
1516             av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
1517                    "device to derive a VA display from.\n");
1518             return AVERROR(EINVAL);
1519         }
1520
1521         priv = av_mallocz(sizeof(*priv));
1522         if (!priv)
1523             return AVERROR(ENOMEM);
1524
1525         // Inherits the fd from the source context, which will close it.
1526         priv->drm_fd = -1;
1527
1528         ctx->user_opaque = priv;
1529         ctx->free        = &vaapi_device_free;
1530
1531         display = vaGetDisplayDRM(src_hwctx->fd);
1532         if (!display) {
1533             av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
1534                    "DRM device.\n");
1535             return AVERROR(EIO);
1536         }
1537
1538         return vaapi_device_connect(ctx, display);
1539     }
1540 #endif
1541     return AVERROR(ENOSYS);
1542 }
1543
1544 const HWContextType ff_hwcontext_type_vaapi = {
1545     .type                   = AV_HWDEVICE_TYPE_VAAPI,
1546     .name                   = "VAAPI",
1547
1548     .device_hwctx_size      = sizeof(AVVAAPIDeviceContext),
1549     .device_priv_size       = sizeof(VAAPIDeviceContext),
1550     .device_hwconfig_size   = sizeof(AVVAAPIHWConfig),
1551     .frames_hwctx_size      = sizeof(AVVAAPIFramesContext),
1552     .frames_priv_size       = sizeof(VAAPIFramesContext),
1553
1554     .device_create          = &vaapi_device_create,
1555     .device_derive          = &vaapi_device_derive,
1556     .device_init            = &vaapi_device_init,
1557     .device_uninit          = &vaapi_device_uninit,
1558     .frames_get_constraints = &vaapi_frames_get_constraints,
1559     .frames_init            = &vaapi_frames_init,
1560     .frames_uninit          = &vaapi_frames_uninit,
1561     .frames_get_buffer      = &vaapi_get_buffer,
1562     .transfer_get_formats   = &vaapi_transfer_get_formats,
1563     .transfer_data_to       = &vaapi_transfer_data_to,
1564     .transfer_data_from     = &vaapi_transfer_data_from,
1565     .map_to                 = &vaapi_map_to,
1566     .map_from               = &vaapi_map_from,
1567
1568     .pix_fmts = (const enum AVPixelFormat[]) {
1569         AV_PIX_FMT_VAAPI,
1570         AV_PIX_FMT_NONE
1571     },
1572 };