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