目录
一.RV1126多线程分别获取QP的VENC数据和普通VENC数据的流程
1.2 普通VENC模块初始化、 QP量化VENC模块初始化:
1.4VI模块绑定普通VENC模块和QP量化的VENC模块:
一.RV1126多线程分别获取QP的VENC数据和普通VENC数据的流程
RV1126利用多线程同时获取普通VENC数据和QP量化的VENC数据,需要上面8个步骤,分别是:VI模块的初始化、普通VENC模块的初始化、量化QP的VENC模块的初始化、绑定VI模块和普通VENC模块、设置QP的VENC模块QP量化参数、绑定VI模块和QP量化VENC模块、多线程获取每一帧普通VENC编码数据、多线程获取每一帧QP量化后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 普通VENC模块初始化、 QP量化VENC模块初始化:
//普通的VENC模块初始化
VENC_CHN_ATTR_S common_venc_attr;
。。。。。。。。。。。。。。。。。。。。。。。
RK_MPI_VENC_CreateChn(COMMON_VENC_CHN, &common_venc_attr);
//QP的VENC模块初始化
VENC_CHN_ATTR_S qp_venc_attr;
。。。。。。。。。。。。。。。。。。。。。。。
RK_MPI_VENC_CreateChn(QP_VENC_CHN, &qp_venc_attr);
这里需要创建两个VENC层,一个是普通的VENC层、另外一个是QP的VENC层
1.3设置QP的VENC模块QP量化参数:
VENC_RC_PARAM_S venc_rc_params;
venc_rc_params.s32FirstFrameStartQp = -1;
venc_rc_params.stParamH264.u32StepQp = 1;
venc_rc_params.stParamH264.u32MaxQp = 30;
venc_rc_params.stParamH264.u32MinQp = 10;
venc_rc_params.stParamH264.u32MinIQp = 10;
venc_rc_params.stParamH264.u32MaxIQp = 30;
ret = RK_MPI_VENC_SetRcParam(QP_VENC_CHN, &venc_rc_params);
上面的代码主要是设置的QP调节的参数,设置完成之后调用 RK_MPI_VENC_SetRcParam
1.4VI模块绑定普通VENC模块和QP量化的VENC模块:
//VI模块节点
MPP_CHN_S vi_chn_s;
vi_chn_s.enModId = RK_ID_VI;
vi_chn_s.s32ChnId = VI_CHN_ID;
//普通VENC模块节点
MPP_CHN_S common_venc_chn_s;
common_venc_chn_s.enModId = RK_ID_VENC;
common_venc_chn_s.s32ChnId = COMMON_VENC_CHN;
ret = RK_MPI_SYS_Bind(&vi_chn_s, &common_venc_chn_s);
//QP的VENC模块节点
MPP_CHN_S qp_venc_chn_s;
qp_venc_chn_s.enModId = RK_ID_VENC;
qp_venc_chn_s.s32ChnId = QP_VENC_CHN;
ret = RK_MPI_SYS_Bind(&vi_chn_s, &qp_venc_chn_s);
注意:VI模块分别绑定普通VENC模块和QP量化的VENC模块
1.5多线程获取普通VENC模块数据:
开启一个线程去采集每一帧普通的VENC模块数据,使用的API是RK_MPI_SYS_GetMediaBuffer, 模块ID是RK_ID_VENC,通道号ID是普通的 VENC创建ID号。这个API伪代码如下:
while(1)
{
.........................
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, COMMON_VENC_CHN, -1);
fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, common_h264_file);
.......................
}
1.6多线程获取QP的VENC模块数据:
开启一个线程去采集每一帧QP的VENC模块数据,使用的API是RK_MPI_SYS_GetMediaBuffer, 模块ID是RK_ID_VENC,通道号ID是QP的VENC层。这个API伪代码如下:
while(1)
{
.........................
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, QP_VENC_CHN, -1);
fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, qp_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 "common/sample_common.h"
#include "rkmedia_api.h"
#define PIPE_ID 0
#define VI_CHN_ID 0
#define COMMON_VENC_CHN 0
#define QP_VENC_CHN 1
//获取每一帧COMMON_VENC的编码数据
void * get_common_venc_thread(void * args)
{
pthread_detach(pthread_self());
FILE * origin_h264 = fopen("test_origin.h264", "w+");
MEDIA_BUFFER mb = NULL;
while (1)
{
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, COMMON_VENC_CHN, -1);
if(!mb)
{
printf("Get Origin Venc break....\n");
break;
}
printf("Get Origin File Venc Success....\n");
fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, origin_h264);
RK_MPI_MB_ReleaseBuffer(mb);
}
return NULL;
}
//获取每一帧QP_VENC的编码数据
void * get_qp_venc_thread(void * args)
{
pthread_detach(pthread_self());
FILE * origin_h264 = fopen("test_qp.h264", "w+");
MEDIA_BUFFER mb = NULL;
while (1)
{
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, QP_VENC_CHN, -1);
if(!mb)
{
printf("Get QP Venc break....\n");
break;
}
printf("Get QP File Venc Success....\n");
fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, origin_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 Venc Parameter
VENC_CHN_ATTR_S common_venc_attr;
common_venc_attr.stVencAttr.enType = RK_CODEC_TYPE_H264; // 设置编码器类型
common_venc_attr.stVencAttr.imageType = IMAGE_TYPE_NV12; // 设置编码图像类型
common_venc_attr.stVencAttr.u32PicWidth = 1920; // 设置编码分辨率宽度
common_venc_attr.stVencAttr.u32PicHeight = 1080; // 设置编码分辨率高度
common_venc_attr.stVencAttr.u32VirWidth = 1920; // 设置编码分辨率虚宽
common_venc_attr.stVencAttr.u32VirHeight = 1080; // 设置编码分辨率虚高
common_venc_attr.stVencAttr.u32Profile = 66; // 设置编码等级
common_venc_attr.stVencAttr.enRotation = VENC_ROTATION_0; // 设置编码的旋转角度
//********* 设置码率控制属性 *******************//
common_venc_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; // 设置H264的CBR码率控制模式
common_venc_attr.stRcAttr.stH264Cbr.u32Gop = 25; // 设置GOP关键帧间隔
common_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25; // 设置源帧率分子
common_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; // 设置源帧率分母
common_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25; // 设置目标帧率分子
common_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; // 设置目标帧率分母
common_venc_attr.stRcAttr.stH264Cbr.u32BitRate = 8388608; // 设置码率大小
ret = RK_MPI_VENC_CreateChn(COMMON_VENC_CHN, &common_venc_attr);
if (ret)
{
printf("Set Common Venc Attr Failed.....\n");
}
else
{
printf("Set Common Venc Attr Success.....\n");
}
// QP Venc Parameter
VENC_CHN_ATTR_S qp_venc_attr;
qp_venc_attr.stVencAttr.enType = RK_CODEC_TYPE_H264; // 设置编码器类型
qp_venc_attr.stVencAttr.imageType = IMAGE_TYPE_NV12; // 设置编码图像类型
qp_venc_attr.stVencAttr.u32PicWidth = 1920; // 设置编码分辨率宽度
qp_venc_attr.stVencAttr.u32PicHeight = 1080; // 设置编码分辨率高度
qp_venc_attr.stVencAttr.u32VirWidth = 1920; // 设置编码分辨率虚宽
qp_venc_attr.stVencAttr.u32VirHeight = 1080; // 设置编码分辨率虚高
qp_venc_attr.stVencAttr.u32Profile = 66; // 设置编码等级
qp_venc_attr.stVencAttr.enRotation = VENC_ROTATION_0; // 设置编码的旋转角度
//********* 设置码率控制属性 *******************//
qp_venc_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; // 设置H264的CBR码率控制模式
qp_venc_attr.stRcAttr.stH264Cbr.u32Gop = 25; // 设置GOP关键帧间隔
qp_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25; // 设置源帧率分子
qp_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; // 设置源帧率分母
qp_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25; // 设置目标帧率分子
qp_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; // 设置目标帧率分母
qp_venc_attr.stRcAttr.stH264Cbr.u32BitRate = 8388608; // 设置码率大小
ret = RK_MPI_VENC_CreateChn(QP_VENC_CHN, &qp_venc_attr);
if (ret)
{
printf("Set QP Venc Attr Failed.....\n");
}
else
{
printf("Set QP Venc Attr Success.....\n");
}
//QP Set
VENC_RC_PARAM_S venc_rc_params;
venc_rc_params.s32FirstFrameStartQp = -1;//初始化StartQP的值,-1
venc_rc_params.stParamH264.u32StepQp = 1; //QStep Set
venc_rc_params.stParamH264.u32MaxQp = 30; //设置MAXQP的值
venc_rc_params.stParamH264.u32MinQp = 10; //设置MINQP的值
venc_rc_params.stParamH264.u32MaxIQp = 30;//设置MAXIQP的值
venc_rc_params.stParamH264.u32MinIQp = 10;//设置MINIQP的值
ret = RK_MPI_VENC_SetRcParam(QP_VENC_CHN, &venc_rc_params);
if(ret)
{
printf("Set RC_PARAM Failed.....\n");
}
else
{
printf("Set RC_PARAM 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_venc_chn_s;
common_venc_chn_s.enModId = RK_ID_VENC;
common_venc_chn_s.s32ChnId = COMMON_VENC_CHN;
MPP_CHN_S qp_venc_chn_s;
qp_venc_chn_s.enModId = RK_ID_VENC;
qp_venc_chn_s.s32ChnId = QP_VENC_CHN;
ret = RK_MPI_SYS_Bind(&vi_chn_s, &common_venc_chn_s); 绑定VI模块和COMMON_VENC模块
if(ret)
{
printf("Bind Vi Chn And Common Venc Chn Failed.....\n");
}
else
{
printf("Bind Vi Chn And Common Venc Chn Success.....\n");
}
ret = RK_MPI_SYS_Bind(&vi_chn_s, &qp_venc_chn_s); 绑定VI模块和QP_VENC模块
if(ret)
{
printf("Bind Vi Chn And QP Venc Chn Failed.....\n");
}
else
{
printf("Bind Vi Chn And QP Venc Chn Success.....\n");
}
pthread_t common_pid, qp_pid;
pthread_create(&common_pid, NULL, get_common_venc_thread, NULL);
pthread_create(&qp_pid, NULL, get_qp_venc_thread, NULL);
while (1)
{
sleep(1);
}
RK_MPI_SYS_UnBind(&vi_chn_s, &common_venc_chn_s);
RK_MPI_SYS_UnBind(&vi_chn_s, &qp_venc_chn_s);
RK_MPI_VENC_DestroyChn(COMMON_VENC_CHN);
RK_MPI_VENC_DestroyChn(QP_VENC_CHN);
RK_MPI_VI_DisableChn(PIPE_ID, VI_CHN_ID);
return 0;
}
三.播放效果对比
分别播放两个H264文件,test_origin.h264、test_qp.h264。
ffplay test_origin.h264
ffplay test_qp.h264
然后对比两个画面的质量,test_origin.h264的画面质量低于test_qp.h264的画面质量。