在嵌入式视频处理领域,OSD(On-Screen Display)叠加是提升用户体验的关键技术之一。Rockchip的RGN(Region)模块为开发者提供了强大的视频叠加能力,无论是安防监控中的时间戳显示,还是智能设备上的状态提示,都能通过这套API实现高效开发。本文将带您从零开始,用最精简的步骤完成OSD叠加,并分享实际项目中积累的宝贵经验。
在开始编码之前,我们需要确保开发环境正确配置。Rockchip平台通常使用Rockit多媒体框架,而RGN模块是其核心组件之一。以下是必要的准备工作:
bash复制# 安装基础开发工具
sudo apt-get install build-essential cmake
# 获取Rockit SDK(需Rockchip授权)
git clone https://gitlab.com/rockchip-linux/rockit.git
CONFIG_VIDEO_ROCKCHIP_MPP和CONFIG_VIDEO_ROCKCHIP_RGNRGN模块支持多种叠加模式,最常用的是OVERLAY类型。理解以下几个核心概念至关重要:
| 概念 | 说明 |
|---|---|
| Region Handle | 区域操作的唯一标识符,类似文件描述符 |
| Canvas | 内存中的画布区域,用于存储OSD图像数据 |
| Channel Binding | 将区域绑定到视频通道(如VENC/VO)的关联操作 |
| Alpha Blending | 透明度混合技术,实现OSD与视频画面的自然叠加 |
提示:开发前建议阅读
platform/external/rockit/mpi/doc下的官方文档,特别是RGN_API_Reference.md文件。
RGN模块需要基于已有的视频图层进行操作,因此首先需要建立图形帧缓冲区。这是最容易被忽视的关键步骤,也是后续操作的基础。
c复制// 示例:创建图形帧缓冲区
VO_FRAME_INFO_S stFrameInfo;
memset(&stFrameInfo, 0, sizeof(VO_FRAME_INFO_S));
stFrameInfo.u32Width = 1920;
stFrameInfo.u32Height = 1080;
stFrameInfo.enPixelFormat = RK_FMT_ARGB8888;
RK_S32 s32Ret = RK_MPI_VO_CreateGraphicsFrameBuffer(0, &stFrameInfo);
if (s32Ret != RK_SUCCESS) {
printf("Create frame buffer failed! Error code: %#x\n", s32Ret);
return -1;
}
常见问题及解决方案:
黑屏无显示:
RK_MPI_VO_SendFrame是否调用内存分配失败:
dmesg输出的内存错误信息格式不支持:
c复制// 推荐使用以下兼容性较好的格式
typedef enum {
RK_FMT_ARGB8888 = 0, // 带透明通道的32位色
RK_FMT_RGB888, // 24位真彩色
RK_FMT_BGRA8888, // 蓝绿红透明顺序
} RGN_PIXEL_FORMAT_E;
成功建立帧缓冲区后,即可开始RGN区域的核心操作。这个阶段需要特别注意区域尺寸和类型的合理配置。
c复制RGN_ATTR_S stRgnAttr;
memset(&stRgnAttr, 0, sizeof(RGN_ATTR_S));
stRgnAttr.enType = OVERLAY_RGN; // 叠加类型区域
stRgnAttr.unAttr.stOverlay.stSize.u32Width = 320;
stRgnAttr.unAttr.stOverlay.stSize.u32Height = 240;
stRgnAttr.unAttr.stOverlay.enPixelFmt = RK_FMT_ARGB8888;
RGN_HANDLE RgnHandle = 0; // 区域句柄
s32Ret = RK_MPI_RGN_Create(RgnHandle, &stRgnAttr);
if (s32Ret != RK_SUCCESS) {
printf("Region create failed! Error code: %#x\n", s32Ret);
RK_MPI_RGN_Destroy(RgnHandle);
return -1;
}
关键参数解析:
区域类型选择:
OVERLAY_RGN:最常用的叠加显示COVER_RGN:用于遮挡特定区域MOSAIC_RGN:马赛克效果LINE_RGN:绘制线条内存优化技巧:
注意:单个进程最多可创建255个区域(RGN_HANDLE范围0-254),超出会导致
RK_ERR_RGN_EXIST错误。
创建区域后,需要将其绑定到目标通道才能显示。这个步骤的灵活性很高,但也最容易出现位置错乱问题。
c复制RGN_CHN_ATTR_S stRgnChnAttr;
memset(&stRgnChnAttr, 0, sizeof(RGN_CHN_ATTR_S));
stRgnChnAttr.bShow = RK_TRUE;
stRgnChnAttr.unChnAttr.stOverlayChn.stPoint.s32X = 50; // X坐标
stRgnChnAttr.unChnAttr.stOverlayChn.stPoint.s32Y = 30; // Y坐标
stRgnChnAttr.unChnAttr.stOverlayChn.u32BgAlpha = 128; // 背景透明度
stRgnChnAttr.unChnAttr.stOverlayChn.u32FgAlpha = 255; // 前景透明度
stRgnChnAttr.unChnAttr.stOverlayChn.u32Layer = 1; // 显示层级
MPP_CHN_S stChn;
stChn.enModId = RK_ID_VENC; // 绑定到编码通道
stChn.s32DevId = 0;
stChn.s32ChnId = 0;
s32Ret = RK_MPI_RGN_AttachToChn(RgnHandle, &stChn, &stRgnChnAttr);
if (s32Ret != RK_SUCCESS) {
printf("Attach to channel failed! Error code: %#x\n", s32Ret);
return -1;
}
坐标系统陷阱:
透明度设置技巧:
c复制// 完全透明(仅适用于ARGB格式)
u32BgAlpha = 0, u32FgAlpha = 0
// 半透明效果
u32BgAlpha = 128, u32FgAlpha = 255
// 不透明显示
u32BgAlpha = 255, u32FgAlpha = 255
OSD内容需要动态更新时,RGN模块提供两种主要方式,各有适用场景。
方式一:直接位图设置(适合静态内容)
c复制RGN_BITMAP_S stBitmap;
memset(&stBitmap, 0, sizeof(RGN_BITMAP_S));
stBitmap.enPixelFormat = RK_FMT_ARGB8888;
stBitmap.u32Width = 320;
stBitmap.u32Height = 240;
stBitmap.pData = (RK_VOID*)pImageData; // 预渲染的图像数据
s32Ret = RK_MPI_RGN_SetBitMap(RgnHandle, &stBitmap);
方式二:画布操作(适合动态内容)
c复制RGN_CANVAS_INFO_S stCanvasInfo;
s32Ret = RK_MPI_RGN_GetCanvasInfo(RgnHandle, &stCanvasInfo);
if (s32Ret == RK_SUCCESS) {
// 直接操作画布内存
draw_text_to_buffer((uint32_t*)stCanvasInfo.u64VirAddr, "Live Update");
RK_MPI_RGN_UpdateCanvas(RgnHandle);
}
性能优化对比表:
| 指标 | 位图设置 | 画布操作 |
|---|---|---|
| 更新延迟 | 较高(全量更新) | 较低(增量更新) |
| CPU占用 | 中等 | 取决于绘制复杂度 |
| 内存使用 | 需要额外缓冲区 | 直接使用内部内存 |
| 适用场景 | 预渲染静态内容 | 实时动态内容 |
在智能相机项目中,我们发现同时更新多个区域时,采用以下策略可提升30%的渲染性能:
pthread_mutex_lock保护画布操作根据多个量产项目经验,总结以下高频问题及解决方案:
问题1:叠加内容闪烁
RK_MPI_RGN_UpdateCanvas调用c复制static pthread_mutex_t rgn_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&rgn_mutex);
RK_MPI_RGN_UpdateCanvas(RgnHandle);
pthread_mutex_unlock(&rgn_mutex);
问题2:位置偏移
c复制// 转换为百分比坐标
stRgnChnAttr.unChnAttr.stOverlayChn.stPoint.s32X = main_width * 0.1;
stRgnChnAttr.unChnAttr.stOverlayChn.stPoint.s32Y = main_height * 0.05;
问题3:内存泄漏
/proc/meminfo中的CMA内存使用Create都有对应的Destroyc复制void release_rgn_resource() {
RK_MPI_RGN_DetachFromChn(RgnHandle, &stChn);
RK_MPI_RGN_Destroy(RgnHandle);
}
问题4:跨平台兼容性
c复制RK_U32 u32FmtSupport = 0;
RK_MPI_RGN_QueryFormatSupport(enPixelFormat, &u32FmtSupport);
if (!u32FmtSupport) {
printf("Pixel format not supported!\n");
}
在车载中控项目中,我们通过实现自动降级机制解决了不同硬件平台的兼容性问题:当检测到性能不足时,自动将ARGB8888降级为RGB565格式,同时调整OSD的复杂度保证流畅显示。