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