OpusEncoder
1:获得 OpusEncoder结构的大小
int opus_encoder_get_size (int channels);
2:分配和初始化 encoder状态.
一个编码器状态在同一时间不得用于多于一个音频流。同样,编码器状态不能对于每帧重新初始化。
OpusEncoder* opus_encoder_create (opus_int32 Fs, int channels, int application, int *error);
[in] Fs opus_int32: 输入信号的采样率 (Hz),必须是8000、12000、16000、24000、或48000。
[in] channels int:输入信号的通道数 (1 or 2)
[in] application int:编码模式(OPUS_APPLICATION_VOIP/OPUS_APPLICATION_AUDIO/OPUS_APPLICATION_RESTRICTED_LOWDELAY)
[out] error int*: 错误代码
注意:无论选择什么样的采样率和通道数, 如果选择的比特率太低,Opus编码器可以切换到一个较低的音频带宽或通道数。这也意味着总是使用48 kHz立体声输入和让编码器优化编码是安全的。
application:编码模式(三种):
OPUS_APPLICATION_VOIP
:在给定比特率条件下为声音信号提供最高质量,它通过高通滤波和强调共振峰和谐波增强了输入信号。它包括带内前向错误检查以预防包丢失。典型的VOIP应用程序使用这种模式。由于进行了增强,即使是高比特率的情况下,输出的声音与输入相比,听起来可能不一样。
OPUS_APPLICATION_AUDIO
:对大多数非语音信号,如音乐,在给定比特率条件下提供了最高的质量。使用这种模式的场合包括音乐、混音(音乐/声音),广播,和需要不到15 毫秒的信号延迟的其他应用。
OPUS_APPLICATION_RESTRICTED_LOWDELAY
:配置低延迟模式将为减少延迟禁用语音优化模式。这种模式只能在刚初始化或刚重设编码器的情况下使用,因为在这些情况下编解码器的延迟被修改了
3:初始化一个以前分配的编码器状态。
所指向的内存必须至少是opus_encoder_get_size()
返回的大小.
int opus_encoder_init (OpusEncoder *st, opus_int32 Fs, int channels, int application)
4:对一个 Opus帧进行编码
opus_int32 opus_encode (OpusEncoder *st, const opus_int16 *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes)
5:根据浮点输入对一个 Opus帧进行编码
opus_int32 opus_encode_float (OpusEncoder *st, const float *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes)
6:释放一个根据opus_encoder_create()已分配的OpusEncoder 对象。
void opus_encoder_destroy (OpusEncoder *st)
7:改变一些编码器的参数设置。
所有这些参数都已有缺省值,所以只在必要的情况下改变它们
int opus_encoder_ctl (OpusEncoder *st, int request,...)
opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate)); //(比特率)的单位是比特/秒(b / s)
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity)); //(复杂性)是一个值从1到10,1最低,10最高,值越大越复杂
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal_type));(信号的类型)包括OPUS_AUTO (缺省), OPUS_SIGNAL_VOICE, or OPUS_SIGNAL_MUSIC
8:为了对一个帧进行编码,必须正确地用音频数据的帧(2.5, 5, 10, 20, 40 或60 毫秒)来调用
opus_encode()
或opus_encode_float()
函数。
//audio_frame(音频帧)是opus_int16(或用于```opus_encode_float()```的浮点)格式的音频数据
//frame_size(帧大小)是样本中帧的最大数(每个通道)
//packet(包)是写成压缩数据的字节数组
//max_packet是可以写入包的字节数的最大值推荐(4000字节)。不要使用max_packet控制VBR的目标比特率,而应该用OPUS_SET_BITRATE CTL
opus_encode(enc, audio_frame, frame_size, packet, max_packet)
opus_encode()
和opus_encode_float()
返回实际写入包的字节数。返回值可以是负数,这表明一个错误已经发生。如果返回值是1个字节,那么包不需要传播(DTX)。
一旦一个编码器状态已不再需要,可以用以下方式解构:
opus_encoder_destroy(enc);
如果编码器是用opus_encoder_init()
创建的,而不是使用opus_encoder_create()函数,那么不需要采取行动,要求从潜在的释放为它手动分配的内存(上述例子是调用 free(enc))中分离。
////////////////////////////////////////////////////////////////////////////////////////////////////////
OpusDecoder
1:获得OpusDecoder 结构的大小
int opus_decoder_get_size (int channels)
2: 分配和初始化解码器状态
OpusDecoder *opus_decoder_create (opus_int32 Fs, int channels, int *error)
[out] error int*:成功时是 OPUS_OK Success或错误代码
Opus在内部用48000 Hz来存储数据,所以对于FS来说48000 Hz是缺省值。
然而,解码器在8, 12, 16, 和 24 kHz下也可以有效解码到缓冲,所以,由于某些原因调用者不能在全采样率下使用数据,
或知道被压缩的数据不能在全频率范围内使用,可以请求用更小的频率解码。同样的,解码器可以根据调用者的请求,
填充单声道或交叉立体声的PCM缓冲区。
3:初始化以前分配的解码器状态
int opus_decoder_init (OpusDecoder *st, opus_int32 Fs, int channels)
4:解码一个 Opus 包
/*
st: 解码器状态
data 是包含压缩数据的字节数组,输入负载.对包丢失使用一个空指针来表示。
len 是包内字节的精确数量,在输入负载中的字节数
pcm 是opus_int16 (或由 opus_decode_float()定义的浮点型)格式的解码后的音频数据。
输出信号(如果是2通道有交叉)。长度等于frame_size*channels*sizeof(opus_int16)
frame_size是可以放入解码帧的每个通道各样本中帧的最大值,在PCM可用空间中每通道的样本数。
如果小于最大包的时长(120毫秒,4848kHz5760个),这个函数将不能解码一些包。
如果是PLC (data==NULL) 或 FEC (decode_fec=1)的情况,那么frame_size必须正好是丢失音频的时长,
否则解码器无法在解码下一个包时进入优化状态。对于PLC 和 FEC 的情况,frame_size必须是2.5毫秒的倍数。
decode_fec 对于请求任何带内前向错误纠正数据进行解码的状态标志 (0 or 1)
如果没有这样的数据可用,帧在解码时被认为已经丢失
*/
int opus_decode (OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec)
5:解码一个浮点输出的Opus 包
int opus_decode_float (OpusDecoder *st, const unsigned char *data, opus_int32 len, float *pcm, int frame_size, int decode_fec)
(1)opus_decode()和opus_decode_float() 返回从包解码后的每通道样本的数量。如果这个值是负的,表示有错误发生。
如果包损坏或音频缓冲太小不足以容纳解码后的音频,错误就会发生。
Opus是包含重叠块的有状态的编解码器,其结果是Opus 包并不是彼此独立编码。
包必须按正确的次序,连续地进入解码器进行正确的解码。丢失的包可以用遗失隐藏来替换,遗失隐藏用一个空的指针和0长度的包来调用解码器。
(2)一个单独的编解码器状态在一个时间只能由一个单独的线程来访问,调用者执行任何需要的锁定。
各分开的音频数据流可以用各自分开的解码器状态平行地进行解码,除非API库在编译时用了NONTHREADSAFE_PSEUDOSTACK定义。
6:向一个Opus解码器执行CTL 函数
int opus_decoder_ctl (OpusDecoder *st, int request,...)
7:释放通过opus_decoder_create().分配过的OpusDecoder
void opus_decoder_destroy (OpusDecoder *st)
8:将一个 opus 包解析成1个或多个帧
/*
[in] data char*:要进行解析的 Opus包
[in] len opus_int32: 数据的大小
[out] out_toc char*: TOC 指针
[out] frames char*[48] 封装过的帧
[out] size short[48] 封装过的帧的大小
[out] payload_offset int*: 返回在包内负载的位置(按字节)
返回: 帧的数量
*/
int opus_packet_parse (const unsigned char *data, opus_int32 len, unsigned char *out_toc, const unsigned char *frames[48], short size[48], int *payload_offset);
Opus_decode在内部执行这个操作,所以大多数应用程序不需要用到这个函数。这个函数不复制各帧,返回的指针是输入包内部的指针。
9:获得一个 Opus包的带宽
/*
[in] data char*: Opus 包
返回值:
OPUS_BANDWIDTH_NARROWBAND 窄带 (4kHz bandpass)
OPUS_BANDWIDTH_MEDIUMBAND 中等带宽 (6kHz bandpass)
OPUS_BANDWIDTH_WIDEBAND 宽带 (8kHz bandpass)
OPUS_BANDWIDTH_SUPERWIDEBAND 高宽带 (12kHz bandpass)
OPUS_BANDWIDTH_FULLBAND 全宽带 (20kHz bandpass)
OPUS_INVALID_PACKET 通过的被压缩数据已损坏或其格式不被支持
*/
int opus_packet_get_bandwidth (const unsigned char *data)
1:获得Opus 包每帧的样本数
int opus_packet_get_samples_per_frame (const unsigned char *data, opus_int32 Fs)
2:获得Opus 包的通道数
/*
[in] dec OpusDecoder*: 解码器状态
[in] packet char*: Opus包
[in] len opus_int32: 包的长度
返回:样本的数量
返回值:OPUS_INVALID_PACKET:通过的被压缩数据已损坏或其格式不被支持。
*/
int opus_packet_get_nb_channels (const unsigned char *data)
3:获得Opus 包所有帧的数量
int opus_packet_get_nb_frames (const unsigned char packet[], opus_int32 len)
4:获得Opus 包的样本数
int opus_packet_get_nb_samples (const unsigned char packet[], opus_int32 len, opus_int32 Fs)
5:获得Opus 包的样本数
int opus_decoder_get_nb_samples (const OpusDecoder *dec, const unsigned char packet[], opus_int32 len)
Multistream API允许将多个Opus数据流组合成一个包,能够支持多达255通道
多流Opus包的格式是定义在Ogg封装的规格,也是基于RFC 6716附录B所阐述的自限定Opus框架。标准的Opus包正好是多流Opus包的退化版本,可以用对流API进行编码和解码,只要在初始化编码器和解码器时将流数量设置为1.
编码器的输出通道应使用Vorbis(免费音乐格式)的通道规则。解码器可能希望应用一个附加的排列以映射用于实现不同输出通道规则的编码器(例如用于WAV规则的输出)
多流包包含对应每个流的各Opus包,在单一多流包内的所有Opus包必须有相同的时长。因此,一个多流包的时长可以从位于包开始位置的第一个流的TOC序列提取,就象一个基本的Opus流
1:获得OpusMSEncoder结构的大小
opus_int32 opus_multistream_encoder_get_size (int streams, int coupled_streams)
2:分配和初始化多流编码器状态
OpusMSEncoder *opus_multistream_encoder_create (opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int application, int *error)
3:初始化以前分配的多流编码器状态
int opus_multistream_encoder_init (OpusMSEncoder *st, opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int application)
4:编码一个多流Opus帧
int opus_multistream_encode (OpusMSEncoder *st, const opus_int16 *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes)
5:从浮点型输入编码一个多流Opus帧
int opus_multistream_encode_float (OpusMSEncoder *st, const float *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes)
** 6: 释放经opus_multistream_encoder_create ()
分配过的OpusMSEncoder **
void opus_multistream_encoder_destroy (OpusMSEncoder *st)
7:向一个多流Opus编码器执行CTL 函数
int opus_multistream_encoder_ctl (OpusMSEncoder *st, int request,...)
///////////////////////////////////////////////////////////////////////////////////////////////////
opus_int32 opus_multistream_decoder_get_size (int streams, int coupled_streams)
OpusMSDecoder *opus_multistream_decoder_create (opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int *error)
int opus_multistream_decoder_init (OpusMSDecoder *st, opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping)
int opus_multistream_decode (OpusMSDecoder *st, const unsigned char *data, opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec)
int opus_multistream_decode_float (OpusMSDecoder *st, const unsigned char *data, opus_int32 len, float *pcm, int frame_size, int decode_fec)
int opus_multistream_decoder_ctl (OpusMSDecoder *st, int request,...)
void opus_multistream_decoder_destroy (OpusMSDecoder *st)
OPUS_SET_COMPLEXITY
OPUS_SET_BITRATE
OPUS_SET_VBR
OPUS_SET_VBR_CONSTRAINT
OPUS_SET_PACKET_LOSS_PERC
OPUS_SET_MAX_BANDWIDTH
OPUS_SET_FORCE_CHANNELS
OPUS_SET_BANDWIDTH
OPUS_SET_SIGNAL
OPUS_SET_APPLICATION
OPUS_SET_INBAND_FEC
OPUS_SET_DTX
OPUS_SET_LSB_DEPTH
OPUS_SET_EXPERT_FRAME_DURATION
OPUS_SET_PREDICTION_DISABLED
1. 简介
SILK 是Skype 内部开发的一个语音频编解码器,作为Skype 到Skype 调用的默认编解码器。SILK 在音频带宽、网络码率和复杂度等方面的高度可扩展性使其成为可选择多种模式和应用的编解码器。
Skype 鼓励第三方合作伙伴组织(3GPP)在可能与Skype 互操作的网络中采用SILK。因此,本文件定义了将SILK 语音频编码数据打包所用的RTP 负载格式和文件存储格式,这些数据是以最兼容方式实现SILK 必不可少的。本文件进一步描述了RTP 负载格式和文件存储格式媒体类型注册。
关于SILK 的更多信息可以通过以下链接获得:
https://developer.skype.com/silk
2. 本文件使用的公约、定义和缩略语
本文件中出现的关键词“MUST”,“MUST NOT”,“REQUIRED”,“SHALL”,“SHALL NOT”,“SHOULD”,“SHOULD NOT”,“RECOMMEDED”,“MAY”,“OPTIONAL”的解释参见RFC 2119 [1]。
定义和缩略词:
CPU —— 中央处理单元
IP —— 网络协议
MTU —— 最大传输单元
PSTN —— 公共交换电话网
Samples —— 语音或音频采样点
SDP —— 会话描述协议
3. SILK 编解码器
SILK 语音频编解码器在音频带宽、网络码率和复杂度等方面具有高度的可扩展性。
SILK 支持四种不同的音频带宽:窄带8000 Hz 采样率,中带12000 Hz 采样率,宽带16000 Hz 采样率,超宽带24000 Hz 采样率。窄带模式应该(SHOULD)只用于PSTN 网络的接口或者用在不支持大于8000 Hz 采样率的低端设备上。中带模式应该(SHOULD)用于不支持大于12000 Hz 采样率的更低端设备或者严重的网络带宽限制的条件下(例如无线设备)。宽带模式应该(SHOULD)用于所有不支持大于16000 Hz 采样率的所有网络协议平台。超宽带模式应该(SHOULD)用于所有支持24000 Hz 或者更高采样率的平台。
平均目标网络码率在表1 对应音频带宽下规定的范围内是可变的。平均网络目标码率可以实时定义和修正,而实际码率依赖于输入信号,且随时间而改变。实际码率可能高于或低于表1 规定的自适应目标码率。推荐采用表中码率范围的上限。
表1. fs 代表以赫兹为单位的采样率,BR 代表以千位每秒为单位的自适应目标码率范围
Fs (Hz) | BR (kbps) | 带 |
---|---|---|
8000 | 5 – 20 | 窄带 |
12000 | 7 – 25 | 中带 |
16000 | 8 – 30 | 宽带 |
24000 | 20 – 40 | 超宽带 |
复杂度可扩展用于实时优化CPU 资源,多用于与网络码率的权衡。
SILK 内部帧长为20 ms。SILK 编码器可以设置为捆绑5 个内部帧为一个输出帧,允许编码语音频数据的帧长为20,40,60,80,100 ms。表2 显示了不同帧长和采样率时一帧语音或者音频包含的采样点数。
表2. 不同帧长和采样率时,一帧包含的采样点数
帧长 | 20ms | 40ms | 60ms | 80ms | 120ms |
---|---|---|---|---|---|
窄带采样点 | 160 | 320 | 480 | 640 | 800 |
中带采样点 | 240 | 480 | 720 | 960 | 1200 |
宽带采样点 | 320 | 640 | 960 | 1280 | 1600 |
超宽带采样点 | 480 | 960 | 1440 | 1920 | 2400 |
SILK 操作在非常低的算法延迟,包括算法打包延迟(例如20,40,60,80,100 ms)加上5 ms 前瞻延迟。
3.1.自适应采样频率
SILK 编码的语音频信号内部采样率可以随着传输时间而变化,这可能在以下两种情况下发生。
第一种情况是,SILK 提供一种内部逻辑,决定自动将编码的语音频信号内部采样率调整到最有效的采样率,这个最有效采样率取决于输入信号和信道容量信息。
这种内部逻辑使得SILK 支持拥塞控制和网络负荷管理。
第二种情况是,SILK 提供手动设置编码的语音频信号最大内部采样率的API 功能。这个最大内部采样率不能(MUST NOT)被设置高于呼叫建立协商时商定的采样率,
这是因为SILK 的早期版本不能解码内部采样率高于解码端输出采样率(解码器API 采样率)的信号。
对于以上两种情况,只有被编码成码流的语音频信号的内部采样率被切换时,SILK 编码器输入的语音频信号和解码器输出的语音频信号的采样率才能单独设置而互不影响。
会话建立的时候,解码端应该(SHOULD)将系统可以利用的所有采样率(率参数)发信号给给编码端
3.2.自适应网络码率
SILK 编码器可以设置不同的平均目标码率输出编码的语音频数据。规定的平均目标码率是为积极信号(例如非静音帧)设置的,
因此每一帧的实际码率根据数据语音频信号的感知重要性而变化。平均目标码率可以以帧为单位进行调整,这使SILK 支持拥塞控制和网络负荷管理。
为了有效的实现这些,必须提供信道容量和存储设备的相关信息。这些信息可以通过多种渠道获得,但不再本文件讨论范围。
当无法获得信道容量信息时,SILK 可以以固定的平均目标码率运行。这个固定的平均目标码率必须谨慎选择,因为超过信道容量时可能导致额外的延迟和语音帧丢失。
除非最宽带宽网络接口技术的信道限制是已知的,否则推荐使用表1 中提供的最大平均目标码率达到最好的语音音质。
3.3.不连续传输(DTX)
SILK 编解码器是码率自适应的。对于特定的输入信号,比如静音周期,码率会被自动削减。在连续传输模式下,当输入信号允许的时候,编码器会削减码率,
但是,到接收器的传输不会被中断。因此,接收到的信号在全部传输过程中使码率最小化但保持较高水平的音质。
在SILK 的平均码率需要进一步削减的情况下,SILK 编码器可以设置成不连续传输(DTX),此时部分编码信号,即输入语音频信号的静音周期,不被传送到接收端。
在接收端,没有传送的部分将被SILK 解码器当作丢失帧处理,即产生舒适噪声信号代替没有传送的语音频信号部 分。
SILK 的不连续传输模式的语音频音质会比连续模式稍差。因此,推荐(RECOMMENDED)使用SILK 的连续模式,除非网络带宽的限制比较严格。
3.4.前向差错纠正(FEC)
SILK 编解码器允许在SILK 码流中嵌入“带内”前向差错纠正(FEC)数据。这种FEC 方案将前一帧(n-1)或者前两帧(n-2)的冗余信息加到当前帧(n)。
对于每一帧,编码器根据以下信息决定是否使用FEC :(1)外部提供的信道丢包率估计值;(2)外部提供的信道容量估计值;(3)语音频信号对丢包的敏感度;
(4)接收端解码器是否显示可以利用“带内”FEC 信息。发送“带内”FEC 信息的决定完全受编码器控制,因此不需要关于负载或存储格式的特殊注意事项。
在接收端,当某一帧丢失而未来帧可接收到的时候,解码器可以利用这些附加信息。为了使用FEC 数据,抖动缓冲区需要提供包含SILK
未来帧的负载和相对SIK 上一解码帧偏移量的信息。
一个特殊的API 函数可以搜索可用的FEC 数据,如果搜索成功的话,可以作为当前丢失帧提供给解码器。
如果这种FEC 方案没有在接收端实现的话,FEC不应该(SHOULD NOT)被使用,因为它将导致网络带宽的低效使用。解码器支持FEC 的话,应该(SHOULD)在会话建立的时候显示。
光脉冲数字信号在光纤传输过程中,会受到各种不利因素的劣化影响,这些不利影响有外界的也有内部的,也有外界的干扰信号,
内部有电路的热噪声、EDFA的ASE噪声及模分配噪声、光纤衰耗和色散的影响等,这些不利因素会使传输质量下降,如接收端的光功率减少、光脉冲发生畸变、光信噪比降低等,
导致接收端出现误码,减少系统的传输距离。从误码发生性质看,可分为两类:1、随机性误码2、突发性误码
误码纠错方法通常用三种:1、自动请求重发(ARQ)2、前向纠错(FEC)3、混合纠错(HEC)。在海缆传输中主要使用前向纠错FEC比较多。
波分设备或者海缆SLTE/LTU设备收高误码,其相连的SDH设备极大可能没有误码,因为收到的误码经过FEC纠错后就变成没有误码的“好信号”传给SDH了,SDH就没有告警和任何误码了。
前向纠错主要是利用软件技术(也需少量硬件)在发送端对输入信息进行编码,在接收端再对之进行解码,从而获得增益,达到降低系统误码率、增加传输距离的目的。
前向纠错(FEC)是指利用软件技术在发送端对信源信息进行一定形式的编码(如BCH编码、R-S编码),
然后用新的编码流进行传输,在接收端再进行解码与纠错,以此获得增益从而增加系统的传输距离。
带内FEC与带外FEC
对于SDH而言,FEC有带内与带外之分。
带内FEC指对信源信息进行编码后的冗余码,存放于SDH段开销中的某些尚未使用的字节中,带内FEC常用BCH编码方法。
由于带内FEC可供使用的字节数量有限,所以它的编码增益不高。
带外FEC指把对SDH信息进行编码后的冗余码放在SDH帧结构的外部,然后把冗余码与信息码组合在一起形成新的帧信号进行传输。
带外FEC通常采用R-S编码方法。由于带外FEC的冗余码数量可以很大,所以它的纠错能力远高于带内FEC。带外FEC的缺点是增加了系统的传输速率。
对于WDM系统而言,采用FEC编码所获得的增益主要用于对OSNR的改善,以增加系统的传输距离。
编码分为信源编码和信道编码。FEC编码属于信道编码,FEC编码可理解是把信息包进行再包装,使得传输时更加可靠稳定,不易损坏或者破碎(纠正误码),
使得接收者收到时物品(通信质量)完美。 同时包装使得物体体积更大(可理解为传输速率更大),使得要传输的信息量相对而言会减少。
通过FEC编码降低误码率,提高通信可靠性。前向纠错(FEC)编码,发送端编码器将信息码组编成具有一定纠错能力的码字,接收端译码时对接收马子进行译码,
若传输中产生的差错数目在码的纠错能力之内时,译码器对差错进行定位并加以纠正。
Opus的内核包含两种编码方式,分别是CELT和SILK,它会根据不同的信号类型(语音、音乐)来采用不同的音频压缩方式。CELT与MP3功能类似,负责音乐存储,而SILK会在编码之前优化语音信号。
音乐和语音
CELT编码可以实时压缩音乐。为了达到该目标,它将数据打包为小的音频帧,从而保证只有2.5ms的延迟。
SILK编码首先降低采用频率到16kHz,然后才开始真正的编码过程。
语音编码
语音信号在正式编码之前,首先要经过SILK编码器的详细分解。
语音识别 将语音与周围环境噪音分隔开。
音高分析 降低语音帧的采样率。
噪声分析 优化噪声到相应的比特率。
预过滤器 调整信号,然后将其转移到编码区。
音高预测 通过目前的音频帧计算未来的音频帧变化。
频率量化 使传输的音高变得平稳。
失真优化 确保在可接受的语音失真内采用最低比特率。
噪音量化 调整预过滤的噪声与已编码的语音帧。
区域编码 处理每一个已编码的音频帧信号。
音频编码对比
与其他的音频编码方式相比,Opus在低延迟情况下覆盖了所有的比特率。手机的语音编码相对是快的,但是很难达到较高的通话质量,而传统的音频编码则在实时性上有欠缺。
新的音频编码标准Opus比传统的MP3和AAC编码更高效,尤其适用于网络音频传输。更重要的是,它是一个完全开放的标准。
包括VoIP、在线音乐、有声读物和播客(Podcasts)等在内,网络流量中相当大的一部分贡献给了音频传输。上面提到的这几项操作,在快速的DSL线路上可以流畅地进行。
但如果使用的是蜂窝数据连接,VoIP通话就难免会产生令人不爽的延迟,在线音乐也会变得时断时续。
不过,现在已经有一种名为Opus的音频编码会解决这些问题。它的最大优势是可以利用更小的带宽带来更高质量的音频,而且可以实时进行。
除此之外,Opus本身的编码是完全开源的,开发者不需要担心任何专利授权的问题。
从一开始,Opus编码就是为交互式音频传输而开发的。这意味着Opus编码不仅适用于音乐,而且支持双向音频传输,例如为视频电话和VoIP通话做了充分的优化。
2012年9月初,IETF(互联网工程任务组)将其提升为正式的官方标准,也就是说它事实上已经成为了HTML标准的一部分。
Opus音频编码是由非盈利的Xiph.org基金会、Skype和Mozilla等共同主导开发的。像MP3一样,Opus也是一种有损音频编码,这意味着在传输过程中内容会被压缩。
低延迟是它的最大特色,也就是说,它的信号延迟非常小,数据包会在20ms之内被处理。相比之下, MP3文件的延迟是它的10倍。
高度的灵活性
Opus的开发者在灵活性上付出了很大的心血,从而保证了Opus的比特率可以从6Kb/s到510Kb/s,采样率可以在8kHz到48kHz之间摇摆,音频信号帧可以从2.5ms到60ms。
从内部结构来看,Opus是开发者利用已经存在的音频编码进行优化后组合而成的所谓混合编码。Opus联合了CELT(Constrained Energy Lapped Transform)和SILK编码,
并且对两者进行了改良。CELT编码由于“实时”的特性,已经被作为OGG家族的一部分。最初来自Skype的SILK编码,从Skype 4.0开始已经作为语音编码的方式被引入。
大体上,Opus编码器的结构和功能都非常简单。首先,输入信号被以最高达48kHz的采样率进行采样。因为人的耳朵已经很难分辨更高采样率音频的更多细节了。
然后,根据频率的不同,使数据流通过CELT或者SILK编码器。如果最终需要的是高品质的音乐,那么CELT编码器是首选。
而利用SILK,人们可以使用最优化的带宽来传输音频。为了这个目标,SILK编码必须做一些与之前的编码不一样的事情。
对话中的语音分析
SILK编码适合采样率不超过16kHz的低频信号,一个典型的场景就是语音通话。因此,在Opus编码中,所有低于16kHz采样率的音频内容都由SILK编码。
SILK编码器包含一系列组件,在这里将它们总结为4个部分:分析、预过滤、编码和输出。其中,音频分析的背后其实是语音识别。
首先,将音频信号分为语音和环境噪音两部分。将语音帧根据频率分解为更小的音频碎片,SILK编码器过滤掉延迟信息,识别出有效语音信号的特点。
第二项优化是噪声分析,它将周围环境中重复的噪音打包为越小越好的音频子帧(sub-frames),使其占用更小的带宽。
利用分析阶段获得的信息,SILK编码器就可以进行音高预测和频率量化。例如,如果在一段对话中,音高(pitch)没有大的变化,那么只需要传输变化部分的信息就可以了。
这一步的目标是让数据流在保证质量的同时,越少越好。噪声的量化是另一种达成目标的方法。在这个例子中,SILK编码可以确保不进行不必要的优化,
而且不可避免的噪声没有消耗过多的比特率。
所有的高频信号,也就是频率最高达20kHz的信号,都采用CELT编码器处理。与MP3和AAC编码一样,它通过修改后的离散余弦变换(DCF)将频率转换为系数,
从而消减在随后的量化中难以覆盖或者人耳很难感知的频率。开发者为Opus定义了3个模式,使得SILK和CELT也能够同时工作:纯SILK模式负责低带宽下的语音传输;
混合模式负责高质量的语音传输;纯CELT模式负责音乐传输。Firefox用户从第15版开始可以在不安装插件的情况下,直接播放Opus文件。
视频播放器VLC Media Player也将集成Opus编解码。现在打开opus-codec.org/examples网站就可以立即试听采用Opus编码的音频文件样例。