0%

audio_opus

content:

1 简介
2 opus编码总览
控制参数:
2.1. Control Parameters ………………………………….10
2.1.1. Bitrate ……………………………………..10
2.1.2. Number of Channels (Mono/Stereo) ……………….11
2.1.3. Audio Bandwidth ………………………………11
2.1.4. Frame Duration ……………………………….11
2.1.5. Complexity …………………………………..11
2.1.6. Packet Loss Resilience ………………………..12
2.1.7. Forward Error Correction (FEC) …………………12
2.1.8. Constant/Variable Bitrate ……………………..12
2.1.9. Discontinuous Transmission (DTX) ……………….13
3 internal framing
3.1. The TOC Byte ……………………………………….13
3.2. Frame Packing ………………………………………16
3.2.1. Frame Length Coding …………………………..16
3.2.2. Code 0: One Frame in the Packet ………………..16
3.2.3. Code 1: Two Frames in the Packet, Each with
Equal Compressed Size …………………………17
3.2.4. Code 2: Two Frames in the Packet, with
Different Compressed Sizes …………………….17
3.2.5. Code 3: A Signaled Number of Frames in the Packet ..18
3.3. Examples …………………………………………..21
3.4. Receiving Malformed Packets ………………………….22
4 opus decoder
4.1. Range Decoder ………………………………………23
4.1.1. Range Decoder Initialization …………………..25
4.1.2. Decoding Symbols ……………………………..25
4.1.3. Alternate Decoding Methods …………………….27
4.1.4. Decoding Raw Bits …………………………….29
4.1.5. Decoding Uniformly Distributed Integers …………29
4.1.6. Current Bit Usage …………………………….30
4.2. SILK Decoder ……………………………………….32
4.2.1. SILK Decoder Modules ………………………….32
4.2.2. LP Layer Organization …………………………33
4.2.3. Header Bits ………………………………….35
4.2.4. Per-Frame LBRR Flags ………………………….36
4.2.5. LBRR Frames ………………………………….36
4.2.6. Regular SILK Frames …………………………..37
4.2.7. SILK Frame Contents …………………………..37
4.2.7.1. Stereo Prediction Weights ……………..40
4.2.7.2. Mid-Only Flag ………………………..42
4.2.7.3. Frame Type …………………………..43
4.2.7.4. Subframe Gains ……………………….44
4.2.7.5. Normalized Line Spectral Frequency
(LSF) and Linear Predictive Coding (LPC)
Coeffieients …………………………46
4.2.7.6. Long-Term Prediction (LTP) Parameters …..74
4.2.7.7. Linear Congruential Generator (LCG) Seed ..86
4.2.7.8. Excitation …………………………..86
4.2.7.9. SILK Frame Reconstruction ……………..98
4.2.8. Stereo Unmixing ……………………………..102
4.2.9. Resampling ………………………………….103
4.3. CELT Decoder ………………………………………104
4.3.1. Transient Decoding …………………………..108
4.3.2. Energy Envelope Decoding ……………………..108
4.3.3. Bit Allocation ………………………………110
4.3.4. Shape Decoding ………………………………116
4.3.5. Anti-collapse Processing ……………………..120
4.3.6. Denormalization ……………………………..121
4.3.7. Inverse MDCT ………………………………..121
4.4. Packet Loss Concealment (PLC) ……………………….122
4.4.1. Clock Drift Compensation ……………………..122
4.5. Configuration Switching …………………………….123
4.5.1. Transition Side Information (Redundancy) ……….124
4.5.2. State Reset …………………………………127
4.5.3. Summary of Transitions ……………………….128
5 opus encoder
5.1. Range Encoder ……………………………………..132
5.1.1. Encoding Symbols …………………………….133
5.1.2. Alternate Encoding Methods ……………………134
5.1.3. Encoding Raw Bits ……………………………135
5.1.4. Encoding Uniformly Distributed Integers ………..135
5.1.5. Finalizing the Stream ………………………..135
5.1.6. Current Bit Usage ……………………………136
5.2. SILK Encoder ………………………………………136
5.2.1. Sample Rate Conversion ……………………….137
5.2.2. Stereo Mixing ……………………………….137
5.2.3. SILK Core Encoder ……………………………138
5.3. CELT Encoder ………………………………………150
5.3.1. Pitch Pre-filter …………………………….150
5.3.2. Bands and Normalization ………………………151
5.3.3. Energy Envelope Quantization ………………….151
5.3.4. Bit Allocation ………………………………151
5.3.5. Stereo Decisions …………………………….152
5.3.6. Time-Frequency Decision ………………………153
5.3.7. Spreading Values Decision …………………….153
5.3.8. Spherical Vector Quantization …………………154
6. Conformance ……………………………………………155
6.1. Testing …………………………………………..155
6.2. Opus Custom ……………………………………….156
7. Security Considerations …………………………………157
8. Acknowledgements ……………………………………….158
9. References …………………………………………….159
9.1. Normative References ……………………………….159
9.2. Informative References ……………………………..159

opus简述:

  • Opus编解码器是一种实时交互式音频编解码器,旨在满足[要求]中所述的要求。它由一个基于线性预测(LP)[LPC]的层和一个基于改进的离散余弦变换(MDCT)[MDCT]的层组成。使用两层的主要思想如下:在语音中,
    线性预测技术(如code - excited linear prediction 码激励线性预测,或CELP)比变换(如MDCT)域技术更有效地编码低频,而音乐和更高的语音频率则相反。因此,具有两个可用层的编解码器可以在比单独使用其中一个更宽的范围内运行,并且通过组合它们可以实现比单独使用其中一个更好的质量。

  • 本规范的主要规范性部分由附录A中的源代码提供。尽管编码器和解码器共享大量代码,但本软件的解码器部分是规范性的。第6节提供了解码器一致性测试。解码器包含大量需要精确执行的整数和定点算法,包括所有舍入考虑,因此任何有用的规范都需要特定于域的符号语言来充分定义这些操作。此外,必须解决符号表示和包含的引用实现之间的任何冲突。出于兼容性和可测试性的实际原因,在任何不一致的情况下,给予参考实现优先权都是有利的。C语言也是最广泛理解的、人类可读的机器行为符号表示之一。由于这些原因,此RFC使用参考实现作为编解码器的唯一符号表示。

  • 虽然符号表示是明确和完整的,但它并不总是理解编解码器操作的最简单方法。因此,本文档还以散文的形式描述了编解码器的重要部分,并借此机会解释了设计中许多更令人惊讶的元素背后的基本原理。这些描述旨在准确且信息丰富,但普通英语的局限性有时会导致歧义,因此预计读者将始终在阅读符号表示的同时阅读这些描述。为此目的,提供了大量实施参考。这些描述有时在顺序上或通过数学简化与参考不同,因为这种偏差使得解释更容易理解。例如,参考实现中的右移和左移操作通常使用除法和除法来描述

1.1符号约定:

编解码器中的各种操作需要位精确的定点行为,即使在编写浮点实现时也是如此。符号“Q”,其中n是整数,表示定点数字中小数点右侧的二进制位数。例如,16位字中的有符号Q14值可以表示从-2.0到1.999938964875(包括-2.0)的值。此符号仅供参考。当描述算术时,总是对基础整数进行运算。例如,文本将明确指示乘法后所需的任何移位。

文本中包含的表达式遵循C运算符规则和优先级,但语法“x**y”表示x升为y的幂。本文还使用了以下功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
1.1.1.  min(x,y)

The smallest of two values x and y.

1.1.2. max(x,y)

The largest of two values x and y.

1.1.3. clamp(lo,x,hi)

clamp(lo,x,hi) = max(lo,min(x,hi))

With this definition, if lo > hi, then lo is returned.

1.1.4. sign(x)

The sign of x, i.e.,

( -1, x < 0
sign(x) = < 0, x == 0
( 1, x > 0

1.1.5. abs(x)

The absolute value of x, i.e.,

abs(x) = sign(x)*x

1.1.6. floor(f)

The largest integer z such that z <= f.

1.1.7. ceil(f)

The smallest integer z such that z >= f.

1.1.8. round(f)

The integer z nearest to f, with ties rounded towards negative
infinity, i.e.,

round(f) = ceil(f - 0.5)

1.1.9. log2(f)

The base-two logarithm of f.

1.1.10. ilog(n)

The minimum number of bits required to store a positive integer n in
binary, or 0 for a non-positive integer n.

( 0, n <= 0
ilog(n) = <
( floor(log2(n))+1, n > 0

Examples:

o ilog(-1) = 0

o ilog(0) = 0

o ilog(1) = 1

o ilog(2) = 2

o ilog(3) = 2

o ilog(4) = 3


2 Opus编解码器概述:

Opus编解码器从6 kbit/s窄带单声道语音扩展到510 kbit/s全波段立体声音乐,算法延迟从5 ms到65.2 ms。在任何给定时间,LP层、MDCT层或两者都可能处于活动状态。它可以在所有不同的操作模式之间无缝切换,使其具有很大的灵活性以适应不同的内容和网络条件,而无需重新协商当前会话。编解码器允许输入和输出各种音频带宽,定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+----------------------+-----------------+-------------------------+
| Abbreviation | Audio Bandwidth | Sample Rate (Effective) |
+----------------------+-----------------+-------------------------+
| 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 |
+----------------------+-----------------+-------------------------+
表1

(*)尽管采样定理允许带宽高达采样率的一半,但Opus从未对20 kHz以上的音频进行编码,因为这是公认的人类听力上限。

  • Opus定义了有效采样率为24 kHz的超宽带(SWB),与其他一些使用32 kHz的音频编码标准不同。选择这一点有很多原因。MDCT层中的频带布局自然允许跳过12 kHz以上频率的系数,但不允许仅将那些超过16 kHz的频率完全丢弃。24 kHz的采样率也使得MDCT层中的重采样更容易,因为24均匀地除以48,并且当24 kHz足够时,它可以节省其他处理中的计算,例如声学回波消除(AEC)。对频带布局进行实验性更改,以允许16 kHz截止(32 kHz有效采样率)显示在其他采样率下存在潜在的质量下降,并且在典型比特率下,使用这种截止而不是在全频带(FB)模式下编码所节省的比特数非常少。因此,如果应用程序希望处理以32 kHz采样的信号,则应仅使用FB。

  • LP层基于SILK编解码器[SILK]。它支持10毫秒到60毫秒的NB、MB或WB音频和帧大小,并且需要额外的5毫秒前瞻以进行噪声整形估计。采样率转换可能需要额外的小延迟(高达1.5 ms)。与Vorbis[Vorbis-WEBSITE]和许多其他现代编解码器一样,SILK天生就是为可变比特率(VBR)编码而设计的,尽管编码器也可以产生恒定比特率(CBR)流。Opus中使用的SILK版本实质上是从Skype先前部署的独立SILK编解码器修改而来的,与之不兼容。本文档不用于定义该格式,但对原始SILK编解码器感兴趣的人应该查看[SILK]。

  • MDCT层基于受限能量重叠变换(CELT)编解码器[CELT]。它支持NB、WB、SWB或FB音频,并且其帧大小从2.5 ms到20 ms,并且由于MDCT窗口重叠,需要额外的2.5 ms前瞻。CELT编解码器本质上是为CBR编码而设计的,但与许多CBR编解码器不同,它不限于一组预定速率。它在内部分配位来精确地填充任何给定的目标预算,编码器可以通过每帧改变目标来生成VBR流。当音频带宽为WB或更低时,MDCT层不用于语音,因为它在那里没有用处。另一方面,非语音信号并不总是使用线性预测进行充分编码。因此,MDCT层应用于音乐信号。

  • “混合”模式允许以10或20 ms的帧大小和SWB或FB音频带宽同时使用两层。LP层通过将信号重采样到WB来对低频进行编码。随后是MDCT层,对信号的高频部分进行编码。两者之间的截止频率为8 kHz,即最大WB音频带宽。在MDCT层中,所有低于8 kHz的频带都被丢弃,因此两层之间没有编码冗余。

  • 采样率(与实际音频带宽相反)可在编码器和解码器侧独立选择,例如,全频带(fullband)信号可解码为宽带(wideband),反之亦然。这种方法确保发送方和接收方始终可以互操作,而不管其实际音频硬件的功能如何。在内部,LP层始终以音频带宽两倍的采样率运行,最高可达16 kHz,它将继续用于SWB和FB。解码器只需重新采样其输出,以支持不同的采样率。MDCT层始终以48 kHz的采样率在内部运行。由于所有支持的采样率均匀地划分该速率,并且由于解码器可以容易地将频域中频谱的高频部分归零,因此它可以简单地抽取MDCT层输出,以非常便宜地实现其他支持的采样率。

  • 在转换到普通的、期望的输出采样率之后,解码器将两层的输出简单地相加。为了补偿各层所需的不同前瞻性,CELT编码器输入额外延迟2.7毫秒。这确保低频和高频同时到达。编码器可以通过使用较少的噪声整形前瞻或在LP层中使用更简单的重采样器来减少额外延迟,但这会降低质量。然而,在编码器中不能减少CELT层中的基本2.5 ms前瞻,因为MDCT重叠需要它,其大小由解码器固定。

  • 这两层都使用相同的熵编码器,避免了它们之间“填充位”的浪费。这种混合方法使得CBR和VBR编码都可以很容易地支持。尽管LP层是VBR,但MDCT层的位分配可以通过使用LP层未使用的所有位来生成最终的CBR流。

2.1 控制参数:

Opus编解码器包括许多控制参数,这些参数可以在编解码器的常规操作期间动态更改,而不会中断从编码器到解码器的音频流。这些参数仅影响编码器,因为它们对比特流的任何影响都在带内发出信号,使得解码器可以在没有带外信号的情况下解码任何Opus流。任何Opus实现都可以添加或修改这些控制参数,而不会影响互操作性。参考编码器中最重要的编码器控制参数如下所示。

2.1.1. 比特率

Opus支持从6 kbit/s到510 kbit/s的所有比特率。所有其他参数相等时,比特率越高,质量越高。对于20 ms的帧大小,以下是各种配置中OPU的比特率“最佳点”:
o 8-12 kbit/s for NB speech,
o 16-20 kbit/s for WB speech,
o 28-40 kbit/s for FB speech,
o 48-64 kbit/s for FB mono music, and
o 64-128 kbit/s for FB stereo music.

2.1.2 通道数(单声道/立体声)

Opus可以在单个流中传输单声道或立体声帧。在立体声解码器中解码单声道帧时,左声道和右声道是相同的,在单声道解码器中解码立体声帧时,单声道输出是左声道和右声道的平均值。在某些情况下,希望以单声道编码立体声输入流(例如,因为比特率太低,无法以足够的质量编码立体声)。编码的通道数可以实时选择,但默认情况下,参考编码器会尝试在给定当前比特率的情况下做出最佳决策。

2.1.3. 音频带宽

Opus支持的音频带宽如表1所示。就像信道数量一样,任何解码器都可以对在任何带宽上编码的音频进行解码。例如,以8khz工作的任何Opus解码器可以解码FB Opus帧,并且以48khz工作的任何Opus解码器可以解码NB帧。类似地,参考编码器可以获取48 kHz输入信号并将其编码为NB。音频带宽越高,达到可接受质量所需的比特率越高。音频带宽可以实时明确指定,但默认情况下,参考编码器会尝试在给定当前比特率的情况下做出最佳带宽决策。

2.1.4. 帧持续时间

Opus可以对2.5、5、10、20、40或60毫秒的帧进行编码。它还可以将多个帧组合成高达120毫秒的数据包。对于实时应用程序,每秒发送更少的数据包可以降低比特率,因为它减少了IP、UDP和RTP报头的开销。然而,它增加了延迟和对数据包丢失的敏感性,因为丢失一个数据包就意味着丢失更大的音频块。增加帧持续时间也会略微提高编码效率,但对于大于20 ms的帧大小,增益会变小。因此,对于大多数应用,20 ms帧是一个不错的选择。

2.1.5. 复杂性

Opus编码过程的各个方面都可以在CPU复杂性和质量/比特率之间进行权衡。在参考编码器中,使用0到10之间的整数选择复杂度,其中0是最低复杂度,10是最高复杂度。可能出现此类权衡的计算示例如下:

  • The order of the pitch analysis whitening filter [WHITENING],基音分析白化滤波器的阶数[白化],
  • The order of the short-term noise shaping filter,短期噪声整形滤波器的阶数
  • The number of states in delayed decision quantization of the residual signal, and 剩余信号的延迟判决量化中的状态数,以及
  • The use of certain bitstream features such as variable time-frequency resolution and the pitch post-filter.某些比特流特性的使用,如可变时频分辨率和基音后置滤波器。
2.1.6. 丢包恢复能力

音频编解码器通常利用帧间相关性来降低比特率,但要以错误传播为代价:丢失一个数据包后,需要接收多个数据包,解码器才能准确地重建语音信号。Opus利用帧间相关性的程度可以动态调整,以在比特率和错误传播量之间进行权衡。

2.1.7. 前向纠错(FEC)

提供抗分组丢失鲁棒性的另一种机制是带内前向纠错(FEC)。被确定为包含感知上重要的语音信息(例如启动或瞬态)的分组以较低的比特率再次编码,并且该重新编码的信息被添加到后续分组中。

2.1.8. 恒定/可变比特率

Opus在使用默认的可变比特率(VBR)操作时效率更高。当需要通过相对较慢的连接进行低延迟传输时,也可以使用受限VBR。这以模拟“比特库”的方式使用VBR,相当于MP3(MPEG 1,第3层)和AAC(高级音频编码)所称的CBR(即,由于比特库而不是真正的CBR)。在某些(罕见)应用中,需要恒定比特率(CBR)。在CBR模式下运行有两个主要原因:

  • 当传输仅支持每个压缩帧的固定大小时,或
  • 当加密用于高度受限(例如,是/否,录制的提示)或高度敏感[SRTP-VBR]的音频流时。
    即使使用敏感数据,只要变化不是由输入信号驱动的(例如,为了匹配不断变化的网络条件),比特率仍然可以变化。要实现这一点,应用程序仍应在CBR模式下运行Opus,但在每个数据包之前更改目标速率。
2.1.9. 不连续传输(DTX)

不连续传输(DTX)在静音或背景噪声期间降低比特率。启用DTX时,每400毫秒仅对一帧进行编码。

3 内部组帧:

Opus编码器产生“数据包”,每个数据包都是一组连续的字节,将作为单个单元传输。这里描述的数据包不包括诸如IP、UDP或RTP头之类的东西,它们通常在传输层数据包中找到。一个数据包可以包含多个音频帧,只要它们共享一组公共参数,包括操作模式、音频带宽、帧大小和通道计数(单声道与立体声)。本节描述了这些参数和用于将多个帧打包到单个数据包中的内部帧的可能组合。这种组帧不是自我界定的。相反,它假设较低的层(例如UDP或RTP[RFC3550]或Ogg[RFC3533]或Matroska[Matroska-WEBSITE])将传输数据包的长度(以字节为单位),并使用此信息来减少数据包本身的帧开销。解码器实现必须支持本节中描述的帧。附录B中描述了框架的另一种自定界变体。该变体的支持是可选的。

本文档中的所有位图都对位进行了编号,因此位0是第一个字节的最高有效位,位7是最低有效位。因此,第8位是第二字节的最高有效位,等等。格式良好的Opus数据包遵守某些要求,标记为下面的[R1]到[R7]。第3.4节总结了这些问题以及处理格式错误数据包的适当方法。

3.1 TOC字节 (单字节)

格式良好的Opus数据包必须至少包含一个字节[R1]。该字节形成一个目录(TOC)头,该头表示给定数据包使用的各种模式和配置中的哪一种。它由配置号“config”、立体声标志“s”和帧计数代码“c”组成,如图1所示。下面是对这些字段的描述。

1
2
3
4
5
6
 0
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
| config |s| c |
+-+-+-+-+-+-+-+-+
图1:TOC字节
  • TOC字节的前五位标记为“config”,对32种可能的操作模式、音频带宽和帧大小配置之一进行编码。As described, the LP (SILK) layer and MDCT (CELT) layer can be combined in three possible operating modes:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
       1.  A SILK-only mode for use in low bitrate connections with an audio bandwidth of WB(wideband) or less,
    2. A Hybrid (SILK+CELT) mode for SWB(super-wideband) or FB(fullband) speech at medium bitrates, and
    3. A CELT-only mode for very low delay speech transmission as well as music transmission (NB(narrowband) to FB).
    32种可能的配置分别标识数据包使用哪种操作模式,以及音频带宽和帧大小。表2列出了每个配置的参数。
    +-----------------------+-----------+-----------+-------------------+
    | Configuration | Mode | Bandwidth | Frame Sizes |
    | Number(s) | | | |
    +-----------------------+-----------+-----------+-------------------+
    | 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 |
    +-----------------------+-----------+-----------+-------------------+

    Table 2: TOC Byte Configuration Parameters
    每个范围内的配置编号(e.g., 0...3 for NB SILK-only)以相同的顺序对应不同的frame size选择。例如,配置0的帧大小为10毫秒,配置3的帧大小为60毫秒。
  • 接下来的一bit,表示是否为立体声: One additional bit, labeled “s”, signals mono vs. stereo, with 0indicating mono and 1 indicating stereo.

  • 接下来的两位,表示一个packet有几个frame:The remaining two bits of the TOC byte, labeled “c”, code the number of frames per packet (codes 0 to 3) as follows:

    1
    2
    3
    4
    5
    6
    7
    o  0: 1 frame in the packet
    o 1: 2 frames in the packet, each with equal compressed size
    o 2: 2 frames in the packet, with different compressed sizes
    o 3: an arbitrary number of frames in the packet

    This document refers to a packet as a code 0 packet, code 1 packet,
    etc., based on the value of "c".
3.2 frame packing 帧如何打包:

本节描述如何根据TOC字节中每个可能的“c”值打包帧。

3.2.1 Frame Length Coding

当数据包包含多个VBR帧(即code 2或3)时,这些帧中的一个或多个的压缩长度用一个或两个字节序列表示,第一个字节的含义如下:

1
2
3
o 0:无帧(不连续传输(DTX)或丢失数据包)
o 1251:帧的长度(字节)
o 252255:需要第二个字节。总长度为(第二个字节*4)+第一个字节
  • 特殊长度0表示没有可用的帧,这可能是因为它在传输过程中被某个中介丢弃,也可能是因为编码器选择不传输它。任何模式下的任何Opus帧的长度都可以为0。

  • 最大可表示长度为255*4+255=1275字节。对于20 ms帧,这表示510 kbit/s的比特率,这大约是无损压缩全波段立体声音乐的最高有用速率。除此之外,无损编解码器更合适。它也大致是MDCT层的最大可用速率,因为此后不久,由于码本大小的限制,质量不再随附加比特而提高。

  • 对于VBR数据包中的最后一帧或CBR数据包中的任何帧,不传输长度,因为可以从数据包的总大小和数据包中所有其他数据的大小推断长度。但是,任何单个帧的长度不得超过1275字节[R2],以允许通过网关、会议网桥或其他软件重新打包。

3.2.2. Code 0: One Frame in the Packet 数据包中只有一个帧

对于code 0数据包,TOC字节后面紧跟着单个帧的N-1字节压缩数据(其中N是数据包的大小),如图2所示。

1
2
3
4
5
6
7
8
9
10
11
     0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| config |s|0|0| |
+-+-+-+-+-+-+-+-+ |
| Compressed frame 1 (N-1 bytes)... :
: |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 2: A Code 0 Packet
3.2.3. code 1:数据包中的两个帧,每个帧具有相同的压缩大小

对于code 1数据包,TOC字节后面紧跟第一帧的(N-1)/2字节压缩数据,然后紧跟第二帧的(N-1)/2字节压缩数据,如图3所示。对于所有code 1数据包,可用于压缩数据的有效负载字节数N-1必须为偶数[R3]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
     0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| config |s|0|1| |
+-+-+-+-+-+-+-+-+ :
| Compressed frame 1 ((N-1)/2 bytes)... |
: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ :
| Compressed frame 2 ((N-1)/2 bytes)... |
: +-+-+-+-+-+-+-+-+
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 3: A Code 1 Packet
3.2.4. code 2:数据包中有两个帧,压缩后大小不同
  • 对于code 2数据包,TOC字节后面是一个单字节或双字节序列,指示第一帧的长度(图4中标记为N1),然后是第一帧的N1字节压缩数据。剩余的N-N1-2或N-N1-3字节是第二帧的压缩数据。这如图4所示。
    code 2数据包必须包含足够的字节来表示有效长度。例如,1字节code 2数据包始终无效,而第二个字节在252…255范围内的2字节code 2数据包也无效。

  • 第一帧的长度N1也必须不大于对所有code 2分组解码该长度后剩余有效载荷的大小[R4]。例如,这使得第二个字节在1…251范围内的2字节code 2数据包也无效(唯一有效的2字节code 2数据包是两个帧的长度均为零的数据包)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
         0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | config |s|1|0| N1 (1-2 bytes): |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ :
    | Compressed frame 1 (N1 bytes)... |
    : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
    | Compressed frame 2... :
    : |
    | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    Figure 4: A Code 2 Packet
Code 3: A Signaled Number of Frames in the Packet
  • Code 3 packets将帧的数量填充在包中的字段,as well as additional padding,称为“Opus padding”,以指示此填充是在Opus层而不是在传输层添加的。

  • Code 3数据包必须至少有2个字节[R6,R7]。TOC字节后接一个字节,该字节编码数据包中的帧数,其位为2到7(图5中标记为“M”),位1表示是否插入了Opus填充(图5中标记为“p”),位0表示VBR(图5中标记为“v”)。M不得为零,且数据包中包含的音频持续时间不得超过120毫秒[R5]。这将任何帧大小的最大帧计数限制为48(对于2.5毫秒帧),对于较长的帧大小,限制为较低。图5显示了帧计数字节的布局。

    1
    2
    3
    4
    5
    6
    7
      0
    0 1 2 3 4 5 6 7
    +-+-+-+-+-+-+-+-+
    |v|p| M |
    +-+-+-+-+-+-+-+-+

    Figure 5: The frame count byte
  • 使用Opus填充时,填充的字节数以帧计数字节后的字节进行编码。0…254的值表示除了0…254字节之外,还包括0…254字节的填充

  • 用于指示填充大小的字节。如果值为255,则附加填充的大小为254字节,加上下一个字节中编码的填充值。在这种情况下,数据包中必须至少还有一个字节[R6,R7]。额外的填充字节出现在数据包的末尾,编码器必须将其设置为零,以避免创建隐蔽通道。但是,解码器必须接受填充字节的任何值。

  • 尽管这种编码提供了多种方法来指示给定数量的填充字节,但每种方法都使用不同数量的字节来指示填充大小,因此将以不同的量增加总数据包大小。例如,要向数据包添加255个字节,请将填充位p设置为1,在帧计数字节后插入一个值为254的单字节,并将值为零的254个填充字节追加到数据包的末尾。要向数据包添加256个字节,请将填充位设置为1,在帧计数字节后插入两个字节,分别为255和0,并在数据包末尾追加254个填充字节,值为0。通过多次使用值255,可以创建任意特定、所需大小的数据包。设P为用于指示填充大小的报头字节数加上填充字节数(即,P为添加到数据包的总字节数)。那么,P必须不大于N-2[R6,R7]。

  • 在CBR情况下,让R=N-2-P为减去(可选)填充后数据包中剩余的字节数。然后,每个帧的压缩长度(以字节为单位)等于R/M。值R必须是M[R6]的非负整数倍。所有M帧的压缩数据如下,每个大小为R/M字节,如图6所示。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | config |s|1|1|0|p| M | Padding length (Optional) :
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | |
    : Compressed frame 1 (R/M bytes)... :
    | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | |
    : Compressed frame 2 (R/M bytes)... :
    | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | |
    : ... :
    | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | |
    : Compressed frame M (R/M bytes)... :
    | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    : Opus Padding (Optional)... |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    Figure 6: A CBR Code 3 Packet
  • 在VBR的情况下,(可选)填充长度后面是M-1帧长度(在图7中由“N1”到“N[M-1]”表示),每个帧长度按照如上所述的一个或两个字节序列进行编码。删除(可选)填充后,数据包必须包含足够的M-1长度数据,并且这些长度的总和不得大于解码后数据包中剩余的字节数[R7]。所有M帧的压缩数据如下,每个帧由指定数量的字节组成,最终帧在最终填充之前消耗任何剩余字节,如图6所示。报头字节数(TOC字节、帧计数字节、填充长度字节和帧长度字节)加上前M-1帧本身的信号长度,再加上填充的信号长度不得大于数据包总大小N。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | config |s|1|1|1|p| M | Padding length (Optional) :
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    : N1 (1-2 bytes): N2 (1-2 bytes): ... : N[M-1] |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | |
    : Compressed frame 1 (N1 bytes)... :
    | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | |
    : Compressed frame 2 (N2 bytes)... :
    | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | |
    : ... :
    | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | |
    : Compressed frame M... :
    | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    : Opus Padding (Optional)... |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    Figure 7: A VBR Code 3 Packet
3.3 examples:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Simplest case, one NB mono 20 ms SILK frame:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 1 |0|0|0| compressed data... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 8



Two FB mono 5 ms CELT frames of the same compressed size:

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 29 |0|0|1| compressed data... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 9

Two FB mono 20 ms Hybrid frames of different compressed size:

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 15 |0|1|1|1|0| 2 | N1 | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| compressed data... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 10

Four FB stereo 20 ms CELT frames of the same compressed size:

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 31 |1|1|1|0|0| 4 | compressed data... :
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 11
3.4 Receiving Malformed Packets 接收格式错误的数据包

接收方不得将违反上述任何规则的数据包作为正常Opus数据包进行处理。它们是为将来的应用程序保留的,例如带内标头(包含元数据等)。违反这些约束的数据包可能会导致该规范的实现将其视为格式错误并丢弃。
此处总结了这些约束条件,以供参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

[R1] Packets are at least one byte.

[R2] No implicit frame length is larger than 1275 bytes.

[R3] Code 1 packets have an odd total length, N, so that (N-1)/2 is
an integer.

[R4] Code 2 packets have enough bytes after the TOC for a valid
frame length, and that length is no larger than the number of
bytes remaining in the packet.

[R5] Code 3 packets contain at least one frame, but no more than
120 ms of audio total.

[R6] The length of a CBR code 3 packet, N, is at least two bytes,
the number of bytes added to indicate the padding size plus the
trailing padding bytes themselves, P, is no more than N-2, and
the frame count, M, satisfies the constraint that (N-2-P) is a
non-negative integer multiple of M.

[R7] VBR code 3 packets are large enough to contain all the header
bytes (TOC byte, frame count byte, any padding length bytes,
and any frame length bytes), plus the length of the first M-1
frames, plus any trailing padding bytes.

Opus解码器

The Opus decoder consists of two main blocks: the SILK decoder and the CELT decoder.
在任何给定时间,一个或两个SILK和CELT解码器都可能处于活动状态。Opus解码的输出是来自SILK和CELT解码器的输出的总和,在SILK侧具有适当的采样率转换和延迟补偿,在CELT侧具有可选的抽取(当解码到采样率小于48 kHz时),如下图所示。

1
2
3
4
5
6
7
8
9
10
11
                     +---------+    +------------+
| SILK | | Sample |
+->| Decoder |--->| Rate |----+
Bit- +---------+ | | | | Conversion | v
stream | Range |---+ +---------+ +------------+ /---\ Audio
------->| Decoder | | + |------>
| |---+ +---------+ +------------+ \---/
+---------+ | | CELT | | Decimation | ^
+->| Decoder |--->| (Optional) |----+
| | | |
+---------+ +------------+

more :
https://datatracker.ietf.org/doc/html/rfc6716
参考翻译:
https://rfc2cn.com/rfc6716.html

ref:
https://datatracker.ietf.org/doc/html/rfc6716
https://xiph.org/ogg/doc/oggstream.html

PS Webrtc中相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
void WebRtcWrap::createOggFirstPage()
{
string oggString;

ogg_packet data;

ogg_page page;

string headString;
data.b_o_s = 1;
data.e_o_s = 0;

headString.append("OpusHead", 8);
char version = 1;
headString.append(1, version);
char channels = 2;
headString.append(1, channels);
char padding = 0;
headString.append(2, padding);
uint32_t sampleRate = 48000;
headString.append((char*)& sampleRate, 4);
char gain = 0;
headString.append(2, gain);
char mapping = 0;
headString.append(1, mapping);

//oggString.append((char*)rtpData.payLoad.data() + pRtpHeader->getHeaderLength(), rtpData.payLoad.size() - pRtpHeader->getHeaderLength());
data.packet = (unsigned char*)headString.data();
data.bytes = headString.size();

data.packetno = 0;
data.granulepos = 0;

ogg_stream_packetin(&m_oggEncoder, &data);
//ogg_stream_flush(&m_oggEncoder, &page);
int result = ogg_stream_flush(&m_oggEncoder, &page);
if (result)
{
oggString.append((char*)page.header, (int)page.header_len);
oggString.append((char*)page.body, (int)page.body_len);
}

//2nd packet
string comment;
comment.append("OpusTags");
uint32_t len = 4;
comment.append((char*)& len, 4);
comment.append("hhhh");
len = 0;
comment.append((char*)& len, 4);

data.packet = (unsigned char*)comment.data();
data.bytes = comment.size();
data.packetno = 1;
data.b_o_s = 0;
data.e_o_s = 0;
data.granulepos = 0;

ogg_stream_packetin(&m_oggEncoder, &data);
result = ogg_stream_flush(&m_oggEncoder, &page);

if (result)
{
oggString.append((char*)page.header, (int)page.header_len);
oggString.append((char*)page.body, (int)page.body_len);
}

//fwrite(oggString.data(), oggString.size(), 1, m_fp);
//fflush(m_fp);

m_oggFirstPageStr = oggString;
FUNLOG(Info, "createOggFirstPage for uuid %s uid=%lu m_oggFirstPageStr size=%u", m_uuid.c_str(), m_uid, m_oggFirstPageStr.size());
}