保姆级教程:在Rockchip RK3588开发板上用rkmedia实现摄像头实时预览(附完整代码)

张开发
2026/4/21 9:26:44 15 分钟阅读

分享文章

保姆级教程:在Rockchip RK3588开发板上用rkmedia实现摄像头实时预览(附完整代码)
RK3588开发实战基于rkmedia的摄像头实时预览系统深度解析刚拿到Rockchip RK3588开发板时最令人兴奋的莫过于验证摄像头模块的功能。作为一款高性能嵌入式处理器RK3588在多媒体处理方面表现出色而rkmedia作为其官方媒体处理框架为开发者提供了便捷的硬件抽象层。本文将带您从零开始构建一个完整的摄像头实时预览系统避开那些新手常踩的坑。1. 环境准备与基础配置在开始编码之前我们需要确保开发环境配置正确。不同于简单的Hello World程序多媒体应用对系统环境有更严格的要求。1.1 开发工具准备首先确认您的开发主机已安装以下工具链交叉编译工具链建议使用Rockchip官方提供的gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnuSDK获取从Rockchip开发者网站下载最新Linux SDK重点关注external/rkmedia目录# 检查工具链是否安装成功 aarch64-linux-gnu-gcc --version1.2 关键配置文件RK3588的ISP图像信号处理器需要特定的IQ文件来校准图像质量。这些文件通常位于开发板的/etc/iqfiles目录下。在我的项目中使用OV13850摄像头模组时发现以下配置最为稳定配置项推荐值说明IQ文件路径/etc/iqfiles/ov13850摄像头特定的调校参数工作模式RK_AIQ_WORKING_MODE_NORMAL普通模式非HDR帧率设置30fps平衡流畅度和处理负担注意不同摄像头模组需要匹配对应的IQ文件错误的选择会导致图像色彩异常或无法启动ISP。2. rkmedia核心架构解析rkmedia采用模块化设计理解其数据流架构是成功实现预览功能的关键。2.1 模块化数据流rkmedia的核心思想是将各硬件资源抽象为独立模块通过绑定机制建立数据通道。在摄像头预览场景中典型的数据流路径为VIVideo Input负责从摄像头采集原始数据RGARockchip Graphics Accelerator处理图像缩放、旋转和格式转换VOVideo Output将处理后的图像输出到显示设备// 典型的数据流绑定关系 MPP_CHN_S stSrcChn, stDestChn; // VI → RGA stSrcChn.enModId RK_ID_VI; stSrcChn.s32ChnId 0; stDestChn.enModId RK_ID_RGA; stDestChn.s32ChnId 0; RK_MPI_SYS_Bind(stSrcChn, stDestChn); // RGA → VO stSrcChn.enModId RK_ID_RGA; stSrcChn.s32ChnId 0; stDestChn.enModId RK_ID_VO; stDestChn.s32ChnId 0; RK_MPI_SYS_Bind(stSrcChn, stDestChn);2.2 图像格式处理RK3588支持多种图像格式在实际项目中需要特别注意格式匹配VI输入格式通常为NV12YUV420 semi-planarRGA处理格式支持RGB888、NV12等VO输出格式根据显示设备选择一般为RGB888我曾遇到一个棘手问题当VI设置为NV16格式而RGA预期NV12时图像会出现错位。解决方法是在RGA属性中明确指定输入输出格式stRgaAttr.stImgIn.imgType IMAGE_TYPE_NV12; // 必须与VI输出一致 stRgaAttr.stImgOut.imgType IMAGE_TYPE_RGB888; // 必须与VO输入一致3. 实战代码实现让我们从零构建一个完整的预览应用逐段分析关键代码。3.1 ISP初始化ISP初始化是图像质量保证的基础这段代码处理ISP的启动和配置#ifdef RKAIQ rk_aiq_working_mode_t hdr_mode RK_AIQ_WORKING_MODE_NORMAL; int fps 30; SAMPLE_COMM_ISP_Init(s32CamId, hdr_mode, bMultictx, iq_file_dir); SAMPLE_COMM_ISP_Run(s32CamId); SAMPLE_COMM_ISP_SetFrameRate(s32CamId, fps); #endif提示如果遇到ISP初始化失败首先检查iq文件路径是否正确其次确认没有其他进程如ispserver占用了ISP资源。3.2 通道创建与配置每个模块都需要正确配置才能建立完整的数据管道。VI通道配置示例VI_CHN_ATTR_S vi_chn_attr { .pcVideoNode rkispp_scale0, // 视频节点名称 .u32BufCnt 3, // 缓冲区数量 .u32Width 1920, // 采集宽度 .u32Height 1080, // 采集高度 .enPixFmt IMAGE_TYPE_NV12, // 像素格式 .enWorkMode VI_WORK_MODE_NORMAL // 工作模式 }; RK_MPI_VI_SetChnAttr(0, 0, vi_chn_attr); RK_MPI_VI_EnableChn(0, 0);RGA通道的特殊处理RGA在旋转图像时表现优异以下配置实现了90度旋转stRgaAttr.u16Rotaion 90; stRgaAttr.stImgIn.u32Width 1920; // 输入图像宽 stRgaAttr.stImgIn.u32Height 1080; // 输入图像高 stRgaAttr.stImgOut.u32Width 1080; // 旋转后宽高交换 stRgaAttr.stImgOut.u32Height 1920;4. 常见问题排查与优化在实际部署中会遇到各种预料之外的问题。这里分享几个典型案例和解决方案。4.1 资源冲突处理症状运行预览程序时出现ISP已被占用错误。排查步骤检查ispserver是否正在运行ps -aux | grep ispserver如果存在冲突可以选择终止ispserver进程kill -9 [PID]或者修改程序不使用IQ文件依赖ispserver的配置经验分享在量产环境中建议统一使用ispserver管理ISP资源避免多个应用竞争。4.2 图像异常排查当预览图像出现花屏、错位时可按以下顺序检查Stride对齐确保图像宽度是16的倍数// 正确的stride设置示例 stRgaAttr.stImgIn.u32HorStride 1920; // 必须等于宽度且16字节对齐 stRgaAttr.stImgIn.u32VirStride 1080;格式匹配确认VI、RGA、VO各阶段的图像格式一致缓冲区数量适当增加缓冲区数量如从3改为6可能解决卡顿问题4.3 性能优化技巧降低分辨率从4K降到1080P可显著减少处理延迟调整帧率根据实际需要平衡流畅度和CPU负载使用硬件加速确保RGA的所有转换都使用硬件加速路径// 性能优化后的RGA配置示例 stRgaAttr.bEnBufPool RK_TRUE; // 启用缓冲区池 stRgaAttr.u16BufPoolCnt 6; // 增加缓冲区数量5. 高级功能扩展基础预览功能实现后可以进一步扩展更专业的特性。5.1 区域覆盖Region Cover在预览画面上叠加信息是常见需求rkmedia提供了区域覆盖功能COVER_INFO_S CoverInfo { .enPixelFormat PIXEL_FORMAT_ARGB_8888, .u32Color 0xFFFF0000 // ARGB格式的红色 }; OSD_REGION_INFO_S RngInfo { .enRegionId REGION_ID_0, .u32PosX 0, .u32PosY 0, .u32Width 100, .u32Height 100, .u8Enable 1 }; RK_MPI_VI_RGN_SetCover(0, 1, RngInfo, CoverInfo);5.2 多摄像头支持RK3588支持多摄像头同时输入关键配置如下// 摄像头1 (MIPI CSI0) SAMPLE_COMM_ISP_Init(0, hdr_mode, RK_TRUE, iq_file_dir); RK_MPI_VI_EnableChn(0, 0); // 摄像头ID 0, 通道0 // 摄像头2 (MIPI CSI1) SAMPLE_COMM_ISP_Init(1, hdr_mode, RK_TRUE, iq_file_dir); RK_MPI_VI_EnableChn(1, 0); // 摄像头ID 1, 通道0注意启用多摄像头时需要设置bMultictx为RK_TRUE并确保IQ文件支持多路ISP处理。6. 完整代码实现与注释以下是整合所有功能的完整实现关键部分添加了详细注释#include stdio.h #include unistd.h #include rkmedia_api.h #include common/sample_common.h static bool quit false; static void sigterm_handler(int sig) { quit true; } int main() { // 初始化ISP假设使用MIPI CSI0 #ifdef RKAIQ SAMPLE_COMM_ISP_Init(0, RK_AIQ_WORKING_MODE_NORMAL, RK_FALSE, /etc/iqfiles); SAMPLE_COMM_ISP_Run(0); SAMPLE_COMM_ISP_SetFrameRate(0, 30); #endif // RKMedia系统初始化 RK_MPI_SYS_Init(); // 1. 创建VI通道 VI_CHN_ATTR_S vi_attr { .pcVideoNode rkispp_scale0, .u32BufCnt 3, .u32Width 1920, .u32Height 1080, .enPixFmt IMAGE_TYPE_NV12, .enWorkMode VI_WORK_MODE_NORMAL }; RK_MPI_VI_SetChnAttr(0, 0, vi_attr); RK_MPI_VI_EnableChn(0, 0); // 2. 创建RGA通道带90度旋转 RGA_ATTR_S rga_attr { .bEnBufPool RK_TRUE, .u16BufPoolCnt 3, .u16Rotaion 90, .stImgIn { .imgType IMAGE_TYPE_NV12, .u32Width 1920, .u32Height 1080, .u32HorStride 1920, .u32VirStride 1080 }, .stImgOut { .imgType IMAGE_TYPE_RGB888, .u32Width 1080, .u32Height 1920, .u32HorStride 1080, .u32VirStride 1920 } }; RK_MPI_RGA_CreateChn(0, rga_attr); // 3. 创建VO通道 VO_CHN_ATTR_S vo_attr { .pcDevNode /dev/dri/card0, .emPlaneType VO_PLANE_PRIMARY, .enImgType IMAGE_TYPE_RGB888, .u16Zpos 0, .stDispRect {0, 0, 1080, 1920} }; RK_MPI_VO_CreateChn(0, vo_attr); // 绑定数据流VI → RGA → VO MPP_CHN_S src {RK_ID_VI, 0}, dst {RK_ID_RGA, 0}; RK_MPI_SYS_Bind(src, dst); src.enModId RK_ID_RGA; dst.enModId RK_ID_VO; RK_MPI_SYS_Bind(src, dst); // 设置信号处理 signal(SIGINT, sigterm_handler); // 主循环 while(!quit) { usleep(100000); } // 资源释放 RK_MPI_SYS_UnBind(src, dst); RK_MPI_VO_DestroyChn(0); RK_MPI_RGA_DestroyChn(0); RK_MPI_VI_DisableChn(0, 0); #ifdef RKAIQ SAMPLE_COMM_ISP_Stop(0); #endif return 0; }在实际部署中建议将各模块初始化封装为独立函数并添加更完善的错误处理。例如每次MPI调用后都应检查返回值if((ret RK_MPI_VI_EnableChn(0, 0)) ! 0) { printf(VI启用失败: %d\n, ret); return -1; }

更多文章