参考文章:海思 3403 MPP 全链路解析: VI、VPSS、VENC 的运行逻辑与实践_vi 4k switch to fhd -> vpss -> venc && vo-CSDN博客
一、Hi3516CV610 sdk目录层级:
sudo apt-get install tree # 预装tree命令。 tree -L 2 -d # -L 2:只显示目录的两层深度, -d:只显示目录不显示文件。sdk根目录:
open_source/:所有第三方开源软件的完整源代码,busybox/、linux/、u-boot/、optee/、mtd-utils/、ethtool/、squashfs/、zlib/、mbedtls/ 等。提供操作系统基础组件,你可以在这些目录里修改配置、进行交叉编译。内核和 U-Boot 的源码都在这里,驱动开发和设备树修改需要。
package/:存放open_source/、platform/、smp/三个目录的.tgz压缩包备份。
platform/:存放secure/安全子系统。
scripts/:存放SDK 环境配置和依赖检查的Shell 脚本,快速配置编译环境、检查系统依赖是否完整,首次搭建编译环境使用。
sdk.unpack | 解压 SDK。将package/目录下的.tgz压缩包(open_source.tgz、platform.tgz、smp.tgz)解压到对应目录,构建完整的可编译环境 |
sdk.cleanup | 清理 SDK。删除解压出来的目录(如open_source/、platform/、smp/),恢复到刚拿到 SDK 时的纯净状态 |
~/sdk/Hi3516CV610_SDK_V1.0.1.0-BAK/smp/a7_linux/vendor/extdrv目录:
extdrv/:存放外部设备的驱动源码和编译脚本,ES8388 音频 Codec 驱动源码。
Hi3516CV610_SDK_V1.0.1.0-BAK/smp/a7_linux/source目录:
bsp/components/:gsl(Generic System Library)和 secure_c安全世界的客户端库。运行在CPU 安全世界中的一个小型 RTOS 系统的源码。
cfg.mak:顶层 Makefile 配置文件,定义编译选项和路径。
common/:内存管理子系统,负责整个系统的内存分配、管理和多媒体大块内存(MMZ)的分配与回收。
interdrv/:内部驱动,1.mipi_rx/,MIPI CSI-2 接收驱动,负责接收 IMX586 摄像头的图像数据。2.ot_adc,ADC 驱动,用于采集模拟信号(如电池电压、光照强度等)3.ot_usr,用户空间接口,提供驱动与用户程序交互的通道。4.sysconfig,系统配置驱动,管理硬件模块的使能/禁用、引脚复用等。5.wtdg,看门狗驱动,防止系统死机时无法自动恢复。6.init,驱动初始化脚本或代码。
mpp/:媒体处理平台。
osal/:操作系统抽象层,屏蔽不同操作系统(Linux/RTOS)的差异,提供统一的 API 接口(如线程创建、互斥锁、信号量、内存分配等)。
out/:编译产出(头文件、库、驱动模块)。
out下的目录层级:
scripts/:编译脚本。
scripts下目录层级:
sdk/Hi3516CV610_SDK_V1.0.1.0-BAK/smp/a7_linux/source/mpp目录:
RTSP推流示例程序:
Hi3516CV610_SDK_V1.0.1.0-BAK\smp\a7_linux\source\mpp\sample\rtsp\sample_rtsp.c
struct sample_comm_venc_chn_param单个编码通道的参数配置结构体。
frame_rate; // 帧率
stats_time; // 码率统计时间
gop; // GOP大小(关键帧间隔)
venc_size; // 编码尺寸(宽高),实际存储宽和高。
size; // 存储分辨率枚举,ot_pic_size是一个枚举类型,会有单独的函数将枚举类型转换成实际的宽和高。
profile; // 编码档次(baseline/main/high等)
is_rcn_ref_share_buf; // 参考帧共享缓冲
gop_attr; // GOP属性
type; // 编码类型(H.264/H.265/JPEG等)
rc_mode; // 码率控制模式(CBR/VBR/ABR等)
index只判断了通道1所以在推流时只推通道1。
二、VI部分
参考文档:
HI3516CV610-SIM-开发板资料\原厂sdk\ReleaseDoc\zh\01.software\board\MPP\MPP 媒体处理软件 V6.0 开发参考.pdf"
VI模块api位置:
Hi3516CV610_SDK_V1.0.1.0-BAK\smp\a7_linux\source\mpp\sample\vie\sample_vie.c
Hi3516CV610_SDK_V1.0.1.0BAK\smp\a7_linux\source\mpp\sample\common\sample_comm_sys.c
Hi3516CV610_SDK_V1.0.1.0BAK\smp\a7_linux\source\mpp\sample\common\sample_comm_vi.c
Hi3516CV610_SDK_V1.0.1.0BAK\smp\a7_linux\source\mpp\sample\common\sample_comm_vpss.c
下面学习内容以 sample_vie.c 为出发点。
阶段一:系统初始化
sample_vie_sys_init()
→ sample_comm_sys_init_with_vb_supplement() // 初始化VB视频缓冲池
→ ss_mpi_vi_set_vi_vpss_mode() // 设置VI/VPSS工作模式
阶段二:VI设备配置与启动
sample_comm_vi_get_default_vi_cfg() //获取VI默认配置(sensor\mipi\dev\bind\gpr\pipe info).
sample_vie_get_pipe_wrap_line()
→ss_mpi_sys_get_vi_wrap_buffer_line() //获取VICAP-VPPROC离线卷绕模式下的buffer卷绕行数。buf_line行数与sensor时序、sensor路数及主从模式、VIPROC性能、VPSS性能、 VENC性能等多方面因素有关,实际业务场景中如果CPU不能每帧都立即响应VI/VPSS/VENC中断,存在中断延迟等情况,客户需要以实际测试场景为准适当增大buf_line。
sample_comm_vi_start_vi()
→sample_comm_vi_start_mipi_rx() //MIPI RX 接口的初始化启动函数,负责配置和启用 MIPI CSI 接收器。
→sample_comm_vi_start_dev() //设备级初始化函数,负责配置 VI 设备的硬件时序参数并启用设备。
→sample_comm_vi_dev_bind_pipe() //设备与管道绑定函数,负责建立 VI 设备(VI DEV)与 VI 管道(VI PIPE)之间的数据关联关系,实现多路数据流的分发与汇聚。
→sample_comm_vi_set_grp_info() //WDR(宽动态)融合组配置函数,负责设置多帧曝光数据的融合策略,实现高动态范围图像的合成。
→sample_comm_vi_start_pipe() //遍历启动所有绑定的pipe。
→sample_comm_vi_start_isp() //法的启动函数,负责为每个 VI PIPE 加载并运行 ISP 固件,实现图像信号处理的核心算法。
PIPE 是 VI 模块的ISP 处理管道,承担以下关键职责:
VI PIPE 是 VI 模块的 ISP 处理管道,由硬件电路(数据通路、ISP 硬核、行缓冲 FIFO)和软件线程(AE/AWB/AF/3DNR 算法)共同组成。
核心职责:
接收数据— 从 VI DEV 获取 Bayer RAW 数据
ISP 处理— 硬件执行去马赛克、颜色校正、WDR 融合;软件线程实时计算 3A 参数并反馈调节 Sensor
时序控制— 帧率控制、帧同步、WDR 多帧时序对齐
输出推送— 将处理后的 YUV 数据推送到 VI CHN
后续流转— CHN 输出至 VPSS(视频前处理)或 VENC(编码)
阶段三:VI与VPSS绑定(数据流转关键)
sample_comm_vi_bind_vpss() //配置vi端与vpss端通道结构体
→ss_mpi_sys_bind() //VI 模块与 VPSS 模块的系统级绑定函数,通过 MPP(Media Process Platform)框架的标准绑定机制,建立从 VI 通道到 VPSS 通道的数据流转通路。
阶段四、VPSS图像处理启动
sample_vie_start_vpss
→sample_common_vpss_start() //创建并启动 VPSS 处理组及其下属通道,实现图像的缩放、裁剪、格式转换等前处理功能。
组(GRP)与通道(CHN)的层级关系:
组是资源管理单元,每个grp对应一个dev即一个sensor。通道是物理通道单独处理输出。grp可以接收一个sensor,在一个grp下存在多个chn,每个chn可按要求输出不同规格的数据流。
阶段五、VPSS与VENC绑定+编码启动
sample_vie_start_and_bind_venc
→sample_vie_start_venc()//逐个创建编码通道并配置参数
→sample_comm_venc_start() //逐个创建编码通道并配置参数
→sample_comm_venc_start_get_stream() //创建线程,轮询/阻塞获取编码后的H.264/H.265码流,接着回调或写入文件/网络推流。
→sample_comm_vpss_bind_venc() // vpss通道0/1绑定venc通道0/1。
阶段六、业务功能运行(代码支持的 VI 扩展能力)
VI 模块启动后,可执行硬件级图像功能:
1.FPN 校准与矫正:sample_vie_fpn() → 消除传感器固定模式噪声。
2.LDC 畸变矫正 + 旋转:sample_vie_ldc_rotation() → 90° 旋转、镜头畸变校正。
3.低延迟模式:sample_vie_lowdelay() → VI+VPSS 双低延迟,降低传输时延。
4.用户自定义图像:sample_vie_user_pic() → 加载自定义图片替代摄像头输入。
5.双摄像头采集:sample_vie_two_sensor() → 两路 VI 同时采集。
6.模式切换:线性模式 ↔ WDR 宽动态模式、分辨率切换(4M→1080P)。
通过接口 sample_vie_execute_case(td_u32 case_index) 选择功能。
阶段七、退出流程(逆序释放资源)
sample_vie_stop_route()
→sample_vie_stop_and_unbind_venc()
→sample_comm_vpss_un_bind_venc() //解绑所有模块链路
→ sample_vie_stop_venc() //停止编码
→sample_vie_stop_vpss()
→sample_common_vpss_stop()
→ss_mpi_vpss_disable_chn() //关闭通道
→ ss_mpi_vpss_stop_grp() //关闭通道
→ss_mpi_vpss_destroy_grp()//释放GRP资源
→sample_comm_vi_un_bind_vpss() //逆阶段五中 sample_comm_vpss_bind_venc()
→sample_comm_vi_stop_vi()
→sample_comm_vi_stop_isp() //停止ISP算法线程,注销3A库
→sample_comm_vi_stop_pipe() //销毁VI PIPE硬件资源
→sample_comm_vi_dev_unbind_pipe() //解除DEV与PIPE的绑定关系
→sample_comm_vi_stop_dev() //禁用VI DEV,停止时序解析
→sample_comm_vi_stop_mipi_rx() //关闭MIPI接收器,复位Sensor→sample_comm_sys_exit() //系统退出
→ss_mpi_sys_exit() //去初始化MPP系统。包括视频输入输出、视频编解码、视频叠加区域、视频处理、图形处理等模块都会被销毁或者禁用。
→ss_mpi_vb_exit() //去初始化MPP视频缓存池。
VI 模块的数据流向总结:
sensor(MIPI) → VI硬件采集(ISP处理、FPN/LDC矫正)→ VB Pool(RAW/YUV数据)→ VI通道输出→ VPSS(缩放、双码流、包装)→ VENC(H265编码)→ 最终码流输出
三、VPSS部分
阶段一、VPSS默认配置
sample_comm_vpss_get_default_vpss_cfg()
遍历物理通道,初始化3DNR,主码流使能。子码流是否使能、配置可选(配置了grp、chn、wrap)。
阶段二、VPSS帧包装配置(wrap)
sample_vie_vpss_get_wrap_cfg()
1.根据sensor确定full_lines_std。
full_lines_std用于表示sensor 一帧图像的有效行数 + VBlank 行数,用于计算wrap缓冲区的大小和跳转地址。不同 sensor 的垂直同步时序不同,导致此值差异。
2.配置wrap参数。
VPSS Wrap 模式是一种内存优化机制:图像数据在 DDR 中循环存储,而非每次重新分配。通过复用缓冲区,减少内存分配/释放开销。配合 VDEC(解码)或 VENC(编码)进行数据流转。
3.记录所需缓冲区大小。
阶段三、启动VPSS
sample_vie_start_vpss()
→sample_vie_vpss_set_wrap_grp_int_attr() //配置组属性
→ sample_common_vpss_start() //创建组,启动组,启动通道
→ss_mpi_vpss_set_chn_low_delay() //对通道1开启低延迟
阶段四、VPSS与VI绑定
参考VI部分阶段三。
阶段五、VPSS与VENC绑定
参考VI部分阶段五。
阶段六、停止VPSS
参考VI部分阶段七。
四、VENC部分
Hi3516CV610_SDK_V1.0.1.0-BAK\smp\a7_linux\source\mpp\sample\venc\sample_venc.c
Hi3516CV610_SDK_V1.0.1.0BAK\smp\a7_linux\source\mpp\sample\common\sample_comm_venc.c
| 编号 | 示例函数 | 关键额外步骤 | 说明 |
|---|---|---|---|
| 0 | sample_venc_normal | 无 | 基础流程(章节 3) |
| 1 | sample_venc_intra_refresh | sample_venc_set_intra_refresh→ss_mpi_venc_set_intra_refresh | 设置行/列内部刷新(IR),让编码器在特定位置强制 I 帧 |
| 2 | sample_venc_roi_bg | sample_venc_set_roi_attr+ss_mpi_venc_set_roi_bg_frame_rate | ROI(Region‑of‑Interest)+背景帧率调低,节省码率 |
| 3 | sample_venc_mjpeg_roi_set | sample_venc_set_mjpeg_roi→ss_mpi_venc_set_jpeg_roi_attr | MJPEG自定义 ROI(仅对 JPEG 有效) |
| 4 | sample_venc_svac3 | 编码类型切换(payload = OT_PT_SVAC3)+sample_venc_set_svac3_video_param | SVAC3(特定行业编码) |
| 5 | sample_venc_inner_scale | sample_venc_chn_bind_venc_chn→ 系统层sys_bind把VENC0输出直接绑定到VENC1输入,实现内部缩放(不经过 VPSS) | 需要Mini‑Buf启用,保证两路 VENC 能共享同一块内部缓冲区 |
| 6 | sample_venc_qpmap | sample_comm_venc_qpmap_send_frame→ 业务层把QP‑map(每宏块的QP)随帧送进 VENC | 需先把GOP参数取出来再创建通道 |
| 7 | sample_venc_debreath_effect | sample_venc_set_debreath_effect→ss_mpi_venc_set_debreath_effect | 呼吸光抑制参数配置 |
| 8 | sample_venc_mjpeg_roimap | sample_comm_venc_send_roimap_frame→ 按自定义 ROI Map发送帧 | 业务层自行准备ot_venc_jpeg_roi_attr数组 |
| 9 | sample_venc_mosaic_map | sample_comm_venc_mosaic_map_send_frame→马赛克(Mosaic)示例 | 业务层会把马赛克块信息交给 VENC |
| 10 | sample_venc_composite | Composite(复合) · sample_composite_venc_start_venc(打开composite_enc_en、mosaic_en)· sample_composite_vpss_init(VPSS 深度 2)· 多线程 composite_send_multi_frame_proc把两路(马赛克 + 原图)帧合成后送 VENC(ss_mpi_venc_send_multi_frame) | 演示一帧内多路(Mosaic + 原图)合成编码,使用系统 bind(sys_bind)实现 VENC‑VENC 直接链路,适合复合场景 |
下面以模式0举例。
阶段一、参数准备
阶段二、系统初始化
sample_venc_online_wrap_start_sys_vi_vpss() 内部追踪如下
sample_venc_online_wrap_get_default_vpss_cfg()
sample_venc_online_wrap_get_default_sys_cfg()
→sample_venc_online_wrap_sys_init()
→sample_venc_online_wrap_sys_init()
→sample_venc_online_wrap_get_default_vb_cfg()
sample_venc_vi_init()
→sample_comm_vi_start_vi(vi_cfg);
sample_venc_online_wrap_start_vpss()
sample_comm_vi_bind_vpss()
主要分为三个子步骤:
1.VB初始化 (sample_venc_online_wrap_sys_init)
sample_venc_online_wrap_get_default_vb_cfg 计算输入(VI-YUV)与输出(VPSS-YUV)各自所需的块大小以及块数。填入ot_vb_cfg结构体的common_pool[i]中,随后调用sample_comm_sys_vb_init 把VB资源加入系统。
为什么要手动算块大小?
- 不同分辨率、像素格式、压缩模式会导致实际占用的显存不同。
- 对每一路流都算一次,防止VB 不够导致帧丢。
2.VI初始化(sample_venc_vi_init)
sample_comm_vi_start_vi会自动把vi_cfg(分辨率、帧率、工作模式)写入硬件寄存器,开启传感器采流。
3.VPSS初始化(sample_venc_online_wrap_start_vpss)
外部传入配置好的vpss_cfg,
再调用sample_common_vpss_start完成VPSS的创建,把组号、属性交给底层驱动。
4.VI → VPSS 绑定 (sample_comm_vi_bind_vpss)
把 VI 的 Pipe 0 / Channel 0 的输出直接送给 VPSS 组 0 / 通道 0。
这里 不需要再手动创建通路,系统内部会自动在硬件上建立相应的 DMA 路径。
阶段三、创建编码通道
核心函数:sample_venc_normal_start_encode
1.码流配置
- GOP(Group Of Pictures)(两个关键帧即 I 帧间的一组画面)模式决定关键帧、P‑帧的切换方式。GOP模式=I帧、P帧、B帧的排列规则。
sample_venc_set_video_param为每条通道(这里是两条)填好sample_comm_venc_chn_param:- 帧率、GOP 长度、统计时间
- payload type(H.265、H.264)
- 码率控制模式(CBR/VBR/ABR/…)
- 码流尺寸(从
enc_size取) - profile / mini‑buf(内部缓冲区)
- 通过
sample_comm_venc_mini_buf_en开启 Mini‑Buf 功能,提升硬件复用率(适合多路复用时)。
2.启动VENC
sample_comm_venc_start();
→ sample_comm_venc_create() //创建编码通道
→ ss_mpi_venc_start_chn() //开启编码通道接收输入图像,允许指定接收帧数,超出指定的帧数后自动停止接收图像。
3.VPSS与VENC绑定
sample_comm_vpss_bind_venc(vpss_grp, vpss_chn[i], venc_chn[i]);
- 把VPSS 物理通道(0/1)映射到VENC 逻辑通道(0/1)。
- 这样 VPSS 输出的YUV 帧会直接送进对应的 VENC 编码器。
- 注意:绑定成功后,如果不再使用某路 VENC,一定要
sample_comm_vpss_un_bind_venc解绑,否则 VPSS 会一直往这个通道发送数据,引起帧丢或卡死。
向VENC发送VPSS输出的YUV帧
sample_comm_qpmap_send_frame_proc()//线程处理函数
#申请QP Map + SkipWeight 硬件内存(MMZ)给编码算法用的专用内存
→sample_comm_alloc_qpmap_skipweight_memory()//申请一块硬件内存,用来存放 “每个区域用什么 QP” 的映射表。VENC 编码时会读这张表。
#配置PSS通道缓冲区深度(depth=3)防止送帧卡顿、丢帧
→ss_mpi_vpss_get_chn_attr()// 获取当前VPSS通道属性
→ss_mpi_vpss_set_chn_attr() // 把新配置写回硬件
#正式启动:把VPSS图像帧 → 送给VENC编码
→sample_comm_qpmap_send_frame_start()
→ss_mpi_vpss_get_chn_frame()//从 VPSS 通道**获取一帧图像**配置 QP Map 编码参数告诉编码器:这块区域用什么QP、权重、映射表
→sample_comm_qpmap_send_frame_ex()//把 **带QP映射的图像帧** 发送给VENC 编码
→ss_mpi_vpss_release_chn_frame()//释放VPSS获取的帧(必须释放,否则硬件卡死)
出错会释放QP Map和SkipWeight内存
QPMap(Quantization Parameter)的核心价值在于区域化码率控制:
普通模式:整帧使用相同QP,无法区分重要性。
QPMap模式:每个宏块独立QP,重要区域高质量,其他区域低质量。
阶段四、码流的获取&保存
sample_comm_venc_start_get_stream()//创建线程,注册下面的线程处理函数。
sample_comm_venc_get_venc_stream_proc()//从每个通道中获取码流并保存
→sample_comm_set_name_save_stream()
1.决定码流文件名称,打开文件保存码流
→ss_mpi_venc_get_chn_attr()
→sample_comm_venc_get_file_postfix()//获取文件后缀根据有效载荷即编码方式(payload)
→sample_comm_set_file_name()
2. 获取 VENC 通道文件描述符与码流缓冲区基础信息
→ss_mpi_venc_get_fd()//获取VENC通道的文件描述符
→ss_mpi_venc_get_stream_buf_info()//获取码流缓冲区信息,视频数据的大小、位置....
这里将码流保存到了文件中。
阶段五、退出路径
资源释放顺序非常重要:
- 先停止码流获取线程(防止线程仍在读取已销毁的硬件)。
- 再解绑 VPSS–VENC,确保 VPSS 不再往已经停止的 VENC 推帧。
- 停止VENC、VPSS、VI。
- 最后释放 VB,防止还有块在使用时被系统收回。
VENC在这套 Demo 中的核心是:先准备好统一的内存(VB) → 把原始 YUV 通过 VI → VPSS 流向 VENC → (可选)在 VENC 前做 ROI / QP‑map / DE‑Breath 等高级配置 → 把压好的码流取出来保存。
所有业务的底层链路都是 “VI → VPSS → VENC → Get‑Stream”。
不同模式的唯一区别就是在 VENC 创建后向 VENC 注入的额外属性(payload、GOP、RC、ROI、QP‑map、Mosaic、Debreath、Composite)以及发送帧时是否使用了带‑aux‑buffer 的专用 API(qpmap、roimap、mosaic、multi‑frame)。
五、加入RTSP推流
位置:
Hi3516CV610_SDK_V1.0.1.0-BAK\smp\a7_linux\source\mpp\sample\rtsp\sample_rtsp.c
1.拿到 H264 裸流,交给 RTSP(你加的推流入口)
2.RTSP 核心调度:打包 RTP、管理队列
第一部分
第二部分
3.同步心跳(发送 SR 包(Sender Report))
4.真正发送到网络
5.rtsp_do_eventRTSP 服务的主循环(大脑)
负责:接收连接、处理请求、发送音视频、处理网络事件