概述
Speex 技术介绍
1、Speex 介绍
speex是近年来开发出的一套功能强大的语音引擎,能够实现高质量和低比特率的编码。它不仅提供了基于码激励线性预测(CELP)算法的编/解码模块,而且在其最新发布的版本中还提供了声音预处理和声学回声消除模块,为保障IP网络中的语音通信质量提供了技术手段。此外,Speex还具有压缩后的比特率低(2.15~44.2kbps)的特点,并支持多种比特率。这些特点使得Speex特别适合VoIP, 音视频系统
宽带和超宽带下的比特率
2、Speex特点
(1)开源,纯C开发,跨平台,兼容ARM,编译简单
(2)占用比特率小,提供消除回声功能
(3)API使用简单
(4) 码率支持 : 2.15~44.2kbps
(5)采样率支持:8khz 、16khz、32khz
3、 Speex 和其它语音编码的对比
编码方式 | 打包时间 | 带宽 |
G711 | 20ms | 90.4 Kbit/s |
G729 | 20ms | 34.4 Kbit/s |
G723 | 30ms | 22.9 Kbit/s |
speex | 20ms | 19.6 Kbit/s |
从对比来看,speex占用的带宽是最小的,比较适合在在窄带宽下使用
4、 语音带宽的计算公式:
( 网络包数据 + 数据包数据 ) / 采样时间
网络包数据 : 58 bytes 由以下五个部分组成
- 网络 CRC: 4 bytes
(2)mac 地址: 14 bytes
(3)IP 头: 20 bytes
(4)UDP 头: 8 bytes
(5)RTP 头: 12 bytes
数据包数据: 20bytes
采样时间:20ms
Speex 传输率
如果每个rtp包只传输1个数据包:
(58 +20)*8 /20*1000 = 31.2kb/s
如果每个rtp包只传输2个数据包:
(58 +40)*8 /40*1000 = 19.6 kb/s
如果每个rtp包只传输3个数据包:
(58 +60)*8 /60*1000 = 15.7 kb/s
如果每个rtp包只传输3个数据包:
(58 +80)*8 /80*1000 = 13.8 kb/s
码率计算公式:
码率 = 采样率 * 位声 * 声道数 * 压缩率
压缩率 = encoded audio size / pcm size
5、Speex 在RTP 中的传输
5.1、RTP Speex 头部
Payload Type (PT): 本格式的负责类型号。
Marker (M) bit: 此位被用来标志一段无声后有声的开始。打在有声数据的第一个包上。 Speex支持声音检测,可以在无声时不产生帧数据。所以包可能是非连续传输的。
Extension (X) bit: 见RTP的规定。
Timestamp: 一个32位的整数,表示一个包中第一帧的采样时间。
5.2、Speex的RTP负载格式
Speex的RTP负载如图1所示。本格式没有附加的头部,所以只什用标准的RTP头部 头部之后是一个或多个负载数据库(speex帧)。包尾部可能需要一些填补数据。
5.3、 Speex 负载
为了把编码后的数据打包进RTP,我们只需要考虑Speex编码器输出的比特流必须以相同的顺序出现在解码端。此处所说的负载格式保持了这个顺序。
一个典型的Speex帧,最大编码码率大约是110个字节。一个包中所有的Speex帧的总字节数应小用路径MTU以避免被分割。Speex帧绝不能被分割!
必须按时间序把帧打到包里。
一个RTP包中可能包含相同码率的帧也可能包含不同码率的帧。然而码率是在带内传送的,每帧中包含了自己的码率,所以打包时不必在意它。
编码和解码算法可以以20毫秒的帧为边界改变码率。 码率改变的通知是在带内传送的。每个帧都包含采样率(窄带, 宽带, 或 超宽带)和"模式"(码率)信息。所以不需要带外数据通知解码器处理那些变化。
采样率必须是8000 Hz, 16000 Hz, 或 32000 Hz之一。
RTP负载必须被填补数据以保证能提供整数个字节的数据,这些填补位是LSB(最低有效位)-对齐的并且按网络字节序放置,它是由一个0跟着一群1组成的。填补数据仅被包中最后一个帧所需要。并且仅仅为了保证一个包的内容按字节边界结束。
5.4、 Speex RTP包的例子
下面的示例中,我们的包中一个Speex帧,还有5位的填补数据来保证包的大小是字节对齐的。
5.5、多Speex帧的RTP包
下面的例子演示的是一个RTP包中有两个Speex帧。这个例子中的Speex帧的长度是字节对齐的,所以不需要填补数据。
Speex解码器可以从负载侦测码率,并负责在各帧之间检查20毫秒的的帧界限。
5.6、 媒体类型
媒体类型名字: audio
媒体字类型名字: speex
所需参数:
rate: RTP 时间戳时钟频率,等于采样率Hz。采样率必须是8000, 16000, 或 32000。
可选的参数:
ptime: 必须能被20毫秒整除 [RFC4566]
maxptime: 必须能被20毫秒整除 [RFC4566]
vbr: 可变码率 - 可为 'on', 'off', 或 'vad' (默认是'off')。如果是'on',可变码率被使用。如果是 'off',则不被使用。如果是'vad',那么固定码率被使用,但是无声时段将被编码为特殊的短帧来表明那段时间没有声音。这个参数
用于编码器。
cng: 产生舒适噪音 - 可为 'on' 或 'off' (默认是'off')。如果为 'off',无声帧就是无声,如果为'on',那么这些帧将被以舒适噪音填充。此参数被用于编码器。
mode: 以逗号分隔的多个speex支持的解码模式,按优先度排列。第一个具有最优先级,剩余的依次排列。对于窄带和宽带可用的模式值不一样,见以下定义:
* {1,2,3,4,5,6,7,8,any} 用于窄带
* {0,1,2,3,4,5,6,7,8,9,10,any} 用于宽带
mode'参数可能包含多个值。这此情况下,远端的编码器必须被配置成能支持模式列表中的第一个值。 当 'any' 被使用时,表明自己支持所有的解码模式。'mode' 参数必须永远有值。如果'mode' 没有出现,那么mode的值被置为:在
窄带下是'mode="3,any"',在宽带和超宽带下是'mode="8,any"' 。注意每个包含mode(或码率)的Speex帧必须被解码。因此,一个应用程序必须能解码任何Speex帧,除非在SDP中明确指明某些模式不被支持(例如,不含'mode="any"')。
解码端指定支持哪些模式意味着编码端也支持那些模式。
6 、speex 编码和解码
6.1 、speex 编码流程
1、定义一个SpeexBits类型变量bits和一个Speex编码器状态变量enc_state。
2、 调用speex_bits_init(&bits)初始化bits。
3、 调用speex_encoder_init(&speex_nb_mode)来初始化enc_state。其中speex_nb_mode是SpeexMode类型的变量,表示的是窄带模式。还有speex_wb_mode表示宽带模式、speex_uwb_mode表示超宽带模式。
4、 调用函数int speex_encoder_ ctl(void *state, int request, void *ptr)来设定编码器的参数,其中参数state表示编码器的状态;参数request表示要定义的参数类型,如SPEEX_ GET_ FRAME_SIZE表示设置帧大小,SPEEX_ SET_QUALITY表示量化大小,这决定了编码的质量;参数ptr表示要设定的值。
可通过speex_encoder_ctl(enc_state, SPEEX_GET_FRAME_SIZE, &frame_size) 和speex_encoder_ctl(enc_state, SPEEX_SET_QUALITY, &quality)来设定编码器的参数。
5、 初始化完毕后,对每一帧声音作如下处理:调用函数speex_bits_reset(&bits)再次设定SpeexBits,然后调用函数speex_encode(enc_state, input_frame, &bits),参数bits中保存编码后的数据流。
6、 编码结束后,调用函数speex_bits_destroy (&bits),speex_encoder_destroy (enc_state)来
编码demo 代码
#include <speex/speex.h> #include <stdio.h> /*The frame size in hardcoded for this sample code but it doesn't have to be*/ #define FRAME_SIZE 160 int main(int argc, char **argv) { char *inFile; FILE *fin; short in[FRAME_SIZE]; float input[FRAME_SIZE]; char cbits[200]; int nbBytes; /*Holds the state of the encoder*/ void *state; /*Holds bits so they can be read and written to by the Speex routines*/ SpeexBits bits; int i, tmp; /*Create a new encoder state in narrowband mode*/ state = speex_encoder_init(&speex_nb_mode); /*Set the quality to 8 (15 kbps)*/ tmp=8; speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp); inFile = argv[1]; fin = fopen(inFile, "r"); /*Initialization of the structure that holds the bits*/ speex_bits_init(&bits); while (1) { /*Read a 16 bits/sample audio frame*/ fread(in, sizeof(short), FRAME_SIZE, fin); if (feof(fin)) break; /*Copy the 16 bits values to float so Speex can work on them*/ for (i=0;i<FRAME_SIZE;i++) input[i]=in[i]; /*Flush all the bits in the struct so we can encode a new frame*/ speex_bits_reset(&bits); /*Encode the frame*/ speex_encode(state, input, &bits); /*Copy the bits to an array of char that can be written*/ nbBytes = speex_bits_write(&bits, cbits, 200); /*Write the size of the frame first. This is what sampledec expects but it's likely to be different in your own application*/ fwrite(&nbBytes, sizeof(int), 1, stdout); /*Write the compressed data*/ fwrite(cbits, 1, nbBytes, stdout);
}
/*Destroy the encoder state*/ speex_encoder_destroy(state); /*Destroy the bit-packing struct*/ speex_bits_destroy(&bits); fclose(fin); return 0; } |
6.2 、speex 解码流程
对已经编码过的音频数据进行解码要经过以下步骤:
1、定义一个SpeexBits类型变量bits和一个Speex编码状态变量enc_state。
2、调用speex_bits_init(&bits)初始化bits。
3、调用speex_decoder_init (&speex_nb_mode)来初始化enc_state。
4、调用函数speex_decoder_ctl (void *state, int request, void *ptr)来设定编码器的参数。
5、调用函数 speex_decode(void *state, SpeexBits *bits, float *out)对参数bits中的音频数 据进行解编码,参数out中保存解码后的数据流。
6、调用函数speex_bits_destroy(&bits), speex_ decoder_ destroy (void *state)来关闭和销毁 SpeexBits和解码器。
解码demo 代码
#include <speex.h> #include <stdio.h> /*帧的大小在这个例程中是一个固定的值,但它并不是必须这样*/ #define FRAME_SIZE 160 int main(int argc, char **argv) { char *inFile; FILE *fin; short in[FRAME_SIZE]; float input[FRAME_SIZE]; char cbits[200]; int nbBytes; /*保存编码的状态*/ void *state; /*保存字节因此他们可以被speex常规读写*/ SpeexBits bits; int i, tmp; //新建一个新的编码状态在窄宽(narrowband)模式下 state = speex_encoder_init(&speex_nb_mode); //设置质量为8(15kbps) tmp=8; speex_encoder_ctl(state, SPEEX_SET_QUALITY, &tmp); inFile = argv[1]; fin = fopen(inFile, "r"); //初始化结构使他们保存数据 speex_bits_init(&bits); while (1) { //读入一帧16bits的声音 fread(in, sizeof(short), FRAME_SIZE, fin); if (feof(fin)) break; //把16bits的值转化为float,以便speex库可以在上面工作 for (i=0;i<FRAME_SIZE;i++) input[i]=in[i]; //清空这个结构体里所有的字节,以便我们可以编码一个新的帧 speex_bits_reset(&bits); //对帧进行编码 speex_encode(state, input, &bits); //把bits拷贝到一个利用写出的char型数组 nbBytes = speex_bits_write(&bits, cbits, 200); //首先写出帧的大小,这是sampledec文件需要的一个值,但是你的应用程序中可能不一样 fwrite(&nbBytes, sizeof(int), 1, stdout); //写出压缩后的数组 fwrite(cbits, 1, nbBytes, stdout); } //释放编码器状态量 speex_encoder_destroy(state); //释放bit_packing结构 speex_bits_destroy(&bits); fclose(fin); return 0; } |
7、speex 压缩率
最后
以上就是自觉机器猫为你收集整理的speex 编码简介Speex 技术介绍的全部内容,希望文章能够帮你解决speex 编码简介Speex 技术介绍所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复