0051acb4f6b33a4e40c67beeff28ce21c2fdf2e8
[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         "Intel iHD",
280         "ubit",
281         AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE,
282     },
283 };
284
285 static int vaapi_device_init(AVHWDeviceContext *hwdev)
286 {
287     VAAPIDeviceContext *ctx = hwdev->internal->priv;
288     AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
289     VAImageFormat *image_list = NULL;
290     VAStatus vas;
291     const char *vendor_string;
292     int err, i, image_count;
293     enum AVPixelFormat pix_fmt;
294     unsigned int fourcc;
295
296     image_count = vaMaxNumImageFormats(hwctx->display);
297     if (image_count <= 0) {
298         err = AVERROR(EIO);
299         goto fail;
300     }
301     image_list = av_malloc(image_count * sizeof(*image_list));
302     if (!image_list) {
303         err = AVERROR(ENOMEM);
304         goto fail;
305     }
306     vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
307     if (vas != VA_STATUS_SUCCESS) {
308         err = AVERROR(EIO);
309         goto fail;
310     }
311
312     ctx->formats  = av_malloc(image_count * sizeof(*ctx->formats));
313     if (!ctx->formats) {
314         err = AVERROR(ENOMEM);
315         goto fail;
316     }
317     ctx->nb_formats = 0;
318     for (i = 0; i < image_count; i++) {
319         fourcc  = image_list[i].fourcc;
320         pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
321         if (pix_fmt == AV_PIX_FMT_NONE) {
322             av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n",
323                    fourcc);
324         } else {
325             av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
326                    fourcc, av_get_pix_fmt_name(pix_fmt));
327             ctx->formats[ctx->nb_formats].pix_fmt      = pix_fmt;
328             ctx->formats[ctx->nb_formats].image_format = image_list[i];
329             ++ctx->nb_formats;
330         }
331     }
332
333     if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
334         av_log(hwdev, AV_LOG_VERBOSE, "Not detecting driver: "
335                "quirks set by user.\n");
336     } else {
337         // Detect the driver in use and set quirk flags if necessary.
338         vendor_string = vaQueryVendorString(hwctx->display);
339         hwctx->driver_quirks = 0;
340         if (vendor_string) {
341             for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
342                 if (strstr(vendor_string,
343                            vaapi_driver_quirks_table[i].match_string)) {
344                     av_log(hwdev, AV_LOG_VERBOSE, "Matched \"%s\" as known "
345                            "driver \"%s\".\n", vendor_string,
346                            vaapi_driver_quirks_table[i].friendly_name);
347                     hwctx->driver_quirks |=
348                         vaapi_driver_quirks_table[i].quirks;
349                     break;
350                 }
351             }
352             if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
353                 av_log(hwdev, AV_LOG_VERBOSE, "Unknown driver \"%s\", "
354                        "assuming standard behaviour.\n", vendor_string);
355             }
356         }
357     }
358
359     av_free(image_list);
360     return 0;
361 fail:
362     av_freep(&ctx->formats);
363     av_free(image_list);
364     return err;
365 }
366
367 static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
368 {
369     VAAPIDeviceContext *ctx = hwdev->internal->priv;
370
371     av_freep(&ctx->formats);
372 }
373
374 static void vaapi_buffer_free(void *opaque, uint8_t *data)
375 {
376     AVHWFramesContext     *hwfc = opaque;
377     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
378     VASurfaceID surface_id;
379     VAStatus vas;
380
381     surface_id = (VASurfaceID)(uintptr_t)data;
382
383     vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
384     if (vas != VA_STATUS_SUCCESS) {
385         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
386                "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
387     }
388 }
389
390 static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
391 {
392     AVHWFramesContext     *hwfc = opaque;
393     VAAPIFramesContext     *ctx = hwfc->internal->priv;
394     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
395     AVVAAPIFramesContext  *avfc = hwfc->hwctx;
396     VASurfaceID surface_id;
397     VAStatus vas;
398     AVBufferRef *ref;
399
400     vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
401                            hwfc->width, hwfc->height,
402                            &surface_id, 1,
403                            ctx->attributes, ctx->nb_attributes);
404     if (vas != VA_STATUS_SUCCESS) {
405         av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
406                "%d (%s).\n", vas, vaErrorStr(vas));
407         return NULL;
408     }
409     av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
410
411     ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
412                            sizeof(surface_id), &vaapi_buffer_free,
413                            hwfc, AV_BUFFER_FLAG_READONLY);
414     if (!ref) {
415         vaDestroySurfaces(hwctx->display, &surface_id, 1);
416         return NULL;
417     }
418
419     if (hwfc->initial_pool_size > 0) {
420         // This is a fixed-size pool, so we must still be in the initial
421         // allocation sequence.
422         av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
423         avfc->surface_ids[avfc->nb_surfaces] = surface_id;
424         ++avfc->nb_surfaces;
425     }
426
427     return ref;
428 }
429
430 static int vaapi_frames_init(AVHWFramesContext *hwfc)
431 {
432     AVVAAPIFramesContext  *avfc = hwfc->hwctx;
433     VAAPIFramesContext     *ctx = hwfc->internal->priv;
434     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
435     VAImageFormat *expected_format;
436     AVBufferRef *test_surface = NULL;
437     VASurfaceID test_surface_id;
438     VAImage test_image;
439     VAStatus vas;
440     int err, i;
441     unsigned int fourcc, rt_format;
442
443     for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
444         if (vaapi_format_map[i].pix_fmt == hwfc->sw_format) {
445             fourcc    = vaapi_format_map[i].fourcc;
446             rt_format = vaapi_format_map[i].rt_format;
447             break;
448         }
449     }
450     if (i >= FF_ARRAY_ELEMS(vaapi_format_map)) {
451         av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
452                av_get_pix_fmt_name(hwfc->sw_format));
453         return AVERROR(EINVAL);
454     }
455
456     if (!hwfc->pool) {
457         int need_memory_type = !(hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_ATTRIB_MEMTYPE);
458         int need_pixel_format = 1;
459         for (i = 0; i < avfc->nb_attributes; i++) {
460             if (ctx->attributes[i].type == VASurfaceAttribMemoryType)
461                 need_memory_type  = 0;
462             if (ctx->attributes[i].type == VASurfaceAttribPixelFormat)
463                 need_pixel_format = 0;
464         }
465         ctx->nb_attributes =
466             avfc->nb_attributes + need_memory_type + need_pixel_format;
467
468         ctx->attributes = av_malloc(ctx->nb_attributes *
469                                         sizeof(*ctx->attributes));
470         if (!ctx->attributes) {
471             err = AVERROR(ENOMEM);
472             goto fail;
473         }
474
475         for (i = 0; i < avfc->nb_attributes; i++)
476             ctx->attributes[i] = avfc->attributes[i];
477         if (need_memory_type) {
478             ctx->attributes[i++] = (VASurfaceAttrib) {
479                 .type          = VASurfaceAttribMemoryType,
480                 .flags         = VA_SURFACE_ATTRIB_SETTABLE,
481                 .value.type    = VAGenericValueTypeInteger,
482                 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
483             };
484         }
485         if (need_pixel_format) {
486             ctx->attributes[i++] = (VASurfaceAttrib) {
487                 .type          = VASurfaceAttribPixelFormat,
488                 .flags         = VA_SURFACE_ATTRIB_SETTABLE,
489                 .value.type    = VAGenericValueTypeInteger,
490                 .value.value.i = fourcc,
491             };
492         }
493         av_assert0(i == ctx->nb_attributes);
494
495         ctx->rt_format = rt_format;
496
497         if (hwfc->initial_pool_size > 0) {
498             // This pool will be usable as a render target, so we need to store
499             // all of the surface IDs somewhere that vaCreateContext() calls
500             // will be able to access them.
501             avfc->nb_surfaces = 0;
502             avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
503                                           sizeof(*avfc->surface_ids));
504             if (!avfc->surface_ids) {
505                 err = AVERROR(ENOMEM);
506                 goto fail;
507             }
508         } else {
509             // This pool allows dynamic sizing, and will not be usable as a
510             // render target.
511             avfc->nb_surfaces = 0;
512             avfc->surface_ids = NULL;
513         }
514
515         hwfc->internal->pool_internal =
516             av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
517                                  &vaapi_pool_alloc, NULL);
518         if (!hwfc->internal->pool_internal) {
519             av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
520             err = AVERROR(ENOMEM);
521             goto fail;
522         }
523     }
524
525     // Allocate a single surface to test whether vaDeriveImage() is going
526     // to work for the specific configuration.
527     if (hwfc->pool) {
528         test_surface = av_buffer_pool_get(hwfc->pool);
529         if (!test_surface) {
530             av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
531                    "user-configured buffer pool.\n");
532             err = AVERROR(ENOMEM);
533             goto fail;
534         }
535     } else {
536         test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
537         if (!test_surface) {
538             av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
539                    "internal buffer pool.\n");
540             err = AVERROR(ENOMEM);
541             goto fail;
542         }
543     }
544     test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
545
546     ctx->derive_works = 0;
547
548     err = vaapi_get_image_format(hwfc->device_ctx,
549                                  hwfc->sw_format, &expected_format);
550     if (err == 0) {
551         vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
552         if (vas == VA_STATUS_SUCCESS) {
553             if (expected_format->fourcc == test_image.format.fourcc) {
554                 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
555                 ctx->derive_works = 1;
556             } else {
557                 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
558                        "derived image format %08x does not match "
559                        "expected format %08x.\n",
560                        expected_format->fourcc, test_image.format.fourcc);
561             }
562             vaDestroyImage(hwctx->display, test_image.image_id);
563         } else {
564             av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
565                    "deriving image does not work: "
566                    "%d (%s).\n", vas, vaErrorStr(vas));
567         }
568     } else {
569         av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
570                "image format is not supported.\n");
571     }
572
573     av_buffer_unref(&test_surface);
574     return 0;
575
576 fail:
577     av_buffer_unref(&test_surface);
578     av_freep(&avfc->surface_ids);
579     av_freep(&ctx->attributes);
580     return err;
581 }
582
583 static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
584 {
585     AVVAAPIFramesContext *avfc = hwfc->hwctx;
586     VAAPIFramesContext    *ctx = hwfc->internal->priv;
587
588     av_freep(&avfc->surface_ids);
589     av_freep(&ctx->attributes);
590 }
591
592 static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
593 {
594     frame->buf[0] = av_buffer_pool_get(hwfc->pool);
595     if (!frame->buf[0])
596         return AVERROR(ENOMEM);
597
598     frame->data[3] = frame->buf[0]->data;
599     frame->format  = AV_PIX_FMT_VAAPI;
600     frame->width   = hwfc->width;
601     frame->height  = hwfc->height;
602
603     return 0;
604 }
605
606 static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
607                                       enum AVHWFrameTransferDirection dir,
608                                       enum AVPixelFormat **formats)
609 {
610     VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
611     enum AVPixelFormat *pix_fmts, preferred_format;
612     int i, k;
613
614     preferred_format = hwfc->sw_format;
615
616     pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
617     if (!pix_fmts)
618         return AVERROR(ENOMEM);
619
620     pix_fmts[0] = preferred_format;
621     k = 1;
622     for (i = 0; i < ctx->nb_formats; i++) {
623         if (ctx->formats[i].pix_fmt == preferred_format)
624             continue;
625         av_assert0(k < ctx->nb_formats);
626         pix_fmts[k++] = ctx->formats[i].pix_fmt;
627     }
628     av_assert0(k == ctx->nb_formats);
629     pix_fmts[k] = AV_PIX_FMT_NONE;
630
631     *formats = pix_fmts;
632     return 0;
633 }
634
635 static void vaapi_unmap_frame(void *opaque, uint8_t *data)
636 {
637     AVHWFramesContext *hwfc = opaque;
638     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
639     VAAPISurfaceMap *map = (VAAPISurfaceMap*)data;
640     const AVFrame *src;
641     VASurfaceID surface_id;
642     VAStatus vas;
643
644     src = map->source;
645     surface_id = (VASurfaceID)(uintptr_t)src->data[3];
646     av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
647
648     vas = vaUnmapBuffer(hwctx->display, map->image.buf);
649     if (vas != VA_STATUS_SUCCESS) {
650         av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
651                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
652     }
653
654     if ((map->flags & VAAPI_MAP_WRITE) &&
655         !(map->flags & VAAPI_MAP_DIRECT)) {
656         vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
657                          0, 0, hwfc->width, hwfc->height,
658                          0, 0, hwfc->width, hwfc->height);
659         if (vas != VA_STATUS_SUCCESS) {
660             av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
661                    "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
662         }
663     }
664
665     vas = vaDestroyImage(hwctx->display, map->image.image_id);
666     if (vas != VA_STATUS_SUCCESS) {
667         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
668                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
669     }
670
671     av_free(map);
672 }
673
674 static int vaapi_map_frame(AVHWFramesContext *hwfc,
675                            AVFrame *dst, const AVFrame *src, int flags)
676 {
677     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
678     VAAPIFramesContext *ctx = hwfc->internal->priv;
679     VASurfaceID surface_id;
680     VAImageFormat *image_format;
681     VAAPISurfaceMap *map;
682     VAStatus vas;
683     void *address = NULL;
684     int err, i;
685
686     surface_id = (VASurfaceID)(uintptr_t)src->data[3];
687     av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
688
689     if (!ctx->derive_works && (flags & VAAPI_MAP_DIRECT)) {
690         // Requested direct mapping but it is not possible.
691         return AVERROR(EINVAL);
692     }
693     if (dst->format == AV_PIX_FMT_NONE)
694         dst->format = hwfc->sw_format;
695     if (dst->format != hwfc->sw_format && (flags & VAAPI_MAP_DIRECT)) {
696         // Requested direct mapping but the formats do not match.
697         return AVERROR(EINVAL);
698     }
699
700     err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
701     if (err < 0) {
702         // Requested format is not a valid output format.
703         return AVERROR(EINVAL);
704     }
705
706     map = av_malloc(sizeof(VAAPISurfaceMap));
707     if (!map)
708         return AVERROR(ENOMEM);
709
710     map->source         = src;
711     map->flags          = flags;
712     map->image.image_id = VA_INVALID_ID;
713
714     vas = vaSyncSurface(hwctx->display, surface_id);
715     if (vas != VA_STATUS_SUCCESS) {
716         av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
717                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
718         err = AVERROR(EIO);
719         goto fail;
720     }
721
722     // The memory which we map using derive need not be connected to the CPU
723     // in a way conducive to fast access.  On Gen7-Gen9 Intel graphics, the
724     // memory is mappable but not cached, so normal memcpy()-like access is
725     // very slow to read it (but writing is ok).  It is possible to read much
726     // faster with a copy routine which is aware of the limitation, but we
727     // assume for now that the user is not aware of that and would therefore
728     // prefer not to be given direct-mapped memory if they request read access.
729     if (ctx->derive_works &&
730         ((flags & VAAPI_MAP_DIRECT) || !(flags & VAAPI_MAP_READ))) {
731         vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
732         if (vas != VA_STATUS_SUCCESS) {
733             av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
734                    "surface %#x: %d (%s).\n",
735                    surface_id, vas, vaErrorStr(vas));
736             err = AVERROR(EIO);
737             goto fail;
738         }
739         if (map->image.format.fourcc != image_format->fourcc) {
740             av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
741                    "is in wrong format: expected %#08x, got %#08x.\n",
742                    surface_id, image_format->fourcc, map->image.format.fourcc);
743             err = AVERROR(EIO);
744             goto fail;
745         }
746         map->flags |= VAAPI_MAP_DIRECT;
747     } else {
748         vas = vaCreateImage(hwctx->display, image_format,
749                             hwfc->width, hwfc->height, &map->image);
750         if (vas != VA_STATUS_SUCCESS) {
751             av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
752                    "surface %#x: %d (%s).\n",
753                    surface_id, vas, vaErrorStr(vas));
754             err = AVERROR(EIO);
755             goto fail;
756         }
757         if (flags & VAAPI_MAP_READ) {
758             vas = vaGetImage(hwctx->display, surface_id, 0, 0,
759                              hwfc->width, hwfc->height, map->image.image_id);
760             if (vas != VA_STATUS_SUCCESS) {
761                 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
762                        "surface %#x: %d (%s).\n",
763                        surface_id, vas, vaErrorStr(vas));
764                 err = AVERROR(EIO);
765                 goto fail;
766             }
767         }
768     }
769
770     vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
771     if (vas != VA_STATUS_SUCCESS) {
772         av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
773                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
774         err = AVERROR(EIO);
775         goto fail;
776     }
777
778     dst->width  = src->width;
779     dst->height = src->height;
780
781     for (i = 0; i < map->image.num_planes; i++) {
782         dst->data[i] = (uint8_t*)address + map->image.offsets[i];
783         dst->linesize[i] = map->image.pitches[i];
784     }
785     if (
786 #ifdef VA_FOURCC_YV16
787         map->image.format.fourcc == VA_FOURCC_YV16 ||
788 #endif
789         map->image.format.fourcc == VA_FOURCC_YV12) {
790         // Chroma planes are YVU rather than YUV, so swap them.
791         FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
792     }
793
794     dst->buf[0] = av_buffer_create((uint8_t*)map, sizeof(*map),
795                                    &vaapi_unmap_frame, hwfc, 0);
796     if (!dst->buf[0]) {
797         err = AVERROR(ENOMEM);
798         goto fail;
799     }
800
801     return 0;
802
803 fail:
804     if (map) {
805         if (address)
806             vaUnmapBuffer(hwctx->display, map->image.buf);
807         if (map->image.image_id != VA_INVALID_ID)
808             vaDestroyImage(hwctx->display, map->image.image_id);
809         av_free(map);
810     }
811     return err;
812 }
813
814 static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
815                                     AVFrame *dst, const AVFrame *src)
816 {
817     AVFrame *map;
818     int err;
819
820     if (dst->width > hwfc->width || dst->height > hwfc->height)
821         return AVERROR(EINVAL);
822
823     map = av_frame_alloc();
824     if (!map)
825         return AVERROR(ENOMEM);
826     map->format = dst->format;
827
828     err = vaapi_map_frame(hwfc, map, src, VAAPI_MAP_READ);
829     if (err)
830         goto fail;
831
832     map->width  = dst->width;
833     map->height = dst->height;
834
835     err = av_frame_copy(dst, map);
836     if (err)
837         goto fail;
838
839     err = 0;
840 fail:
841     av_frame_free(&map);
842     return err;
843 }
844
845 static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
846                                   AVFrame *dst, const AVFrame *src)
847 {
848     AVFrame *map;
849     int err;
850
851     if (src->width > hwfc->width || src->height > hwfc->height)
852         return AVERROR(EINVAL);
853
854     map = av_frame_alloc();
855     if (!map)
856         return AVERROR(ENOMEM);
857     map->format = src->format;
858
859     err = vaapi_map_frame(hwfc, map, dst, VAAPI_MAP_WRITE);
860     if (err)
861         goto fail;
862
863     map->width  = src->width;
864     map->height = src->height;
865
866     err = av_frame_copy(map, src);
867     if (err)
868         goto fail;
869
870     err = 0;
871 fail:
872     av_frame_free(&map);
873     return err;
874 }
875
876 static void vaapi_device_free(AVHWDeviceContext *ctx)
877 {
878     AVVAAPIDeviceContext *hwctx = ctx->hwctx;
879     VAAPIDevicePriv      *priv  = ctx->user_opaque;
880
881     if (hwctx->display)
882         vaTerminate(hwctx->display);
883
884 #if HAVE_VAAPI_X11
885     if (priv->x11_display)
886         XCloseDisplay(priv->x11_display);
887 #endif
888
889     if (priv->drm_fd >= 0)
890         close(priv->drm_fd);
891
892     av_freep(&priv);
893 }
894
895 static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
896                                AVDictionary *opts, int flags)
897 {
898     AVVAAPIDeviceContext *hwctx = ctx->hwctx;
899     VAAPIDevicePriv *priv;
900     VADisplay display = 0;
901     VAStatus vas;
902     int major, minor;
903
904     priv = av_mallocz(sizeof(*priv));
905     if (!priv)
906         return AVERROR(ENOMEM);
907
908     priv->drm_fd = -1;
909
910     ctx->user_opaque = priv;
911     ctx->free        = vaapi_device_free;
912
913 #if HAVE_VAAPI_X11
914     if (!display && !(device && device[0] == '/')) {
915         // Try to open the device as an X11 display.
916         priv->x11_display = XOpenDisplay(device);
917         if (!priv->x11_display) {
918             av_log(ctx, AV_LOG_VERBOSE, "Cannot open X11 display "
919                    "%s.\n", XDisplayName(device));
920         } else {
921             display = vaGetDisplay(priv->x11_display);
922             if (!display) {
923                 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
924                        "from X11 display %s.\n", XDisplayName(device));
925                 return AVERROR_UNKNOWN;
926             }
927
928             av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
929                    "X11 display %s.\n", XDisplayName(device));
930         }
931     }
932 #endif
933
934 #if HAVE_VAAPI_DRM
935     if (!display) {
936         // Try to open the device as a DRM path.
937         // Default to using the first render node if the user did not
938         // supply a path.
939         const char *path = device ? device : "/dev/dri/renderD128";
940         priv->drm_fd = open(path, O_RDWR);
941         if (priv->drm_fd < 0) {
942             av_log(ctx, AV_LOG_VERBOSE, "Cannot open DRM device %s.\n",
943                    path);
944         } else {
945             display = vaGetDisplayDRM(priv->drm_fd);
946             if (!display) {
947                 av_log(ctx, AV_LOG_ERROR, "Cannot open a VA display "
948                        "from DRM device %s.\n", path);
949                 return AVERROR_UNKNOWN;
950             }
951
952             av_log(ctx, AV_LOG_VERBOSE, "Opened VA display via "
953                    "DRM device %s.\n", path);
954         }
955     }
956 #endif
957
958     if (!display) {
959         av_log(ctx, AV_LOG_ERROR, "No VA display found for "
960                "device: %s.\n", device ? device : "");
961         return AVERROR(EINVAL);
962     }
963
964     vas = vaInitialize(display, &major, &minor);
965     if (vas != VA_STATUS_SUCCESS) {
966         av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
967                "connection: %d (%s).\n", vas, vaErrorStr(vas));
968         return AVERROR(EIO);
969     }
970     hwctx->display = display;
971     av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
972            "version %d.%d\n", major, minor);
973
974     return 0;
975 }
976
977 const HWContextType ff_hwcontext_type_vaapi = {
978     .type                   = AV_HWDEVICE_TYPE_VAAPI,
979     .name                   = "VAAPI",
980
981     .device_hwctx_size      = sizeof(AVVAAPIDeviceContext),
982     .device_priv_size       = sizeof(VAAPIDeviceContext),
983     .device_hwconfig_size   = sizeof(AVVAAPIHWConfig),
984     .frames_hwctx_size      = sizeof(AVVAAPIFramesContext),
985     .frames_priv_size       = sizeof(VAAPIFramesContext),
986
987     .device_create          = &vaapi_device_create,
988     .device_init            = &vaapi_device_init,
989     .device_uninit          = &vaapi_device_uninit,
990     .frames_get_constraints = &vaapi_frames_get_constraints,
991     .frames_init            = &vaapi_frames_init,
992     .frames_uninit          = &vaapi_frames_uninit,
993     .frames_get_buffer      = &vaapi_get_buffer,
994     .transfer_get_formats   = &vaapi_transfer_get_formats,
995     .transfer_data_to       = &vaapi_transfer_data_to,
996     .transfer_data_from     = &vaapi_transfer_data_from,
997
998     .pix_fmts = (const enum AVPixelFormat[]) {
999         AV_PIX_FMT_VAAPI,
1000         AV_PIX_FMT_NONE
1001     },
1002 };