diff --git a/configure b/configure index 98b582a5d5..27ffb178ce 100755 --- a/configure +++ b/configure @@ -4038,7 +4038,7 @@ avfilter_deps="avutil" avfilter_suggest="libm stdatomic" avformat_deps="avcodec avutil" avformat_suggest="libm network zlib stdatomic" -avutil_suggest="clock_gettime ffnvcodec gcrypt libm libdrm libmfx opencl openssl user32 vaapi vulkan videotoolbox corefoundation corevideo coremedia bcrypt stdatomic" +avutil_suggest="clock_gettime ffnvcodec gcrypt libm libdrm libmfx opencl openssl rkmpp user32 vaapi vulkan videotoolbox corefoundation corevideo coremedia bcrypt stdatomic" postproc_deps="avutil gpl" postproc_suggest="libm stdatomic" swresample_deps="avutil" @@ -7129,8 +7129,8 @@ enabled openssl && { { check_pkg_config openssl "openssl >= 3.0.0" ope check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto -lws2_32 -lgdi32 || die "ERROR: openssl not found"; } enabled pocketsphinx && require_pkg_config pocketsphinx pocketsphinx pocketsphinx/pocketsphinx.h ps_init -enabled rkmpp && { require_pkg_config rkmpp rockchip_mpp rockchip/rk_mpi.h mpp_create && - require_pkg_config rockchip_mpp "rockchip_mpp >= 1.3.7" rockchip/rk_mpi.h mpp_create && +enabled rkmpp && { require_pkg_config rkmpp rockchip_mpp rockchip/rk_mpi.h mpp_create && + require_pkg_config rockchip_mpp "rockchip_mpp >= 1.3.9" rockchip/rk_mpi.h mpp_create && { enabled libdrm || die "ERROR: rkmpp requires --enable-libdrm"; } } diff --git a/libavutil/Makefile b/libavutil/Makefile index 6e6fa8d800..26e9b30ee7 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -52,6 +52,7 @@ HEADERS = adler32.h \ hwcontext_videotoolbox.h \ hwcontext_vdpau.h \ hwcontext_vulkan.h \ + hwcontext_rkmpp.h \ iamf.h \ imgutils.h \ intfloat.h \ @@ -205,6 +206,7 @@ OBJS-$(CONFIG_VAAPI) += hwcontext_vaapi.o OBJS-$(CONFIG_VIDEOTOOLBOX) += hwcontext_videotoolbox.o OBJS-$(CONFIG_VDPAU) += hwcontext_vdpau.o OBJS-$(CONFIG_VULKAN) += hwcontext_vulkan.o vulkan.o +OBJS-$(CONFIG_RKMPP) += hwcontext_rkmpp.o OBJS-$(!CONFIG_VULKAN) += hwcontext_stub.o @@ -228,6 +230,7 @@ SKIPHEADERS-$(CONFIG_VDPAU) += hwcontext_vdpau.h SKIPHEADERS-$(CONFIG_VULKAN) += hwcontext_vulkan.h vulkan.h \ vulkan_functions.h \ vulkan_loader.h +SKIPHEADERS-$(CONFIG_RKMPP) += hwcontext_rkmpp.h TESTPROGS = adler32 \ aes \ diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c index fa99a0d8a4..2dda02dee8 100644 --- a/libavutil/hwcontext.c +++ b/libavutil/hwcontext.c @@ -65,6 +65,9 @@ static const HWContextType * const hw_table[] = { #endif #if CONFIG_VULKAN &ff_hwcontext_type_vulkan, +#endif +#if CONFIG_RKMPP + &ff_hwcontext_type_rkmpp, #endif NULL, }; @@ -82,6 +85,7 @@ static const char *const hw_type_names[] = { [AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox", [AV_HWDEVICE_TYPE_MEDIACODEC] = "mediacodec", [AV_HWDEVICE_TYPE_VULKAN] = "vulkan", + [AV_HWDEVICE_TYPE_RKMPP] = "rkmpp", }; typedef struct FFHWDeviceContext { diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h index bac30debae..1f10233010 100644 --- a/libavutil/hwcontext.h +++ b/libavutil/hwcontext.h @@ -38,6 +38,7 @@ enum AVHWDeviceType { AV_HWDEVICE_TYPE_MEDIACODEC, AV_HWDEVICE_TYPE_VULKAN, AV_HWDEVICE_TYPE_D3D12VA, + AV_HWDEVICE_TYPE_RKMPP, }; /** diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h index e32b786238..e795853cc4 100644 --- a/libavutil/hwcontext_internal.h +++ b/libavutil/hwcontext_internal.h @@ -163,5 +163,6 @@ extern const HWContextType ff_hwcontext_type_vdpau; extern const HWContextType ff_hwcontext_type_videotoolbox; extern const HWContextType ff_hwcontext_type_mediacodec; extern const HWContextType ff_hwcontext_type_vulkan; +extern const HWContextType ff_hwcontext_type_rkmpp; #endif /* AVUTIL_HWCONTEXT_INTERNAL_H */ diff --git a/libavutil/hwcontext_rkmpp.c b/libavutil/hwcontext_rkmpp.c new file mode 100644 index 0000000000..a03f8027be --- /dev/null +++ b/libavutil/hwcontext_rkmpp.c @@ -0,0 +1,603 @@ +/* + * 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 +#include +#include + +/* 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 +#include +#endif + +#include "avassert.h" +#include "hwcontext.h" +#include "hwcontext_rkmpp.h" +#include "hwcontext_internal.h" +#include "imgutils.h" +#include "mem.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; + } + + ffhwframesctx(hwfc)->pool_internal = + av_buffer_pool_init2(sizeof(AVRKMPPDRMFrameDescriptor), hwfc, + rkmpp_drm_pool_alloc, NULL); + if (!ffhwframesctx(hwfc)->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 + }, +}; diff --git a/libavutil/hwcontext_rkmpp.h b/libavutil/hwcontext_rkmpp.h new file mode 100644 index 0000000000..75394699c3 --- /dev/null +++ b/libavutil/hwcontext_rkmpp.h @@ -0,0 +1,153 @@ +/* + * 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_RKMPP_H +#define AVUTIL_HWCONTEXT_RKMPP_H + +#include +#include +#include +#include + +#include "hwcontext_drm.h" + +#ifndef DRM_FORMAT_P010 +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') +#endif +#ifndef DRM_FORMAT_P210 +#define DRM_FORMAT_P210 fourcc_code('P', '2', '1', '0') +#endif +#ifndef DRM_FORMAT_NV15 +#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5') +#endif +#ifndef DRM_FORMAT_NV20 +#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') +#endif +#ifndef DRM_FORMAT_YUV420_8BIT +#define DRM_FORMAT_YUV420_8BIT fourcc_code('Y', 'U', '0', '8') +#endif +#ifndef DRM_FORMAT_YUV420_10BIT +#define DRM_FORMAT_YUV420_10BIT fourcc_code('Y', 'U', '1', '0') +#endif +#ifndef DRM_FORMAT_Y210 +#define DRM_FORMAT_Y210 fourcc_code('Y', '2', '1', '0') +#endif +#ifndef DRM_FORMAT_VUY888 +#define DRM_FORMAT_VUY888 fourcc_code('V', 'U', '2', '4') +#endif + +/* ARM AFBC (16x16) */ +#ifndef DRM_FORMAT_MOD_VENDOR_ARM +#define DRM_FORMAT_MOD_VENDOR_ARM 0x08 +#endif +#ifndef DRM_FORMAT_MOD_ARM_TYPE_AFBC +#define DRM_FORMAT_MOD_ARM_TYPE_AFBC 0x00 +#endif +#ifndef AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 +#define AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 (1ULL) +#endif +#ifndef AFBC_FORMAT_MOD_SPARSE +#define AFBC_FORMAT_MOD_SPARSE (1ULL << 6) +#endif + +#define drm_is_afbc(mod) \ + ((mod >> 52) == (DRM_FORMAT_MOD_ARM_TYPE_AFBC | \ + (DRM_FORMAT_MOD_VENDOR_ARM << 4))) + +/* Rockchip RFBC (64x4) */ +#undef DRM_FORMAT_MOD_VENDOR_ROCKCHIP +#define DRM_FORMAT_MOD_VENDOR_ROCKCHIP 0x0b +#undef DRM_FORMAT_MOD_ROCKCHIP_TYPE_SHIFT +#define DRM_FORMAT_MOD_ROCKCHIP_TYPE_SHIFT 52 +#undef DRM_FORMAT_MOD_ROCKCHIP_TYPE_MASK +#define DRM_FORMAT_MOD_ROCKCHIP_TYPE_MASK 0xf +#undef DRM_FORMAT_MOD_ROCKCHIP_TYPE_RFBC +#define DRM_FORMAT_MOD_ROCKCHIP_TYPE_RFBC 0x1 +#undef ROCKCHIP_RFBC_BLOCK_SIZE_64x4 +#define ROCKCHIP_RFBC_BLOCK_SIZE_64x4 (1ULL) + +#undef fourcc_mod_code +#define fourcc_mod_code(vendor, val) \ + ((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | ((val) & 0x00ffffffffffffffULL)) + +#undef DRM_FORMAT_MOD_ROCKCHIP_CODE +#define DRM_FORMAT_MOD_ROCKCHIP_CODE(__type, __val) \ + fourcc_mod_code(ROCKCHIP, ((__u64)(__type) << DRM_FORMAT_MOD_ROCKCHIP_TYPE_SHIFT) | \ + ((__val) & 0x000fffffffffffffULL)) + +#undef DRM_FORMAT_MOD_ROCKCHIP_RFBC +#define DRM_FORMAT_MOD_ROCKCHIP_RFBC(mode) \ + DRM_FORMAT_MOD_ROCKCHIP_CODE(DRM_FORMAT_MOD_ROCKCHIP_TYPE_RFBC, mode) + +#define drm_is_rfbc(mod) \ + (((mod >> 56) & 0xff) == DRM_FORMAT_MOD_VENDOR_ROCKCHIP) && \ + (((mod >> 52) & DRM_FORMAT_MOD_ROCKCHIP_TYPE_MASK) == DRM_FORMAT_MOD_ROCKCHIP_TYPE_RFBC) + +/** + * DRM Prime Frame descriptor for RKMPP HWDevice. + */ +typedef struct AVRKMPPDRMFrameDescriptor { + /** + * Backwards compatibility with AVDRMFrameDescriptor. + */ + AVDRMFrameDescriptor drm_desc; + + /** + * References to MppBuffer instances which are used + * on each drm frame index. + */ + MppBuffer buffers[AV_DRM_MAX_PLANES]; +} AVRKMPPDRMFrameDescriptor; + +/** + * RKMPP-specific data associated with a frame pool. + * + * Allocated as AVHWFramesContext.hwctx. + */ +typedef struct AVRKMPPFramesContext { + /** + * MPP buffer group. + */ + MppBufferGroup buf_group; + /** + * MPP buffer allocation flags at frames context level. + */ + int flags; + + /** + * The descriptors of all frames in the pool after creation. + * Only valid if AVHWFramesContext.initial_pool_size was positive. + * These are intended to be used as the buffer of RKMPP decoder. + */ + AVRKMPPDRMFrameDescriptor *frames; + int nb_frames; +} AVRKMPPFramesContext; + +/** + * RKMPP device details. + * + * Allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVRKMPPDeviceContext { + /** + * MPP buffer allocation flags at device context level. + */ + int flags; +} AVRKMPPDeviceContext; + +#endif /* AVUTIL_HWCONTEXT_RKMPP_H */