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