LINUX学习之视频编码增强模块:QP原理及实战

部署运行你感兴趣的模型镜像

目录

一。QP原理介绍

1.1 QP 调节详解

1.2 QP 值与码率的关系

1.3 QP调节核心三参数:QStep、MinQp、MaxQp解析

1.3.1 QStep(量化步长)

1.3.2MinQp(最小量化参数)

1.3.3MaxQp(最大量化参数)

二。QP参数讲解

2.1 VENC_RC_PARAM_S 结构体详解

2.2 H.264 QP参数结构体:

2.3 H.265 QP参数结构体:

2.4 VENC_MJPEG_RC_PARAM_S 结构体详解

2.5 QP API函数---RK_MPI_VENC_SetRcParam

三。编码实战

3.1 程序架构

3.2 调试过程

3.2.1第一次调试:

3.2.2第二次调试:

3.2.3 结果

3.3 代码


一。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帧的内容可以回顾一下:

https://blog.youkuaiyun.com/ALIANG2000/article/details/154201656?spm=1001.2014.3001.5501https://blog.youkuaiyun.com/ALIANG2000/article/details/154201656?spm=1001.2014.3001.5501

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行增量参数设置异常(负值过大)

  • 原因QpRowDeltaIQpRowDeltaP 设置不合理

  • 问题定义: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;
}

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值