RV1126——GOP模式调节画面质量

前面我们弄懂了 QP 量化参数对画质的影响,也知道了 GOP 就是一组连续编码帧的集合。但很多同学其实不知道,GOP 不只是改一个间隔数字,它还有三种完全不同的工作模式。

不同的 GOP 模式,会直接改变编码器的帧参考逻辑、码率分配方式、画面稳定性,甚至适配不同的网络环境。

在 RK 平台的 VENC 硬件编码中,一共提供三种主流 GOP 模式:普通 NormalP 模式、智能 SMARTP 模式、TSVC 分层编码模式。下面我结合实际工程体验,通俗讲清楚三者的原理、区别、优缺点和适用场景。

一、三种GOP模式

1.1 普通 GOP 模式(NORMALP)

普通 GOP 模式是我们默认使用、也是最简单稳定的一种编码模式。

它的编码逻辑非常直白:编码器严格按照我们设置的 GOP 间隔,固定生成 I 帧。整体帧结构就是:一个 I 帧 + 连续一串 P 帧,循环往复。

每一帧的参考逻辑很单一:所有 P 帧只参考前一帧画面,不会跨帧参考,也不会智能判断画面变化。

举个例子,我们代码中设置 GOP 为 25,帧率 25 帧,那么每秒固定生成一个 I 帧,中间全部是 P 帧。不管当前画面是静止不动,还是剧烈运动,编码器的工作逻辑完全不变。

这种模式最大的特点就是稳、简单、兼容性拉满。 因为帧结构规整、参考关系简单,几乎所有播放器、流媒体服务都能完美解析,而且编码延迟最低,非常适合常规监控录像、实时预览场景。

但缺点也很明显,不够智能: 画面静止的时候,依然会定时刷新 I 帧,造成不必要的码率浪费;画面剧烈运动时,又因为参考方式单一,容易出现细节丢失、马赛克增多的问题,码率利用率不算高。

总结一下: 普通 GOP 模式,就是死板但最稳的基础编码模式,适合绝大多数常规固定监控场景,也是我们本次实验普通编码通道使用的模式。

1.2 智能 GOP 模式(SMARTP)

SMARTP 智能 GOP 模式,算是 RK 平台针对监控场景优化的进阶模式,也是实际项目中非常推荐的模式。

它跳出了普通 GOP "固定死板" 的逻辑,核心优化就是长短帧参考 + 虚拟 I 帧机制

简单说,它不再让所有 P 帧只参考前一帧。 当画面静止、背景不变时,后续的 P 帧会远距离参考最开始的 I 帧,不会逐帧累积误差,极大节省码率,避免普通模式下的 "画面呼吸效应"; 当画面出现运动物体、画面变化剧烈时,它又会自动切换为近距离参考,精准捕捉画面变化,减少运动模糊和马赛克。

除此之外,SMARTP 模式会插入虚拟 I 帧。它不是真正的完整 I 帧,体积远小于真 I 帧,但可以起到刷新参考帧、截断误差累积的作用。既不用频繁生成大体积真 I 帧浪费带宽,又能保证长时间编码画面不崩、不花屏。

所以这个模式的优势非常贴合监控设备: 摄像头大多固定不动,背景长期静止,只有少量物体移动。SMARTP 可以大幅节省静态画面的码率,同时保证动态画面的清晰度,画质和带宽做到了很好的平衡。

唯一的小缺点就是,相比普通模式编码逻辑更复杂,极少数老旧播放器兼容性一般,晃动场景效果不如普通模式稳定。

1.3 TSVC 分层 GOP 模式

TSVC 时间可伸缩编码模式,和前两种模式完全不是一个思路,它是为弱网、不稳定网络场景设计的分层编码模式

前两种模式,编码出来的视频都是 "一整路",画质帧率固定。一旦网络带宽不够,就会直接卡顿、丢帧、花屏,没有折中方案。

而 TSVC 的核心思路是分层输出 。 它会把一路视频,拆分为基础层 + 增强层: 基础层保留基础画面、低帧率,保证最差网络下也能正常播放、不卡顿; 增强层负责补充细节、提升帧率和清晰度,用来提升整体画质。

网络状态好的时候,设备同时传输基础层 + 增强层,我们看到的就是高清流畅的画面; 网络卡顿、带宽不足时,设备会自动丢弃增强层数据 ,只保留基础层传输。画面会略微变模糊、帧率降低,但绝对不会花屏、不会卡顿中断

所以 TSVC 模式最大的优势就是极强的网络适应性和抗丢包能力

但它的短板也很明显:编码结构复杂、延迟更高、资源占用稍大,不适合低延迟的实时对讲、极速预览场景,更多用于车载监控、无线户外监控、4G 弱网传输等场景。

1.4 三种 GOP 模式工程选型总结

这里我结合实际开发经验,给大家整理一个最实用的选型结论:

  1. NormalP 普通模式:通用性最强、零延迟、兼容性最好。适合常规室内监控、固定录像、基础预览,是新手开发、基础实验的默认首选。
  2. SMARTP 智能模式:监控场景最优解。静态省码率、动态保画质,兼顾清晰度和存储带宽,是商用 IPC 设备的主流方案。
  3. TSVC 分层模式:弱网专属。主打网络自适应、抗卡顿,专门用于无线、移动、4G/5G 等不稳定网络环境。
  • RV1126 中设置 GOP 模式的结构体和 API

1.5 RV1126 GOP 属性结构体

上图就是RV1126设置GOP属性的结构体,下面我们来分别介绍它的成员变量:

1. enGopMode 编码GOP类型的设置,RV1126中分别提供了三种GOP类型的设置,分别是:VENC_GOPMODE_NORMALP (普通GOP模式)VENC_GOPMODE_ SMARTP (智能SMARTP模式)、VENC_GOPMODE_ TSVC (多层时域参考模型)

2. u32GopSize **:**编码GOP的长度大小,在普通的GOP模式下这个值就是I帧关键帧间隔。在SMARTP模式下,它是短期参考帧的间隔。

3. s32IPQpDelta **:**I帧相对于P帧的差值,这个值主要是调节I帧过大或者呼吸效应的场景,默认值为6, 取值范围是-10, 30

4. u32BgInterval **:**长期参考帧的间隔, 主要运用在SMARTP模式下, 普通GOP模式不起作用。一般而言,长参考帧是短参考帧的整数倍。

5. s32ViQpDelta **:**虚拟I帧相对于P帧的差值,这个值主要是调节虚拟I帧过大默认值为6,这个值主要使用在SMARTP模式下调节运动画面的场景,取值范围是-10, 30

RV1126 设置 GOP 属性的 API

二 、多线程获取SMARTP的GOP模式数据和普通GOP模式数据

本次实验沿用之前双编码通道 + 多线程取流的方案,一路通道使用传统普通 GOP 模式,另一路开启 RK 平台优化的 SMARTP 智能 GOP 模式。两路使用完全一致的分辨率、码率、帧率与 QP 限制,同步采集、编码并保存码流文件,通过实际播放、解码日志、画面表现,直观对比两种 GOP 模式的差异。

RV1126 VI采集摄像头数据并同时编码SMARTP模式和普通GOP模式的编码码流流程

RV1126利用多线程同时获取普通GOP的VENC码流数据和SMARTP的码流数据一般如上图,分为8个步骤:分别是VI模块初始化、普通GOP的VENC模块初始化、智能SMARTP_GOP的VENC模块初始化、VI绑定普通GOP的VENC编码器节点、设置GOP属性为SMARTP属性、VI绑定SMARTP_GOP的VENC编码器节点、创建多线程获取普通GOP的VENC码流数据并保存、创建多线程获取SMART_GOP的VENC码流数据并保存。

    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. 两种 GOP 模式 VENC 模块初始化:

    VENC_CHN_ATTR_S common_gop_venc_chn_attr;

    ..................................

    RK_MPI_VENC_CreateChn(COMMON_GOP_VENC_CHN, &common_gop_venc_chn_attr);

    VENC_CHN_ATTR_S smartp_gop_venc_chn_attr;

    ..................................

    RK_MPI_VENC_CreateChn(SMARTP_VENC_CHN, &smartp_gop_venc_chn_attr);

注意:这里需要创建两个编码器层,分别是普通GOP模式编码器和SMART的GOP模式编码器。

​​​​​​​3. 设置 SMARTP VENC 模块 GOP 参数调节

复制代码
VENC_GOP_ATTR_S venc_gop_attr;

venc_gop_attr.enGopMode = VENC_GOPMODE_SMARTP; //设置GOP模式为SMARTP模式

venc_gop_attr.u32GopSize = 25; //设置短参考帧间隔是25

venc_gop_attr.s32IPQpDelta = 6; //设置I帧和P帧的差值是6

venc_gop_attr.s32ViQpDelta = 6;//设置虚拟I帧和P帧的差值是6

venc_gop_attr.u32BgInterval = 25 * 5; //设置长参考帧的长度是短参考帧的5倍

ret = RK_MPI_VENC_SetGopMode(SMARTP_GOP_VENC_CHN, &venc_gop_attr);

上面是 SMARTP GOP 模式一些参数的设置,设置完成调用 RK_MPI_VENC_SetGopMode 去调用

​​​​​​​4. VI 模块绑定普通 GOP VENC 模块和 SMARTP_GOP VENC 模块,下面是伪代码:

//VI 模块节点

复制代码
MPP_CHN_S vi_chn_s;

vi_chn_s.enModId = RK_ID_VI;

vi_chn_s.s32ChnId = VI_CHN_ID;

// 普通 GOP VENC 模块节点

复制代码
MPP_CHN_S common_gop_venc_chn_s;

common_gop_venc_chn_s.enModId = RK_ID_VENC;

common_gop_venc_chn_s.s32ChnId = COMMON_GOP_VENC_CHN;

ret = RK_MPI_SYS_Bind(&vi_chn_s, &common_gop_venc_chn_s);

//SMARTP_GOP VENC 模块

复制代码
MPP_CHN_S smartp_gop_venc_chn_s;

smartp_gop_venc_chn_s_s.enModId = RK_ID_VENC;

smartp_gop_venc_chn_s.s32ChnId = SMARTP_GOP_VENC_CHN;

ret = RK_MPI_SYS_Bind(&vi_chn_s, &smartp_gop_venc_chn_s);

​​​​​​​5. 创建多线程获取普通GOP的VENC码流数据并保存

开启一个线程去采集每一帧普通GOP的VENC模块数据,使用的API是RK_MPI_SYS_GetMediaBuffer 模块ID是RK_ID_VENC,通道号ID是普通GOP的 VENC创建ID号**。** 这个API伪代码如下

复制代码
while(1)

{

  .........................

  mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, COMMON_GOP_VENC_CHN, -1);

  fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, common_gop_h264_file);

.......................

}

​​​​​​​6. 多线程获取 SMARTP_GOP VENC 模块数据:

开启一个线程去采集每一帧SMARTP_GOP 的VENC模块数据,使用的API是RK_MPI_SYS_GetMediaBuffer 模块ID是RK_ID_VENC,通道号ID是SMARTP_GOP的VENC层**。这个** API 伪代码如下

复制代码
while(1)

{

  .........................

  mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, SMARTP_GOP_VENC_CHN, -1);

  fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, smartp_gop_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>

// RKMedia 多媒体框架核心API头文件
#include "rkmedia_api.h"

// ===================== 硬件通道宏定义 =====================
#define PIPE_ID               0       // VI视频管道编号,RV1126默认使用0号管道
#define VI_CHN_ID             0       // 摄像头采集通道编号

#define COMMON_GOP_VENC_CHN   0       // 普通NormalP GOP编码通道编号
#define SMARTP_GOP_VENC_CHN   1       // 智能SMARTP GOP编码通道编号

/**
 * @brief  普通GOP码流读取线程
 * @param  args 线程入参,本示例未使用
 * @retval NULL
 * @note   循环读取0号VENC通道数据,将H264裸流写入 test_common_gop.h264
 */
void * get_common_gop_thread(void * args)
{
    // 设置线程为分离态:线程退出后系统自动回收资源,无需主线程join等待
    pthread_detach(pthread_self());
    // 以读写模式打开文件,存储普通GOP编码后的H264码流
    FILE * common_gop_h264 = fopen("test_common_gop.h264", "w+");
    MEDIA_BUFFER mb = NULL;  // 媒体缓冲区句柄,用于接收VENC输出的帧数据

    // 循环持续拉取编码帧数据
    while(1)
    {
        /*
         * 阻塞式获取媒体缓冲区
         * 参数1:模块ID(视频编码VENC)
         * 参数2:编码通道号
         * 参数3:超时时间,-1表示永久阻塞等待数据
         */
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, COMMON_GOP_VENC_CHN, -1);
        // 缓冲区获取失败,代表取流异常,退出循环
        if(!mb)
        {
            printf("Get COMMON_GOP_VENC break...\n");
            break;
        }
        printf("Get COMMON_GOP_VENC Succeed...\n");

        // 读取缓冲区数据并写入本地文件
        // RK_MPI_MB_GetPtr:获取缓冲区数据首地址
        // RK_MPI_MB_GetSize:获取当前帧数据长度
        fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, common_gop_h264);

        // 重要:释放媒体缓冲区,防止内存池溢出、程序卡死
        RK_MPI_MB_ReleaseBuffer(mb);
    }
    return NULL;
}

/**
 * @brief  智能SMARTP GOP码流读取线程
 * @param  args 线程入参,本示例未使用
 * @retval NULL
 * @note   循环读取1号VENC通道数据,将SMARTP模式编码流写入 test_smartp_gop.h264
 */
void * get_smartp_gop_thread(void * args)
{
    pthread_detach(pthread_self());
    // 新建文件用于保存SMARTP智能GOP编码流
    FILE * smartp_gop_h264 = fopen("test_smartp_gop.h264", "w+");
    MEDIA_BUFFER mb = NULL;

    while(1)
    {
        // 阻塞获取SMARTP编码通道的帧数据
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, SMARTP_GOP_VENC_CHN, -1);
        if(!mb)
        {
            printf("Get SMARTP_GOP_VENC break...\n");
            break;
        }
        printf("Get SMARTP_GOP_VENC Succeed...\n");

        // 帧数据写入本地文件
        fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, smartp_gop_h264);
        // 释放缓冲区资源
        RK_MPI_MB_ReleaseBuffer(mb);
    }
    return NULL;
}

int main()
{
    int ret;
    // 初始化RKMedia整个多媒体系统,所有音视频模块使用前必须先调用
    RK_MPI_SYS_Init();

    // ===================== 第一步:初始化VI摄像头采集模块 =====================
    VI_CHN_ATTR_S vi_chn_attr;
    vi_chn_attr.pcVideoNode = "rkispp_scale0";    // 绑定板载摄像头设备节点
    vi_chn_attr.u32Width = 1920;                  // 采集图像宽度 1920
    vi_chn_attr.u32Height = 1080;                 // 采集图像高度 1080
    vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12;       // 图像格式NV12,RK平台摄像头默认输出格式
    vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // 缓冲区类型为内存映射,提升采集效率
    vi_chn_attr.u32BufCnt = 3;                    // 开辟3帧缓存,防止画面丢帧、卡顿
    vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL; // VI工作在常规采集模式

    // 设置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");
    }

    // 启用VI通道,开始摄像头图像采集
    ret |= RK_MPI_VI_EnableChn(PIPE_ID, VI_CHN_ID);
    if (ret)
    {
        printf("VI_CHN_ATTR Enable Failed.....\n");
    }
    else
    {
        printf("VI_CHN_ATTR Enable Success.....\n");
    }

    // ===================== 第二步:创建【普通GOP】编码通道 =====================
    VENC_CHN_ATTR_S common_gop_venc_attr;
    // 编码基础属性配置
    common_gop_venc_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;      // 编码格式为H264
    common_gop_venc_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;      // 输入图像格式NV12,和VI输出对齐
    common_gop_venc_attr.stVencAttr.u32PicWidth = 1920;               // 编码画面宽度
    common_gop_venc_attr.stVencAttr.u32PicHeight = 1080;              // 编码画面高度
    common_gop_venc_attr.stVencAttr.u32VirWidth = 1920;                // 硬件虚拟宽度,此处与实际宽度一致
    common_gop_venc_attr.stVencAttr.u32VirHeight = 1080;               // 硬件虚拟高度
    common_gop_venc_attr.stVencAttr.u32Profile = 66;                   // H264 Profile 66 = Baseline基线模式,监控常用
    common_gop_venc_attr.stVencAttr.enRotation = VENC_ROTATION_0;      // 画面不做旋转处理

    // 码率控制 & GOP基础配置
    common_gop_venc_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;     // 码率模式:CBR恒定码率
    common_gop_venc_attr.stRcAttr.stH264Cbr.u32Gop = 25;               // GOP长度25帧,25fps下每秒1个I帧
    common_gop_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;   // 源帧率分子
    common_gop_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;    // 源帧率分母,组合为25fps
    common_gop_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;  // 编码输出帧率分子
    common_gop_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;   // 编码输出帧率分母
    common_gop_venc_attr.stRcAttr.stH264Cbr.u32BitRate = 8388608;      // 恒定码率 8Mbps

    // 创建普通GOP编码通道,默认走NormalP模式
    ret = RK_MPI_VENC_CreateChn(COMMON_GOP_VENC_CHN, &common_gop_venc_attr);
    if (ret)
    {
        printf("Set Common_Gop Venc Attr Failed.....\n");
    }
    else
    {
        printf("Set Common_Gop Venc Attr Success.....\n");
    }

    // ===================== 第三步:创建【SMARTP智能GOP】编码通道 =====================
    // 基础编码参数与普通通道完全一致,保证对照实验条件统一
    VENC_CHN_ATTR_S smartp_gop_venc_attr;
    smartp_gop_venc_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;
    smartp_gop_venc_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;
    smartp_gop_venc_attr.stVencAttr.u32PicWidth = 1920;
    smartp_gop_venc_attr.stVencAttr.u32PicHeight = 1080;
    smartp_gop_venc_attr.stVencAttr.u32VirWidth = 1920;
    smartp_gop_venc_attr.stVencAttr.u32VirHeight = 1080;
    smartp_gop_venc_attr.stVencAttr.u32Profile = 66;
    smartp_gop_venc_attr.stVencAttr.enRotation = VENC_ROTATION_0;

    // 码率、帧率、基础GOP间隔和普通通道保持一致
    smartp_gop_venc_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
    smartp_gop_venc_attr.stRcAttr.stH264Cbr.u32Gop = 25;
    smartp_gop_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;
    smartp_gop_venc_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;
    smartp_gop_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;
    smartp_gop_venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
    smartp_gop_venc_attr.stRcAttr.stH264Cbr.u32BitRate = 8388608;

    // 创建SMARTP编码通道
    ret = RK_MPI_VENC_CreateChn(SMARTP_GOP_VENC_CHN, &smartp_gop_venc_attr);
    if (ret)
    {
        printf("Set SMARTP_Gop Venc Attr Failed.....\n");
    }
    else
    {
        printf("Set SMARTP_Gop Venc Attr Success.....\n");
    }

    // ===================== 第四步:配置SMARTP专属GOP模式参数(核心) =====================
    VENC_GOP_ATTR_S venc_gop_attr_s;
    venc_gop_attr_s.enGopMode = VENC_GOPMODE_SMARTP;   // 指定GOP工作模式为智能SMARTP
    venc_gop_attr_s.u32GopSize = 25;                   // 主I帧间隔帧数
    venc_gop_attr_s.u32BgInterval = 25 * 5;            // 虚拟I帧(VI帧)间隔,每125帧插入一个虚拟I帧
    venc_gop_attr_s.s32IPQpDelta = 6;                  // I帧与P帧的QP偏移值,控制I/P帧画质差异
    venc_gop_attr_s.s32ViQpDelta = 6;                  // 虚拟I帧与普通P帧的QP偏移值

    // 把GOP模式参数生效到SMARTP编码通道
    ret = RK_MPI_VENC_SetGopMode(SMARTP_GOP_VENC_CHN, &venc_gop_attr_s);
    if (ret)
    {
        printf("Set SMARTP_Gop_Mode Failed.....\n");
    }
    else
    {
        printf("Set SMARTP_Gop_Mode Success.....\n");
    }

    // ===================== 第五步:模块绑定,构建数据流链路 =====================
    // 定义VI采集通道信息
    MPP_CHN_S vi_chn_s;
    vi_chn_s.enModId = RK_ID_VI;
    vi_chn_s.s32ChnId = VI_CHN_ID;

    // 定义普通GOP编码通道信息
    MPP_CHN_S common_gop_venc_chn_s;
    common_gop_venc_chn_s.enModId = RK_ID_VENC;
    common_gop_venc_chn_s.s32ChnId = COMMON_GOP_VENC_CHN;

    // 定义SMARTP编码通道信息
    MPP_CHN_S smartp_gop_venc_chn_s;
    smartp_gop_venc_chn_s.enModId = RK_ID_VENC;
    smartp_gop_venc_chn_s.s32ChnId = SMARTP_GOP_VENC_CHN;

    // VI通道数据绑定到普通GOP编码通道:一路图像流向普通编码
    ret = RK_MPI_SYS_Bind(&vi_chn_s, &common_gop_venc_chn_s);
    if (ret)
    {
        printf("Bind Vi And Common_Gop Failed.....\n");
    }
    else
    {
        printf("Bind Vi And Common_Gop Success.....\n");
    }

    // VI通道数据同时绑定到SMARTP编码通道:一源双出,两路编码同步工作
    ret = RK_MPI_SYS_Bind(&vi_chn_s, &smartp_gop_venc_chn_s);
    if (ret)
    {
        printf("Bind Vi And Smartp_Gop Failed.....\n");
    }
    else
    {
        printf("Bind Vi And Smartp_Gop Success.....\n");
    }

    // ===================== 第六步:创建多线程,并行拉取两路码流 =====================
    pthread_t common_gop_pid, smartp_gop_pid;
    // 启动普通GOP取流线程
    pthread_create(&common_gop_pid, NULL, get_common_gop_thread, NULL);
    // 启动SMARTP GOP取流线程
    pthread_create(&smartp_gop_pid, NULL, get_smartp_gop_thread, NULL);

    // 主线程循环休眠,保持程序持续运行
    while(1)
    {
        sleep(2);
    }

    // ===================== 第七步:资源释放(正常退出时执行) =====================
    // 解除模块绑定
    RK_MPI_SYS_UnBind(&vi_chn_s, &common_gop_venc_chn_s);
    RK_MPI_SYS_UnBind(&vi_chn_s, &smartp_gop_venc_chn_s);
    // 销毁两路编码通道
    RK_MPI_VENC_DestroyChn(COMMON_GOP_VENC_CHN);
    RK_MPI_VENC_DestroyChn(SMARTP_GOP_VENC_CHN);
    // 关闭VI采集通道
    RK_MPI_VI_DisableChn(PIPE_ID,VI_CHN_ID);

    return 0;
}

编译和移植以及运行流程参考前面的博客内容

四、编译运行与效果对比

1. 程序运行

将代码编译后在 RV1126 设备上执行,终端会持续打印取流成功日志。运行一段时间后终止程序,设备目录下会生成两个文件:

  • normal_gop.h264:普通 NormalP GOP 码流
  • smartp_gop.h264:SMARTP 智能 GOP 码流

2. 文件体积对比

在码率、时长完全一致的前提下,我对比了两个文件的大小: 普通 GOP 模式因为每 25 帧就生成一个体积较大的真 I 帧,文件整体尺寸偏大; 而 SMARTP 模式拉长了主 I 帧间隔,依靠虚拟 I 帧替代部分真 I 帧,同时借助长参考帧压缩静态画面,同等时长下文件体积明显更小,码率利用率更高。

3. 播放与解码日志对比

使用 FFmpeg 分别解码两个文件,观察解码日志和画面表现:

  1. 普通 GOP 模式 帧参考关系简单,播放器兼容性拉满,解码日志基本无报错。但能明显看到画面呼吸效应:每次 I 帧刷新时,画面亮度、细节会有轻微跳变;画面出现大幅度运动时,局部马赛克也会略多一些。

  2. SMARTP 智能 GOP 模式 解码日志仅有少量轻微纠错提示,整体解码稳定。静态画面下几乎看不到呼吸效应,画面过渡更加平滑;动态场景中,运动物体的边缘细节保留更好,马赛克现象得到明显改善。

4. 场景化体验总结

  • 静态画面(墙面、静物):SMARTP 优势极大,画面更稳定、码率消耗更低;
  • 动态画面(人物走动、物体移动):两者都能正常播放,SMARTP 细节表现更优;
  • 剧烈抖动画面(摄像头晃动):普通 GOP 模式稳定性略胜一筹。

五、实验总结与项目选型建议

通过这次多线程双路取流的对比实验,我们把抽象的 GOP 理论落到了实际效果上,也验证了两种模式的设计初衷。

  1. 普通 NormalP GOP 模式 结构简单、延迟最低、全平台兼容,是最稳妥的选择。如果项目追求极致兼容性、低延迟,或是摄像头存在频繁晃动的场景,优先使用该模式。缺点是码率利用率一般,静态画面存在呼吸效应。

  2. SMARTP 智能 GOP 模式 是 RK 平台针对监控场景深度优化的方案,也是商用 IPC 设备的主流选择。针对固定摄像头、背景长期静止的场景,它既能节省存储空间与带宽,又能提升动态画面的清晰度。只要项目设备不需要对接老旧终端,首选 SMARTP 模式。

  3. 结合 QP 参数的搭配思路 本次实验两路通道都做了 QP 区间限制,也再次印证了之前的结论:GOP 模式决定编码的整体框架与码率利用率,QP 参数守住画质的底线。在实际项目中,两者需要配合调试,才能在带宽、存储、画质三者之间找到最优解。

相关推荐
huangdong_2 小时前
京东商品图片与视频采集技术深度解析:m3u8视频合并、SKU图提取
经验分享·音视频
EasyDSS4 小时前
私有化音视频系统/视频直播点播EasyDSS一站式视频平台重构企业全域数字化协作
重构·音视频
ai产品老杨4 小时前
解耦视频高并发与边缘计算AI布控:基于Docker的高性能安防平台,破局GB28181/RTSP协议兼容与源码交付痛点
人工智能·音视频·边缘计算
luoyayun3616 小时前
Qt + FFmpeg 实战:音频静音段检测
qt·ffmpeg·音视频·静音段检测
Johnstons6 小时前
网页加载到一半卡住?视频看到关键处花屏?可能是丢包在作祟
开发语言·php·音视频·弱网测试·网络损伤
七夜zippoe6 小时前
OpenClaw 节点摄像头:远程拍照与视频录制实
音视频·视频录制·openclaw·节点摄像头·远程拍照
jinglong.zha6 小时前
AI视频全流程实战:广告/动画/短剧都适用,解决角色一致性+后期合成难题
人工智能·ai·音视频·光照贴图·叙事照片
qq_366566507 小时前
短视频批量翻译+配音自动化:Python脚本处理TikTok/Reels/Shorts全流程
python·chatgpt·自动化·音视频·媒体
MemoriKu7 小时前
Flutter 相册 APP 视频模态稳定化实战:从远端重构冲突到真机 Smoke Test
人工智能·python·flutter·机器学习·重构·音视频·新人首发