lavu: VAAPI hwcontext implementation
[libav.git] / libavutil / hwcontext_vaapi.c
1 /*
2 * This file is part of Libav.
3 *
4 * Libav 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 * Libav 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 Libav; 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 };