第 04 篇

丢包后,重传、FEC 和 PLC 怎么选

把重传、Opus 带内 FEC 与丢包隐藏放回同一播放预算,计算何时来得及恢复,再用真实 libopus 的正反例检查恢复冗余、解码顺序和数据成本。

Hex4C59 18 分钟阅读

软件工程·原创·概念与设计

接收端已经拿到 200、201、203,缺少 202。203 对应的声音还没到播放时刻,202 却快要用到了。

第三篇说明,这时需要分别记录序号缺口和播放可用性。现在继续处理这个缺口:能否把 202 要回来?能否利用 203 带来的信息补出前一段声音?如果都来不及,播放怎样继续?

三种办法改变的环节不同。重传让发送端再送一份原来编码的数据;FEC(Forward Error Correction,前向纠错)提前发送恢复信息;PLC(Packet Loss Concealment,丢包隐藏)在接收端产生缺失区间的替代声音。选择它们时,既要看手里有什么数据,也要看完成处理后还能否赶上播放。

本篇延续前面的固定时长 Opus 示例,先算同一份时间预算,再运行一个真实 libopus 实验。时间预算实验只需 Python 3.9+;真实编解码还需要导出相关接口的 libopus 动态库。后一个实验不录音、不联网,也不测音质。

读完本篇,你应该能区分“原数据重发”“靠额外信息恢复”“没有新信息时产生替代声音”,并按剩余时间与现有数据判断哪些路径仍可行。阅读需要前两篇的声音分块概念,以及第三篇的包序和播放计划;不要求先会读 C 接口。先完成时间预算和新条件练习,再按需要阅读接口与真实编解码实验。

缺了 20 毫秒,为什么不能把后面的声音直接接上 #

假设连续声音分为 A、B、C 三段,每段 20 毫秒,B 没到,C 到了。直接把 C 提前 20 毫秒接在 A 后面,会跳过 B 的声音,也改动原来的媒体时间安排;让输出设备一直停住等 B,则会把后面的声音一起推迟。

本篇采用固定播放计划,目标是在轮到 B 的区间时仍提供相应长度的输出。可用信息决定了不同办法的能力:重传去找 B 的原编码内容;FEC 使用发送端事先附带的恢复信息;PLC 在缺少可用内容时,利用已有状态产生替代样本。具体系统还可能调整播放节奏,这里先不改变时间安排。

下面继续使用第三篇的包号 202、203。202 是缺失的那段声音;203 是它后面的声音,不是“替 202 播一下”就能解决的内容。

先确定 202 最晚什么时候能用 #

沿用第三篇的教学条件:每包 20 毫秒声音,202 在接收端本地第 60 毫秒开始播放,恢复完成到可输出还预留 5 毫秒。所有时刻都使用接收端同一条时间轴。

计划播放时刻:60 毫秒
后续准备成本:5 毫秒
最晚取得可用输入:60 − 5 = 55 毫秒

这里暂把恢复解码、排队和输出准备合并为固定的 5 毫秒,方便比较;它不是实测或产品默认值。后面的真实编解码实验只检查数据和接口行为,不用运行速度替代这笔预算。

在第 40 毫秒,203 到达,接收端发现 202 的序号缺口。距离播放还有 20 毫秒,但留给取回数据的时间只有 15 毫秒。把这两个数字混用,就可能发出已经赶不上播放的请求。

下图使用同一个 60 毫秒期限比较三条路径。FEC 一行先假设 203 确实带有可用冗余;怎样检查这个条件,后面会实际验证。

202 的三条处理路径:重传在 72 毫秒准备好,FEC 在 45 毫秒准备好,PLC 按 60 毫秒输出 · 在独立窗口中查看

请求重传:要算完整的返回过程 #

NACK 是 Negative Acknowledgement,即否定确认。RTP 的 Generic NACK 反馈用于指出哪些包没有收到;它提供请求恢复所需的信息,并不保证发送端一定能重传。1

假设接收端在 40 毫秒发现缺口,等 2 毫秒发送反馈。随后发生:

过程本段耗时累计到本地哪个时刻
发现缺口并等待反馈发送2 毫秒42 毫秒:发出 NACK
请求传到发送端10 毫秒52 毫秒
发送端查找缓存的原包1 毫秒53 毫秒
等待发送调度4 毫秒57 毫秒
重传数据返回接收端10 毫秒67 毫秒
接收端完成后续准备5 毫秒72 毫秒

最终到 72 毫秒才准备好,比 60 毫秒晚了 12 毫秒。只拿“两段网络各 10 毫秒,往返 20 毫秒”与剩余 20 毫秒相比,会漏掉反馈等待、发送端处理、发送排队和播放准备。

计算应当覆盖整个过程:

准备好时刻
= 发现缺口时刻 + 反馈等待
+ 请求网络耗时 + 发送端处理与排队 + 返回网络耗时
+ 接收端后续准备

RTP 重传的适用性需要考虑应用允许的延迟。2 在这组条件下,继续等待这次返回不能满足原定播放;这不等于重传在所有通话中都没用。

把两个方向的网络耗时各降为 5 毫秒,发送排队降为 1 毫秒,其他条件不变:

40 + 2 + 5 + 1 + 1 + 5 + 5 = 59 毫秒

这次能赶上,余量是 1 毫秒。余量很小,实际处理或网络波动仍可能改变结果。生产实现需要用适合当前路径的估计,不能把一次探测值当作稳定承诺。

还有一个先于时间的条件:原包仍在缓存中 #

即使算出 59 毫秒,发送端已经删除了 202,也没有这份原包可供返回。接收端发出 NACK,只说明请求已发出;还要跟踪发送端是否找到原数据、是否排入发送,以及返回数据最终是否可用。2

NACK 是反馈机制,RTX 是 RTP 重传负载格式,两者也不等同。这里计算的是恢复 202 所需内容何时返回,没有生成或解析 RTX 的独立包序与封装。2

Opus 带内 FEC:把前一段的恢复信息提前带过来 #

先用一个只需加减法的假设例子理解“额外信息”。发送端原来要传两个数字 A=6、B=9;它还算出一份辅助信息 R=A+B=15。如果 A 丢了,但 B 和 R 都到了,接收端可以算出 A=15−9=6。如果 A、B 都没到,只知道和为 15,就无法唯一确定两个数。

这里没有把缺失内容凭空猜出来:恢复依据是发送端额外传来的关系信息,代价也正是多传了数据。这个算术模型只解释冗余为何有用,不是 Opus 的编码算法,也不是可直接用于网络协议的设计。

前向纠错的关键是发送端不等待针对这次缺失的反馈,就随编码数据发送恢复信息,让接收端在条件满足时利用它。

“带内”表示恢复信息与当前声音的编码一起放在 Opus 包里。本文只讨论这种 Opus 带内 FEC:在语音编码路径中,当前包可以携带此前声音的低码率冗余。对于固定 20 毫秒的这个例子,可以把 203 理解为包含自己的声音编码,以及用于恢复 202 的信息。是否加入冗余由编码器决定,需要结合预期丢包、容量和信号等条件。3

这种恢复针对此前的音频表示,不会补回丢失的 RTP 报头,也不保证原封不动重建整份编码包;恢复输出也不保证与正常解码逐样本相同。4

它没有等待接收端发出一次请求。若 203 在 40 毫秒已经到达,里面又有可用冗余,按本篇统一的 5 毫秒恢复与准备预算,可以在 45 毫秒准备好,早于 60 毫秒播放。

但“已经开启 FEC”还缺少三个检查:

检查不满足时会怎样
后续包是否到达恢复信息可能也没到手
该包是否含有适用于缺失区间的冗余开关已开,也可能无相应信息
恢复与准备是否赶得上有冗余,仍可能错过原定播放

例如只把 203 的到达改为 58 毫秒,准备好就到 63 毫秒,已经迟到。FEC 不等反馈往返,也仍要等承载恢复信息的包到达。RFC 7587 因此要求接收缓冲能够把含有 FEC 的后续负载提供给解码器。3

连续丢包会改变可恢复范围 #

仍限定为当前包保护紧邻前一个 20 毫秒包的情形:

收到的内容能检查的恢复机会
202 缺失,203 到达检查 203 是否能恢复 202
202、203 都缺失,204 到达204 可能帮助恢复 203,不能据此承诺恢复更早的 202
203 到了,但没有相应冗余不能用这次 FEC 请求证明 202 已恢复

这是本篇固定包长与相邻保护的边界。Opus 的模式、帧长切换和其他冗余方案还有各自的组织方式,不能把上表推广成所有 FEC 的能力。4

PLC:没有新包,也要推进播放时间 #

丢包隐藏由接收端利用已有解码状态,也就是此前解码时保留的信息,为缺失区间产生替代输出,尽量让声音继续。它不会因为生成了样本,就把缺失的原始信息从网络上找回来。5

这里的“替代”不等于恢复了原话。例如缺失区间恰好发生音节变化,仅凭此前的声音状态不能保证知道对方原本要说什么。PLC 的作用是尽量维持输出,不能据此把缺失包记为已经收到。

回到 60 毫秒的播放计划,如果统一预留的本地生成和准备成本是 5 毫秒,就需要最晚在 55 毫秒开始处理。播放调度器知道当前要输出哪个媒体区间,不必一直等到更大的序号出现,才决定缺失区间如何输出。

连续缺失、语音突然变化等情况,会让“根据过去产生替代声音”遇到更难处理的内容。即使输出连续,也不能保证对方说的门牌号或名字被准确重建。本篇不比较听感算法,只区分使用了哪些输入、产生了什么结果。

三种办法付出的成本不同 #

把它们放在同一组维度里比较:

办法需要的新信息时间约束主要成本
重传发送端缓存中的原编码内容请求与返回全程要赶上反馈、额外发送、排队与缓存
Opus 带内 FEC后续包中的恢复冗余后续包和恢复处理要赶上冗余占用编码或发送预算,可能需要额外等待
PLC不需要新包,使用已有解码状态本地生成和输出准备要赶上内容是估计或替代,不能保证还原原始信息

这些机制可以协作,并非整通电话只选一个。例如先评估重传能否赶上,在收到后续包后检查 FEC,到播放调度的截止点仍没有可用内容,则为该区间产生替代输出。具体优先级与解码状态处理由实现决定,上表不是一份可直接用于生产的调度算法。

冗余数据也要放进带宽预算 #

用一笔独立的教学预算说明:假设主音频负载是 32 kbit/s,另允许 8 kbit/s 恢复数据,那么编码与恢复负载合计 40 kbit/s。若仍每 20 毫秒一包,并沿用第三篇“无选项 IPv4、UDP、12 字节 RTP 固定头”的每包 40 字节开销:

每秒 50 包,头开销 = 40 × 50 × 8 = 16 kbit/s
原来:32 + 16 = 48 kbit/s
额外增加 8 kbit/s 恢复负载后:32 + 8 + 16 = 56 kbit/s

这是增加数据预算的算术示例,没有给 Opus 设置“冗余固定 8 kbit/s”的参数。真实 Opus 编码可以在给定总预算下调整主要声音与保护信息的分配;提高抗丢失能力也可能改变无丢失时的质量取舍。6

若可用容量没有增加,却继续叠加恢复流量,排队可能变长。RFC 4588 要求把原始流与重传一同纳入可接受的发送速率;恢复流量不能游离在拥塞控制以外。7 更多恢复数据若最终使包更迟到,未必有利于原定播放。

先运行时间预算,改变一个条件 #

下载 recovery_budget.py。在保存脚本的目录使用 Python 3.9 或更新版本运行,不需要额外依赖:

python3 recovery_budget.py

关键结果应包含:

Latest usable input: 55 ms
NACK request: 42 ms
Retransmission arrives: 67 ms; ready: 72 ms
Retransmission: too late
Next packet prepared: 45 ms; redundancy assumed present: True
FEC recovery: on time

脚本明确把两个网络方向的耗时总和设为 20 毫秒,没有发网络探测。改为前面的快速返回条件:

python3 recovery_budget.py --network-rtt-ms 10 --send-queue-ms 1

重传在 54 毫秒返回、59 毫秒准备好,输出变为 Retransmission: on time。分别再试两种反例:

python3 recovery_budget.py --next-packet-ms 58
python3 recovery_budget.py --no-fec

前者的 FEC 准备时刻是 63 毫秒,赶不上 60 毫秒。在这个模型里,序号缺口也要到 203 到达才被发现,所以 NACK 路径同样后移。后者虽然准备时间仍可能足够,但假设包内没有恢复冗余,结果是 FEC recovery: unavailable。

还可以增加 --cache-miss 检查发送端无原包的情况。脚本没有动态缓冲、反馈抑制、真实重传、解码或播放;PLC 的开始时间只是按既定成本反推的调度位置,不能从输出的毫秒数得出某台设备的实际处理能力。

换一组恢复条件 #

下面分别计算重传与 FEC 的独立条件:缺失段计划在 100 毫秒播放,恢复解码与输出准备统一需要 6 毫秒。第 1 题中,在 75 毫秒发现缺口;反馈等待 2 毫秒,请求传输 5 毫秒,发送端查找与排队共 3 毫秒,返回传输 5 毫秒。

  1. 发送端仍有原包,重传何时准备好,是否来得及?
  2. 一个后续包在 92 毫秒到达,带有适用于缺失区间的冗余。FEC 来得及吗?若改为 96 毫秒到达呢?
  3. 重传缓存中没有原包,后续包里也没有冗余,但解码器仍产生了等长输出。能否说缺失内容已经成功恢复?
展开判断依据
  1. 原包在 75 + 2 + 5 + 3 + 5 = 90 毫秒返回,再准备 6 毫秒,到 96 毫秒就绪,比期限早 4 毫秒。这里已经给出缓存存在的条件。
  2. 92 毫秒到达时,92 + 6 = 98 毫秒就绪,可以赶上。96 毫秒到达时,102 毫秒才就绪,晚 2 毫秒。有冗余与能及时利用冗余,是两项不同检查。
  3. 不能。已知两个新信息来源都不可用,等长输出可以来自 PLC。必须检查实际输入与执行路径;输出样本数没有证明原信息被恢复。

如果第二题只看包里有冗余就判为成功,回到播放期限;如果第三题把“有声音”当成“原话恢复”,回看 PLC 和 FEC 请求回退的区别。

进阶:把恢复过程对应到解码器调用 #

libopus 是 Opus 的编解码实现库,程序通过它的接口进行编码和解码。前面的判断只需要知道输入来自哪里、结果何时可用。下面把这些动作对应到真实接口,方便继续做编解码实验;前面的新条件练习不要求掌握这些调用。

这里改用 16 kHz、单声道 PCM,20 毫秒对应 16,000 × 0.020 = 320 个每声道样本。第三篇讲的 Opus RTP 时间戳仍按每秒 48,000 刻度计数;同一个 20 毫秒区间可以同时对应 320 个解码样本和 960 个 RTP 刻度,两个数使用不同单位。8

后续包为什么要交给解码器两次 #

要先处理缺失的 202 区间,再处理 203 自己的区间。以 16 kHz、单声道、每段 20 毫秒为例,每段是 320 个每声道样本:

/* 示意接口调用,省略错误检查与缓冲分配,不是完整程序。 */
opus_decode(decoder, packet_203, bytes_203, pcm_202, 320, 1);
opus_decode(decoder, packet_203, bytes_203, pcm_203, 320, 0);

第一次把 decode_fec 设为 1,请求其中的前向纠错数据,产生缺失区间的输出;第二次设为 0,正常解码当前包自己的声音。两次调用使用同一个持续的解码状态,第二次没有把 203 的声音重复播放。9

这与第三篇中丢弃网络重复包不矛盾:203 的当前声音只正常解码一次,另一次请求的目标是此前缺失的媒体区间。

没有可用数据时,怎样请求替代输出 #

对 libopus,可以用空数据指针和零长度表示丢失。在这里的 16 kHz、20 毫秒条件下,调用形态为:

/* 仍为接口示意,320 表示这个缺失区间的每声道样本数。 */
opus_decode(decoder, NULL, 0, pcm_missing, 320, 0);

这里没有传入 202 的负载。程序取得 320 个输出样本,只证明该区间有了替代输出,不能据此增加“原包收到”或“原始声音成功恢复”的计数。

decode_fec=1 返回了样本,也可能只是 PLC #

libopus 解码接口明确规定:请求 FEC 而当前输入没有这种信息时,会按丢失情形处理。9

因此,下面这条推理不成立:

设置 decode_fec=1 → 返回 320 个样本 → 一定使用了恢复冗余

应当另查包内是否含有冗余,并结合执行路径记录结果。本文使用 opus_packet_has_lbrr();LBRR 是 Low Bit-Rate Redundancy,即低码率冗余。4 这个检查再与实际 FEC 调用关联,才能把“用冗余恢复”和“请求后回退到 PLC”区分开。

再用真实 libopus 检查 FEC 与回退 #

这个实验是可选的进一步验证,不是完成基础练习的前提。时间条件能够成立,还要确认解码器实际读到了什么。下载 opus_recovery.py,使用本机的 libopus 动态库运行这个独立实验。它通过 Python 标准库 ctypes 调用真实编解码接口,不是用公式模拟出 PCM。

运行条件与输出 #

需要 Python 3.9+,以及导出 opus_packet_has_lbrr 的 libopus。本文核验版本为 1.6.1。FFmpeg 支持 Opus,不必然表示 Python 能找到可单独加载的动态库;脚本先用系统方法查找,再尝试已有 pkg-config 提供的目录。若仍找不到,可用 --opus-library 指定实际动态库文件。

python3 opus_recovery.py --output-dir opus-recovery-on

脚本只生成合成信号,不采集麦克风。输出目录必须是新目录,重复实验时换一个名称,程序不会覆盖已有结果。找不到库或缺少所需接口时会明确报错,不能只安装同名 Python 包就视为准备完成。

实验固定为 16 kHz、单声道、16 位整数,20 毫秒一块,合计 2 秒、100 块。每块有 16,000 × 0.020 = 320 个样本,占 640 字节。索引从 0 开始,丢弃索引 12 的包,它对应源媒体的 240—260 毫秒区间;这里的索引不是前面 RTP 的序列号。

合成输入包含变化的基频、谐波与幅度,不是真人语音录音。编码设置是面向网络语音的应用模式(OPUS_APPLICATION_VOIP)、24 kbit/s 目标、复杂度 5、语音信号提示、最大宽带模式、关闭不连续发送,并向编码器提供 20% 的预期丢包。本次保留编码器默认的可变码率,24 kbit/s 不表示每包固定为 60 字节。实际模拟只丢弃指定的一个包,预期丢包参数不等于本次实际丢包比例。这些设置是实验选择,不是通话默认值。6

先用相同合成 PCM 分别关闭和开启 FEC 编码,记录编码字节数。随后三条解码路径都读取开启组的同一批包,避免比较接收方法时又改变编码输入:

解码路径索引 12 的动作索引 13 的动作
normal正常解码包 12正常解码包 13
plc输入空数据,请求 PLC正常解码包 13
fec_request用包 13 请求 FEC在同一解码状态下正常解码包 13

三条路径使用各自独立、持续推进的解码实例;每条内部按媒体顺序处理。后续包在本地内存里已经可用,因此这个实验能检查 FEC 机制,不能检查公网等待或播放是否及时。

程序输出四份 WAV:input.wav、normal.wav、plc.wav、fec_request.wav。另有 report.json 保存版本、配置与输出摘要,frames.csv 记录每块读取的包、字节数、冗余标志和执行动作。没有自动播放音频。

本次运行,哪一条证据说明使用了冗余 #

2026 年 10 月 15 日,在 macOS arm64、Python 3.9.6、libopus 1.6.1 下,使用脚本默认合成输入与索引 12,得到:

Runtime: libopus 1.6.1
Encoder FEC enabled: True
Selected stream: 5456 bytes; FEC-disabled reference: 5482 bytes
Packet 13 has LBRR: 1
Missing-block action: fec_from_next
All paths: 32000 samples/channel
Missing block FEC-request equals PLC: False

包 13 的 LBRR 检查为 1,缺失区间实际执行了 FEC 请求;该区间的输出与独立 PLC 路径不同。三条路径各输出 32,000 个每声道样本、64,000 字节 PCM,但等长不等于内容相同,也不代表与原始 PCM 无损一致。

在 CSV 中对照 fec_request 路径里 block_index 为 12、13 的两行,还能看到它们都引用包 13:第一行标为 fec_from_next,下一行标为 normal。这两行对应不同的媒体区间,而非把同一段声音排进输出两遍。

需要特别解释 5456 和 5482:开启组的整体编码字节数比关闭组少了 26 字节。这个差值不能解释成“冗余没有成本”或“开启 FEC 一定省带宽”。两组使用相同目标预算,编码器还会改变内容分配;这里只记录总字节数,没有单独测量主声音和冗余各占多少。合成信号也不能代表真实语音上的普遍大小与质量结果。

关闭冗余,再提出同一个 FEC 请求 #

保持输入和解码程序不变,只关闭编码器的带内 FEC:

python3 opus_recovery.py --disable-fec --output-dir opus-recovery-off

本次运行的关键输出为:

Encoder FEC enabled: False
Packet 13 has LBRR: 0
Missing-block action: fec_request_plc_fallback
All paths: 32000 samples/channel
Missing block FEC-request equals PLC: True

请求仍然返回了与缺失区间等长的样本,但包内没有 LBRR,缺失块输出与 PLC 相同。这样就把“调用了 FEC 接口”与“确实利用恢复信息”分开验证了。

也可以换 --loss-index,程序接受 0—98,保证缺失位置后还有一个包。换位置或版本后重新检查实际冗余标志,不能把默认位置的结果照搬过去。这个实验没有声学播放、音质评分、输入输出延迟补偿、连续丢包或动态缓冲调度;判断质量还需要对齐声音、选择相应输入和评价方法。

排查时,把请求、收到、恢复与隐藏分别记下来 #

如果日志只有“请求恢复成功”或“解码输出了样本”,仍无法判断刚才那段声音发生了什么。对同一媒体区间,可以关联这些记录:

事件至少保留的判断依据
发出重传请求缺失包身份、发出时刻、剩余播放预算
重传数据返回对应哪个原包、接收时刻、何时准备好
请求带内 FEC使用哪个后续包、是否有相应冗余、产生哪个媒体区间
生成 PLC缺失区间、替代样本数、输出安排
提交与实际播放是否赶上,以及最终输出采用了哪一路内容

统计时也保留这些区别:发了多少请求、实际收到多少重传、多少恢复赶上播放、多少 FEC 请求回退到 PLC。替代样本让时间轴继续,并不能让“原包收到”的计数自动增加;一次重传已到,也可能只增加了迟到接收。

202 的选择因而取决于两个条件:恢复数据是否存在,以及处理后是否赶得上。本文的慢重传到 72 毫秒才就绪,及时到达的冗余在 45 毫秒就绪;若冗余也不可用,接收端仍需要为 60 毫秒的输出安排生成替代声音。改变到达、缓存或冗余条件,结果就会改变。

第五篇把恢复流量放回整条发送路径:当编码、原包和重传一起争用有限带宽,队列为什么越积越长,又怎样根据网络反馈限制发送。增加保护之前,先确认它没有把更多内容推过播放期限。

回看第三篇:从一段收包记录读懂 RTP · 系列目录

参考资料 #

资料核对于 2026 年 10 月 15 日。播放时间与网络预算是教学构造;libopus 输出是上述合成输入、版本和参数的实际运行结果。


  1. RFC 4585 §6.2.1:Generic NACK,2006 年 7 月发布,定义用于报告 RTP 包缺失的反馈格式。 ↩︎

  2. RFC 4588 §1、§4、§6.3 与 §8.1:重传、反馈与缓存保留时间,2006 年 7 月发布,讨论延迟约束、独立重传流与发送端处理。 ↩︎ ↩︎ ↩︎

  3. RFC 7587 §3.3:Opus 带内 FEC,2015 年 6 月发布,说明前一包恢复信息、编码器决策以及接收缓冲与解码器的配合。 ↩︎ ↩︎

  4. RFC 6716 §4.2 与 §4.2.5:LBRR 的组织,2012 年 9 月发布,说明低码率恢复信息及帧长和模式变化的边界。opus_packet_has_lbrr 的返回含义见 libopus 解码器文档。 ↩︎ ↩︎ ↩︎

  5. RFC 6716 §4.4:丢包隐藏,以及上述 libopus 解码器 API 的空输入约定。 ↩︎

  6. libopus 1.6 编码器控制 API,说明带内 FEC、预期丢包百分比、编码预算与质量取舍。代码中的请求编号依据对应版本公开头文件 opus_defines.h。 ↩︎ ↩︎

  7. RFC 4588 §7:重传与拥塞控制,说明原始数据和重传应共同受可接受发送速率约束。 ↩︎

  8. RFC 7587 §4.1:Opus RTP 时间戳,规定 RTP 时钟固定为 48 kHz,与编解码时使用的采样率分别理解;2026 年 10 月 15 日复核。 ↩︎

  9. libopus 1.6 解码器 API,说明 decode_fec、没有相应冗余时的丢失处理、持续解码状态,以及 PLC/FEC 所需的缺失时长参数。 ↩︎ ↩︎

一起讨论

选中正文中的文字,可以针对这段内容发起讨论。

正在读取讨论…

在 GitHub 查看与管理讨论 ↗

文章讨论