Files
ffmpeg-rockchip/libavutil/hwcontext_rkmpp.c
nyanmisaka f37c17d024 fixup! lavu: add RKMPP hwcontext
fix width stride align 16 of rgb24 in rga3.

Signed-off-by: nyanmisaka <nst799610810@gmail.com>
2025-06-05 00:17:34 +08:00

603 lines
19 KiB
C

/*
* 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"
#define _GNU_SOURCE
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
/* This was introduced in version 4.6. And may not exist all without an
* optional package. So to prevent a hard dependency on needing the Linux
* kernel headers to compile, make this optional. */
#if HAVE_LINUX_DMA_BUF_H
#include <linux/dma-buf.h>
#include <sys/ioctl.h>
#endif
#include "avassert.h"
#include "hwcontext.h"
#include "hwcontext_rkmpp.h"
#include "hwcontext_internal.h"
#include "imgutils.h"
static const struct {
enum AVPixelFormat pixfmt;
uint32_t drm_format;
} supported_formats[] = {
/* grayscale */
{ AV_PIX_FMT_GRAY8, DRM_FORMAT_R8, },
/* fully-planar YUV */
{ AV_PIX_FMT_YUV420P, DRM_FORMAT_YUV420, },
{ AV_PIX_FMT_YUVJ420P, DRM_FORMAT_YUV420, },
{ AV_PIX_FMT_YUV422P, DRM_FORMAT_YUV422, },
{ AV_PIX_FMT_YUVJ422P, DRM_FORMAT_YUV422, },
{ AV_PIX_FMT_YUV444P, DRM_FORMAT_YUV444, },
{ AV_PIX_FMT_YUVJ444P, DRM_FORMAT_YUV444, },
/* semi-planar YUV */
{ AV_PIX_FMT_NV12, DRM_FORMAT_NV12, },
{ AV_PIX_FMT_NV21, DRM_FORMAT_NV21, },
{ AV_PIX_FMT_NV16, DRM_FORMAT_NV16, },
{ AV_PIX_FMT_NV24, DRM_FORMAT_NV24, },
{ AV_PIX_FMT_NV42, DRM_FORMAT_NV42, },
/* semi-planar YUV 10-bit */
{ AV_PIX_FMT_P010, DRM_FORMAT_P010, },
{ AV_PIX_FMT_P210, DRM_FORMAT_P210, },
{ AV_PIX_FMT_NV15, DRM_FORMAT_NV15, },
{ AV_PIX_FMT_NV20, DRM_FORMAT_NV20, },
/* packed YUV */
{ AV_PIX_FMT_YUYV422, DRM_FORMAT_YUYV, },
{ AV_PIX_FMT_YVYU422, DRM_FORMAT_YVYU, },
{ AV_PIX_FMT_UYVY422, DRM_FORMAT_UYVY, },
/* packed RGB */
{ AV_PIX_FMT_RGB444LE, DRM_FORMAT_XRGB4444, },
{ AV_PIX_FMT_RGB444BE, DRM_FORMAT_XRGB4444 | DRM_FORMAT_BIG_ENDIAN, },
{ AV_PIX_FMT_BGR444LE, DRM_FORMAT_XBGR4444, },
{ AV_PIX_FMT_BGR444BE, DRM_FORMAT_XBGR4444 | DRM_FORMAT_BIG_ENDIAN, },
{ 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_RGBA, DRM_FORMAT_ABGR8888, },
{ AV_PIX_FMT_RGB0, DRM_FORMAT_XBGR8888, },
{ AV_PIX_FMT_BGRA, DRM_FORMAT_ARGB8888, },
{ AV_PIX_FMT_BGR0, DRM_FORMAT_XRGB8888, },
{ AV_PIX_FMT_ARGB, DRM_FORMAT_BGRA8888, },
{ AV_PIX_FMT_0RGB, DRM_FORMAT_BGRX8888, },
{ AV_PIX_FMT_ABGR, DRM_FORMAT_RGBA8888, },
{ AV_PIX_FMT_0BGR, DRM_FORMAT_RGBX8888, },
{ AV_PIX_FMT_X2RGB10LE, DRM_FORMAT_XRGB2101010, },
{ AV_PIX_FMT_X2RGB10BE, DRM_FORMAT_XRGB2101010 | DRM_FORMAT_BIG_ENDIAN, },
{ AV_PIX_FMT_X2BGR10LE, DRM_FORMAT_XBGR2101010, },
{ AV_PIX_FMT_X2BGR10BE, DRM_FORMAT_XBGR2101010 | DRM_FORMAT_BIG_ENDIAN, },
};
static int rkmpp_device_create(AVHWDeviceContext *hwdev, const char *device,
AVDictionary *opts, int flags)
{
AVRKMPPDeviceContext *hwctx = hwdev->hwctx;
AVDictionaryEntry *opt_d = NULL;
hwctx->flags = MPP_BUFFER_FLAGS_DMA32;
opt_d = av_dict_get(opts, "dma32", NULL, 0);
if (opt_d && !strtol(opt_d->value, NULL, 10))
hwctx->flags &= ~MPP_BUFFER_FLAGS_DMA32;
opt_d = av_dict_get(opts, "cacheable", NULL, 0);
if (opt_d && strtol(opt_d->value, NULL, 10))
hwctx->flags |= MPP_BUFFER_FLAGS_CACHABLE;
return 0;
}
static int rkmpp_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 rkmpp_free_drm_frame_descriptor(void *opaque, uint8_t *data)
{
MppBuffer mpp_buf = opaque;
AVRKMPPDRMFrameDescriptor *desc = (AVRKMPPDRMFrameDescriptor *)data;
int ret;
if (!desc)
return;
if (mpp_buf) {
ret = mpp_buffer_put(mpp_buf);
if (ret != MPP_OK)
av_log(NULL, AV_LOG_WARNING,
"Failed to put MPP buffer: %d\n", ret);
}
memset(desc, 0, sizeof(*desc));
av_free(desc);
}
static int rkmpp_get_aligned_linesize(enum AVPixelFormat pix_fmt, int width, int plane)
{
const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(pix_fmt);
const int is_rgb = pixdesc->flags & AV_PIX_FMT_FLAG_RGB;
const int is_yuv = !is_rgb && pixdesc->nb_components >= 2;
const int is_planar = pixdesc->flags & AV_PIX_FMT_FLAG_PLANAR;
const int is_packed_fmt = is_rgb || (!is_rgb && !is_planar);
const int is_fully_planar = is_planar &&
pixdesc->comp[1].plane != pixdesc->comp[2].plane;
int linesize;
if (pix_fmt == AV_PIX_FMT_NV15 ||
pix_fmt == AV_PIX_FMT_NV20) {
const int width_align_256_odds = FFALIGN(width, 256) | 256;
return FFALIGN(width_align_256_odds * 10 / 8, 64);
}
linesize = av_image_get_linesize(pix_fmt, width, plane);
if (is_packed_fmt) {
const int pixel_width = av_get_padded_bits_per_pixel(pixdesc) / 8;
linesize = FFALIGN(linesize / pixel_width, 16) * pixel_width;
} else if (is_yuv && is_fully_planar) {
linesize = FFALIGN(linesize, 16 >> (plane ? pixdesc->log2_chroma_w : 0));
} else
linesize = FFALIGN(linesize, 64);
return linesize;
}
static AVBufferRef *rkmpp_drm_pool_alloc(void *opaque, size_t size)
{
int ret;
AVHWFramesContext *hwfc = opaque;
AVRKMPPFramesContext *avfc = hwfc->hwctx;
AVRKMPPDeviceContext *hwctx = hwfc->device_ctx->hwctx;
AVRKMPPDRMFrameDescriptor *desc;
AVDRMLayerDescriptor *layer;
AVBufferRef *ref;
int i;
const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(hwfc->sw_format);
const int bits_pp = av_get_padded_bits_per_pixel(pixdesc);
const int aligned_w = FFALIGN(hwfc->width * 5 / 4, 64);
const int aligned_h = FFALIGN(hwfc->height * 5 / 4, 64);
MppBuffer mpp_buf = NULL;
size_t mpp_buf_size = aligned_w * aligned_h * bits_pp / 8;
if (hwfc->initial_pool_size > 0 &&
avfc->nb_frames >= hwfc->initial_pool_size)
return NULL;
desc = av_mallocz(sizeof(*desc));
if (!desc)
return NULL;
desc->drm_desc.nb_objects = 1;
desc->drm_desc.nb_layers = 1;
ret = mpp_buffer_get(avfc->buf_group, &mpp_buf, mpp_buf_size);
if (ret != MPP_OK || !mpp_buf) {
av_log(hwctx, AV_LOG_ERROR, "Failed to get MPP buffer: %d\n", ret);
ret = AVERROR(ENOMEM);
goto fail;
}
desc->buffers[0] = mpp_buf;
desc->drm_desc.objects[0].fd = mpp_buffer_get_fd(mpp_buf);
desc->drm_desc.objects[0].size = mpp_buffer_get_size(mpp_buf);
layer = &desc->drm_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 =
rkmpp_get_aligned_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].offset +
layer->planes[i-1].pitch * (FFALIGN(hwfc->height, 2) >> (i > 1 ? pixdesc->log2_chroma_h : 0));
layer->planes[i].pitch =
rkmpp_get_aligned_linesize(hwfc->sw_format, hwfc->width, i);
}
ref = av_buffer_create((uint8_t*)desc, sizeof(*desc), rkmpp_free_drm_frame_descriptor,
mpp_buf, 0);
if (!ref) {
av_log(hwfc, AV_LOG_ERROR, "Failed to create RKMPP buffer.\n");
goto fail;
}
if (hwfc->initial_pool_size > 0) {
av_assert0(avfc->nb_frames < hwfc->initial_pool_size);
memcpy(&avfc->frames[avfc->nb_frames], desc, sizeof(*desc));
++avfc->nb_frames;
}
return ref;
fail:
rkmpp_free_drm_frame_descriptor(mpp_buf, (uint8_t *)desc);
return NULL;
}
static int rkmpp_frames_init(AVHWFramesContext *hwfc)
{
AVRKMPPFramesContext *avfc = hwfc->hwctx;
AVRKMPPDeviceContext *hwctx = hwfc->device_ctx->hwctx;
int i, ret;
if (hwfc->pool)
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);
}
avfc->nb_frames = 0;
avfc->frames = NULL;
if (hwfc->initial_pool_size > 0) {
avfc->frames = av_malloc(hwfc->initial_pool_size *
sizeof(*avfc->frames));
if (!avfc->frames)
return AVERROR(ENOMEM);
}
ret = mpp_buffer_group_get_internal(&avfc->buf_group,
MPP_BUFFER_TYPE_DRM | hwctx->flags | avfc->flags);
if (ret != MPP_OK) {
av_log(hwfc, AV_LOG_ERROR, "Failed to get MPP internal buffer group: %d\n", ret);
return AVERROR_EXTERNAL;
}
hwfc->internal->pool_internal =
av_buffer_pool_init2(sizeof(AVRKMPPDRMFrameDescriptor), hwfc,
rkmpp_drm_pool_alloc, NULL);
if (!hwfc->internal->pool_internal) {
av_log(hwfc, AV_LOG_ERROR, "Failed to create RKMPP buffer pool.\n");
return AVERROR(ENOMEM);
}
return 0;
}
static void rkmpp_frames_uninit(AVHWFramesContext *hwfc)
{
AVRKMPPFramesContext *avfc = hwfc->hwctx;
av_freep(&avfc->frames);
if (avfc->buf_group) {
mpp_buffer_group_put(avfc->buf_group);
avfc->buf_group = NULL;
}
}
static int rkmpp_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 RKMPPDRMMapping {
// Address and length of each mmap()ed region.
int nb_regions;
int sync_flags;
int object[AV_DRM_MAX_PLANES];
void *address[AV_DRM_MAX_PLANES];
size_t length[AV_DRM_MAX_PLANES];
int unmap[AV_DRM_MAX_PLANES];
} RKMPPDRMMapping;
static void rkmpp_unmap_frame(AVHWFramesContext *hwfc,
HWMapDescriptor *hwmap)
{
AVRKMPPFramesContext *avfc = hwfc->hwctx;
RKMPPDRMMapping *map = hwmap->priv;
for (int i = 0; i < map->nb_regions; i++) {
#if HAVE_LINUX_DMA_BUF_H
struct dma_buf_sync sync = { .flags = DMA_BUF_SYNC_END | map->sync_flags };
if (avfc->flags & MPP_BUFFER_FLAGS_CACHABLE)
ioctl(map->object[i], DMA_BUF_IOCTL_SYNC, &sync);
#endif
if (map->address[i] && map->unmap[i])
munmap(map->address[i], map->length[i]);
}
av_free(map);
}
static int rkmpp_map_frame(AVHWFramesContext *hwfc,
AVFrame *dst, const AVFrame *src, int flags)
{
AVRKMPPFramesContext *avfc = hwfc->hwctx;
const AVRKMPPDRMFrameDescriptor *desc = (AVRKMPPDRMFrameDescriptor *)src->data[0];
#if HAVE_LINUX_DMA_BUF_H
struct dma_buf_sync sync_start = { 0 };
#endif
RKMPPDRMMapping *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;
#if HAVE_LINUX_DMA_BUF_H
if (flags & AV_HWFRAME_MAP_READ)
map->sync_flags |= DMA_BUF_SYNC_READ;
if (flags & AV_HWFRAME_MAP_WRITE)
map->sync_flags |= DMA_BUF_SYNC_WRITE;
sync_start.flags = DMA_BUF_SYNC_START | map->sync_flags;
#endif
if (desc->drm_desc.objects[0].format_modifier != DRM_FORMAT_MOD_LINEAR) {
av_log(hwfc, AV_LOG_ERROR, "Transfer non-linear DRM_PRIME frame is not supported!\n");
return AVERROR(ENOSYS);
}
av_assert0(desc->drm_desc.nb_objects <= AV_DRM_MAX_PLANES);
for (i = 0; i < desc->drm_desc.nb_objects; i++) {
addr = NULL;
if (desc->buffers[i])
addr = mpp_buffer_get_ptr(desc->buffers[i]);
if (addr) {
map->unmap[i] = 0;
} else {
addr = mmap(NULL, desc->drm_desc.objects[i].size, mmap_prot, MAP_SHARED,
desc->drm_desc.objects[i].fd, 0);
if (addr == MAP_FAILED) {
err = AVERROR(errno);
av_log(hwfc, AV_LOG_ERROR, "Failed to map RKMPP object %d to "
"memory: %d.\n", desc->drm_desc.objects[i].fd, errno);
goto fail;
}
map->unmap[i] = 1;
}
map->address[i] = addr;
map->length[i] = desc->drm_desc.objects[i].size;
map->object[i] = desc->drm_desc.objects[i].fd;
#if HAVE_LINUX_DMA_BUF_H
/* We're not checking for errors here because the kernel may not
* support the ioctl, in which case its okay to carry on */
if (avfc->flags & MPP_BUFFER_FLAGS_CACHABLE)
ioctl(desc->drm_desc.objects[i].fd, DMA_BUF_IOCTL_SYNC, &sync_start);
#endif
}
map->nb_regions = i;
plane = 0;
for (i = 0; i < desc->drm_desc.nb_layers; i++) {
const AVDRMLayerDescriptor *layer = &desc->drm_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,
&rkmpp_unmap_frame, map);
if (err < 0)
goto fail;
return 0;
fail:
for (i = 0; i < desc->drm_desc.nb_objects; i++) {
if (map->address[i] && map->unmap[i])
munmap(map->address[i], map->length[i]);
}
av_free(map);
return err;
}
static int rkmpp_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 rkmpp_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 = rkmpp_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 rkmpp_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 = rkmpp_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 rkmpp_map_from(AVHWFramesContext *hwfc, AVFrame *dst,
const AVFrame *src, int flags)
{
int err;
if (hwfc->sw_format != dst->format)
return AVERROR(ENOSYS);
err = rkmpp_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_rkmpp = {
.type = AV_HWDEVICE_TYPE_RKMPP,
.name = "RKMPP",
.device_hwctx_size = sizeof(AVRKMPPDeviceContext),
.frames_hwctx_size = sizeof(AVRKMPPFramesContext),
.device_create = &rkmpp_device_create,
.frames_get_constraints = &rkmpp_frames_get_constraints,
.frames_get_buffer = &rkmpp_get_buffer,
.frames_init = &rkmpp_frames_init,
.frames_uninit = &rkmpp_frames_uninit,
.transfer_get_formats = &rkmpp_transfer_get_formats,
.transfer_data_to = &rkmpp_transfer_data_to,
.transfer_data_from = &rkmpp_transfer_data_from,
.map_from = &rkmpp_map_from,
.pix_fmts = (const enum AVPixelFormat[]) {
AV_PIX_FMT_DRM_PRIME,
AV_PIX_FMT_NONE
},
};