RV1126画面质量三:QP调节

一.什么是 QP 调节?

QP 参数调节,指的是量化参数调节。它主要是来调节图像的细节,最终达到调节画面质量的作用。QP 值和比特率成反比QP值越小画面质量越高;反之 QP 值越大,画面质量越低。而且随着视频源复杂度,这种反比的关系会更加明显。QP 调节是改变画面质量最常用的手段之一。

QP调节H264和H265画面最常用的一个手段。

二.QP 调节的基本概念:

QP 调节一般由 QStep、MinQp、MaxQp 这三个参数进行调节。下面我们来看看,这三个参数的作用。下面这个是QP值和QStep对应的一个关键值。假设我的Qset是1,QP值就是4,在开发的时候,一个是直接调Qset就可以了

前面说了QP值越小,画面越好,反之画面越差。 因为QP值和QStep对应的一个关键值。所以我们通过调Qset来设置QP值。

2.2. minqp 最小量化步长(调节禁止画面)

设置最小量化器,限制最好的图像质量(重点在静止画面比如建筑,植物......),当 QP 达到这个值的时候,数值不会变。这就会使得在静止场景下,码率到达一定数量后不会进行调整。minqp 越小,静止时候码率越大,质量越好,建议值[8,20]。简单说就是解决禁止画面的异常,比如呼吸效应。这个值是经验值来的,是经过多调试才能得出一个比较不错的值,8是最好,但是有代价就是码率比较大。20画面质量也还行,但是码效率小。感根据实际情况取舍。如果我填8,这个植物是没有任何呼吸效应和马赛克情况。

比如下面这写球员站着的球员用minqp 最小量化步长,因为画面相对禁止。

2.3. maxqp 最大量化步长(调节运动画面)

设置最大量化器,最大 QP 值,限制最差的画面(重点在运动的时候),在运动画面把宏块和马赛克去掉,maxQp 越小,2同样道理,当运动情况下到达一定码率后就不会调整。建议值[20,50],一样要多试。

比如踢球,因为是运动的,就要调maxqp

QSet调整体画面,minQp调禁止的画面,maxqp调运动画面。

QP调节的有一个数据结构体rvVENC_RC_PARAM_S数据结构

u32ThrdI、u32ThrdP:分别衡量 I 帧、P 帧宏块复杂的一组阈值。这组阈值是从小到大依次排序,每个阈值的取值是[0,255]。这组阈值主要用于宏块级别的码率控制,并根据图像复杂程度对每个宏块进行 QP 调节。这部分大部分也用在 CBR 上面。

u32ThrdI 默认值:[0,0,0,0,3,3,5,5,8,8,8,15,15,20,25,25]

u32ThrdP 默认值:[0,0,0,0,3,3,5,5,8,8,8,15,15,20,25,25]

注意:u32ThrdI、u32ThrdP 在一般情况下不需要设置

下面解析一下:

我们知道每一帧都是一张图片,图片又是是一个一个像素点组成,16X16的像素点,为一个宏快,u32ThrdI、u32ThrdP是对这些宏块调节,可以说是微观调节,但是用处不大,不如整体调节

u32RowQpDeltaI: 基于行的宏块级别码率控制,每一行的宏块的起始 QP 相对于 I 帧的 QP 波动幅度值,主要用在 CBR 控制模式。对于码率波动比较严格的场景下,可以通过调节这个参数使得码率控制更加精确。在高码率时,该值推荐为 0;中码率时推荐该值为 0 或 1;低码率时推荐 该值为 2~5。

意思是每一个小宏块的QP 相对于 I 帧的 QP 波动幅度值,宏块的QP - I 帧的 QP = 码率的幅度

u32RowQpDeltaP: 基于行的宏块级别码率控制,每一行的宏块的起始 QP 相对于 P 帧的 QP 波动幅度值,主要用在 CBR 控制模式。对于码率波动比较严格的场景下,可以通过调节这个参数使得码率控制更加精确。在高码率时,该值推荐为 0;中码率时推荐该值为 0 或 1;低码率时推荐 该值为 2~5。

意思是每一个小宏块的QP 相对于 I 帧的 QP 波动幅度值,宏块的QP - I 帧的 QP = 码率的幅度

下面的表格就是来解释哪些范围是高码率,中码率,低码率

s32FirstFrameStartQp:第一帧 QP 值,默认-1。-1 代表的是第一帧的起始 QP 由编码器内部进行计算。若是其他值,则由用户指定该合法值为第一帧起始 QP。

在数据结果里面,这三个才是重点,实际开发的时候也是调这个

  1. stParamH264:主要是调节 H264 的 QP 参数
  2. stParamH265:主要是调节 H265 的 QP 参数
  3. stParamMjpeg:主要是调节 Mjpeg 的 QP 参数

stParamH264:主要是调节 H264 的 QP 参数:

  1. u32StepQp: QP 的 STEP 步长,STEP 步长去调节 QP 值,要想画面最好直接填1
  2. u32MaxQp:QP 的最大值调节非I帧的,对运动画面进行限制,若忽视码率则建议 51;若对质量讲究,则建议设置[20,51]。
  3. u32MinQp:QP 的最小值,希望在静止画面的时候节省码率,取值范围[0,48]。VBR 建议设置为[24,32],CBR 建议设置[10,20]
  4. u32MaxIQp: I 帧的 QP 最大值,取值范围[8,51],图像运动的时候节省码率,默认和 u32MaxQP 的值是一致。
  5. u32MinIQp: I 帧的 QP 最小值,取值范围[0,48],图像静止或小运动的时候节省码率,默认和 u32MinIQp 是一致。

u32MaxQp和u32MinQp还是基于I帧P帧B帧直接整体调整,u32RowQpDeltaI和u32RowQpDeltaP:基于宏块调整。宏块调整的效果远远低于整体调整

stParamH265:主要是调节 H265 的 QP 参数:和264差不多

  1. u32StepQp: QP 的 STEP 步长
  2. u32MaxQp:QP 的最大值,对运动画面进行限制,若忽视码率则建议 51;若对质量讲究,则建议设置[40,51]。取值范围[8,51]。
  3. u32MinQp:QP 的最小值,希望在静止画面的时候节省码率,取值范围[0,48]。VBR 建议设置为[24,32],CBR 建议设置[10,20]
  4. u32MaxIQp: I 帧的 QP 最大值,取值范围[8,51],图像运动的时候节省码率,默认和 u32MaxQP 的值是一致。
  5. u32MinIQp: I 帧的 QP 最小值,取值范围[0,48],图像静止或小运动的时候节省码率,默认和 u32MinIQp 是一致。

编码 :

#include <stdio.h>
#include "rkmedia_config.h"

void * collect_venc_thread(void * args)
{
   pthread_detach(pthread_self());
   MEDIA_BUFFER mb;
   FILE * h264_file = fopen("./test_output_smart.h264", "w+");

   while (1)
   {
       mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, 0 , -1);
       if(!mb)
       {
           printf("Get Venc Buffer Break....\n");
           break;
       }

       printf("mmmmmm\n");
       fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, h264_file);
       RK_MPI_MB_ReleaseBuffer(mb);
   }
   return NULL;
}

int main()
{
    RK_U32 u32Width = 1920;
    RK_U32 u32Height = 1080;
    RK_CHAR *pDeviceName = "rkispp_scale0";
    RK_CHAR *pOutPath = NULL;
    RK_CHAR *pIqfilesPath = NULL;
    CODEC_TYPE_E enCodecType = RK_CODEC_TYPE_H264;
    RK_CHAR *pCodecName = "H264";
    RK_S32 s32CamId = 0;
    RK_U32 u32BufCnt = 3;

    rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL;
    SAMPLE_COMM_ISP_Init(hdr_mode, RK_FALSE);
    SAMPLE_COMM_ISP_Run();
    SAMPLE_COMM_ISP_SetFrameRate(30);

    int ret;
    RK_MPI_SYS_Init();
    VI_CHN_ATTR_S vi_chn_attr;
    vi_chn_attr.pcVideoNode = pDeviceName;
    vi_chn_attr.u32BufCnt = u32BufCnt;
    vi_chn_attr.u32Width = u32Width;
    vi_chn_attr.u32Height = u32Height;
    vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12;
    vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP;
    vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;
    ret = RK_MPI_VI_SetChnAttr(s32CamId, 1, &vi_chn_attr);
    ret |= RK_MPI_VI_EnableChn(s32CamId, 1);
    if (ret)
    {
        printf("ERROR: create VI[0] error! ret=%d\n", ret);
        return 0;
    }

    VENC_CHN_ATTR_S venc_chn_attr;
    memset(&venc_chn_attr, 0, sizeof(venc_chn_attr));
    venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;
    venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
    venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 30 * 4;
    venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = u32Width * u32Height;
    // frame rate: in 30/1, out 30/1.
    venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
    venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 30;
    venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;
    venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 30;
    venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;
    venc_chn_attr.stVencAttr.u32PicWidth = u32Width;
    venc_chn_attr.stVencAttr.u32PicHeight = u32Height;
    venc_chn_attr.stVencAttr.u32VirWidth = u32Width;
    venc_chn_attr.stVencAttr.u32VirHeight = u32Height;
    venc_chn_attr.stVencAttr.u32Profile = 77;
    ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr);
    if (ret)
    {
        printf("ERROR: create VENC[0] error! ret=%d\n", ret);
        return 0;
    }

    RK_U32 u32StepQp = 1; //因为是一个整形,我们尽量填整型
    RK_U32 u32MaxQp = 24; //这个只能说一个经验值,可能24还会一点点马赛克的情况,只能说多调试
    RK_U32 u32MinQp = 10; //禁止的时候我们把画面质量提高,10是比较高的了
    RK_U32 u32MaxIQp = 24; 
    RK_U32 u32MinIQp = 10;
    VENC_RC_PARAM_S venc_rc_param;

    venc_rc_param.u32RowQpDeltaI = 1; //因为是中码率所以填1
    venc_rc_param.u32RowQpDeltaP = 1; //因为是中码率所以填1
    venc_rc_param.s32FirstFrameStartQp = -1; //默认-1
    //把值设置进去
    venc_rc_param.stParamH264.u32StepQp = u32StepQp;
    venc_rc_param.stParamH264.u32MaxQp = u32MaxQp;
    venc_rc_param.stParamH264.u32MinQp = u32MinQp;
    venc_rc_param.stParamH264.u32MinIQp = u32MinIQp;
    venc_rc_param.stParamH264.u32MaxIQp = u32MaxIQp;
    ret = RK_MPI_VENC_SetRcParam(0, &venc_rc_param);
    if (ret != 0)
    {
        printf("venc_params failed....\n");
    }
    else
    {
        printf("venc_params success....\n");
    }


    MPP_CHN_S stSrcChn;
    stSrcChn.enModId = RK_ID_VI;
    stSrcChn.s32DevId = 0;
    stSrcChn.s32ChnId = 1;
    
    MPP_CHN_S stDestChn;
    stDestChn.enModId = RK_ID_VENC;
    stDestChn.s32DevId = 0;
    stDestChn.s32ChnId = 0;
    ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn);
    if (ret)
    {
        printf("ERROR: Bind VI[0] and VENC[0] error! ret=%d\n", ret);
        return 0;
    }

    pthread_t pid;
    ret = pthread_create(&pid, NULL, collect_venc_thread, NULL);
    if(ret != 0)
    {
        printf("Create Venc Thread Failed....\n");
    }

    while (1)
    {
       sleep(20);
    }
    

    return 0;
}
相关推荐
消失的旧时光-194314 小时前
android 音视频系列引导
android·音视频
daqinzl20 小时前
Ubuntu x64下交叉编译ffmpeg、sdl2到目标架构为aarch64架构的系统(生成ffmpeg、ffprobe、ffplay)
ubuntu·ffmpeg·ffplay·sdl2·交叉编译 aarch64
ai产品老杨2 天前
能够对设备的历史数据进行学习与分析,通过与设备当前状态的比对,识别潜在故障并做出预判的名厨亮灶开源了。
vue.js·人工智能·学习·安全·开源·音视频
学而知不足~2 天前
WebRtc06: 音视频数据采集
音视频·实时音视频
yerennuo2 天前
FFmpeg 自定义IO和格式转换
ffmpeg
AI视觉网奇2 天前
视频拼接,拼接时长版本
音视频
小奥超人2 天前
PPT教程:怎样在PPT中嵌入视频或音频文件?
windows·经验分享·powerpoint·音视频·办公技巧
h39743 天前
DirectShow过滤器开发-读MP4视频文件过滤器(再写)
c++·windows·音视频
daqinzl3 天前
ubuntu x64下交叉编译ffmpeg到目标架构为aarch架构的系统
ubuntu·ffmpeg·交叉编译·arm64 aarch64