RV1126 NO.22:多线程获取SMARTP的GOP模式数据和普通GOP模式数据
前言:通过编程方式同步获取SMARTP模式和常规GOP模式的VENC码流数据,并对其画质表现进行对比分析。
一.RV1126 VI采集摄像头数据并同时编码SMARTP模式和普通GOP模式的编码码流流程
RV1126利用多线程同时获取普通GOP的VENC码流数据和SMARTP的码流数据一般如上图,分为8个步骤:分别是VI模块初始化、普通GOP的VENC模块初始化、智能SMARTP_GOP的VENC模块初始化、VI绑定普通GOP的VENC编码器节点、设置GOP属性为SMARTP属性、VI绑定SMARTP_GOP的VENC编码器节点、创建多线程获取普通GOP的VENC码流数据并保存、创建多线程获取SMART_GOP的VENC码流数据并保存。
1.1.初始化VI模块
VI模块的初始化实际上就是对VI_CHN_ATTR_S的参数进行设置、然后调用RK_MPI_VI_SetChnAttr设置VI模块并使能RK_MPI_VI_EnableChn,伪代码如下:
VI_CHN_ATTR_S vi_chn_attr;
。。。。。。。。。。。。。。。(这里是设置VI的属性)
ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, 0, &vi_chn_attr);
ret |= RK_MPI_VI_EnableChn(CAMERA_ID, 0);
1.2.两种GOP模式VENC模块初始化
VENC_CHN_ATTR_S common_gop_venc_chn_attr;
..................................
RK_MPI_VENC_CreateChn(COMMON_GOP_VENC_CHN, &common_gop_venc_chn_attr);
VENC_CHN_ATTR_S smartp_gop_venc_chn_attr;
..................................
RK_MPI_VENC_CreateChn(SMARTP_VENC_CHN, &smartp_gop_venc_chn_attr);
注意:这里需要创建两个编码器层,分别是普通GOP模式编码器和SMART的GOP模式编码器。
1.3.设置SMARTP的VENC模块GOP参数调节
VENC_GOP_ATTR_S venc_gop_attr;
venc_gop_attr.enGopMode = VENC_GOPMODE_SMARTP; //设置GOP模式为SMARTP模式
venc_gop_attr.u32GopSize = 25; //设置短参考帧间隔是25
venc_gop_attr.s32IPQpDelta = 6; //设置I帧和P帧的差值是6
venc_gop_attr.s32ViQpDelta = 6;//设置虚拟I帧和P帧的差值是6
venc_gop_attr.u32BgInterval = 25 * 5; //设置长参考帧的长度是短参考帧的5倍
ret = RK_MPI_VENC_SetGopMode(SMARTP_GOP_VENC_CHN, &venc_gop_attr);
上面是SMARTP的GOP模式一些参数的设置,设置完成调用RK_MPI_VENC_SetGopMode去调用
1.4.VI模块绑定普通GOP的VENC模块和SMARTP_GOP的VENC模块,下面是伪代码
//VI模块节点
MPP_CHN_S vi_chn_s;
vi_chn_s.enModId = RK_ID_VI;
vi_chn_s.s32ChnId = VI_CHN_ID;
//普通GOP的VENC模块节点
MPP_CHN_S common_gop_venc_chn_s;
common_gop_venc_chn_s.enModId = RK_ID_VENC;
common_gop_venc_chn_s.s32ChnId = COMMON_GOP_VENC_CHN;
ret = RK_MPI_SYS_Bind(&vi_chn_s, &common_gop_venc_chn_s);
//SMARTP_GOP的VENC模块
MPP_CHN_S smartp_gop_venc_chn_s;
smartp_gop_venc_chn_s_s.enModId = RK_ID_VENC;
smartp_gop_venc_chn_s.s32ChnId = SMARTP_GOP_VENC_CHN;
ret = RK_MPI_SYS_Bind(&vi_chn_s, &smartp_gop_venc_chn_s);
1.5.创建多线程获取普通GOP的VENC码流数据并保存:
开启一个线程去采集每一帧普通GOP的VENC模块数据,使用的API是RK_MPI_SYS_GetMediaBuffer, 模块ID是RK_ID_VENC,通道号ID是普通GOP的 VENC创建ID号。这个API伪代码如下:
while(1)
{
.........................
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, COMMON_GOP_VENC_CHN, -1);
fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, common_gop_h264_file);
.......................
}
1.6.多线程获取SMARTP_GOP的VENC模块数据:
开启一个线程去采集每一帧SMARTP_GOP的VENC模块数据,使用的API是RK_MPI_SYS_GetMediaBuffer, 模块ID是RK_ID_VENC,通道号ID是SMARTP_GOP的VENC层。这个API伪代码如下:
while(1)
{
.........................
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, SMARTP_GOP_VENC_CHN, -1);
fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, smartp_gop_h264_file);
.......................
}
好的,接下来我们进行完整的代码
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "rkmedia_api.h"#define PIPE_ID 0
#define VI_CHN_ID 0#define COMMON_GOP_VENC_CHN 0
#define SMARTP_GOP_VENC_CHN 1//获取每一帧COMMON_GOP的编码数据
void * get_common_gop_thread(void * args)
{pthread_detach(pthread_self());FILE * common_gop_h264 = fopen("test_common_gop.h264", "w+");MEDIA_BUFFER mb = NULL;while (1){mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, COMMON_GOP_VENC_CHN, -1);if(!mb){printf("Get COMMON_GOP VENC break.......\n");break;}printf("Get COMMON_GOP VENC Success.......\n");fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, common_gop_h264);RK_MPI_MB_ReleaseBuffer(mb);}return NULL;
}//获取每一帧SMARTP_GOP的编码数据
void * get_smartp_gop_thread(void * args)
{pthread_detach(pthread_self());FILE * smartp_gop_h264 = fopen("test_smartp_gop.h264", "w+");MEDIA_BUFFER mb = NULL;while (1){mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, SMARTP_GOP_VENC_CHN, -1);if(!mb){printf("Get SMARTP_GOP VENC break.......\n");break;}printf("Get SMARTP_GOP VENC Success.......\n");fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, smartp_gop_h264);RK_MPI_MB_ReleaseBuffer(mb);}return NULL;
}int main()
{int ret;RK_MPI_SYS_Init();// VI Init......VI_CHN_ATTR_S vi_chn_attr;vi_chn_attr.pcVideoNode = "rkispp_scale0"; // 设置视频设备节点路径vi_chn_attr.u32Width = 1920; // 设置分辨率的宽度vi_chn_attr.u32Height = 1080; // 设置分辨率的高度vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12; // 设置图像类型vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // 设置VI获取类型vi_chn_attr.u32BufCnt = 3; // 设置缓冲数量vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL; // 设置VI工作类型ret = RK_MPI_VI_SetChnAttr(PIPE_ID, VI_CHN_ID, &vi_chn_attr);if (ret){printf("VI_CHN_ATTR Set Failed.....\n");return -1;}else{printf("VI_CHN_ATTR Set Success.....\n");}ret |= RK_MPI_VI_EnableChn(PIPE_ID, VI_CHN_ID);if (ret){printf("VI_CHN_ATTR Enable Failed.....\n");return -1;}else{printf("VI_CHN_ATTR Enable Success.....\n");}// Common_Gop Venc ParameterVENC_CHN_ATTR_S common_gop_venc_attr;common_gop_venc_attr.stVencAttr.enType = RK_CODEC_TYPE_H264; // 设置编码器类型common_gop_venc_attr.stVencAttr.imageType = IMAGE_TYPE_NV12; // 设置编码图像类型common_gop_venc_attr.stVencAttr.u32PicWidth = 1920; // 设置编码分辨率宽度common_gop_venc_attr.stVencAttr.u32PicHeight = 1080; // 设置编码分辨率高度common_gop_venc_attr.stVencAttr.u32VirWidth = 1920; // 设置编码分辨率虚宽common_gop_venc_attr.stVencAttr.u32VirHeight = 1080; // 设置编码分辨率虚高common_gop_venc_attr.stVencAttr.u32Profile = 66; // 设置编码等级common_gop_venc_attr.stVencAttr.enRotation = VENC_ROTATION_0; // 设置编码的旋转角度//********* 设置码率控制属性 *******************//common_gop_venc_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; // 设置H264的CBR码率控制模式common_gop_venc_attr.stRcAttr.stH264Cbr.u32Gop = 25; // 设置GOP关键帧间隔common_gop_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25; // 设置源帧率分子common_gop_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; // 设置源帧率分母common_gop_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25; // 设置目标帧率分子common_gop_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; // 设置目标帧率分母common_gop_venc_attr.stRcAttr.stH264Cbr.u32BitRate = 8388608; // 设置码率大小ret = RK_MPI_VENC_CreateChn(COMMON_GOP_VENC_CHN, &common_gop_venc_attr);if (ret){printf("Set Common_Gop Venc Attr Failed.....\n");}else{printf("Set Common_Gop Venc Attr Success.....\n");}// smart_Gop Venc ParameterVENC_CHN_ATTR_S smartp_gop_venc_attr;smartp_gop_venc_attr.stVencAttr.enType = RK_CODEC_TYPE_H264; // 设置编码器类型smartp_gop_venc_attr.stVencAttr.imageType = IMAGE_TYPE_NV12; // 设置编码图像类型smartp_gop_venc_attr.stVencAttr.u32PicWidth = 1920; // 设置编码分辨率宽度smartp_gop_venc_attr.stVencAttr.u32PicHeight = 1080; // 设置编码分辨率高度smartp_gop_venc_attr.stVencAttr.u32VirWidth = 1920; // 设置编码分辨率虚宽smartp_gop_venc_attr.stVencAttr.u32VirHeight = 1080; // 设置编码分辨率虚高smartp_gop_venc_attr.stVencAttr.u32Profile = 66; // 设置编码等级smartp_gop_venc_attr.stVencAttr.enRotation = VENC_ROTATION_0; // 设置编码的旋转角度//********* 设置码率控制属性 *******************//smartp_gop_venc_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; // 设置H264的CBR码率控制模式smartp_gop_venc_attr.stRcAttr.stH264Cbr.u32Gop = 25; // 设置GOP关键帧间隔smartp_gop_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25; // 设置源帧率分子smartp_gop_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; // 设置源帧率分母smartp_gop_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25; // 设置目标帧率分子smartp_gop_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; // 设置目标帧率分母smartp_gop_venc_attr.stRcAttr.stH264Cbr.u32BitRate = 8388608; // 设置码率大小ret = RK_MPI_VENC_CreateChn(SMARTP_GOP_VENC_CHN, &smartp_gop_venc_attr);if (ret){printf("Set SMARTP_Gop Venc Attr Failed.....\n");}else{printf("Set SMARTP_Gop Venc Attr Success.....\n");}VENC_GOP_ATTR_S venc_gop_attr_s;venc_gop_attr_s.enGopMode = VENC_GOPMODE_SMARTP;//设置GOP模式为SMARTP模式venc_gop_attr_s.u32GopSize = 25;//设置短参考帧间隔是25venc_gop_attr_s.u32BgInterval = 25 * 5;//长期参考帧的间隔venc_gop_attr_s.s32IPQpDelta = 6;//设置I帧和P帧的差值是6venc_gop_attr_s.s32ViQpDelta = 6;//设置虚拟I帧和P帧的差值是6ret = RK_MPI_VENC_SetGopMode(SMARTP_GOP_VENC_CHN,&venc_gop_attr_s);if (ret){printf("Set SMARTP_GOP_MODE Failed.....\n");}else{printf("Set SMARTP_GOP_MODE Success.....\n");}MPP_CHN_S vi_chn_s;vi_chn_s.enModId = RK_ID_VI;vi_chn_s.s32ChnId = VI_CHN_ID;MPP_CHN_S common_gop_venc_chn_s;common_gop_venc_chn_s.enModId = RK_ID_VENC;common_gop_venc_chn_s.s32ChnId = COMMON_GOP_VENC_CHN;MPP_CHN_S smartp_gop_venc_chn_s;smartp_gop_venc_chn_s.enModId = RK_ID_VENC;smartp_gop_venc_chn_s.s32ChnId = SMARTP_GOP_VENC_CHN;ret = RK_MPI_SYS_Bind(&vi_chn_s, &common_gop_venc_chn_s); //绑定VI和COMMON_GOP的VENC节点if (ret){printf("Bind Vi And Common_Gop Failed.....\n");}else{printf("Bind Vi And Common_Gop Success.....\n");}ret = RK_MPI_SYS_Bind(&vi_chn_s, &smartp_gop_venc_chn_s); //绑定VI和SMARTP_GOP的VENC节点if (ret){printf("Bind Vi And Smartp_Gop Failed.....\n");}else{printf("Bind Vi And Smartp_Gop Success.....\n");}pthread_t common_gop_pid, smartp_gop_pid;pthread_create(&common_gop_pid, NULL, get_common_gop_thread, NULL);pthread_create(&smartp_gop_pid, NULL, get_smartp_gop_thread, NULL);while (1){sleep(2);}RK_MPI_SYS_UnBind(&vi_chn_s, &common_gop_venc_chn_s);RK_MPI_SYS_UnBind(&vi_chn_s, &smartp_gop_venc_chn_s);RK_MPI_VENC_DestroyChn(COMMON_GOP_VENC_CHN);RK_MPI_VENC_DestroyChn(SMARTP_GOP_VENC_CHN);RK_MPI_VI_DisableChn(PIPE_ID, VI_CHN_ID);return 0;
}
疑问:
这段代码中RV1126这里的GOP, 一个是普通GOP, 一个是 smart GOP 就是这里只是设置了smart GOP, 普通GOP就并没有没设置, 是默认还是?
关键点分析:
- 默认 GOP 模式:
- 在 Rockchip 的 RKMEDIA 库中,如果未显式设置 GOP 模式,编码器通常会使用 默认的普通 GOP 模式(可能是
VENC_GOPMODE_NORMALP
或类似模式,具体取决于实现)。 - 默认的 GOP 结构通常是简单的 I-P-P-P... 序列,GOP 大小由
u32Gop
参数(在stH264Cbr
中设置为 25)决定,即每 25 帧一个 I 帧,其余为 P 帧。
- 在 Rockchip 的 RKMEDIA 库中,如果未显式设置 GOP 模式,编码器通常会使用 默认的普通 GOP 模式(可能是
- 代码中的显式设置:
- 只有
SMARTP_GOP_VENC_CHN
调用了RK_MPI_VENC_SetGopMode
,明确配置了SMARTP
模式的参数(如u32GopSize=25
、u32BgInterval=125
等)。 COMMON_GOP_VENC_CHN
未调用此函数,因此会使用默认配置。
- 只有