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