前面我们弄懂了 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 模式工程选型总结
这里我结合实际开发经验,给大家整理一个最实用的选型结论:
- NormalP 普通模式:通用性最强、零延迟、兼容性最好。适合常规室内监控、固定录像、基础预览,是新手开发、基础实验的默认首选。
- SMARTP 智能模式:监控场景最优解。静态省码率、动态保画质,兼顾清晰度和存储带宽,是商用 IPC 设备的主流方案。
- 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码流数据并保存。
-
- 初始化 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);
-
两种 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 分别解码两个文件,观察解码日志和画面表现:
-
普通 GOP 模式 帧参考关系简单,播放器兼容性拉满,解码日志基本无报错。但能明显看到画面呼吸效应:每次 I 帧刷新时,画面亮度、细节会有轻微跳变;画面出现大幅度运动时,局部马赛克也会略多一些。
-
SMARTP 智能 GOP 模式 解码日志仅有少量轻微纠错提示,整体解码稳定。静态画面下几乎看不到呼吸效应,画面过渡更加平滑;动态场景中,运动物体的边缘细节保留更好,马赛克现象得到明显改善。

4. 场景化体验总结
- 静态画面(墙面、静物):SMARTP 优势极大,画面更稳定、码率消耗更低;
- 动态画面(人物走动、物体移动):两者都能正常播放,SMARTP 细节表现更优;
- 剧烈抖动画面(摄像头晃动):普通 GOP 模式稳定性略胜一筹。
五、实验总结与项目选型建议
通过这次多线程双路取流的对比实验,我们把抽象的 GOP 理论落到了实际效果上,也验证了两种模式的设计初衷。
-
普通 NormalP GOP 模式 结构简单、延迟最低、全平台兼容,是最稳妥的选择。如果项目追求极致兼容性、低延迟,或是摄像头存在频繁晃动的场景,优先使用该模式。缺点是码率利用率一般,静态画面存在呼吸效应。
-
SMARTP 智能 GOP 模式 是 RK 平台针对监控场景深度优化的方案,也是商用 IPC 设备的主流选择。针对固定摄像头、背景长期静止的场景,它既能节省存储空间与带宽,又能提升动态画面的清晰度。只要项目设备不需要对接老旧终端,首选 SMARTP 模式。
-
结合 QP 参数的搭配思路 本次实验两路通道都做了 QP 区间限制,也再次印证了之前的结论:GOP 模式决定编码的整体框架与码率利用率,QP 参数守住画质的底线。在实际项目中,两者需要配合调试,才能在带宽、存储、画质三者之间找到最优解。