海思 3403 MPP 全链路解析: VI、VPSS、VENC 的运行逻辑与实践

一、海思MPP 官方 Sample 总体分析

(一)Sample 核心定位与架构适配​

Hi3403 作为高端视觉融合计算 SOC,其 MPP Sample 深度匹配芯片硬件特性 —— 针对四核 A55 CPU、10TOPS NN 加速引擎、双核 Vision DSP 等核心单元,提供从基础音视频处理到智能融合计算的全链路示例。Sample 基于 MPI(MPP Program Interface)接口开发,完全屏蔽底层硬件差异,所有例程均遵循 “驱动加载→系统初始化→模块配置→数据流转→资源释放” 的标准流程,与《HiMPP IPC V2.0 媒体处理软件开发参考.pdf》文档高度契合。

(二)Sample 功能模块分类与关键例程解析​

基础媒体处理类(核心必学)
1. 视频输入输出(sample_vio):

        适配 Hi3403 的 4 路 sensor 输入能力,支持 MIPI/LVDS 等多接口配置,示例中包含 4K60fps 数据流捕获与 HDMI 输出的完整逻辑,可直接验证 VI(视频输入)、VO(视频输出)模块的硬件适配性。​

2. 编解码模块(sample_venc/sample_vdec):​

        编码例程支持 H.265/H.264 4K120fps 主码流 + 1080p30fps 辅码流的多码流输出,覆盖 CBR/VBR/QPMAP 等码率控制模式,匹配芯片 160Mbps 最大码率特性;​
解码例程支持 8K 分辨率解码与 YUV/RGB 格式转换,演示 VDEC→VPSS→VO 的数据通路,可直接用于 4K 监控、超高清回放场景验证。

3. 音频处理(sample_audio):

涵盖 AI(音频输入)→AENC(音频编码)→ADEC(音频解码)→AO(音频输出)全链路,支持 VQE 音质增强与 HDMI 音频透传,适配 Hi3403 的多通道音频接口。​

图像增强与智能计算类(高端特性)
1. ISP 与画质优化(sample_awb_calibration/sample_dis):

        针对 Hi3403 的 3F WDR、3D 降噪硬件能力,提供自动白平衡校准、六轴数字稳像示例,可通过修改参数验证 12dB 信噪比提升效果。

2. 智能加速单元(sample_nnie_main/sample_dpu_main):​

        NNIE 例程演示 10TOPS 算力的神经网络推理流程,支持 INT8/FP16 精度模型加载,可直接运行 ResNet 等经典模型;​
        DPU 例程实现双目深度计算,展示 “VI 采集→矩形裁剪→特征匹配” 的深度图生成链路,适配硬件深度加速单元。​

高级融合应用类(综合实践)​
1.全景拼接(sample_avs):

        基于 Hi3403 的硬件拼接引擎,实现多 sensor 图像的实时拼接与畸变矫正,示例中包含鱼眼矫正算法(sample_fisheye),可用于安防全景摄像头开发。​

2.图形叠加(sample_vgs/sample_tde):

演示 8 区域 OSD 叠加与 2D 图形加速绘制,结合 HIFB 帧缓冲管理,适配 GUI 界面与视频画面的融合显示需求

(三)视频缓存池(VB Pool):用作MPP 数据流转

视频缓存池(简称 VB,Video Buffer)是海思 MPP 架构中统一的媒体内存管理单元,负责为 VI、ISP、VPSS、VENC 等所有模块提供 “可复用的内存块”,是避免模块间数据拷贝、提升效率的关键 —— 没有 VB 池,各模块需独立申请内存,会导致内存碎片化严重、数据传输延迟高,甚至引发系统崩溃。​

1. 视频缓存池的核心价值​

  • 内存复用:缓存池创建后,各模块通过 “申请 - 使用 - 释放” 机制复用内存块,避免重复分配 / 销毁(如 VI 采集的 YUV 数据块,可直接传递给 VPSS 裁剪,无需拷贝);​
  • 硬件适配:VB 池基于海思 MMZ(媒体内存区)创建,内存地址被映射到硬件可直接访问的区域(如 Hi3403 的 VI/ISP 硬件单元可直接读写 VB 块数据,无需 CPU 中转);​
  • 统一管理:通过 MPI 接口(如HI_MPI_VB_CreatePool)可统一配置缓存块大小、数量、内存类型,支持动态扩容,简化多模块内存协同。​

2. 视频缓存池的分类与结构(树状图)

视频缓存池(VB Pool)
├─ 按归属分类
│  ├─ 系统默认池:MPP初始化时自动创建,供通用模块(如VI、VENC)使用,默认分配32MB MMZ内存
│  └─ 用户自定义池:开发者通过`HI_MPI_VB_CreatePool`创建,用于特殊场景(如4K大分辨率缓存、多通道独立缓存)
│     ├─ 例:为Hi3403的4K VI采集创建256MB自定义池,避免与其他模块抢占内存
│     └─ 例:为NNIE智能推理创建64MB独立池,确保推理数据不被覆盖
└─ 按数据类型分类
   ├─ YUV缓存块:存储YUV420/YUV422格式数据,是视频处理的核心载体,大小=分辨率×每个像素字节数(如4K YUV420:3840×2160×1.5=11980800字节≈11.4MB)
   ├─ RGB缓存块:存储RGB888/RGB565格式数据,用于ISP后处理、VO显示
   └─ 元数据缓存块:存储图像参数(如曝光值、白平衡增益)、编码参数(如码率控制信息),体积小(通常1KB~16KB)

3. 缓存池的关键运作机制​

  • 内存分配:VB 池的内存来自 MMZ(媒体内存区),需在初始化时指定VB_ATTR_S参数(块大小、块数量、内存类型),Hi3403 支持最大 4GB MMZ 内存分配(需在 bootargs 中配置mem=xxxM mmz=yyyM@zzzM);​
  • 块管理逻辑:​
  • a.申请:模块通过HI_MPI_VB_Alloc申请缓存块,指定块大小和池 ID,返回 “VB 块句柄”(唯一标识);​
  • b.使用:通过句柄获取缓存块的物理地址(供硬件访问)和虚拟地址(供 CPU 访问),直接写入 / 读取数据;​
  • c.释放:模块使用完毕后通过HI_MPI_VB_Free释放块,缓存块回归池内等待下次复用;​
  • 异常处理:若申请时池内无空闲块,接口返回HI_ERR_VB_NO_FREE_BLOCK,需通过增大池容量或优化释放逻辑解决(如 Hi3403 多通道 4K 采集时,需将 VB 池扩容至 512MB 以上)。

4.视频采集 - 处理 - 编码 - 输出全流程(含缓存池协同)​

Hi3403 的视频全流程本质是 “数据在各模块间通过 VB 池流转” 的过程,每个环节均依赖缓存池提供的内存块,流程如下:

Hi3403 MPP视频全流程
├─ 1. 初始化阶段:创建缓存池+模块初始化
│  ├─ 1.1 系统初始化:调用`HI_MPI_SYS_Init`,初始化MPP核心环境
│  ├─ 1.2 缓存池创建:调用`HI_MPI_VB_CreatePool`,创建YUV缓存池(如4K×20块)
│  └─ 1.3 模块初始化:依次初始化VI、ISP、VPSS、VENC、VO模块
├─ 2. 数据采集阶段(VI→ISP)
│  ├─ 2.1 VI采集:CMOS传感器输出rawRGB数据,VI模块通过MIPI接口接收,申请“raw缓存块”存储原始数据
│  ├─ 2.2 ISP处理:VI将raw缓存块传递给ISP,ISP完成去马赛克、白平衡、降噪后,生成YUV数据,写入新的“YUV缓存块”(从VB池申请)
│  └─ 2.3 缓存块传递:ISP释放raw缓存块(回归池),将YUV缓存块句柄传递给VPSS
├─ 3. 数据处理阶段(VPSS)
│  ├─ 3.1 图像优化:VPSS申请YUV缓存块,对ISP输出的YUV数据进行裁剪(如4K→1080p)、缩放、OSD叠加
│  ├─ 3.2 多通道分发:VPSS将处理后的YUV数据写入2个缓存块,1个传递给VENC(编码),1个传递给VO(预览)
│  └─ 3.3 缓存释放:VPSS释放原始YUV缓存块(回归池)
├─ 4. 编码与输出阶段(VENC+VO)
│  ├─ 4.1 VENC编码:VENC接收VPSS的YUV缓存块,进行H.265/H.264编码,生成码流数据,写入“码流缓存块”(从VB池申请),编码完成后释放YUV缓存块
│  ├─ 4.2 码流输出:VENC将码流缓存块数据写入文件(本地存储)或通过网络发送(如RTSP),释放码流缓存块
│  ├─ 4.3 VO预览:VO接收VPSS的YUV缓存块,将数据渲染到HDMI/BT1120接口显示,显示完成后释放YUV缓存块
│  └─ 4.4 循环流转:各模块持续申请-使用-释放缓存块,实现视频实时处理
└─ 5. 销毁阶段
   ├─ 5.1 停止模块:依次停止VO、VENC、VPSS、ISP、VI
   ├─ 5.2 释放缓存:调用`HI_MPI_VB_DestroyPool`销毁自定义缓存池
   └─ 5.3 系统销毁:调用`HI_MPI_SYS_DeInit`释放MPP资源

5.关键环节的缓存池协同逻辑​

  • VI→ISP 的 raw 数据传递:VI 采集的 raw 数据存储在 “raw 缓存块”(大小 = 分辨率 ×12bit/8,如 4K 12bit raw:3840×2160×1.5=11.4MB),ISP 处理后直接复用该块的内存空间(覆盖写入 YUV 数据),避免数据拷贝;​
  • VPSS 的多通道分发:VPSS 通过 “缓存块共享” 机制,将同一 YUV 数据的句柄传递给 VENC 和 VO,无需复制数据(Hi3403 的硬件支持 “多端口同时访问同一缓存块”);​
  • VENC 的码流缓存:码流缓存块大小需根据码率动态调整(如 4K H.265 16Mbps 码率,每帧码流约 16KB),通常创建 100 块以上避免码流丢失。

6.MPP 系统本身的初始化流程(核心步骤)​

MPP 系统初始化是 “模块可用” 的前提,需严格遵循 “环境→内存→模块” 的顺序,否则会导致接口调用失败(如未初始化系统就创建缓存池,会返回HI_ERR_SYS_NOT_INIT)。​

6.1. 系统初始化完整流程(树状图)
MPP系统初始化流程(基于Hi3403)
├─ 步骤1:配置系统参数(前置准备)
│  ├─ 1.1 设置MMZ内存:在bootargs中配置MMZ大小(如`mmz=512M@1024M`,表示从1024MB地址开始分配512MB作为媒体内存)
│  ├─ 1.2 加载驱动:通过`insmod`加载MPP核心驱动(hi_mpp.ko)、VI驱动(hi_vi.ko)、VENC驱动(hi_venc.ko)
│  └─ 1.3 头文件与库依赖:在代码中包含MPI头文件(如`#include <hi_mpp.h>`),链接MPP静态库(libmpi.a)
├─ 步骤2:初始化MPP核心环境(HI_MPI_SYS_Init)
│  ├─ 2.1 内部逻辑:初始化MPP系统的信号量、消息队列、模块管理链表
│  ├─ 2.2 硬件资源初始化:初始化Hi3403的媒体总线(如MIPI、HDMI)、时钟(VI/ISP时钟使能)、中断控制器
│  ├─ 2.3 注意事项:该接口需在所有模块初始化前调用,且仅能调用一次(重复调用返回`HI_ERR_SYS_ALREADY_INIT`)
├─ 步骤3:创建视频缓存池(HI_MPI_VB_CreatePool)
│  ├─ 3.1 配置VB属性结构体(VB_ATTR_S):
│  │  ├─ u32BlkSize:单块大小(如4K YUV420需11980800字节,建议向上对齐到4KB)
│  │  ├─ u32BlkCnt:块数量(如20块,确保多帧数据同时处理)
│  │  └─ enMemType:内存类型(VB_MEM_MMZ:优先使用MMZ内存;VB_MEM_SYS:使用系统内存,仅用于调试)
│  ├─ 3.2 调用接口创建池:返回池ID(如0为系统池,1为自定义池)
│  └─ 3.3 校验:通过`HI_MPI_VB_GetPoolInfo`获取池信息,确认块数量、空闲块数是否符合预期
├─ 步骤4:初始化业务模块(按依赖顺序)
│  ├─ 4.1 ISP初始化(HI_MPI_ISP_Init):加载ISP算法库(如awb、3a算法),配置图像处理参数(如WDR模式、降噪强度)
│  ├─ 4.2 VI初始化(HI_MPI_VI_Init):配置传感器接口(MIPI/LVDS)、采集分辨率(如3840×2160)、帧率(如30fps)
│  ├─ 4.3 VPSS初始化(HI_MPI_VPSS_Init):配置通道数量(如4通道)、图像处理能力(如支持4K缩放、8区域OSD)
│  ├─ 4.4 VENC初始化(HI_MPI_VENC_Init):配置编码格式(H.265/H.264)、码率控制模式(CBR/VBR)、最大码率(如160Mbps)
│  └─ 4.5 VO初始化(HI_MPI_VO_Init):配置显示接口(HDMI/BT1120)、显示分辨率(如4K@60Hz)、图层数量(如2层:视频层+OSD层)
└─ 步骤5:初始化校验(关键)
   ├─ 5.1 接口返回值检查:所有MPI接口需判断返回值是否为`HI_SUCCESS`,若失败通过`HI_MPI_SYS_GetErrCode`获取详细错误信息
   ├─ 5.2 硬件状态检查:通过`HI_MPI_VI_GetSensorInfo`确认传感器是否正常连接,`HI_MPI_VENC_GetChnAttr`确认编码通道配置正确
   └─ 5.3 缓存池状态检查:通过`HI_MPI_VB_GetFreeBlkCnt`确认缓存池有足够空闲块(如空闲块数≥5,避免初始化后无内存可用)
6.2. 初始化常见问题与解决​
  • 问题 1:调用HI_MPI_VB_CreatePool返回HI_ERR_VB_MMZ_NO_MEM(MMZ 内存不足)​

解决:在 bootargs 中增大 MMZ 分配(如mmz=1024M@1024M),或减少缓存块大小 / 数量;​

  • 问题 2:VI 初始化失败,返回HI_ERR_VI_SENSOR_NOT_CONNECT​

解决:检查传感器驱动是否加载(lsmod | grep hi_sensor),确认 VI 接口配置与传感器匹配(如 MIPI 通道数、数据格式);​

  • 问题 3:初始化后缓存池空闲块为 0​

解决:检查是否有模块未释放缓存块(如提前调用了HI_MPI_VI_Start),或在创建池时增加块数量。

二、VI部分

VI 部分详解 1:函数调用与 VI 调用图谱

VI(视频输入)是海思 MPP 的 “数据入口”,负责从传感器采集原始图像数据(rawRGB),其函数调用逻辑严谨且层级分明。咱们可以通过 “功能分类法” 绘制调用图谱,快速理清核心接口的作用与依赖关系。

1. 函数调用图谱的分析方法

学习 VI 模块的关键是 “按流程拆解函数”:先区分 “全局初始化”“设备配置”“通道操作”“数据流转” 四大功能块,再梳理块内函数的调用顺序(如必须先初始化设备,才能创建通道)。这种方法能帮咱们在 sample_vi 代码中快速定位关键逻辑,比如看到HI_MPI_VI_SetSensorAttr就知道是配置传感器参数,看到HI_MPI_VI_StartChn就知道要开始采集数据了。

2. VI 模块函数调用图谱

VI模块函数调用图谱(基于MPI接口)
├─ 1. 全局初始化/销毁(模块级操作)
│  ├─ HI_MPI_VI_Init():VI模块全局初始化(需在MPP_SYS_Init之后调用,和咱们之前讲的系统初始化流程一致)
│  ├─ HI_MPI_VI_DeInit():VI模块全局销毁(需先停止所有通道和设备)
│  └─ 注意:重复初始化会返回HI_ERR_VI_ALREADY_INIT,错误逻辑和VPSS类似
├─ 2. 设备(dev)操作(硬件接口层)
│  ├─ HI_MPI_VI_CreateDev():创建VI设备(对应物理传感器接口,如MIPI CSI-2)
│  │  └─ 参数:dev ID(0~3,Hi3403支持4路设备)、传感器类型(如AR0230)、接口模式(MIPI/LVDS)
│  ├─ HI_MPI_VI_DestroyDev():销毁VI设备(需先销毁下属通道)
│  ├─ HI_MPI_VI_SetDevAttr():配置设备属性(如MIPI通道数、数据格式12bit raw)
│  └─ HI_MPI_VI_GetDevAttr():获取当前设备配置(用于校验)
├─ 3. 通道(chn)操作(数据采集层)
│  ├─ HI_MPI_VI_CreateChn():在设备下创建通道(1个dev可创建多个chn,如主通道+辅通道)
│  │  └─ 参数:chn ID(0~7)、采集分辨率(如3840×2160)、帧率(30fps)
│  ├─ HI_MPI_VI_DestroyChn():销毁通道(需先停止通道)
│  ├─ HI_MPI_VI_StartChn():启动通道采集数据(开始从传感器接收raw数据)
│  └─ HI_MPI_VI_StopChn():停止通道采集
├─ 4. 传感器配置(硬件适配层)
│  ├─ HI_MPI_VI_SetSensorAttr():配置传感器参数(曝光时间、增益、白平衡)
│  ├─ HI_MPI_VI_GetSensorAttr():获取传感器当前状态(如实际曝光值)
│  └─ HI_MPI_VI_SensorRegWrite():直接写入传感器寄存器(高级调试用,如修改ISP参数)
└─ 5. 数据流转(与ISP/VPSS交互)
   ├─ HI_MPI_VI_GetFrame():从VI通道获取raw数据帧(返回VB池缓存块句柄)
   ├─ HI_MPI_VI_ReleaseFrame():释放raw数据帧(归还VB池,避免内存泄漏)
   └─ HI_MPI_VI_BindISP():将VI通道绑定到ISP(自动将raw数据传给ISP处理,咱们详解2会重点讲)

VI 部分详解 2:Sensor 操作与 ISP 模块的代码细节

Sensor(图像传感器)是 VI 的数据源头,而 ISP(图像信号处理器)是 raw 数据的 “第一站优化”,两者与 VI 模块的交互是视频采集的核心。咱们结合 sample_vi 代码,拆解其操作逻辑。

1. Sensor 与 VI 的连接关系

组件作用与 VI 的交互方式Hi3403 典型配置
Sensor将光信号转为电信号(rawRGB)通过 MIPI/LVDS 接口连接 VI 设备4 路 AR0230(1080p@60fps,12bit raw)
VI 设备(dev)物理接口控制器驱动 Sensor 并接收电信号dev 0 绑定 MIPI 通道 0,对应 Sensor 0
VI 通道(chn)逻辑数据通道从 dev 中提取特定分辨率的数据流chn 0 采集 4K 主码流,chn 1 采集 1080p 辅码流

2. Sensor 初始化流程(树状图 + 代码细节)

Sensor初始化流程(sample_vi关键步骤)
├─ 步骤1:加载Sensor驱动
│  ├─ 代码:insmod hi_sensor_ar0230.ko(通过脚本加载对应型号驱动)
│  └─ 校验:lsmod | grep hi_sensor_ar0230(确认驱动加载成功)
├─ 步骤2:创建VI设备(绑定Sensor)
│  ├─ 代码:
│  │  VI_DEV_ATTR_S stDevAttr;
│  │  stDevAttr.enIntfMode = VI_INTF_MODE_MIPI; // MIPI接口
│  │  stDevAttr.u32MipiLane = 4; // 4路MIPI lane
│  │  HI_MPI_VI_CreateDev(0, &stDevAttr); // 创建dev 0
│  └─ 关键:dev ID需与Sensor物理接口一一对应(如dev 0对应第一路MIPI)
├─ 步骤3:配置Sensor属性(曝光、增益等)
│  ├─ 代码:
│  │  VI_SENSOR_ATTR_S stSensorAttr;
│  │  stSensorAttr.u32Exposure = 10000; // 曝光时间10ms
│  │  stSensorAttr.u32Gain = 1024; // 增益(1024=1x,2048=2x)
│  │  HI_MPI_VI_SetSensorAttr(0, 0, &stSensorAttr); // dev 0的chn 0
│  └─ 注意:曝光时间越长画面越亮,但可能导致运动模糊(需与帧率匹配)
└─ 步骤4:启动Sensor采集
   ├─ 代码:HI_MPI_VI_StartChn(0, 0); // 启动dev 0的chn 0
   └─ 数据流向:Sensor→MIPI→VI dev→VI chn→VB池raw缓存块

3. ISP 模块与 VI 的协同(代码细节)

ISP 负责将 raw 数据优化为 YUV 数据(去马赛克、降噪等),与 VI 的绑定是自动数据流转的关键:

// 代码示例:VI绑定ISP(sample_vi中SAMPLE_VI_BindISP函数)
VI_CHN_ATTR_S stChnAttr;
stChnAttr.enPixFmt = PIXEL_FORMAT_BAYER_12BITS; // raw格式
HI_MPI_VI_SetChnAttr(0, 0, &stChnAttr); // 配置VI通道输出raw

// 绑定VI chn到ISP
HI_MPI_VI_BindISP(0, 0, 0); // dev 0的chn 0绑定到ISP 0

// ISP处理后输出YUV(自动存入VB池YUV缓存块)
ISP_CHN_ATTR_S stIspAttr;
stIspAttr.enOutFmt = PIXEL_FORMAT_YUV_SEMIPLANAR_420; // YUV420
HI_MPI_ISP_SetChnAttr(0, &stIspAttr);
HI_MPI_ISP_StartChn(0);

关键逻辑:VI 采集的 raw 数据通过绑定关系自动传入 ISP,ISP 处理后生成的 YUV 数据直接存入 VB 池,供后续 VPSS 使用(和咱们之前讲的视频全流程完全衔接)。

VI 部分详解 3:宽动态、dev 与 chn 概念及程序细节

宽动态是 VI 的核心增强功能(解决高对比度场景过曝 / 欠曝),而 dev 与 chn 的层级关系是理解多通道采集的关键,咱们结合硬件特性和代码深入分析。

1. dev 与 chn 的层级关系(树状图)

VI dev与chn的层级结构(Hi3403支持4路dev)
├─ VI子系统
│  ├─ 设备0(dev 0):对应物理接口MIPI CSI-0,连接Sensor 0(如AR0230)
│  │  ├─ 通道0(chn 0):主通道,采集4K@30fps raw数据→ISP 0处理
│  │  └─ 通道1(chn 1):辅通道,采集1080p@30fps raw数据→ISP 1处理
│  ├─ 设备1(dev 1):对应MIPI CSI-1,连接Sensor 1
│  │  ├─ 通道0(chn 0):采集2K@60fps raw数据
│  │  └─ 通道1(chn 1):采集720p@60fps raw数据
│  └─ 设备2~3:同理,支持最多4路传感器并行采集

核心区别

  • dev 是 “物理设备”(对应传感器接口),每个 dev 绑定一个传感器;
  • chn 是 “逻辑通道”(从同一传感器提取不同分辨率 / 帧率的数据流),实现 “一 sensor 多流输出”。

2. 宽动态(WDR)原理与配置

宽动态通过 “多帧合成” 解决高对比度场景问题(如逆光下同时看清明暗区域),Hi3403 支持 3F WDR(3 帧合成):

宽动态模式原理适用场景关键参数(VI_SENSOR_ATTR_S)
3F WDR短曝光(亮部清晰)+ 中曝光 + 长曝光(暗部清晰)→ 融合逆光监控、户外enWDRMode=VI_WDR_MODE_3F,u32Exposure 分别为 5ms/15ms/45ms
// 使能3F WDR(sample_vi中SAMPLE_VI_SetWDR函数)
VI_SENSOR_ATTR_S stSensorAttr;
HI_MPI_VI_GetSensorAttr(0, 0, &stSensorAttr); // 获取当前配置

stSensorAttr.enWDRMode = VI_WDR_MODE_3F; // 3帧宽动态
// 配置三帧曝光时间(短:中:长=1:3:9,避免帧间运动模糊)
stSensorAttr.stWDRAttr.au32Exposure[0] = 5000;  // 短曝光5ms(亮部)
stSensorAttr.stWDRAttr.au32Exposure[1] = 15000; // 中曝光15ms
stSensorAttr.stWDRAttr.au32Exposure[2] = 45000; // 长曝光45ms

HI_MPI_VI_SetSensorAttr(0, 0, &stSensorAttr); // 应用配置

注意:宽动态会增加数据量(3 帧合成 1 帧),需确保 VB 池 raw 缓存块数量增加 3 倍(避免缓存不足导致丢帧)。

3. 程序细节:多通道采集的启动顺序

在 sample_vi 中,多通道启动必须遵循 “dev→chn→绑定 ISP” 的顺序,否则会返回错误:

// 正确顺序示例(dev 0的2个通道)
HI_MPI_VI_Init(); // 1. 初始化VI模块

// 2. 创建设备
HI_MPI_VI_CreateDev(0, &stDevAttr); 

// 3. 创建并配置通道
HI_MPI_VI_CreateChn(0, 0, &stChnAttr4K);  // 通道0(4K)
HI_MPI_VI_CreateChn(0, 1, &stChnAttr1080p); // 通道1(1080p)

// 4. 配置传感器和宽动态
HI_MPI_VI_SetSensorAttr(0, 0, &stSensorAttr);
HI_MPI_VI_SetSensorAttr(0, 1, &stSensorAttr);

// 5. 绑定ISP
HI_MPI_VI_BindISP(0, 0, 0); 
HI_MPI_VI_BindISP(0, 1, 1);

// 6. 启动通道
HI_MPI_VI_StartChn(0, 0);
HI_MPI_VI_StartChn(0, 1);

三、VPSS部分

MPP 手册解读与函数调用图谱

 1.VPSS 函数调用图谱(基于 MPI 接口,树状图)

VPSS函数调用图谱(按功能分类)
├─ 1. 模块初始化/销毁(全局操作)
│  ├─ HI_MPI_VPSS_Init():VPSS模块全局初始化(需在所有VPSS操作前调用,对应咱们之前MPP系统初始化的步骤4.3)
│  ├─ HI_MPI_VPSS_DeInit():VPSS模块全局销毁(需在所有grp/chn销毁后调用)
│  └─ 注意:重复初始化会返回HI_ERR_VPSS_ALREADY_INIT,和VI模块的错误逻辑一致
├─ 2. 组(grp)操作(核心容器)
│  ├─ HI_MPI_VPSS_CreateGrp():创建VPSS组(需指定grp ID、输入分辨率/格式)
│  ├─ HI_MPI_VPSS_DestroyGrp():销毁VPSS组(需先停止grp并销毁下属chn)
│  ├─ HI_MPI_VPSS_StartGrp():启动grp接收输入帧(通常接ISP的YUV帧,需传入VB块句柄)
│  └─ HI_MPI_VPSS_StopGrp():停止grp,暂停图像处理
├─ 3. 通道(chn)操作(数据输出口)
│  ├─ HI_MPI_VPSS_CreateChn():在指定grp下创建chn(需指定输出分辨率、缩放模式)
│  ├─ HI_MPI_VPSS_DestroyChn():销毁chn(需先停止chn并释放帧资源)
│  ├─ HI_MPI_VPSS_SetChnAttr():配置chn属性(如裁剪区域、OSD开关,咱们后面详解3会用到)
│  └─ HI_MPI_VPSS_GetChnAttr():获取chn当前配置(用于校验或调试)
├─ 4. 数据流转(帧收发)
│  ├─ HI_MPI_VPSS_SendFrame():向grp发送输入帧(传入ISP输出的YUV VB块句柄,无需拷贝)
│  ├─ HI_MPI_VPSS_RecvFrame():从chn接收处理后的帧(供VENC/VO调用,返回新的VB块句柄)
│  └─ HI_MPI_VPSS_ReleaseFrame():释放接收的帧(关键!避免VB池空闲块耗尽,和之前缓存池异常处理呼应)
└─ 5. 特殊功能配置
   ├─ HI_MPI_VPSS_SetChnOsdAttr():配置OSD(如时间水印位置、字体大小)
   ├─ HI_MPI_VPSS_SetChnCropAttr():配置裁剪(如将4K帧裁剪为1080p)
   └─ HI_MPI_VPSS_SetChnScaleAttr():配置缩放(如Hi3403支持硬件加速的 bicubic 算法)

核心概念 ——grp(组)与 chn(通道)​

前面说 VI 模块时,VI 的 chn 直接对应传感器的采集通道(比如 4 路 sensor 对应 4 个 VI chn),但 VPSS 的 grp 和 chn 是 “层级容器” 关系 —— 因为 VPSS 要处理 “一路输入、多路输出”(比如 ISP 输出 1 路 4K 帧,VPSS 要分 1 路给 4K VENC 编码,1 路缩放到 1080p 给 VO 预览),所以需要 grp 做 “统一处理单元”,chn 做 “差异化输出口”。​

1. grp 与 chn 的层级关系(树状图)

VPSS grp与chn层级结构(以Hi3403为例)
├─ VPSS子系统(全局)
│  ├─ VPSS组1(grp 0):对应1路输入图像(如ISP输出的4K YUV420帧)
│  │  ├─ 通道1(chn 0):输出4K帧→给VENC编码(H.265 4K)
│  │  ├─ 通道2(chn 1):缩放为1080p帧→给VO预览(HDMI显示)
│  │  ├─ 通道3(chn 2):裁剪为720p帧→给NNIE做目标检测(智能推理)
│  │  └─ 通道4(chn 3):叠加OSD后→给本地存储(YUV文件)
│  ├─ VPSS组2(grp 1):对应另一路输入图像(如第二路sensor的1080p帧)
│  │  ├─ 通道1(chn 0):输出1080p帧→给第二路VENC编码
│  │  └─ 通道2(chn 1):输出1080p帧→给VO画中画显示
│  └─ 最多支持8个grp(Hi3403硬件限制),每组最多8个chn

2.关键理解与 Hi3403 适配注意事项

grp 的核心作用:对 “同一输入源” 做统一预处理(如降噪、稳像),避免多 chn 重复处理 —— 比如 grp 0 接收 4K 帧后先做一次硬件降噪,下属 4 个 chn 直接用处理后的帧,节省算力;​


chn 的差异化:每个 chn 可独立配置输出参数(分辨率、格式、OSD),但共享 grp 的预处理结果 —— 比如 chn 0 输出 4K,chn 1 输出 1080p,仅缩放步骤不同;​


避坑:创建 chn 时必须指定所属 grp ID,且 grp 未启动时 chn 无法接收帧;销毁时需先销毁 chn 再销毁 grp(顺序错会返回 HI_ERR_VPSS_CHN_EXIST)。

代码执行流程(基于 Hi3403 sample_vpss)​

结合之前的 “视频全流程”,VPSS 的代码流程本质是 “创建 grp→配置 chn→收发帧→销毁资源”,下面拆解关键步骤(关联 MPI 接口和缓存池逻辑):​

1. 完整代码流程(树状图 + 代码逻辑)

VPSS代码执行流程(以“ISP→VPSS→VENC/VO”为例)
├─ 步骤1:VPSS模块初始化(前置)
│  ├─ 调用HI_MPI_VPSS_Init():全局初始化VPSS(需在MPP_SYS_Init之后,和咱们之前的初始化顺序一致)
│  └─ 校验返回值:若为HI_SUCCESS则继续,否则打印错误码(用HI_MPI_SYS_GetErrCode()查原因)
├─ 步骤2:创建VPSS组(grp)
│  ├─ 定义VPSS_GRP_ATTR_S结构体:配置输入分辨率(3840×2160)、格式(PIXEL_FORMAT_YUV_SEMIPLANAR_420)
│  ├─ 调用HI_MPI_VPSS_CreateGrp(0, &stGrpAttr):创建grp 0(对应第一路ISP输入)
│  ├─ 调用HI_MPI_VPSS_StartGrp(0):启动grp 0,开始接收ISP的YUV帧
│  └─ 内存关联:grp会自动从VB池申请“输入缓存块”(需确保VB池有足够4K YUV块)
├─ 步骤3:创建并配置VPSS通道(chn)
│  ├─ 通道1(给VENC):
│  │  ├─ 定义VPSS_CHN_ATTR_S结构体:输出分辨率(3840×2160)、缩放模式(SCALE_MODE_HARDWARE)
│  │  ├─ 调用HI_MPI_VPSS_CreateChn(0, 0, &stChnAttr):在grp 0下创建chn 0
│  │  └─ 调用HI_MPI_VPSS_SetChnAttr(0, 0, &stChnAttr):确认配置
│  ├─ 通道2(给VO):
│  │  ├─ 配置输出分辨率(1920×1080)、裁剪区域(0,0,1920,1080)
│  │  ├─ 调用HI_MPI_VPSS_CreateChn(0, 1, &stChnAttr2):创建chn 1
│  │  └─ 调用HI_MPI_VPSS_SetChnOsdAttr(0, 1, &stOsdAttr):叠加时间OSD
│  └─ 注意:每个chn会申请独立的“输出缓存块”,需确保VB池空闲块数≥chn数量
├─ 步骤4:帧收发(核心数据流转)
│  ├─ 接收ISP帧:从ISP获取YUV VB块句柄(stFrameInfo.u64VirAddr),调用HI_MPI_VPSS_SendFrame(0, &stFrameInfo)
│  ├─ 分发处理:grp 0自动处理帧(降噪+缩放),将结果写入chn 0和chn 1的输出缓存块
│  ├─ 下游取帧:
│  │  ├─ VENC调用HI_MPI_VPSS_RecvFrame(0, 0, &stOutFrame, 1000):从chn 0取4K帧
│  │  ├─ VO调用HI_MPI_VPSS_RecvFrame(0, 1, &stOutFrame2, 1000):从chn 1取1080p帧
│  │  └─ 释放帧:VENC/VO使用后调用HI_MPI_VPSS_ReleaseFrame(0, chnId, &stOutFrame),将VB块归还池
│  └─ 循环执行:直到停止指令(如用户按Ctrl+C)
└─ 步骤5:资源销毁(逆序)
   ├─ 停止chn:调用HI_MPI_VPSS_StopChn(0, 0)、HI_MPI_VPSS_StopChn(0, 1)
   ├─ 销毁chn:调用HI_MPI_VPSS_DestroyChn(0, 0)、HI_MPI_VPSS_DestroyChn(0, 1)
   ├─ 停止并销毁grp:调用HI_MPI_VPSS_StopGrp(0)、HI_MPI_VPSS_DestroyGrp(0)
   └─ 销毁VPSS模块:调用HI_MPI_VPSS_DeInit()

四、VENC部分

MPP 手册中 VENC 模块解读(Hi3403 适配版)​

VENC 是咱们视频全流程的 “编码出口”(将 VPSS 输出的 YUV 帧转为 H.265/H.264 码流),MPP 手册对 VENC 的定义是 “硬件视频编码子系统”,Hi3403 的 VENC 支持 4K120fps H.265 编码(最大码率 160Mbps),下面结合手册拆解核心内容:​

1. VENC 核心功能与参数

MPP手册VENC模块核心解读(Hi3403版)
├─ 1. 编码格式与硬件能力
│  ├─ 支持格式:H.265(HEVC)、H.264(AVC)、MJPEG(仅用于低码率场景)
│  ├─ 分辨率范围:16x16 ~ 8192x4320(8K),支持非对齐分辨率(需满足16x16整数倍)
│  ├─ 帧率上限:4K@120fps、1080p@240fps(Hi3403硬件编码引擎能力)
│  └─ 码率范围:16kbps ~ 160Mbps(支持CBR/VBR/QPMAP三种码率控制)
├─ 2. 关键编码参数(影响画质与码率)
│  ├─ 码率控制模式(VENC_RC_MODE_E):
│  │  ├─ VENC_RC_MODE_CBR:恒定码率(监控场景首选,避免码率波动导致网络卡顿)
│  │  ├─ VENC_RC_MODE_VBR:可变码率(动态场景首选,画面复杂时提码率,简单时降码率)
│  │  └─ VENC_RC_MODE_QPMAP:自定义QP值(精细调控画质,如暗部设高QP减少码率)
│  ├─ I帧配置:
│  │  ├─ I帧间隔(u32Gop):默认30帧(1秒1个I帧),监控场景可设60~120帧(减少码率)
│  │  └─ I帧QP偏移(s32IFrameQpDelta):通常比P帧低2~5(保证I帧清晰度)
│  └─ 其他参数:
│     ├─ 最大码率(u32MaxBitrate):CBR模式下等于目标码率,VBR模式下设上限(如4K设160Mbps)
│     └─ 量化参数范围(u32MinQp/u32MaxQp):H.265默认0~51,建议设10~40(平衡画质与码率)
├─ 3. 与其他模块的协同(咱们熟悉的流程)
│  ├─ 输入:从VPSS chn接收YUV帧(需YUV420/YUV422格式,通过HI_MPI_VENC_SendFrame()传入)
│  ├─ 内存:码流输出需从VB池申请“码流缓存块”(大小=单帧最大码率/帧率,如4K@30fps CBR 16Mbps:单帧约66KB)
│  └─ 输出:码流可通过HI_MPI_VENC_GetStream()获取,传给RTSP服务器(如live555)或本地存储
└─ 4. 常见错误码与解决(咱们实战避坑)
   ├─ HI_ERR_VENC_CHN_NOT_EXIST:编码通道未创建,需先调用HI_MPI_VENC_CreateChn()
   ├─ HI_ERR_VENC_NO_FREE_STREAM_BUF:码流缓存块不足,需增大VB池码流块数量(如从100块增至200块)
   └─ HI_ERR_VENC_FRAME_FORMAT_ERR:输入帧格式错误,需确保是YUV420(VPSS输出默认格式)

VENC 模块源码解读(以 sample_venc 为例)​

咱们之前在 “视频全流程” 中提到过 sample_venc 是编码的核心示例,下面拆解其源码结构(基于 Hi3403 SDK),重点看 “初始化→编码→码流获取” 的关键逻辑:​

1. 源码结构

sample_venc源码结构(关键文件与函数)
├─ 1. 核心文件
│  ├─ sample_venc.c:主函数与流程控制(咱们重点看)
│  ├─ sample_venc.h:宏定义与结构体声明(如编码通道属性、码流信息)
│  └─ common/sample_common.c:通用函数(如VB池创建、模块初始化,复用咱们之前讲的逻辑)
├─ 2. 关键函数拆解(按流程)
│  ├─ main():入口函数,调用初始化→编码→销毁
│  │  ├─ Step 1:调用SAMPLE_COMM_VENC_SysInit():初始化MPP系统+VB池(创建YUV和码流缓存块)
│  │  ├─ Step 2:调用SAMPLE_VENC_Start():创建编码通道+启动编码
│  │  ├─ Step 3:调用SAMPLE_VENC_GetStream():循环获取码流并存储为文件(如test.h265)
│  │  └─ Step 4:调用SAMPLE_VENC_Stop():停止编码+销毁资源
│  ├─ SAMPLE_COMM_VENC_CreateChn():创建编码通道(核心函数)
│  │  ├─ 配置VENC_CHN_ATTR_S结构体:
│  │  │  ├─ enCodecType:VENC_CODEC_TYPE_H265(指定H.265编码)
│  │  │  ├─ stRcAttr.enRcMode:VENC_RC_MODE_CBR(恒定码率)
│  │  │  ├─ stRcAttr.u32Bitrate:16000(16Mbps,4K编码推荐值)
│  │  │  └─ stPicSize:u32Width=3840, u32Height=2160(4K分辨率)
│  │  ├─ 调用HI_MPI_VENC_CreateChn(chnId, &stVencAttr):创建编码通道(chnId=0)
│  │  └─ 调用HI_MPI_VENC_StartChn(chnId):启动编码通道
│  ├─ SAMPLE_VENC_SendFrame():向VENC发送VPSS输出的YUV帧
│  │  ├─ 从VPSS chn 0接收帧:调用HI_MPI_VPSS_RecvFrame(0, 0, &stFrame, 1000)
│  │  ├─ 发送帧给VENC:调用HI_MPI_VENC_SendFrame(0, &stFrame, 1000)
│  │  └─ 释放VPSS帧:调用HI_MPI_VPSS_ReleaseFrame(0, 0, &stFrame)(归还VB池)
│  └─ SAMPLE_VENC_GetStream():获取编码后的码流
│     ├─ 调用HI_MPI_VENC_GetStream(0, &stStream, 1000):从VENC通道0获取码流
│     ├─ 判断码流类型:stStream.pstPack[0].enType(I帧/P帧/B帧)
│     ├─ 写入文件:fwrite(stStream.pstPack[0].pBuf, 1, stStream.pstPack[0].u32Len, fp)
│     └─ 释放码流缓存:调用HI_MPI_VENC_ReleaseStream(0, &stStream)(归还码流VB块)
└─ 3. 咱们实战修改点(快速适配需求)
   ├─ 改分辨率:修改stPicSize的u32Width/u32Height(如1920×1080)
   ├─ 改码率:调整stRcAttr.u32Bitrate(如1080p设4Mbps)
   ├─ 改编码格式:将enCodecType改为VENC_CODEC_TYPE_H264
   └─ 改I帧间隔:配置stVencAttr.stRefFrmCtrl.u32Gop(如设60,2秒1个I帧)

四、图像编码压缩基本原理:从冗余消除到效率优化​

咱们之前在讲 YUV 格式时提到 “YUV420 降采样减少 50% 数据量”,这其实就是编码压缩的基础 —— 编码的核心是 “消除冗余信息”,同时保证人眼几乎感知不到画质损失(有损压缩),常见冗余分为三类:​

1. 三大冗余类型

图像编码的三大冗余类型
├─ 1. 空间冗余:同一帧内相邻像素的颜色/亮度相似(占比最大,约60%)
│  ├─ 实例:蓝色天空区域,相邻像素的Y(亮度)和U/V(色度)值几乎相同
│  ├─ 消除方式:
│  │  ├─ 帧内预测(I帧):用相邻像素预测当前像素值,只存储“预测误差”(如H.265的35种预测模式)
│  │  └─ 离散余弦变换(DCT):将空间域数据转为频率域,丢弃高频细节(人眼不敏感)
│  └─ 关联咱们之前的内容:YUV420降采样就是对色度分量的空间冗余优化
├─ 2. 时间冗余:连续帧之间的画面变化小(如监控场景,约30%)
│  ├─ 实例:静态监控画面,只有人物移动,背景帧几乎不变
│  ├─ 消除方式:
│  │  ├─ 帧间预测(P/B帧):P帧参考前一帧,B帧参考前后帧,只存储“运动矢量”和“残差”
│  │  └─ 运动估计/补偿:找到相邻帧的相似区域,避免重复存储(如H.265的AMVP自适应运动矢量预测)
│  └─ 咱们实战注意:监控场景用P帧间隔10~15帧,可平衡码率和流畅度
└─ 3. 视觉冗余:人眼对某些信息不敏感(如亮度敏感、色度不敏感,约10%)
   ├─ 实例:人眼对暗部细节的分辨能力弱于亮部,对高频噪声不敏感
   ├─ 消除方式:
   │  ├─ 量化:对DCT系数按视觉敏感度调整量化步长(亮部细量化,暗部粗量化)
   │  └─ 色度降采样:YUV420/YUV422,减少色度数据但不影响视觉体验
   └─ 关联Hi3403:VENC模块支持“视觉感知量化”,可自定义暗部量化参数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值