Discussion:
[FFmpeg-devel] [PATCH 1/5] lavu: Add DRM hwcontext
Mark Thompson
2017-09-07 21:56:15 UTC
Permalink
---
Unchanged.


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

diff --git a/configure b/configure
index 2f3fa2ba3d..6581c53c1a 100755
--- a/configure
+++ b/configure
@@ -304,6 +304,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]
@@ -1552,6 +1553,7 @@ EXTERNAL_LIBRARY_LIST="
libcaca
libcelt
libdc1394
+ libdrm
libflite
libfontconfig
libfreetype
@@ -5856,6 +5858,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 e45871fd11..65e285a701 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 2a755a6878..aa6b3ad176 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_LIBDRM
+ &ff_hwcontext_type_drm,
+#endif
#if CONFIG_DXVA2
&ff_hwcontext_type_dxva2,
#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 afb0d80d59..03334e20e0 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 0000000000..e7be9abd4c
--- /dev/null
+++ b/libavutil/hwcontext_drm.c
@@ -0,0 +1,291 @@
+/*
+ * 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>
+#include <unistd.h>
+
+#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(errno);
+
+ version = drmGetVersion(hwctx->fd);
+ if (!version) {
+ av_log(hwdev, AV_LOG_ERROR, "Failed to get 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++)
+ 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)
+ goto fail;
+
+ 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 0000000000..2e225451e1
--- /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 c4913a649f..2d75d3db08 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 d45eae5772..33aa2d705f 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 6dd094376f..24889c8e52 100644
--- a/libavutil/pixfmt.h
+++ b/libavutil/pixfmt.h
@@ -334,6 +334,13 @@ enum AVPixelFormat {
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
};
--
2.11.0
Mark Thompson
2017-09-07 21:56:16 UTC
Permalink
---
Unchanged.


libavutil/hwcontext_vaapi.c | 215 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 214 insertions(+), 1 deletion(-)

diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
index 3970726d30..c69db39238 100644
--- a/libavutil/hwcontext_vaapi.c
+++ b/libavutil/hwcontext_vaapi.c
@@ -25,6 +25,11 @@
# include <va/va_drm.h>
#endif

+#if CONFIG_LIBDRM
+# include <va/va_drmcommon.h>
+# include <drm_fourcc.h>
+#endif
+
#include <fcntl.h>
#if HAVE_UNISTD_H
# include <unistd.h>
@@ -41,6 +46,10 @@
#include "pixdesc.h"
#include "pixfmt.h"

+#if CONFIG_LIBDRM
+# include "hwcontext_drm.h"
+#endif
+
typedef struct VAAPIDevicePriv {
#if HAVE_VAAPI_X11
Display *x11_display;
@@ -897,6 +906,170 @@ static int vaapi_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
return 0;
}

+#if CONFIG_LIBDRM
+
+#define DRM_MAP(va, layers, ...) { \
+ VA_FOURCC_ ## va, \
+ layers, \
+ { __VA_ARGS__ } \
+ }
+static const struct {
+ uint32_t va_fourcc;
+ int nb_layer_formats;
+ uint32_t layer_formats[AV_DRM_MAX_PLANES];
+} vaapi_drm_format_map[] = {
+ DRM_MAP(NV12, 2, DRM_FORMAT_R8, DRM_FORMAT_RG88),
+ DRM_MAP(NV12, 1, DRM_FORMAT_NV12),
+#ifdef VA_FOURCC_P010
+ DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
+#endif
+ DRM_MAP(BGRA, 1, DRM_FORMAT_BGRA8888),
+ DRM_MAP(BGRX, 1, DRM_FORMAT_BGRX8888),
+ DRM_MAP(RGBA, 1, DRM_FORMAT_RGBA8888),
+ DRM_MAP(RGBX, 1, DRM_FORMAT_RGBX8888),
+#ifdef VA_FOURCC_ABGR
+ DRM_MAP(ABGR, 1, DRM_FORMAT_ABGR8888),
+ DRM_MAP(XBGR, 1, DRM_FORMAT_XBGR8888),
+#endif
+ DRM_MAP(ARGB, 1, DRM_FORMAT_ARGB8888),
+ DRM_MAP(XRGB, 1, DRM_FORMAT_XRGB8888),
+};
+#undef DRM_MAP
+
+static void vaapi_unmap_from_drm(AVHWFramesContext *dst_fc,
+ HWMapDescriptor *hwmap)
+{
+ AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
+
+ VASurfaceID surface_id = (VASurfaceID)(uintptr_t)hwmap->priv;
+
+ vaDestroySurfaces(dst_dev->display, &surface_id, 1);
+}
+
+static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
+ const AVFrame *src, int flags)
+{
+ AVHWFramesContext *dst_fc =
+ (AVHWFramesContext*)dst->hw_frames_ctx->data;
+ AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
+ const AVDRMFrameDescriptor *desc;
+ VASurfaceID surface_id;
+ VAStatus vas;
+ uint32_t va_fourcc, va_rt_format;
+ int err, i, j, k;
+
+ unsigned long buffer_handle;
+ VASurfaceAttribExternalBuffers buffer_desc;
+ VASurfaceAttrib attrs[2] = {
+ {
+ .type = VASurfaceAttribMemoryType,
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
+ .value.type = VAGenericValueTypeInteger,
+ .value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
+ },
+ {
+ .type = VASurfaceAttribExternalBufferDescriptor,
+ .flags = VA_SURFACE_ATTRIB_SETTABLE,
+ .value.type = VAGenericValueTypePointer,
+ .value.value.p = &buffer_desc,
+ }
+ };
+
+ desc = (AVDRMFrameDescriptor*)src->data[0];
+
+ if (desc->nb_objects != 1) {
+ av_log(dst_fc, AV_LOG_ERROR, "VAAPI can only map frames "
+ "made from a single DRM object.\n");
+ return AVERROR(EINVAL);
+ }
+
+ va_fourcc = 0;
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_drm_format_map); i++) {
+ if (desc->nb_layers != vaapi_drm_format_map[i].nb_layer_formats)
+ continue;
+ for (j = 0; j < desc->nb_layers; j++) {
+ if (desc->layers[j].format !=
+ vaapi_drm_format_map[i].layer_formats[j])
+ break;
+ }
+ if (j != desc->nb_layers)
+ continue;
+ va_fourcc = vaapi_drm_format_map[i].va_fourcc;
+ break;
+ }
+ if (!va_fourcc) {
+ av_log(dst_fc, AV_LOG_ERROR, "DRM format not supported "
+ "by VAAPI.\n");
+ return AVERROR(EINVAL);
+ }
+
+ av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
+ "%08x.\n", desc->objects[0].fd, va_fourcc);
+
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
+ if (vaapi_format_map[i].fourcc == va_fourcc)
+ va_rt_format = vaapi_format_map[i].rt_format;
+ }
+
+ buffer_handle = desc->objects[0].fd;
+ buffer_desc.pixel_format = va_fourcc;
+ buffer_desc.width = src_fc->width;
+ buffer_desc.height = src_fc->height;
+ buffer_desc.data_size = desc->objects[0].size;
+ buffer_desc.buffers = &buffer_handle;
+ buffer_desc.num_buffers = 1;
+ buffer_desc.flags = 0;
+
+ k = 0;
+ for (i = 0; i < desc->nb_layers; i++) {
+ for (j = 0; j < desc->layers[i].nb_planes; j++) {
+ buffer_desc.pitches[k] = desc->layers[i].planes[j].pitch;
+ buffer_desc.offsets[k] = desc->layers[i].planes[j].offset;
+ ++k;
+ }
+ }
+ buffer_desc.num_planes = k;
+
+ vas = vaCreateSurfaces(dst_dev->display, va_rt_format,
+ src->width, src->height,
+ &surface_id, 1,
+ attrs, FF_ARRAY_ELEMS(attrs));
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(dst_fc, AV_LOG_ERROR, "Failed to create surface from DRM "
+ "object: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR(EIO);
+ }
+
+ err = ff_hwframe_map_create(dst->hw_frames_ctx, dst, src,
+ &vaapi_unmap_from_drm,
+ (void*)(uintptr_t)surface_id);
+ if (err < 0)
+ return err;
+
+ dst->width = src->width;
+ dst->height = src->height;
+ dst->data[3] = (uint8_t*)(uintptr_t)surface_id;
+
+ av_log(dst_fc, AV_LOG_DEBUG, "Mapped DRM object %d to "
+ "surface %#x.\n", desc->objects[0].fd, surface_id);
+
+ return 0;
+}
+#endif
+
+static int vaapi_map_to(AVHWFramesContext *hwfc, AVFrame *dst,
+ const AVFrame *src, int flags)
+{
+ switch (src->format) {
+#if CONFIG_LIBDRM
+ case AV_PIX_FMT_DRM_PRIME:
+ return vaapi_map_from_drm(hwfc, dst, src, flags);
+#endif
+ default:
+ return AVERROR(ENOSYS);
+ }
+}
+
static void vaapi_device_free(AVHWDeviceContext *ctx)
{
AVVAAPIDeviceContext *hwctx = ctx->hwctx;
@@ -999,6 +1172,45 @@ static int vaapi_device_create(AVHWDeviceContext *ctx, const char *device,
return 0;
}

+static int vaapi_device_derive(AVHWDeviceContext *ctx,
+ AVHWDeviceContext *src_ctx, int flags)
+{
+#if CONFIG_LIBDRM
+ if (src_ctx->type == AV_HWDEVICE_TYPE_DRM) {
+ AVDRMDeviceContext *src_hwctx = src_ctx->hwctx;
+ AVVAAPIDeviceContext *hwctx = ctx->hwctx;
+ VADisplay *display;
+ VAStatus vas;
+ int major, minor;
+
+ if (src_hwctx->fd < 0) {
+ av_log(ctx, AV_LOG_ERROR, "DRM instance requires an associated "
+ "device to derive a VA display from.\n");
+ return AVERROR(EINVAL);
+ }
+
+ display = vaGetDisplayDRM(src_hwctx->fd);
+ if (!display) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to open a VA display from "
+ "DRM device.\n");
+ return AVERROR(EIO);
+ }
+
+ vas = vaInitialize(display, &major, &minor);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI "
+ "connection: %d (%s).\n", vas, vaErrorStr(vas));
+ vaTerminate(display);
+ return AVERROR(EIO);
+ }
+
+ hwctx->display = display;
+ return 0;
+ }
+#endif
+ return AVERROR(ENOSYS);
+}
+
const HWContextType ff_hwcontext_type_vaapi = {
.type = AV_HWDEVICE_TYPE_VAAPI,
.name = "VAAPI",
@@ -1010,6 +1222,7 @@ const HWContextType ff_hwcontext_type_vaapi = {
.frames_priv_size = sizeof(VAAPIFramesContext),

.device_create = &vaapi_device_create,
+ .device_derive = &vaapi_device_derive,
.device_init = &vaapi_device_init,
.device_uninit = &vaapi_device_uninit,
.frames_get_constraints = &vaapi_frames_get_constraints,
@@ -1019,7 +1232,7 @@ const HWContextType ff_hwcontext_type_vaapi = {
.transfer_get_formats = &vaapi_transfer_get_formats,
.transfer_data_to = &vaapi_transfer_data_to,
.transfer_data_from = &vaapi_transfer_data_from,
- .map_to = NULL,
+ .map_to = &vaapi_map_to,
.map_from = &vaapi_map_from,

.pix_fmts = (const enum AVPixelFormat[]) {
--
2.11.0
Mark Thompson
2017-09-07 21:56:17 UTC
Permalink
This post might be inappropriate. Click to display it.
Mark Thompson
2017-09-07 21:56:18 UTC
Permalink
Intended for use with hardware frames for which rawvideo is not
sufficient. Requires the trusted packet flag to be set - decoding
fails if not to avoid security issues (the wrapped AVFrame can
contain pointers to arbitrary data).
---
Now uses the trusted packet flag, rejects the packet if not set.


libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 2 +-
libavcodec/wrapped_avframe.c | 36 ++++++++++++++++++++++++++++++++++++
3 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 999632cf9e..943e5db511 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -653,6 +653,7 @@ OBJS-$(CONFIG_WMV2_DECODER) += wmv2dec.o wmv2.o wmv2data.o \
OBJS-$(CONFIG_WMV2_ENCODER) += wmv2enc.o wmv2.o wmv2data.o \
msmpeg4.o msmpeg4enc.o msmpeg4data.o
OBJS-$(CONFIG_WNV1_DECODER) += wnv1.o
+OBJS-$(CONFIG_WRAPPED_AVFRAME_DECODER) += wrapped_avframe.o
OBJS-$(CONFIG_WRAPPED_AVFRAME_ENCODER) += wrapped_avframe.o
OBJS-$(CONFIG_WS_SND1_DECODER) += ws-snd1.o
OBJS-$(CONFIG_XAN_DPCM_DECODER) += dpcm.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index ce0bc7ecf3..625720578f 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -377,7 +377,7 @@ static void register_all(void)
REGISTER_DECODER(VQA, vqa);
REGISTER_DECODER(BITPACKED, bitpacked);
REGISTER_DECODER(WEBP, webp);
- REGISTER_ENCODER(WRAPPED_AVFRAME, wrapped_avframe);
+ REGISTER_ENCDEC (WRAPPED_AVFRAME, wrapped_avframe);
REGISTER_ENCDEC (WMV1, wmv1);
REGISTER_ENCDEC (WMV2, wmv2);
REGISTER_DECODER(WMV3, wmv3);
diff --git a/libavcodec/wrapped_avframe.c b/libavcodec/wrapped_avframe.c
index 14360320ff..5f88a668b9 100644
--- a/libavcodec/wrapped_avframe.c
+++ b/libavcodec/wrapped_avframe.c
@@ -75,6 +75,33 @@ static int wrapped_avframe_encode(AVCodecContext *avctx, AVPacket *pkt,
return 0;
}

+static int wrapped_avframe_decode(AVCodecContext *avctx, void *data,
+ int *got_frame, AVPacket *pkt)
+{
+ AVFrame *in, *out;
+ int err;
+
+ if (!(pkt->flags & AV_PKT_FLAG_TRUSTED)) {
+ // This decoder is not usable with untrusted input.
+ return AVERROR(EPERM);
+ }
+
+ if (pkt->size < sizeof(AVFrame))
+ return AVERROR(EINVAL);
+
+ in = (AVFrame*)pkt->data;
+ out = data;
+
+ err = ff_decode_frame_props(avctx, out);
+ if (err < 0)
+ return err;
+
+ av_frame_move_ref(out, in);
+
+ *got_frame = 1;
+ return 0;
+}
+
AVCodec ff_wrapped_avframe_encoder = {
.name = "wrapped_avframe",
.long_name = NULL_IF_CONFIG_SMALL("AVFrame to AVPacket passthrough"),
@@ -83,3 +110,12 @@ AVCodec ff_wrapped_avframe_encoder = {
.encode2 = wrapped_avframe_encode,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
};
+
+AVCodec ff_wrapped_avframe_decoder = {
+ .name = "wrapped_avframe",
+ .long_name = NULL_IF_CONFIG_SMALL("AVPacket to AVFrame passthrough"),
+ .type = AVMEDIA_TYPE_VIDEO,
+ .id = AV_CODEC_ID_WRAPPED_AVFRAME,
+ .decode = wrapped_avframe_decode,
+ .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
+};
--
2.11.0
Mark Thompson
2017-09-07 21:56:19 UTC
Permalink
---
Now sets the trusted packet flag; otherwise unchanged.


configure | 1 +
libavdevice/Makefile | 1 +
libavdevice/alldevices.c | 1 +
libavdevice/kmsgrab.c | 455 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 458 insertions(+)
create mode 100644 libavdevice/kmsgrab.c

diff --git a/configure b/configure
index 6581c53c1a..76a7591ceb 100755
--- a/configure
+++ b/configure
@@ -3040,6 +3040,7 @@ gdigrab_indev_select="bmp_decoder"
iec61883_indev_deps="libiec61883"
jack_indev_deps="jack"
jack_indev_deps_any="sem_timedwait dispatch_dispatch_h"
+kmsgrab_indev_deps="libdrm"
lavfi_indev_deps="avfilter"
libcdio_indev_deps="libcdio"
libdc1394_indev_deps="libdc1394"
diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index 2a27d20388..f40f4d5298 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -32,6 +32,7 @@ OBJS-$(CONFIG_FBDEV_OUTDEV) += fbdev_enc.o \
OBJS-$(CONFIG_GDIGRAB_INDEV) += gdigrab.o
OBJS-$(CONFIG_IEC61883_INDEV) += iec61883.o
OBJS-$(CONFIG_JACK_INDEV) += jack.o timefilter.o
+OBJS-$(CONFIG_KMSGRAB_INDEV) += kmsgrab.o
OBJS-$(CONFIG_LAVFI_INDEV) += lavfi.o
OBJS-$(CONFIG_OPENAL_INDEV) += openal-dec.o
OBJS-$(CONFIG_OPENGL_OUTDEV) += opengl_enc.o
diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
index 38010e288a..b31558bcb5 100644
--- a/libavdevice/alldevices.c
+++ b/libavdevice/alldevices.c
@@ -53,6 +53,7 @@ static void register_all(void)
REGISTER_INDEV (GDIGRAB, gdigrab);
REGISTER_INDEV (IEC61883, iec61883);
REGISTER_INDEV (JACK, jack);
+ REGISTER_INDEV (KMSGRAB, kmsgrab);
REGISTER_INDEV (LAVFI, lavfi);
REGISTER_INDEV (OPENAL, openal);
REGISTER_OUTDEV (OPENGL, opengl);
diff --git a/libavdevice/kmsgrab.c b/libavdevice/kmsgrab.c
new file mode 100644
index 0000000000..d0b9cf5001
--- /dev/null
+++ b/libavdevice/kmsgrab.c
@@ -0,0 +1,455 @@
+/*
+ * KMS/DRM input device
+ *
+ * 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 <fcntl.h>
+#include <unistd.h>
+
+#include <drm.h>
+#include <drm_fourcc.h>
+#include <drm_mode.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "libavutil/hwcontext.h"
+#include "libavutil/hwcontext_drm.h"
+#include "libavutil/internal.h"
+#include "libavutil/mathematics.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixfmt.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/time.h"
+
+#include "libavformat/avformat.h"
+#include "libavformat/internal.h"
+
+typedef struct KMSGrabContext {
+ const AVClass *class;
+
+ AVBufferRef *device_ref;
+ AVHWDeviceContext *device;
+ AVDRMDeviceContext *hwctx;
+
+ AVBufferRef *frames_ref;
+ AVHWFramesContext *frames;
+
+ uint32_t plane_id;
+ uint32_t drm_format;
+ unsigned int width;
+ unsigned int height;
+
+ int64_t frame_delay;
+ int64_t frame_last;
+
+ const char *device_path;
+ enum AVPixelFormat format;
+ int64_t drm_format_modifier;
+ int64_t source_plane;
+ int64_t source_crtc;
+ AVRational framerate;
+} KMSGrabContext;
+
+static void kmsgrab_free_desc(void *opaque, uint8_t *data)
+{
+ AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*)data;
+
+ close(desc->objects[0].fd);
+
+ av_free(desc);
+}
+
+static void kmsgrab_free_frame(void *opaque, uint8_t *data)
+{
+ AVFrame *frame = (AVFrame*)data;
+
+ av_frame_free(&frame);
+}
+
+static int kmsgrab_read_packet(AVFormatContext *avctx, AVPacket *pkt)
+{
+ KMSGrabContext *ctx = avctx->priv_data;
+ drmModePlane *plane;
+ drmModeFB *fb;
+ AVDRMFrameDescriptor *desc;
+ AVFrame *frame;
+ int64_t now;
+ int err, fd;
+
+ now = av_gettime();
+ if (ctx->frame_last) {
+ int64_t delay;
+ while (1) {
+ delay = ctx->frame_last + ctx->frame_delay - now;
+ if (delay <= 0)
+ break;
+ av_usleep(delay);
+ now = av_gettime();
+ }
+ }
+ ctx->frame_last = now;
+
+ plane = drmModeGetPlane(ctx->hwctx->fd, ctx->plane_id);
+ if (!plane) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to get plane "
+ "%"PRIu32".\n", ctx->plane_id);
+ return AVERROR(EIO);
+ }
+ if (!plane->fb_id) {
+ av_log(avctx, AV_LOG_ERROR, "Plane %"PRIu32" no longer has "
+ "an associated framebuffer.\n", ctx->plane_id);
+ return AVERROR(EIO);
+ }
+
+ fb = drmModeGetFB(ctx->hwctx->fd, plane->fb_id);
+ if (!fb) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to get framebuffer "
+ "%"PRIu32".\n", plane->fb_id);
+ return AVERROR(EIO);
+ }
+ if (fb->width != ctx->width || fb->height != ctx->height) {
+ av_log(avctx, AV_LOG_ERROR, "Plane %"PRIu32" framebuffer "
+ "dimensions changed: now %"PRIu32"x%"PRIu32".\n",
+ ctx->plane_id, fb->width, fb->height);
+ return AVERROR(EIO);
+ }
+ if (!fb->handle) {
+ av_log(avctx, AV_LOG_ERROR, "No handle set on framebuffer.\n");
+ return AVERROR(EIO);
+ }
+
+ err = drmPrimeHandleToFD(ctx->hwctx->fd, fb->handle, O_RDONLY, &fd);
+ if (err < 0) {
+ err = errno;
+ av_log(avctx, AV_LOG_ERROR, "Failed to get PRIME fd from "
+ "framebuffer handle: %s.\n", strerror(errno));
+ return AVERROR(err);
+ }
+
+ desc = av_mallocz(sizeof(*desc));
+ if (!desc)
+ return AVERROR(ENOMEM);
+
+ *desc = (AVDRMFrameDescriptor) {
+ .nb_objects = 1,
+ .objects[0] = {
+ .fd = fd,
+ .size = fb->height * fb->pitch,
+ .format_modifier = ctx->drm_format_modifier,
+ },
+ .nb_layers = 1,
+ .layers[0] = {
+ .format = ctx->drm_format,
+ .nb_planes = 1,
+ .planes[0] = {
+ .object_index = 0,
+ .offset = 0,
+ .pitch = fb->pitch,
+ },
+ },
+ };
+
+ frame = av_frame_alloc();
+ if (!frame)
+ return AVERROR(ENOMEM);
+
+ frame->hw_frames_ctx = av_buffer_ref(ctx->frames_ref);
+ if (!frame->hw_frames_ctx)
+ return AVERROR(ENOMEM);
+
+ frame->buf[0] = av_buffer_create((uint8_t*)desc, sizeof(*desc),
+ &kmsgrab_free_desc, avctx, 0);
+ if (!frame->buf[0])
+ return AVERROR(ENOMEM);
+
+ frame->data[0] = (uint8_t*)desc;
+ frame->format = AV_PIX_FMT_DRM_PRIME;
+ frame->width = fb->width;
+ frame->height = fb->height;
+
+ drmModeFreeFB(fb);
+ drmModeFreePlane(plane);
+
+ pkt->buf = av_buffer_create((uint8_t*)frame, sizeof(*frame),
+ &kmsgrab_free_frame, avctx, 0);
+ if (!pkt->buf)
+ return AVERROR(ENOMEM);
+
+ pkt->data = (uint8_t*)frame;
+ pkt->size = sizeof(*frame);
+ pkt->pts = now;
+ pkt->flags = AV_PKT_FLAG_TRUSTED;
+
+ return 0;
+}
+
+static const struct {
+ enum AVPixelFormat pixfmt;
+ uint32_t drm_format;
+} kmsgrab_formats[] = {
+ { AV_PIX_FMT_GRAY8, DRM_FORMAT_R8 },
+ { AV_PIX_FMT_GRAY16LE, DRM_FORMAT_R16 },
+ { AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
+ { AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_YUYV422, DRM_FORMAT_YUYV },
+ { AV_PIX_FMT_YVYU422, DRM_FORMAT_YVYU },
+ { AV_PIX_FMT_UYVY422, DRM_FORMAT_UYVY },
+ { AV_PIX_FMT_NV12, DRM_FORMAT_NV12 },
+ { AV_PIX_FMT_YUV420P, DRM_FORMAT_YUV420 },
+ { AV_PIX_FMT_YUV422P, DRM_FORMAT_YUV422 },
+ { AV_PIX_FMT_YUV444P, DRM_FORMAT_YUV444 },
+};
+
+static av_cold int kmsgrab_read_header(AVFormatContext *avctx)
+{
+ KMSGrabContext *ctx = avctx->priv_data;
+ drmModePlaneRes *plane_res = NULL;
+ drmModePlane *plane = NULL;
+ drmModeFB *fb = NULL;
+ AVStream *stream;
+ int err, i;
+
+ for (i = 0; i < FF_ARRAY_ELEMS(kmsgrab_formats); i++) {
+ if (kmsgrab_formats[i].pixfmt == ctx->format) {
+ ctx->drm_format = kmsgrab_formats[i].drm_format;
+ break;
+ }
+ }
+ if (i >= FF_ARRAY_ELEMS(kmsgrab_formats)) {
+ av_log(avctx, AV_LOG_ERROR, "Unsupported format %s.\n",
+ av_get_pix_fmt_name(ctx->format));
+ return AVERROR(EINVAL);
+ }
+
+ err = av_hwdevice_ctx_create(&ctx->device_ref, AV_HWDEVICE_TYPE_DRM,
+ ctx->device_path, NULL, 0);
+ if (err < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to open DRM device.\n");
+ return err;
+ }
+ ctx->device = (AVHWDeviceContext*) ctx->device_ref->data;
+ ctx->hwctx = (AVDRMDeviceContext*)ctx->device->hwctx;
+
+ err = drmSetClientCap(ctx->hwctx->fd,
+ DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+ if (err < 0) {
+ av_log(avctx, AV_LOG_WARNING, "Failed to set universal planes "
+ "capability: primary planes will not be usable.\n");
+ }
+
+ if (ctx->source_plane > 0) {
+ plane = drmModeGetPlane(ctx->hwctx->fd, ctx->source_plane);
+ if (!plane) {
+ err = errno;
+ av_log(avctx, AV_LOG_ERROR, "Failed to get plane %"PRId64": "
+ "%s.\n", ctx->source_plane, strerror(err));
+ err = AVERROR(err);
+ goto fail;
+ }
+
+ if (plane->fb_id == 0) {
+ av_log(avctx, AV_LOG_ERROR, "Plane %"PRId64" does not have "
+ "an attached framebuffer.\n", ctx->source_plane);
+ err = AVERROR(EINVAL);
+ goto fail;
+ }
+ } else {
+ plane_res = drmModeGetPlaneResources(ctx->hwctx->fd);
+ if (!plane_res) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to get plane "
+ "resources: %s.\n", strerror(errno));
+ err = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ for (i = 0; i < plane_res->count_planes; i++) {
+ plane = drmModeGetPlane(ctx->hwctx->fd,
+ plane_res->planes[i]);
+ if (!plane) {
+ err = errno;
+ av_log(avctx, AV_LOG_VERBOSE, "Failed to get "
+ "plane %"PRIu32": %s.\n",
+ plane_res->planes[i], strerror(err));
+ continue;
+ }
+
+ av_log(avctx, AV_LOG_DEBUG, "Plane %"PRIu32": "
+ "CRTC %"PRIu32" FB %"PRIu32".\n",
+ plane->plane_id, plane->crtc_id, plane->fb_id);
+
+ if ((ctx->source_crtc > 0 &&
+ plane->crtc_id != ctx->source_crtc) ||
+ plane->fb_id == 0) {
+ // Either not connected to the target source CRTC
+ // or not active.
+ drmModeFreePlane(plane);
+ plane = NULL;
+ continue;
+ }
+
+ break;
+ }
+
+ if (i == plane_res->count_planes) {
+ if (ctx->source_crtc > 0) {
+ av_log(avctx, AV_LOG_ERROR, "No usable planes found on "
+ "CRTC %"PRId64".\n", ctx->source_crtc);
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "No usable planes found.\n");
+ }
+ err = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ av_log(avctx, AV_LOG_INFO, "Using plane %"PRIu32" to "
+ "locate framebuffers.\n", plane->plane_id);
+ }
+
+ ctx->plane_id = plane->plane_id;
+
+ fb = drmModeGetFB(ctx->hwctx->fd, plane->fb_id);
+ if (!fb) {
+ err = errno;
+ av_log(avctx, AV_LOG_ERROR, "Failed to get "
+ "framebuffer %"PRIu32": %s.\n",
+ plane->fb_id, strerror(err));
+ err = AVERROR(err);
+ goto fail;
+ }
+
+ av_log(avctx, AV_LOG_INFO, "Template framebuffer is %"PRIu32": "
+ "%"PRIu32"x%"PRIu32" %"PRIu32"bpp %"PRIu32"b depth.\n",
+ fb->fb_id, fb->width, fb->height, fb->bpp, fb->depth);
+
+ ctx->width = fb->width;
+ ctx->height = fb->height;
+
+ if (!fb->handle) {
+ av_log(avctx, AV_LOG_ERROR, "No handle set on framebuffer: "
+ "maybe you need some additional capabilities?\n");
+ err = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ stream = avformat_new_stream(avctx, NULL);
+ if (!stream) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ stream->codecpar->codec_id = AV_CODEC_ID_WRAPPED_AVFRAME;
+ stream->codecpar->width = fb->width;
+ stream->codecpar->height = fb->height;
+ stream->codecpar->format = AV_PIX_FMT_DRM_PRIME;
+
+ avpriv_set_pts_info(stream, 64, 1, 1000000);
+
+ ctx->frames_ref = av_hwframe_ctx_alloc(ctx->device_ref);
+ if (!ctx->frames_ref) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ ctx->frames = (AVHWFramesContext*)ctx->frames_ref->data;
+
+ ctx->frames->format = AV_PIX_FMT_DRM_PRIME;
+ ctx->frames->sw_format = ctx->format,
+ ctx->frames->width = fb->width;
+ ctx->frames->height = fb->height;
+
+ err = av_hwframe_ctx_init(ctx->frames_ref);
+ if (err < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to initialise "
+ "hardware frames context: %d.\n", err);
+ goto fail;
+ }
+
+ ctx->frame_delay = av_rescale_q(1, (AVRational) { ctx->framerate.den,
+ ctx->framerate.num }, AV_TIME_BASE_Q);
+
+ err = 0;
+fail:
+ if (plane_res)
+ drmModeFreePlaneResources(plane_res);
+ if (plane)
+ drmModeFreePlane(plane);
+ if (fb)
+ drmModeFreeFB(fb);
+
+ return err;
+}
+
+static av_cold int kmsgrab_read_close(AVFormatContext *avctx)
+{
+ KMSGrabContext *ctx = avctx->priv_data;
+
+ av_buffer_unref(&ctx->frames_ref);
+ av_buffer_unref(&ctx->device_ref);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(KMSGrabContext, x)
+#define FLAGS AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+ { "device", "DRM device path",
+ OFFSET(device_path), AV_OPT_TYPE_STRING,
+ { .str = "/dev/dri/card0" }, 0, 0, FLAGS },
+ { "format", "Pixel format for framebuffer",
+ OFFSET(format), AV_OPT_TYPE_PIXEL_FMT,
+ { .i64 = AV_PIX_FMT_BGR0 }, 0, UINT32_MAX, FLAGS },
+ { "format_modifier", "DRM format modifier for framebuffer",
+ OFFSET(drm_format_modifier), AV_OPT_TYPE_INT64,
+ { .i64 = DRM_FORMAT_MOD_NONE }, 0, INT64_MAX, FLAGS },
+ { "crtc_id", "CRTC ID to define capture source",
+ OFFSET(source_crtc), AV_OPT_TYPE_INT64,
+ { .i64 = 0 }, 0, UINT32_MAX, FLAGS },
+ { "plane_id", "Plane ID to define capture source",
+ OFFSET(source_plane), AV_OPT_TYPE_INT64,
+ { .i64 = 0 }, 0, UINT32_MAX, FLAGS },
+ { "framerate", "Framerate to capture at",
+ OFFSET(framerate), AV_OPT_TYPE_RATIONAL,
+ { .dbl = 30.0 }, 0, 1000, FLAGS },
+ { NULL },
+};
+
+static const AVClass kmsgrab_class = {
+ .class_name = "kmsgrab indev",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_kmsgrab_demuxer = {
+ .name = "kmsgrab",
+ .long_name = NULL_IF_CONFIG_SMALL("KMS screen capture"),
+ .priv_data_size = sizeof(KMSGrabContext),
+ .read_header = &kmsgrab_read_header,
+ .read_packet = &kmsgrab_read_packet,
+ .read_close = &kmsgrab_read_close,
+ .flags = AVFMT_NOFILE,
+ .priv_class = &kmsgrab_class,
+};
--
2.11.0
Mark Thompson
2017-09-11 22:09:15 UTC
Permalink
Post by Mark Thompson
---
Now sets the trusted packet flag; otherwise unchanged.
configure | 1 +
libavdevice/Makefile | 1 +
libavdevice/alldevices.c | 1 +
libavdevice/kmsgrab.c | 455 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 458 insertions(+)
create mode 100644 libavdevice/kmsgrab.c
Series approved by atomnuker on IRC. Does anyone else want to comment on the wrapped_avframe / AV_PKT_FLAG_TRUSTED setup (or anything else)? If not, I'll push this the day after tomorrow.

Thanks,

- Mark
Paul B Mahol
2017-09-11 22:15:07 UTC
Permalink
Post by Mark Thompson
Post by Mark Thompson
---
Now sets the trusted packet flag; otherwise unchanged.
configure | 1 +
libavdevice/Makefile | 1 +
libavdevice/alldevices.c | 1 +
libavdevice/kmsgrab.c | 455
+++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 458 insertions(+)
create mode 100644 libavdevice/kmsgrab.c
Series approved by atomnuker on IRC. Does anyone else want to comment on
the wrapped_avframe / AV_PKT_FLAG_TRUSTED setup (or anything else)? If not,
I'll push this the day after tomorrow.
Missing docs.
Mark Thompson
2017-09-11 23:23:35 UTC
Permalink
---
Post by Paul B Mahol
Post by Mark Thompson
Post by Mark Thompson
---
Now sets the trusted packet flag; otherwise unchanged.
configure | 1 +
libavdevice/Makefile | 1 +
libavdevice/alldevices.c | 1 +
libavdevice/kmsgrab.c | 455
+++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 458 insertions(+)
create mode 100644 libavdevice/kmsgrab.c
Series approved by atomnuker on IRC. Does anyone else want to comment on
the wrapped_avframe / AV_PKT_FLAG_TRUSTED setup (or anything else)? If not,
I'll push this the day after tomorrow.
Missing docs.
doc/indevs.texi | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)

diff --git a/doc/indevs.texi b/doc/indevs.texi
index ad6418751b..30b7ac2380 100644
--- a/doc/indevs.texi
+++ b/doc/indevs.texi
@@ -331,6 +331,68 @@ ffmpeg -channels 16 -format_code Hi50 -f decklink -i 'UltraStudio Mini Recorder'

@end itemize

+@section kmsgrab
+
+KMS video input device.
+
+Captures the KMS scanout framebuffer associated with a specified CRTC or plane as a
+DRM object that can be passed to other hardware functions.
+
+Requires either DRM master or CAP_SYS_ADMIN to run.
+
+If you don't understand what all of that means, you probably don't want this. Look at
+@option{x11grab} instead.
+
+@subsection Options
+
+@table @option
+
+@item device
+DRM device to capture on. Defaults to @option{/dev/dri/card0}.
+
+@item format
+Pixel format of the framebuffer. Defaults to @option{bgr0}.
+
+@item format_modifier
+Format modifier to signal on output frames. This is necessary to import correctly into
+some APIs, but can't be autodetected. See the libdrm documentation for possible values.
+
+@item crtc_id
+KMS CRTC ID to define the capture source. The first active plane on the given CRTC
+will be used.
+
+@item plane_id
+KMS plane ID to define the capture source. Defaults to the first active plane found if
+neither @option{crtc_id} nor @option{plane_id} are specified.
+
+@item framerate
+Framerate to capture at. This is not synchronised to any page flipping or framebuffer
+changes - it just defines the interval at which the framebuffer is sampled. Sampling
+faster than the framebuffer update rate will generate independent frames with the same
+content. Defaults to @code{30}.
+
+@end table
+
+@subsection Examples
+
+@itemize
+
+@item
+Capture from the first active plane, download the result to normal frames and encode.
+This will only work if the framebuffer is both linear and mappable - if not, the result
+may be scrambled or fail to download.
+@example
+ffmpeg -f kmsgrab -i - -vf 'hwdownload,format=bgr0' output.mp4
+@end example
+
+@item
+Capture from CRTC ID 42 at 60fps, map the result to VAAPI, convert to NV12 and encode as H.264.
+@example
+ffmpeg -crtc_id 42 -framerate 60 -f kmsgrab -i - -vf 'hwmap=derive_device=vaapi,scale_vaapi=w=1920:h=1080:format=nv12' -c:v h264_vaapi output.mp4
+@end example
+
+@end itemize
+
@section libndi_newtek

The libndi_newtek input device provides capture capabilities for using NDI (Network
--
2.11.0
James Almer
2017-09-14 04:35:09 UTC
Permalink
Post by Mark Thompson
+ pkt->data = (uint8_t*)frame;
+ pkt->size = sizeof(*frame);
+ pkt->pts = now;
+ pkt->flags = AV_PKT_FLAG_TRUSTED;
pkt->flags |= AV_PKT_FLAG_TRUSTED;

I know pkt->flags is zeroed by default, but that may change in the
future. And ORing/XORing is the proper way to set flags anyway.
Mark Thompson
2017-09-14 08:27:10 UTC
Permalink
Post by James Almer
Post by Mark Thompson
+ pkt->data = (uint8_t*)frame;
+ pkt->size = sizeof(*frame);
+ pkt->pts = now;
+ pkt->flags = AV_PKT_FLAG_TRUSTED;
pkt->flags |= AV_PKT_FLAG_TRUSTED;
I know pkt->flags is zeroed by default, but that may change in the
future. And ORing/XORing is the proper way to set flags anyway.
Fixed.

Thanks!

- Mark
Carl Eugen Hoyos
2017-09-14 21:30:36 UTC
Permalink
Post by Mark Thompson
+static const struct {
+ enum AVPixelFormat pixfmt;
+ uint32_t drm_format;
+} kmsgrab_formats[] = {
+ { AV_PIX_FMT_GRAY8, DRM_FORMAT_R8 },
+ { AV_PIX_FMT_GRAY16LE, DRM_FORMAT_R16 },
+ { AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
+ { AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_YUYV422, DRM_FORMAT_YUYV },
+ { AV_PIX_FMT_YVYU422, DRM_FORMAT_YVYU },
+ { AV_PIX_FMT_UYVY422, DRM_FORMAT_UYVY },
+ { AV_PIX_FMT_NV12, DRM_FORMAT_NV12 },
+ { AV_PIX_FMT_YUV420P, DRM_FORMAT_YUV420 },
+ { AV_PIX_FMT_YUV422P, DRM_FORMAT_YUV422 },
+ { AV_PIX_FMT_YUV444P, DRM_FORMAT_YUV444 },
Which of those were you able to test?
I find the comments in the header file very misleading:
What is "little-endian 8:8:8:8 ARGB"?

Thank you, Carl Eugen
Mark Thompson
2017-09-14 22:37:07 UTC
Permalink
Post by Carl Eugen Hoyos
Post by Mark Thompson
+static const struct {
+ enum AVPixelFormat pixfmt;
+ uint32_t drm_format;
+} kmsgrab_formats[] = {
+ { AV_PIX_FMT_GRAY8, DRM_FORMAT_R8 },
+ { AV_PIX_FMT_GRAY16LE, DRM_FORMAT_R16 },
+ { AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
+ { AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_YUYV422, DRM_FORMAT_YUYV },
+ { AV_PIX_FMT_YVYU422, DRM_FORMAT_YVYU },
+ { AV_PIX_FMT_UYVY422, DRM_FORMAT_UYVY },
+ { AV_PIX_FMT_NV12, DRM_FORMAT_NV12 },
+ { AV_PIX_FMT_YUV420P, DRM_FORMAT_YUV420 },
+ { AV_PIX_FMT_YUV422P, DRM_FORMAT_YUV422 },
+ { AV_PIX_FMT_YUV444P, DRM_FORMAT_YUV444 },
Which of those were you able to test?
Only the 32-bit RGB0-type ones (all of them are testable because you just get the colours in a different order). Intel at least should work with others in near-future versions (e.g. <https://lists.freedesktop.org/archives/intel-gfx/2017-July/132642.html>), though I haven't actually tested this. It seemed sensible to include all core DRM formats which map to ffmpeg pixfmts to account for other drivers (possibly future) which I can't test now.

I've tested on amdgpu, exynos, i915 and rockchip. It should work on other KMS drivers, though if the output is tiled or not-CPU-mappable it can be hard to get the output somewhere where it can actually be used. (The ARM ones are fine and allow hwdownload directly. Intels I've tried are mappable but tiled so hwdownload is messed up, but they interoperate cleanly with VAAPI. The AMD I have makes the objects completely unmappable from the CPU, but they can be imported to other GPU things with Mesa.)
Post by Carl Eugen Hoyos
What is "little-endian 8:8:8:8 ARGB"?
It is just A-R-G-B in memory in that order as you might expect without the comment. Not sure why it says little-endian - maybe to emphasise that it doesn't change based on the host architecture?

Thanks,

- Mark
Carl Eugen Hoyos
2017-09-15 12:09:24 UTC
Permalink
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
+static const struct {
+ enum AVPixelFormat pixfmt;
+ uint32_t drm_format;
+} kmsgrab_formats[] = {
+ { AV_PIX_FMT_GRAY8, DRM_FORMAT_R8 },
+ { AV_PIX_FMT_GRAY16LE, DRM_FORMAT_R16 },
+ { AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
+ { AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_YUYV422, DRM_FORMAT_YUYV },
+ { AV_PIX_FMT_YVYU422, DRM_FORMAT_YVYU },
+ { AV_PIX_FMT_UYVY422, DRM_FORMAT_UYVY },
+ { AV_PIX_FMT_NV12, DRM_FORMAT_NV12 },
+ { AV_PIX_FMT_YUV420P, DRM_FORMAT_YUV420 },
+ { AV_PIX_FMT_YUV422P, DRM_FORMAT_YUV422 },
+ { AV_PIX_FMT_YUV444P, DRM_FORMAT_YUV444 },
Which of those were you able to test?
Only the 32-bit RGB0-type ones (all of them are testable because you just get the colours in a different order).
So RGB0, BGR0, 0RGB and 0BGR all work fine?
Post by Mark Thompson
Intel at least should work with others in near-future versions (e.g. <https://lists.freedesktop.org/archives/intel-gfx/2017-July/132642.html>), though I haven't actually tested this. It seemed sensible to include all core DRM formats which map to ffmpeg pixfmts to account for other drivers (possibly future) which I can't test now.
Good idea, twelve more are attached.
Post by Mark Thompson
I've tested on amdgpu, exynos, i915 and rockchip. It should work on other KMS drivers, though if the output is tiled or not-CPU-mappable it can be hard to get the output somewhere where it can actually be used. (The ARM ones are fine and allow hwdownload directly. Intels I've tried are mappable but tiled so hwdownload is messed up, but they interoperate cleanly with VAAPI. The AMD I have makes the objects completely unmappable from the CPU, but they can be imported to other GPU things with Mesa.)
Post by Carl Eugen Hoyos
What is "little-endian 8:8:8:8 ARGB"?
It is just A-R-G-B in memory in that order as you might expect
without the comment.
So the comment is simply wrong for the 8:8:8:8 RGB formats?
Iirc, the same comment in the SDL sources defines another
order (or OpenGL, I don't remember atm), as does FFmpeg
through its RGB32 formats.
Post by Mark Thompson
Not sure why it says little-endian - maybe to emphasise that it doesn't change based on the host architecture?
Wouldn't it be better if the comment said "does not change based
on the host architecture"?

Carl Eugen
Mark Thompson
2017-09-15 14:37:47 UTC
Permalink
Post by Carl Eugen Hoyos
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
+static const struct {
+ enum AVPixelFormat pixfmt;
+ uint32_t drm_format;
+} kmsgrab_formats[] = {
+ { AV_PIX_FMT_GRAY8, DRM_FORMAT_R8 },
+ { AV_PIX_FMT_GRAY16LE, DRM_FORMAT_R16 },
+ { AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
+ { AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_YUYV422, DRM_FORMAT_YUYV },
+ { AV_PIX_FMT_YVYU422, DRM_FORMAT_YVYU },
+ { AV_PIX_FMT_UYVY422, DRM_FORMAT_UYVY },
+ { AV_PIX_FMT_NV12, DRM_FORMAT_NV12 },
+ { AV_PIX_FMT_YUV420P, DRM_FORMAT_YUV420 },
+ { AV_PIX_FMT_YUV422P, DRM_FORMAT_YUV422 },
+ { AV_PIX_FMT_YUV444P, DRM_FORMAT_YUV444 },
Which of those were you able to test?
Only the 32-bit RGB0-type ones (all of them are testable because you just get the colours in a different order).
So RGB0, BGR0, 0RGB and 0BGR all work fine?
Yes.

I've verified YUYV/UYVY directly on Intel as well now.
Post by Carl Eugen Hoyos
Post by Mark Thompson
Intel at least should work with others in near-future versions (e.g. <https://lists.freedesktop.org/archives/intel-gfx/2017-July/132642.html>), though I haven't actually tested this. It seemed sensible to include all core DRM formats which map to ffmpeg pixfmts to account for other drivers (possibly future) which I can't test now.
Good idea, twelve more are attached.
Looks sensible.

I think the ordering of the packed-within-bytes formats (565, etc.) should be verified before applying them, though, just in case there is something funny going on here. I had a look at RGB565, which is usable for output on Intel, but I can't easily get the result anywhere (map fails, VAAPI doesn't like the format).

On BIG_ENDIAN, I'm not sure whether it has any use or testing at all - none of the libdrm test programs allow it, and it is suspiciously absent from all but the most generic parts of drivers/gpu/drm in Linux.
Post by Carl Eugen Hoyos
Post by Mark Thompson
I've tested on amdgpu, exynos, i915 and rockchip. It should work on other KMS drivers, though if the output is tiled or not-CPU-mappable it can be hard to get the output somewhere where it can actually be used. (The ARM ones are fine and allow hwdownload directly. Intels I've tried are mappable but tiled so hwdownload is messed up, but they interoperate cleanly with VAAPI. The AMD I have makes the objects completely unmappable from the CPU, but they can be imported to other GPU things with Mesa.)
Post by Carl Eugen Hoyos
What is "little-endian 8:8:8:8 ARGB"?
It is just A-R-G-B in memory in that order as you might expect
without the comment.
So the comment is simply wrong for the 8:8:8:8 RGB formats?
Iirc, the same comment in the SDL sources defines another
order (or OpenGL, I don't remember atm), as does FFmpeg
through its RGB32 formats.
Hmm. Maybe this is actually wrong in my code. The format is provided by the user, because there is no way to retrieve that information from the framebuffer itself, and therefore we are always doing the mapping in both directions - the default of AV_PIX_FMT_BGR0 is mapped to DRM_FORMAT_BGRX8888 and then back to AV_PIX_FMT_BGR0 for hwdownload or hwmap. If the sense were actually the opposite and the framebuffer was in fact DRM_FORMAT_XRGB8888, this would still work identically and have correct output, because the intermediate doesn't matter as long as it's reversible.

I think I need to test this with an explicit program to do the modeset and set framebuffer formats directly and then match them to the output pixel values, because there is no other way to tell.

Thanks,

- Mark
Carl Eugen Hoyos
2017-09-15 15:11:40 UTC
Permalink
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
+static const struct {
+ enum AVPixelFormat pixfmt;
+ uint32_t drm_format;
+} kmsgrab_formats[] = {
+ { AV_PIX_FMT_GRAY8, DRM_FORMAT_R8 },
+ { AV_PIX_FMT_GRAY16LE, DRM_FORMAT_R16 },
+ { AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
+ { AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_YUYV422, DRM_FORMAT_YUYV },
+ { AV_PIX_FMT_YVYU422, DRM_FORMAT_YVYU },
+ { AV_PIX_FMT_UYVY422, DRM_FORMAT_UYVY },
+ { AV_PIX_FMT_NV12, DRM_FORMAT_NV12 },
+ { AV_PIX_FMT_YUV420P, DRM_FORMAT_YUV420 },
+ { AV_PIX_FMT_YUV422P, DRM_FORMAT_YUV422 },
+ { AV_PIX_FMT_YUV444P, DRM_FORMAT_YUV444 },
Which of those were you able to test?
Only the 32-bit RGB0-type ones (all of them are testable because you just get the colours in a different order).
So RGB0, BGR0, 0RGB and 0BGR all work fine?
Yes.
I've verified YUYV/UYVY directly on Intel as well now.
Post by Carl Eugen Hoyos
Post by Mark Thompson
Intel at least should work with others in near-future versions (e.g. <https://lists.freedesktop.org/archives/intel-gfx/2017-July/132642.html>), though I haven't actually tested this. It seemed sensible to include all core DRM formats which map to ffmpeg pixfmts to account for other drivers (possibly future) which I can't test now.
Good idea, twelve more are attached.
Looks sensible.
May I commit?
Post by Mark Thompson
I think the ordering of the packed-within-bytes formats (565, etc.) should be verified before applying them, though, just in case there is something funny going on here. I had a look at RGB565, which is usable for output on Intel, but I can't easily get the result anywhere (map fails, VAAPI doesn't like the format).
On BIG_ENDIAN, I'm not sure whether it has any use or testing at all - none of the libdrm test programs allow it, and it is suspiciously absent from all but the most generic parts of drivers/gpu/drm in Linux.
Post by Carl Eugen Hoyos
Post by Mark Thompson
I've tested on amdgpu, exynos, i915 and rockchip. It should work on other KMS drivers, though if the output is tiled or not-CPU-mappable it can be hard to get the output somewhere where it can actually be used. (The ARM ones are fine and allow hwdownload directly. Intels I've tried are mappable but tiled so hwdownload is messed up, but they interoperate cleanly with VAAPI. The AMD I have makes the objects completely unmappable from the CPU, but they can be imported to other GPU things with Mesa.)
Post by Carl Eugen Hoyos
What is "little-endian 8:8:8:8 ARGB"?
It is just A-R-G-B in memory in that order as you might expect
without the comment.
So the comment is simply wrong for the 8:8:8:8 RGB formats?
Iirc, the same comment in the SDL sources defines another
order (or OpenGL, I don't remember atm), as does FFmpeg
through its RGB32 formats.
Hmm. Maybe this is actually wrong in my code. The format is provided by the user, because there is no way to retrieve that information from the framebuffer itself, and therefore we are always doing the mapping in both directions - the default of AV_PIX_FMT_BGR0 is mapped to DRM_FORMAT_BGRX8888 and then back to AV_PIX_FMT_BGR0 for hwdownload or hwmap. If the sense were actually the opposite and the framebuffer was in fact DRM_FORMAT_XRGB8888, this would still work identically and have correct output, because the intermediate doesn't matter as long as it's reversible.
I think I need to test this with an explicit program to do the modeset and set framebuffer formats directly and then match them to the output pixel values, because there is no other way to tell.
Thank you!

Could we agree, just for the wording (you know that I am all for
commit early), that you were - so far - not able to test any of
above formats?

Carl Eugen
Mark Thompson
2017-09-15 16:19:44 UTC
Permalink
Post by Carl Eugen Hoyos
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
+static const struct {
+ enum AVPixelFormat pixfmt;
+ uint32_t drm_format;
+} kmsgrab_formats[] = {
+ { AV_PIX_FMT_GRAY8, DRM_FORMAT_R8 },
+ { AV_PIX_FMT_GRAY16LE, DRM_FORMAT_R16 },
+ { AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
+ { AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_YUYV422, DRM_FORMAT_YUYV },
+ { AV_PIX_FMT_YVYU422, DRM_FORMAT_YVYU },
+ { AV_PIX_FMT_UYVY422, DRM_FORMAT_UYVY },
+ { AV_PIX_FMT_NV12, DRM_FORMAT_NV12 },
+ { AV_PIX_FMT_YUV420P, DRM_FORMAT_YUV420 },
+ { AV_PIX_FMT_YUV422P, DRM_FORMAT_YUV422 },
+ { AV_PIX_FMT_YUV444P, DRM_FORMAT_YUV444 },
Which of those were you able to test?
Only the 32-bit RGB0-type ones (all of them are testable because you just get the colours in a different order).
So RGB0, BGR0, 0RGB and 0BGR all work fine?
Yes.
I've verified YUYV/UYVY directly on Intel as well now.
Post by Carl Eugen Hoyos
Post by Mark Thompson
Intel at least should work with others in near-future versions (e.g. <https://lists.freedesktop.org/archives/intel-gfx/2017-July/132642.html>), though I haven't actually tested this. It seemed sensible to include all core DRM formats which map to ffmpeg pixfmts to account for other drivers (possibly future) which I can't test now.
Good idea, twelve more are attached.
Looks sensible.
May I commit?
Are you able to test at all? I believe I should be able to test at least 565 properly a bit later today (and settle the ordering question - I do think now that my interpretation is wrong and that it was working because the same method was used in both directions).
Post by Carl Eugen Hoyos
Post by Mark Thompson
I think the ordering of the packed-within-bytes formats (565, etc.) should be verified before applying them, though, just in case there is something funny going on here. I had a look at RGB565, which is usable for output on Intel, but I can't easily get the result anywhere (map fails, VAAPI doesn't like the format).
On BIG_ENDIAN, I'm not sure whether it has any use or testing at all - none of the libdrm test programs allow it, and it is suspiciously absent from all but the most generic parts of drivers/gpu/drm in Linux.
Post by Carl Eugen Hoyos
Post by Mark Thompson
I've tested on amdgpu, exynos, i915 and rockchip. It should work on other KMS drivers, though if the output is tiled or not-CPU-mappable it can be hard to get the output somewhere where it can actually be used. (The ARM ones are fine and allow hwdownload directly. Intels I've tried are mappable but tiled so hwdownload is messed up, but they interoperate cleanly with VAAPI. The AMD I have makes the objects completely unmappable from the CPU, but they can be imported to other GPU things with Mesa.)
Post by Carl Eugen Hoyos
What is "little-endian 8:8:8:8 ARGB"?
It is just A-R-G-B in memory in that order as you might expect
without the comment.
So the comment is simply wrong for the 8:8:8:8 RGB formats?
Iirc, the same comment in the SDL sources defines another
order (or OpenGL, I don't remember atm), as does FFmpeg
through its RGB32 formats.
Hmm. Maybe this is actually wrong in my code. The format is provided by the user, because there is no way to retrieve that information from the framebuffer itself, and therefore we are always doing the mapping in both directions - the default of AV_PIX_FMT_BGR0 is mapped to DRM_FORMAT_BGRX8888 and then back to AV_PIX_FMT_BGR0 for hwdownload or hwmap. If the sense were actually the opposite and the framebuffer was in fact DRM_FORMAT_XRGB8888, this would still work identically and have correct output, because the intermediate doesn't matter as long as it's reversible.
I think I need to test this with an explicit program to do the modeset and set framebuffer formats directly and then match them to the output pixel values, because there is no other way to tell.
Thank you!
Could we agree, just for the wording (you know that I am all for
commit early), that you were - so far - not able to test any of
above formats?
The ones in your list, no.

The things I have actually tested are all permutations of RGBX (though not necessarily being able to distinguish between them), and then also YUYV and UYVY (4:2:2).

- Mark
Carl Eugen Hoyos
2017-09-15 16:26:02 UTC
Permalink
Post by Mark Thompson
Post by Carl Eugen Hoyos
May I commit?
Are you able to test at all?
No, I am sorry if I gave a wrong impression!

Carl Eugen
Mark Thompson
2017-09-15 20:47:30 UTC
Permalink
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
+static const struct {
+ enum AVPixelFormat pixfmt;
+ uint32_t drm_format;
+} kmsgrab_formats[] = {
+ { AV_PIX_FMT_GRAY8, DRM_FORMAT_R8 },
+ { AV_PIX_FMT_GRAY16LE, DRM_FORMAT_R16 },
+ { AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
+ { AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_YUYV422, DRM_FORMAT_YUYV },
+ { AV_PIX_FMT_YVYU422, DRM_FORMAT_YVYU },
+ { AV_PIX_FMT_UYVY422, DRM_FORMAT_UYVY },
+ { AV_PIX_FMT_NV12, DRM_FORMAT_NV12 },
+ { AV_PIX_FMT_YUV420P, DRM_FORMAT_YUV420 },
+ { AV_PIX_FMT_YUV422P, DRM_FORMAT_YUV422 },
+ { AV_PIX_FMT_YUV444P, DRM_FORMAT_YUV444 },
Which of those were you able to test?
Only the 32-bit RGB0-type ones (all of them are testable because you just get the colours in a different order).
So RGB0, BGR0, 0RGB and 0BGR all work fine?
Yes.
I've verified YUYV/UYVY directly on Intel as well now.
Post by Carl Eugen Hoyos
Post by Mark Thompson
Intel at least should work with others in near-future versions (e.g. <https://lists.freedesktop.org/archives/intel-gfx/2017-July/132642.html>), though I haven't actually tested this. It seemed sensible to include all core DRM formats which map to ffmpeg pixfmts to account for other drivers (possibly future) which I can't test now.
Good idea, twelve more are attached.
Looks sensible.
May I commit?
Are you able to test at all? I believe I should be able to test at least 565 properly a bit later today (and settle the ordering question - I do think now that my interpretation is wrong and that it was working because the same method was used in both directions).
Ok, I've done a lot more format testing now, with explicitly created framebuffers on Rockchip:

* The four-element RGB formats are indeed in the order stated in the file, not the order in the name. This means they are reversed wrt everything else in ffmpeg, so I'm fixing that.
* RGB24 and BGR24 /are/ correct already, the endianness comments in drm_fourcc are misleading for them.
* The multiple-plane formats are not usable, because the planes need not be contiguous (you get garbage in the chroma plane), so I'm going to remove support for them. There is currently no way to query the offsets of later planes, but if that were added then they could be supported.
* RGB565 and BGR565 work correctly, the rest of formats in your patch are untested.

Patches follow, including yours.

Thanks,

- Mark
Mark Thompson
2017-09-15 20:51:05 UTC
Permalink
The 32-bit DRM formats are defined in terms of little-endian words, so
32-bit RGB formats like XRGB actually have the elements in the opposite
order in memory to the order they are in the name.

This does not affect YUYV and similar YUV 4:2:2 formats, which are in
the expected order.
---
libavdevice/kmsgrab.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/libavdevice/kmsgrab.c b/libavdevice/kmsgrab.c
index 67a83ef84a..bcb6865f60 100644
--- a/libavdevice/kmsgrab.c
+++ b/libavdevice/kmsgrab.c
@@ -210,14 +210,14 @@ static const struct {
#endif
{ AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
{ AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
- { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
- { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
- { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
- { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
- { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
- { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
- { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
- { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_ARGB8888 },
{ AV_PIX_FMT_YUYV422, DRM_FORMAT_YUYV },
{ AV_PIX_FMT_YVYU422, DRM_FORMAT_YVYU },
{ AV_PIX_FMT_UYVY422, DRM_FORMAT_UYVY },
--
2.11.0
Mark Thompson
2017-09-15 20:51:07 UTC
Permalink
---
libavutil/hwcontext_vaapi.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
index 852f0abda2..c4473a229f 100644
--- a/libavutil/hwcontext_vaapi.c
+++ b/libavutil/hwcontext_vaapi.c
@@ -925,16 +925,16 @@ static const struct {
#if defined(VA_FOURCC_P010) && defined(DRM_FORMAT_R16)
DRM_MAP(P010, 2, DRM_FORMAT_R16, DRM_FORMAT_RG1616),
#endif
- DRM_MAP(BGRA, 1, DRM_FORMAT_BGRA8888),
- DRM_MAP(BGRX, 1, DRM_FORMAT_BGRX8888),
- DRM_MAP(RGBA, 1, DRM_FORMAT_RGBA8888),
- DRM_MAP(RGBX, 1, DRM_FORMAT_RGBX8888),
+ DRM_MAP(BGRA, 1, DRM_FORMAT_ARGB8888),
+ DRM_MAP(BGRX, 1, DRM_FORMAT_XRGB8888),
+ DRM_MAP(RGBA, 1, DRM_FORMAT_ABGR8888),
+ DRM_MAP(RGBX, 1, DRM_FORMAT_XBGR8888),
#ifdef VA_FOURCC_ABGR
- DRM_MAP(ABGR, 1, DRM_FORMAT_ABGR8888),
- DRM_MAP(XBGR, 1, DRM_FORMAT_XBGR8888),
+ DRM_MAP(ABGR, 1, DRM_FORMAT_RGBA8888),
+ DRM_MAP(XBGR, 1, DRM_FORMAT_RGBX8888),
#endif
- DRM_MAP(ARGB, 1, DRM_FORMAT_ARGB8888),
- DRM_MAP(XRGB, 1, DRM_FORMAT_XRGB8888),
+ DRM_MAP(ARGB, 1, DRM_FORMAT_BGRA8888),
+ DRM_MAP(XRGB, 1, DRM_FORMAT_BGRX8888),
};
#undef DRM_MAP
--
2.11.0
Mark Thompson
2017-09-15 20:51:08 UTC
Permalink
From: Carl Eugen Hoyos <***@gmail.com>

---
libavdevice/kmsgrab.c | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/libavdevice/kmsgrab.c b/libavdevice/kmsgrab.c
index e54534006e..11f23d8af7 100644
--- a/libavdevice/kmsgrab.c
+++ b/libavdevice/kmsgrab.c
@@ -207,7 +207,18 @@ static const struct {
#endif
#ifdef DRM_FORMAT_R16
{ AV_PIX_FMT_GRAY16LE, DRM_FORMAT_R16 },
+ { AV_PIX_FMT_GRAY16BE, DRM_FORMAT_R16 | DRM_FORMAT_BIG_ENDIAN },
#endif
+ { AV_PIX_FMT_RGB8, DRM_FORMAT_RGB332 },
+ { AV_PIX_FMT_BGR8, DRM_FORMAT_BGR233 },
+ { AV_PIX_FMT_RGB555LE, DRM_FORMAT_XRGB1555 },
+ { AV_PIX_FMT_RGB555BE, DRM_FORMAT_XRGB1555 | DRM_FORMAT_BIG_ENDIAN },
+ { AV_PIX_FMT_BGR555LE, DRM_FORMAT_XBGR1555 },
+ { AV_PIX_FMT_BGR555BE, DRM_FORMAT_XBGR1555 | DRM_FORMAT_BIG_ENDIAN },
+ { AV_PIX_FMT_RGB565LE, DRM_FORMAT_RGB565 },
+ { AV_PIX_FMT_RGB565BE, DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN },
+ { AV_PIX_FMT_BGR565LE, DRM_FORMAT_BGR565 },
+ { AV_PIX_FMT_BGR565BE, DRM_FORMAT_BGR565 | DRM_FORMAT_BIG_ENDIAN },
{ AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
{ AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
{ AV_PIX_FMT_0RGB, DRM_FORMAT_BGRX8888 },
--
2.11.0
Mark Thompson
2017-09-15 20:51:06 UTC
Permalink
The planes have no reason to be contiguous, assuming they are results in
very broken output.
---
libavdevice/kmsgrab.c | 4 ----
1 file changed, 4 deletions(-)

diff --git a/libavdevice/kmsgrab.c b/libavdevice/kmsgrab.c
index bcb6865f60..e54534006e 100644
--- a/libavdevice/kmsgrab.c
+++ b/libavdevice/kmsgrab.c
@@ -221,10 +221,6 @@ static const struct {
{ AV_PIX_FMT_YUYV422, DRM_FORMAT_YUYV },
{ AV_PIX_FMT_YVYU422, DRM_FORMAT_YVYU },
{ AV_PIX_FMT_UYVY422, DRM_FORMAT_UYVY },
- { AV_PIX_FMT_NV12, DRM_FORMAT_NV12 },
- { AV_PIX_FMT_YUV420P, DRM_FORMAT_YUV420 },
- { AV_PIX_FMT_YUV422P, DRM_FORMAT_YUV422 },
- { AV_PIX_FMT_YUV444P, DRM_FORMAT_YUV444 },
};

static av_cold int kmsgrab_read_header(AVFormatContext *avctx)
--
2.11.0
Carl Eugen Hoyos
2017-09-15 21:43:01 UTC
Permalink
Post by Mark Thompson
The planes have no reason to be contiguous, assuming they
are results in very broken output.
Is being contiguous a requirement of DRM?

Carl Eugen
Mark Thompson
2017-09-15 21:44:52 UTC
Permalink
Post by Carl Eugen Hoyos
Post by Mark Thompson
The planes have no reason to be contiguous, assuming they
are results in very broken output.
Is being contiguous a requirement of DRM?
For multiple-plane formats, no. Hence the change.

- Mark
Carl Eugen Hoyos
2017-09-15 21:50:35 UTC
Permalink
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
The planes have no reason to be contiguous, assuming they
are results in very broken output.
Is being contiguous a requirement of DRM?
For multiple-plane formats, no. Hence the change.
Is it a requirement in FFmpeg?

Sorry if I misunderstand, Carl Eugen
Mark Thompson
2017-09-15 21:53:20 UTC
Permalink
Post by Carl Eugen Hoyos
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
The planes have no reason to be contiguous, assuming they
are results in very broken output.
Is being contiguous a requirement of DRM?
For multiple-plane formats, no. Hence the change.
Is it a requirement in FFmpeg?
Sorry if I misunderstand, Carl Eugen
Right, sorry, I wasn't reading the right thing into the question.

Only the address of the first plane is retrievable, so we don't know the addresses of the other planes. Assuming they are contiguous is a possible guess, but not one we can actually make in practice.

- Mark
Carl Eugen Hoyos
2017-09-15 21:56:06 UTC
Permalink
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
The planes have no reason to be contiguous, assuming they
are results in very broken output.
Is being contiguous a requirement of DRM?
For multiple-plane formats, no. Hence the change.
Is it a requirement in FFmpeg?
Sorry if I misunderstand, Carl Eugen
Right, sorry, I wasn't reading the right thing into the question.
Only the address of the first plane is retrievable, so we don't know
the addresses of the other planes. Assuming they are contiguous
is a possible guess, but not one we can actually make in practice.
How are the formats supposed to be used?

Just curious, Carl Eugen
Mark Thompson
2017-09-15 22:12:37 UTC
Permalink
Post by Carl Eugen Hoyos
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Mark Thompson
The planes have no reason to be contiguous, assuming they
are results in very broken output.
Is being contiguous a requirement of DRM?
For multiple-plane formats, no. Hence the change.
Is it a requirement in FFmpeg?
Sorry if I misunderstand, Carl Eugen
Right, sorry, I wasn't reading the right thing into the question.
Only the address of the first plane is retrievable, so we don't know
the addresses of the other planes. Assuming they are contiguous
is a possible guess, but not one we can actually make in practice.
How are the formats supposed to be used?
The process which created the planes (and will render to them) knows where they are.

You create a multiple-plane framebuffer with drmModeAddFB2() or drmModeAddFB2WithModifiers(). The only retrieval function is drmModeGetFB(), which matches the single-plane-only drmModeAddFB(). See <https://cgit.freedesktop.org/mesa/drm/tree/xf86drmMode.h#n359>.

If drmModeGetFB2(), or ideally drmModeGetFB2WithModifiers(), were added then we could support multiple-plane formats in kmsgrab. (Better versions like this would also remove some of the guessing of formats and modifiers, which would be nice.)

(These are all simple ioctl() wrappers which land in the kernel at <drivers/gpu/drm/drm_framebuffer.c>. E.g. drmModeGetFB() ends up at <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/drm_framebuffer.c#n430>, where you can also see other things like the DRM-master-or-CAP_SYS_ADMIN requirement.)

- Mark
Carl Eugen Hoyos
2017-09-15 21:40:33 UTC
Permalink
Post by Mark Thompson
The 32-bit DRM formats are defined in terms of little-endian words, so
32-bit RGB formats like XRGB actually have the elements in the opposite
order in memory to the order they are in the name.
This does not affect YUYV and similar YUV 4:2:2 formats, which are in
the expected order.
---
libavdevice/kmsgrab.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libavdevice/kmsgrab.c b/libavdevice/kmsgrab.c
index 67a83ef84a..bcb6865f60 100644
--- a/libavdevice/kmsgrab.c
+++ b/libavdevice/kmsgrab.c
@@ -210,14 +210,14 @@ static const struct {
#endif
{ AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
{ AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
- { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
- { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
- { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
- { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
- { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
- { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
- { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
- { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_ARGB8888 },
Is it possible to compile kmsgrab on big-endian hardware?

Thank you, Carl Eugen
Mark Thompson
2017-09-15 21:44:17 UTC
Permalink
Post by Carl Eugen Hoyos
Post by Mark Thompson
The 32-bit DRM formats are defined in terms of little-endian words, so
32-bit RGB formats like XRGB actually have the elements in the opposite
order in memory to the order they are in the name.
This does not affect YUYV and similar YUV 4:2:2 formats, which are in
the expected order.
---
libavdevice/kmsgrab.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libavdevice/kmsgrab.c b/libavdevice/kmsgrab.c
index 67a83ef84a..bcb6865f60 100644
--- a/libavdevice/kmsgrab.c
+++ b/libavdevice/kmsgrab.c
@@ -210,14 +210,14 @@ static const struct {
#endif
{ AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
{ AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
- { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
- { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
- { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
- { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
- { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
- { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
- { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
- { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_ARGB8888 },
Is it possible to compile kmsgrab on big-endian hardware?
Yes.

- Mark
Carl Eugen Hoyos
2017-09-15 21:49:13 UTC
Permalink
Post by Carl Eugen Hoyos
Post by Mark Thompson
The 32-bit DRM formats are defined in terms of little-endian words, so
32-bit RGB formats like XRGB actually have the elements in the opposite
order in memory to the order they are in the name.
This does not affect YUYV and similar YUV 4:2:2 formats, which are in
the expected order.
---
libavdevice/kmsgrab.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libavdevice/kmsgrab.c b/libavdevice/kmsgrab.c
index 67a83ef84a..bcb6865f60 100644
--- a/libavdevice/kmsgrab.c
+++ b/libavdevice/kmsgrab.c
@@ -210,14 +210,14 @@ static const struct {
#endif
{ AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
{ AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
- { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
- { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
- { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
- { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
- { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
- { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
- { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
- { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_ARGB8888 },
Is it possible to compile kmsgrab on big-endian hardware?
Yes.
And you assume that on big-endian hardware, above formats
would still be little-endian?

Or that they would not be used at all and the big-endian bit would
be set?
In that case, it may be simpler to mask the most significant
bit away in the code and use the endianness-aware formats
for FFmpeg in the table above (it would reduce the table size
and make it more readable). Especially as we don't know if
the bit would also be set for the packed yuv formats.

Carl Eugen
Mark Thompson
2017-09-15 22:03:00 UTC
Permalink
Post by Carl Eugen Hoyos
Post by Carl Eugen Hoyos
Post by Mark Thompson
The 32-bit DRM formats are defined in terms of little-endian words, so
32-bit RGB formats like XRGB actually have the elements in the opposite
order in memory to the order they are in the name.
This does not affect YUYV and similar YUV 4:2:2 formats, which are in
the expected order.
---
libavdevice/kmsgrab.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libavdevice/kmsgrab.c b/libavdevice/kmsgrab.c
index 67a83ef84a..bcb6865f60 100644
--- a/libavdevice/kmsgrab.c
+++ b/libavdevice/kmsgrab.c
@@ -210,14 +210,14 @@ static const struct {
#endif
{ AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
{ AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
- { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
- { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
- { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
- { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
- { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
- { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
- { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
- { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_ARGB8888 },
Is it possible to compile kmsgrab on big-endian hardware?
Yes.
And you assume that on big-endian hardware, above formats
would still be little-endian?
Or that they would not be used at all and the big-endian bit would
be set?
In that case, it may be simpler to mask the most significant
bit away in the code and use the endianness-aware formats
for FFmpeg in the table above (it would reduce the table size
and make it more readable). Especially as we don't know if
the bit would also be set for the packed yuv formats.
I think that unlike with RGB565 and similar, the big-endian bit does not need to be set for any of these formats, because the opposite-endian form already exists for each one. That is, you can always use DRM_FORMAT_XBGR8888 when the order of bytes in memory is RGBX, regardless of the host endianness.

(I think that's a consistent interpretation?)

- Mark
Carl Eugen Hoyos
2017-09-15 22:19:25 UTC
Permalink
Post by Mark Thompson
Post by Carl Eugen Hoyos
Post by Carl Eugen Hoyos
Post by Mark Thompson
The 32-bit DRM formats are defined in terms of little-endian words, so
32-bit RGB formats like XRGB actually have the elements in the opposite
order in memory to the order they are in the name.
This does not affect YUYV and similar YUV 4:2:2 formats, which are in
the expected order.
---
libavdevice/kmsgrab.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libavdevice/kmsgrab.c b/libavdevice/kmsgrab.c
index 67a83ef84a..bcb6865f60 100644
--- a/libavdevice/kmsgrab.c
+++ b/libavdevice/kmsgrab.c
@@ -210,14 +210,14 @@ static const struct {
#endif
{ AV_PIX_FMT_RGB24, DRM_FORMAT_RGB888 },
{ AV_PIX_FMT_BGR24, DRM_FORMAT_BGR888 },
- { AV_PIX_FMT_0RGB, DRM_FORMAT_XRGB8888 },
- { AV_PIX_FMT_0BGR, DRM_FORMAT_XBGR8888 },
- { AV_PIX_FMT_RGB0, DRM_FORMAT_RGBX8888 },
- { AV_PIX_FMT_BGR0, DRM_FORMAT_BGRX8888 },
- { AV_PIX_FMT_ARGB, DRM_FORMAT_ARGB8888 },
- { AV_PIX_FMT_ABGR, DRM_FORMAT_ABGR8888 },
- { AV_PIX_FMT_RGBA, DRM_FORMAT_RGBA8888 },
- { AV_PIX_FMT_BGRA, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_0RGB, DRM_FORMAT_BGRX8888 },
+ { AV_PIX_FMT_0BGR, DRM_FORMAT_RGBX8888 },
+ { AV_PIX_FMT_RGB0, DRM_FORMAT_XBGR8888 },
+ { AV_PIX_FMT_BGR0, DRM_FORMAT_XRGB8888 },
+ { AV_PIX_FMT_ARGB, DRM_FORMAT_BGRA8888 },
+ { AV_PIX_FMT_ABGR, DRM_FORMAT_RGBA8888 },
+ { AV_PIX_FMT_RGBA, DRM_FORMAT_ABGR8888 },
+ { AV_PIX_FMT_BGRA, DRM_FORMAT_ARGB8888 },
Is it possible to compile kmsgrab on big-endian hardware?
Yes.
And you assume that on big-endian hardware, above formats
would still be little-endian?
Or that they would not be used at all and the big-endian bit would
be set?
In that case, it may be simpler to mask the most significant
bit away in the code and use the endianness-aware formats
for FFmpeg in the table above (it would reduce the table size
and make it more readable). Especially as we don't know if
the bit would also be set for the packed yuv formats.
I think that unlike with RGB565 and similar, the big-endian bit does not need to be set for any of these formats, because the opposite-endian form already exists for each one. That is, you can always use DRM_FORMAT_XBGR8888 when the order of bytes in memory is RGBX, regardless of the host endianness.
(I think that's a consistent interpretation?)
If that is what the comments in the header file mean, it is
probably consistent.

Thank you, Carl Eugen
Andy Furniss
2017-09-14 23:08:42 UTC
Permalink
Post by Mark Thompson
---
Now sets the trusted packet flag; otherwise unchanged.
configure | 1 +
libavdevice/Makefile | 1 +
libavdevice/alldevices.c | 1 +
libavdevice/kmsgrab.c | 455 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 458 insertions(+)
create mode 100644 libavdevice/kmsgrab.c
diff --git a/configure b/configure
index 6581c53c1a..76a7591ceb 100755
--- a/configure
+++ b/configure
@@ -3040,6 +3040,7 @@ gdigrab_indev_select="bmp_decoder"
iec61883_indev_deps="libiec61883"
jack_indev_deps="jack"
jack_indev_deps_any="sem_timedwait dispatch_dispatch_h"
+kmsgrab_indev_deps="libdrm"
Doesn't get built for me = doesn't show up as indev after configure
anything special needed?
Andy Furniss
2017-09-14 23:15:39 UTC
Permalink
Post by Andy Furniss
Post by Mark Thompson
---
Now sets the trusted packet flag; otherwise unchanged.
configure | 1 +
libavdevice/Makefile | 1 +
libavdevice/alldevices.c | 1 +
libavdevice/kmsgrab.c | 455
+++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 458 insertions(+)
create mode 100644 libavdevice/kmsgrab.c
diff --git a/configure b/configure
index 6581c53c1a..76a7591ceb 100755
--- a/configure
+++ b/configure
@@ -3040,6 +3040,7 @@ gdigrab_indev_select="bmp_decoder"
iec61883_indev_deps="libiec61883"
jack_indev_deps="jack"
jack_indev_deps_any="sem_timedwait dispatch_dispatch_h"
+kmsgrab_indev_deps="libdrm"
Doesn't get built for me = doesn't show up as indev after configure
anything special needed?
Never mind I found --enable-libdrm (had tried --enable-kmsgrab)
Mark Thompson
2017-09-14 23:59:11 UTC
Permalink
Post by Andy Furniss
Post by Mark Thompson
---
Now sets the trusted packet flag; otherwise unchanged.
  configure                |   1 +
  libavdevice/Makefile     |   1 +
  libavdevice/alldevices.c |   1 +
  libavdevice/kmsgrab.c    | 455 +++++++++++++++++++++++++++++++++++++++++++++++
  4 files changed, 458 insertions(+)
  create mode 100644 libavdevice/kmsgrab.c
diff --git a/configure b/configure
index 6581c53c1a..76a7591ceb 100755
--- a/configure
+++ b/configure
@@ -3040,6 +3040,7 @@ gdigrab_indev_select="bmp_decoder"
  iec61883_indev_deps="libiec61883"
  jack_indev_deps="jack"
  jack_indev_deps_any="sem_timedwait dispatch_dispatch_h"
+kmsgrab_indev_deps="libdrm"
Doesn't get built for me = doesn't show up as indev after configure anything special needed?
Never mind I found --enable-libdrm (had tried --enable-kmsgrab)
I assume you're going to try AMD + Mesa + VAAPI.

VAAPI_DISABLE_INTERLACE=1 ./ffmpeg_g -y -format bgr0 -device /dev/dri/card1 -f kmsgrab -i - -vsync 0 -init_hw_device vaapi=v:/dev/dri/renderD129 -filter_hw_device v -vf 'hwmap,scale_vaapi=w=1920:h=1080:format=nv12' -c:v h264_vaapi -profile 578 -bf 0 out.mp4

Three Mesa issues I had to get around:

* Device derivation doesn't work because the Mesa driver doesn't want to initialise on the DRM master device for some reason; making the matching device separately does work.
* Against current git, you need to reapply the VAAPI_DISABLE_INTERLACE patch and use it - if not, the colour conversion just barfs because it wants interlaced surfaces.
* The postproc scaler seems to only write the luma plane when converting from RGB - this is also visible when uploading normal RGB images, so just a bug somewhere.

With that, it works to record the screen in greyscale...

I have some other Mesa stuff to do queued up (libva2 with VAAPI export for EGL import on AMD), so I'll pursue these further soonish.

- Mark
Andy Furniss
2017-09-15 09:26:16 UTC
Permalink
Post by Mark Thompson
Post by Andy Furniss
Post by Mark Thompson
---
Now sets the trusted packet flag; otherwise unchanged.
configure | 1 +
libavdevice/Makefile | 1 +
libavdevice/alldevices.c | 1 +
libavdevice/kmsgrab.c | 455 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 458 insertions(+)
create mode 100644 libavdevice/kmsgrab.c
diff --git a/configure b/configure
index 6581c53c1a..76a7591ceb 100755
--- a/configure
+++ b/configure
@@ -3040,6 +3040,7 @@ gdigrab_indev_select="bmp_decoder"
iec61883_indev_deps="libiec61883"
jack_indev_deps="jack"
jack_indev_deps_any="sem_timedwait dispatch_dispatch_h"
+kmsgrab_indev_deps="libdrm"
Doesn't get built for me = doesn't show up as indev after configure anything special needed?
Never mind I found --enable-libdrm (had tried --enable-kmsgrab)
I assume you're going to try AMD + Mesa + VAAPI.
VAAPI_DISABLE_INTERLACE=1 ./ffmpeg_g -y -format bgr0 -device /dev/dri/card1 -f kmsgrab -i - -vsync 0 -init_hw_device vaapi=v:/dev/dri/renderD129 -filter_hw_device v -vf 'hwmap,scale_vaapi=w=1920:h=1080:format=nv12' -c:v h264_vaapi -profile 578 -bf 0 out.mp4
* Device derivation doesn't work because the Mesa driver doesn't want to initialise on the DRM master device for some reason; making the matching device separately does work.
* Against current git, you need to reapply the VAAPI_DISABLE_INTERLACE patch and use it - if not, the colour conversion just barfs because it wants interlaced surfaces.
* The postproc scaler seems to only write the luma plane when converting from RGB - this is also visible when uploading normal RGB images, so just a bug somewhere.
With that, it works to record the screen in greyscale...
I have some other Mesa stuff to do queued up (libva2 with VAAPI export for EGL import on AMD), so I'll pursue these further soonish.
Thanks for the info, as it happens I am running some testing patches
from Leo currently that fix up postproc deinterlace now the env is gone.

I'll point Leo at this mail and test this evening.
Post by Mark Thompson
- Mark
_______________________________________________
ffmpeg-devel mailing list
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Andy Furniss
2017-09-19 21:21:24 UTC
Permalink
Post by Mark Thompson
Post by Andy Furniss
Post by Mark Thompson
---
Now sets the trusted packet flag; otherwise unchanged.
configure | 1 +
libavdevice/Makefile | 1 +
libavdevice/alldevices.c | 1 +
libavdevice/kmsgrab.c | 455 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 458 insertions(+)
create mode 100644 libavdevice/kmsgrab.c
diff --git a/configure b/configure
index 6581c53c1a..76a7591ceb 100755
--- a/configure
+++ b/configure
@@ -3040,6 +3040,7 @@ gdigrab_indev_select="bmp_decoder"
iec61883_indev_deps="libiec61883"
jack_indev_deps="jack"
jack_indev_deps_any="sem_timedwait dispatch_dispatch_h"
+kmsgrab_indev_deps="libdrm"
Doesn't get built for me = doesn't show up as indev after configure anything special needed?
Never mind I found --enable-libdrm (had tried --enable-kmsgrab)
I assume you're going to try AMD + Mesa + VAAPI.
VAAPI_DISABLE_INTERLACE=1 ./ffmpeg_g -y -format bgr0 -device /dev/dri/card1 -f kmsgrab -i - -vsync 0 -init_hw_device vaapi=v:/dev/dri/renderD129 -filter_hw_device v -vf 'hwmap,scale_vaapi=w=1920:h=1080:format=nv12' -c:v h264_vaapi -profile 578 -bf 0 out.mp4
* Device derivation doesn't work because the Mesa driver doesn't want to initialise on the DRM master device for some reason; making the matching device separately does work.
* Against current git, you need to reapply the VAAPI_DISABLE_INTERLACE patch and use it - if not, the colour conversion just barfs because it wants interlaced surfaces.
* The postproc scaler seems to only write the luma plane when converting from RGB - this is also visible when uploading normal RGB images, so just a bug somewhere.
With that, it works to record the screen in greyscale...
I have some other Mesa stuff to do queued up (libva2 with VAAPI export for EGL import on AMD), so I'll pursue these further soonish.
Leo just posted a patchset on the mesa list that makes vaapi work better
with this and postproc generally.

You don't need the env with those and kmsgrab is now working for me - up
to a point ...

That point being around 7k frames it will run out of something.

[AVHWFramesContext @ 0x31ed880] Failed to create surface from DRM
object: 2 (resource allocation failed).
[Parsed_hwmap_0 @ 0x3114c40] Failed to map frame: -5.

I see that memory is reducing before this although I still have spare -
is this the same issue you explained on users WRT leaking on decode?
Mark Thompson
2017-09-20 00:14:29 UTC
Permalink
Post by Mark Thompson
Post by Andy Furniss
Post by Mark Thompson
---
Now sets the trusted packet flag; otherwise unchanged.
   configure                |   1 +
   libavdevice/Makefile     |   1 +
   libavdevice/alldevices.c |   1 +
   libavdevice/kmsgrab.c    | 455 +++++++++++++++++++++++++++++++++++++++++++++++
   4 files changed, 458 insertions(+)
   create mode 100644 libavdevice/kmsgrab.c
diff --git a/configure b/configure
index 6581c53c1a..76a7591ceb 100755
--- a/configure
+++ b/configure
@@ -3040,6 +3040,7 @@ gdigrab_indev_select="bmp_decoder"
   iec61883_indev_deps="libiec61883"
   jack_indev_deps="jack"
   jack_indev_deps_any="sem_timedwait dispatch_dispatch_h"
+kmsgrab_indev_deps="libdrm"
Doesn't get built for me = doesn't show up as indev after configure anything special needed?
Never mind I found --enable-libdrm (had tried --enable-kmsgrab)
I assume you're going to try AMD + Mesa + VAAPI.
VAAPI_DISABLE_INTERLACE=1 ./ffmpeg_g -y -format bgr0 -device /dev/dri/card1 -f kmsgrab -i - -vsync 0 -init_hw_device vaapi=v:/dev/dri/renderD129 -filter_hw_device v -vf 'hwmap,scale_vaapi=w=1920:h=1080:format=nv12' -c:v h264_vaapi -profile 578 -bf 0 out.mp4
* Device derivation doesn't work because the Mesa driver doesn't want to initialise on the DRM master device for some reason; making the matching device separately does work.
* Against current git, you need to reapply the VAAPI_DISABLE_INTERLACE patch and use it - if not, the colour conversion just barfs because it wants interlaced surfaces.
* The postproc scaler seems to only write the luma plane when converting from RGB - this is also visible when uploading normal RGB images, so just a bug somewhere.
With that, it works to record the screen in greyscale...
I have some other Mesa stuff to do queued up (libva2 with VAAPI export for EGL import on AMD), so I'll pursue these further soonish.
Leo just posted a patchset on the mesa list that makes vaapi work better with this and postproc generally.
You don't need the env with those and kmsgrab is now working for me - up to a point ...
Yep, that works for me now too - with colour! (The export to GL for playback is still messed up by interlacing without patching it out, but that looked orthogonal anyway.)
That point being around 7k frames it will run out of something.
I see that memory is reducing before this although I still have spare - is this the same issue you explained on users WRT leaking on decode?
Yeah, I also run out of ... something ... at around 7200 frames. It's not fds or memory. I don't think it's the buffer problem (which, incidentally, should finally be fixable sensibly in libva2 soon), because that ended up manifesting as leaking memory. It's also not a problem for Intel (I've already been running that for a long time to test). Maybe some other sort of handle on the Mesa side? I'll investigate further tomorrow.

Thanks,

- Mark
Andy Furniss
2017-09-20 16:10:57 UTC
Permalink
Post by Mark Thompson
Post by Andy Furniss
That point being around 7k frames it will run out of something.
I see that memory is reducing before this although I still have spare - is this the same issue you explained on users WRT leaking on decode?
Yeah, I also run out of ... something ... at around 7200 frames. It's not fds or memory. I don't think it's the buffer problem (which, incidentally, should finally be fixable sensibly in libva2 soon), because that ended up manifesting as leaking memory. It's also not a problem for Intel (I've already been running that for a long time to test). Maybe some other sort of handle on the Mesa side? I'll investigate further tomorrow.
Leo has fixed the leak.
Mark Thompson
2017-09-20 21:07:22 UTC
Permalink
Post by Andy Furniss
Post by Andy Furniss
That point being around 7k frames it will run out of something.
I see that memory is reducing before this although I still have spare - is this the same issue you explained on users WRT leaking on decode?
Yeah, I also run out of ... something ... at around 7200 frames.  It's not fds or memory.  I don't think it's the buffer problem (which, incidentally, should finally be fixable sensibly in libva2 soon), because that ended up manifesting as leaking memory.  It's also not a problem for Intel (I've already been running that for a long time to test).  Maybe some other sort of handle on the Mesa side?  I'll investigate further tomorrow.
Leo has fixed the leak.
Yep, checked with the updated Mesa postproc patches + libva2 fixes and it all looks good now. (And colours are even correct, yay! I still need to look into why the default comes out wrong with the postproc bits for that on Intel...)

Thanks,

- Mark
Andy Furniss
2017-09-24 10:08:30 UTC
Permalink
Post by Mark Thompson
Post by Andy Furniss
Post by Mark Thompson
Post by Andy Furniss
That point being around 7k frames it will run out of something.
I see that memory is reducing before this although I still have spare - is this the same issue you explained on users WRT leaking on decode?
Yeah, I also run out of ... something ... at around 7200 frames. It's not fds or memory. I don't think it's the buffer problem (which, incidentally, should finally be fixable sensibly in libva2 soon), because that ended up manifesting as leaking memory. It's also not a problem for Intel (I've already been running that for a long time to test). Maybe some other sort of handle on the Mesa side? I'll investigate further tomorrow.
Leo has fixed the leak.
Yep, checked with the updated Mesa postproc patches + libva2 fixes and it all looks good now. (And colours are even correct, yay! I still need to look into why the default comes out wrong with the postproc bits for that on Intel...)
One thing that comes out in testing the patches WRT postproc is that for
deinterlace 1080i25 -> 1080p50 there is an issue with the surface size
being 1088.
This means the result gets scaled to 1088. It is possible to put scale
after to get 1080, but this seems sub-optimal, costs about 8% perf and
hypothetical quality issue (not that I could see it).

Apparently vaapi_scale does things differently and the driver is set for
that way, so changing in the driver will break scale.

AFAICT this is orthogonal to the 1080/1088 encode issue discussed long
ago where ffmpeg is doing the right thing, that should be fixable in the
driver.

This case can be demonstrated with 1920x1080 nv12 hwupload -> deint ->
hwdownload, so no chance for h26x cropping info being used.

Loading...