Audio Opus Codec
Abstract |
Opus Codec |
Authors |
Walter Fan |
Status |
v1.0 |
Updated |
2026-03-20 |
概述
Opus 是 IETF 标准化的开源音频编解码器(RFC 6716),是 WebRTC 的**强制实现**(mandatory-to-implement)音频编解码器。 它由 Skype 的 SILK 语音编码器和 Xiph.Org 的 CELT 音乐编码器融合而成,兼顾语音和音乐的优秀表现。
为什么 Opus 是 WebRTC 的首选?
唯一被 WebRTC 规范要求所有实现必须支持的音频编解码器
6 kb/s ~ 510 kb/s 的超宽比特率范围,覆盖电话到高保真音乐
5 ms 的算法延迟,满足实时交互需求
内置 FEC 和带宽自适应,天然适合不稳定网络
从窄带(8 kHz)到全频带(48 kHz)无缝切换,无需重新协商
核心特性一览:
特性 |
说明 |
|---|---|
比特率 |
6 kb/s ~ 510 kb/s,支持 CBR 和 VBR |
采样率 |
8 kHz(窄带)~ 48 kHz(全频带) |
帧大小 |
2.5 ms / 5 ms / 10 ms / 20 ms / 40 ms / 60 ms |
声道 |
单声道、立体声,最多 255 通道(多流) |
算法延迟 |
最低 5 ms(CELT-only),典型 26.5 ms(SILK 20ms 帧) |
丢包恢复 |
内置 In-band FEC + PLC + DRED(Opus 1.5+) |
自适应 |
比特率、带宽、帧大小、模式均可逐帧动态调整 |
互联网音频编码需求
Requirements for an Internet Audio Codec
音频带宽定义
名称 |
音频带宽 |
有效采样率 |
|---|---|---|
NB(窄带 narrowband) |
4 kHz |
8 kHz |
MB(中带 medium-band) |
6 kHz |
12 kHz |
WB(宽带 wideband) |
8 kHz |
16 kHz |
SWB(超宽带 super-wideband) |
12 kHz |
24 kHz |
FB(全频带 fullband) |
20 kHz |
48 kHz |
应用场景
点对点通话(VoIP)
多方会议(Conferencing)
远程呈现(Telepresence)
游戏内语音(In-game voice chat)
远程音乐表演 / 在线音乐教学
延迟容忍型网络 / 一键通(Push-to-talk)
编解码器架构
Opus 的设计核心是将两种互补的编码技术融合到一个统一框架中:
┌─────────────────────────────────────────────────────────────┐
│ Opus Encoder │
│ │
│ PCM Input ──► ┌──────────────────────────────────────┐ │
│ │ Mode Decision │ │
│ │ (根据信号类型/比特率/带宽自动选择) │ │
│ └───┬──────────┬──────────┬────────────┘ │
│ │ │ │ │
│ ┌─────▼────┐ ┌──▼───────┐ ┌▼──────────────┐ │
│ │ SILK │ │ Hybrid │ │ CELT │ │
│ │ (LP层) │ │(SILK+CELT)│ │ (MDCT层) │ │
│ │ │ │ │ │ │ │
│ │ 语音优化 │ │ 超宽带/ │ │ 音乐/低延迟 │ │
│ │ NB~WB │ │ 全频带 │ │ NB~FB │ │
│ │ ≥10ms帧 │ │ 10/20ms帧│ │ ≥2.5ms帧 │ │
│ └─────┬────┘ └──┬───────┘ └┬──────────────┘ │
│ │ │ │ │
│ └─────────┴──────────┘ │
│ │ │
│ ┌─────────▼──────────┐ │
│ │ Range Encoder │ │
│ │ (熵编码器) │ │
│ └─────────┬──────────┘ │
│ │ │
│ Opus Packet (TOC + Payload) │
└──────────────────────────────┴──────────────────────────────┘
三种操作模式
模式 |
带宽 |
帧大小 |
适用场景 |
|---|---|---|---|
SILK-only |
NB / MB / WB |
10~60 ms |
低比特率语音通话,电话级质量。利用线性预测(LP)对语音频谱建模 |
Hybrid |
SWB / FB |
10 / 20 ms |
中高比特率超宽带/全频带语音。低频用 SILK,高频用 CELT 补充 |
CELT-only |
NB ~ FB |
2.5~20 ms |
音乐、低延迟语音。使用 MDCT 变换编码,最低仅 5 ms 延迟 |
模式选择是自动的——编码器根据信号类型(语音/音乐)、目标比特率、带宽等参数,逐帧决定使用哪种模式。
也可以通过 OPUS_SET_SIGNAL 和 OPUS_SET_APPLICATION 手动引导。
SILK 层详解
SILK 是 Opus 中负责语音编码的层,源自 Skype 的同名编解码器,基于 **CELP**(Code-Excited Linear Prediction)框架。
编码原理
CELP 的核心思想是:语音信号 = 声道滤波器(声道模型)× 激励信号(残差)
PCM 输入
│
▼
┌────────────────────┐
│ 线性预测分析 (LP) │ ← 提取声道特征(LPC 系数 → 转为 LSF/LSP)
│ 10~16 阶 │
└────────┬───────────┘
│
▼
┌────────────────────┐
│ LP 残差计算 │ ← 原始信号 - LP 预测信号 = 残差
└────────┬───────────┘
│
▼
┌────────────────────┐
│ 长时预测 (LTP) │ ← 利用基音周期(pitch)对残差建模
│ 又称 Pitch Filter │ 消除浊音的周期性冗余
└────────┬───────────┘
│
▼
┌────────────────────┐
│ 激励量化 │ ← 对最终残差进行标量/向量量化
│ (Excitation) │
└────────┬───────────┘
│
▼
Range Encoder 输出
关键参数:
参数 |
说明 |
|---|---|
LP 阶数 |
NB: 10 阶, MB: 12 阶, WB: 16 阶 |
子帧 |
每帧分为 4 个子帧(20ms 帧时每子帧 5ms) |
基音周期 |
搜索范围 2~18 ms,对浊音(voiced speech)编码至关重要 |
LSF 量化 |
LP 系数转为 Line Spectral Frequencies,使用多级 VQ 量化 |
噪声整形 |
通过感知加权滤波器将量化噪声推入不敏感频段 |
CELT 层详解
CELT(Constrained Energy Lapped Transform)是 Opus 中负责通用音频/音乐编码的层,基于 MDCT 变换编码。
编码原理
PCM 输入
│
▼
┌─────────────────────┐
│ 预加重 (Pre-emphasis)│ ← 减少频谱泄漏
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ MDCT 变换 │ ← 时域 → 频域,窗口重叠 2.5ms
│ (Modified DCT) │
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ 频带划分 │ ← MDCT 系数按 Bark 尺度分组
│ (Bark-scale bands) │ 每个频带独立处理
└────────┬────────────┘
│
├── 能量编码 ──► 粗量化(6dB 精度)+ 细量化(可变精度)
│ 用前帧预测 + 熵编码
│
└── 形状编码 ──► PVQ(Pyramid Vector Quantization)
归一化到单位球面,用 K 个有符号脉冲编码
关键设计:
Bark 尺度频带:MDCT 系数分组近似临界带宽,低频细分、高频粗分,符合人耳感知
能量-形状分离:每个频带先编码能量(响度),再编码归一化后的频谱形状
PVQ 量化:将每个频带的归一化频谱向量投影到 N 维单位球面,用 K 个脉冲的组合表示
隐式比特分配:编码器和解码器使用相同算法分配比特,无需额外信令
带间折叠(Band Folding):当某个频带比特不足时,复制其他频带的形状作为替代,避免频谱空洞
Hybrid 模式
当需要超宽带(SWB)或全频带(FB)的语音质量时,Opus 同时启用 SILK 和 CELT:
┌──────────────────────────────────┐
│ 频率 (Hz) │
0 8000 16000 24000 │
│◄─── SILK (LP) ────►│ │
│ │◄────── CELT (MDCT) ────────►│
└──────────────────────────────────┘
SILK 负责 0 ~ 8 kHz 的低频语音部分(线性预测对语音基频和共振峰效率最高)
CELT 负责 8 kHz 以上的高频部分(变换编码对高频和瞬态信号更高效)
带间平滑过渡通过交叉衰减(crossfade)实现
Hybrid 模式仅在 SWB/FB + 10/20 ms 帧时可用
内部分帧与 TOC
Opus 包(packet)的结构定义了如何将编码数据封装在 RTP 载荷中。
TOC 字节
每个 Opus 包的第一个字节是 TOC(Table Of Contents),编码了该包的配置信息:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
| config |s| c |
+-+-+-+-+-+-+-+-+
config (bit 0-4):5 位,表示操作模式 + 音频带宽 + 帧大小(共 32 种配置)
s (bit 5):0 = 单声道,1 = 立体声
c (bit 6-7):帧数编码
+---------+-----------+-----------+-------------------+
| Config | Mode | Bandwidth | Frame Sizes |
+---------+-----------+-----------+-------------------+
| 0...3 | SILK-only | NB | 10, 20, 40, 60 ms |
| 4...7 | SILK-only | MB | 10, 20, 40, 60 ms |
| 8...11 | SILK-only | WB | 10, 20, 40, 60 ms |
| 12...13 | Hybrid | SWB | 10, 20 ms |
| 14...15 | Hybrid | FB | 10, 20 ms |
| 16...19 | CELT-only | NB | 2.5, 5, 10, 20 ms |
| 20...23 | CELT-only | WB | 2.5, 5, 10, 20 ms |
| 24...27 | CELT-only | SWB | 2.5, 5, 10, 20 ms |
| 28...31 | CELT-only | FB | 2.5, 5, 10, 20 ms |
+---------+-----------+-----------+-------------------+
帧数编码 c 的含义:
0:1 帧1:2 帧,压缩大小相同2:2 帧,压缩大小不同3:任意数量的帧(CBR 或 VBR,由后续字节指定)
RTP 时间戳递增
类型 |
采样率 |
2.5ms |
5ms |
10ms |
20ms |
40ms |
60ms |
|---|---|---|---|---|---|---|---|
时间戳增量 |
全部 |
120 |
240 |
480 |
960 |
1920 |
2880 |
voice 模式 |
NB~FB |
x |
x |
o |
o |
o |
o |
audio 模式 |
NB~FB |
o |
o |
o |
o |
x |
x |
o 表示推荐,x 表示可用但非最优。WebRTC 中最常用的是 20ms 帧(960 个采样 @48kHz)。
小技巧
解析 TOC 字节的快速方法:
uint8_t toc = opus_packet[0];
int config = toc >> 3; // 高 5 位
int stereo = (toc >> 2) & 0x01; // 第 6 位
int frame_code = toc & 0x03; // 低 2 位
控制参数详解
参数 |
CTL 宏 |
说明 |
|---|---|---|
比特率 |
|
目标比特率(bps)。语音通话建议 24~32 kbps,高质量音乐 64~128 kbps |
复杂度 |
|
0~10。越高 CPU 越高、质量越好。实时场景建议 5~8 |
信号类型 |
|
|
应用类型 |
|
|
带宽 |
|
|
VBR |
|
1=VBR(默认),0=CBR |
VBR 约束 |
|
1=受约束的 VBR(不超过目标太多) |
丢包率 |
|
期望丢包率(0~100),影响 FEC 冗余量 |
FEC |
|
1=启用 In-band FEC |
DTX |
|
1=启用不连续传输(静音时降低比特率) |
FEC(前向纠错)
Opus 的 In-band FEC 仅在 SILK 层工作(因此 ptime ≥ 10ms):
原理:编码器将感知上重要的帧(如语音起始、瞬态)以较低比特率重新编码, 附加到**下一个**包中。解码端收到当前包后,如果发现前一个包丢失,可以从当前包中提取低比特率冗余来恢复。
时间 ──────────────────────────────►
Packet N: [帧N 正常编码]
Packet N+1: [帧N+1 正常编码] + [帧N 低比特率 FEC 冗余]
Packet N+2: [帧N+2 正常编码] + [帧N+1 低比特率 FEC 冗余]
如果 Packet N 丢失:
收到 Packet N+1 后 → 用其中的 FEC 数据恢复帧 N(质量略低但远好于 PLC)
启用条件:
ptime ≥ 10ms(否则 Opus 工作在纯 CELT 模式,无 FEC)
比特率不能太低(需要额外空间存放 LBRR 包),语音建议 ≥ 24 kbps
必须通过
OPUS_SET_INBAND_FEC(1)启用必须通过
OPUS_SET_PACKET_LOSS_PERC告知编码器期望丢包率
编码端配置:
opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1));
opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(20)); // 预期 20% 丢包
解码端使用:
// 检测到 packet N 丢失,但已收到 packet N+1
// 第一步:用 N+1 的 FEC 数据恢复帧 N
opus_decoder_ctl(decoder, OPUS_GET_LAST_PACKET_DURATION(&frame_size));
opus_decode(decoder,
packet_n1_data, packet_n1_len,
pcm_output, frame_size,
1); // decode_fec = 1,从当前包提取前一帧的 FEC
play_audio(pcm_output, frame_size);
// 第二步:正常解码帧 N+1
opus_decode(decoder,
packet_n1_data, packet_n1_len,
pcm_output, frame_size,
0); // decode_fec = 0,正常解码
play_audio(pcm_output, frame_size);
DTX(不连续传输)
当检测到静音或背景噪声时,DTX 将发送频率降低到每 400ms 一帧,大幅节省带宽:
opus_encoder_ctl(encoder, OPUS_SET_DTX(1));
注解
DTX 在多方会议中特别有用——大多数时间只有一两个人在说话,其他人的音频流都处于静默状态。
DRED:深度冗余(Opus 1.5+)
Deep REDundancy (DRED) 是 Opus 1.5(2024 年 3 月发布)引入的革命性丢包恢复技术,在 Opus 1.6(2025 年 12 月)中进一步改进。
传统 FEC vs DRED
特性 |
传统 In-band FEC (LBRR) |
DRED |
|---|---|---|
冗余范围 |
仅前 1 帧 |
最多数秒的历史音频 |
额外带宽 |
较高(低比特率重编码) |
极低(约正常比特率的 1/50) |
技术基础 |
传统信号处理 |
深度学习(神经网络声码器) |
恢复质量 |
可接受 |
接近原始 |
突发丢包 |
只能恢复单帧 |
可恢复数百毫秒甚至数秒 |
工作原理:
DRED 将过去的音频编码为 20 维声学特征向量,以极低比特率嵌入当前包中。 当接收端检测到丢包,可以用最近收到的包中的声学特征,通过神经网络声码器合成丢失的音频。
Packet N: [帧N] + [DRED: 帧N-1到帧N-50的声学特征, ~32kbps 总开销]
如果 Packet N-1 ~ N-10 全部丢失(突发丢包):
收到 Packet N → 用 DRED 声学特征合成 N-1 ~ N-10 的全部音频
启用 DRED(需要 libopus ≥ 1.5):
opus_encoder_ctl(encoder, OPUS_SET_DRED_DURATION(400)); // 最多 400ms 冗余
opus_decoder_ctl(decoder, OPUS_SET_DRED(1));
Deep PLC(深度丢包隐藏)
Opus 1.5 同时引入了基于深度学习的 PLC(Packet Loss Concealment)。 即使没有 FEC/DRED 冗余数据,解码器也能利用神经网络从之前的音频上下文中推断丢失的帧, 效果远好于传统的基于插值的 PLC。
SDP 协商参数
在 WebRTC 的 SDP Offer/Answer 中,Opus 的参数通过 a=fmtp 行传递:
参数 |
默认值 |
说明 |
|---|---|---|
|
(无限制) |
最大平均比特率(bps)。接收方的解码能力上限 |
|
48000 |
最大回放采样率(Hz)。暗示发送方无需编码超过此频率的内容 |
|
10 |
最小包时间(ms)。影响 FEC 可用性 |
|
0 |
1=接收方希望收到立体声 |
|
0 |
1=使用恒定比特率 |
|
0 |
1=接收方支持 FEC 解码 |
|
0 |
1=发送方可以使用 DTX |
|
48000 |
发送方最大采集采样率 |
|
0 |
1=发送方会发立体声 |
SDP 示例
典型的 WebRTC 语音通话 SDP:
m=audio 9 UDP/TLS/RTP/SAVPF 111
a=rtpmap:111 opus/48000/2
a=ptime:20
a=maxptime:60
a=fmtp:111 minptime=10; useinbandfec=1; usedtx=1
高质量音乐共享 SDP:
m=audio 9 UDP/TLS/RTP/SAVPF 111
a=rtpmap:111 opus/48000/2
a=ptime:20
a=fmtp:111 stereo=1; sprop-stereo=1; maxaveragebitrate=128000; maxplaybackrate=48000
注解
opus/48000/2 中的 48000 是 RTP 时钟速率(固定为 48kHz),2 是 RTP 通道数(固定为 2)。
这不代表实际采样率或声道数——实际由 fmtp 参数和编码器配置决定。
实际例子
例 1:C 语言完整编解码
使用 libopus 进行 PCM → Opus → PCM 的完整编解码流程:
#include <opus/opus.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SAMPLE_RATE 48000
#define CHANNELS 1
#define FRAME_SIZE_MS 20
#define FRAME_SIZE (SAMPLE_RATE * FRAME_SIZE_MS / 1000) // 960 samples
#define MAX_PACKET_SIZE 4000
int main(int argc, char *argv[]) {
int err;
// --- 创建编码器 ---
OpusEncoder *encoder = opus_encoder_create(
SAMPLE_RATE, CHANNELS, OPUS_APPLICATION_VOIP, &err);
if (err != OPUS_OK) {
fprintf(stderr, "创建编码器失败: %s\n", opus_strerror(err));
return 1;
}
// 配置编码参数
opus_encoder_ctl(encoder, OPUS_SET_BITRATE(24000));
opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(8));
opus_encoder_ctl(encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1));
opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(10));
// --- 创建解码器 ---
OpusDecoder *decoder = opus_decoder_create(
SAMPLE_RATE, CHANNELS, &err);
if (err != OPUS_OK) {
fprintf(stderr, "创建解码器失败: %s\n", opus_strerror(err));
return 1;
}
// --- 编码一帧 ---
opus_int16 pcm_input[FRAME_SIZE * CHANNELS];
unsigned char opus_packet[MAX_PACKET_SIZE];
// 生成测试信号:1kHz 正弦波
for (int i = 0; i < FRAME_SIZE; i++) {
pcm_input[i] = (opus_int16)(32767.0 * sin(2.0 * 3.14159265 * 1000.0 * i / SAMPLE_RATE));
}
int packet_len = opus_encode(encoder, pcm_input, FRAME_SIZE,
opus_packet, MAX_PACKET_SIZE);
if (packet_len < 0) {
fprintf(stderr, "编码失败: %s\n", opus_strerror(packet_len));
return 1;
}
printf("编码: %d samples → %d bytes (%.1f kbps)\n",
FRAME_SIZE, packet_len, packet_len * 8.0 * 1000 / FRAME_SIZE_MS / 1000);
// 解析 TOC 字节
uint8_t toc = opus_packet[0];
printf("TOC: config=%d, stereo=%d, frame_code=%d\n",
toc >> 3, (toc >> 2) & 1, toc & 3);
// --- 解码一帧 ---
opus_int16 pcm_output[FRAME_SIZE * CHANNELS];
int decoded_samples = opus_decode(decoder, opus_packet, packet_len,
pcm_output, FRAME_SIZE, 0);
if (decoded_samples < 0) {
fprintf(stderr, "解码失败: %s\n", opus_strerror(decoded_samples));
return 1;
}
printf("解码: %d bytes → %d samples\n", packet_len, decoded_samples);
// --- 模拟丢包 + FEC 恢复 ---
// 假设前一帧丢失,用当前包的 FEC 恢复
opus_int16 pcm_fec[FRAME_SIZE * CHANNELS];
int fec_samples = opus_decode(decoder, opus_packet, packet_len,
pcm_fec, FRAME_SIZE, 1); // decode_fec=1
printf("FEC 恢复: %d samples\n", fec_samples);
opus_encoder_destroy(encoder);
opus_decoder_destroy(decoder);
return 0;
}
编译与运行:
# Ubuntu/Debian
sudo apt install libopus-dev
# macOS
brew install opus
# 编译
gcc -o opus_demo opus_demo.c -lopus -lm
# 运行
./opus_demo
例 2:Python 编解码(PyOgg)
import pyogg
import struct
import math
# --- 编码器配置 ---
encoder = pyogg.OpusBufferedEncoder()
encoder.set_application("voip")
encoder.set_sampling_frequency(48000)
encoder.set_channels(1)
encoder.set_frame_size(20) # 20ms
# --- 解码器 ---
decoder = pyogg.OpusDecoder()
decoder.set_channels(1)
decoder.set_sampling_frequency(48000)
# 生成 20ms 的 1kHz 正弦波测试信号 (48000 * 0.02 = 960 samples)
samples = 960
pcm_data = b""
for i in range(samples):
sample = int(32767 * math.sin(2 * math.pi * 1000 * i / 48000))
pcm_data += struct.pack("<h", sample)
# 编码
encoded_packets = encoder.buffered_encode(
memoryview(bytearray(pcm_data)),
flush=True
)
for packet, _, _ in encoded_packets:
print(f"编码: {samples} samples → {len(packet)} bytes")
# 解码
decoded_pcm = decoder.decode(memoryview(bytearray(packet)))
print(f"解码: {len(packet)} bytes → {len(decoded_pcm)//2} samples")
安装依赖:
pip install PyOgg
例 3:WebRTC 中配置 Opus(JavaScript)
在浏览器中通过 WebRTC API 配置 Opus 参数:
const pc = new RTCPeerConnection();
// 获取音频轨道
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const audioTrack = stream.getAudioTracks()[0];
// 添加轨道并获取 transceiver
const transceiver = pc.addTransceiver(audioTrack, { direction: 'sendrecv' });
// 设置 Opus 为首选编解码器
const codecs = RTCRtpReceiver.getCapabilities('audio').codecs;
const opusCodecs = codecs.filter(c => c.mimeType === 'audio/opus');
const otherCodecs = codecs.filter(c => c.mimeType !== 'audio/opus');
transceiver.setCodecPreferences([...opusCodecs, ...otherCodecs]);
// 创建 Offer
const offer = await pc.createOffer();
// 在 SDP 中设置 Opus 参数(useinbandfec, stereo 等)
// 注意:现代浏览器推荐用 setCodecPreferences 而非 SDP munging
let sdp = offer.sdp;
// 查看 Opus 相关的 SDP 行
const opusLines = sdp.split('\n').filter(l =>
l.includes('opus') || l.includes('111')
);
console.log('Opus SDP lines:', opusLines);
await pc.setLocalDescription(offer);
通过 SDP munging 启用 FEC(不推荐但有时需要):
function enableOpusFec(sdp) {
return sdp.replace(
/(a=fmtp:111 .*)/,
'$1;useinbandfec=1'
);
}
function setOpusBitrate(sdp, maxBitrate) {
return sdp.replace(
/(a=fmtp:111 .*)/,
`$1;maxaveragebitrate=${maxBitrate}`
);
}
通过 RTCRtpSender.setParameters() 动态调整比特率:
const sender = pc.getSenders().find(s => s.track?.kind === 'audio');
const params = sender.getParameters();
if (params.encodings.length > 0) {
params.encodings[0].maxBitrate = 32000; // 32 kbps
}
await sender.setParameters(params);
例 4:FFmpeg 处理 Opus
# WAV 转 Opus(VoIP 模式,24kbps)
ffmpeg -i input.wav -c:a libopus -b:a 24k -application voip output.opus
# Opus 转 WAV
ffmpeg -i input.opus -c:a pcm_s16le output.wav
# 从视频中提取音频为 Opus
ffmpeg -i video.mp4 -vn -c:a libopus -b:a 64k audio.opus
# 高质量音乐编码(立体声,128kbps)
ffmpeg -i music.wav -c:a libopus -b:a 128k -application audio music.opus
# 指定帧大小和复杂度
ffmpeg -i input.wav -c:a libopus -b:a 32k \
-frame_duration 20 -compression_level 8 output.opus
# 推送 Opus RTP 流
ffmpeg -re -i input.wav -c:a libopus -b:a 48k \
-f rtp rtp://127.0.0.1:5004
# 接收 Opus RTP 流(需要 SDP 文件描述流格式)
ffmpeg -protocol_whitelist file,udp,rtp -i stream.sdp -c:a pcm_s16le output.wav
例 5:opus-tools 命令行
# 安装
# Ubuntu: sudo apt install opus-tools
# macOS: brew install opus-tools
# 编码
opusenc --bitrate 64 input.wav output.opus
# 高质量编码
opusenc --bitrate 128 --comp 10 input.wav hq_output.opus
# 低延迟编码
opusenc --bitrate 32 --framesize 10 input.wav lowdelay.opus
# 解码
opusdec output.opus decoded.wav
# 查看 Opus 文件信息
opusinfo output.opus
# Opus 文件的详细信息示例输出:
# Processing file "output.opus"...
# Channels: 2
# Rate: 48000
# Pre-skip: 356
# Playback gain: 0 dB
# Encoder: libopus 1.5.2
例 6:Go + Pion WebRTC 中的 Opus 处理
在 Go 后端接收 WebRTC 音频并解码 Opus:
package main
import (
"fmt"
"github.com/pion/webrtc/v4"
"gopkg.in/hraban/opus.v2"
)
func handleAudioTrack(track *webrtc.TrackRemote) {
// 创建 Opus 解码器
dec, err := opus.NewDecoder(48000, 1)
if err != nil {
panic(err)
}
pcmBuf := make([]int16, 960) // 20ms @ 48kHz
buf := make([]byte, 1500)
for {
n, _, readErr := track.Read(buf)
if readErr != nil {
return
}
// 解码 Opus → PCM
samplesDecoded, decErr := dec.Decode(buf[:n], pcmBuf)
if decErr != nil {
fmt.Printf("解码错误: %v\n", decErr)
continue
}
fmt.Printf("解码: %d bytes → %d samples\n", n, samplesDecoded)
// 将 pcmBuf[:samplesDecoded] 写入文件或送入语音识别引擎...
}
}
Go 的 Opus 绑定安装:
# 需要系统安装 libopus
sudo apt install libopus-dev # Ubuntu
brew install opus # macOS
# Go 依赖
go get gopkg.in/hraban/opus.v2
常见问题与调优
比特率选择指南
场景 |
建议比特率 |
说明 |
|---|---|---|
电话级语音 |
8~16 kbps |
窄带,SILK-only,省带宽 |
VoIP 通话 |
20~32 kbps |
宽带/超宽带,清晰语音 |
高质量语音 |
32~64 kbps |
全频带,接近面对面 |
音乐共享 |
64~128 kbps |
立体声,CELT-only 或 Hybrid |
高保真音乐 |
128~256 kbps |
立体声全频带,透明质量 |
常见问题
Q: 为什么 Opus 的 RTP 时钟速率固定是 48000?
A: 这是 RFC 7587 的规定。不管实际采样率是 8kHz 还是 48kHz,RTP 时间戳一律按 48kHz 计算。 这简化了时间戳处理——不需要因为采样率变化而调整时间戳。
Q: 为什么 SDP 中 opus 的通道数固定是 2?
A: a=rtpmap:111 opus/48000/2 中的 2 不表示立体声,而是 Opus RTP 的固定参数。
实际声道数由 fmtp 中的 stereo 参数和编码器配置决定。
Q: FEC 启用后为什么看不到效果?
A: 常见原因:
帧大小 < 10ms(此时 Opus 使用纯 CELT 模式,不支持 FEC)
比特率太低(没有空间放 FEC 冗余数据)
没有设置 ``OPUS_SET_PACKET_LOSS_PERC``(编码器不知道需要添加 FEC)
解码端没有用
decode_fec=1调用opus_decode
Q: 如何判断当前帧使用的是什么模式?
A: 解析 TOC 字节的 config 字段:
int bandwidth;
opus_packet_get_bandwidth(opus_packet); // 返回 OPUS_BANDWIDTH_*
int nb_frames;
nb_frames = opus_packet_get_nb_frames(opus_packet, packet_len);
int nb_samples;
nb_samples = opus_packet_get_nb_samples(opus_packet, packet_len, 48000);
术语表
术语 |
说明 |
|---|---|
CELP |
Code-Excited Linear Prediction,码本激励线性预测 |
MDCT |
Modified Discrete Cosine Transform,修正离散余弦变换 |
LP |
Linear Prediction,线性预测 |
LPC |
Linear Predictive Coding,线性预测编码 |
LSF/LSP |
Line Spectral Frequencies/Pairs,线谱频率/对 |
LTP |
Long-Term Prediction,长时预测(基音预测) |
PVQ |
Pyramid Vector Quantization,金字塔向量量化 |
PLC |
Packet Loss Concealment,丢包隐藏 |
DRED |
Deep REDundancy,深度冗余 |
LBRR |
Low Bitrate Redundancy,低比特率冗余(传统 FEC) |
DTX |
Discontinuous Transmission,不连续传输 |
CBR |
Constant Bit Rate,恒定比特率 |
VBR |
Variable Bit Rate,可变比特率 |
TOC |
Table Of Contents,内容表(Opus 包首字节) |
参考资料
标准文档
官方资源
版本发布