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