---
The intent of this is to have a common structure which can be used in all cases 
where DRM objects need to be shared between components.  It would be helpful if 
anyone familiar with specific drivers or use-cases could ensure that the 
structure (see the hwcontext_drm.h header) is sufficiently general to cover 
them - we would like this to the one answer and never require any more formats 
in future.

Also unclear about how the libdrm dependency should actually be included.  Alot 
of this (including the header) doesn't actually require it, and it wouldn't be 
difficult to make it a build-time-only dependency by replacing a few calls with 
ioctls().

(See <https://lists.ffmpeg.org/pipermail/ffmpeg-devel/2017-June/212546.html> 
for more context.)


 configure                      |   5 +-
 libavutil/Makefile             |   1 +
 libavutil/hwcontext.c          |   4 +
 libavutil/hwcontext.h          |   1 +
 libavutil/hwcontext_drm.c      | 281 +++++++++++++++++++++++++++++++++++++++++
 libavutil/hwcontext_drm.h      | 145 +++++++++++++++++++++
 libavutil/hwcontext_internal.h |   1 +
 libavutil/pixdesc.c            |   4 +
 libavutil/pixfmt.h             |   6 +
 9 files changed, 447 insertions(+), 1 deletion(-)
 create mode 100644 libavutil/hwcontext_drm.c
 create mode 100644 libavutil/hwcontext_drm.h

diff --git a/configure b/configure
index fd879bc9d..12d831995 100755
--- a/configure
+++ b/configure
@@ -190,6 +190,7 @@ External library support:
   --enable-avisynth          video frameserver
   --enable-avxsynth          Linux version of AviSynth
   --enable-bzlib             bzip2 compression [autodetect]
+  --enable-drm               DRM buffer sharing
   --enable-frei0r            video filtering plugins
   --enable-gnutls            crypto
   --enable-libbs2b           Bauer stereophonic-to-binaural DSP
@@ -1303,6 +1304,7 @@ EXTERNAL_LIBRARY_LIST="
     $EXTERNAL_LIBRARY_VERSION3_LIST
     avisynth
     avxsynth
+    drm
     frei0r
     gnutls
     libbs2b
@@ -2547,7 +2549,7 @@ avdevice_extralibs="libm_extralibs"
 avformat_extralibs="libm_extralibs"
 avfilter_extralibs="pthreads_extralibs libm_extralibs"
 avresample_extralibs="libm_extralibs"
-avutil_extralibs="clock_gettime_extralibs cuda_extralibs libm_extralibs 
libmfx_extralibs nanosleep_extralibs pthreads_extralibs user32_extralibs 
vaapi_extralibs vaapi_drm_extralibs vaapi_x11_extralibs vdpau_x11_extralibs 
wincrypt_extralibs"
+avutil_extralibs="clock_gettime_extralibs cuda_extralibs libdrm_extralibs 
libm_extralibs libmfx_extralibs nanosleep_extralibs pthreads_extralibs 
user32_extralibs vaapi_extralibs vaapi_drm_extralibs vaapi_x11_extralibs 
vdpau_x11_extralibs wincrypt_extralibs"
 swscale_extralibs="libm_extralibs"
 
 # programs
@@ -4734,6 +4736,7 @@ done
 enabled avisynth          && require_header avisynth/avisynth_c.h
 enabled avxsynth          && require_header avxsynth/avxsynth_c.h
 enabled cuda              && require cuda cuda.h cuInit -lcuda
+enabled drm               && require_pkg_config libdrm libdrm xf86drm.h 
drmGetVersion
 enabled frei0r            && require_header frei0r.h
 enabled gnutls            && require_pkg_config gnutls gnutls gnutls/gnutls.h 
gnutls_global_init
 enabled libbs2b           && require_pkg_config libbs2b libbs2b bs2b.h 
bs2b_open
diff --git a/libavutil/Makefile b/libavutil/Makefile
index 6fb24db67..9493a0059 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -114,6 +114,7 @@ OBJS = adler32.o                                            
            \
 
 OBJS-$(CONFIG_CUDA)                     += hwcontext_cuda.o
 OBJS-$(CONFIG_D3D11VA)                  += hwcontext_d3d11va.o
+OBJS-$(CONFIG_DRM)                      += hwcontext_drm.o
 OBJS-$(CONFIG_DXVA2)                    += hwcontext_dxva2.o
 OBJS-$(CONFIG_LIBMFX)                   += hwcontext_qsv.o
 OBJS-$(CONFIG_LZO)                      += lzo.o
diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
index 6dc95bba1..e0febaf26 100644
--- a/libavutil/hwcontext.c
+++ b/libavutil/hwcontext.c
@@ -35,6 +35,9 @@ static const HWContextType * const hw_table[] = {
 #if CONFIG_D3D11VA
     &ff_hwcontext_type_d3d11va,
 #endif
+#if CONFIG_DRM
+    &ff_hwcontext_type_drm,
+#endif
 #if CONFIG_DXVA2
     &ff_hwcontext_type_dxva2,
 #endif
@@ -52,6 +55,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 203ea510e..a48b2e53c 100644
--- a/libavutil/hwcontext.h
+++ b/libavutil/hwcontext.h
@@ -32,6 +32,7 @@ enum AVHWDeviceType {
     AV_HWDEVICE_TYPE_DXVA2,
     AV_HWDEVICE_TYPE_QSV,
     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 000000000..8a156a37a
--- /dev/null
+++ b/libavutil/hwcontext_drm.c
@@ -0,0 +1,281 @@
+/*
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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 Libav; 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 "common.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.
+    void *address[AV_NUM_DATA_POINTERS];
+    size_t length[AV_NUM_DATA_POINTERS];
+} DRMMapping;
+
+static void drm_unmap_frame(AVHWFramesContext *hwfc,
+                            HWMapDescriptor *hwmap)
+{
+    DRMMapping *map = hwmap->priv;
+    int plane;
+
+    for (plane = 0; plane < FF_ARRAY_ELEMS(map->address); plane++) {
+        if (map->address[plane])
+            munmap(map->address[plane], map->length[plane]);
+    }
+
+    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;
+    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;
+
+    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);
+            // TODO: cleanup.
+            goto fail;
+        }
+
+        map->address[i] = addr;
+        map->length[i]  = desc->objects[i].size;
+    }
+
+    for (p = 0; p < desc->nb_planes; p++) {
+        dst->data[p] = (uint8_t*)map->address[desc->planes[p].object_index] +
+                           desc->planes[p].offset;
+        dst->linesize[p] = desc->planes[p].pitch;
+    }
+
+    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:
+    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 000000000..12e18d24a
--- /dev/null
+++ b/libavutil/hwcontext_drm.h
@@ -0,0 +1,145 @@
+/*
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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 Libav; 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 "frame.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.
+ */
+
+
+/**
+ * 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;
+    /**
+     * Format modifier applied to the object (DRM_FORMAT_MOD_*).
+     */
+    uint64_t format_modifier;
+    /**
+     * Total size of the object.
+     *
+     * (This includes any parts not which do not contain image data.)
+     */
+    size_t size;
+} AVDRMObjectDescriptor;
+
+/**
+ * DRM plane descriptor.
+ *
+ * Describes a single plane of an image, which is contained within
+ * single object.
+ */
+typedef struct AVDRMPlaneDescriptor {
+    /**
+     * Format of the plane (DRM_FORMAT_*).
+     *
+     * Note that this can be a multiple-plane format such as NV12:
+     * if so, this structure is only describes one plane of the
+     * image.
+     */
+    uint32_t format;
+    /**
+     * Index of this plane in within the current image.
+     *
+     * For single plane formats, this is always zero.
+     */
+    int plane_index;
+    /**
+     * 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 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.)
+ */
+typedef struct AVDRMFrameDescriptor {
+    /**
+     * Number of DRM objects making up this frame.
+     */
+    int nb_objects;
+    /**
+     * Array of objects making up the frame.
+     */
+    AVDRMObjectDescriptor objects[AV_NUM_DATA_POINTERS];
+    /**
+     * Number of planes in the frame.
+     */
+    int nb_planes;
+    /**
+     * Array of planes in the frame.
+     */
+    AVDRMPlaneDescriptor planes[AV_NUM_DATA_POINTERS];
+} 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 99612931f..dfe741c16 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 7fa6dd7c0..28015b441 100644
--- a/libavutil/pixdesc.c
+++ b/libavutil/pixdesc.c
@@ -1743,6 +1743,10 @@ static const AVPixFmtDescriptor 
av_pix_fmt_descriptors[AV_PIX_FMT_NB] = {
         .name = "d3d11",
         .flags = AV_PIX_FMT_FLAG_HWACCEL,
     },
+    [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 2ba7ad1c8..66532d403 100644
--- a/libavutil/pixfmt.h
+++ b/libavutil/pixfmt.h
@@ -249,6 +249,12 @@ enum AVPixelFormat {
      */
     AV_PIX_FMT_D3D11,
 
+    /** 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
 };
 
-- 
2.11.0

_______________________________________________
libav-devel mailing list
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to