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;
}
相关推荐
微帧Visionular5 小时前
AI视频单帧清晰,播放时却闪烁,原因是什么?
人工智能·深度学习·音视频
辰辰观影7 小时前
Windows网盘视频播放器推荐:无广告4K HDR怎么选
音视频
2601_966827459 小时前
电视端云盘在线播放怎么选?支持4K HDR的应用推荐
音视频
开开心心就好10 小时前
音频降噪剪辑工具,音量裁剪一键处理
运维·服务器·前端·spring·ocr·音视频·1024程序员节
zslefour13 小时前
使用ffmpeg遍历获取音频文件相关信息
ffmpeg
AI视频工具指南13 小时前
AI视频的声音和画面怎么配合?先分清谁在传递信息
人工智能·aigc·音视频
辰辰观影16 小时前
无广告播放器推荐来啦
音视频
TrueDei16 小时前
GStreamer 使用 valve 分别控制音视频流的暂停与恢复
音视频
Android系统攻城狮16 小时前
Linux FFmpeg深度解析之avcodec_parameters_from_context调用流程与实战(十六)
linux·运维·ffmpeg·音视频进阶·ffmpeg音视频·车载音视频进阶
一条破秋裤17 小时前
基于 Hi3516CV610 的立定跳远视频测量系统调研与实施方案
arm开发·音视频