]> git.sesse.net Git - ffmpeg/blob - libavutil/hwcontext_vaapi.c
avcodec/libx264: Remove unused variable
[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 "avassert.h"
20 #include "buffer.h"
21 #include "common.h"
22 #include "hwcontext.h"
23 #include "hwcontext_internal.h"
24 #include "hwcontext_vaapi.h"
25 #include "mem.h"
26 #include "pixdesc.h"
27 #include "pixfmt.h"
28
29 typedef struct VAAPISurfaceFormat {
30     enum AVPixelFormat pix_fmt;
31     VAImageFormat image_format;
32 } VAAPISurfaceFormat;
33
34 typedef struct VAAPIDeviceContext {
35     // Surface formats which can be used with this device.
36     VAAPISurfaceFormat *formats;
37     int              nb_formats;
38 } VAAPIDeviceContext;
39
40 typedef struct VAAPIFramesContext {
41     // Surface attributes set at create time.
42     VASurfaceAttrib *attributes;
43     int           nb_attributes;
44     // RT format of the underlying surface (Intel driver ignores this anyway).
45     unsigned int rt_format;
46     // Whether vaDeriveImage works.
47     int derive_works;
48 } VAAPIFramesContext;
49
50 enum {
51     VAAPI_MAP_READ   = 0x01,
52     VAAPI_MAP_WRITE  = 0x02,
53     VAAPI_MAP_DIRECT = 0x04,
54 };
55
56 typedef struct VAAPISurfaceMap {
57     // The source hardware frame of this mapping (with hw_frames_ctx set).
58     const AVFrame *source;
59     // VAAPI_MAP_* flags which apply to this mapping.
60     int flags;
61     // Handle to the derived or copied image which is mapped.
62     VAImage image;
63 } VAAPISurfaceMap;
64
65 #define MAP(va, rt, av) { \
66         VA_FOURCC_ ## va, \
67         VA_RT_FORMAT_ ## rt, \
68         AV_PIX_FMT_ ## av \
69     }
70 // The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
71 // plane swap cases.  The frame handling below tries to hide these.
72 static struct {
73     unsigned int fourcc;
74     unsigned int rt_format;
75     enum AVPixelFormat pix_fmt;
76 } vaapi_format_map[] = {
77     MAP(NV12, YUV420,  NV12),
78     MAP(YV12, YUV420,  YUV420P), // With U/V planes swapped.
79     MAP(IYUV, YUV420,  YUV420P),
80   //MAP(I420, YUV420,  YUV420P), // Not in libva but used by Intel driver.
81 #ifdef VA_FOURCC_YV16
82     MAP(YV16, YUV422,  YUV422P), // With U/V planes swapped.
83 #endif
84     MAP(422H, YUV422,  YUV422P),
85     MAP(UYVY, YUV422,  UYVY422),
86     MAP(YUY2, YUV422,  YUYV422),
87     MAP(Y800, YUV400,  GRAY8),
88 #ifdef VA_FOURCC_P010
89     MAP(P010, YUV420_10BPP, P010),
90 #endif
91     MAP(BGRA, RGB32,   BGRA),
92     MAP(BGRX, RGB32,   BGR0),
93     MAP(RGBA, RGB32,   RGBA),
94     MAP(RGBX, RGB32,   RGB0),
95     MAP(ABGR, RGB32,   ABGR),
96     MAP(XBGR, RGB32,   0BGR),
97     MAP(ARGB, RGB32,   ARGB),
98     MAP(XRGB, RGB32,   0RGB),
99 };
100 #undef MAP
101
102 static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
103 {
104     int i;
105     for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
106         if (vaapi_format_map[i].fourcc == fourcc)
107             return vaapi_format_map[i].pix_fmt;
108     return AV_PIX_FMT_NONE;
109 }
110
111 static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
112                                   enum AVPixelFormat pix_fmt,
113                                   VAImageFormat **image_format)
114 {
115     VAAPIDeviceContext *ctx = hwdev->internal->priv;
116     int i;
117
118     for (i = 0; i < ctx->nb_formats; i++) {
119         if (ctx->formats[i].pix_fmt == pix_fmt) {
120             *image_format = &ctx->formats[i].image_format;
121             return 0;
122         }
123     }
124     return AVERROR(EINVAL);
125 }
126
127 static int vaapi_frames_get_constraints(AVHWDeviceContext *hwdev,
128                                         const void *hwconfig,
129                                         AVHWFramesConstraints *constraints)
130 {
131     AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
132     const AVVAAPIHWConfig *config = hwconfig;
133     AVVAAPIHWConfig *tmp_config;
134     VASurfaceAttrib *attr_list = NULL;
135     VAStatus vas;
136     enum AVPixelFormat pix_fmt;
137     unsigned int fourcc;
138     int err, i, j, attr_count, pix_fmt_count;
139
140     if (!hwconfig) {
141         // No configuration was provided, so we create a temporary pipeline
142         // configuration in order to query all supported image formats.
143
144         tmp_config = av_mallocz(sizeof(*config));
145         if (!tmp_config)
146             return AVERROR(ENOMEM);
147
148         vas = vaCreateConfig(hwctx->display,
149                              VAProfileNone, VAEntrypointVideoProc,
150                              NULL, 0, &tmp_config->config_id);
151         if (vas != VA_STATUS_SUCCESS) {
152             // No vpp.  We might still be able to do something useful if
153             // codecs are supported, so try to make the most-commonly
154             // supported decoder configuration we can to query instead.
155             vas = vaCreateConfig(hwctx->display,
156                                  VAProfileH264ConstrainedBaseline,
157                                  VAEntrypointVLD, NULL, 0,
158                                  &tmp_config->config_id);
159             if (vas != VA_STATUS_SUCCESS) {
160                 av_freep(&tmp_config);
161                 return AVERROR(ENOSYS);
162             }
163         }
164
165         config = tmp_config;
166     }
167
168     attr_count = 0;
169     vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
170                                    0, &attr_count);
171     if (vas != VA_STATUS_SUCCESS) {
172         av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
173                "%d (%s).\n", vas, vaErrorStr(vas));
174         err = AVERROR(ENOSYS);
175         goto fail;
176     }
177
178     attr_list = av_malloc(attr_count * sizeof(*attr_list));
179     if (!attr_list) {
180         err = AVERROR(ENOMEM);
181         goto fail;
182     }
183
184     vas = vaQuerySurfaceAttributes(hwctx->display, config->config_id,
185                                    attr_list, &attr_count);
186     if (vas != VA_STATUS_SUCCESS) {
187         av_log(hwdev, AV_LOG_ERROR, "Failed to query surface attributes: "
188                "%d (%s).\n", vas, vaErrorStr(vas));
189         err = AVERROR(ENOSYS);
190         goto fail;
191     }
192
193     pix_fmt_count = 0;
194     for (i = 0; i < attr_count; i++) {
195         switch (attr_list[i].type) {
196         case VASurfaceAttribPixelFormat:
197             fourcc = attr_list[i].value.value.i;
198             pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
199             if (pix_fmt != AV_PIX_FMT_NONE) {
200                 ++pix_fmt_count;
201             } else {
202                 // Something unsupported - ignore.
203             }
204             break;
205         case VASurfaceAttribMinWidth:
206             constraints->min_width  = attr_list[i].value.value.i;
207             break;
208         case VASurfaceAttribMinHeight:
209             constraints->min_height = attr_list[i].value.value.i;
210             break;
211         case VASurfaceAttribMaxWidth:
212             constraints->max_width  = attr_list[i].value.value.i;
213             break;
214         case VASurfaceAttribMaxHeight:
215             constraints->max_height = attr_list[i].value.value.i;
216             break;
217         }
218     }
219     if (pix_fmt_count == 0) {
220         // Nothing usable found.  Presumably there exists something which
221         // works, so leave the set null to indicate unknown.
222         constraints->valid_sw_formats = NULL;
223     } else {
224         constraints->valid_sw_formats = av_malloc_array(pix_fmt_count + 1,
225                                                         sizeof(pix_fmt));
226         if (!constraints->valid_sw_formats) {
227             err = AVERROR(ENOMEM);
228             goto fail;
229         }
230
231         for (i = j = 0; i < attr_count; i++) {
232             if (attr_list[i].type != VASurfaceAttribPixelFormat)
233                 continue;
234             fourcc = attr_list[i].value.value.i;
235             pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
236             if (pix_fmt != AV_PIX_FMT_NONE)
237                 constraints->valid_sw_formats[j++] = pix_fmt;
238         }
239         av_assert0(j == pix_fmt_count);
240         constraints->valid_sw_formats[j] = AV_PIX_FMT_NONE;
241     }
242
243     constraints->valid_hw_formats = av_malloc_array(2, sizeof(pix_fmt));
244     if (!constraints->valid_hw_formats) {
245         err = AVERROR(ENOMEM);
246         goto fail;
247     }
248     constraints->valid_hw_formats[0] = AV_PIX_FMT_VAAPI;
249     constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
250
251     err = 0;
252 fail:
253     av_freep(&attr_list);
254     if (!hwconfig) {
255         vaDestroyConfig(hwctx->display, tmp_config->config_id);
256         av_freep(&tmp_config);
257     }
258     return err;
259 }
260
261 static int vaapi_device_init(AVHWDeviceContext *hwdev)
262 {
263     VAAPIDeviceContext *ctx = hwdev->internal->priv;
264     AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
265     AVHWFramesConstraints *constraints = NULL;
266     VAImageFormat *image_list = NULL;
267     VAStatus vas;
268     int err, i, j, image_count;
269     enum AVPixelFormat pix_fmt;
270     unsigned int fourcc;
271
272     constraints = av_mallocz(sizeof(*constraints));
273     if (!constraints)
274         goto fail;
275
276     err = vaapi_frames_get_constraints(hwdev, NULL, constraints);
277     if (err < 0)
278         goto fail;
279
280     image_count = vaMaxNumImageFormats(hwctx->display);
281     if (image_count <= 0) {
282         err = AVERROR(EIO);
283         goto fail;
284     }
285     image_list = av_malloc(image_count * sizeof(*image_list));
286     if (!image_list) {
287         err = AVERROR(ENOMEM);
288         goto fail;
289     }
290     vas = vaQueryImageFormats(hwctx->display, image_list, &image_count);
291     if (vas != VA_STATUS_SUCCESS) {
292         err = AVERROR(EIO);
293         goto fail;
294     }
295
296     ctx->formats  = av_malloc(image_count * sizeof(*ctx->formats));
297     if (!ctx->formats) {
298         err = AVERROR(ENOMEM);
299         goto fail;
300     }
301     ctx->nb_formats = 0;
302     for (i = 0; i < image_count; i++) {
303         fourcc  = image_list[i].fourcc;
304         pix_fmt = vaapi_pix_fmt_from_fourcc(fourcc);
305         for (j = 0; constraints->valid_sw_formats[j] != AV_PIX_FMT_NONE; j++) {
306             if (pix_fmt == constraints->valid_sw_formats[j])
307                 break;
308         }
309         if (constraints->valid_sw_formats[j] != AV_PIX_FMT_NONE) {
310             av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> %s.\n",
311                    fourcc, av_get_pix_fmt_name(pix_fmt));
312             ctx->formats[ctx->nb_formats].pix_fmt      = pix_fmt;
313             ctx->formats[ctx->nb_formats].image_format = image_list[i];
314             ++ctx->nb_formats;
315         } else {
316             av_log(hwdev, AV_LOG_DEBUG, "Format %#x -> unknown.\n", fourcc);
317         }
318     }
319
320     av_free(image_list);
321     av_hwframe_constraints_free(&constraints);
322     return 0;
323 fail:
324     av_freep(&ctx->formats);
325     av_free(image_list);
326     av_hwframe_constraints_free(&constraints);
327     return err;
328 }
329
330 static void vaapi_device_uninit(AVHWDeviceContext *hwdev)
331 {
332     VAAPIDeviceContext *ctx = hwdev->internal->priv;
333
334     av_freep(&ctx->formats);
335 }
336
337 static void vaapi_buffer_free(void *opaque, uint8_t *data)
338 {
339     AVHWFramesContext     *hwfc = opaque;
340     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
341     VASurfaceID surface_id;
342     VAStatus vas;
343
344     surface_id = (VASurfaceID)(uintptr_t)data;
345
346     vas = vaDestroySurfaces(hwctx->display, &surface_id, 1);
347     if (vas != VA_STATUS_SUCCESS) {
348         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy surface %#x: "
349                "%d (%s).\n", surface_id, vas, vaErrorStr(vas));
350     }
351 }
352
353 static AVBufferRef *vaapi_pool_alloc(void *opaque, int size)
354 {
355     AVHWFramesContext     *hwfc = opaque;
356     VAAPIFramesContext     *ctx = hwfc->internal->priv;
357     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
358     AVVAAPIFramesContext  *avfc = hwfc->hwctx;
359     VASurfaceID surface_id;
360     VAStatus vas;
361     AVBufferRef *ref;
362
363     vas = vaCreateSurfaces(hwctx->display, ctx->rt_format,
364                            hwfc->width, hwfc->height,
365                            &surface_id, 1,
366                            ctx->attributes, ctx->nb_attributes);
367     if (vas != VA_STATUS_SUCCESS) {
368         av_log(hwfc, AV_LOG_ERROR, "Failed to create surface: "
369                "%d (%s).\n", vas, vaErrorStr(vas));
370         return NULL;
371     }
372     av_log(hwfc, AV_LOG_DEBUG, "Created surface %#x.\n", surface_id);
373
374     ref = av_buffer_create((uint8_t*)(uintptr_t)surface_id,
375                            sizeof(surface_id), &vaapi_buffer_free,
376                            hwfc, AV_BUFFER_FLAG_READONLY);
377     if (!ref) {
378         vaDestroySurfaces(hwctx->display, &surface_id, 1);
379         return NULL;
380     }
381
382     if (hwfc->initial_pool_size > 0) {
383         // This is a fixed-size pool, so we must still be in the initial
384         // allocation sequence.
385         av_assert0(avfc->nb_surfaces < hwfc->initial_pool_size);
386         avfc->surface_ids[avfc->nb_surfaces] = surface_id;
387         ++avfc->nb_surfaces;
388     }
389
390     return ref;
391 }
392
393 static int vaapi_frames_init(AVHWFramesContext *hwfc)
394 {
395     AVVAAPIFramesContext  *avfc = hwfc->hwctx;
396     VAAPIFramesContext     *ctx = hwfc->internal->priv;
397     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
398     VAImageFormat *expected_format;
399     AVBufferRef *test_surface = NULL;
400     VASurfaceID test_surface_id;
401     VAImage test_image;
402     VAStatus vas;
403     int err, i;
404     unsigned int fourcc, rt_format;
405
406     for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
407         if (vaapi_format_map[i].pix_fmt == hwfc->sw_format) {
408             fourcc    = vaapi_format_map[i].fourcc;
409             rt_format = vaapi_format_map[i].rt_format;
410             break;
411         }
412     }
413     if (i >= FF_ARRAY_ELEMS(vaapi_format_map)) {
414         av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
415                av_get_pix_fmt_name(hwfc->sw_format));
416         return AVERROR(EINVAL);
417     }
418
419     if (!hwfc->pool) {
420         int need_memory_type = 1, need_pixel_format = 1;
421         for (i = 0; i < avfc->nb_attributes; i++) {
422             if (ctx->attributes[i].type == VASurfaceAttribMemoryType)
423                 need_memory_type  = 0;
424             if (ctx->attributes[i].type == VASurfaceAttribPixelFormat)
425                 need_pixel_format = 0;
426         }
427         ctx->nb_attributes =
428             avfc->nb_attributes + need_memory_type + need_pixel_format;
429
430         ctx->attributes = av_malloc(ctx->nb_attributes *
431                                         sizeof(*ctx->attributes));
432         if (!ctx->attributes) {
433             err = AVERROR(ENOMEM);
434             goto fail;
435         }
436
437         for (i = 0; i < avfc->nb_attributes; i++)
438             ctx->attributes[i] = avfc->attributes[i];
439         if (need_memory_type) {
440             ctx->attributes[i++] = (VASurfaceAttrib) {
441                 .type          = VASurfaceAttribMemoryType,
442                 .flags         = VA_SURFACE_ATTRIB_SETTABLE,
443                 .value.type    = VAGenericValueTypeInteger,
444                 .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA,
445             };
446         }
447         if (need_pixel_format) {
448             ctx->attributes[i++] = (VASurfaceAttrib) {
449                 .type          = VASurfaceAttribPixelFormat,
450                 .flags         = VA_SURFACE_ATTRIB_SETTABLE,
451                 .value.type    = VAGenericValueTypeInteger,
452                 .value.value.i = fourcc,
453             };
454         }
455         av_assert0(i == ctx->nb_attributes);
456
457         ctx->rt_format = rt_format;
458
459         if (hwfc->initial_pool_size > 0) {
460             // This pool will be usable as a render target, so we need to store
461             // all of the surface IDs somewhere that vaCreateContext() calls
462             // will be able to access them.
463             avfc->nb_surfaces = 0;
464             avfc->surface_ids = av_malloc(hwfc->initial_pool_size *
465                                           sizeof(*avfc->surface_ids));
466             if (!avfc->surface_ids) {
467                 err = AVERROR(ENOMEM);
468                 goto fail;
469             }
470         } else {
471             // This pool allows dynamic sizing, and will not be usable as a
472             // render target.
473             avfc->nb_surfaces = 0;
474             avfc->surface_ids = NULL;
475         }
476
477         hwfc->internal->pool_internal =
478             av_buffer_pool_init2(sizeof(VASurfaceID), hwfc,
479                                  &vaapi_pool_alloc, NULL);
480         if (!hwfc->internal->pool_internal) {
481             av_log(hwfc, AV_LOG_ERROR, "Failed to create VAAPI surface pool.\n");
482             err = AVERROR(ENOMEM);
483             goto fail;
484         }
485     }
486
487     // Allocate a single surface to test whether vaDeriveImage() is going
488     // to work for the specific configuration.
489     if (hwfc->pool) {
490         test_surface = av_buffer_pool_get(hwfc->pool);
491         if (!test_surface) {
492             av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
493                    "user-configured buffer pool.\n");
494             err = AVERROR(ENOMEM);
495             goto fail;
496         }
497     } else {
498         test_surface = av_buffer_pool_get(hwfc->internal->pool_internal);
499         if (!test_surface) {
500             av_log(hwfc, AV_LOG_ERROR, "Unable to allocate a surface from "
501                    "internal buffer pool.\n");
502             err = AVERROR(ENOMEM);
503             goto fail;
504         }
505     }
506     test_surface_id = (VASurfaceID)(uintptr_t)test_surface->data;
507
508     ctx->derive_works = 0;
509
510     err = vaapi_get_image_format(hwfc->device_ctx,
511                                  hwfc->sw_format, &expected_format);
512     if (err == 0) {
513         vas = vaDeriveImage(hwctx->display, test_surface_id, &test_image);
514         if (vas == VA_STATUS_SUCCESS) {
515             if (expected_format->fourcc == test_image.format.fourcc) {
516                 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping possible.\n");
517                 ctx->derive_works = 1;
518             } else {
519                 av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
520                        "derived image format %08x does not match "
521                        "expected format %08x.\n",
522                        expected_format->fourcc, test_image.format.fourcc);
523             }
524             vaDestroyImage(hwctx->display, test_image.image_id);
525         } else {
526             av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
527                    "deriving image does not work: "
528                    "%d (%s).\n", vas, vaErrorStr(vas));
529         }
530     } else {
531         av_log(hwfc, AV_LOG_DEBUG, "Direct mapping disabled: "
532                "image format is not supported.\n");
533     }
534
535     av_buffer_unref(&test_surface);
536     return 0;
537
538 fail:
539     av_buffer_unref(&test_surface);
540     av_freep(&avfc->surface_ids);
541     av_freep(&ctx->attributes);
542     return err;
543 }
544
545 static void vaapi_frames_uninit(AVHWFramesContext *hwfc)
546 {
547     AVVAAPIFramesContext *avfc = hwfc->hwctx;
548     VAAPIFramesContext    *ctx = hwfc->internal->priv;
549
550     av_freep(&avfc->surface_ids);
551     av_freep(&ctx->attributes);
552 }
553
554 static int vaapi_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
555 {
556     frame->buf[0] = av_buffer_pool_get(hwfc->pool);
557     if (!frame->buf[0])
558         return AVERROR(ENOMEM);
559
560     frame->data[3] = frame->buf[0]->data;
561     frame->format  = AV_PIX_FMT_VAAPI;
562     frame->width   = hwfc->width;
563     frame->height  = hwfc->height;
564
565     return 0;
566 }
567
568 static int vaapi_transfer_get_formats(AVHWFramesContext *hwfc,
569                                       enum AVHWFrameTransferDirection dir,
570                                       enum AVPixelFormat **formats)
571 {
572     VAAPIDeviceContext *ctx = hwfc->device_ctx->internal->priv;
573     enum AVPixelFormat *pix_fmts, preferred_format;
574     int i, k;
575
576     preferred_format = hwfc->sw_format;
577
578     pix_fmts = av_malloc((ctx->nb_formats + 1) * sizeof(*pix_fmts));
579     if (!pix_fmts)
580         return AVERROR(ENOMEM);
581
582     pix_fmts[0] = preferred_format;
583     k = 1;
584     for (i = 0; i < ctx->nb_formats; i++) {
585         if (ctx->formats[i].pix_fmt == preferred_format)
586             continue;
587         av_assert0(k < ctx->nb_formats);
588         pix_fmts[k++] = ctx->formats[i].pix_fmt;
589     }
590     av_assert0(k == ctx->nb_formats);
591     pix_fmts[k] = AV_PIX_FMT_NONE;
592
593     *formats = pix_fmts;
594     return 0;
595 }
596
597 static void vaapi_unmap_frame(void *opaque, uint8_t *data)
598 {
599     AVHWFramesContext *hwfc = opaque;
600     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
601     VAAPISurfaceMap *map = (VAAPISurfaceMap*)data;
602     const AVFrame *src;
603     VASurfaceID surface_id;
604     VAStatus vas;
605
606     src = map->source;
607     surface_id = (VASurfaceID)(uintptr_t)src->data[3];
608     av_log(hwfc, AV_LOG_DEBUG, "Unmap surface %#x.\n", surface_id);
609
610     vas = vaUnmapBuffer(hwctx->display, map->image.buf);
611     if (vas != VA_STATUS_SUCCESS) {
612         av_log(hwfc, AV_LOG_ERROR, "Failed to unmap image from surface "
613                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
614     }
615
616     if ((map->flags & VAAPI_MAP_WRITE) &&
617         !(map->flags & VAAPI_MAP_DIRECT)) {
618         vas = vaPutImage(hwctx->display, surface_id, map->image.image_id,
619                          0, 0, hwfc->width, hwfc->height,
620                          0, 0, hwfc->width, hwfc->height);
621         if (vas != VA_STATUS_SUCCESS) {
622             av_log(hwfc, AV_LOG_ERROR, "Failed to write image to surface "
623                    "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
624         }
625     }
626
627     vas = vaDestroyImage(hwctx->display, map->image.image_id);
628     if (vas != VA_STATUS_SUCCESS) {
629         av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image from surface "
630                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
631     }
632
633     av_free(map);
634 }
635
636 static int vaapi_map_frame(AVHWFramesContext *hwfc,
637                            AVFrame *dst, const AVFrame *src, int flags)
638 {
639     AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
640     VAAPIFramesContext *ctx = hwfc->internal->priv;
641     VASurfaceID surface_id;
642     VAImageFormat *image_format;
643     VAAPISurfaceMap *map;
644     VAStatus vas;
645     void *address = NULL;
646     int err, i;
647
648     surface_id = (VASurfaceID)(uintptr_t)src->data[3];
649     av_log(hwfc, AV_LOG_DEBUG, "Map surface %#x.\n", surface_id);
650
651     if (!ctx->derive_works && (flags & VAAPI_MAP_DIRECT)) {
652         // Requested direct mapping but it is not possible.
653         return AVERROR(EINVAL);
654     }
655     if (dst->format == AV_PIX_FMT_NONE)
656         dst->format = hwfc->sw_format;
657     if (dst->format != hwfc->sw_format && (flags & VAAPI_MAP_DIRECT)) {
658         // Requested direct mapping but the formats do not match.
659         return AVERROR(EINVAL);
660     }
661
662     err = vaapi_get_image_format(hwfc->device_ctx, dst->format, &image_format);
663     if (err < 0) {
664         // Requested format is not a valid output format.
665         return AVERROR(EINVAL);
666     }
667
668     map = av_malloc(sizeof(VAAPISurfaceMap));
669     if (!map)
670         return AVERROR(ENOMEM);
671
672     map->source         = src;
673     map->flags          = flags;
674     map->image.image_id = VA_INVALID_ID;
675
676     vas = vaSyncSurface(hwctx->display, surface_id);
677     if (vas != VA_STATUS_SUCCESS) {
678         av_log(hwfc, AV_LOG_ERROR, "Failed to sync surface "
679                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
680         err = AVERROR(EIO);
681         goto fail;
682     }
683
684     // The memory which we map using derive need not be connected to the CPU
685     // in a way conducive to fast access.  On Gen7-Gen9 Intel graphics, the
686     // memory is mappable but not cached, so normal memcpy()-like access is
687     // very slow to read it (but writing is ok).  It is possible to read much
688     // faster with a copy routine which is aware of the limitation, but we
689     // assume for now that the user is not aware of that and would therefore
690     // prefer not to be given direct-mapped memory if they request read access.
691     if (ctx->derive_works &&
692         ((flags & VAAPI_MAP_DIRECT) || !(flags & VAAPI_MAP_READ))) {
693         vas = vaDeriveImage(hwctx->display, surface_id, &map->image);
694         if (vas != VA_STATUS_SUCCESS) {
695             av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from "
696                    "surface %#x: %d (%s).\n",
697                    surface_id, vas, vaErrorStr(vas));
698             err = AVERROR(EIO);
699             goto fail;
700         }
701         if (map->image.format.fourcc != image_format->fourcc) {
702             av_log(hwfc, AV_LOG_ERROR, "Derive image of surface %#x "
703                    "is in wrong format: expected %#08x, got %#08x.\n",
704                    surface_id, image_format->fourcc, map->image.format.fourcc);
705             err = AVERROR(EIO);
706             goto fail;
707         }
708         map->flags |= VAAPI_MAP_DIRECT;
709     } else {
710         vas = vaCreateImage(hwctx->display, image_format,
711                             hwfc->width, hwfc->height, &map->image);
712         if (vas != VA_STATUS_SUCCESS) {
713             av_log(hwfc, AV_LOG_ERROR, "Failed to create image for "
714                    "surface %#x: %d (%s).\n",
715                    surface_id, vas, vaErrorStr(vas));
716             err = AVERROR(EIO);
717             goto fail;
718         }
719         if (flags & VAAPI_MAP_READ) {
720             vas = vaGetImage(hwctx->display, surface_id, 0, 0,
721                              hwfc->width, hwfc->height, map->image.image_id);
722             if (vas != VA_STATUS_SUCCESS) {
723                 av_log(hwfc, AV_LOG_ERROR, "Failed to read image from "
724                        "surface %#x: %d (%s).\n",
725                        surface_id, vas, vaErrorStr(vas));
726                 err = AVERROR(EIO);
727                 goto fail;
728             }
729         }
730     }
731
732     vas = vaMapBuffer(hwctx->display, map->image.buf, &address);
733     if (vas != VA_STATUS_SUCCESS) {
734         av_log(hwfc, AV_LOG_ERROR, "Failed to map image from surface "
735                "%#x: %d (%s).\n", surface_id, vas, vaErrorStr(vas));
736         err = AVERROR(EIO);
737         goto fail;
738     }
739
740     dst->width  = src->width;
741     dst->height = src->height;
742
743     for (i = 0; i < map->image.num_planes; i++) {
744         dst->data[i] = (uint8_t*)address + map->image.offsets[i];
745         dst->linesize[i] = map->image.pitches[i];
746     }
747     if (
748 #ifdef VA_FOURCC_YV16
749         map->image.format.fourcc == VA_FOURCC_YV16 ||
750 #endif
751         map->image.format.fourcc == VA_FOURCC_YV12) {
752         // Chroma planes are YVU rather than YUV, so swap them.
753         FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
754     }
755
756     dst->buf[0] = av_buffer_create((uint8_t*)map, sizeof(*map),
757                                    &vaapi_unmap_frame, hwfc, 0);
758     if (!dst->buf[0]) {
759         err = AVERROR(ENOMEM);
760         goto fail;
761     }
762
763     return 0;
764
765 fail:
766     if (map) {
767         if (address)
768             vaUnmapBuffer(hwctx->display, map->image.buf);
769         if (map->image.image_id != VA_INVALID_ID)
770             vaDestroyImage(hwctx->display, map->image.image_id);
771         av_free(map);
772     }
773     return err;
774 }
775
776 static int vaapi_transfer_data_from(AVHWFramesContext *hwfc,
777                                     AVFrame *dst, const AVFrame *src)
778 {
779     AVFrame *map;
780     int err;
781
782     map = av_frame_alloc();
783     if (!map)
784         return AVERROR(ENOMEM);
785     map->format = dst->format;
786
787     err = vaapi_map_frame(hwfc, map, src, VAAPI_MAP_READ);
788     if (err)
789         goto fail;
790
791     err = av_frame_copy(dst, map);
792     if (err)
793         goto fail;
794
795     err = 0;
796 fail:
797     av_frame_free(&map);
798     return err;
799 }
800
801 static int vaapi_transfer_data_to(AVHWFramesContext *hwfc,
802                                   AVFrame *dst, const AVFrame *src)
803 {
804     AVFrame *map;
805     int err;
806
807     map = av_frame_alloc();
808     if (!map)
809         return AVERROR(ENOMEM);
810     map->format = src->format;
811
812     err = vaapi_map_frame(hwfc, map, dst, VAAPI_MAP_WRITE);
813     if (err)
814         goto fail;
815
816     err = av_frame_copy(map, src);
817     if (err)
818         goto fail;
819
820     err = 0;
821 fail:
822     av_frame_free(&map);
823     return err;
824 }
825
826 const HWContextType ff_hwcontext_type_vaapi = {
827     .type                   = AV_HWDEVICE_TYPE_VAAPI,
828     .name                   = "VAAPI",
829
830     .device_hwctx_size      = sizeof(AVVAAPIDeviceContext),
831     .device_priv_size       = sizeof(VAAPIDeviceContext),
832     .device_hwconfig_size   = sizeof(AVVAAPIHWConfig),
833     .frames_hwctx_size      = sizeof(AVVAAPIFramesContext),
834     .frames_priv_size       = sizeof(VAAPIFramesContext),
835
836     .device_init            = &vaapi_device_init,
837     .device_uninit          = &vaapi_device_uninit,
838     .frames_get_constraints = &vaapi_frames_get_constraints,
839     .frames_init            = &vaapi_frames_init,
840     .frames_uninit          = &vaapi_frames_uninit,
841     .frames_get_buffer      = &vaapi_get_buffer,
842     .transfer_get_formats   = &vaapi_transfer_get_formats,
843     .transfer_data_to       = &vaapi_transfer_data_to,
844     .transfer_data_from     = &vaapi_transfer_data_from,
845
846     .pix_fmts = (const enum AVPixelFormat[]) {
847         AV_PIX_FMT_VAAPI,
848         AV_PIX_FMT_NONE
849     },
850 };