19 / 32 编码基础与编码器实战

第 18 课:视频为什么能被压缩这么狠 —— I/P/B 帧、关键帧、GOP

# 第 18 课:视频为什么能被压缩这么狠 —— I/P/B 帧、关键帧、GOP

嘿,我是小方。 前面四个模块,我们把一帧画面、一段声音,从采集一路走到了"交给编码"那一刻。现在进入 模块 5:压缩(编码)。 但在碰编码器代码之前,得先回答一个"凭什么"的问题:第 5 课我们算过,1080p60 的裸视频每秒好几个 GB,根本没法推流、也存不起;可你天天看的直播,码率才几 Mbps —— 压到了原来的百分之零点几,还基本看不出损失。凭什么能压这么狠? 这一课不写编码器(那是后面几课的事),但会把"凭什么能压 + 到底怎么压 + 画质损失从哪来"讲透:两种冗余、I/P/B 三种帧、块匹配、DCT+量化+熵编码这条真实流水线、关键帧、GOP。这是理解编码器的地基,也是你以后调"关键帧间隔""B 帧""码率"这些设置时,心里有底的前提。

📖 先记五个词(全课反复出现):

  • I / P / B 帧:三种视频帧。I 自给自足,P 参考前面,B 参考前后(18.4)。
  • 关键帧(keyframe):能独立解码的帧(就是 I 帧),是"从头开始"的落脚点(18.9)。
  • GOP:一个关键帧到下一个关键帧之间的一组帧(18.10)。
  • 码率(bitrate):每秒用多少比特 = 画质/体积的总预算(18.8)。
  • 有损:视频编码会故意扔掉你眼睛注意不到的信息(不像 ZIP 无损)——这正是能压这么狠的原因(18.8)。

🧭 顺便认个门:H.264 是一个编码标准(一套"该怎么压"的规矩);x264 / NVENC 是它的实现(分别是软件、显卡实现,第 19、20 课讲)。还有更新的 HEVC(H.265)、AV1,压得更狠但更吃算力。这一课讲的原理,这些标准全都通用。


# 18.1 回到第 5 课:为什么必须压缩

📍我们在哪:流水线到了"编码"这一站。开讲前先把体积感找回来 —— 1080p60 裸视频每秒几百 MB,凭什么能压到直播那点码率、还基本看不出损失?

先把体积感找回来。拿最常见的 1080p60 直播算一笔账:

为什么必须压缩

  • 未压缩(裸 RGB):每个像素 3 字节,1920 × 1080 × 3 = 6.2 MB 一帧,× 60 帧 ≈ 356 MB / 秒(约 2.85 Gbps)。
  • H.264 直播码率:典型 6 Mbps ≈ 0.75 MB / 秒

压缩比 ≈ 356 / 0.75 ≈ 475 : 1 —— 压到了原来的不到 0.3%,画面却基本看不出损失。这不是魔法,是因为裸视频里全是"冗余":大量本可以"算出来、猜出来"的信息,被完整地、重复地存了下来。编码,就是把这些冗余省掉。

冗余分两种,分别对应两种压缩手段。


# 18.2 压缩的两把钥匙:空间冗余 + 时间冗余

📍接上一节:上一节说"能压是因为裸视频全是冗余"。冗余到底长什么样?这里拆成空间、时间两种,分别对应两把压缩钥匙。

两种冗余

  • ① 空间冗余(一帧"之内"):随便看一帧画面,一大片蓝天几乎是同一个颜色,一块草地也差不多。相邻像素高度相似,没必要每个像素都完整地存一遍。对付它的手段叫帧内压缩(和你熟悉的 JPEG 图片压缩同理)。
  • ② 时间冗余(一帧"之间"):视频是连续的帧,相邻两帧之间,背景基本不动,只有一小块在变(人在动、球在飞)。没必要每一帧都从头存一整幅,只存"变化"就够了。对付它的手段叫帧间压缩

视频编码 = 同时吃掉这两种冗余。空间冗余靠帧内压缩,时间冗余靠帧间压缩,而后者正是视频能比图片压得狠得多的关键 —— 一段视频里,绝大多数帧和前后帧只差一点点。下面看它怎么用"三种帧"把时间冗余榨到极致。


# 18.3 两种压缩:自己压(帧内)vs 参考别人(帧间)

📍承上启下:两种冗余,对应两种压法 —— 自己压(帧内)对付空间冗余,参考别人(帧间)对付时间冗余。这一拆,就引出了三种帧。

帧内 vs 帧间

  • 帧内压缩(intra):只用"这一帧自己"的信息压缩,不看别的帧 —— 把相近的像素/区块合并、量化(和 JPEG 一个原理,具体几步 18.7 拆开)。优点:自给自足,拿到它就能独立解码出一整幅画。缺点:压不了时间冗余,体积偏大。→ 这就是 I 帧
  • 帧间压缩(inter):参考"别的帧",只存"差异"——具体是两样:运动矢量(这一块从哪儿移到了哪儿)和残差(移过来后还差多少)。优点:背景不动的部分几乎不占体积,压缩率极高。→ 这就是 P 帧、B 帧

所以"三种帧"其实就是这两招的组合:纯帧内的 I 帧,和帧间的 P、B 帧。逐个看。


# 18.4 I / P / B:三种帧,三种代价

📍接上一节:上一节的"帧内 / 帧间"两招,组合出三种帧。逐个看它们各参考谁、多大、能不能独立解码 —— 顺带撞见 pts/dts。

I/P/B 三种帧

类型 参考谁 体积 能独立解码?
I 帧 帧内 谁都不靠,自己完整 最大
P 帧 帧间(前) 参考前面的帧,存差异 否(要先有它依赖的帧)
B 帧 帧间(前后) 参考前 + 后,双向预测 最小
  • I 帧(Intra-coded):一幅完整的画,不依赖任何别的帧。它是整段视频的"锚点"(18.9 细讲)。
  • P 帧(Predicted):只记录"和前面某帧的差异"。因为背景通常不动,P 帧往往很小。
  • B 帧(Bi-directional):同时参考前面和后面的帧来预测,所以能压得最狠。但它有个代价:要等它依赖的"后面那帧"先到,才能解出来

这就引出一个关键副作用:B 帧让"解码顺序"不等于"播放顺序"。比如播放顺序是 I B B P,但解码时必须先解出 P(因为两个 B 都要参考它),顺序变成 I P B B。于是每个编码包必须同时带两个时间戳:pts(显示时刻)dts(解码时刻)。记住这一点,18.11 你会在 OBS 的 encoder_packet 结构里亲眼看到这两个字段


# 18.5 具体点:一个球在动,P 帧到底存了什么

📍承上启下:上一节说 P 帧"只存差异",这话太抽象。用一个球在背景上移动的例子,看它到底存了哪两样东西。

抽象的"存差异"到底是什么?举个最直观的例子:一个球,在不动的背景上从左移到右。

一个球在动

编码第 N+1 帧(P 帧)时,它这么干:

  • 背景那一大块:和第 N 帧一模一样 → 只需记一句"和前一帧相同",几乎 0 字节
  • 球那一小块:记两样 —— 运动矢量(比如"右移 120 像素")+ 残差(移过来后还差的一点点光影)。

整帧几十万像素,这个 P 帧可能只花了"一个运动矢量 + 一点残差"的体积。 时间冗余就是这么被榨干的。这也顺带解释了两个你能直接感受到的现象:

  • 画面越"静"(背景不动、镜头不晃),P/B 帧越小,码率越省 —— 所以静态画面(比如挂机、PPT)直播很省流量、很清晰。
  • 画面越"乱"(粒子特效、快速运动、镜头猛晃),帧间差异越大,P/B 帧越大 —— 同样码率下,画面就越糊(编码器没有足够码率去存这么多"差异",只能牺牲清晰度)。

# 18.6 编码器怎么"发现"球动了?—— 块匹配

📍接上一节:上一节说 P 帧存"运动矢量",可编码器又不认识"球",它怎么知道球移到哪了?答案是很机械的块匹配。

上面说 P 帧存"运动矢量",可编码器怎么知道球从这儿移到了那儿?它又不认识"球"。答案是一套很机械但很有效的办法:块匹配(motion estimation)

块匹配

  • ① 切块:先把画面切成一个个小方块(叫宏块,比如 16×16 像素)。编码器不认识"球",它只认识"方块"。
  • ② 搜索:对当前帧的每一个块,拿去参考帧的附近区域里"搜"——找那个和它最像的块。
  • ③ 命中:找到了最像的块 → 两者的位移就是运动矢量,再记下"移过来还差多少"的残差。极省。
  • ④ 搜不到:如果一块是全新内容(比如突然入画的东西),参考帧里根本没有像它的块 → 这一块就退回帧内压缩,自己压自己。

所以"运动矢量"不是凭空来的 —— 是编码器拿一块画面,在前一帧里**"按图索骥"搜出来的**。你也能想到:这个搜索非常费算力(每块都要在一大片区域里比对无数次)。这正是"编码为什么吃 CPU / GPU"的一大原因,也是软件编码(x264)和显卡编码(NVENC)拉开差距的地方(第 20 课细说)。


# 18.7 编码一帧,到底是哪几步?—— 完整流水线

📍承上启下:前面讲的都是"参考谁、存什么差异"的策略。现在落到实处 —— 一帧到底怎么一步步变成字节:DCT、量化、熵编码这条真实流水线。

前面讲的都是"策略"(参考谁、存什么差异)。那么落到实处,一帧到底怎么变成一串字节的?这里有一条真实的流水线。OBS 自己不做这些(x264/NVENC 内部做),但你该知道它长什么样 —— 这样你才明白 DCT、量化这些词在说什么。

编码流水线

I 帧(帧内)四步:

  1. ① 分块:把画面切成小方块(宏块)。
  2. ② DCT 变换:把一块像素换个说法 —— 不再逐点存颜色,而是存"这块里低频(大色块)有多少 + 高频(细节)有多少"。(DCT = 离散余弦变换,你不用会算,只要知道它把"像素"翻译成了"频率成分"。)
  3. ③ 量化:把那些不敏感的高频成分粗略化,甚至直接归零。★ 有损,就发生在这一步(下一节专讲)。
  4. ④ 熵编码:把剩下的数据无损地紧凑打包(重复的、常见的用更少的位表示,哈夫曼/算术编码)。→ 字节。

P / B 帧(帧间)三步:

  1. ① 块匹配:找运动矢量(就是 18.6)。
  2. ② 算残差:实际画面 − 预测画面 = 还差多少
  3. ③ 残差走后三步:把残差再送进 DCT → 量化 → 熵编码(和 I 帧后三步一模一样)。→ 运动矢量 + 字节。

两条线,后半段其实是同一段:都靠 DCT + 量化 + 熵编码 把数据压小。区别只在开头:I 帧直接压像素,P/B 帧压的是"残差"。两个关键点先记住:

  • 熵编码是【无损】的 —— 它只是把数据打包得更紧凑,能完全还原。
  • 真正"扔东西"的是量化 —— 这就是画质损失的唯一来源。下一节把它讲透。

# 18.8 量化:画质损失从哪来,码率又是什么

📍接上一节:上一节说"真正扔东西的是量化"。这一节就盯住它 —— 画质损失到底从哪来、码率越低为什么越糊。

这一节回答两个新手最关心、却常被跳过的问题:压缩到底损失了什么?为什么码率越低越糊?

量化与有损

# 损失来自"量化"

上一节 DCT 把一块像素拆成了"低频(大色块)+ 高频(细节)"。量化这一步,就是把高频细节粗略化、甚至直接归零——扔掉的,正是眼睛最不敏感的那部分。量化越狠,扔得越多,数据越少(体积越小),但画面也越糊。这就是视频编码"有损"的唯一来源。

# 码率 = 每秒的"字节预算"

码率(bitrate),就是你给编码器的"每秒能用多少比特"的预算。编码器必须把画面塞进这个预算里:

  • 预算多(码率高) → 量化可以轻 → 细节留得多 → 清晰;
  • 预算少(码率低) → 量化必须狠 → 细节扔得多 →

这就是"码率越低越糊"的根源:不是分辨率变小了,而是高频细节被量化扔掉了。(第 21 课细讲 CBR/VBR 怎么分配这份预算。)

# 有损 vs 无损

  • ZIP / PNG 是【无损】:能一字节不差地还原原文件。
  • 视频编码是【有损】:扔掉了看不出的东西,不能完全还原原始画面 —— 但正因为敢扔,才换来了 475:1 这种无损压缩根本做不到的压缩率。

# 凭什么敢扔?—— 因为人眼有"盲点"(回连第 3 课)

编码器敢扔东西,是因为人眼本身就有察觉不到的地方,专挑这些下手:

  • 眼睛对"亮度"比"颜色"敏感 → 颜色可以少存一些 → 这就是第 3 课的"色度下采样 4:2:0"!它在采集阶段就已经悄悄做了一次有损压缩(把色度分辨率砍一半),你却看不出来。
  • 眼睛对"高频细节"不敏感 → 量化把细节粗略化。

两招都是"专挑你注意不到的地方下手"——所以压缩率高得吓人,你却基本看不出损失。理解了这一点,你就理解了整个"有损压缩"的哲学:不是把画面等比缩小,而是精准地扔掉你察觉不到的信息。


# 18.9 关键帧(keyframe)= 能"从头解码"的落脚点

📍承上启下:压缩讲完了,I 帧还有第二个身份 —— 关键帧。它是拖进度条、直播中途加入、丢包恢复都要落脚的锚点。

I 帧还有一个和压缩同等重要的身份:关键帧(keyframe)。因为它能独立解码,它就是整段视频里可以"从头开始"的落脚点。

关键帧 = 随机访问点

想想一串 I P P P P P P:每个 P 帧都依赖前一帧,一路回溯,最终都得追到最左边那个 I 帧。没有 I 帧,后面的 P/B 帧全都无从解起。 于是关键帧成了三件事的必需锚点:

  • 拖进度条(seek):你跳到视频某处,播放器得从最近的关键帧开始解码(所以有时拖动后画面会"顿一下"再出来)。
  • 直播中途加入:观众刚点进你的直播间,得等下一个关键帧到来,才能开始有画面。
  • 错误恢复:网络丢包导致花屏后,下一个关键帧会把画面"刷新"、重新来过(因为它不依赖任何被丢坏的帧)。

所以关键帧不只关乎压缩,更是"随机访问 / 容错"的锚点。 这也解释了直播为什么常设"每 2 秒一个关键帧"—— 让观众加入、断线重连都不用等太久(设太稀,新观众可能要黑屏好几秒)。


# 18.10 GOP:一组图片(Group of Pictures)

📍接上一节:一个关键帧到下一个关键帧之间的那组帧,就是 GOP。它多长是个权衡 —— 也就是你在 OBS 里填的"关键帧间隔"。

从一个关键帧,到下一个关键帧之前,这一整组帧(I P P P …),叫一个 GOP(Group of Pictures)。它的长度,就是"关键帧间隔"。

GOP 结构

I P P P P P | I P P P P P | … —— 每个 GOP 以一个 I 帧打头,后面跟一串 P/B 帧,然后下一个 I 帧开启新 GOP。GOP 该多长,是一个权衡:

  • GOP 长(关键帧稀):更省体积(I 帧大,越少越省);但拖动/加入/容错更慢(要等更久才有关键帧),而且一处丢包,花屏会传播更久。
  • GOP 短(关键帧密):拖动/加入/容错更快更稳,花屏能很快被下一个关键帧刷新;但更占体积(关键帧多)。

直播场景对"新观众别黑屏太久、断线快恢复"更敏感,所以常用较短的 GOP(2 秒一个关键帧);而离线存档(不在乎 seek 延迟)可以用长 GOP 换更小的文件。2 秒 × 帧率 = GOP 帧数(60fps 就是 120 帧)。这就是 OBS 设置里那个"关键帧间隔(秒)"。 你填"秒",编码器把它换算成"帧" —— 下一节就在 x264 源码里看到这一步。


# 18.11 源码印证:这些概念,在 OBS 里落在哪

📍收束:原理讲完,回到 OBS 源码 —— I/P/B、关键帧、GOP 不是纸上概念,看它们落在 encoder_packet、x264 参数、RTMP 丢帧逻辑的哪几行。

OBS 自己不实现编码(H.264 的活儿交给 x264、NVENC 这些编码器,下一课讲接口)。但 I/P/B、关键帧、GOP 这些概念,OBS 全都要"碰到"—— 因为它要配置编码器,还要编码结果去跟网络、跟观众打交道。

OBS 源码印证

# ① 编码器吐出的每个包,都带着这些概念

编码器每编好一帧,就吐出一个 encoder_packet(obs-encoder.h:99)。看它的字段,这一课的概念全在里面:

// libobs/obs-encoder.h:99(节选)
struct encoder_packet {
	uint8_t *data;      // 编码后的字节
	size_t   size;
	int64_t  pts;       // ★ 显示时刻(Presentation)
	int64_t  dts;       // ★ 解码时刻(Decode)—— 有 B 帧时,dts ≠ pts
	...
	bool keyframe;      // ★ 是不是关键帧(I 帧)
	...
	int priority;       // 包的重要度(关键帧最高)
	int drop_priority;  // ★ 若要丢它,下一个包必须≥这个优先级,才能续上
};
1
2
3
4
5
6
7
8
9
10
11
12

pts / dts 分开存,正是 18.4 说的 B 帧重排(解码顺序≠显示顺序);keyframe 标记哪些是 I 帧;drop_priority 那句注释(obs-encoder.h:130)——"若要丢它,下一个包必须≥这个优先级才能续上"——就是 18.9 那个"丢了 P 帧就得等下一个关键帧"的代码化表达。

# ② 你设的"关键帧间隔",怎么变成 GOP 长度

18.10 说"填秒、换算成帧",这一步就在 x264 插件里(obs-x264.c:405):

// plugins/obs-x264/obs-x264.c:405
if (keyint_sec)
	obsx264->params.i_keyint_max = keyint_sec * voi->fps_num / voi->fps_den;
1
2
3

keyint_sec(你填的秒数)× 帧率 = i_keyint_max —— 这正是 x264 的"两个关键帧之间最多多少帧",也就是 GOP 的长度上限。旁边还有 B 帧数(obs-x264.c:429:i_bframe = bf)、码率(rc.i_bitrate,下一课细讲)。这些"旋钮",就是你在 OBS 编码设置里调的那几项。

# ③ 关键帧不只为压缩 —— 网络卡了,OBS 靠它决定"丢谁"

这是关键帧最"实战"的用途。推流时网络跟不上、发送缓冲积压,OBS 必须丢一些帧来追上。丢谁?看 RTMP 输出的丢帧逻辑(rtmp-stream.c:1440):

// plugins/obs-outputs/rtmp-stream.c:1440(节选)
/* do not drop audio data or video keyframes */
if (packet.type == OBS_ENCODER_AUDIO || packet.drop_priority >= highest_priority) {
	deque_push_back(&new_buf, &packet, sizeof(packet));   // 保留:音频 + 关键帧
} else {
	num_frames_dropped++;
	obs_encoder_packet_release(&packet);                  // 丢弃:普通 P/B 帧
}
1
2
3
4
5
6
7
8

音频和关键帧优先保留,普通 P/B 帧优先丢弃。 而且一旦开始丢,会一直丢到"下一个能独立解码的关键帧"才敢续上(:1641drop_priority < min_priority 判断)——因为丢了一个 P 帧,后面依赖它的帧全都会花屏,只有等一个关键帧来"重置"。

看到没:I/P/B、关键帧、GOP,不是纸上概念 —— 它们是 OBS 每天在跟网络、跟观众打交道时的筹码。 你现在理解了原理,以后调"关键帧间隔""B 帧""码率",就知道每一项在动什么、会带来什么权衡。


# 18.12 本课小结

  • 为什么能狠压:1080p60 裸视频 ≈ 356 MB/s,直播码率 ≈ 0.75 MB/s,压缩比约 475:1。靠的是消除冗余 + 有损地扔掉看不出的东西。
  • 两种冗余:空间冗余(一帧内相邻像素相似,靠帧内压缩/ 类 JPEG)+ 时间冗余(相邻帧大部分没变,靠帧间压缩/ 存差异)。视频比图片压得狠,主要赢在时间冗余。
  • I / P / B 三种帧:I(帧内,自给自足,能独立解码,最大)、P(参考前帧存差异,小)、B(参考前后双向,最小,但需重排)。B 帧导致解码顺序 ≠ 显示顺序,所以包要同时带 ptsdts
  • 帧间存什么 + 怎么找:运动矢量(往哪移)+ 残差(还差多少);靠块匹配(切宏块 → 在参考帧搜最像的块)找出运动矢量,搜不到就退回帧内;搜索费算力。
  • 编码一帧的流水线:I 帧 = 分块 → DCT(像素→频率)→ 量化 → 熵编码;P/B 帧 = 块匹配 → 算残差 → 残差走 DCT/量化/熵编码熵编码无损,量化有损
  • 有损与码率:损失只来自量化(扔高频细节)。码率 = 每秒字节预算,预算少→量化狠→糊(码率越低越糊的根源)。视频有损(≠ ZIP 无损),敢扔是因为人眼盲点:亮度>颜色(→ 色度下采样 4:2:0,第 3 课采集时就做了)、高频不敏感(→ 量化)。
  • 关键帧(= I 帧)= 随机访问 / 容错锚点:seek、直播中途加入、丢包恢复,都必须从一个关键帧开始。直播常设"2 秒一个关键帧"。
  • GOP:一个关键帧到下一个关键帧之间的一组帧;长度 = 关键帧间隔。长 GOP 省体积但 seek/容错慢,短 GOP 反之。
  • OBS 源码印证:encoder_packet(obs-encoder.h:99)带 pts/dts(B 帧重排)、keyframepriority/drop_priority;keyint_sec × fps → i_keyint_max(GOP,obs-x264.c:405),bf → i_bframe(:429);拥塞时 RTMP 先丢非关键帧、丢到下一个关键帧才续(rtmp-stream.c:1440)。

# 18.13 动手 / 观察(本课作业)

  1. 看关键帧间隔设置:OBS →"设置 → 输出"(高级模式)→ 视频编码器,找到"关键帧间隔(秒)"。想想:设成 2、帧率 60,GOP 是多少帧?(答:120。)设成 0 是什么意思?(交给编码器用它自己的默认,x264 约 fps×10。)
  2. 感受静 vs 乱的码率:同样码率下,分别录一段"静态桌面"和"快速晃动的游戏/粒子"。对比文件清晰度 —— 为什么乱的那段更糊?(提示:P/B 帧的差异体积 + 量化。)
  3. 理解"码率越低越糊":用自己的话讲清 —— 降低码率,画面糊,到底是分辨率变了,还是别的?(答:分辨率没变,是量化更狠、高频细节被扔了。)
  4. B 帧与 pts/dts:为什么有了 B 帧,一个视频包就必须同时带"显示时刻 pts"和"解码时刻 dts"?
  5. 翻源码:打开 libobs/obs-encoder.h:99encoder_packet,找到 keyframeptsdtsdrop_priority,对照本课它们各自代表什么;再到 plugins/obs-x264/obs-x264.c:405,确认"秒 × 帧率 = GOP 帧数"那一行。

# 下一课预告

这一课我们理解了编码的原理,也瞥见了 x264 那几个"旋钮"。可 OBS 支持一堆编码器(x264、NVENC、QSV、AMF……),它们内部天差地别,OBS 却能用同一套代码创建、配置、驱动它们、收取编码包。这是怎么做到的?

第 19 课:编码器的统一接口 —— obs_encoder —— 我们回到熟悉的套路:又是一张"登记表"(obs_encoder_info)、又是一套 create/encode/destroy 回调(还记得第 6 课的 source 吗?)。看 OBS 怎么把"五花八门的编码器"抽象成一个统一接口,让上层完全不用关心底下是软件编码还是显卡编码。下节课见。


📁 本课配图:imgs/18-01 ~ imgs/18-11 📌 源码锚点: libobs/obs-encoder.h:99(struct encoder_packet)、:103/:104(pts/dts)、:111(keyframe)、:128/:136(priority/drop_priority,含 :130 注释)、:43(enum obs_encoder_type); libobs/obs-nal.h:26(OBS_NAL_PRIORITY_* 优先级)、libobs/obs-avc.c:55(compute_avc_keyframe_priority:从 IDR NAL 判定关键帧+优先级)、:103(drop_priority = priority); plugins/obs-x264/obs-x264.c:405(keyint_sec × fps → i_keyint_max = GOP)、:429(bf → i_bframe)、:416(rc.i_bitrate)、:695(keyframe 来自 pic_out->b_keyframe)、:100(defaults:keyint_sec=0/bitrate=6000/CBR); plugins/obs-outputs/rtmp-stream.c:1440(drop_frames:保留音频+关键帧、丢 P/B)、:1471(找非关键帧丢)、:1641(丢到下一个关键帧才续); libobs/obs-output.c:2089(按 dts 排序 = 解码顺序)、:2237(等第一个关键帧才开始);libobs/obs-encoder.c:1478(receive_video)、:1421(do_encode 调插件 encode)。 注:DCT / 量化 / 熵编码 / 块匹配是通用视频编码原理,由 x264/NVENC 等编码器内部实现,OBS 不含这部分代码。

上次更新: 2026/07/14, 19:37:40

社区交流

讨论与留言

前往 GitHub Issues →

本留言区仅对应当前文章,欢迎补充观点、提出问题或帮助修正文中疏漏。 留言由 GitHub/Gitalk 提供,需要使用 GitHub 登录。

最近更新
第 1 课:专栏导论与安全边界
07-01
第 3 课:安装编译与调试环境
07-01
第 4 课:第三方库下载、编译与依赖管理
07-01
更多文章>