hwcontext_vaapi: Add support for legacy DRM mapping

The old vaAcquireBufferHandle() API works in fewer cases and provides
less information than the current vaExportSurfaceHandle(), but it exists
on older versions and is already used by the OpenCL code.
This commit is contained in:
Mark Thompson 2018-03-11 21:12:29 +00:00
parent f86e8c91d0
commit c6bbb8cca7
1 changed files with 183 additions and 13 deletions

View File

@ -28,6 +28,9 @@
#if CONFIG_LIBDRM
# include <va/va_drmcommon.h>
# include <drm_fourcc.h>
# ifndef DRM_FORMAT_MOD_INVALID
# define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
# endif
#endif
#include <fcntl.h>
@ -1071,8 +1074,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;
@ -1083,10 +1087,9 @@ 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;
@ -1138,7 +1141,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;
@ -1153,16 +1156,183 @@ 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
}
#endif
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;
}
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
return vaapi_map_to_drm_abh(hwfc, dst, src, flags);
}
#endif /* CONFIG_LIBDRM */
static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
const AVFrame *src, int flags)
{