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

Submitted by Mark Thompson on March 11, 2018, 10:41 p.m.

Details

Message ID 20180311224156.23986-2-sw@jkqxz.net
State New
Headers show

Commit Message

Mark Thompson March 11, 2018, 10:41 p.m.
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 probably
doesn't have much use directly, but it will be used to replace the ad-hoc
OpenCL code in a following patch.
---
 libavutil/hwcontext_vaapi.c | 191 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 179 insertions(+), 12 deletions(-)

Comments

Rostislav Pehlivanov March 12, 2018, 12:01 a.m.
On 11 March 2018 at 22:41, 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.  This probably
> doesn't have much use directly, but it will be used to replace the ad-hoc
> OpenCL code in a following patch.
> ---
>  libavutil/hwcontext_vaapi.c | 191 ++++++++++++++++++++++++++++++
> +++++++++++---
>  1 file changed, 179 insertions(+), 12 deletions(-)
>
> diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
> index 99f76b9169..15f1c4d1c0 100644
> --- a/libavutil/hwcontext_vaapi.c
> +++ b/libavutil/hwcontext_vaapi.c
> @@ -1073,8 +1073,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,10 +1086,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;
> @@ -1140,7 +1140,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,15 +1155,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_NONE,
> +    };
> +
> +    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
>

Why? libva 1.1.0 is 6 years old now. It doesn't make sense to have 2 pieces
of code to do the same thing.
Mark Thompson March 12, 2018, 12:23 a.m.
On 12/03/18 00:01, Rostislav Pehlivanov wrote:
> On 11 March 2018 at 22:41, 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.  This probably
>> doesn't have much use directly, but it will be used to replace the ad-hoc
>> OpenCL code in a following patch.
>> ---
>>  libavutil/hwcontext_vaapi.c | 191 ++++++++++++++++++++++++++++++
>> +++++++++++---
>>  1 file changed, 179 insertions(+), 12 deletions(-)
>>
>> ...
> 
> Why? libva 1.1.0 is 6 years old now. It doesn't make sense to have 2 pieces
> of code to do the same thing.

VAAPI 1.1.0 != libva 1.1.0.  VAAPI 1.1.0 is in libva 2.1.0, released last month.

(This is not confusing at all.)

- Mark
Rostislav Pehlivanov March 12, 2018, 5:40 p.m.
On 12 March 2018 at 00:23, Mark Thompson <sw@jkqxz.net> wrote:

> On 12/03/18 00:01, Rostislav Pehlivanov wrote:
> > On 11 March 2018 at 22:41, 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.  This probably
> >> doesn't have much use directly, but it will be used to replace the
> ad-hoc
> >> OpenCL code in a following patch.
> >> ---
> >>  libavutil/hwcontext_vaapi.c | 191 ++++++++++++++++++++++++++++++
> >> +++++++++++---
> >>  1 file changed, 179 insertions(+), 12 deletions(-)
> >>
> >> ...
> >
> > Why? libva 1.1.0 is 6 years old now. It doesn't make sense to have 2
> pieces
> > of code to do the same thing.
>
> VAAPI 1.1.0 != libva 1.1.0.  VAAPI 1.1.0 is in libva 2.1.0, released last
> month.
>
> (This is not confusing at all.)
>
> - Mark
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>

ABH seems pretty much worse than ESH, and considering you can't get the
format modifiers makes it useless for Vulkan. I don't see why we need to
support it in addition to ESH.
Mark Thompson March 12, 2018, 10:46 p.m.
On 12/03/18 17:40, Rostislav Pehlivanov wrote:
> On 12 March 2018 at 00:23, Mark Thompson <sw@jkqxz.net> wrote:
> 
>> On 12/03/18 00:01, Rostislav Pehlivanov wrote:
>>> On 11 March 2018 at 22:41, 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.  This probably
>>>> doesn't have much use directly, but it will be used to replace the
>> ad-hoc
>>>> OpenCL code in a following patch.
>>>> ---
>>>>  libavutil/hwcontext_vaapi.c | 191 ++++++++++++++++++++++++++++++
>>>> +++++++++++---
>>>>  1 file changed, 179 insertions(+), 12 deletions(-)
>>>>
>>>> ...
>>>
>>> Why? libva 1.1.0 is 6 years old now. It doesn't make sense to have 2
>> pieces
>>> of code to do the same thing.
>>
>> VAAPI 1.1.0 != libva 1.1.0.  VAAPI 1.1.0 is in libva 2.1.0, released last
>> month.
>>
>> (This is not confusing at all.)
>>
> 
> ABH seems pretty much worse than ESH, and considering you can't get the
> format modifiers makes it useless for Vulkan. I don't see why we need to
> support it in addition to ESH.

It's what it currently used by the Beignet/OpenCL interop (since it was the only thing which existed when that was written, and the format modifier isn't needed), and I don't want to break that.

Since it was only released a few weeks ago it will probably be a while before it lands in all distributions, and it isn't implemented at all by the Intel ex-blob driver yet.

- Mark
Rostislav Pehlivanov March 13, 2018, 1:52 a.m.
On 12 March 2018 at 22:46, Mark Thompson <sw@jkqxz.net> wrote:

> On 12/03/18 17:40, Rostislav Pehlivanov wrote:
> > On 12 March 2018 at 00:23, Mark Thompson <sw@jkqxz.net> wrote:
> >
> >> On 12/03/18 00:01, Rostislav Pehlivanov wrote:
> >>> On 11 March 2018 at 22:41, 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.  This
> probably
> >>>> doesn't have much use directly, but it will be used to replace the
> >> ad-hoc
> >>>> OpenCL code in a following patch.
> >>>> ---
> >>>>  libavutil/hwcontext_vaapi.c | 191 ++++++++++++++++++++++++++++++
> >>>> +++++++++++---
> >>>>  1 file changed, 179 insertions(+), 12 deletions(-)
> >>>>
> >>>> ...
> >>>
> >>> Why? libva 1.1.0 is 6 years old now. It doesn't make sense to have 2
> >> pieces
> >>> of code to do the same thing.
> >>
> >> VAAPI 1.1.0 != libva 1.1.0.  VAAPI 1.1.0 is in libva 2.1.0, released
> last
> >> month.
> >>
> >> (This is not confusing at all.)
> >>
> >
> > ABH seems pretty much worse than ESH, and considering you can't get the
> > format modifiers makes it useless for Vulkan. I don't see why we need to
> > support it in addition to ESH.
>
> It's what it currently used by the Beignet/OpenCL interop (since it was
> the only thing which existed when that was written, and the format modifier
> isn't needed), and I don't want to break that.
>
> Since it was only released a few weeks ago it will probably be a while
> before it lands in all distributions, and it isn't implemented at all by
> the Intel ex-blob driver yet.
>
> - Mark
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
>
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>

In this case wouldn't it be better if the ABH mapping function is moved to
hwcontext_opencl? Then hwcontext_vaapi can be trusted to always produce
completely mapped to DRM frames.

Patch hide | download patch | download mbox

diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
index 99f76b9169..15f1c4d1c0 100644
--- a/libavutil/hwcontext_vaapi.c
+++ b/libavutil/hwcontext_vaapi.c
@@ -1073,8 +1073,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,10 +1086,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;
@@ -1140,7 +1140,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,15 +1155,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_NONE,
+    };
+
+    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)