
一、RGA模块1.1 RGA模块的定义RGA Raster Graphic Acceleration Unit瑞芯微 2D 硬件图形加速器RV1126 芯片内置独立硬件图像处理单元纯硬件运算、不占用 CPU核心能力 图像缩放、裁剪、旋转 (0/90/180/270°)、YUV/RGB 格式互转、镜像翻转是 RKMedia 媒体链路标配模块。比方说要把一个原分辨率1920 * 1080的视频压缩成1280 * 720的视频此时就要用到RGA模块了。比方说下图比方说把一个视频用RGA进行旋转如下图比方说把一个视频用RGA进行镜像处理(镜像就是指物体相对于该物体反射出来的虚像就像一个人照镜子反射出来的图像)如下图要注意的是RGA是直接对VI的图像进行处理并不是对VENC数据进行处理这一点要非常注意。1.2 RGA结构体的定义1. 区域属性结构体RGA_INFO_S输入 / 输出图像描述单幅图像信息typedef struct rkRGA_INFO_S { IMAGE_TYPE_E imgType; // 图像格式NV12/NV21/RGB888等 RK_U32 u32X; // 裁剪起始X坐标(左上角) RK_U32 u32Y; // 裁剪起始Y坐标 RK_U32 u32Width; // 有效图像宽 RK_U32 u32Height; // 有效图像高 RK_U32 u32HorStride; // 行跨度(内存宽度≥u32Width) RK_U32 u32VirStride; // 列跨度(内存高度≥u32Height) } RGA_INFO_S;2. RGA 通道总属性RGA_ATTR_S创建 RGA 通道入参typedef struct rkRGA_ATTR_S { RGA_INFO_S stImgIn; // 输入端图像配置来自VI RGA_INFO_S stImgOut; // 输出端图像配置缩放后 RK_U16 u16Rotaion; // 旋转角度0/90/180/270 RK_BOOL bEnBufPool; // RK_TRUE开启硬件缓冲池必须开你的代码启用 RK_U16 u16BufPoolCnt; // 缓冲池缓存块数量根源你之前丢帧就是这个值太小 RGA_FLIP_E enFlip; // 镜像上下/左右翻转默认0关闭 } RGA_ATTR_S;3. RGA 镜像翻转枚举rkRGA_FLIP_E这个枚举用来控制画面镜像翻转填在RGA_ATTR_S.enFlip参数里硬件 RGA 直接完成翻转不占用 CPU。枚举宏含义白话解释RGA_FLIP_NULL不翻转默认值原图原样输出左右上下都不变你的代码默认填这个RGA_FLIP_HHorizontal 水平翻转左右镜像像照镜子画面左右反过来摄像头左右反了就开这个RGA_FLIP_VVertical 垂直翻转上下镜像画面上下颠倒倒立画面用这个修正RGA_FLIP_HVHV 同时翻转先左右翻、再上下翻等效旋转 180°代码使用示例// 1、默认正常画面不翻转 rga_attr.enFlip RGA_FLIP_NULL; // 2、开启左右镜像 //rga_attr.enFlip RGA_FLIP_H; // 3、开启上下镜像 //rga_attr.enFlip RGA_FLIP_V; // 4、左右上下一起翻转 //rga_attr.enFlip RGA_FLIP_HV;1.3 RKMedia RGA 常用 API 定义// 1. 创建RGA通道你代码在用 RK_S32 RK_MPI_RGA_CreateChn(RK_S32 s32ChnId, RGA_ATTR_S *pstAttr); // 2. 销毁通道 RK_S32 RK_MPI_RGA_DestroyChn(RK_S32 s32ChnId); // 3. 系统绑定VI输出 → RGA输入Bind接口 RK_S32 RK_MPI_SYS_Bind(MPP_CHN_S *pstSrcChn, MPP_CHN_S *pstDstChn); // 4. 取RGA输出缓存线程取帧接口 MEDIA_BUFFER RK_MPI_SYS_GetMediaBuffer(RK_ID_RGA, RK_S32 s32ChnId, RK_S32 s32TimeoutMs); // 5. 释放缓存必须调用归还到RGA缓冲池 RK_VOID RK_MPI_MB_ReleaseBuffer(MEDIA_BUFFER mb);二、获取RGA数据基于瑞芯微 RV1126 平台 RKMedia 多媒体框架实现摄像头原图采集→RGA 硬件缩放→裸 NV12 文件本地保存整套链路全程 RGA 由硬件加速缩放不占用 CPU 资源拆解从模块初始化到帧数据落地的完整开发流程适配初学者理解 RKMedia 模块绑定思想。 整体执行流程VI模块初始化 → RGA模块初始化 → VI与RGA通道绑定 → 启动VI数据流 → 子线程循环拉取RGA输出帧并落盘2.1 流程2.1 VI 视频采集模块初始化VIVideo Input负责对接 MIPI 摄像头从 ISP 获取原始 1920×1080 NV12 图像数据初始化分为参数配置→属性设置→通道使能三步暂不启动数据流避免未绑定 RGA 造成数据丢失。参数填充VI_CHN_ATTR_S该结构体配置摄像头分辨率、图像格式、缓存数量、设备节点名称pcVideoNode绑定内核 V4L2 设备节点rkispp_scale0u32Width/u32Height原始采集分辨率 1920×1080enPixFmt指定输出像素格式 NV12enBufType内存映射模式 MMAPRKMedia 标准采集配置u32BufCntVI 底层缓存帧数 3缓存原始摄像头画面。API 配置通道// 把配置参数写入VI通道 RK_MPI_VI_SetChnAttr(PIPE_ID, VI_CHN_ID, vi_chn_attr); // 硬件通道使能打开V4L2设备 RK_MPI_VI_EnableChn(PIPE_ID, VI_CHN_ID);2.2 RGA 硬件缩放模块初始化RGA2D 硬件图形加速器实现原图 1920×1080 → 720P (1280×720) 硬件缩放初始化核心是填充RGA_ATTR_S通道属性结构体再创建 RGA 硬件通道。RGA_ATTR_S 结构体分层配置结构体由输入图像区域 RGA_INFO_S、输出图像区域 RGA_INFO_S、通道全局配置三部分组成stImgIn输入画面参数和 VI 输出保持一致1920×1080、NV12 格式HorStride1920VirStride1080stImgOut缩放后输出画面1280×720、NV12 格式HorStride1280VirStride720u16Rotation画面旋转角度0/90/180/270项目填 0 不旋转enFlip镜像枚举 RGA_FLIP_E可选无翻转 / 左右 / 上下 / 双向翻转默认RGA_FLIP_NULLbEnBufPoolRK_TRUE开启 RGA 硬件输出缓存池u16BufPoolCnt缓存池帧数量建议 8~10解决写盘速率不足导致的丢帧告警。创建 RGA 硬件通道 APIRK_MPI_RGA_CreateChn(RGA_CHN_ID, rga_attr);调用后内核根据配置申请 RGA 硬件资源与缓存内存通道就绪等待上游 VI 输入图像。2.3 RK_MPI_SYS_BindVI 与 RGA 通道绑定RKMedia 依靠RK_MPI_SYS_Bind完成模块数据流串联实现VI 输出缓存自动流转至 RGA 输入无需 CPU 搬运图像内存是媒体链路的核心接口。MPP_CHN_S 通道描述体分别封装源VI、目的RGA的模块 ID 与通道号// VI源节点 MPP_CHN_S vi_chn {.enModId RK_ID_VI, .s32ChnId VI_CHN_ID}; // RGA目的节点 MPP_CHN_S rga_chn {.enModId RK_ID_RGA, .s32ChnId RGA_CHN_ID};绑定 API 原型与作用RK_S32 RK_MPI_SYS_Bind(MPP_CHN_S *pSrcChn, MPP_CHN_S *pDstChn);pSrcChn上游数据源模块VIpDstChn下游处理模块RGA绑定成功后VI 产生的每一帧图像自动通过 DMA 硬件直送 RGA 输入端RGA 硬件自动完成缩放处理处理完成的帧存入 RGA 自身缓存池等待应用层读取。反向解绑接口RK_MPI_SYS_UnBind程序退出时调用释放模块链路。2.4 启动 VI 数据流 子线程采集 RGA 帧数据2.4.1 绑定完成后开启 VI 推流RK_MPI_VI_StartStream(PIPE_ID, VI_CHN_ID);VI 正式从摄像头源源不断采集图像经由绑定链路自动送入 RGA 做缩放。2.4.2 新建子线程循环取帧落盘单独开辟采集线程避免阻塞主线程休眠核心接口RK_MPI_SYS_GetMediaBuffer从 RGA 缓存池取出缩放完成的 NV12 裸数据接口说明MEDIA_BUFFER RK_MPI_SYS_GetMediaBuffer(RK_S32 enModId,RK_S32 s32ChnId,RK_S32 timeout);enModId固定RK_ID_RGA指定从 RGA 模块取数据s32ChnIdRGA 创建时的通道编号 RGA_CHN_IDtimeout-1 代表阻塞等待无帧时线程休眠有帧立刻返回。线程伪代码落盘优化要点void *get_rga_data_thread(void *args) { // 使用wb二进制打开避免文本模式篡改NV12二进制数据/tmp为内存盘大幅减少磁盘IO丢帧 FILE *fp fopen(/tmp/vi_rga.nv12,wb); if(!fp) return NULL; MEDIA_BUFFER mb; while(1) { mb RK_MPI_SYS_GetMediaBuffer(RK_ID_RGA,RGA_CHN_ID,-1); if(!mb) break; // 写入原始NV12数据 fwrite(RK_MPI_MB_GetPtr(mb),1,RK_MPI_MB_GetSize(mb),fp); // 关键释放缓存归还至RGA缓存池不释放会导致缓存耗尽丢帧 RK_MPI_MB_ReleaseBuffer(mb); } fclose(fp); return NULL; }主线程创建采集线程pthread_t pid; pthread_create(pid,NULL,get_rga_data_thread,NULL); // 主线程循环休眠等待CtrlC退出 while(1) sleep(1);2.5 程序退出资源释放规范按下 CtrlC 结束程序时按停止数据流→解绑通道→销毁模块顺序释放资源防止 V4L2 设备占用RK_MPI_VI_StopStream(PIPE_ID,VI_CHN_ID); RK_MPI_SYS_UnBind(vi_chn,rga_chn); RK_MPI_VI_DisableChn(PIPE_ID,VI_CHN_ID); RK_MPI_RGA_DestroyChn(RGA_CHN_ID);三、代码#include assert.h #include fcntl.h #include getopt.h #include pthread.h #include signal.h #include stdbool.h #include stdio.h #include stdlib.h #include time.h #include unistd.h #include rkmedia_api.h // RKMedia 核心API头文件 // 宏定义 #define PIPE_ID 0 // VI 管道ID固定0 #define VI_CHN_ID 0 // VI 通道号固定0 #define RGA_CHN_ID 0 // RGA 硬件通道号固定0 // 线程函数 /** * brief RGA数据采集线程从RGA硬件通道取帧保存为NV12裸流文件 * param args 线程参数未使用 * return 线程退出指针 */ void *get_rga_data_thread(void *args) { // 以二进制只写方式打开文件保存RGA输出的NV12数据 // wb二进制写入不破坏原始图像数据必须用wb不能用w FILE *rga_nv12_file fopen(vi_rga.nv12, wb); if (!rga_nv12_file) { // 文件打开失败直接退出线程 return NULL; } MEDIA_BUFFER mb; // 媒体缓存句柄 // 循环持续取RGA输出帧 while (1) { // 从 RGA 模块阻塞式获取一帧处理完成的数据 // RK_ID_RGA模块ID // RGA_CHN_IDRGA通道号 // -1永久阻塞等待直到有数据 mb RK_MPI_SYS_GetMediaBuffer(RK_ID_RGA, RGA_CHN_ID, -1); if (!mb) { // 获取缓存失败退出循环 printf(get_rga_buffer break...\n); break; } printf(get_rga_buffer succeed...\n); // 将缓存中的图像数据写入文件 // RK_MPI_MB_GetPtr(mb)获取数据指针 // RK_MPI_MB_GetSize(mb)获取数据长度 fwrite(RK_MPI_MB_GetPtr(mb), 1, RK_MPI_MB_GetSize(mb), rga_nv12_file); // 必须释放缓存归还给RGA缓冲池否则会耗尽缓存导致丢帧 RK_MPI_MB_ReleaseBuffer(mb); } fclose(rga_nv12_file); // 关闭文件 return NULL; } // 主函数 int main() { int ret; // 1. RKMedia系统初始化必须第一个调用 RK_MPI_SYS_Init(); // 2. 初始化VI模块摄像头采集 VI_CHN_ATTR_S vi_chn_attr; // VI通道属性结构体 vi_chn_attr.pcVideoNode rkispp_scale0; // 摄像头节点RV1126默认 vi_chn_attr.u32Width 1920; // 摄像头输出宽度 1920 vi_chn_attr.u32Height 1080; // 摄像头输出高度 1080 vi_chn_attr.enPixFmt IMAGE_TYPE_NV12; // 输出格式 NV12最常用 vi_chn_attr.enBufType VI_CHN_BUF_TYPE_MMAP;// 内存映射模式RK推荐 vi_chn_attr.u32BufCnt 3; // VI缓存帧数3帧足够 vi_chn_attr.enWorkMode VI_WORK_MODE_NORMAL;// 正常工作模式 // 设置VI通道属性 ret RK_MPI_VI_SetChnAttr(PIPE_ID, VI_CHN_ID, vi_chn_attr); if (ret) { printf(VI_CHN_ATTR Set Failed...\n); return -1; } else { printf(VI_CHN_ATTR Set Succeed...\n); } // 使能VI通道开启硬件 ret RK_MPI_VI_EnableChn(PIPE_ID, VI_CHN_ID); if (ret) { printf(Enable_Vi Failed...\n); return -1; } else { printf(Enable_Vi Succeed...\n); } // 3. 初始化RGA模块硬件缩放 RGA_ATTR_S rga_attr; // RGA通道配置结构体 // --- RGA输入配置和VI输出完全一致 1920x1080 NV12 --- rga_attr.stImgIn.u32Width 1920; rga_attr.stImgIn.u32Height 1080; rga_attr.stImgIn.imgType IMAGE_TYPE_NV12; rga_attr.stImgIn.u32X 0; // 裁剪起始X不裁剪填0 rga_attr.stImgIn.u32Y 0; // 裁剪起始Y不裁剪填0 rga_attr.stImgIn.u32VirStride 1080; // 垂直跨度 高度 rga_attr.stImgIn.u32HorStride 1920; // 水平跨度 宽度 // --- RGA输出配置缩放到 1280x720 NV12 --- rga_attr.stImgOut.u32Width 1280; rga_attr.stImgOut.u32Height 720; rga_attr.stImgOut.imgType IMAGE_TYPE_NV12; rga_attr.stImgOut.u32X 0; rga_attr.stImgOut.u32Y 0; rga_attr.stImgOut.u32VirStride 720; rga_attr.stImgOut.u32HorStride 1280; rga_attr.u16Rotaion 0; // 旋转角度0不旋转 rga_attr.u16BufPoolCnt 8; // RGA缓存池数量8帧防丢帧 rga_attr.bEnBufPool RK_TRUE; // 开启RGA硬件缓冲池必须开启 // 创建RGA硬件通道 ret RK_MPI_RGA_CreateChn(RGA_CHN_ID, rga_attr); if (ret) { printf(RK_MPI_RGA_CreateChn Failed...\n); return -1; } else { printf(RK_MPI_RGA_CreateChn Succeed...\n); } // 4. 绑定 VI 和 RGA 模块 // 定义源模块VI MPP_CHN_S vi_chn_s; vi_chn_s.enModId RK_ID_VI; vi_chn_s.s32ChnId VI_CHN_ID; // 定义目标模块RGA MPP_CHN_S rga_chn_s; rga_chn_s.enModId RK_ID_RGA; rga_chn_s.s32ChnId RGA_CHN_ID; // 执行绑定VI输出 → RGA输入自动流转无需CPU搬运 ret RK_MPI_SYS_Bind(vi_chn_s, rga_chn_s); if (ret) { printf(RK_MPI_SYS_Bind Failed...\n); return -1; } else { printf(RK_MPI_SYS_Bind Succeed...\n); } // 5. 启动VI数据流 ret RK_MPI_VI_StartStream(PIPE_ID, VI_CHN_ID); if (ret) { printf(Start Vi Failed...\n); return -1; } else { printf(Start Vi Succeed...\n); } // 6. 创建线程读取RGA数据 pthread_t pid; pthread_create(pid, NULL, get_rga_data_thread, NULL); // 主线程休眠保持程序运行 while (1) { sleep(2); } // 7. 资源释放正常退出时执行 RK_MPI_VI_DisableChn(PIPE_ID, VI_CHN_ID); // 关闭VI RK_MPI_RGA_DestroyChn(RGA_CHN_ID); // 销毁RGA return 0; }写好后进行编译移植进板子详细流程在上篇博客这里不再赘述。四、采集数据并播放4.1 采集数据移植进板子后进行运行这便是采集成功了文件夹里会出现对应的vi_rga.nv12的一个文件这就是采集到的数据如果出现采集了几帧数据就开始失败那有可能是你选则保存问价的文件夹内存不够导致的输入命令df -h查看一下各个文件夹的内存再做选择4.2 数据播放将vi_rga.nv12文件下载到ffmpeg的bin文件夹中至于为什么放到这个文件夹中上篇博客也详细说了winr输入cmdcd 进入这个bin文件夹配套 PC 端 NV12 播放指令ffplay -f rawvideo -pix_fmt nv12 -s 1280x720 vi_rga.nv12FFmpeg 直接播放 720P 裸流播放完了就是这个效果五、踩坑总结坑 1模块启动时序颠倒高频错误错误写法VI_Enable → StartStream → 再创建 RGABindVI 提前开启推流画面源源不断输出但下游 RGA 还未初始化绑定图像无处挂载内核丢帧后续GetMediaBuffer频繁空指针。正确顺序VI 初始化使能→RGA 创建→Bind 绑定→最后 StartStream 开流。坑 2文件打开模式错误w 存 NV12 裸数据最初用fopen(xx,w)文本模式写入 YUV/NV12Linux 文本 IO 自动修正换行字节原始裸流被篡改视频花屏同时文本 IO 读写效率低加重磁盘阻塞丢帧。修正二进制专用wb优先存/tmp内存盘大幅降低 IO 耗时。坑 3RGA 缓存池数量过小丢帧告警 drop buffer初始u16BufPoolCnt3RGA 只有 3 帧缓存线程写硬盘速度摄像头出帧速度缓存被一直占用无法及时 Release 归还缓存池被掏空RGA 硬件无空闲缓存存放新帧直接丢弃帧并打印 drop。优化调整 BufPoolCnt8~10扩容缓存池缓解读写速率差。坑 4用完 MediaBuffer 忘记 ReleaseBuffer致命隐患RK_MPI_SYS_GetMediaBuffer拿到缓存后必须调用 ReleaseBuffer 归还缓存至 RGA 缓冲池遗漏释放会造成内存泄漏、缓存永久占用短时间池子耗尽程序卡死。坑 5程序异常退出资源未释放V4L2 设备占用异常 kill 程序、CtrlC 直接退出没有执行UnBind、StopStream、DisableChn、DestroyChnVI 对应的rkispp_scale0驱动节点被内核占用再次运行程序提示设备忙、VI 打开失败。规范上线代码添加信号捕获退出反向逐级释放资源停流→解绑→关闭 VI→销毁 RGA。坑 6RGA 的 HorStride/VirStride 参数混淆HorStride 图像宽度、VirStride 图像高度输出尺寸 1280×720 时错填 HorStride1080RGA硬件校验图像跨度异常缩放出错画面异常修正输出 Hor1280Vir720。坑 7对 Bind 数据流逻辑理解误区误以为 Bind 是软件拷贝数据实际 RKMedia Bind 后DMA 硬件直通传输零 CPU 拷贝VI 数据自动送入 RGA 做硬件缩放不用应用层搬运图像。