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