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