[AI codec]opus-1.6\DRED 编码侧 学习笔记

备注:全部内容来自AI,记录下来后续慢慢消化

一、简介

DRED(DNN-based Redundant Encoding 神经网络冗余编码)

https://blog.csdn.net/CrystalShaw/article/details/155348053https://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、函数原理

    1. 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);
}
    1. 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);
}
    1. dred_convert_to_16k:统一重采样到 16kHz

原理:DRED AI 模型只支持 16kHz 单声道输入,所以必须:

多声道 → 转单声道

8k/12k/24k/48k/96k → 重采样 → 16k

使用 椭圆滤波器(ellip filter) 防混叠

支持:8000、12000、16000、24000、48000、96000(ENABLE_QEXT)

    1. dred_compute_latents:计算 AI 隐向量

作用:把 PCM 语音 → 转换成 AI 冗余需要的 latents(隐向量)

流程:重采样到 16k

填满输入缓冲

缓冲满 2 帧 DRED 帧 → 触发 dred_process_frame

滑动窗口,不断生成新的隐向量

    1. dred_process_frame:核心 AI 编码

真正的 AI 编码流水线:

cpp 复制代码
LPCNet 特征提取 → RDOVAE 编码 → 得到 latents + state

特征:每帧 36 维,2 帧拼成 72 维

RDOVAE:把 72 维特征压缩到 16 维 latent + 32 维 state

缓冲区保存最近 N 帧,用于冗余修复

    1. dred_encode_latents:隐向量量化 + 熵编码

原理:把浮点数隐向量 → 压缩成紧凑二进制包

量化:浮点数 → 整数

死区量化(deadzone quant):让小数值归零,提升压缩率

拉普拉斯熵编码:超高压缩语音冗余数据

    1. dred_encode_silk_frame:生成最终冗余包

作用:把所有隐向量打包成 可发送的冗余帧,并写入输出 buffer。

逻辑:检查语音活动(VAD),静音不发冗余

编码 state 向量

编码多帧 latents 向量

控制冗余包大小(max_bytes)

返回冗余包长度(=0 表示不发送)

相关推荐
张张123y1 小时前
RAG从0到1学习:技术架构、项目实践与面试指南
人工智能·python·学习·面试·架构·langchain·transformer
·醉挽清风·1 小时前
学习笔记—Linux—文件IO
linux·服务器·学习
Accerlator2 小时前
计算机网络学习
学习·计算机网络
星爷AG I2 小时前
14-12 动作序列学习(AGI基础理论)
人工智能·学习·agi
澄澈青空~2 小时前
Unity3D VR 游戏开发 — 技术路线与学习路线完整大纲
学习·vr
sheeta19985 小时前
苍穹外卖Day05笔记
笔记
想搞艺术的程序员5 小时前
Java Survivor区学习笔记
java·笔记·学习·垃圾回收
吃杠碰小鸡5 小时前
学习英语的思路性问题
学习
不能隔夜的咖喱5 小时前
all-in-rag零散的笔记(自存/持续更新)
笔记