X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavutil%2Fhwcontext_vaapi.c;h=a2387d4fc4abd4ddbd7b03a3a19a3848dc616fc9;hb=4737387d288d1e87e7c7df6203a42d6b1e88231e;hp=af9a136ef05311f232eaea102899460436494c86;hpb=061337a073236cb0bc6c56036f50f883d2887681;p=ffmpeg diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c index af9a136ef05..a2387d4fc4a 100644 --- a/libavutil/hwcontext_vaapi.c +++ b/libavutil/hwcontext_vaapi.c @@ -28,6 +28,9 @@ #if CONFIG_LIBDRM # include # include +# ifndef DRM_FORMAT_MOD_INVALID +# define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1) +# endif #endif #include @@ -40,15 +43,13 @@ #include "buffer.h" #include "common.h" #include "hwcontext.h" +#include "hwcontext_drm.h" #include "hwcontext_internal.h" #include "hwcontext_vaapi.h" #include "mem.h" #include "pixdesc.h" #include "pixfmt.h" -#if CONFIG_LIBDRM -# include "hwcontext_drm.h" -#endif typedef struct VAAPIDevicePriv { #if HAVE_VAAPI_X11 @@ -1023,10 +1024,14 @@ static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst, "%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) + if (vaapi_format_map[i].fourcc == va_fourcc) { va_rt_format = vaapi_format_map[i].rt_format; + break; + } } + av_assert0(i < FF_ARRAY_ELEMS(vaapi_format_map)); + buffer_handle = desc->objects[0].fd; buffer_desc.pixel_format = va_fourcc; buffer_desc.width = src_fc->width; @@ -1073,8 +1078,9 @@ static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst, return 0; } -static void vaapi_unmap_to_drm(AVHWFramesContext *dst_fc, - HWMapDescriptor *hwmap) +#if VA_CHECK_VERSION(1, 1, 0) +static void vaapi_unmap_to_drm_esh(AVHWFramesContext *hwfc, + HWMapDescriptor *hwmap) { AVDRMFrameDescriptor *drm_desc = hwmap->priv; int i; @@ -1085,24 +1091,28 @@ static void vaapi_unmap_to_drm(AVHWFramesContext *dst_fc, av_freep(&drm_desc); } -static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst, - const AVFrame *src, int flags) +static int vaapi_map_to_drm_esh(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) { -#if VA_CHECK_VERSION(1, 1, 0) AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx; VASurfaceID surface_id; VAStatus vas; VADRMPRIMESurfaceDescriptor va_desc; AVDRMFrameDescriptor *drm_desc = NULL; + uint32_t export_flags; int err, i, j; surface_id = (VASurfaceID)(uintptr_t)src->data[3]; + export_flags = VA_EXPORT_SURFACE_SEPARATE_LAYERS; + if (flags & AV_HWFRAME_MAP_READ) + export_flags |= VA_EXPORT_SURFACE_READ_ONLY; + if (flags & AV_HWFRAME_MAP_WRITE) + export_flags |= VA_EXPORT_SURFACE_WRITE_ONLY; + 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); + export_flags, &va_desc); if (vas != VA_STATUS_SUCCESS) { if (vas == VA_STATUS_ERROR_UNIMPLEMENTED) return AVERROR(ENOSYS); @@ -1140,7 +1150,7 @@ static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst, } err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, - &vaapi_unmap_to_drm, drm_desc); + &vaapi_unmap_to_drm_esh, drm_desc); if (err < 0) goto fail; @@ -1155,16 +1165,188 @@ fail: 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 + +#if VA_CHECK_VERSION(0, 36, 0) +typedef struct VAAPIDRMImageBufferMapping { + VAImage image; + VABufferInfo buffer_info; + + AVDRMFrameDescriptor drm_desc; +} VAAPIDRMImageBufferMapping; + +static void vaapi_unmap_to_drm_abh(AVHWFramesContext *hwfc, + HWMapDescriptor *hwmap) +{ + AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx; + VAAPIDRMImageBufferMapping *mapping = hwmap->priv; + VASurfaceID surface_id; + VAStatus vas; + + surface_id = (VASurfaceID)(uintptr_t)hwmap->source->data[3]; + av_log(hwfc, AV_LOG_DEBUG, "Unmap VAAPI surface %#x from DRM.\n", + surface_id); + + // DRM PRIME file descriptors are closed by vaReleaseBufferHandle(), + // so we shouldn't close them separately. + + vas = vaReleaseBufferHandle(hwctx->display, mapping->image.buf); + if (vas != VA_STATUS_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to release buffer " + "handle of image %#x (derived from surface %#x): " + "%d (%s).\n", mapping->image.buf, surface_id, + vas, vaErrorStr(vas)); + } + + vas = vaDestroyImage(hwctx->display, mapping->image.image_id); + if (vas != VA_STATUS_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to destroy image " + "derived from surface %#x: %d (%s).\n", + surface_id, vas, vaErrorStr(vas)); + } + + av_free(mapping); +} + +static int vaapi_map_to_drm_abh(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ + AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx; + VAAPIDRMImageBufferMapping *mapping = NULL; + VASurfaceID surface_id; + VAStatus vas; + int err, i, p; + + surface_id = (VASurfaceID)(uintptr_t)src->data[3]; + av_log(hwfc, AV_LOG_DEBUG, "Map VAAPI surface %#x to DRM.\n", + surface_id); + + mapping = av_mallocz(sizeof(*mapping)); + if (!mapping) + return AVERROR(ENOMEM); + + vas = vaDeriveImage(hwctx->display, surface_id, + &mapping->image); + if (vas != VA_STATUS_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to derive image from " + "surface %#x: %d (%s).\n", + surface_id, vas, vaErrorStr(vas)); + err = AVERROR(EIO); + goto fail; + } + + for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) { + if (vaapi_drm_format_map[i].va_fourcc == + mapping->image.format.fourcc) + break; + } + if (i >= FF_ARRAY_ELEMS(vaapi_drm_format_map)) { + av_log(hwfc, AV_LOG_ERROR, "No matching DRM format for " + "VAAPI format %#x.\n", mapping->image.format.fourcc); + err = AVERROR(EINVAL); + goto fail_derived; + } + + mapping->buffer_info.mem_type = + VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; + + mapping->drm_desc.nb_layers = + vaapi_drm_format_map[i].nb_layer_formats; + if (mapping->drm_desc.nb_layers > 1) { + if (mapping->drm_desc.nb_layers != mapping->image.num_planes) { + av_log(hwfc, AV_LOG_ERROR, "Image properties do not match " + "expected format: got %d planes, but expected %d.\n", + mapping->image.num_planes, mapping->drm_desc.nb_layers); + err = AVERROR(EINVAL); + goto fail_derived; + } + + for(p = 0; p < mapping->drm_desc.nb_layers; p++) { + mapping->drm_desc.layers[p] = (AVDRMLayerDescriptor) { + .format = vaapi_drm_format_map[i].layer_formats[p], + .nb_planes = 1, + .planes[0] = { + .object_index = 0, + .offset = mapping->image.offsets[p], + .pitch = mapping->image.pitches[p], + }, + }; + } + } else { + mapping->drm_desc.layers[0].format = + vaapi_drm_format_map[i].layer_formats[0]; + mapping->drm_desc.layers[0].nb_planes = mapping->image.num_planes; + for (p = 0; p < mapping->image.num_planes; p++) { + mapping->drm_desc.layers[0].planes[p] = (AVDRMPlaneDescriptor) { + .object_index = 0, + .offset = mapping->image.offsets[p], + .pitch = mapping->image.pitches[p], + }; + } + } + + vas = vaAcquireBufferHandle(hwctx->display, mapping->image.buf, + &mapping->buffer_info); + if (vas != VA_STATUS_SUCCESS) { + av_log(hwfc, AV_LOG_ERROR, "Failed to get buffer " + "handle from image %#x (derived from surface %#x): " + "%d (%s).\n", mapping->image.buf, surface_id, + vas, vaErrorStr(vas)); + err = AVERROR(EIO); + goto fail_derived; + } + + av_log(hwfc, AV_LOG_DEBUG, "DRM PRIME fd is %ld.\n", + mapping->buffer_info.handle); + + mapping->drm_desc.nb_objects = 1; + mapping->drm_desc.objects[0] = (AVDRMObjectDescriptor) { + .fd = mapping->buffer_info.handle, + .size = mapping->image.data_size, + // There is no way to get the format modifier with this API. + .format_modifier = DRM_FORMAT_MOD_INVALID, + }; + + err = ff_hwframe_map_create(src->hw_frames_ctx, + dst, src, &vaapi_unmap_to_drm_abh, + mapping); + if (err < 0) + goto fail_mapped; + + dst->data[0] = (uint8_t*)&mapping->drm_desc; + dst->width = src->width; + dst->height = src->height; + + return 0; + +fail_mapped: + vaReleaseBufferHandle(hwctx->display, mapping->image.buf); +fail_derived: + vaDestroyImage(hwctx->display, mapping->image.image_id); +fail: + av_freep(&mapping); + return err; } #endif +static int vaapi_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ +#if VA_CHECK_VERSION(1, 1, 0) + int err; + err = vaapi_map_to_drm_esh(hwfc, dst, src, flags); + if (err != AVERROR(ENOSYS)) + return err; +#endif +#if VA_CHECK_VERSION(0, 36, 0) + return vaapi_map_to_drm_abh(hwfc, dst, src, flags); +#endif + return AVERROR(ENOSYS); +} + +#endif /* CONFIG_LIBDRM */ + static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst, const AVFrame *src, int flags) { @@ -1324,7 +1506,7 @@ static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device, static int vaapi_device_derive(AVHWDeviceContext *ctx, AVHWDeviceContext *src_ctx, int flags) { -#if CONFIG_LIBDRM +#if HAVE_VAAPI_DRM if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) { AVDRMDeviceContext *src_hwctx = src_ctx->hwctx; VADisplay *display;