目录
1.3 QP调节核心三参数:QStep、MinQp、MaxQp解析
2.4 VENC_MJPEG_RC_PARAM_S 结构体详解
2.5 QP API函数---RK_MPI_VENC_SetRcParam
一。QP原理介绍
1.1 QP 调节详解
-
作用:控制视频编码中的量化精度,直接影响压缩率和图像质量
-
范围:H.264/H.265 中 QP 值范围通常是 0-51
-
关系:QP值 ⇌ 量化步长 ⇌ 图像质量 ⇌ 码率
1.2 QP 值与码率的关系
QP值减小 → 量化步长减小 → 更多细节保留 → 码率增加 → 质量提高
QP值增大 → 量化步长增大 → 更多细节丢失 → 码率降低 → 质量下降

1.3 QP调节核心三参数:QStep、MinQp、MaxQp解析
1.3.1 QStep(量化步长)
定义:DCT系数量化的基本单位,决定细节保留程度
与QP关系:QP每增加6,QStep增加一倍(指数关系)
量化公式:
FQ = round(y / QStep)
FQ:量化后的值
y:原始样本点
QStep:量化步长
QP取值范围:0-51
QP值越小,整体画面越精细
1.3.2MinQp(最小量化参数)
核心作用
设置最小量化器,限制最佳图像质量,主要影响静止画面表现
工作机制
当QP达到MinQp设定值时,量化参数不再降低
在静止场景下,码率达到一定水平后保持稳定,不再继续调整
防止在简单场景下过度消耗码率资源
参数特性
取值范围:[0, 48]
质量关系:MinQp值越小,静止场景码率越高,图像质量越好
应用重点:特别适用于静态画面、低复杂度场景的质量保障
当minQp=48时,画面可能模糊情况,静态画面可能存在呼吸效应的情况,但是很省码率
呼吸效应:在电子设备或系统中,某些参数按照特定的"呼吸"节奏进行周期性变化的效果。
1.3.3MaxQp(最大量化参数)
核心作用
设置最大量化器,限制最差画面质量,主要影响运动画面的表现。
工作机制
当QP达到MaxQp设定值时,量化参数不再升高
在运动场景下,码率下降到一定水平后保持稳定,防止质量进一步劣化
确保高动态场景的基本可视性
参数特性
取值范围:[8, 51]
质量关系:MaxQp值越小,运动场景码率越高,图像质量相对越好
应用重点:特别适用于运动画面、高复杂度场景的质量保障
二。QP参数讲解
2.1 VENC_RC_PARAM_S 结构体详解
typedef struct rk_VENC_RC_PARAM_S {
RK_S32 s32FirstFrameStartQp; // 初始化QP值
union {
VENC_H264_RC_PARAM_S stParamH264; // H.264编码参数
VENC_H265_RC_PARAM_S stParamH265; // H.265编码参数
VENC_MJPEG_RC_PARAM_S stParamMjpeg; // MJPEG编码参数
};
} VENC_RC_PARAM_S;
核心参数说明
1. s32FirstFrameStartQp(初始化QP值) :一般设置成 -1 ---表示第一帧的起帧OP由编码器内部进行计算,如果是其他值,则由用户指定该合法值
2.stParamH264(H264的QP调节参数)、stParamH265(H265的QP调节参数)、stParamMjpeg(mjpeg的QP调节参数是不同编码格式参数结构体,使用union联合体实现,但结构体设置内容都是一致的
注意:当VENC没有进行QP量化参数调节的时候,所有参数都是默认最差值,比如:maxQp默认值是51、minQp默认值是48、maxIQp默认值是51、minIQp默认值是48。说明原始数据精细度是最差的
2.2 H.264 QP参数结构体:
typedef struct rk_VENC_H264_RC_PARAM_S {
RK_S32 u32MinQp; // 最小QP [0,48]
RK_S32 u32MaxQp; // 最大QP [8,51]
RK_S32 u32MinIQp; // I帧最小QP
RK_S32 u32MaxIQp; // I帧最大QP
} VENC_H264_RC_PARAM_S;
2.3 H.265 QP参数结构体:
typedef struct rk_VENC_H265_RC_PARAM_S {
RK_S32 u32MinQp; // 最小QP [0,48]
RK_S32 u32MaxQp; // 最大QP [8,51]
RK_S32 u32MinIQp; // I帧最小QP
RK_S32 u32MaxIQp; // I帧最大QP
// 其他H.265特有参数...
} VENC_H265_RC_PARAM_S;
P帧和I帧的内容可以回顾一下:
2.4 VENC_MJPEG_RC_PARAM_S 结构体详解
typedef struct rk_VENC_MJPEG_RC_PARAM_S {
RK_S32 u32Qfactor; // 质量因子 [1, 99]
RK_S32 u32MaxQfactor; // 最大质量因子
RK_S32 u32MinQfactor; // 最小质量因子
} VENC_MJPEG_RC_PARAM_S;
2.5 QP API函数---RK_MPI_VENC_SetRcParam
RK_S32 RK_MPI_VENC_SetRcParam(VENC_CHN VeChn, const VENC_RC_PARAM_S *pstRcParam);
功能:设置编码通道的QP和码率控制参数
参数:
VeChn:编码通道号
pstRcParam:QP参数结构体指针返回值:成功返回0,失败返回错误码
三。编码实战
3.1 程序架构

重点在于:如何将QP与VENC绑定起来---使用RK_MPI_VENC_SetRcParam
3.2 调试过程
3.2.1第一次调试:

主要两个问题:
1. QP行增量参数错误
mpp_enc: invalid hw qp delta row [-7987:-9121]
问题:QP行增量参数设置异常(负值过大)
原因:
QpRowDeltaI和QpRowDeltaP设置不合理问题定义:QP设置本身没有问题,主要VENC设置不对,导致QP的参数不在VENC中,修改VENC中的分辨率后,可以正常运行
2. 输入缓冲区分辨率错误
[RKMEDIA][VENC][Error]:The resolution of the input buffer is wrong.
问题:输入图像分辨率与编码器配置不匹配
问题定位:我是在上一个程序基础上修改的,分辨率忘记修改了
3.2.2第二次调试:

3.2.3 结果
由于1920*1080原始画面过大,占据整个屏幕,不好对比。在cmd对播放尺寸做了限制
ffplay -x 1000 test_qt_venc_20251118.h264

看上去二者好像没有太大的区别:qp的画面更加亮点?可能处理的不是很明显
3.3 代码
#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 CAMERA_PATH "r"
#define PIPE_ID 0
#define VI_CHN_ID 0//#define CAMERA_PATH "r"
#define COMMON_VENC_CHN 0
#define QP_VENC_CHN 1
//创建多线程获取高分辨率的编码码流
void *get_common_venc_thread(void * args)
{
pthread_detach(pthread_self());
/*
pthread_detach(pthread_self());
作用:
将调用此代码的线程设置为分离状态(detached state)
分离状态的线程在结束时会自动释放资源,不需要其他线程调用 pthread_join() 来回收
*/
FILE * origin_h264 = fopen("test_origin_venc_20251118.h264","w+");
MEDIA_BUFFER mb;
while(1)
{
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC,COMMON_VENC_CHN,-1);
if(!mb)
{
printf(" Get common Venc break .....\n");
break;
}
printf("Get common Venc Success.....\n");
fwrite(RK_MPI_MB_GetPtr(mb),RK_MPI_MB_GetSize(mb),1,origin_h264);
RK_MPI_MB_ReleaseBuffer(mb);
}
return NULL;
}
void *get_qp_venc_thread(void * args)
{
pthread_detach(pthread_self());
/*
pthread_detach(pthread_self());
作用:
将调用此代码的线程设置为分离状态(detached state)
分离状态的线程在结束时会自动释放资源,不需要其他线程调用 pthread_join() 来回收
*/
FILE * qp_h264 = fopen("test_qt_venc_20251118.h264","w+");
MEDIA_BUFFER mb;
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 Venc Success.....\n");
fwrite(RK_MPI_MB_GetPtr(mb),RK_MPI_MB_GetSize(mb),1,qp_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"; //set the node path of the video device
vi_chn_attr.u32Width = 1920; //set the width of the resolution
vi_chn_attr.u32Height=1080; //set the height of the resolution
vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12; //set the image tpye
vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP; //set the acquisition type of VI
vi_chn_attr.u32BufCnt = 3; //set the buffer quantity
vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL; //set the VI job type
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_Enable Failed....\n");
return -1;
}else{
printf("VI_Enable Success....\n");
}
ret = RK_MPI_VI_StartStream(PIPE_ID,VI_CHN_ID);
if(ret)
{
printf("VI_stare Failed....\n");
return -1;
}else{
printf("VI_stare Success....\n");
}
//High Venc Parameter
VENC_CHN_ATTR_S commen_venc_attr;
commen_venc_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;
commen_venc_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;
commen_venc_attr.stVencAttr.u32PicWidth = 1920;
commen_venc_attr.stVencAttr.u32PicHeight = 1080;
commen_venc_attr.stVencAttr.u32VirWidth = 1920;
commen_venc_attr.stVencAttr.u32VirHeight = 1080;
commen_venc_attr.stVencAttr.u32Profile = 66; //set the encoding level
commen_venc_attr.stVencAttr.enRotation = VENC_ROTATION_0;
/**set the encoding level**/
commen_venc_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
commen_venc_attr.stRcAttr.stH264Cbr.u32Gop = 25;
commen_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum=25;
commen_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen=1;
commen_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;
commen_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
commen_venc_attr.stRcAttr.stH264Cbr.u32BitRate = 8388608;
ret = RK_MPI_VENC_CreateChn(COMMON_VENC_CHN,&commen_venc_attr);
if (ret)
{
printf("Set commen_venc_attr Failed.....\n");
}
else
{
printf("Set commen_venc_attr Success.....\n");
}
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; //set the encoding level
QP_venc_attr.stVencAttr.enRotation = VENC_ROTATION_0;
/**set the encoding level**/
QP_venc_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
QP_venc_attr.stRcAttr.stH264Cbr.u32Gop = 25;
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;
venc_rc_params.stParamH264.u32StepQp = 1;
venc_rc_params.stParamH264.u32MaxQp = 30;
venc_rc_params.stParamH264.u32MinQp = 10;
venc_rc_params.stParamH264.u32MaxIQp = 30;
venc_rc_params.stParamH264.u32MinIQp = 10;
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;
/**vi bind the high-resolution VENC module **/
ret = RK_MPI_SYS_Bind(&vi_chn_s,&common_venc_chn_s);
if (ret)
{
printf("VI Bind COMMON Venc Failed.....\n");
return -1;
}
else
{
printf("VI Bind COMMON Venc Success.....\n");
}
/**vi bind the RGA module **/
ret = RK_MPI_SYS_Bind(&vi_chn_s, &qp_venc_chn_s);
if (ret)
{
printf("VI Bind QP_VENC Failed.....\n");
return -1;
}
else
{
printf("VI Bind QP_VENC Success.....\n");
}
pthread_t common_venc_pid;
pthread_t qp_venc_pid;
pthread_create(&common_venc_pid, NULL, get_common_venc_thread, NULL); //创建多线程获取高分辨率的编码码流
pthread_create(&qp_venc_pid, NULL, get_qp_venc_thread, NULL);//创建多线程获取RGA码流并发送到低分辨率编码器
while(1)
{
sleep(2);
}
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;
}

1788

被折叠的 条评论
为什么被折叠?



