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
|