]> git.sesse.net Git - ffmpeg/blob - libavutil/hwcontext_vaapi.c
configure: Simplify and fix avfoundation indev handling
[ffmpeg] / libavutil / hwcontext_vaapi.c
1 /*
2  * This file is part of Libav.
3  *
4  * Libav 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  * Libav 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 Libav; 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 #include <fcntl.h>
29 #if HAVE_UNISTD_H
30 #   include <unistd.h>
31 #endif
32
33
34 #include "avassert.h"
35 #include "buffer.h"
36 #include "common.h"
37 #include "hwcontext.h"
38 #include "hwcontext_internal.h"
39 #include "hwcontext_vaapi.h"
40 #include "mem.h"
41 #include "pixdesc.h"
42 #include "pixfmt.h"
43
44 typedef struct VAAPIDevicePriv {
45 #if HAVE_VAAPI_X11
46     Display *x11_display;
47 #endif
48
49     int drm_fd;
50 } VAAPIDevicePriv;
51
52 typedef struct VAAPISurfaceFormat {
53     enum AVPixelFormat pix_fmt;
54     VAImageFormat image_format;
55 } VAAPISurfaceFormat;
56
57 typedef struct VAAPIDeviceContext {
58     // Surface formats which can be used with this device.
59     VAAPISurfaceFormat *formats;
60     int              nb_formats;
61 } VAAPIDeviceContext;
62
63 typedef struct VAAPIFramesContext {
64     // Surface attributes set at create time.
65     VASurfaceAttrib *attributes;
66     int           nb_attributes;
67     // RT format of the underlying surface (Intel driver ignores this anyway).
68     unsigned int rt_format;
69     // Whether vaDeriveImage works.
70     int derive_works;
71 } VAAPIFramesContext;
72
73 typedef struct VAAPIMapping {
74     // Handle to the derived or copied image which is mapped.
75     VAImage image;
76     // The mapping flags actually used.
77     int flags;
78 } VAAPIMapping;
79
80 #define MAP(va, rt, av) { \
81         VA_FOURCC_ ## va, \
82         VA_RT_FORMAT_ ## rt, \
83         AV_PIX_FMT_ ## av \
84     }
85 // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
86 // plane swap cases.  The frame handling below tries to hide these.
87 static struct {
88     unsigned int fourcc;
89     unsigned int rt_format;
90     enum AVPixelFormat pix_fmt;
91 } vaapi_format_map[] = {
92     MAP(NV12, YUV420,  NV12),
93     MAP(YV12, YUV420,  YUV420P), // With U/V planes swapped.
94     MAP(IYUV, YUV420,  YUV420P),
95   //MAP(I420, YUV420,  YUV420P), // Not in libva but used by Intel driver.
96 #ifdef VA_FOURCC_YV16
97     MAP(YV16, YUV422,  YUV422P), // With U/V planes swapped.
98 #endif
99     MAP(422H, YUV422,  YUV422P),
100     MAP(UYVY, YUV422,  UYVY422),
101     MAP(YUY2, YUV422,  YUYV422),
102     MAP(Y800, YUV400,  GRAY8),
103 #ifdef VA_FOURCC_P010
104     MAP(P010, YUV420_10BPP, P010),
105 #endif
106     MAP(BGRA, RGB32,   BGRA),
107   //MAP(BGRX, RGB32,   BGR0),
108     MAP(RGBA, RGB32,   RGBA),
109   //MAP(RGBX, RGB32,   RGB0),
110     MAP(ABGR, RGB32,   ABGR),
111   //MAP(XBGR, RGB32,   0BGR),
112     MAP(ARGB, RGB32,   ARGB),
113   //MAP(XRGB, RGB32,   0RGB),
114 };
115 #undef MAP
116
117 static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
118 {
119     int i;
120     for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
121         if (vaapi_format_map[i].fourcc == fourcc)
122             return vaapi_format_map[i].pix_fmt;
123     return AV_PIX_FMT_NONE;
124 }
125
126 static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
127                                   enum AVPixelFormat pix_fmt,
128                                   VAImageFormat **image_format)
129 {
130     VAAPIDeviceContext *ctx = hwdev->internal->priv;
131     int i;
132
133     for (i = 0; i < ctx->nb_formats; i++) {
134         if (ctx->formats[i].pix_fmt == pix_fmt) {
135             if (image_format)
136                 *image_format = &ctx->formats[i].image_format;
137             return 0;
138         }
139     }
140     return AVERROR(EINVAL);
141 }
142
143 static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
144                                         const void *hwconfig,
145                                         AVHWFramesConstraints *constraints)
146 {
147     AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
148     const AVVAAPIHWConfig *config = hwconfig;
149     VAAPIDeviceContext *ctx = hwdev->internal->priv;
150     VASurfaceAttrib *attr_list = NULL;
151     VAStatus vas;
152     enum AVPixelFormat pix_fmt;
153     unsigned int fourcc;
154     int err, i, j, attr_count, pix_fmt_count;
155
156     if (config) {
157         attr_count = 0;
158         vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
159                                        0, &attr_count);
160         if (vas != VA_STATUS_SUCCESS) {
161             av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
162                    "%d (%s).\n", vas, vaErrorStr(vas));
163             err = AVERROR(ENOSYS);
164             goto fail;
165         }
166
167         attr_list = av_malloc(attr_count * sizeof(*attr_list));
168         if (!attr_list) {
169             err = AVERROR(ENOMEM);
170             goto fail;
171         }
172
173         vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
174                                        attr_list, &attr_count);
175         if (vas != VA_STATUS_SUCCESS) {
176             av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
177                    "%d (%s).\n", vas, vaErrorStr(vas));
178             err = AVERROR(ENOSYS);
179             goto fail;
180         }
181
182         pix_fmt_count = 0;
183         for (i = 0; i < attr_count; i++) {
184             switch (attr_list[i].type) {
185             case VASurfaceAttribPixelFormat:
186                 fourcc = attr_list[i].value.value.i;
187                 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
188                 if (pix_fmt != AV_PIX_FMT_NONE) {
189                     ++pix_fmt_count;
190                 } else {
191                     // Something unsupported - ignore.
192                 }
193                 break;
194             case VASurfaceAttribMinWidth:
195                 constraints->min_width  = attr_list[i].value.value.i;
196                 break;
197             case VASurfaceAttribMinHeight:
198                 constraints->min_height = attr_list[i].value.value.i;
199                 break;
200             case VASurfaceAttribMaxWidth:
201                 constraints->max_width  = attr_list[i].value.value.i;
202                 break;
203             case VASurfaceAttribMaxHeight:
204                 constraints->max_height = attr_list[i].value.value.i;
205                 break;
206             }
207         }
208         if (pix_fmt_count == 0) {
209             // Nothing usable found.  Presumably there exists something which
210             // works, so leave the set null to indicate unknown.
211             constraints->valid_sw_formats = NULL;
212         } else {
213             constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
214                                                             sizeof(pix_fmt));
215             if (!constraints->valid_sw_formats) {
216                 err = AVERROR(ENOMEM);
217                 goto fail;
218             }
219
220             for (i = j = 0; i < attr_count; i++) {
221                 if (attr_list[i].type != VASurfaceAttribPixelFormat)
222                     continue;
223                 fourcc = attr_list[i].value.value.i;
224                 pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
225                 if (pix_fmt != AV_PIX_FMT_NONE)
226                     constraints->valid_sw_formats[j++] = pix_fmt;
227             }
228             av_assert0(j == pix_fmt_count);
229             constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
230         }
231     } else {
232         // No configuration supplied.
233         // Return the full set of image formats known by the implementation.
234         constraints->valid_sw_formats = av_malloc_array(ctx->nb_formats + 1,
235                                                         sizeof(pix_fmt));
236         if (!constraints->valid_sw_formats) {
237             err = AVERROR(ENOMEM);
238             goto fail;
239         }
240         for (i = 0; i < ctx->nb_formats; i++)
241             constraints->valid_sw_formats[i] = ctx->formats[i].pix_fmt;
242         constraints->valid_sw_formats[i] = AV_PIX_FMT_NONE;
243     }
244
245     constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
246     if (!constraints->valid_hw_formats) {
247         err = AVERROR(ENOMEM);
248         goto fail;
249     }
250     constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
251     constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
252
253     err = 0;
254 fail:
255     av_freep(&attr_list);
256     return err;
257 }
258
259 static const struct {
260     const char *friendly_name;
261     const char *match_string;
262     unsigned int quirks;
263 } vaapi_driver_quirks_table[] = {
264     {
265         "Intel i965 (Quick Sync)",
266         "i965",
267         AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
268     },
269     {
270         "Intel iHD",
271         "ubit",
272         AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
273     },
274 };
275
276 static int vaapi_device_init(AVHWDeviceContext *hwdev)
277 {
278     VAAPIDeviceContext *ctx = hwdev->internal->priv;
279     AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
280     VAImageFormat *image_list = NULL;
281     VAStatus vas;
282     const char *vendor_string;
283     int err, i, image_count;
284     enum AVPixelFormat pix_fmt;
285     unsigned int fourcc;
286
287     image_count = vaMaxNumImageFormats(hwctx->display);
288     if (image_count <= 0) {
289         err = AVERROR(EIO);
290         goto fail;
291     }
292     image_list = av_malloc(image_count * sizeof(*image_list));
293     if (!image_list) {
294         err = AVERROR(ENOMEM);
295         goto fail;
296     }
297     vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
298     if (vas != VA_STATUS_SUCCESS) {
299         err = AVERROR(EIO);
300         goto fail;
301     }
302
303     ctx->formats  = av_malloc(image_count * sizeof(*ctx->formats));
304     if (!ctx->formats) {
305         err = AVERROR(ENOMEM);
306         goto fail;
307     }
308     ctx->nb_formats = 0;
309     for (i = 0; i < image_count; i++) {
310         fourcc  = image_list[i].fourcc;
311         pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
312         if (pix_fmt == AV_PIX_FMT_NONE) {
313             av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
314                    fourcc);
315         } else {
316             av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
317                    fourcc, av_get_pix_fmt_name(pix_fmt));
318             ctx->formats[ctx->nb_formats].pix_fmt      = pix_fmt;
319             ctx->formats[ctx->nb_formats].image_format = image_list[i];
320             ++ctx->nb_formats;
321         }
322     }
323
324     if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
325         av_log(hwdev, AV_LOG_VERBOSE, "Not detecting driver: "
326                "quirks set by user.\n");
327     } else {
328         // Detect the driver in use and set quirk flags if necessary.
329         vendor_string = vaQueryVendorString(hwctx->display);
330         hwctx->driver_quirks = 0;
331         if (vendor_string) {
332             for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
333                 if (strstr(vendor_string,
334                            vaapi_driver_quirks_table[i].match_string)) {
335                     av_log(hwdev, AV_LOG_VERBOSE, "Matched \"%s\" as known "
336                            "driver \"%s\".\n", vendor_string,
337                            vaapi_driver_quirks_table[i].friendly_name);
338                     hwctx->driver_quirks |=
339                         vaapi_driver_quirks_table[i].quirks;
340                     break;
341                 }
342             }
343             if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
344                 av_log(hwdev, AV_LOG_VERBOSE, "Unknown driver \"%s\", "
345                        "assuming standard behaviour.\n", vendor_string);
346             }
347         }
348     }
349
350     av_free(image_list);
351     return 0;
352 fail:
353     av_freep(&ctx->formats);
354     av_free(image_list);
355     return err;
356 }
357
358 static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
359 {
360     VAAPIDeviceContext *ctx = hwdev->internal->priv;
361
362     av_freep(&ctx->formats);
363 }
364
365 static void vaapi_buffer_free(void *opaque, uint8_t *data)
366 {
367     AVHWFramesContext     *hwfc = opaque;
368     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
369     VASurfaceID surface_id;
370     VAStatus vas;
371
372     surface_id = (VASurfaceID)(uintptr_t)data;
373
374     vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
375     if (vas != VA_STATUS_SUCCESS) {
376         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
377                "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
378     }
379 }
380
381 static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
382 {
383     AVHWFramesContext     *hwfc = opaque;
384     VAAPIFramesContext     *ctx = hwfc->internal->priv;
385     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
386     AVVAAPIFramesContext  *avfc = hwfc->hwctx;
387     VASurfaceID surface_id;
388     VAStatus vas;
389     AVBufferRef *ref;
390
391     if (hwfc->initial_pool_size > 0 &&
392         avfc->nb_surfaces >= hwfc->initial_pool_size)
393         return NULL;
394
395     vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
396                            hwfc->width, hwfc->height,
397                            &surface_id, 1,
398                            ctx->attributes, ctx->nb_attributes);
399     if (vas != VA_STATUS_SUCCESS) {
400         av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
401                "%d (%s).\n", vas, vaErrorStr(vas));
402         return NULL;
403     }
404     av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
405
406     ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
407                            sizeof(surface_id), &vaapi_buffer_free,
408                            hwfc, AV_BUFFER_FLAG_READONLY);
409     if (!ref) {
410         vaDestroySurfaces(hwctx->display, &surface_id, 1);
411         return NULL;
412     }
413
414     if (hwfc->initial_pool_size > 0) {
415         // This is a fixed-size pool, so we must still be in the initial
416         // allocation sequence.
417         av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
418         avfc->surface_ids[avfc->nb_surfaces] = surface_id;
419         ++avfc->nb_surfaces;
420     }
421
422     return ref;
423 }
424
425 static int vaapi_frames_init(AVHWFramesContext *hwfc)
426 {
427     AVVAAPIFramesContext  *avfc = hwfc->hwctx;
428     VAAPIFramesContext     *ctx = hwfc->internal->priv;
429     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
430     VAImageFormat *expected_format;
431     AVBufferRef *test_surface = NULL;
432     VASurfaceID test_surface_id;
433     VAImage test_image;
434     VAStatus vas;
435     int err, i;
436     unsigned int fourcc, rt_format;
437
438     for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
439         if (vaapi_format_map[i].pix_fmt == hwfc->sw_format) {
440             fourcc    = vaapi_format_map[i].fourcc;
441             rt_format = vaapi_format_map[i].rt_format;
442             break;
443         }
444     }
445     if (i >= FF_ARRAY_ELEMS(vaapi_format_map)) {
446         av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
447                av_get_pix_fmt_name(hwfc->sw_format));
448         return AVERROR(EINVAL);
449     }
450
451     if (!hwfc->pool) {
452         int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
453         int need_pixel_format = 1;
454         for (i = 0; i < avfc->nb_attributes; i++) {
455             if (ctx->attributes[i].type == VASurfaceAttribMemoryType)
456                 need_memory_type  = 0;
457             if (ctx->attributes[i].type == VASurfaceAttribPixelFormat)
458                 need_pixel_format = 0;
459         }
460         ctx->nb_attributes =
461             avfc->nb_attributes + need_memory_type + need_pixel_format;
462
463         ctx->attributes = av_malloc(ctx->nb_attributes *
464                                         sizeof(*ctx->attributes));
465         if (!ctx->attributes) {
466             err = AVERROR(ENOMEM);
467             goto fail;
468         }
469
470         for (i = 0; i < avfc->nb_attributes; i++)
471             ctx->attributes[i] = avfc->attributes[i];
472         if (need_memory_type) {
473             ctx->attributes[i++] = (VASurfaceAttrib) {
474                 .type          = VASurfaceAttribMemoryType,
475                 .flags         = VA_SURFACE_ATTRIB_SETTABLE,
476                 .value.type    = VAGenericValueTypeInteger,
477                 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
478             };
479         }
480         if (need_pixel_format) {
481             ctx->attributes[i++] = (VASurfaceAttrib) {
482                 .type          = VASurfaceAttribPixelFormat,
483                 .flags         = VA_SURFACE_ATTRIB_SETTABLE,
484                 .value.type    = VAGenericValueTypeInteger,
485                 .value.value.i = fourcc,
486             };
487         }
488         av_assert0(i == ctx->nb_attributes);
489
490         ctx->rt_format = rt_format;
491
492         if (hwfc->initial_pool_size > 0) {
493             // This pool will be usable as a render target, so we need to store
494             // all of the surface IDs somewhere that vaCreateContext() calls
495             // will be able to access them.
496             avfc->nb_surfaces = 0;
497             avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
498                                           sizeof(*avfc->surface_ids));
499             if (!avfc->surface_ids) {
500                 err = AVERROR(ENOMEM);
501                 goto fail;
502             }
503         } else {
504             // This pool allows dynamic sizing, and will not be usable as a
505             // render target.
506             avfc->nb_surfaces = 0;
507             avfc->surface_ids = NULL;
508         }
509
510         hwfc->internal->pool_internal =
511             av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
512                                  &vaapi_pool_alloc, NULL);
513         if (!hwfc->internal->pool_internal) {
514             av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
515             err = AVERROR(ENOMEM);
516             goto fail;
517         }
518     }
519
520     // Allocate a single surface to test whether vaDeriveImage() is going
521     // to work for the specific configuration.
522     if (hwfc->pool) {
523         test_surface = av_buffer_pool_get(hwfc->pool);
524         if (!test_surface) {
525             av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
526                    "user-configured buffer pool.\n");
527             err = AVERROR(ENOMEM);
528             goto fail;
529         }
530     } else {
531         test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
532         if (!test_surface) {
533             av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
534                    "internal buffer pool.\n");
535             err = AVERROR(ENOMEM);
536             goto fail;
537         }
538     }
539     test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
540
541     ctx->derive_works = 0;
542
543     err = vaapi_get_image_format(hwfc->device_ctx,
544                                  hwfc->sw_format, &expected_format);
545     if (err == 0) {
546         vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
547         if (vas == VA_STATUS_SUCCESS) {
548             if (expected_format->fourcc == test_image.format.fourcc) {
549                 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
550                 ctx->derive_works = 1;
551             } else {
552                 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
553                        "derived image format %08x does not match "
554                        "expected format %08x.\n",
555                        expected_format->fourcc, test_image.format.fourcc);
556             }
557             vaDestroyImage(hwctx->display, test_image.image_id);
558         } else {
559             av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
560                    "deriving image does not work: "
561                    "%d (%s).\n", vas, vaErrorStr(vas));
562         }
563     } else {
564         av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
565                "image format is not supported.\n");
566     }
567
568     av_buffer_unref(&test_surface);
569     return 0;
570
571 fail:
572     av_buffer_unref(&test_surface);
573     av_freep(&avfc->surface_ids);
574     av_freep(&ctx->attributes);
575     return err;
576 }
577
578 static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
579 {
580     AVVAAPIFramesContext *avfc = hwfc->hwctx;
581     VAAPIFramesContext    *ctx = hwfc->internal->priv;
582
583     av_freep(&avfc->surface_ids);
584     av_freep(&ctx->attributes);
585 }
586
587 static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
588 {
589     frame->buf[0] = av_buffer_pool_get(hwfc->pool);
590     if (!frame->buf[0])
591         return AVERROR(ENOMEM);
592
593     frame->data[3] = frame->buf[0]->data;
594     frame->format  = AV_PIX_FMT_VAAPI;
595     frame->width   = hwfc->width;
596     frame->height  = hwfc->height;
597
598     return 0;
599 }
600
601 static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
602                                       enum AVHWFrameTransferDirection dir,
603                                       enum AVPixelFormat **formats)
604 {
605     VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
606     enum AVPixelFormat *pix_fmts, preferred_format;
607     int i, k;
608
609     preferred_format = hwfc->sw_format;
610
611     pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
612     if (!pix_fmts)
613         return AVERROR(ENOMEM);
614
615     pix_fmts[0] = preferred_format;
616     k = 1;
617     for (i = 0; i < ctx->nb_formats; i++) {
618         if (ctx->formats[i].pix_fmt == preferred_format)
619             continue;
620         av_assert0(k < ctx->nb_formats);
621         pix_fmts[k++] = ctx->formats[i].pix_fmt;
622     }
623     av_assert0(k == ctx->nb_formats);
624     pix_fmts[k] = AV_PIX_FMT_NONE;
625
626     *formats = pix_fmts;
627     return 0;
628 }
629
630 static void vaapi_unmap_frame(AVHWFramesContext *hwfc,
631                               HWMapDescriptor *hwmap)
632 {
633     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
634     VAAPIMapping           *map = hwmap->priv;
635     VASurfaceID surface_id;
636     VAStatus vas;
637
638     surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3];
639     av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
640
641     vas = vaUnmapBuffer(hwctx->display, map->image.buf);
642     if (vas != VA_STATUS_SUCCESS) {
643         av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
644                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
645     }
646
647     if ((map->flags & AV_HWFRAME_MAP_WRITE) &&
648         !(map->flags & AV_HWFRAME_MAP_DIRECT)) {
649         vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
650                          0, 0, hwfc->width, hwfc->height,
651                          0, 0, hwfc->width, hwfc->height);
652         if (vas != VA_STATUS_SUCCESS) {
653             av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
654                    "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
655         }
656     }
657
658     vas = vaDestroyImage(hwctx->display, map->image.image_id);
659     if (vas != VA_STATUS_SUCCESS) {
660         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
661                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
662     }
663
664     av_free(map);
665 }
666
667 static int vaapi_map_frame(AVHWFramesContext *hwfc,
668                            AVFrame *dst, const AVFrame *src, int flags)
669 {
670     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
671     VAAPIFramesContext *ctx = hwfc->internal->priv;
672     VASurfaceID surface_id;
673     VAImageFormat *image_format;
674     VAAPIMapping *map;
675     VAStatus vas;
676     void *address = NULL;
677     int err, i;
678
679     surface_id = (VASurfaceID)(uintptr_t)src->data[3];
680     av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
681
682     if (!ctx->derive_works && (flags & AV_HWFRAME_MAP_DIRECT)) {
683         // Requested direct mapping but it is not possible.
684         return AVERROR(EINVAL);
685     }
686     if (dst->format == AV_PIX_FMT_NONE)
687         dst->format = hwfc->sw_format;
688     if (dst->format != hwfc->sw_format && (flags & AV_HWFRAME_MAP_DIRECT)) {
689         // Requested direct mapping but the formats do not match.
690         return AVERROR(EINVAL);
691     }
692
693     err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
694     if (err < 0) {
695         // Requested format is not a valid output format.
696         return AVERROR(EINVAL);
697     }
698
699     map = av_malloc(sizeof(*map));
700     if (!map)
701         return AVERROR(ENOMEM);
702     map->flags = flags;
703     map->image.image_id = VA_INVALID_ID;
704
705     vas = vaSyncSurface(hwctx->display, surface_id);
706     if (vas != VA_STATUS_SUCCESS) {
707         av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
708                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
709         err = AVERROR(EIO);
710         goto fail;
711     }
712
713     // The memory which we map using derive need not be connected to the CPU
714     // in a way conducive to fast access.  On Gen7-Gen9 Intel graphics, the
715     // memory is mappable but not cached, so normal memcpy()-like access is
716     // very slow to read it (but writing is ok).  It is possible to read much
717     // faster with a copy routine which is aware of the limitation, but we
718     // assume for now that the user is not aware of that and would therefore
719     // prefer not to be given direct-mapped memory if they request read access.
720     if (ctx->derive_works && dst->format == hwfc->sw_format &&
721         ((flags & AV_HWFRAME_MAP_DIRECT) || !(flags & AV_HWFRAME_MAP_READ))) {
722         vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
723         if (vas != VA_STATUS_SUCCESS) {
724             av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
725                    "surface %#x: %d (%s).\n",
726                    surface_id, vas, vaErrorStr(vas));
727             err = AVERROR(EIO);
728             goto fail;
729         }
730         if (map->image.format.fourcc != image_format->fourcc) {
731             av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
732                    "is in wrong format: expected %#08x, got %#08x.\n",
733                    surface_id, image_format->fourcc, map->image.format.fourcc);
734             err = AVERROR(EIO);
735             goto fail;
736         }
737         map->flags |= AV_HWFRAME_MAP_DIRECT;
738     } else {
739         vas = vaCreateImage(hwctx->display, image_format,
740                             hwfc->width, hwfc->height, &map->image);
741         if (vas != VA_STATUS_SUCCESS) {
742             av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
743                    "surface %#x: %d (%s).\n",
744                    surface_id, vas, vaErrorStr(vas));
745             err = AVERROR(EIO);
746             goto fail;
747         }
748         if (!(flags & AV_HWFRAME_MAP_OVERWRITE)) {
749             vas = vaGetImage(hwctx->display, surface_id, 0, 0,
750                              hwfc->width, hwfc->height, map->image.image_id);
751             if (vas != VA_STATUS_SUCCESS) {
752                 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
753                        "surface %#x: %d (%s).\n",
754                        surface_id, vas, vaErrorStr(vas));
755                 err = AVERROR(EIO);
756                 goto fail;
757             }
758         }
759     }
760
761     vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
762     if (vas != VA_STATUS_SUCCESS) {
763         av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
764                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
765         err = AVERROR(EIO);
766         goto fail;
767     }
768
769     err = ff_hwframe_map_create(src->hw_frames_ctx,
770                                 dst, src, &vaapi_unmap_frame, map);
771     if (err < 0)
772         goto fail;
773
774     dst->width  = src->width;
775     dst->height = src->height;
776
777     for (i = 0; i < map->image.num_planes; i++) {
778         dst->data[i] = (uint8_t*)address + map->image.offsets[i];
779         dst->linesize[i] = map->image.pitches[i];
780     }
781     if (
782 #ifdef VA_FOURCC_YV16
783         map->image.format.fourcc == VA_FOURCC_YV16 ||
784 #endif
785         map->image.format.fourcc == VA_FOURCC_YV12) {
786         // Chroma planes are YVU rather than YUV, so swap them.
787         FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
788     }
789
790     return 0;
791
792 fail:
793     if (map) {
794         if (address)
795             vaUnmapBuffer(hwctx->display, map->image.buf);
796         if (map->image.image_id != VA_INVALID_ID)
797             vaDestroyImage(hwctx->display, map->image.image_id);
798         av_free(map);
799     }
800     return err;
801 }
802
803 static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
804                                     AVFrame *dst, const AVFrame *src)
805 {
806     AVFrame *map;
807     int err;
808
809     if (dst->width > hwfc->width || dst->height > hwfc->height)
810         return AVERROR(EINVAL);
811
812     map = av_frame_alloc();
813     if (!map)
814         return AVERROR(ENOMEM);
815     map->format = dst->format;
816
817     err = vaapi_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
818     if (err)
819         goto fail;
820
821     map->width  = dst->width;
822     map->height = dst->height;
823
824     err = av_frame_copy(dst, map);
825     if (err)
826         goto fail;
827
828     err = 0;
829 fail:
830     av_frame_free(&map);
831     return err;
832 }
833
834 static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
835                                   AVFrame *dst, const AVFrame *src)
836 {
837     AVFrame *map;
838     int err;
839
840     if (src->width > hwfc->width || src->height > hwfc->height)
841         return AVERROR(EINVAL);
842
843     map = av_frame_alloc();
844     if (!map)
845         return AVERROR(ENOMEM);
846     map->format = src->format;
847
848     err = vaapi_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
849     if (err)
850         goto fail;
851
852     map->width  = src->width;
853     map->height = src->height;
854
855     err = av_frame_copy(map, src);
856     if (err)
857         goto fail;
858
859     err = 0;
860 fail:
861     av_frame_free(&map);
862     return err;
863 }
864
865 static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
866                           const AVFrame *src, int flags)
867 {
868     int err;
869
870     if (dst->format != AV_PIX_FMT_NONE) {
871         err = vaapi_get_image_format(hwfc->device_ctx, dst->format, NULL);
872         if (err < 0)
873             return AVERROR(ENOSYS);
874     }
875
876     err = vaapi_map_frame(hwfc, dst, src, flags);
877     if (err)
878         return err;
879
880     err = av_frame_copy_props(dst, src);
881     if (err)
882         return err;
883
884     return 0;
885 }
886
887 static void vaapi_device_free(AVHWDeviceContext *ctx)
888 {
889     AVVAAPIDeviceContext *hwctx = ctx->hwctx;
890     VAAPIDevicePriv      *priv  = ctx->user_opaque;
891
892     if (hwctx->display)
893         vaTerminate(hwctx->display);
894
895 #if HAVE_VAAPI_X11
896     if (priv->x11_display)
897         XCloseDisplay(priv->x11_display);
898 #endif
899
900     if (priv->drm_fd >= 0)
901         close(priv->drm_fd);
902
903     av_freep(&priv);
904 }
905
906 static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
907                                AVDictionary *opts, int flags)
908 {
909     AVVAAPIDeviceContext *hwctx = ctx->hwctx;
910     VAAPIDevicePriv *priv;
911     VADisplay display = 0;
912     VAStatus vas;
913     int major, minor;
914
915     priv = av_mallocz(sizeof(*priv));
916     if (!priv)
917         return AVERROR(ENOMEM);
918
919     priv->drm_fd = -1;
920
921     ctx->user_opaque = priv;
922     ctx->free        = vaapi_device_free;
923
924 #if HAVE_VAAPI_X11
925     if (!display && !(device && device[0] == '/')) {
926         // Try to open the device as an X11 display.
927         priv->x11_display = XOpenDisplay(device);
928         if (!priv->x11_display) {
929             av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
930                    "%s.\n", XDisplayName(device));
931         } else {
932             display = vaGetDisplay(priv->x11_display);
933             if (!display) {
934                 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
935                        "from X11 display %s.\n", XDisplayName(device));
936                 return AVERROR_UNKNOWN;
937             }
938
939             av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
940                    "X11 display %s.\n", XDisplayName(device));
941         }
942     }
943 #endif
944
945 #if HAVE_VAAPI_DRM
946     if (!display) {
947         // Try to open the device as a DRM path.
948         // Default to using the first render node if the user did not
949         // supply a path.
950         const char *path = device ? device : "/dev/dri/renderD128";
951         priv->drm_fd = open(path, O_RDWR);
952         if (priv->drm_fd < 0) {
953             av_log(ctx, AV_LOG_VERBOSE, "Cannot open DRM device %s.\n",
954                    path);
955         } else {
956             display = vaGetDisplayDRM(priv->drm_fd);
957             if (!display) {
958                 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
959                        "from DRM device %s.\n", path);
960                 return AVERROR_UNKNOWN;
961             }
962
963             av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
964                    "DRM device %s.\n", path);
965         }
966     }
967 #endif
968
969     if (!display) {
970         av_log(ctx, AV_LOG_ERROR, "No VA display found for "
971                "device: %s.\n", device ? device : "");
972         return AVERROR(EINVAL);
973     }
974
975     hwctx->display = display;
976
977     vas = vaInitialize(display, &major, &minor);
978     if (vas != VA_STATUS_SUCCESS) {
979         av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
980                "connection: %d (%s).\n", vas, vaErrorStr(vas));
981         return AVERROR(EIO);
982     }
983     av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
984            "version %d.%d\n", major, minor);
985
986     return 0;
987 }
988
989 const HWContextType ff_hwcontext_type_vaapi = {
990     .type                   = AV_HWDEVICE_TYPE_VAAPI,
991     .name                   = "VAAPI",
992
993     .device_hwctx_size      = sizeof(AVVAAPIDeviceContext),
994     .device_priv_size       = sizeof(VAAPIDeviceContext),
995     .device_hwconfig_size   = sizeof(AVVAAPIHWConfig),
996     .frames_hwctx_size      = sizeof(AVVAAPIFramesContext),
997     .frames_priv_size       = sizeof(VAAPIFramesContext),
998
999     .device_create          = &vaapi_device_create,
1000     .device_init            = &vaapi_device_init,
1001     .device_uninit          = &vaapi_device_uninit,
1002     .frames_get_constraints = &vaapi_frames_get_constraints,
1003     .frames_init            = &vaapi_frames_init,
1004     .frames_uninit          = &vaapi_frames_uninit,
1005     .frames_get_buffer      = &vaapi_get_buffer,
1006     .transfer_get_formats   = &vaapi_transfer_get_formats,
1007     .transfer_data_to       = &vaapi_transfer_data_to,
1008     .transfer_data_from     = &vaapi_transfer_data_from,
1009     .map_to                 = NULL,
1010     .map_from               = &vaapi_map_from,
1011
1012     .pix_fmts = (const enum AVPixelFormat[]) {
1013         AV_PIX_FMT_VAAPI,
1014         AV_PIX_FMT_NONE
1015     },
1016 };