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