RV1126+FFMPEG推流项目(4)VENC模块视频编码流程
在RV1126+FFMPEG推流项目(3)VI模块视频编码流程-CSDN博客,说了vi的编码流程,这篇说VENC的初始化(硬件编码)。
上一篇提到了几个重要的数据结构体,这节说这个RV1126_VENC_CONFIG。继续认识一下。
/**
* @brief RV1126_VENC_CONFIG 结构体定义
*
* 该结构体用于配置RV1126视频编码(VENC)的相关参数。
* 包含视频编码通道的ID和属性配置。
*/
typedef struct
{
unsigned int id; //< 视频编码通道ID,用于标识不同的视频编码通道
VENC_CHN_ATTR_S attr; //< 视频编码通道属性,包含通道的具体配置信息
} RV1126_VENC_CONFIG;
之前提到RV1126_VENC_CONFIG 是自封装的一个数据结构,里面最重要的成员就是VENC_CHN_ATTR_S 数据结构。
typedef struct rkVENC_CHN_ATTR_S {
VENC_ATTR_S stVencAttr; // 编码器基础属性结构体
VENC_RC_ATTR_S stRcAttr; // 编码码率控制属性结构体
VENC_GOP_ATTR_S stGopAttr; // 编码器GOP属性结构体
} VENC_CHN_ATTR_S;
可以看到VENC_CHN_ATTR_S数据结构里面又套着三个数据结构体。下面通过编码来学习一下。
VENC_ATTR_S(定义编码器属性结构体):来源于rk1126开发手册,看一下成员
第一个参数编码器的类型,把视频和音频的都放在了这个枚举里面,要哪个就选择那个。
编码:
//初始化Vemc编码模块
RV1126_VENC_CONFIG rv_venc;
memset(&rv_venc, 0, sizeof(rv_venc)); //先把空间初始化
rv_venc.id = 0;
//视频编码器属性
rv_venc.attr. stVencAttr.enType = RK_CODEC_TYPE_H264; // 编码协议 设置成h264,需要265就写成265的
rv_venc.attr.stVencAttr.imageType = IMAGE_TYPE_NV12; // 图形格式 和rv_vi.attr.enPixFmt保持一样
rv_venc.attr.stVencAttr.u32VirHeight = 1080; //图片高度和分辨率 vi的u32Height一样
rv_venc.attr.stVencAttr.u32VirWidth = 1920; //图片高度和分辨率 vi的u32Width一样
rv_venc.attr.stVencAttr.u32PicHeight = 1080;//图片高度和分辨率 vi的u32Height一样
rv_venc.attr.stVencAttr.u32PicWidth = 1920;图片高度和分辨率 vi的u32Width一样
rv_venc.attr.stVencAttr.u32Profile = 77; // //后面调节画面质量更详细所说,传输的编码质量,66基本能看,77还不错,100最好,更具业务而定
VENC_RC_ATTR_S(定义编码通道码率控制器属性):
这里有一个非常重要的问题,你要是选着了264的编码器,就像选择264的码率控制器,H264CBR和 H264VBR和H264AVBR都可以,就是不能用265的,如果要用265码率控制器就要用265的编码器。
编码:
//初始化所有的rkmedia
int init_rkmedia_module_function()
{
int ret;
rkmedia_function_init(); //初始化rkmedia功能
RV1126_VI_CONFIG rv_vi;
memset(&rv_vi, 0, sizeof(rv_vi));
//配置rv_vi
rv_vi.id = 0; //视频通道ID
rv_vi.attr.pcVideoNode = COMS_DEVICE_NAME; //video视频节点路径
/*
VI捕获视频缓冲区计数,默认是3
比如说vi和摄像头直接基础没有缓存区,vi模块如果数度跟不上会造成数据的丢失
所以加上缓存区的,就可以减轻vi模块的压力,也是类似与一个生产着和消费着模型,主要是摄像头每时每刻都在采集数据不会停的
所以加上缓存去,就可以大大保存数据的完整性。至于为啥3,这是工程师的经验
*/
rv_vi.attr.u32BufCnt = 3;
rv_vi.attr.u32Width = 1920;//视频输入的宽度,一般和CMOS摄像头或者外设的宽度一致
rv_vi.attr.u32Height = 1080;视频输入的高度,一般和CMOS摄像头或者外设的高度一致
rv_vi.attr.enPixFmt = IMAGE_TYPE_NV12; // 图形格式 和vi_chn_attr.enPixFmt保持一样
/**
* VI_CHN_BUF_TYPE_MMAP和VI_CHN_BUF_TYPE_DMA
* VI_CHN_BUF_TYPE_DMA:vi模块直接和内存打交道,优点:速度快,缺点:如果硬件差的,可能会造成发热,频繁操作加快硬件损坏
* I_CHN_BUF_TYPE_MMAP:中间有一个虚拟内存,vi和内存不直接接触,优点:硬件使用时间会长,缺点:有一点点延迟,但是不影响
*/
rv_vi.attr.enBufType = VI_CHN_BUF_TYPE_MMAP; //视频输入的图像格式,默认是NV12(IMAGE_TYPE_NV12)
rv_vi.attr.enWorkMode = VI_WORK_MODE_NORMAL; //VI的工作模式,默认是NORMAL(VI_WORK_MODE_NORMAL)
ret = rkmedia_vi_init(&rv_vi); //初始化rv_vi
if(ret != 0)
{
printf("vi初始化失败\n");
}
else
{
printf("vi 初始化成功\n");
/**
* 设置容器的目的是为了后面开发,可以轻易拿到 vi的id,直接从容器里面拿
*/
RV1126_VI_CONTAINER vi_container; //创建出容器
vi_container.id = 0; //所有vi_id的索引
vi_container.vi_id = rv_vi.id; //id,储存到里面去
set_vi_container(0, & vi_container); //把vi_id设置到容器里面
}
//配置Vemc编码模块
RV1126_VENC_CONFIG rv_venc;
memset(&rv_venc, 0, sizeof(rv_venc)); //先把空间初始化
rv_venc.id = 0; //一般都是从0开始的通道号
//视频编码器属性
rv_venc.attr. stVencAttr.enType = RK_CODEC_TYPE_H264; // 编码协议 设置成h264,需要265就写成265的
rv_venc.attr.stVencAttr.imageType = IMAGE_TYPE_NV12; // 图形格式 和rv_vi.attr.enPixFmt保持一样
rv_venc.attr.stVencAttr.u32VirHeight = 1080; //图片高度和分辨率 vi的u32Height一样
rv_venc.attr.stVencAttr.u32VirWidth = 1920; //图片高度和分辨率 vi的u32Width一样
rv_venc.attr.stVencAttr.u32PicHeight = 1080;//图片高度和分辨率 vi的u32Height一样
rv_venc.attr.stVencAttr.u32PicWidth = 1920;图片高度和分辨率 vi的u32Width一样
rv_venc.attr.stVencAttr.u32Profile = 66; // //后面调节画面质量更详细所说,传输的编码质量,66基本能看,77还不错,100最好,更具业务而定
/**
* 视频码率控制属性有一个细节,选择264的编码器,就要选择264的率控制
* 不可以选择264的编码器,就要选择265的率控制
*/
rv_venc.attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; //编码器码率控制模式
rv_venc.attr.stRcAttr.stH264Cbr.u32Gop = 30;//目前是30,每30帧插入一个帧GOPSIZE:关键帧间隔,目前我随意的,根据情况,值大需要的解码时间就,值小就要更更多的带宽
rv_venc.attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;//源帧率分子
rv_venc.attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25; // 源帧率分母
rv_venc.attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 1; // 目标帧率分子
rv_venc.attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 25;// 目标帧率分母
rv_venc.attr.stRcAttr.stH264Cbr.u32BitRate = 1920 * 1080 * 3;//码率 每一个像素都是3中颜色组成
//gop属性器stGopAttr查了很多资料,可以不配置
ret = rkmedia_venc_init(&rv_venc); //初始化Vemc编码模块
if(ret != 0)
{
printf("venc,配置失败\n");
}
else
{
//成功了,
//把可以轻易拿到 enc的id,保存到容器里面,直接从容器里面拿
RV1126_VENC_CONTAINER venc_container;
venc_container.id = 0;//所有venc_id的索引
venc_container.venc_id = rv_venc.id;
set_venc_container(0, &venc_container);//设置进去
}
}