linuxOS_AP05/buildroot/package/ffmpeg/0005-hwcontext_drm-internal-frame-allocation.patch
2025-06-02 13:59:07 +08:00

422 lines
14 KiB
Diff

From 513922ae934cb4a7d399e03a9420ecb211ca45aa Mon Sep 17 00:00:00 2001
From: Hertz Wang <wangh@rock-chips.com>
Date: Mon, 5 Nov 2018 20:56:00 +0800
Subject: [PATCH 05/11] hwcontext_drm: internal frame allocation
Change-Id: Iba12ba90f5395fb72cf39e96e266bb63daaec231
Signed-off-by: Hertz Wang <wangh@rock-chips.com>
---
libavutil/hwcontext_drm.c | 317 +++++++++++++++++++++++++++++++++++++++++++++-
libavutil/hwcontext_drm.h | 10 +-
2 files changed, 320 insertions(+), 7 deletions(-)
diff --git a/libavutil/hwcontext_drm.c b/libavutil/hwcontext_drm.c
index 32cbde8..29cfaff 100644
--- a/libavutil/hwcontext_drm.c
+++ b/libavutil/hwcontext_drm.c
@@ -16,11 +16,12 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#define _GNU_SOURCE
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
-#include <drm.h>
+#include <drm_fourcc.h>
#include <xf86drm.h>
#include "avassert.h"
@@ -29,6 +30,100 @@
#include "hwcontext_internal.h"
#include "imgutils.h"
+/**
+ * Copy from libdrm_macros.h while is not exposed by libdrm,
+ * be replaced by #include "libdrm_macros.h" someday.
+ */
+
+/**
+ * Static (compile-time) assertion.
+ * Basically, use COND to dimension an array. If COND is false/zero the
+ * array size will be -1 and we'll get a compilation error.
+ */
+#define STATIC_ASSERT(COND) \
+ do { \
+ (void) sizeof(char [1 - 2*!(COND)]); \
+ } while (0)
+
+#if defined(ANDROID) && !defined(__LP64__)
+#include <errno.h> /* for EINVAL */
+
+extern void *__mmap2(void *, size_t, int, int, int, size_t);
+
+static inline void *drm_mmap(void *addr, size_t length, int prot, int flags,
+ int fd, loff_t offset)
+{
+ /* offset must be aligned to 4096 (not necessarily the page size) */
+ if (offset & 4095) {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+
+ return __mmap2(addr, length, prot, flags, fd, (size_t) (offset >> 12));
+}
+
+# define drm_munmap(addr, length) \
+ munmap(addr, length)
+
+#else
+
+/* assume large file support exists */
+# define drm_mmap(addr, length, prot, flags, fd, offset) \
+ mmap(addr, length, prot, flags, fd, offset)
+
+static inline int drm_munmap(void *addr, size_t length)
+{
+ /* Copied from configure code generated by AC_SYS_LARGEFILE */
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + \
+ (((off_t) 1 << 31) << 31))
+ STATIC_ASSERT(LARGE_OFF_T % 2147483629 == 721 &&
+ LARGE_OFF_T % 2147483647 == 1);
+#undef LARGE_OFF_T
+
+ return munmap(addr, length);
+}
+#endif
+
+// default
+static int card_index = 0;
+
+static const struct {
+ enum AVPixelFormat pixfmt;
+ uint32_t drm_format;
+} supported_formats[] = {
+#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_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, },
+ { 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, },
+ { AV_PIX_FMT_NV16, DRM_FORMAT_NV16, },
+ { AV_PIX_FMT_YUV422P, DRM_FORMAT_YUV422, },
+ { AV_PIX_FMT_NV21, DRM_FORMAT_NV21, },
+ { AV_PIX_FMT_NV12, DRM_FORMAT_NV12, },
+ { AV_PIX_FMT_P010, DRM_FORMAT_NV12_10, },
+ { AV_PIX_FMT_YUV420P, DRM_FORMAT_YUV420, },
+};
static void drm_device_free(AVHWDeviceContext *hwdev)
{
@@ -42,7 +137,14 @@ static int drm_device_create(AVHWDeviceContext *hwdev, const char *device,
{
AVDRMDeviceContext *hwctx = hwdev->hwctx;
drmVersionPtr version;
+ char drm_dev[] = "/dev/dri/card0000";
+ uint64_t has_dumb;
+ if (!device) {
+ snprintf(drm_dev, sizeof(drm_dev), DRM_DEV_NAME, DRM_DIR_NAME,
+ card_index);
+ device = drm_dev;
+ }
hwctx->fd = open(device, O_RDWR);
if (hwctx->fd < 0)
return AVERROR(errno);
@@ -62,11 +164,219 @@ static int drm_device_create(AVHWDeviceContext *hwdev, const char *device,
drmFreeVersion(version);
+ if (drmGetCap(hwctx->fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 ||
+ !has_dumb) {
+ av_log(hwdev, AV_LOG_ERROR, "drm device '%s' "
+ "does not support dumb buffers\n", device);
+ close(hwctx->fd);
+ return AVERROR(EINVAL);
+ }
+
hwdev->free = &drm_device_free;
return 0;
}
+static int drm_frames_get_constraints(AVHWDeviceContext *hwdev,
+ const void *hwconfig,
+ AVHWFramesConstraints *constraints)
+{
+ int i;
+
+ constraints->min_width = 16;
+ constraints->min_height = 16;
+
+ constraints->valid_hw_formats =
+ av_malloc_array(2, sizeof(enum AVPixelFormat));
+ if (!constraints->valid_hw_formats)
+ return AVERROR(ENOMEM);
+ constraints->valid_hw_formats[0] = AV_PIX_FMT_DRM_PRIME;
+ constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
+
+ constraints->valid_sw_formats =
+ av_malloc_array(FF_ARRAY_ELEMS(supported_formats) + 1,
+ sizeof(enum AVPixelFormat));
+ if (!constraints->valid_sw_formats)
+ return AVERROR(ENOMEM);
+ for(i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++)
+ constraints->valid_sw_formats[i] = supported_formats[i].pixfmt;
+ constraints->valid_sw_formats[i] = AV_PIX_FMT_NONE;
+
+ return 0;
+}
+
+static void free_drm_frame_descriptor(AVDRMDeviceContext *hwctx,
+ AVDRMFrameDescriptor *desc)
+{
+ int i;
+ if (!desc)
+ return;
+
+ for (i = 0; i < desc->nb_objects; i++) {
+ AVDRMObjectDescriptor *object = &desc->objects[i];
+ if (object->ptr)
+ drm_munmap(object->ptr, object->size);
+ if (object->fd > 0) {
+ int ret;
+ uint32_t handle = 0;
+
+ ret = drmPrimeFDToHandle(hwctx->fd, object->fd, &handle);
+ if (ret)
+ av_log(NULL, AV_LOG_WARNING,
+ "Failed to convert drm fd to handle: %m\n");
+ if (handle > 0) {
+ struct drm_mode_destroy_dumb data = {
+ .handle = handle,
+ };
+ ret = drmIoctl(hwctx->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &data);
+ if (ret)
+ av_log(NULL, AV_LOG_WARNING,
+ "Failed to free drm handle: %m\n");
+ }
+
+ ret = close(object->fd);
+ if (ret)
+ av_log(NULL, AV_LOG_WARNING,
+ "Failed to close drm buffer fd = %d: %m\n", object->fd);
+ }
+ }
+
+ memset(desc, 0, sizeof(*desc));
+ av_free(desc);
+}
+
+static void drm_buffer_free(void *opaque, uint8_t *data)
+{
+ AVHWFramesContext *hwfc = opaque;
+ AVDRMDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+ AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)data;
+
+ free_drm_frame_descriptor(hwctx, desc);
+}
+
+static AVBufferRef *drm_pool_alloc(void *opaque, int size)
+{
+ int ret;
+ AVHWFramesContext *hwfc = opaque;
+ AVDRMDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+ AVDRMFrameDescriptor *desc;
+ AVDRMLayerDescriptor *layer;
+ AVBufferRef *ref;
+
+ int i;
+ const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(hwfc->sw_format);
+ struct drm_mode_create_dumb dmcb;
+ struct drm_mode_map_dumb dmmd;
+
+ desc = av_mallocz(sizeof(*desc));
+ if (!desc)
+ return NULL;
+
+ memset(&dmcb, 0, sizeof(struct drm_mode_create_dumb));
+ dmcb.bpp = av_get_bits_per_pixel(pixdesc);
+ dmcb.width = FFALIGN(hwfc->width, 16);
+ dmcb.height = FFALIGN(hwfc->height, 16);
+ ret = drmIoctl(hwctx->fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcb);
+ if (ret < 0) {
+ av_log(hwfc, AV_LOG_ERROR,
+ "Failed to create dumb<w,h,bpp: %d,%d,%d>: %m.\n",
+ dmcb.width, dmcb.height, dmcb.bpp);
+ goto fail;
+ }
+ av_assert0(dmcb.size >= dmcb.width * dmcb.height * dmcb.bpp / 8);
+
+ desc->nb_objects = 1;
+ desc->nb_layers = 1;
+ ret = drmPrimeHandleToFD(hwctx->fd, dmcb.handle, DRM_CLOEXEC,
+ &desc->objects[0].fd);
+ if (ret) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to convert handle to fd: %m\n");
+ goto fail;
+ }
+ memset(&dmmd, 0, sizeof(dmmd));
+ dmmd.handle = dmcb.handle;
+
+ ret = drmIoctl(hwctx->fd, DRM_IOCTL_MODE_MAP_DUMB, &dmmd);
+ if (ret) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to map dumb: %m\n");
+ goto fail;
+ }
+
+ // default read and write
+ desc->objects[0].ptr = drm_mmap(NULL, dmcb.size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, hwctx->fd, dmmd.offset);
+ if (desc->objects[0].ptr == MAP_FAILED) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to drm_mmap: %m\n");
+ goto fail;
+ }
+
+ desc->objects[0].size = dmcb.size;
+
+ layer = &desc->layers[0];
+ for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
+ if (supported_formats[i].pixfmt == hwfc->sw_format) {
+ layer->format = supported_formats[i].drm_format;
+ break;
+ }
+ }
+ layer->nb_planes = av_pix_fmt_count_planes(hwfc->sw_format);
+ layer->planes[0].object_index = 0;
+ layer->planes[0].offset = 0;
+ layer->planes[0].pitch =
+ av_image_get_linesize(hwfc->sw_format, hwfc->width, 0);
+
+ for (i = 1; i < layer->nb_planes; i++) {
+ layer->planes[i].object_index = 0;
+ layer->planes[i].offset = layer->planes[i-1].pitch * hwfc->height;
+ layer->planes[i].pitch =
+ av_image_get_linesize(hwfc->sw_format, hwfc->width, i);
+ }
+
+ ref = av_buffer_create((uint8_t*)desc, sizeof(*desc), drm_buffer_free,
+ opaque, 0);
+ if (!ref) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to create drm buffer.\n");
+ goto fail;
+ }
+
+ return ref;
+
+fail:
+ free_drm_frame_descriptor(hwctx, desc);
+ return NULL;
+}
+
+static int drm_frames_init(AVHWFramesContext *hwfc)
+{
+ int i;
+ if (hwfc->pool) {
+ // has been set outside?
+ return 0;
+ }
+
+ for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++)
+ if (supported_formats[i].pixfmt == hwfc->sw_format)
+ break;
+ if (i >= FF_ARRAY_ELEMS(supported_formats)) {
+ av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
+ av_get_pix_fmt_name(hwfc->sw_format));
+ return AVERROR(EINVAL);
+ }
+
+ hwfc->internal->pool_internal =
+ av_buffer_pool_init2(sizeof(AVDRMFrameDescriptor), hwfc, drm_pool_alloc,
+ NULL);
+ if (!hwfc->internal->pool_internal) {
+ av_log(hwfc, AV_LOG_ERROR, "Failed to create drm buffer pool.\n");
+ return AVERROR(ENOMEM);
+ }
+
+ return 0;
+}
+
+static void drm_frames_uninit(AVHWFramesContext *hwfc av_unused)
+{}
+
static int drm_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame)
{
frame->buf[0] = av_buffer_pool_get(hwfc->pool);
@@ -275,8 +585,11 @@ const HWContextType ff_hwcontext_type_drm = {
.device_create = &drm_device_create,
- .frames_get_buffer = &drm_get_buffer,
+ .frames_get_constraints = &drm_frames_get_constraints,
+ .frames_get_buffer = &drm_get_buffer,
+ .frames_init = &drm_frames_init,
+ .frames_uninit = &drm_frames_uninit,
.transfer_get_formats = &drm_transfer_get_formats,
.transfer_data_to = &drm_transfer_data_to,
.transfer_data_from = &drm_transfer_data_from,
diff --git a/libavutil/hwcontext_drm.h b/libavutil/hwcontext_drm.h
index 4c40d15..fe64f97 100644
--- a/libavutil/hwcontext_drm.h
+++ b/libavutil/hwcontext_drm.h
@@ -25,11 +25,6 @@
/**
* @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 {
@@ -53,6 +48,8 @@ typedef struct AVDRMObjectDescriptor {
/**
* DRM PRIME mapped virtual ptr for above fd.
+ *
+ * The content of this buffer must be readonly when acting decoder's out buffer.
*/
void *ptr;
@@ -147,10 +144,13 @@ typedef struct AVDRMFrameDescriptor {
AVDRMObjectDescriptor objects[AV_DRM_MAX_PLANES];
/**
* Number of layers in the frame.
+ *
+ * Set by users if need more than 1.
*/
int nb_layers;
/**
* Array of layers in the frame.
+ * NOTE: total planes of layers must not be more than AV_NUM_DATA_POINTERS.
*/
AVDRMLayerDescriptor layers[AV_DRM_MAX_PLANES];
} AVDRMFrameDescriptor;
--
2.7.4