diff mbox

[FFmpeg-devel] lavu: Add DRM hwcontext

Message ID MWHPR17MB161425ECB0AFA3801223FA31B4920@MWHPR17MB1614.namprd17.prod.outlook.com
State New
Headers show

Commit Message

LongChair . Sept. 1, 2017, 3:44 p.m. UTC
From: Mark Thompson <sw@jkqxz.net>

---
 configure                      |   3 +
 libavutil/Makefile             |   2 +
 libavutil/hwcontext.c          |   4 +
 libavutil/hwcontext.h          |   1 +
 libavutil/hwcontext_drm.c      | 294 +++++++++++++++++++++++++++++++++++++++++
 libavutil/hwcontext_drm.h      | 166 +++++++++++++++++++++++
 libavutil/hwcontext_internal.h |   1 +
 libavutil/pixdesc.c            |   4 +
 libavutil/pixfmt.h             |   6 +
 9 files changed, 481 insertions(+)
 create mode 100644 libavutil/hwcontext_drm.c
 create mode 100644 libavutil/hwcontext_drm.h

Comments

wm4 Sept. 1, 2017, 4:57 p.m. UTC | #1
On Fri, 1 Sep 2017 15:44:53 +0000
"LongChair ." <LongChair@hotmail.com> wrote:

> From: Mark Thompson <sw@jkqxz.net>
> 
> ---
>  configure                      |   3 +
>  libavutil/Makefile             |   2 +
>  libavutil/hwcontext.c          |   4 +
>  libavutil/hwcontext.h          |   1 +
>  libavutil/hwcontext_drm.c      | 294 +++++++++++++++++++++++++++++++++++++++++
>  libavutil/hwcontext_drm.h      | 166 +++++++++++++++++++++++
>  libavutil/hwcontext_internal.h |   1 +
>  libavutil/pixdesc.c            |   4 +
>  libavutil/pixfmt.h             |   6 +
>  9 files changed, 481 insertions(+)
>  create mode 100644 libavutil/hwcontext_drm.c
>  create mode 100644 libavutil/hwcontext_drm.h
> 

I'd agree with that.
Jorge Ramirez-Ortiz Sept. 1, 2017, 5:48 p.m. UTC | #2
On 09/01/2017 05:44 PM, LongChair . wrote:
> +static int drm_map_frame(AVHWFramesContext *hwfc,
> +                         AVFrame *dst, const AVFrame *src, int flags)
> +{
> +    const AVDRMFrameDescriptor*desc = (AVDRMFrameDescriptor*)src->data[0];
> +    DRMMapping *map;
> +    int err, i, p, plane;
> +    int mmap_prot;
> +    void *addr;
> +
> +    map = av_mallocz(sizeof(*map));
> +    if (!map)
> +        return AVERROR(ENOMEM);
> +
> +    mmap_prot = 0;
> +    if (flags & AV_HWFRAME_MAP_READ)
> +        mmap_prot |= PROT_READ;
> +    if (flags & AV_HWFRAME_MAP_WRITE)
> +        mmap_prot |= PROT_WRITE;
> +
> +    av_assert0(desc->nb_objects <= AV_DRM_MAX_PLANES);
> +    for (i = 0; i < desc->nb_objects; i++) {
> +        addr = mmap(NULL, desc->objects[i].size, mmap_prot, MAP_SHARED,
> +                    desc->objects[i].fd, 0);
> +        if (addr == MAP_FAILED) {
> +            err = AVERROR(errno);
> +            av_log(hwfc, AV_LOG_ERROR, "Failed to map DRM object %d to "
> +                   "memory: %d.\n", desc->objects[i].fd, errno);
> +            goto fail;
> +        }
> +
> +        map->address[i] = addr;
> +        map->length[i]  = desc->objects[i].size;
> +    }
> +    map->nb_regions = i;
> +
> +    plane = 0;
> +    for (i = 0; i < desc->nb_layers; i++) {
> +        const AVDRMLayerDescriptor *layer = &desc->layers[i];
> +        for (p = 0; p < layer->nb_planes; p++) {
> +            dst->data[plane] =
> +                (uint8_t*)map->address[layer->planes[p].object_index] +
> +                                       layer->planes[p].offset;
> +            dst->linesize[plane] =     layer->planes[p].pitch;
> +            ++plane;
> +        }
> +    }
> +    av_assert0(plane <= AV_DRM_MAX_PLANES);
> +
> +    dst->width  = src->width;
> +    dst->height = src->height;
> +
> +    err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
> +                                &drm_unmap_frame, map);
> +    if (err < 0)
shouldn't we unmap and free the map as well?
> +        return err;
> +
> +    return 0;
> +
> +fail:
> +    for (i = 0; i < desc->nb_objects; i++) {
> +        if (map->address[i])
> +            munmap(map->address[i], map->length[i]);
> +    }
> +    av_free(map);
> +    return err;
> +}
Jorge Ramirez-Ortiz Sept. 1, 2017, 5:59 p.m. UTC | #3
On 09/01/2017 05:44 PM, LongChair . wrote:
> +
> +static void drm_unmap_frame(AVHWFramesContext *hwfc,
> +                            HWMapDescriptor *hwmap)
> +{
> +    DRMMapping *map = hwmap->priv;
> +    int i;
> +
> +    for (i = 0; i < map->nb_regions; i++) {
> +        if (map->address[i])
> +            munmap(map->address[i], map->length[i]);

do we need to check the return value of the call?
also if map->address[i] == NULL,  is that not a warning of something 
that went wrong?

> +    }
> +
> +    av_free(map);
> +}
Jorge Ramirez-Ortiz Sept. 1, 2017, 6:01 p.m. UTC | #4
On 09/01/2017 05:44 PM, LongChair . wrote:
> +}
> +
> +static int drm_device_create(AVHWDeviceContext *hwdev, const char *device,
> +                             AVDictionary *opts, int flags)
> +{
> +    AVDRMDeviceContext *hwctx = hwdev->hwctx;
> +    drmVersionPtr version;
> +
> +    hwctx->fd = open(device, O_RDWR);
> +    if (hwctx->fd < 0)
> +        return AVERROR_UNKNOWN;
return errno?
Jorge Ramirez-Ortiz Sept. 1, 2017, 8:05 p.m. UTC | #5
On 09/01/2017 05:44 PM, LongChair . wrote:
> + * @file
> + * API-specific header for AV_HWDEVICE_TYPE_DRM.
> + *
> + * Internal frame allocation is not currently supported - all frames
> + * must be allocated by the user.  Thus AVHWFramesContext is always
> + * NULL, though this may change if support for frame allocation is
> + * added in future.
> + */
> +
> +enum {
> +    /**
> +     * The maximum number of layers/planes in a DRM frame.
> +     */
> +    AV_DRM_MAX_PLANES = 4
> +};

why this limit?

dont you have to call drmModeGetPlaneResources(fd) then use 
planes->count_planes?
Mark Thompson Sept. 2, 2017, 2:03 p.m. UTC | #6
On 01/09/17 18:48, Jorge Ramirez wrote:
> On 09/01/2017 05:44 PM, LongChair . wrote:
>> +static int drm_map_frame(AVHWFramesContext *hwfc,
>> +                         AVFrame *dst, const AVFrame *src, int flags)
>> +{
>> +    const AVDRMFrameDescriptor*desc = (AVDRMFrameDescriptor*)src->data[0];
>> +    DRMMapping *map;
>> +    int err, i, p, plane;
>> +    int mmap_prot;
>> +    void *addr;
>> +
>> +    map = av_mallocz(sizeof(*map));
>> +    if (!map)
>> +        return AVERROR(ENOMEM);
>> +
>> +    mmap_prot = 0;
>> +    if (flags & AV_HWFRAME_MAP_READ)
>> +        mmap_prot |= PROT_READ;
>> +    if (flags & AV_HWFRAME_MAP_WRITE)
>> +        mmap_prot |= PROT_WRITE;
>> +
>> +    av_assert0(desc->nb_objects <= AV_DRM_MAX_PLANES);
>> +    for (i = 0; i < desc->nb_objects; i++) {
>> +        addr = mmap(NULL, desc->objects[i].size, mmap_prot, MAP_SHARED,
>> +                    desc->objects[i].fd, 0);
>> +        if (addr == MAP_FAILED) {
>> +            err = AVERROR(errno);
>> +            av_log(hwfc, AV_LOG_ERROR, "Failed to map DRM object %d to "
>> +                   "memory: %d.\n", desc->objects[i].fd, errno);
>> +            goto fail;
>> +        }
>> +
>> +        map->address[i] = addr;
>> +        map->length[i]  = desc->objects[i].size;
>> +    }
>> +    map->nb_regions = i;
>> +
>> +    plane = 0;
>> +    for (i = 0; i < desc->nb_layers; i++) {
>> +        const AVDRMLayerDescriptor *layer = &desc->layers[i];
>> +        for (p = 0; p < layer->nb_planes; p++) {
>> +            dst->data[plane] =
>> +                (uint8_t*)map->address[layer->planes[p].object_index] +
>> +                                       layer->planes[p].offset;
>> +            dst->linesize[plane] =     layer->planes[p].pitch;
>> +            ++plane;
>> +        }
>> +    }
>> +    av_assert0(plane <= AV_DRM_MAX_PLANES);
>> +
>> +    dst->width  = src->width;
>> +    dst->height = src->height;
>> +
>> +    err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
>> +                                &drm_unmap_frame, map);
>> +    if (err < 0)
> shouldn't we unmap and free the map as well?

Yes, fixed to goto fail as well.

>> +        return err;
>> +
>> +    return 0;
>> +
>> +fail:
>> +    for (i = 0; i < desc->nb_objects; i++) {
>> +        if (map->address[i])
>> +            munmap(map->address[i], map->length[i]);
>> +    }
>> +    av_free(map);
>> +    return err;
>> +}
> 
> 

On 01/09/17 18:59, Jorge Ramirez wrote:
> On 09/01/2017 05:44 PM, LongChair . wrote:
>> +
>> +static void drm_unmap_frame(AVHWFramesContext *hwfc,
>> +                            HWMapDescriptor *hwmap)
>> +{
>> +    DRMMapping *map = hwmap->priv;
>> +    int i;
>> +
>> +    for (i = 0; i < map->nb_regions; i++) {
>> +        if (map->address[i])
>> +            munmap(map->address[i], map->length[i]);
> 
> do we need to check the return value of the call?

And do what with it?  close(2)-type functions are easier to regard as void - I don't think there are any non-undefined-behaviour failures possible here.

> also if map->address[i] == NULL,  is that not a warning of something that went wrong?

Yeah, it appears to be impossible - will remove.  (I think that was probably copied from the cleanup unmap in map below.)

>> +    }
>> +
>> +    av_free(map);
>> +}
> 

On 01/09/17 19:01, Jorge Ramirez wrote:
> On 09/01/2017 05:44 PM, LongChair . wrote:
>> +}
>> +
>> +static int drm_device_create(AVHWDeviceContext *hwdev, const char *device,
>> +                             AVDictionary *opts, int flags)
>> +{
>> +    AVDRMDeviceContext *hwctx = hwdev->hwctx;
>> +    drmVersionPtr version;
>> +
>> +    hwctx->fd = open(device, O_RDWR);
>> +    if (hwctx->fd < 0)
>> +        return AVERROR_UNKNOWN;
> return errno?

Yep, fixed to AVERROR(errno).

On 01/09/17 21:05, Jorge Ramirez wrote:
> On 09/01/2017 05:44 PM, LongChair . wrote:
>> + * @file
>> + * API-specific header for AV_HWDEVICE_TYPE_DRM.
>> + *
>> + * Internal frame allocation is not currently supported - all frames
>> + * must be allocated by the user.  Thus AVHWFramesContext is always
>> + * NULL, though this may change if support for frame allocation is
>> + * added in future.
>> + */
>> +
>> +enum {
>> +    /**
>> +     * The maximum number of layers/planes in a DRM frame.
>> +     */
>> +    AV_DRM_MAX_PLANES = 4
>> +};
> 
> why this limit?
> 
> dont you have to call drmModeGetPlaneResources(fd) then use planes->count_planes?

No - these are image planes, not scanout planes.

The limit of four was decided on as the largest number which might be supported by DRM in the future.  Currently there are at most three image planes in any  DRM format (the three-plane YUV types), but four is certainly possible with planar YUVA or RGBA, so it seemed sensible to set four here just in case.

Thanks,

- Mark
diff mbox

Patch

diff --git a/configure b/configure
index 61d8160..ebd561d 100755
--- a/configure
+++ b/configure
@@ -300,6 +300,7 @@  External library support:
   --disable-cuvid          disable Nvidia CUVID support [autodetect]
   --disable-d3d11va        disable Microsoft Direct3D 11 video acceleration code [autodetect]
   --disable-dxva2          disable Microsoft DirectX 9 video acceleration code [autodetect]
+  --enable-libdrm          enable DRM code (Linux) [no]
   --enable-libmfx          enable Intel MediaSDK (AKA Quick Sync Video) code via libmfx [no]
   --enable-libnpp          enable Nvidia Performance Primitives-based code [no]
   --enable-mmal            enable Broadcom Multi-Media Abstraction Layer (Raspberry Pi) via MMAL [no]
@@ -1543,6 +1544,7 @@  EXTERNAL_LIBRARY_LIST="
     libcaca
     libcelt
     libdc1394
+    libdrm
     libflite
     libfontconfig
     libfreetype
@@ -5848,6 +5850,7 @@  enabled libcelt           && require libcelt celt/celt.h celt_decode -lcelt0 &&
                                die "ERROR: libcelt must be installed and version must be >= 0.11.0."; }
 enabled libcaca           && require_pkg_config caca caca.h caca_create_canvas
 enabled libdc1394         && require_pkg_config libdc1394-2 dc1394/dc1394.h dc1394_new
+enabled libdrm            && require_pkg_config libdrm xf86drm.h drmGetVersion
 enabled libfdk_aac        && { use_pkg_config fdk-aac "fdk-aac/aacenc_lib.h" aacEncOpen ||
                                { require libfdk_aac fdk-aac/aacenc_lib.h aacEncOpen -lfdk-aac &&
                                  warn "using libfdk without pkg-config"; } }
diff --git a/libavutil/Makefile b/libavutil/Makefile
index e45871f..65e285a 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -34,6 +34,7 @@  HEADERS = adler32.h                                                     \
           hwcontext.h                                                   \
           hwcontext_cuda.h                                              \
           hwcontext_d3d11va.h                                           \
+          hwcontext_drm.h                                               \
           hwcontext_dxva2.h                                             \
           hwcontext_qsv.h                                               \
           hwcontext_vaapi.h                                             \
@@ -161,6 +162,7 @@  OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
 OBJS-$(CONFIG_D3D11VA)                  += hwcontext_d3d11va.o
 OBJS-$(CONFIG_DXVA2)                    += hwcontext_dxva2.o
 OBJS-$(CONFIG_QSV)                   += hwcontext_qsv.o
+OBJS-$(CONFIG_LIBDRM)                   += hwcontext_drm.o
 OBJS-$(CONFIG_LZO)                      += lzo.o
 OBJS-$(CONFIG_OPENCL)                   += opencl.o opencl_internal.o
 OBJS-$(CONFIG_VAAPI)                    += hwcontext_vaapi.o
diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
index 2a755a6..9e15f4f 100644
--- a/libavutil/hwcontext.c
+++ b/libavutil/hwcontext.c
@@ -38,6 +38,9 @@  static const HWContextType *const hw_table[] = {
 #if CONFIG_DXVA2
     &ff_hwcontext_type_dxva2,
 #endif
+#if CONFIG_LIBDRM
+    &ff_hwcontext_type_drm,
+#endif
 #if CONFIG_QSV
     &ff_hwcontext_type_qsv,
 #endif
@@ -55,6 +58,7 @@  static const HWContextType *const hw_table[] = {
 
 static const char *const hw_type_names[] = {
     [AV_HWDEVICE_TYPE_CUDA]   = "cuda",
+    [AV_HWDEVICE_TYPE_DRM]    = "drm",
     [AV_HWDEVICE_TYPE_DXVA2]  = "dxva2",
     [AV_HWDEVICE_TYPE_D3D11VA] = "d3d11va",
     [AV_HWDEVICE_TYPE_QSV]    = "qsv",
diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
index afb0d80..03334e2 100644
--- a/libavutil/hwcontext.h
+++ b/libavutil/hwcontext.h
@@ -33,6 +33,7 @@  enum AVHWDeviceType {
     AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
     AV_HWDEVICE_TYPE_NONE,
     AV_HWDEVICE_TYPE_D3D11VA,
+    AV_HWDEVICE_TYPE_DRM,
 };
 
 typedef struct AVHWDeviceInternal AVHWDeviceInternal;
diff --git a/libavutil/hwcontext_drm.c b/libavutil/hwcontext_drm.c
new file mode 100644
index 0000000..c6be329
--- /dev/null
+++ b/libavutil/hwcontext_drm.c
@@ -0,0 +1,294 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * 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.
+ *
+ * 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#if HAVE_UNISTD_H
+#   include <unistd.h>
+#endif
+
+#include <drm.h>
+#include <xf86drm.h>
+
+#include "avassert.h"
+#include "hwcontext.h"
+#include "hwcontext_drm.h"
+#include "hwcontext_internal.h"
+#include "imgutils.h"
+
+
+static void drm_device_free(AVHWDeviceContext *hwdev)
+{
+    AVDRMDeviceContext *hwctx = hwdev->hwctx;
+
+    close(hwctx->fd);
+}
+
+static int drm_device_create(AVHWDeviceContext *hwdev, const char *device,
+                             AVDictionary *opts, int flags)
+{
+    AVDRMDeviceContext *hwctx = hwdev->hwctx;
+    drmVersionPtr version;
+
+    hwctx->fd = open(device, O_RDWR);
+    if (hwctx->fd < 0)
+        return AVERROR_UNKNOWN;
+
+    version = drmGetVersion(hwctx->fd);
+    if (!version) {
+        av_log(hwdev, AV_LOG_ERROR, "Failed to DRM version information "
+               "from %s: probably not a DRM device?\n", device);
+        close(hwctx->fd);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(hwdev, AV_LOG_VERBOSE, "Opened DRM device %s: driver %s "
+           "version %d.%d.%d.\n", device, version->name,
+           version->version_major, version->version_minor,
+           version->version_patchlevel);
+
+    drmFreeVersion(version);
+
+    hwdev->free = &drm_device_free;
+
+    return 0;
+}
+
+static int drm_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
+{
+    frame->buf[0] = av_buffer_pool_get(hwfc->pool);
+    if (!frame->buf[0])
+        return AVERROR(ENOMEM);
+
+    frame->data[0] = (uint8_t*)frame->buf[0]->data;
+
+    frame->format = AV_PIX_FMT_DRM_PRIME;
+    frame->width  = hwfc->width;
+    frame->height = hwfc->height;
+
+    return 0;
+}
+
+typedef struct DRMMapping {
+    // Address and length of each mmap()ed region.
+    int nb_regions;
+    void *address[AV_DRM_MAX_PLANES];
+    size_t length[AV_DRM_MAX_PLANES];
+} DRMMapping;
+
+static void drm_unmap_frame(AVHWFramesContext *hwfc,
+                            HWMapDescriptor *hwmap)
+{
+    DRMMapping *map = hwmap->priv;
+    int i;
+
+    for (i = 0; i < map->nb_regions; i++) {
+        if (map->address[i])
+            munmap(map->address[i], map->length[i]);
+    }
+
+    av_free(map);
+}
+
+static int drm_map_frame(AVHWFramesContext *hwfc,
+                         AVFrame *dst, const AVFrame *src, int flags)
+{
+    const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*)src->data[0];
+    DRMMapping *map;
+    int err, i, p, plane;
+    int mmap_prot;
+    void *addr;
+
+    map = av_mallocz(sizeof(*map));
+    if (!map)
+        return AVERROR(ENOMEM);
+
+    mmap_prot = 0;
+    if (flags & AV_HWFRAME_MAP_READ)
+        mmap_prot |= PROT_READ;
+    if (flags & AV_HWFRAME_MAP_WRITE)
+        mmap_prot |= PROT_WRITE;
+
+    av_assert0(desc->nb_objects <= AV_DRM_MAX_PLANES);
+    for (i = 0; i < desc->nb_objects; i++) {
+        addr = mmap(NULL, desc->objects[i].size, mmap_prot, MAP_SHARED,
+                    desc->objects[i].fd, 0);
+        if (addr == MAP_FAILED) {
+            err = AVERROR(errno);
+            av_log(hwfc, AV_LOG_ERROR, "Failed to map DRM object %d to "
+                   "memory: %d.\n", desc->objects[i].fd, errno);
+            goto fail;
+        }
+
+        map->address[i] = addr;
+        map->length[i]  = desc->objects[i].size;
+    }
+    map->nb_regions = i;
+
+    plane = 0;
+    for (i = 0; i < desc->nb_layers; i++) {
+        const AVDRMLayerDescriptor *layer = &desc->layers[i];
+        for (p = 0; p < layer->nb_planes; p++) {
+            dst->data[plane] =
+                (uint8_t*)map->address[layer->planes[p].object_index] +
+                                       layer->planes[p].offset;
+            dst->linesize[plane] =     layer->planes[p].pitch;
+            ++plane;
+        }
+    }
+    av_assert0(plane <= AV_DRM_MAX_PLANES);
+
+    dst->width  = src->width;
+    dst->height = src->height;
+
+    err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src,
+                                &drm_unmap_frame, map);
+    if (err < 0)
+        return err;
+
+    return 0;
+
+fail:
+    for (i = 0; i < desc->nb_objects; i++) {
+        if (map->address[i])
+            munmap(map->address[i], map->length[i]);
+    }
+    av_free(map);
+    return err;
+}
+
+static int drm_transfer_get_formats(AVHWFramesContext *ctx,
+                                    enum AVHWFrameTransferDirection dir,
+                                    enum AVPixelFormat **formats)
+{
+    enum AVPixelFormat *pix_fmts;
+
+    pix_fmts = av_malloc_array(2, sizeof(*pix_fmts));
+    if (!pix_fmts)
+        return AVERROR(ENOMEM);
+
+    pix_fmts[0] = ctx->sw_format;
+    pix_fmts[1] = AV_PIX_FMT_NONE;
+
+    *formats = pix_fmts;
+    return 0;
+}
+
+static int drm_transfer_data_from(AVHWFramesContext *hwfc,
+                                  AVFrame *dst, const AVFrame *src)
+{
+    AVFrame *map;
+    int err;
+
+    if (dst->width > hwfc->width || dst->height > hwfc->height)
+        return AVERROR(EINVAL);
+
+    map = av_frame_alloc();
+    if (!map)
+        return AVERROR(ENOMEM);
+    map->format = dst->format;
+
+    err = drm_map_frame(hwfc, map, src, AV_HWFRAME_MAP_READ);
+    if (err)
+        goto fail;
+
+    map->width  = dst->width;
+    map->height = dst->height;
+
+    err = av_frame_copy(dst, map);
+    if (err)
+        goto fail;
+
+    err = 0;
+fail:
+    av_frame_free(&map);
+    return err;
+}
+
+static int drm_transfer_data_to(AVHWFramesContext *hwfc,
+                                AVFrame *dst, const AVFrame *src)
+{
+    AVFrame *map;
+    int err;
+
+    if (src->width > hwfc->width || src->height > hwfc->height)
+        return AVERROR(EINVAL);
+
+    map = av_frame_alloc();
+    if (!map)
+        return AVERROR(ENOMEM);
+    map->format = src->format;
+
+    err = drm_map_frame(hwfc, map, dst, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
+    if (err)
+        goto fail;
+
+    map->width  = src->width;
+    map->height = src->height;
+
+    err = av_frame_copy(map, src);
+    if (err)
+        goto fail;
+
+    err = 0;
+fail:
+    av_frame_free(&map);
+    return err;
+}
+
+static int drm_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
+                        const AVFrame *src, int flags)
+{
+    int err;
+
+    if (hwfc->sw_format != dst->format)
+        return AVERROR(ENOSYS);
+
+    err = drm_map_frame(hwfc, dst, src, flags);
+    if (err)
+        return err;
+
+    err = av_frame_copy_props(dst, src);
+    if (err)
+        return err;
+
+    return 0;
+}
+
+const HWContextType ff_hwcontext_type_drm = {
+    .type                   = AV_HWDEVICE_TYPE_DRM,
+    .name                   = "DRM",
+
+    .device_hwctx_size      = sizeof(AVDRMDeviceContext),
+
+    .device_create          = &drm_device_create,
+
+    .frames_get_buffer      = &drm_get_buffer,
+
+    .transfer_get_formats   = &drm_transfer_get_formats,
+    .transfer_data_to       = &drm_transfer_data_to,
+    .transfer_data_from     = &drm_transfer_data_from,
+    .map_from               = &drm_map_from,
+
+    .pix_fmts = (const enum AVPixelFormat[]) {
+        AV_PIX_FMT_DRM_PRIME,
+        AV_PIX_FMT_NONE
+    },
+};
diff --git a/libavutil/hwcontext_drm.h b/libavutil/hwcontext_drm.h
new file mode 100644
index 0000000..2e22545
--- /dev/null
+++ b/libavutil/hwcontext_drm.h
@@ -0,0 +1,166 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * 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.
+ *
+ * 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVUTIL_HWCONTEXT_DRM_H
+#define AVUTIL_HWCONTEXT_DRM_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * @file
+ * API-specific header for AV_HWDEVICE_TYPE_DRM.
+ *
+ * Internal frame allocation is not currently supported - all frames
+ * must be allocated by the user.  Thus AVHWFramesContext is always
+ * NULL, though this may change if support for frame allocation is
+ * added in future.
+ */
+
+enum {
+    /**
+     * The maximum number of layers/planes in a DRM frame.
+     */
+    AV_DRM_MAX_PLANES = 4
+};
+
+/**
+ * DRM object descriptor.
+ *
+ * Describes a single DRM object, addressing it as a PRIME file
+ * descriptor.
+ */
+typedef struct AVDRMObjectDescriptor {
+    /**
+     * DRM PRIME fd for the object.
+     */
+    int fd;
+    /**
+     * Total size of the object.
+     *
+     * (This includes any parts not which do not contain image data.)
+     */
+    size_t size;
+    /**
+     * Format modifier applied to the object (DRM_FORMAT_MOD_*).
+     */
+    uint64_t format_modifier;
+} AVDRMObjectDescriptor;
+
+/**
+ * DRM plane descriptor.
+ *
+ * Describes a single plane of a layer, which is contained within
+ * a single object.
+ */
+typedef struct AVDRMPlaneDescriptor {
+    /**
+     * Index of the object containing this plane in the objects
+     * array of the enclosing frame descriptor.
+     */
+    int object_index;
+    /**
+     * Offset within that object of this plane.
+     */
+    ptrdiff_t offset;
+    /**
+     * Pitch (linesize) of this plane.
+     */
+    ptrdiff_t pitch;
+} AVDRMPlaneDescriptor;
+
+/**
+ * DRM layer descriptor.
+ *
+ * Describes a single layer within a frame.  This has the structure
+ * defined by its format, and will contain one or more planes.
+ */
+typedef struct AVDRMLayerDescriptor {
+    /**
+     * Format of the layer (DRM_FORMAT_*).
+     */
+    uint32_t format;
+    /**
+     * Number of planes in the layer.
+     *
+     * This must match the number of planes required by format.
+     */
+    int nb_planes;
+    /**
+     * Array of planes in this layer.
+     */
+    AVDRMPlaneDescriptor planes[AV_DRM_MAX_PLANES];
+} AVDRMLayerDescriptor;
+
+/**
+ * DRM frame descriptor.
+ *
+ * This is used as the data pointer for AV_PIX_FMT_DRM_PRIME frames.
+ * It is also used by user-allocated frame pools - allocating in
+ * AVHWFramesContext.pool must return AVBufferRefs which contain
+ * an object of this type.
+ *
+ * The fields of this structure should be set such it can be
+ * imported directly by EGL using the EGL_EXT_image_dma_buf_import
+ * and EGL_EXT_image_dma_buf_import_modifiers extensions.
+ * (Note that the exact layout of a particular format may vary between
+ * platforms - we only specify that the same platform should be able
+ * to import it.)
+ *
+ * The total number of planes must not exceed AV_DRM_MAX_PLANES, and
+ * the order of the planes by increasing layer index followed by
+ * increasing plane index must be the same as the order which would
+ * be used for the data pointers in the equivalent software format.
+ */
+typedef struct AVDRMFrameDescriptor {
+    /**
+     * Number of DRM objects making up this frame.
+     */
+    int nb_objects;
+    /**
+     * Array of objects making up the frame.
+     */
+    AVDRMObjectDescriptor objects[AV_DRM_MAX_PLANES];
+    /**
+     * Number of layers in the frame.
+     */
+    int nb_layers;
+    /**
+     * Array of layers in the frame.
+     */
+    AVDRMLayerDescriptor layers[AV_DRM_MAX_PLANES];
+} AVDRMFrameDescriptor;
+
+/**
+ * DRM device.
+ *
+ * Allocated as AVHWDeviceContext.hwctx.
+ */
+typedef struct AVDRMDeviceContext {
+    /**
+     * File descriptor of DRM device.
+     *
+     * This is used as the device to create frames on, and may also be
+     * used in some derivation and mapping operations.
+     *
+     * If no device is required, set to -1.
+     */
+    int fd;
+} AVDRMDeviceContext;
+
+#endif /* AVUTIL_HWCONTEXT_DRM_H */
diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h
index c4913a6..2d75d3d 100644
--- a/libavutil/hwcontext_internal.h
+++ b/libavutil/hwcontext_internal.h
@@ -159,6 +159,7 @@  int ff_hwframe_map_create(AVBufferRef *hwframe_ref,
 
 extern const HWContextType ff_hwcontext_type_cuda;
 extern const HWContextType ff_hwcontext_type_d3d11va;
+extern const HWContextType ff_hwcontext_type_drm;
 extern const HWContextType ff_hwcontext_type_dxva2;
 extern const HWContextType ff_hwcontext_type_qsv;
 extern const HWContextType ff_hwcontext_type_vaapi;
diff --git a/libavutil/pixdesc.c b/libavutil/pixdesc.c
index d45eae5..33aa2d7 100644
--- a/libavutil/pixdesc.c
+++ b/libavutil/pixdesc.c
@@ -2237,6 +2237,10 @@  static const AVPixFmtDescriptor av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
         .flags = AV_PIX_FMT_FLAG_PLANAR | AV_PIX_FMT_FLAG_ALPHA |
                  AV_PIX_FMT_FLAG_RGB | AV_PIX_FMT_FLAG_FLOAT,
     },
+    [AV_PIX_FMT_DRM_PRIME] = {
+        .name = "drm_prime",
+        .flags = AV_PIX_FMT_FLAG_HWACCEL,
+    },
 };
 #if FF_API_PLUS1_MINUS1
 FF_ENABLE_DEPRECATION_WARNINGS
diff --git a/libavutil/pixfmt.h b/libavutil/pixfmt.h
index 6dd0943..b01f706 100644
--- a/libavutil/pixfmt.h
+++ b/libavutil/pixfmt.h
@@ -333,6 +333,12 @@  enum AVPixelFormat {
     AV_PIX_FMT_GBRPF32LE,  ///< IEEE-754 single precision planar GBR 4:4:4,     96bpp, little-endian
     AV_PIX_FMT_GBRAPF32BE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, big-endian
     AV_PIX_FMT_GBRAPF32LE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, little-endian
+    /**
+     * DRM-managed buffers exposed through PRIME buffer sharing.
+     *
+     * data[0] points to an AVDRMFrameDescriptor.
+     */
+    AV_PIX_FMT_DRM_PRIME,
 
     AV_PIX_FMT_NB         ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions
 };