]> git.sesse.net Git - ffmpeg/blob - libavutil/hwcontext_vaapi.c
Merge commit '48e2967cd50c2e1a2a539fd697d20ead2c5c4cc8'
[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     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 };