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