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