备注:全部内容来自AI,记录下来后续慢慢消化
一、简介
DRED(DNN-based Redundant Encoding 神经网络冗余编码)
https://blog.csdn.net/CrystalShaw/article/details/155348053
https://blog.csdn.net/CrystalShaw/article/details/155348053opus的silk编码器里面有带内fec编码,上面链接有详细介绍。实际应用个人更倾向RS-FEC,同样带宽下,RS-FEC保护范围更大,音质完全还原。而opus的带内fec仅能保护上一个报文,并且还牺牲了音质。
opus推出的DRED看来是要解决带内fec的局限性。
1、核心原理对比
|------|----------------------------------------|----------------------------------------------------------|
| 特性 | 传统带内 FEC (Opus Standard FEC) | DRED (Deep Redundancy) |
| 工作原理 | 复制粘贴。将前一帧的音频数据(通常是低比特率版本)直接塞进当前包里一起发送。 | 特征压缩。利用神经网络提取音频的"潜变量"(Latents,即声音的本质特征),将这些极小的特征数据塞进当前包。 |
| 恢复方式 | 直接播放。如果主包丢了,解码器直接把 FEC 数据拿出来播放。 | 神经合成。如果主包丢了,解码器利用收到的特征数据,通过 LPCNet 神经网络 重新"画"出波形。 |
| 数据性质 | 它是波形域或参数域的直接拷贝。 | 它是语义/特征域的抽象表示。 |
| 带宽消耗 | 线性增长。想保护更多帧或更高质量,需要的比特数直线上升。 | 极高压缩率。因为只传特征,同样的比特数可以保护更长的时间窗口,或者在同等保护长度下占用更少带宽。 |
2、DRED 带来的具体收益
- 收益一:在极低码率下实现"可懂度"救急 (最大的优势)
传统FEC困境:Opus 的传统 FEC 通常在总码率低于 ~16-20 kbps 时会自动关闭。
原因:如果在 8 kbps 的通话中拿出 4 kbps 做 FEC,主音频只剩 4 kbps,音质烂到无法接 受;如果不拿,FEC 本身又不够清晰。这是一个死结。
DRED的突破:由于神经网络提取的特征极其紧凑,DRED 可以在 总码率低至 8-12 kbps 的 情况下依然开启。
效果:即使主包丢失,接收端利用 DRED 重建的声音虽然听起来像"电话音"或有点机械感 (神经声码器的特点),但语音内容(说了什么)依然清晰可懂。
场景:弱网环境(如电梯、地下室、移动网络边缘),传统方案会直接断音或变成刺耳的 噪声,DRED 能保证你听清对方在说什么。
- 收益二:更长的保护时间窗口 (抗连续丢包)
传统FEC困境 :通常只能保护前 1 帧。如果遇到连续丢包,FEC 也丢了,那就彻底没救了。
DRED的突破 :由于特征数据很小,可以在一个包里携带过去 3~5 帧甚至更多的冗余信息。
这意味着它能抵抗更长时间的连续丢包。只要包最终到达了,里面的DRED数据就能 把之前丢失的几秒语音都补回来。
- 收益三:避免"机器人音"叠加的听感恶化
传统FEC问题 :当网络抖动导致频繁切换"主包"和"FEC 包"时,听众会听到音质在"清晰"和 "模糊/闷"之间反复横跳,这种不连续性非常干扰听觉注意力。
DRED的优势:DRED设计目标是在丢包发生时提供平滑的降级。虽然重建的音质不如原 声自然,但它提供了一致性听感。神经网络的生成能力可以填补频谱空缺,听起来比传 统插值算法产生的"嗡嗡声"或"机械音"更自然一些。
- 收益四:针对语音特性的极致优化
传统 FEC 是通用的信号处理,它不知道自己在传语音还是音乐,只是盲目保留能量和频谱
DRED 是基于 LPCNet 训练的,它"懂"人类语音的结构(基频、共振峰、浊音/清音)。
在极度受限的比特率下,会优先保留决定语音可懂度的关键特征,丢弃不重要的细节。 这是一种语义层面的压缩,效率远高于传统的信号压缩。
二、实现
1、整体框架
语音 PCM → 重采样到 16kHz → LPCNet 特征提取 → RDOVAE 深度编码 → 隐向量(latents) → 熵编码 → 冗余数据包
cpp
1. 输入PCM → 重采样 16k mono
2. 存入输入缓冲
3. 凑够2帧 → 提取LPCNet特征
4. RDOVAE压缩 → 得到16维latent + 32维state
5. 保存最近多帧向量
6. 熵编码 → 生成小体积冗余包
7. 随Opus主帧一起发送
8. 丢包时,接收端用冗余向量恢复语音
2、模块分工
重采样 :任何采样率都要统一到16kHz(DRED 模型只支持 16k)
LPCNet 特征 :提取语音梅尔 / 线性预测特征
RDOVAE 编码器 :AI 模型把特征压缩成小维度隐向量
量化 / 熵编码 :把浮点数隐向量压缩成比特流
3、数据结构
cpp
typedef struct {
opus_int32 Fs; // 采样率
int channels; // 声道
int loaded; // 模型是否加载
float input_buffer[...]; // 16k输入缓冲
int input_buffer_fill; // 缓冲填充量
float state_buffer[...]; // AI状态向量(32维/帧)
float latents_buffer[...];// AI隐向量(16维/帧)
int latents_buffer_fill;
float resample_mem[...]; // 重采样滤波器记忆
RDOVAEEnc model; // AI模型
LPCNetEnc lpcnet_enc; // 特征提取
RDOVAEEncState rdovae_enc;// 编码器状态
} DREDEnc;
4、函数原理
-
- dred_encoder_init:编码器初始化
作用:创建 DRED 编码器实例,设置采样率、声道、加载 AI 模型
保存采样率 Fs、声道数
加载 RDOVAE + LPCNet 模型
调用 dred_encoder_reset 重置状态
cpp
void dred_encoder_init(DREDEnc* enc, opus_int32 Fs, int channels)
{
enc->Fs = Fs;
enc->channels = channels;
enc->loaded = 0;
// 加载AI模型
#ifndef USE_WEIGHTS_FILE
if (init_rdovaeenc(&enc->model, rdovaeenc_arrays) == 0) enc->loaded = 1;
#endif
// 重置编码器
dred_encoder_reset(enc);
}
-
- dred_encoder_reset:状态重置
作用:清空所有历史状态,重启 AI 编码流水线,这是 DRED 最关键的状态控制函数。
原理:清空从 DREDENC_RESET_START 开始的所有动态状态
隐向量缓冲区
状态缓冲区
输入缓冲区
重采样滤波器记忆
设置输入缓冲区填充值 = 编码器延迟(DRED_SILK_ENCODER_DELAY)
重新初始化 LPCNet 编码器
重新初始化 RDOVAE 编码器
cpp
void dred_encoder_reset(DREDEnc* enc)
{
// 清空动态状态
OPUS_CLEAR((char*)&enc->DREDENC_RESET_START,
sizeof(DREDEnc)-
((char*)&enc->DREDENC_RESET_START - (char*)enc));
// 输入缓冲填充 = 编码器延迟
enc->input_buffer_fill = DRED_SILK_ENCODER_DELAY;
// 重置子模块
lpcnet_encoder_init(&enc->lpcnet_enc_state);
DRED_rdovae_init_encoder(&enc->rdovae_enc);
}
-
- dred_convert_to_16k:统一重采样到 16kHz
原理:DRED AI 模型只支持 16kHz 单声道输入,所以必须:
多声道 → 转单声道
8k/12k/24k/48k/96k → 重采样 → 16k
使用 椭圆滤波器(ellip filter) 防混叠
支持:8000、12000、16000、24000、48000、96000(ENABLE_QEXT)
-
- dred_compute_latents:计算 AI 隐向量
作用:把 PCM 语音 → 转换成 AI 冗余需要的 latents(隐向量)
流程:重采样到 16k
填满输入缓冲
缓冲满 2 帧 DRED 帧 → 触发 dred_process_frame
滑动窗口,不断生成新的隐向量
-
- dred_process_frame:核心 AI 编码
真正的 AI 编码流水线:
cpp
LPCNet 特征提取 → RDOVAE 编码 → 得到 latents + state
特征:每帧 36 维,2 帧拼成 72 维
RDOVAE:把 72 维特征压缩到 16 维 latent + 32 维 state
缓冲区保存最近 N 帧,用于冗余修复
-
- dred_encode_latents:隐向量量化 + 熵编码
原理:把浮点数隐向量 → 压缩成紧凑二进制包
量化:浮点数 → 整数
死区量化(deadzone quant):让小数值归零,提升压缩率
拉普拉斯熵编码:超高压缩语音冗余数据
-
- dred_encode_silk_frame:生成最终冗余包
作用:把所有隐向量打包成 可发送的冗余帧,并写入输出 buffer。
逻辑:检查语音活动(VAD),静音不发冗余
编码 state 向量
编码多帧 latents 向量
控制冗余包大小(max_bytes)
返回冗余包长度(=0 表示不发送)