RV1126——OSD模块

平时我们看监控录像、行车记录仪视频、直播画面,总能看到画面角落的时间、设备编号、通道名称,或是主播添加的文字贴纸。这些额外叠加在原始视频画面上的文字、图标、线条,背后用到的技术就是OSD

(OSD应用在音视频推流上) (OSD应用在ETC上)

很多刚接触嵌入式音视频、RKMedia 这类开发的朋友,第一次碰到 OSD 都会犯懵:它和普通的图片合成有啥区别?为什么做视频水印非要用 OSD,不能直接改原图?今天就用大白话,把 OSD 的底层逻辑、工作场景和核心特点讲明白,零基础也能看懂。

一、OSD概念

OSD 全称是 On-Screen Display,直译过来就是屏幕显示叠加

简单总结一句话:在原始的视频画面之上,再 "贴" 一层文字、图形、图标,并且跟着视频同步输出、编码、保存,这个整体技术就叫 OSD 叠加

举几个生活里最常见的例子,你马上就能对应上:

  1. 家用监控摄像头:画面左上角跳动的年月日、时分秒,右侧的 "通道 1""高清" 字样,全是 OSD 叠加出来的;
  2. 行车记录仪:视频里的车速、定位标识、录制状态图标,不是摄像头拍出来的画面,是后期叠加上去的 OSD 内容;
  3. 电视信号、老式机顶盒:切换频道时弹出的频道号、信号强度提示框,也是典型的 OSD;
  4. 我们现在正在做的项目:给 RK1126 采集的实时视频加上时间水印,本质就是自定义 OSD 文字叠加。

这里一定要区分开一个误区:OSD 不是视频本身自带的画面。摄像头、传感器只负责采集真实的场景图像,文字和图形是硬件 / 软件额外附加上去的两层内容,两层画面最终融合成一帧完整视频。

二、OSD的实现原理

OSD实现一般分为下面几个部分,分别是视频信号处理、 OSD 图像的生成、 OSD 图像的叠加

2.1. 视频信号的处理:

OSD叠加的第一个步骤,就是要对视频信号进行处理。这种信号处理就是要从模拟信号转换成数字信号,转换完成之后需要通过芯片对数字信号进行后处理工作,包括:去噪、锐化等等。

2.2.OSD 图像的生成

视频数据经过数字信号处理后,就可以对其进行OSD图像的生成。OSD的图像包含很多种类型,比方说LOGO图形、文字、图标等,而生成OSD图像的方式一般分为软件模式和硬件模式。硬件模式是利用专门的OSD处理芯片,通过硬件的图像合成器将OSD图像和视频信号合成,整个过程CPU不去进行处理;软件模式是通过软件的图像处理算法生成OSD图像,整个过程CPU都参与处理,常用的OSD生成的框架有:OPENCV、 FFMPEG等。

(OSD硬件生成器) (OSD软件生成器)

2.3.OSD 图像的叠加

最后一步就是把OSD的图像进行叠加,所谓叠加就是把OSD图像和视频信号结合在一起。OSD信号叠加一般分为两种一种是上方叠加、另外一种是下方叠加,上方叠加相当于把OSD图像显示在视频的顶部、下方叠加相当于把OSD图像显示在视频的底部。OSD叠加的原理也非常简单,就是把OSD图像的像素点和视频信号的像素点进行合成,并且在合成的过程中可以通过调整位置、大小等参数显示出来。

(OSD 叠加原理 ) (OSD 顶部位置、底部位置)

三、硬件 OSD 的工作流程:一步步看懂画面是怎么 "加字" 的

结合我们 RK1126 + RKMedia 的实际开发场景,我把整个流程拆成普通人也能理解的步骤,不用记复杂术语,看懂逻辑就行。

我们把整个视频链路比作一条传送流水线

  1. 原始画面入场 摄像头(VI 模块)采集外界画面,转换成标准的视频帧数据,送到流水线里。这一步只有真实场景,没有任何文字水印。

  2. OSD 图层准备 我们的程序提前做好两件事:一是渲染好要显示的文字 / 图形(比如时间字符串),转换成硬件能识别的像素数据;二是配置参数:图层放在画面 X、Y 坐标多少,宽高多大、是否启用这个图层。 这一步相当于:提前把要贴的 "贴纸" 做好,并且标记好要贴在画面的哪个角落。

  3. 硬件自动叠加融合 当原始视频帧流到 OSD 硬件模块时,硬件会按照我们设定的位置,把准备好的文字图层,覆盖在原始画面之上。 这里提一句我们实操踩过的坑:为什么位置、宽高必须 16 对齐?可以理解为硬件划分画面时,是以 16 个像素为一个最小单元处理的。如果尺寸、坐标乱填,硬件找不到对应的单元,就会解析出错,出现花屏、噪点。

  4. 融合后画面编码输出 叠加完成的完整画面,继续流向下一级编码器(VENC 模块),压缩成 H264/H265 视频流,最后保存成视频文件或者推流出去。

整个过程里,主程序只负责准备贴纸 + 设定位置,真正的 "贴画" 动作全由硬件完成,这也是嵌入式 OSD 最核心的工作逻辑。

四、软件 OSD 和 硬件 OSD:简单区分两种实现方式

日常开发中 OSD 还分两大类,很多新手容易混淆,这里也简单区分一下:

  1. 硬件 OSD(我们当前使用的方案) 芯片自带专用硬件模块,就是上面讲的流水线叠加方式。优点是快、省资源、稳定,是 IPC 摄像头、车载视频、嵌入式编码设备的主流方案;缺点是有固定规则限制(对齐要求、像素格式限制),灵活性略低,必须按照硬件规范传参。

  2. 软件 OSD 完全靠 CPU 代码逐像素绘制文字、合成画面,和电脑上修图逻辑一致。优点是灵活,想怎么画就怎么画,没有对齐、格式限制;缺点是耗 CPU,高分辨率、高帧率视频下容易卡顿,一般只用在 PC、安卓这类算力充足的设备上。

我们基于 RKMedia 做的水印功能,全程使用硬件 OSD,这也是嵌入式视频开发最主流的用法。

五、RV1126的 OSD 结构体模块

落地到代码层面,结合实际开发场景,逐一拆解 RKMedia 框架下 OSD 功能依赖的核心结构体常用 API 接口。这两个部分是开发 OSD 水印的基础,也是我们之前踩坑最多的地方,弄懂每一个字段和接口含义,后续写代码、排错都会轻松很多。

说明:本文基于瑞芯微 RV1126 平台 + 标准 RKMedia 接口编写,结构体、接口定义均参考官方 SDK,同时结合我们文字水印的实战案例做解读。

5.1 RV1126 的 OSD 核心结构体模块

在RV1126开发OSD模块的时候,一般要使用下面的结构体分别是OSD_REGION_INFO_SBITMAP_S 。OSD_REGION_INFO_S主要作用是是在编码图像里面划分一个OSD区域,相当于在编码图像中空出一块空间给OSD图层来用,BITMAP_S 的作用是向OSD图层以位图的形式把具体的内容显示出来,下面是OSD_REGION_INFO和Bitmap的关系:

从上面这张图可以看出来,BITMAP_S包含在OSD_REGION_INFO里面,BITMAP_S是OSD_REGION_INFO的具体内容。

5.1.1 OSD_REGION_INFO_S 图层区域信息结构体

这个结构体作用很直白:告诉硬件,你的 OSD 图层要放在画面哪个位置、图层多大、是否生效。可以理解为给 "贴纸" 划定摆放区域。

结构体原型

复制代码
typedef struct {
    REGION_ID_E        enRegionId;      // OSD 图层编号
    unsigned int       u32Width;         // OSD 图层宽度
    unsigned int       u32Height;        // OSD 图层高度
    unsigned int       u32PosX;          // 图层左上角 X 坐标(水平位置)
    unsigned int       u32PosY;          // 图层左上角 Y 坐标(垂直位置)
    unsigned char      u8Enable;         // 图层使能开关
    unsigned char      u8Inverse;        // 颜色反转开关
    unsigned char      u8Reserved[2];    // 保留位,预留扩展使用
} OSD_REGION_INFO_S;

1. enRegionId 图层编号 RV1126 的 VENC 编码模块支持多路独立 OSD 图层,每一个图层都有专属编号,取值为 REGION_ID_0 ~ REGION_ID_7,最多支持 8 个图层同时叠加。

  • 用法:如果只做单路时间水印,任选一个未被系统占用的编号即可
  • 拓展:如果需要同时叠加时间、设备编号两行文字,可以分别启用两个不同编号的图层。

2. u32Width /u32Height 图层宽高 定义当前 OSD 图层的像素尺寸,这是硬件强制约束点之一

  • 硬性规则:RV1126 硬件 OSD 要求宽、高必须做 16 像素对齐。举个例子:文字实际宽度是 100 像素,100 无法被 16 整除,就要向上补齐到 112 像素;
  • 踩坑回顾:如果不做 16 对齐,硬件解析图层时会读取到非法内存,直接出现花屏、彩色噪点,这也是我们前期遇到的典型问题。

3. u32PosX /u32PosY 图层坐标 代表 OSD 图层左上角相对于原始视频画面的坐标,视频画面左上角默认坐标为 (0, 0)

  • 规则:和宽高要求一致,坐标也建议 16 像素对齐,进一步规避硬件解析异常;
  • 实战示例:我们代码中设置 u32PosX = 128u32PosY = 128,就是把水印放在画面左上区域。

4. u8Enable 图层使能开关 图层生效控制位,只有两个取值:

  • 1:启用当前 OSD 图层,叠加功能生效;
  • 0:关闭图层,画面上不会显示任何内容。 想要显示水印,必须把该字段置 1。

5. u8Inverse 颜色反转 简易颜色翻转功能:

  • 0:正常颜色,我们默认使用该配置;
  • 1:颜色反转,黑底变白底、白字变黑色,一般特殊场景才会用到,常规水印开发无需开启。

6. u8Reserved 保留位 系统预留字段,没有实际功能,定义结构体时直接用 memset 清零即可,不用手动赋值。

补充使用注意事项

  • 局部变量定义该结构体时,务必先用 memset 整体清零,避免栈内脏数据导致硬件解析出错;
  • 一个 OSD_REGION_INFO_S 对应一路独立 OSD 图层,多图层叠加就定义多个结构体。

5.1.2 BITMAP_S 位图数据结构体

如果说 OSD_REGION_INFO_S 是 "划定摆放位置",那 BITMAP_S 就是存放贴纸本身的结构体。我们用 SDL+TTF 渲染出的文字像素数据、自定义图片数据,全部通过这个结构体传给硬件。

结构体原型

复制代码
typedef struct {
    unsigned int        u32Width;        // 位图实际宽度
    unsigned int        u32Height;       // 位图实际高度
    unsigned int        u32Stride;       // 位图行跨度(行字节数)
    PIXEL_FORMAT_E      enPixelFormat;   // 像素格式
    unsigned char       *pData;          // 像素数据内存首地址
} BITMAP_S;

1. u32Width /u32Height 位图宽高 和上面 OSD_REGION_INFO_S 中的宽高保持一致即可,同样需要遵循 16 像素对齐 规则,保证图层和位图尺寸匹配。

2. u32Stride 行跨度 也叫行步长,代表位图单行像素占用的总字节数 。对于标准对齐的位图,一般可以直接设置为 宽度 × 单像素字节数

  • 我们使用 32 位像素(4 字节 / 像素),所以 u32Stride = 对齐后宽度 × 4
  • 常规场景下,RKMedia 会自动适配,结构体清零后不手动赋值也可正常运行。

3. enPixelFormat 像素格式(核心重难点) 这是整个 OSD 开发中最容易出问题的字段,决定了像素数据的解析规则。

  • 接口声明统一填写:PIXEL_FORMAT_ARGB_8888,这是 RKMedia OSD 对外标准格式;
  • 底层真相:RV1126 硬件 OSD 实际只识别 BGRA8888 格式(像素顺序:蓝、绿、红、透明通道);
  • 踩坑复盘:SDL 渲染出来的文字是标准 ARGB8888(透明、红、绿、蓝),直接拷贝数据会导致颜色错乱、文字花屏。所以我们代码中增加了一步手动像素格式转换,把 ARGB 转为 BGRA,才最终解决花屏问题。

4. pData 像素数据指针 指向一块内存缓冲区,缓冲区里按顺序存储着所有像素数据,是整个位图的核心。

  • 内存要求:需要我们手动 malloc 申请,大小 = 对齐后宽 × 对齐后高 × 4(32 位像素);
  • 生命周期:每次推送完位图数据后,可及时释放,避免内存泄漏;长时间运行建议预分配固定缓冲区。

补充使用注意事项

  1. 该结构体同样建议使用 memset 清零后再赋值,杜绝脏数据;
  2. 位图宽高、OSD 图层宽高必须严格一致,否则会出现画面裁切、错位;
  3. 像素缓冲区必须保证连续内存,不支持碎片化数据。

5.2 RV1126 的 OSD 模块核心 API 讲解

了解完结构体,接下来讲解 RKMedia 中操作 OSD 的整套 API。按照初始化 → 配置叠加 → 资源释放的调用顺序逐一说明,同时标注使用场景、入参含义和实战注意点。

整套 OSD 接口隶属于 VENC(视频编码)模块,所有接口都需要依赖编码通道。

5.2.1 RK_MPI_VENC_RGN_Init 初始化 OSD 模块

函数原型

复制代码
int RK_MPI_VENC_RGN_Init(int s32ChnId, const RGN_ATTR_S *pstRgnAttr);

功能说明

初始化编码通道对应的 OSD 硬件模块,所有 OSD 操作的前置接口,必须在使用 OSD 功能前调用。

参数解读

  1. s32ChnId :编码通道号,我们项目中固定使用 VENC_CHN = 0
  2. pstRgnAttr :OSD 全局属性配置结构体。如果使用默认参数,直接传入 NULL 即可。

返回值 & 实战用法

  • 返回 0:初始化成功;非 0:初始化失败,后续 OSD 接口全部失效;

  • 调用时机:VENC 编码通道创建完成之后、绑定 VI 与 VENC 之前调用;

  • 示例代码:

    复制代码
    ret = RK_MPI_VENC_RGN_Init(VENC_CHN, NULL);
    if (ret != 0) {
        printf("OSD 模块初始化失败\n");
        return -1;
    }

5.2.2 RK_MPI_VENC_RGN_SetBitMap 设置 OSD 位图(核心接口)

函数原型

复制代码
int RK_MPI_VENC_RGN_SetBitMap(int s32ChnId, 
                              const OSD_REGION_INFO_S *pstRgnInfo, 
                              const BITMAP_S *pstBitmap);

功能说明

这是 OSD 最核心的接口:将准备好的图层区域信息位图数据下发给硬件,硬件收到后自动在下一帧视频中完成画面叠加。 我们的文字水印、图片贴纸,全靠这个接口实现。

参数解读

  1. s32ChnId:编码通道号,和初始化接口保持一致;
  2. pstRgnInfo :传入我们上一节讲解的 OSD_REGION_INFO_S 结构体地址,描述图层位置、大小、开关;
  3. pstBitmap :传入 BITMAP_S 结构体地址,指向我们渲染好的文字 / 图片像素数据。

关键使用规则

  1. 调用频率 :视频是连续帧流,该接口不能只调用一次 。编码器每产生一帧画面,就需要重新推送一次位图数据。所以代码中必须放在 while(1) 循环内,和视频帧率同步刷新;
    • 举例:25 帧 / 秒 的视频,每 40ms 调用一次该接口,保证水印持续显示;
  2. 多图层用法 :多个 OSD 图层,就多次调用该接口,搭配不同的 enRegionId 即可;
  3. 返回值判断:返回 0 代表数据下发成功,非 0 大概率是结构体非法、像素格式错误、宽高未对齐。

典型调用逻辑

复制代码
// 1. 定义并清零两个核心结构体
OSD_REGION_INFO_S rgn_info;
BITMAP_S bitmap;
memset(&rgn_info, 0, sizeof(rgn_info));
memset(&bitmap, 0, sizeof(bitmap));

// 2. 填充结构体参数(省略赋值过程)

// 3. 调用核心接口下发数据
ret = RK_MPI_VENC_RGN_SetBitMap(VENC_CHN, &rgn_info, &bitmap);

5.2.3 补充:模块销毁与资源收尾接口

在程序正常退出时,需要按顺序释放资源,避免硬件占用、内存泄漏。OSD 没有单独的销毁接口,跟随 VENC 模块一起释放即可。

配套调用顺序(完整链路)

  1. 停止线程、停止推送 OSD 位图;
  2. 调用 RK_MPI_SYS_UnBind 解除 VI(视频采集)与 VENC(编码)的绑定;
  3. 调用 RK_MPI_VENC_DestroyChn 销毁编码通道,同时自动释放 OSD 硬件资源
  4. 调用 RK_MPI_VI_DisableChn 关闭视频采集通道。

5.2.4 接口整体调用流程总结(标准流水线)

结合我们完整代码,梳理一套标准、可复用的 OSD 调用流程,方便后续开发查阅:

  1. 初始化 VI 视频采集通道 → 开启采集;
  2. 初始化 VENC 编码通道 → 开启编码;
  3. 调用 RK_MPI_VENC_RGN_Init 初始化 OSD 模块
  4. 绑定 VI 与 VENC 数据流;
  5. 启动业务线程:
    • 渲染文字 / 图片,生成像素数据;
    • 填充 OSD_REGION_INFO_SBITMAP_S 结构体;
    • 循环调用 RK_MPI_VENC_RGN_SetBitMap 刷新 OSD 图层;
  6. 程序退出:解除绑定 → 销毁 VENC → 关闭 VI。
相关推荐
放大的EZ1 小时前
Comfyui 教程-22
图像处理·人工智能·计算机视觉
YOLO数据集集合2 小时前
无人机航拍桥梁巡检数据集 | 桥梁结构缺陷检测 深度学习目标检测数据10338期
深度学习·yolo·目标检测·计算机视觉·无人机
armwind2 小时前
openISP学习12-EE — Edge Enhancement(边缘增强)
图像处理·计算机视觉
Litluecat2 小时前
配合多角色提示语4,学习AI漫剧(刚开始学)
人工智能·学习·计算机视觉
byte轻骑兵2 小时前
【AVRCP】规范精讲[28]:媒体源上电全流程,蓝牙音频控制启动就靠这一套
网络·音视频·人机交互·媒体·avrcp
七月稻草人2 小时前
短视频内容分析项目里,难的不是模型,而是数据
音视频
大蚂蚁2号2 小时前
本地批量音视频转文本免费工具
python·音视频·开源软件
潜创微科技2 小时前
2026年高清音视频方案全景分析:潜创微服务商的适配逻辑与选型参考
音视频
爱吃骨头的鱼儿2 小时前
aac音频
音视频·aac