上一篇已经说了,首先要做的是初始化硬件模块,总共是以下4个:
一、VI模块
VI视频模块是所有摄像头数据的入口。VI模块的配置在源文件rkmedia_module_function.cpp里面。
1.1 VI模块的思维导图:
上面思维导图主要是描述VI模块设置的大致流程,首先对RV1126_VI_CONFIG结构体进行参数设置,然后调用rkmedia_vi_init对VI模块进行设置和使能,设置完成后再把VI的模块ID放到VI数组里面(vi_containers)。
1.2 VI模块代码
之前我们学习rv1126时,使用的是单纯的 VI_CHN_ATTR_S ,但它只能控制硬件,缺少业务标识; 我们自定义 RV1126_VI_CONFIG 把通道编号 + 全套硬件参数打包在一起,实现接口简化、全局统一管理、代码解耦、方便多路摄像头扩展,是嵌入式 RKMedia 项目标准的工程化封装写法。
1.3 VI 配置结构体
在rkmedia_config_public.h中配置这个结构体
typedef struct
{
unsigned int id; // 这个VI通道的编号
VI_CHN_ATTR_S attr; // VI通道所有硬件参数(分辨率、格式、位宽等)
}RV1126_VI_CONFIG; // RV1126官方VI完整配置宏
id:给这路摄像头采集通道贴个身份证号,多路画面的时候靠 id 区分谁是谁VI_CHN_ATTR_S attr:RV 官方封装的摄像头画面全套参数 画面分辨率、图像格式、采集帧率、位深、镜像翻转全存在这里RV1126_VI_CONFIG:一键套用 RV1126 VI 硬件固定配置,不用一个个手写底层寄存器
1.4 VI 初始化主流程
在rkmedia_module_function.cpp里初始化
// 整套RKMedia硬件模块统一初始化入口函数
int init_rkmedia_module_function()
{
// 初始化RKMedia底层基础环境、全局资源、SDK基础接口
rkmedia_function_init();
// 定义VI采集通道配置结构体,存储摄像头采集全套参数
RV1126_VI_CONFIG rkmedia_vi_config;
// 将VI配置结构体内存全部清零,避免脏数据导致初始化异常
memset(&rkmedia_vi_config, 0, sizeof(rkmedia_vi_config));
// 设置当前VI采集通道编号为0
rkmedia_vi_config.id = 0;
// 指定CMOS摄像头对应的设备节点路径
rkmedia_vi_config.attr.pcVideoNode = CMOS_DEVICE_NAME; // VIDEO视频节点路径,
// 设置VI硬件缓冲区数量为3帧,缓存画面防止丢帧卡顿
rkmedia_vi_config.attr.u32BufCnt = 3; // VI捕获视频缓冲区计数,默认是3
// 设置摄像头原始采集画面宽度1920,1080P高清
rkmedia_vi_config.attr.u32Width = 1920; // 视频输入的宽度,一般和CMOS摄像头或者外设的宽度一致
// 设置摄像头原始采集画面高度1080,1080P高清
rkmedia_vi_config.attr.u32Height = 1080; // 视频输入的高度,一般和CMOS摄像头或者外设的高度一致
// 设置图像像素格式为NV12,RV1126硬件原生支持格式
rkmedia_vi_config.attr.enPixFmt = IMAGE_TYPE_NV12; // 视频输入的图像格式,默认是NV12(IMAGE_TYPE_NV12)
// 设置缓冲区共享模式为MMAP内存映射,减少CPU数据拷贝、降低延迟
rkmedia_vi_config.attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // VI捕捉视频的类型
// VI工作模式配置为常规实时采集模式,持续输出视频帧
rkmedia_vi_config.attr.enWorkMode = VI_WORK_MODE_NORMAL; // VI的工作模式,默认是NORMAL(VI_WORK_MODE_NORMAL)
// 调用VI初始化函数,传入完整VI配置,启动摄像头采集硬件
int ret = rkmedia_vi_init(&rkmedia_vi_config); // 初始化VI工作
// 判断VI初始化返回值,非0代表初始化失败
if (ret != 0)
{
// 打印VI模块初始化失败日志
printf("vi init error\n");
}
else
{
// 打印VI模块初始化成功日志
printf("vi init success\n");
// 定义VI全局容器结构体,用于存储当前VI通道信息供全项目线程调用
RV1126_VI_CONTAINTER vi_container;
// 容器自身索引编号,标记为0号VI容器
vi_container.id = 0;
// 绑定容器对应的VI硬件通道ID,和上方配置的通道0保持一致
vi_container.vi_id = rkmedia_vi_config.id;
// 将当前VI通道信息存入全局容器数组,多线程可统一读取VI资源
set_vi_container(0, &vi_container); // 设置VI容器
}
这段代码是负责对VI模块进行配置,最后4行代码是把这路 VI 配置存入全局容器数组 相当于把配置好的摄像头通道,放进全局仓库统一管理,后续编码、缩放线程随时都能调用,因为它们不能直接共用 VI 局部变量rkmedia_vi_config(局部变量生命周期只在初始化函数内,函数执行完就销毁),VI_CONTAINTER的作用是方便VI的ID号能在多个cpp或c文件上面使用,增加它的拓展性。
1.5 VI 通道使能 & 启动硬件
在rkmedia_module.cpp中定义rkmedia_vi_init函数
// VI模块底层初始化函数,根据传入的配置完成硬件参数设置并启动VI通道
int rkmedia_vi_init(RV1126_VI_CONFIG *rv1126_vi_config)
{
// 定义返回值变量,用于接收接口执行状态
int ret;
// 从自定义配置结构体中,取出RK原厂标准VI通道参数
VI_CHN_ATTR_S vi_attr = rv1126_vi_config->attr;
// 从自定义配置结构体中,取出VI硬件通道编号
unsigned int id = rv1126_vi_config->id;
//vi_attr.pcVideoNode = CMOS_DEVICE_NAME;//
// 初始化VI模块:将分辨率、格式、缓存等参数配置到硬件通道
ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, id, &vi_attr);
// 启用VI硬件通道,摄像头开始采集画面;用位或叠加执行结果
ret |= RK_MPI_VI_EnableChn(CAMERA_ID, id);
// 判断配置/启用是否失败,只要有一步出错就进入异常分支
if (ret != 0)
{
printf("create vi failed.....\n", ret);
return -1;
}
// 全部执行成功,返回0
return 0;
}
这个函数是真正驱动硬件的函数,把外面传进来的配置、通道号,拿到函数内部使用,给 VI 摄像头通道,设置我们刚才填好的所有画面参数 分辨率、格式、缓存、模式一次性写入硬件寄存器,打开 VI 硬件通道! 执行完这句,摄像头就开始实时源源不断输出画面数据了 失败就打印报错,正常就返回 0 初始化成功
1.6 全局 VI 容器存储函数
在rkmedia_containter.h中定义数组存储结构体
全局总容器
// 全局总容器:统一管理板卡所有音视频通道
typedef struct
{
unsigned int container_id;
// 视频采集通道数组
RV1126_VI_CONTAINTER vi_containers[ALL_CONTAINER_NUM];
// 音频采集通道数组
RV1126_AI_CONTAINTER ai_containers[ALL_CONTAINER_NUM];
// 视频编码通道数组
RV1126_VENC_CONTAINER venc_containers[ALL_CONTAINER_NUM];
// 音频编码通道数组
RV1126_AENC_CONTAINER aenc_containers[ALL_CONTAINER_NUM];
}RV1126_ALL_CONTAINER;
整个芯片所有模块的全局配置大仓库
结构体里面包含了四个模块的数组存储分别是VI模块(vi_contaianers)、AI模块(ai_containers)、VENC模块(venc_containers)、AENC模块(aenc_containers)。这四个模块容器就是分别存储,四个模块的ID号,让其能够更加方便的管理起来。
VI通道容器
// VI通道容器:记录单路VI的标识信息
typedef struct
{
unsigned int id; // 容器数组下标编号
unsigned int vi_id; // 对应的硬件VI通道号
}RV1126_VI_CONTAINTER;
单路 VI 的简易信息卡片,只存数组下标、硬件通道号。
在rkmedia_containter.cpp中定义容器相关函数
容器初始化函数
int init_all_container_function()
{
// 初始化线程互斥锁,保护全局容器(多线程读写防错乱)
pthread_mutex_init(&all_containers_mutex, NULL);
// 把整个全局容器内存清零,清空旧数据
memset(&all_containers, 0, sizeof(all_containers));
return 0;
}
程序启动时初始化全局仓库 + 线程锁,是所有通道信息管理的前置准备。
容器写入函数
int set_vi_container(unsigned int index, RV1126_VI_CONTAINTER *vi_container)
{
// 加锁:禁止其他线程同时修改
pthread_mutex_lock(&all_containers_mutex);
// 将VI信息卡片存入全局仓库指定位置
all_containers.vi_containers[index] = *vi_container;
// 解锁:允许其他线程正常访问
pthread_mutex_unlock(&all_containers_mutex);
return 0;
}
pthread_mutex_lock:加线程锁 项目是多线程,采集、编码、缩放多个线程同时读写这个数组,不加锁会数据错乱、程序崩溃- 把我们刚才配置好的 VI 通道,存入全局数组对应下标位置
- 解锁,其他线程可以正常读取使用
二、高分辨率VENC模块

在这个项目中VENC编码模块参数的设置是至关重要的,它可以对VI数据进行硬件编码让其可以进行高分辨率编码码流的推流(如上图)。高分辨率VENC模块的配置在源文件rkmedia_module_function.cpp里面。
2.1 高分辨率VENC模块的思维导图

上面思维导图主要是描述VENC模块设置的大致流程,首先对RV1126_VENC_CONFIG结构体进行参数设置,然后调用rkmedia_venc_init对VENC模块进行设置,设置完成后再把VENC的模块ID放到VENC管理数组里面(venc_containers)。
2.2 高分辨率VENC 配置结构体
在rkmedia_config_public.h中配置这个结构体
typedef struct
{
unsigned int id;
VENC_CHN_ATTR_S attr;
} RV1126_VENC_CONFIG;
id:VENC 编码通道编号,多路编码场景下用来区分不同硬件编码通道,本项目分别区分 1080P、720P 两路编码器。VENC_CHN_ATTR_S attr:瑞芯微官方封装的编码器全套参数,包含编码协议、图像格式、分辨率、编码等级、码率、帧率、关键帧间隔等所有编码相关配置。RV1126_VENC_CONFIG:自定义 VENC 配置结构体,整合通道编号与底层编码参数,统一管理编码通道配置,无需直接操作底层寄存器。
2.3 高分辨率VENC 初始化主流程
在rkmedia_module_function.cpp里接着VI模块的下面进行配置初始化
RV1126_VENC_CONFIG rkmedia_venc_config = {0};
// 将VENC配置结构体内存全部清零,避免脏数据导致初始化异常
memset(&rkmedia_venc_config, 0, sizeof(rkmedia_venc_config));
// 设置当前VENC编码通道编号为0
rkmedia_venc_config.id = 0;
// 指定编码器协议类型为H264
rkmedia_venc_config.attr.stVencAttr.enType = RK_CODEC_TYPE_H264;
// 设置输入图像格式为NV12,和VI采集格式保持一致
rkmedia_venc_config.attr.stVencAttr.imageType = IMAGE_TYPE_NV12;
// 设置编码图像实际宽度为1920,对应1080P高清画面
rkmedia_venc_config.attr.stVencAttr.u32PicWidth = 1920;
// 设置编码图像实际高度为1080,对应1080P高清画面
rkmedia_venc_config.attr.stVencAttr.u32PicHeight = 1080;
// 设置编码图像虚拟宽度,与实际宽度保持一致
rkmedia_venc_config.attr.stVencAttr.u32VirWidth = 1920;
// 设置编码图像虚拟高度,与实际高度保持一致
rkmedia_venc_config.attr.stVencAttr.u32VirHeight = 1080;
// 设置H.264编码等级为Baseline,画质适中、网络传输兼容性好
rkmedia_venc_config.attr.stVencAttr.u32Profile = 66;
// 选择H264固定码率模式,适合监控场景稳定推流
rkmedia_venc_config.attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
// 设置关键帧间隔为25帧
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32Gop = 25;
// 配置编码码率,根据分辨率设置对应码率大小
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32BitRate = 1920 * 1080 * 3;
// 目标帧率分子,固定填1
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
// 目标帧率分母,设置输出帧率25帧
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;
// 原始帧率分子,固定填1
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;
// 原始帧率分母,设置输入帧率25帧
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;
// 调用VENC初始化函数,启动硬件编码器
ret = rkmedia_venc_init(&rkmedia_venc_config);
// 判断VENC初始化返回值,非0代表初始化失败
if (ret != 0)
{
// 打印VENC模块初始化失败日志
printf("venc init error\n");
}
else
{
// 定义VENC全局容器结构体,用于存储当前编码通道信息供全项目线程调用
RV1126_VENC_CONTAINER venc_container;
// 容器自身索引编号,标记为0号VENC容器
venc_container.id = 0;
// 绑定容器对应的VENC硬件通道ID,和上方配置的通道0保持一致
venc_container.venc_id = rkmedia_venc_config.id;
// 将当前VENC通道信息存入全局容器数组,多线程可统一读取VENC资源
set_venc_container(0, &venc_container);
// 打印VENC模块初始化成功日志
printf("venc init success\n");
}
这段代码负责对 VENC 编码模块进行参数配置与初始化。最后几行代码是把当前 VENC 通道信息存入全局容器数组,相当于把配置好的硬件编码器放进全局仓库统一管理。后续推流线程、数据处理线程可以随时调用该通道信息,因为rkmedia_venc_config是局部变量,函数执行完毕后就会销毁,无法跨函数、跨文件使用。VENC_CONTAINER专门用来传递 VENC 通道编号,方便多文件、多线程共享资源,提升项目拓展性。
2.4 高分辨率VENC 通道创建 & 启动硬件
在rkmedia_module.cpp中定义rkmedia_venc_init函数
// VENC模块底层初始化函数,根据传入的配置创建并启动硬件编码通道
int rkmedia_venc_init(RV1126_VENC_CONFIG *rv1126_venc_config)
{
// 定义返回值变量,用于接收接口执行状态
int ret;
// 从自定义配置结构体中,取出RK原厂标准VENC通道参数
VENC_CHN_ATTR_S venc_chn_attr = rv1126_venc_config->attr;
// 从自定义配置结构体中,取出VENC硬件通道编号
unsigned int venc_id = rv1126_venc_config->id;
// 创建VENC编码通道,将编码参数写入硬件寄存器
ret = RK_MPI_VENC_CreateChn(rv1126_venc_config->id, &venc_chn_attr);
// 判断通道创建结果,非0代表创建失败
if (ret != 0)
{
printf("create rv1126_venc_module failed\n");
return -1;
}
else
{
printf("create rv1126_venc_module success\n");
}
return 0;
}
这个函数是真正驱动 VENC 硬件的函数,接收上层传入的配置参数与通道号,在函数内部完成参数解析。调用瑞芯微底层 SDK 接口RK_MPI_VENC_CreateChn,把编码格式、分辨率、码率、帧率等参数一次性写入硬件寄存器,完成编码通道的创建。执行成功后,硬件编码器就可以接收 VI/RGA 传来的原始画面并自动编码;创建失败则打印报错信息并返回 - 1,执行成功返回 0。
2.5 全局 高分辨率VENC 容器存储函数
在rkmedia_containter.h中已经定义全局总容器,此处单独说明 VENC 通道容器
VENC 通道容器
// VENC通道容器:记录单路VENC编码通道标识信息
typedef struct
{
unsigned int id; // 容器数组下标编号
unsigned int venc_id;// 对应的硬件VENC通道号
}RV1126_VENC_CONTAINER;
这是单路 高分辨率VENC 的简易信息卡片,仅存储数组下标和硬件编码通道号,用于全局共享通道标识。
容器写入函数
在rkmedia_containter.cpp中定义 高分辨率VENC 容器写入函数
int set_venc_container(unsigned int index, RV1126_VENC_CONTAINER *venc_container)
{
// 加锁:禁止其他线程同时修改全局容器,保证线程安全
pthread_mutex_lock(&all_containers_mutex);
// 将VENC信息卡片存入全局仓库对应下标位置
all_containers.venc_containers[index] = *venc_container;
// 解锁:允许其他线程正常读取、访问容器数据
pthread_mutex_unlock(&all_containers_mutex);
return 0;
}
pthread_mutex_lock为线程互斥锁,本项目是多线程架构,采集、编码、推流等线程会同时读写全局容器数组,加锁可以避免数据错乱、程序崩溃。该函数会把初始化完成的 VENC 通道信息,存入全局容器指定位置,操作完成后解锁,保证多线程正常使用。
三、RGA模块
RGA模块是视频处理模块,这个模块可以对VI视频数据进行缩放、裁剪、格式转换、图片叠加等的功能,在这个项目里面RGA模块最重要的功能是把1920 * 1080的分辨率转换成1280 * 720的分辨率。RGA模块的配置在源文件rkmedia_module_function.cpp里面。

3.1 RGA模块设置的思维导图

上面思维导图是RGA设置的大体流程,RGA设置需要对RGA_ATTR_S结构体进行参数设置,设置完之后调用RK_MPI_RGA_CreateChn设置RGA模块。若成功则打印RGA Set Success,失败则打印RGA Set Failed。
3.2 RGA 模块初始化
RGA 是 RV1126 内置的图像缩放、格式转换硬件模块,本项目主要用于将 1080P 原始画面缩放为 720P 画面。在rkmedia_module_function.cpp里接着高分辨率VENC模块的下面进行配置初始化
// RGA模块参数结构体,存放图像输入、输出、旋转、缓存等全部配置
RGA_ATTR_S rga_info;
/**Image Input ..............*/
// 设置RGA输入图像宽度,对接VI输出的1920*1080原始画面
rga_info.stImgIn.u32Width = 1920; // 设置RGA输入分辨率宽度
// 设置RGA输入图像高度,对接VI输出的1920*1080原始画面
rga_info.stImgIn.u32Height = 1080; // 设置RGA输入分辨率高度
// 设置输入图像水平步长(虚拟宽度),此处与实际宽度保持一致
rga_info.stImgIn.u32HorStride = 1920; // 设置RGA输入分辨率虚宽
// 设置输入图像垂直步长(虚拟高度),此处与实际高度保持一致
rga_info.stImgIn.u32VirStride = 1080; // 设置RGA输入分辨率虚高
// 输入图像格式配置为NV12,和VI采集、VENC编码格式统一,保证硬件兼容
rga_info.stImgIn.imgType = IMAGE_TYPE_NV12; // 设置ImageType图像类型
// 输入图像截取起始X坐标,0代表从画面最左侧开始
rga_info.stImgIn.u32X = 0; // 设置X坐标
// 输入图像截取起始Y坐标,0代表从画面最上方开始
rga_info.stImgIn.u32Y = 0; // 设置Y坐标
/**Image Output......................*/
// 设置RGA输出图像宽度,缩放为目标分辨率1280
rga_info.stImgOut.u32Width = 1280; // 设置RGA输出分辨率宽度
// 设置RGA输出图像高度,缩放为目标分辨率720
rga_info.stImgOut.u32Height = 720; // 设置RGA输出分辨率高度
// 设置输出图像水平步长,与输出实际宽度保持一致
rga_info.stImgOut.u32HorStride = 1280; // 设置RGA输出分辨率虚宽
// 设置输出图像垂直步长,与输出实际高度保持一致
rga_info.stImgOut.u32VirStride = 720; // 设置RGA输出分辨率虚高
// 输出图像格式依旧为NV12,适配后端低分辨率VENC编码器
rga_info.stImgOut.imgType = IMAGE_TYPE_NV12; // 设置输出ImageType图像类型
// 输出图像摆放起始X坐标,0代表全屏输出不偏移
rga_info.stImgOut.u32X = 0; // 设置X坐标
// 输出图像摆放起始Y坐标,0代表全屏输出不偏移
rga_info.stImgOut.u32Y = 0; // 设置Y坐标
// RGA Public Parameter
// 设置RGA硬件缓冲池数量为3帧,缓存图像数据,防止缩放过程中丢帧、卡顿
rga_info.u16BufPoolCnt = 3; // 缓冲池计数
// 图像旋转角度,0代表不进行旋转操作
rga_info.u16Rotaion = 0; //
// 设置图像水平翻转,根据摄像头安装角度做画面矫正
rga_info.enFlip = RGA_FLIP_H;
// 开启RGA缓冲池功能,保障数据稳定流转
rga_info.bEnBufPool = RK_TRUE;
// 调用RK底层SDK接口,创建编号为0的RGA硬件通道,并载入全部配置参数
ret = RK_MPI_RGA_CreateChn(0, &rga_info);
// 判断RGA通道创建结果,非0表示初始化失败
if (ret)
{
printf("RGA Set Failed.....\n");
}
// 初始化成功,RGA模块正式就绪,可执行画面缩放任务
else
{
printf("RGA Set Success.....\n");
}
本段代码直接使用瑞芯微原厂 RGA_ATTR_S 结构体完成全部参数配置,无需自定义结构体与容器。核心作用是定义输入输出分辨率、图像格式、画面翻转、缓存等参数,最终创建 RGA 硬件通道。初始化完成后,RGA 即可接收 1080P 原始视频帧,自动缩放为 720P 画面,供给后端低分辨率 VENC 编码器使用。
四、低分辨率VENC模块

低分辨率VENC的设置和高分辨率的设置方法基本上是一致的,唯一的区别在于分辨率要写成1280 * 720。获取低分辨率编码数据的流程如上图 ,分别是VI模块获取视频数据->RGA模块处理->获取1280*720的原始数据->送到低分辨率编码器处理->获取1280 * 720的编码(h264/h265)压缩数据。低分辨率VENC模块的配置在源文件rkmedia_module_function.cpp里面。
4.1 低分辨率VENC的思维导图

上面思维导图主要是描述低分辨率VENC模块设置的大致流程,它的流程基本上和高分辨率是一致的。首先对RV1126_VENC_CONFIG结构体进行参数设置,然后调用rkmedia_venc_init对VENC模块进行设置,设置完成后再把VENC的模块ID放到VENC管理数组里面(venc_containers)。
4.2 低分辨率VENC模块初始化
前面完成了 1080P 高分辨率 VENC 编码器的初始化,本项目需要同时输出两路不同清晰度码流,因此还需要单独初始化720P 低分辨率 VENC 编码通道。该部分复用之前定义的 VENC 配置结构体、初始化函数与容器管理逻辑,仅修改通道编号、分辨率、码率等参数
// 定义低分辨率VENC编码通道配置结构体,并初始化为0
RV1126_VENC_CONFIG low_rkmedia_venc_config = {0};
// 将配置结构体内存完整清零,清除随机脏数据,避免初始化异常
memset(&low_rkmedia_venc_config, 0, sizeof(low_rkmedia_venc_config));
// 设置低分辨率VENC硬件通道编号为1,与0号高清编码器做区分
low_rkmedia_venc_config.id = 1;
// 指定编码器编码协议为H264,和高清编码格式保持统一
low_rkmedia_venc_config.attr.stVencAttr.enType = RK_CODEC_TYPE_H264; // 编码器协议类型
// 设置输入图像格式为NV12,匹配RGA缩放后的图像格式,保证硬件兼容
low_rkmedia_venc_config.attr.stVencAttr.imageType = IMAGE_TYPE_NV12; // 输入图像类型
// 设置编码图像实际宽度为1280,对应720P标清分辨率
low_rkmedia_venc_config.attr.stVencAttr.u32PicWidth = 1280; // 编码图像宽度
// 设置编码图像实际高度为720,对应720P标清分辨率
low_rkmedia_venc_config.attr.stVencAttr.u32PicHeight = 720; // 编码图像高度
// 设置编码图像虚拟宽度,业务场景下与实际宽度保持一致即可
low_rkmedia_venc_config.attr.stVencAttr.u32VirWidth = 1280; // 编码图像虚宽度,一般来说u32VirWidth和u32PicWidth是一致的
// 设置编码图像虚拟高度,业务场景下与实际高度保持一致即可
low_rkmedia_venc_config.attr.stVencAttr.u32VirHeight = 720; // 编码图像虚高度,一般来说u32VirHeight和u32PicHeight是一致的
// H264编码等级选择Baseline(66),兼容性强、码率低,适合网络监控推流场景
low_rkmedia_venc_config.attr.stVencAttr.u32Profile = 66; // 编码等级H.264: 66: Baseline; 77:Main Profile; 100:High Profile; H.265: default:Main; Jpege/MJpege: default:Baseline(编码等级的作用主要是改变画面质量,66的画面质量最差利于网络传输,100的质量最好)
// 码率控制模式选择CBR固定码率模式,保证监控码流传输稳定
low_rkmedia_venc_config.attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; // 编码器码率控制模式
// 设置关键帧间隔为30帧,按需调整关键帧密度,平衡画质与带宽
low_rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32Gop = 30; // GOPSIZE:关键帧间隔
// 根据720P分辨率计算并设置编码码率,适配标清画面传输需求
low_rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32BitRate = 1280 * 720 * 3; // 码率
// 输出帧率分子,项目固定配置为1
low_rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; // 目的帧率分子:填的是1固定
// 设置编码输出帧率为25帧/秒,符合常规视频采集标准
low_rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25; // 目的帧率分母:填的是25固定
// 输入帧率分子,项目固定配置为1
low_rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; // 源头帧率分子:填的是1固定
// 设置原始输入帧率为25帧/秒,与前端采集帧率保持同步
low_rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25; // 源头帧率分母:填的是25固定
// 调用VENC通用初始化函数,创建并启动1号低分辨率编码通道
ret = rkmedia_venc_init(&low_rkmedia_venc_config); // VENC模块的初始化
// 判断初始化结果,非0代表低分辨率VENC初始化失败
if (ret != 0)
{
printf("venc init error\n");
}
else
{
// 定义低分辨率VENC通道容器,用于全局共享通道信息
RV1126_VENC_CONTAINER low_venc_container;
// 设置容器数组下标编号为1
low_venc_container.id = 1;
// 绑定容器与1号VENC硬件通道,建立映射关系
low_venc_container.venc_id = low_rkmedia_venc_config.id;
// 将低分辨率VENC通道信息存入全局容器,供后续线程调用
set_venc_container(low_venc_container.id, &low_venc_container);
// 打印日志,提示低分辨率VENC初始化完成
printf("low_venc init success\n");
}
这段代码完成1 号低分辨率 VENC 编码通道的全部配置与启动。参数上适配了 RGA 输出的 1280*720 画面,同时单独设置了码率、关键帧间隔等参数。初始化成功后,会将 1 号编码通道信息存入全局容器,后续专门处理标清画面的线程,即可通过全局容器读取通道 ID,正常完成编码与推流工作。至此,项目两路 VENC 硬件编码器(1080P+720P)全部初始化完毕。
五、硬件模块初始化整体总结
以上就依次完成了 VI、高分辨率VENC、RGA、低分辨率VENC 四大核心硬件模块的配置与初始化,也是本项目视频数据流的前置基础。下面对整个硬件初始化流程、分工与运行逻辑做整体梳理,方便衔接后续多线程读取码流、处理数据与推流的内容。
-
各模块核心分工
- VI 模块:作为整个项目的数据源头,驱动 CMOS 摄像头采集原始 1920×1080 NV12 格式视频画面,通过硬件缓冲区保障画面连续输出。
- 高分辨率 VENC(通道 0):接收 VI 原始画面,按照 H264 格式对 1080P 视频进行硬件编码,用于高清码流推流。
- RGA 模块:图像缩放单元,专门将 VI 输出的 1080P 画面,缩放转换为 1280×720 标清画面,为低分辨率编码提供数据源。
- 低分辨率 VENC(通道 1):接收 RGA 缩放后的 720P 画面,同样使用 H264 格式完成硬件编码,用于标清码流推流。
-
统一的工程设计思路 针对 VI、VENC 模块,项目采用自定义配置结构体 + 全局容器的设计方式:先集中填写硬件参数完成初始化,再将通道 ID 存入全局容器。一方面实现参数统一管理、简化底层 SDK 调用;另一方面解决局部变量无法跨线程、跨文件使用的问题,依托线程互斥锁保证多线程访问安全。RGA 模块直接使用原厂结构体配置,按需完成画面缩放功能即可。
-
完整硬件数据流走向 摄像头采集(VI)→ 一路直传至高分辨率 VENC 完成编码;另一路经 RGA 缩放后,传入低分辨率 VENC 完成编码。至此,所有音视频硬件均已正常启动并进入待命状态,硬件层可以持续产出两路不同分辨率的编码码流。
硬件初始化阶段全部结束后,硬件已经可以源源不断输出编码后的视频数据。接下来我们就进入项目核心业务环节:通过多线程分别读取两路 VENC 的码流数据