diff options
Diffstat (limited to 'libavutil/hwcontext_vaapi.c')
-rw-r--r-- | libavutil/hwcontext_vaapi.c | 399 |
1 files changed, 374 insertions, 25 deletions
diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c index 2260518ca6..40a85d288c 100644 --- a/libavutil/hwcontext_vaapi.c +++ b/libavutil/hwcontext_vaapi.c @@ -1,18 +1,18 @@ /* - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,6 +25,11 @@ # include <va/va_drm.h> #endif +#if CONFIG_LIBDRM +# include <va/va_drmcommon.h> +# include <drm_fourcc.h> +#endif + #include <fcntl.h> #if HAVE_UNISTD_H # include <unistd.h> @@ -41,6 +46,10 @@ #include "pixdesc.h" #include "pixfmt.h" +#if CONFIG_LIBDRM +# include "hwcontext_drm.h" +#endif + typedef struct VAAPIDevicePriv { #if HAVE_VAAPI_X11 Display *x11_display; @@ -104,15 +113,15 @@ static const struct { MAP(P010, YUV420_10BPP, P010), #endif MAP(BGRA, RGB32, BGRA), - //MAP(BGRX, RGB32, BGR0), + MAP(BGRX, RGB32, BGR0), MAP(RGBA, RGB32, RGBA), - //MAP(RGBX, RGB32, RGB0), + MAP(RGBX, RGB32, RGB0), #ifdef VA_FOURCC_ABGR MAP(ABGR, RGB32, ABGR), - //MAP(XBGR, RGB32, 0BGR), + MAP(XBGR, RGB32, 0BGR), #endif MAP(ARGB, RGB32, ARGB), - //MAP(XRGB, RGB32, 0RGB), + MAP(XRGB, RGB32, 0RGB), }; #undef MAP @@ -875,8 +884,8 @@ fail: return err; } -static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst, - const AVFrame *src, int flags) +static int vaapi_map_to_memory(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) { int err; @@ -897,6 +906,279 @@ static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst, return 0; } +#if CONFIG_LIBDRM + +#define DRM_MAP(va, layers, ...) { \ + VA_FOURCC_ ## va, \ + layers, \ + { __VA_ARGS__ } \ + } +static const struct { + uint32_t va_fourcc; + int nb_layer_formats; + uint32_t layer_formats[AV_DRM_MAX_PLANES]; +} vaapi_drm_format_map[] = { +#ifdef DRM_FORMAT_R8 + DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88), +#endif + DRM_MAP(NV12, 1, DRM_FORMAT_NV12), +#if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16) + DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616), +#endif + DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888), + DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888), + DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888), + DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888), +#ifdef VA_FOURCC_ABGR + DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888), + DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888), +#endif + DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888), + DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888), +}; +#undef DRM_MAP + +static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc, + HWMapDescriptor *hwmap) +{ + AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx; + + VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv; + + av_log(dst_fc, AV_LOG_DEBUG, "Destroy surface %#x.\n", surface_id); + + vaDestroySurfaces(dst_dev->display, &surface_id, 1); +} + +static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst, + const AVFrame *src, int flags) +{ + AVHWFramesContext *dst_fc = + (AVHWFramesContext*)dst->hw_frames_ctx->data; + AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx; + const AVDRMFrameDescriptor *desc; + VASurfaceID surface_id; + VAStatus vas; + uint32_t va_fourcc, va_rt_format; + int err, i, j, k; + + unsigned long buffer_handle; + VASurfaceAttribExternalBuffers buffer_desc; + VASurfaceAttrib attrs[2] = { + { + .type = VASurfaceAttribMemoryType, + .flags = VA_SURFACE_ATTRIB_SETTABLE, + .value.type = VAGenericValueTypeInteger, + .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME, + }, + { + .type = VASurfaceAttribExternalBufferDescriptor, + .flags = VA_SURFACE_ATTRIB_SETTABLE, + .value.type = VAGenericValueTypePointer, + .value.value.p = &buffer_desc, + } + }; + + desc = (AVDRMFrameDescriptor*)src->data[0]; + + if (desc->nb_objects != 1) { + av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames " + "made from a single DRM object.\n"); + return AVERROR(EINVAL); + } + + va_fourcc = 0; + for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) { + if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats) + continue; + for (j = 0; j < desc->nb_layers; j++) { + if (desc->layers[j].format != + vaapi_drm_format_map[i].layer_formats[j]) + break; + } + if (j != desc->nb_layers) + continue; + va_fourcc = vaapi_drm_format_map[i].va_fourcc; + break; + } + if (!va_fourcc) { + av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported " + "by VAAPI.\n"); + return AVERROR(EINVAL); + } + + av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as " + "%08x.\n", desc->objects[0].fd, va_fourcc); + + for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) { + if (vaapi_format_map[i].fourcc == va_fourcc) + va_rt_format = vaapi_format_map[i].rt_format; + } + + buffer_handle = desc->objects[0].fd; + buffer_desc.pixel_format = va_fourcc; + buffer_desc.width = src_fc->width; + buffer_desc.height = src_fc->height; + buffer_desc.data_size = desc->objects[0].size; + buffer_desc.buffers = &buffer_handle; + buffer_desc.num_buffers = 1; + buffer_desc.flags = 0; + + k = 0; + for (i = 0; i < desc->nb_layers; i++) { + for (j = 0; j < desc->layers[i].nb_planes; j++) { + buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch; + buffer_desc.offsets[k] = desc->layers[i].planes[j].offset; + ++k; + } + } + buffer_desc.num_planes = k; + + vas = vaCreateSurfaces(dst_dev->display, va_rt_format, + src->width, src->height, + &surface_id, 1, + attrs, FF_ARRAY_ELEMS(attrs)); + if (vas != VA_STATUS_SUCCESS) { + av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM " + "object: %d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR(EIO); + } + av_log(dst_fc, AV_LOG_DEBUG, "Create surface %#x.\n", surface_id); + + err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src, + &vaapi_unmap_from_drm, + (void*)(uintptr_t)surface_id); + if (err < 0) + return err; + + dst->width = src->width; + dst->height = src->height; + dst->data[3] = (uint8_t*)(uintptr_t)surface_id; + + av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to " + "surface %#x.\n", desc->objects[0].fd, surface_id); + + return 0; +} + +static void vaapi_unmap_to_drm(AVHWFramesContext *dst_fc, + HWMapDescriptor *hwmap) +{ + AVDRMFrameDescriptor *drm_desc = hwmap->priv; + int i; + + for (i = 0; i < drm_desc->nb_objects; i++) + close(drm_desc->objects[i].fd); + + av_freep(&drm_desc); +} + +static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ +#if CONFIG_VAAPI_1 + AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx; + VASurfaceID surface_id; + VAStatus vas; + VADRMPRIMESurfaceDescriptor va_desc; + AVDRMFrameDescriptor *drm_desc = NULL; + int err, i, j; + + surface_id = (VASurfaceID)(uintptr_t)src->data[3]; + + vas = vaExportSurfaceHandle(hwctx->display, surface_id, + VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, + VA_EXPORT_SURFACE_READ_ONLY | + VA_EXPORT_SURFACE_SEPARATE_LAYERS, + &va_desc); + if (vas != VA_STATUS_SUCCESS) { + if (vas == VA_STATUS_ERROR_UNIMPLEMENTED) + return AVERROR(ENOSYS); + av_log(hwfc, AV_LOG_ERROR, "Failed to export surface %#x: " + "%d (%s).\n", surface_id, vas, vaErrorStr(vas)); + return AVERROR(EIO); + } + + drm_desc = av_mallocz(sizeof(*drm_desc)); + if (!drm_desc) { + err = AVERROR(ENOMEM); + goto fail; + } + + // By some bizarre coincidence, these structures are very similar... + drm_desc->nb_objects = va_desc.num_objects; + for (i = 0; i < va_desc.num_objects; i++) { + drm_desc->objects[i].fd = va_desc.objects[i].fd; + drm_desc->objects[i].size = va_desc.objects[i].size; + drm_desc->objects[i].format_modifier = + va_desc.objects[i].drm_format_modifier; + } + drm_desc->nb_layers = va_desc.num_layers; + for (i = 0; i < va_desc.num_layers; i++) { + drm_desc->layers[i].format = va_desc.layers[i].drm_format; + drm_desc->layers[i].nb_planes = va_desc.layers[i].num_planes; + for (j = 0; j < va_desc.layers[i].num_planes; j++) { + drm_desc->layers[i].planes[j].object_index = + va_desc.layers[i].object_index[j]; + drm_desc->layers[i].planes[j].offset = + va_desc.layers[i].offset[j]; + drm_desc->layers[i].planes[j].pitch = + va_desc.layers[i].pitch[j]; + } + } + + err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, + &vaapi_unmap_to_drm, drm_desc); + if (err < 0) + goto fail; + + dst->width = src->width; + dst->height = src->height; + dst->data[0] = (uint8_t*)drm_desc; + + return 0; + +fail: + for (i = 0; i < va_desc.num_objects; i++) + close(va_desc.objects[i].fd); + av_freep(&drm_desc); + return err; +#else + // Older versions without vaExportSurfaceHandle() are not supported - + // in theory this is possible with a combination of vaDeriveImage() + // and vaAcquireBufferHandle(), but it doesn't carry enough metadata + // to actually use the result in a generic way. + return AVERROR(ENOSYS); +#endif +} +#endif + +static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ + switch (src->format) { +#if CONFIG_LIBDRM + case AV_PIX_FMT_DRM_PRIME: + return vaapi_map_from_drm(hwfc, dst, src, flags); +#endif + default: + return AVERROR(ENOSYS); + } +} + +static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ + switch (dst->format) { +#if CONFIG_LIBDRM + case AV_PIX_FMT_DRM_PRIME: + return vaapi_map_to_drm(hwfc, dst, src, flags); +#endif + default: + return vaapi_map_to_memory(hwfc, dst, src, flags); + } +} + static void vaapi_device_free(AVHWDeviceContext *ctx) { AVVAAPIDeviceContext *hwctx = ctx->hwctx; @@ -916,14 +1198,53 @@ static void vaapi_device_free(AVHWDeviceContext *ctx) av_freep(&priv); } +#if CONFIG_VAAPI_1 +static void vaapi_device_log_error(void *context, const char *message) +{ + AVHWDeviceContext *ctx = context; + + av_log(ctx, AV_LOG_ERROR, "libva: %s", message); +} + +static void vaapi_device_log_info(void *context, const char *message) +{ + AVHWDeviceContext *ctx = context; + + av_log(ctx, AV_LOG_VERBOSE, "libva: %s", message); +} +#endif + +static int vaapi_device_connect(AVHWDeviceContext *ctx, + VADisplay display) +{ + AVVAAPIDeviceContext *hwctx = ctx->hwctx; + int major, minor; + VAStatus vas; + +#if CONFIG_VAAPI_1 + vaSetErrorCallback(display, &vaapi_device_log_error, ctx); + vaSetInfoCallback (display, &vaapi_device_log_info, ctx); +#endif + + hwctx->display = display; + + vas = vaInitialize(display, &major, &minor); + if (vas != VA_STATUS_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI " + "connection: %d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR(EIO); + } + av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: " + "version %d.%d\n", major, minor); + + return 0; +} + static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device, AVDictionary *opts, int flags) { - AVVAAPIDeviceContext *hwctx = ctx->hwctx; VAAPIDevicePriv *priv; - VADisplay display = 0; - VAStatus vas; - int major, minor; + VADisplay display = NULL; priv = av_mallocz(sizeof(*priv)); if (!priv) @@ -985,18 +1306,45 @@ static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device, return AVERROR(EINVAL); } - hwctx->display = display; + return vaapi_device_connect(ctx, display); +} - vas = vaInitialize(display, &major, &minor); - if (vas != VA_STATUS_SUCCESS) { - av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI " - "connection: %d (%s).\n", vas, vaErrorStr(vas)); - return AVERROR(EIO); - } - av_log(ctx, AV_LOG_VERBOSE, "Initialised VAAPI connection: " - "version %d.%d\n", major, minor); +static int vaapi_device_derive(AVHWDeviceContext *ctx, + AVHWDeviceContext *src_ctx, int flags) +{ +#if CONFIG_LIBDRM + if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) { + AVDRMDeviceContext *src_hwctx = src_ctx->hwctx; + VADisplay *display; + VAAPIDevicePriv *priv; + + if (src_hwctx->fd < 0) { + av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated " + "device to derive a VA display from.\n"); + return AVERROR(EINVAL); + } - return 0; + priv = av_mallocz(sizeof(*priv)); + if (!priv) + return AVERROR(ENOMEM); + + // Inherits the fd from the source context, which will close it. + priv->drm_fd = -1; + + ctx->user_opaque = priv; + ctx->free = &vaapi_device_free; + + display = vaGetDisplayDRM(src_hwctx->fd); + if (!display) { + av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from " + "DRM device.\n"); + return AVERROR(EIO); + } + + return vaapi_device_connect(ctx, display); + } +#endif + return AVERROR(ENOSYS); } const HWContextType ff_hwcontext_type_vaapi = { @@ -1010,6 +1358,7 @@ const HWContextType ff_hwcontext_type_vaapi = { .frames_priv_size = sizeof(VAAPIFramesContext), .device_create = &vaapi_device_create, + .device_derive = &vaapi_device_derive, .device_init = &vaapi_device_init, .device_uninit = &vaapi_device_uninit, .frames_get_constraints = &vaapi_frames_get_constraints, @@ -1019,7 +1368,7 @@ const HWContextType ff_hwcontext_type_vaapi = { .transfer_get_formats = &vaapi_transfer_get_formats, .transfer_data_to = &vaapi_transfer_data_to, .transfer_data_from = &vaapi_transfer_data_from, - .map_to = NULL, + .map_to = &vaapi_map_to, .map_from = &vaapi_map_from, .pix_fmts = (const enum AVPixelFormat[]) { |