diff mbox

[FFmpeg-devel,v2,2/7] hwcontext_vaapi: Add support for legacy DRM mapping

Message ID 20180321231236.3024-2-sw@jkqxz.net
State Accepted
Commit c6bbb8cca7fa72bb26d2000453460a6344f425a1
Headers show

Commit Message

Mark Thompson March 21, 2018, 11:12 p.m. UTC
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.
---
Now we set the INVALID format modifier so that a user can distinguish between ESH with linear format modifier and ABH with unknown format modifier.

It is kernel ABI and therefore fixed, but the constant needs a recent libdrm; get around that by defining it locally if it isn't present.


 libavutil/hwcontext_vaapi.c | 194 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 182 insertions(+), 12 deletions(-)

Comments

Rostislav Pehlivanov March 21, 2018, 11:32 p.m. UTC | #1
On 21 March 2018 at 23:12, Mark Thompson <sw@jkqxz.net> wrote:

> 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.
> ---
> Now we set the INVALID format modifier so that a user can distinguish
> between ESH with linear format modifier and ABH with unknown format
> modifier.
>
> It is kernel ABI and therefore fixed, but the constant needs a recent
> libdrm; get around that by defining it locally if it isn't present.
>
>
>  libavutil/hwcontext_vaapi.c | 194 ++++++++++++++++++++++++++++++
> +++++++++++---
>  1 file changed, 182 insertions(+), 12 deletions(-)
>
> diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
> index 3797005433..998e954c44 100644
> --- a/libavutil/hwcontext_vaapi.c
> +++ b/libavutil/hwcontext_vaapi.c
> @@ -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,15 +1156,182 @@ 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
> +
> +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)
> --
> 2.16.1
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>

Still a bit ugly but oh well, va/drm seem to move slow, LGTM
diff mbox

Patch

diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
index 3797005433..998e954c44 100644
--- a/libavutil/hwcontext_vaapi.c
+++ b/libavutil/hwcontext_vaapi.c
@@ -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,15 +1156,182 @@  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
+
+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)