From 513922ae934cb4a7d399e03a9420ecb211ca45aa Mon Sep 17 00:00:00 2001 From: Hertz Wang 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 --- 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 #include #include -#include +#include #include #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 /* 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: %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