两个问题:
Q1:如果修改了rga的库,能否解决mono8转nv12的问题?
A:根本原因在于 RGA 硬件单元本身不支持 YUV400 → NV12 的颜色空间转换(Color Space Conversion, CSC)。
即使修改了库,底层驱动和硬件仍然会拒绝该操作。
Q2:为什么一定要NV12格式才能使用呢?
A:不是“一定要 NV12”,而是:NV12 是 Rockchip VPU(视频编码器)和 GStreamer 硬件编码插件(如 mpph264enc、rkvenc)最广泛支持、效率最高的输入格式。
根据这两个问题,就有了下面的这篇文章:
纯硬件加速方式:
首先查看目前板端能支持的格式:
grep -r "RK_FORMAT_" /usr/include/rga/
forlinx@ok3588:~$ grep -r "RK_FORMAT_" /usr/include/rga/
/usr/include/rga/rga.h:/* In order to be compatible with RK_FORMAT_XX and HAL_PIXEL_FORMAT_XX,
/usr/include/rga/rga.h: * RK_FORMAT_XX is shifted to the left by 8 bits to distinguish. */
/usr/include/rga/rga.h: RK_FORMAT_RGBA_8888 = 0x0 << 8, /* [0:31] R:G:B:A 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_RGBX_8888 = 0x1 << 8, /* [0:31] R:G:B:X 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_RGB_888 = 0x2 << 8, /* [0:23] R:G:B 8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_BGRA_8888 = 0x3 << 8, /* [0:31] B:G:R:A 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_RGB_565 = 0x4 << 8, /* [0:15] R:G:B 5:6:5 little endian */
/usr/include/rga/rga.h: RK_FORMAT_RGBA_5551 = 0x5 << 8, /* [0:15] R:G:B:A 5:5:5:1 little endian */
/usr/include/rga/rga.h: RK_FORMAT_RGBA_4444 = 0x6 << 8, /* [0:15] R:G:B:A 4:4:4:4 little endian */
/usr/include/rga/rga.h: RK_FORMAT_BGR_888 = 0x7 << 8, /* [0:23] B:G:R 8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_YCbCr_422_SP = 0x8 << 8, /* 2 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_YCbCr_422_P = 0x9 << 8, /* 3 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_YCbCr_420_SP = 0xa << 8, /* 2 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_YCbCr_420_P = 0xb << 8, /* 3 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_YCrCb_422_SP = 0xc << 8, /* 2 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_YCrCb_422_P = 0xd << 8, /* 3 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_YCrCb_420_SP = 0xe << 8, /* 2 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_YCrCb_420_P = 0xf << 8, /* 3 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_BPP1 = 0x10 << 8, /* [0] little endian */
/usr/include/rga/rga.h: RK_FORMAT_BPP2 = 0x11 << 8, /* [0:1] little endian */
/usr/include/rga/rga.h: RK_FORMAT_BPP4 = 0x12 << 8, /* [0:3] little endian */
/usr/include/rga/rga.h: RK_FORMAT_BPP8 = 0x13 << 8, /* [0:7] little endian */
/usr/include/rga/rga.h: RK_FORMAT_Y4 = 0x14 << 8, /* [0:3] Y little endian */
/usr/include/rga/rga.h: RK_FORMAT_YCbCr_400 = 0x15 << 8, /* [0:7] Y little endian */
/usr/include/rga/rga.h: RK_FORMAT_BGRX_8888 = 0x16 << 8, /* [0:31] B:G:R:X 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_YVYU_422 = 0x18 << 8, /* [0:31] Y0:Cr0:Y1:cb0 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_YVYU_420 = 0x19 << 8, /* ODD : [0:31] Y0:Cr0:Y1:cb0 8:8:8:8 little endian
/usr/include/rga/rga.h: RK_FORMAT_VYUY_422 = 0x1a << 8, /* [0:31] Cr0:Y0:Cb0:Y1 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_VYUY_420 = 0x1b << 8, /* ODD : [0:31] Cr0:Y0:Cb0:Y1 8:8:8:8 little endian
/usr/include/rga/rga.h: RK_FORMAT_YUYV_422 = 0x1c << 8, /* [0:31] Y0:Cb0:Y1:cr0 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_YUYV_420 = 0x1d << 8, /* ODD : [0:31] Y0:Cb0:Y1:cr0 8:8:8:8 little endian
/usr/include/rga/rga.h: RK_FORMAT_UYVY_422 = 0x1e << 8, /* [0:31] Cb0:Y0:Cr0:Y1 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_UYVY_420 = 0x1f << 8, /* ODD : [0:31] Cb0:Y0:Cr0:Y1 8:8:8:8 little endian
/usr/include/rga/rga.h: RK_FORMAT_YCbCr_420_SP_10B = 0x20 << 8, /* 2 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_YCrCb_420_SP_10B = 0x21 << 8, /* 2 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_YCbCr_422_SP_10B = 0x22 << 8, /* 2 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_YCrCb_422_SP_10B = 0x23 << 8, /* 2 plane YCbCr little endian
/usr/include/rga/rga.h: RK_FORMAT_YCbCr_422_10b_SP = RK_FORMAT_YCbCr_422_SP_10B << 8,
/usr/include/rga/rga.h: RK_FORMAT_YCrCb_422_10b_SP = RK_FORMAT_YCrCb_422_SP_10B << 8,
/usr/include/rga/rga.h: RK_FORMAT_BGR_565 = 0x24 << 8, /* [0:16] B:G:R 5:6:5 little endian */
/usr/include/rga/rga.h: RK_FORMAT_BGRA_5551 = 0x25 << 8, /* [0:16] B:G:R:A 5:5:5:1 little endian */
/usr/include/rga/rga.h: RK_FORMAT_BGRA_4444 = 0x26 << 8, /* [0:16] B:G:R:A 4:4:4:4 little endian */
/usr/include/rga/rga.h: RK_FORMAT_ARGB_8888 = 0x28 << 8, /* [0:31] A:R:G:B 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_XRGB_8888 = 0x29 << 8, /* [0:31] X:R:G:B 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_ARGB_5551 = 0x2a << 8, /* [0:16] A:R:G:B 5:5:5:1 little endian */
/usr/include/rga/rga.h: RK_FORMAT_ARGB_4444 = 0x2b << 8, /* [0:16] A:R:G:B 4:4:4:4 little endian */
/usr/include/rga/rga.h: RK_FORMAT_ABGR_8888 = 0x2c << 8, /* [0:31] A:B:G:R 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_XBGR_8888 = 0x2d << 8, /* [0:31] X:B:G:R 8:8:8:8 little endian */
/usr/include/rga/rga.h: RK_FORMAT_ABGR_5551 = 0x2e << 8, /* [0:16] A:B:G:R 5:5:5:1 little endian */
/usr/include/rga/rga.h: RK_FORMAT_ABGR_4444 = 0x2f << 8, /* [0:16] A:B:G:R 4:4:4:4 little endian */
/usr/include/rga/rga.h: RK_FORMAT_RGBA2BPP = 0x30 << 8, /* [0:1] Color:Alpha 1:1 little endian */
/usr/include/rga/rga.h: RK_FORMAT_UNKNOWN = 0x100 << 8,
forlinx@ok3588:~$
转换成,选择同为单通道的格式,即8-bit 灰度图格式
RK_FORMAT_Y8 、RK_FORMAT_YCbCr_400
度申相机mono格式转换与rk硬件转换
编译没有问题,但是运行过程中出现下面的错误:
问题根本原因: 不支持单通道灰度图(YUV400)直接转 NV12
Rockchip 的 RGA 模块在RK3588上不支持 YUV400 作为 imcvtcolor 的源格式。 它只支持:
-
RGB 系列(RGB888、BGR888、RGBA 等)
-
YUV420/YUV422 半平面或打包格式(如 NV12、YUYV)
根据编译信息得知支持的格式:
RGBA_8888
RGB_888
RGB_565
YUV420_sp_8bit
YUV420_sp_10bit
YUV422_sp_8bit
YUV422_sp_10bit YUYV422
软件扩展+硬件加速方式
MONO8先转换为RGBA8888,然后再用RGA转换为NV12
MONO8(软件扩展)-> RGBA8888(硬件加速转换)-> NV12
初始化函数:
/**************************** RGA 相关 ****************************/
static bool rga_init(CameraConfig *cam) {
size_t align = 128;
cam->rga.src_size = VIDEO_WIDTH * VIDEO_HEIGHT * 4; // RGBA8888格式
cam->rga.dst_size = VIDEO_WIDTH * VIDEO_HEIGHT * 3 / 2; // NV12大小
cam->rga.src_buf = (unsigned char *)aligned_alloc(align, cam->rga.src_size);
cam->rga.dst_buf = (unsigned char *)aligned_alloc(align, cam->rga.dst_size);
if (!cam->rga.src_buf || !cam->rga.dst_buf) {
fprintf(stderr, "RGA 内存分配失败\n");
return false;
}
memset(cam->rga.src_buf, 0, cam->rga.src_size);
memset(cam->rga.dst_buf, 0, cam->rga.dst_size);
cam->rga.src_img = {0};
cam->rga.src_img.vir_addr = cam->rga.src_buf;
cam->rga.src_img.width = VIDEO_WIDTH;
cam->rga.src_img.height = VIDEO_HEIGHT;
cam->rga.src_img.wstride = VIDEO_WIDTH;
cam->rga.src_img.hstride = VIDEO_HEIGHT;
cam->rga.src_img.format = RK_FORMAT_RGBA_8888; // 使用RGBA格式
cam->rga.dst_img = {0};
cam->rga.dst_img.vir_addr = cam->rga.dst_buf;
cam->rga.dst_img.width = VIDEO_WIDTH;
cam->rga.dst_img.height = VIDEO_HEIGHT;
cam->rga.dst_img.wstride = VIDEO_WIDTH;
cam->rga.dst_img.hstride = VIDEO_HEIGHT;
cam->rga.dst_img.format = RK_FORMAT_YCbCr_420_SP;
printf("RGA 初始化成功(MONO8→NV12)\n");
return true;
}
转变函数:
static bool rga_convert(CameraConfig *cam, const unsigned char *mono) {
if (!mono || !cam->rga.src_buf || !cam->rga.dst_buf) return false;
// 将MONO8转换为RGBA8888
unsigned char *rgba_buf = (unsigned char *)malloc(VIDEO_WIDTH * VIDEO_HEIGHT * 4);
if (!rgba_buf) return false;
for (int i = 0; i < VIDEO_WIDTH * VIDEO_HEIGHT; i++) {
rgba_buf[i*4] = mono[i]; // R
rgba_buf[i*4+1] = mono[i]; // G
rgba_buf[i*4+2] = mono[i]; // B
rgba_buf[i*4+3] = 255; // A
}
memcpy(cam->rga.src_buf, rgba_buf, VIDEO_WIDTH * VIDEO_HEIGHT * 4);
free(rgba_buf);
pthread_mutex_lock(&g_rga_mutex);
IM_STATUS ret = imcvtcolor(cam->rga.src_img, cam->rga.dst_img,
RK_FORMAT_RGBA_8888, RK_FORMAT_YCbCr_420_SP);
pthread_mutex_unlock(&g_rga_mutex);
if (ret != IM_STATUS_SUCCESS) {
fprintf(stderr, "RGA 转换失败: %s\n", imStrError(ret));
return false;
}
return true;
}
测试结果:
1、单通道测试显示正常:
2、测试多路推流时遇到问题:
问题的主要原因:使用dvpSetTargetFormatSel不属于linux系统的api
主要是因为linux版本,与windown版本的api不一样,使用dvpSetTargetFormatSel函数在linux端不起作用
更换成dvpSetTargetFormat是可以正常的。
dvpSetTargetFormat(cam->dvp_handle, TARGET_FORMAT);
终于找到bug,修复成功
开关机后重启测试完成。






