音频压缩(Speex使用&Opus简介)--转

博客地址:http://blog.csdn.net/kevindgk

GitHub地址:https://github.com/KevinDGK/MyAudioDemo

 

 

一、简介

现在有个需求,在局域网内实现实时语音,传输层协议使用UDP协议,如果直接使用AudioRecord进行录制音频流并发送到另一端进行播放,音质会非常差,而且断断续续,原因如下:

采样频率: fm = 44.1KHz

量化位数:16bit

声道配置:2(双声道)

那么,码率 V = 44.1K * 16 *2 = 1411.2 Kbps = 176.4KBps,即每秒传输速率大概176.4KB,

若音频帧时间为20ms,每个音频数据包大小为 size = 176.4KBps * 0.02s = 3.528KB,

一般情况下,我们每次读取一个音频帧的数据,可以取整为3600Byte,

所以 每秒大概发送 176.4/3.6=49 个数据包,每个数据包大小为3.6KB。

如果再考虑到数据报头,实测每秒发送约45个数据包,每秒传输速率大概180KB。

由于一般都是使用手机连接Wifi,这就要求网络质量和硬件设备必须很好,而且信道干扰较弱,并且链接的设备不能过多。只要稍微信号不好,就会导致丢包率特别高,而且延时十分大,根本无法满足通信的需要。在这种情况下,我们就需要进行语音压缩、降噪等处理。

二、局域网语音配置

如果传输的仅仅是语音信息,那么不需要很高的采样频率,可以使用8KHz进行采样,单通道即可。

    private int DEFAULT_SAMPLERATEINHZ = 8000;                              // 采样频率
    private int DEFAULT_AUDIOFORMAT = AudioFormat.ENCODING_PCM_16BIT;       // 数据格式
    private int DEFAULT_STREAMTYPE = AudioManager.STREAM_MUSIC;             // 音频类型
    private int DEFAULT_CHANNELCONFIG_OUT = AudioFormat.CHANNEL_OUT_MONO;   // 声道配置
    private int DEFAULT_MODE = AudioTrack.MODE_STREAM;                      // 输出模式
    private int DEFAULT_CHANNELCONFIG_IN = AudioFormat.CHANNEL_IN_MONO;     // 声道配置
    private int DEFAULT_AUDIOSOURCE = MediaRecorder.AudioSource.MIC;        // 音频来源
  • 采样频率:8KHz,可以采集比较完整的语音信息。当然,对于高频的信息无能为力;
  • 数据格式:16bit,可以较为详细的表示声音的幅度;
  • 声道配置:单声道输入和输出,能够适配所有的机型,少部分手机不支持双声道(立体声),如果设置立体声会只出现左耳机有声音的情况;
  • 音频类型:音乐流
  • 输出模式:音频流
  • 音频来源:麦克风

三、Speex

官方网址

3.1 简介

Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用输入门槛 。另外,相对于其它编解码器,Speex也很适合网络应用,在网络应用上有着自己独特的优势。同时,Speex还是GNU工程的一部分,在改版的BSD协议中得到了很好的支持。

3.2 技术特点

Speex是基于CELP并且专门为码率在2-44kbps的语音压缩而设计的。它的特点有:

  • 窄带(8kHz),宽带(16kHz)和超宽带(32kHz)压缩于同一位流。
  • 强化立体编码
  • 数据包丢失隐蔽
  • 可变比特率(VBR)
  • 语音捕捉(VAD)
  • 非连续传输(DTX)
  • 定点运算
  • 感官回声消除(AEC)
  • 噪音屏蔽

3.3 开发-语音压缩

由于语音压缩的底层代码都是用C/C++写的,所以对于我们可怜的Android来说需要使用NDK进行JNI开发了,如果对这方面不了解的,可以参考一下小编整理的 JNI(一) - Android Studio简单开发流程 ,然后需要再了解一下C语言的一些基础知识,就可以进行简单的JNI开发了,在本文的Demo中,小编也写了大量的注解,希望能够帮到大家。

本项目GitHub地址

将代码集成到自己的项目中的步骤:

第一步:将Demo的整个jni目录复制到自己的main目录下;

第二步:修改复制过来的jni文件夹中的speex_jni.cpp中的方法名

​ 方法名为Java _ 包名 _ 类名 _ 方法名(),中间使用单个下划线连接,详见demo;

第三步:添加gradle配置

​ 将红色框圈住的地方复制到自己的项目中即可。

第四步:编译项目生成.so文件

​ 选择Build->Make Project,然后找到.so库复制到自己的libs下即可:

第五步:在程序中使用

​ 将Speex这个编解码工具类复制到自己的项目中,就可以正常使用了,具体使用方式详见Demo。

private Speex speex;                    // Speex音频编解码器

speex = new Speex();                    // 创建Speex编解码实例
speex.open(4);

speex.encode(recordData,0,encodedbytes,readNumber); // 语音压缩-对音频数据编码

int decode = speex.decode(audioData, decodedShorts, audioData.length);  // 对音频数据解码

3.4 相关计算

采样频率: fm = 8KHz

量化位数:16bit

声道配置:1

那么,码率 V = 8K * 16 * 1 = 128Kbps = 16KBps,即每秒传输速率大概16KB,

若音频帧时间为20ms,每帧音频数据大小为 size = 16KBps * 0.02s = 320KB,即160 Short,

设置压缩质量为4,每帧音频数据压缩完后只有20Byte,压缩比为 320:20,即 16:1,每秒发送1/0.02=50个数据包,即单单传递音频数据占用的带宽为 1 KBps,如果需要添加一些数据报头,基本上也能维持在5KBps左右!

将176.4KBps变成16KBps,然后再压缩成1KBps,好了,现在可以满足基本的局域网内的语音传输需求了。如果带上耳机的话,基本上能够很完美的语音通话了。如果不带耳机,随着通话,麦克风或者环境的回声会越来越强,将会严重降低通话质量,我们之后需要做的就是做回声处理了。这个在之后的博客中专门介绍。

尽管Speex也十分的优秀,但是还是被新的技术给踢下了宝座,连它的官网上都写明了该技术已经被Opus给替代,并且宣称Opus的各项性能都将比Speex优秀的多。接下来小编将为大家初步介绍一下Opus的特点和API~

四、Opus - 音频编解码器中的瑞士军刀

4.1 简介

Opus是一个完全开放的、免费的、多功能的音频编解码器。 它在交互式的语音和音乐在互联网中的传输方面有着无与伦比的优势,但是同样致力于存储和流媒体应用程序。它是由互联网工程任务组(IETF)制定的标准,标准格式为RFC 6716,由Skype的SILK编解码器和Xiph.Org的CELT编解码器合并发展而来,号称音频编解码器中的瑞士军刀(来自官方视频)。

官方网址:http://opus-codec.org/

4.2 技术

Opus可以处理广泛的音频应用程序,包括IP电话、视频会议、游戏内聊天、甚至远程现场音乐表演。从低比特率窄带语音到非常高质量的立体声音乐,它都可以适用。技术特点:

  • 6 kb /s到510 kb / s的比特率
  • 采样率从8 kHz(窄带)到48 kHz(全频)
  • 帧大小从2.5毫秒到60毫秒
  • 支持恒定比特率(CBR)和可变比特率(VBR)
  • 从窄带到全频段的音频带宽
  • 支持语音和音乐
  • 支持单声道和立体声
  • 支持多达255个频道(多数据流的帧)
  • 可动态调节比特率,音频带宽和帧大小
  • 良好的鲁棒性丢失率和数据包丢失隐藏(PLC)
  • 浮点和定点实现

你可以在RFC 6716标准中阅读完整的规范,包括参考实现。也可以在下载页面获得一个最新的Opus 标准。

Libopus是Opus编解码器的参考实现,我们可以参考该代码进行开发。

4.3 开发插件

为了能在Firefox中支持Opus,Mozilla提供了专门的Opus工具。Opus-tools提供命令行程序来进行编码、检查和解码.opus文件。在HTML中语音相关的可以使用,原来不是Android的,需要用的自己去官方主页下载即可。

小编翻译到这里还没有找到和Android相关的,蓝瘦,香菇!

4.4 版本信息

尽管Opus现在是由IETF指定标准,但是Opus的实现也会一直持续改进。当然,所有未来版本仍将完全符合标准IETF规范。可以在开发界面查看最新的开发版本信息。

libopus 1.1.3(稳定发行版)

opus-1.1.3.tar.gz

4.5 对比

小编曾说过,没有对比,就没有伤害。

  • 质量和比特率

下图说明了不同的编解码器的质量和比特率直接的函数关系。

narrowband - 窄频

wideband - 宽频

super-wideband - 超宽频

fullband - 全频段

fullband stereo - 全频段立体声

从图上可以看出,Opus的优势十分明显。尤其是较之前的Speex,具有更大的比特率范围以及带宽。

  • 比特率/延迟比较

从图中可以看出,Opus在任何比特率下都具有较少的延迟。

  • 听力测试

Opus进行了几次测试,但是下面仅仅列出基于比特流的几个测试结果。尽管在Opus发布的时候应该给出一个比较好的质量标准化的意见,但是我们希望更新的和更高级的编码器将达到更好的质量。

测试结果

音频测试用例

4.6 模块API文档

截止到目前位置,最新的版本是1.13稳定发行版,所以在这里翻译的都是该版本的API。

4.6.1 Opus Encoder

Opus 编码器

类型定义

- typedef struct OpusEncoder OpusEncoder
  Opus编码器,包含了编码器的全部状态。
  • 1
  • 2
  • 1
  • 2

方法

- int opus_encoder_get_size (int channels)
  获取一个OpusEncoder编码器的大小。

- OpusEncoder * opus_encoder_create (opus_int32 Fs, int channels, int application, int *error)
  分配和初始化一个编码器状态。

- int opus_encoder_init (OpusEncoder *st, opus_int32 Fs, int channels, int application)
  初始化之前分配的编码器指针st指向的内存中的编码器,并且必须通过使用opus_encoder_get_size()方法来返回最小内存大小。

- opus_int32 opus_encode (OpusEncoder *st, const opus_int16 *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes)
  编码。

- opus_int32 opus_encode_float (OpusEncoder *st, const float *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes)
  编码音频流。

- void opus_encoder_destroy (OpusEncoder *st)
  释放一个通过opus_encoder_create()分配的OpusEncoder。

- int opus_encoder_ctl (OpusEncoder *st, int request,...)
  在Opus编码器中执行CTL函数。

详细描述

通过官方文档解释和C语言基础,我们知道OpusEncoder *enc 表示一个Opus编码器结构体的指针,指向该编码器的内存,该结构体内部包含了该编码器的全部状态。C语言中是没有类和对象的概念的,但是有结构体,可以用来模拟Java中的类,所以结构体的实例也就可以比作对象。从现在开始,我就成称enc为该编码器对象(的指针),这样说比较习惯。

因为Opus是有状态编码,编码过程始于创建一个编码器状态:

int error;
OpusEncoder *enc;
enc = opus_encoder_create(Fs, channels, application, &error);   // 创建

 

从这一点开来,enc可以用来编码一个音频流。一个编码器在同一时刻仅仅只能用于一个音频流编码,并且对于每一种音频格式初始化的编码器状态,不可以再次初始化。

当执行 opus_encoder_create() 为编码器分配了内存之后,就可以初始化这个预分配的内存:

int size;
int error;
OpusEncoder *enc;
size = opus_encoder_get_size(channels);     // 获取需要的最小内存
enc = malloc(size);                         // 分配内存
error = opus_encoder_init(enc, Fs, channels, application);  // 初始化内存

opus_encoder_get_size() 返回编码器对象所需要的内存大小,注意,该代码在未来的版本中可能会变成改变内存大小,所以不要根据这个代码有任何假定,即不要根据获取内存大小这个方法来有一些逻辑上的处理,因为之后的版本变动有可能会影响你的代码。

编码器的状态会一直保存在内存中,并且只有浅复制才能有效的复制该状态,例如:memcpy()。

使用 opus_encoder_ctl() 接口可以改变一些编码器的设置。所有的设置已经被默认设置成推荐值,所以只有在必要的时候才去改变它们。最常见的想要改变的设置如下:

opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal_type));

 

  • bitrate:比特率,b/s
  • complexity:复杂度,1-10,1最低,10最高
  • signal_type:信号类型,可以为OPUS_AUTO(默认)、OPUS_SIGNAL_VOICE、OPUS_SIGNAL_MUSIC

查看CTLS相关编码来获取完整的设置参数列表,大部分参数可以在一个音频流的任何时候设置和改变。

为了对一帧数据编码, opus_encode() 或者 opus_encode_float() 在调用的时候必须使用的是恰好的一帧(2.5,5,10,20,40,60毫秒)音频数据。

len = opus_encode(enc, audio_frame, frame_size, packet, max_packet);
  • 1
  • 1
  • audio_frame:opus_int16格式的音频数据
  • frame_size:抽样的每一帧的时间大小(每个通道)
  • packet:压缩后的编码写入的字节数组
  • max_packet:字节数组中可以写入的最大字节数,推荐大小为4000字节,不要使用 max_packet 去控制可变比特率,而是使用 OPUS_SET_BITRATE 的CTL命令。

opus_encode() 和 opus_encode_float() 返回实际接入到packet中的编码后的音频数据的字节数。返回值也许是无效的,表明编码错误。如果返回值小于2字节或者更小,那么这个packet不需要发送出去。

一旦这个编码器对象不需要,可以销毁掉:

opus_encoder_destroy(enc);
  • 1
  • 1

如果编码器对象是使用opus_encoder_init() 而不是 opus_encoder_create() 方法创建的,那么除了可能需要释放我们手动分配的内存之外,不需要其他的动作。

类型定义文档

typedef struct OpusEncoder OpusEncoder
编码器结构体,包含了一个编码器所有的状态。它是位置独立的,可以被随意的复制。
  • 1
  • 2
  • 1
  • 2

方法文档

- opus_int32 opus_encode( OpusEncoder∗ st, const opus_int16∗ pcm, int frame_size, unsigned char∗ data, opus_int32 max_data_bytes)
  • 1
  • 1

参数:

参数 参数类型 入参或出参 解释
st OpusEncoder∗ in 编码器对象
pcm const opus_int16* in 输入信号(双声道则为交错模式),长度为frame_size × channels × sizeof(opus_int16),即采样个数 × 声道数 × 16。
frame_size int in 输入的音频信号的每个声道的采样数量,这一定是一个Opus框架编码器采样率的大小。例如,当采样率为48KHz的时候,采样数量允许的数值为120、240、480、960、1920和2880。传递一个持续时间少于10 ms的音频数据(480个样本48 kHz),编码器将不会使用LPC或混合模式。
data unsigned char∗ out 输出编码结果,至少包含max_data_bytes个字节数。
max_data_bytes opus_int32 in 为了输出编码结果分配的内存,它可能用于控制一个即时比特率的上线,但是不应该作为唯一的比特率控制。

关于采样率和采样数量的关系,上文已经提到过,opus_encode() 或者 opus_encode_float() 在调用的时候必须使用的是恰好的一帧(2.5,5,10,20,40,60毫秒)音频数据,如果采样频率为48KHz,那么:

∵ 采样频率 Fm = 48KHz

∴ 采样间隔 T = 1/Fm = 1/48000 s = 1/48 ms

∴ 当T0 = 2.5 ms 时,N = T0/T = 2.5/(1/48) = 120,

当T0 = 5.0 ms 时,N = T0/T = 2.5/(1/48) = 240,

当T0 = 10 ms 时,N = T0/T = 2.5/(1/48) = 480,

当T0 = 20 ms 时,N = T0/T = 2.5/(1/48) = 960,

当T0 = 40 ms 时,N = T0/T = 2.5/(1/48) = 1920,

当T0 = 60 ms 时,N = T0/T = 2.5/(1/48) = 2880,

即,当Fm = 48KHz时:

采样时间(ms) 2.5 5 10 20 40 60
采样个数 120 240 480 960 1920 2880
- opus_int32 opus_encode_float( OpusEncoder∗ st, const float∗ pcm, int frame_size, unsigned char∗ data, opus_int32 max_data_bytes)
  • 1
  • 1

4.6.2 Opus Decoder

实在是翻译不动了。。。

其实有了上面的介绍,看官们应该能有个大概的认知,下面贴出一个中文版的翻译文档,大家自己浏览,因为目前我也差不多看了一般,所以小编对该文档的后面章节正确性与否并不负责~

Opus-官方文档中文版

五、小结

如果对比一下Speex和Opus,会发现从集成和使用的角度来看,十分的相似,API的时候方式也差不多,只不过Opus的功能更加的强大,API也更加丰富,但是也变相的增加了我们的开发难度,需要对C语言有相当好的功底,才可以定制实现自己的需求。

编程之路,任重而道远。先是为了生活而编程,后是为了超越而编程!

时间: 2024-10-04 11:20:00

音频压缩(Speex使用&Opus简介)--转的相关文章

lame音频压缩解码(一)Lame知识小解

  LAME 是最好的MP3编码器,编码高品质MP3的最好也是唯一的选择.LAME本身是DOS下的文件,需要加外壳程序才比较容易使用,也可以在别的软件(比如EAC)中间调用.是一款出色的MP3压缩程序.   ID3标签是MP3音乐档案中的歌曲附加讯息,它能够在MP3中附加曲子的演出者.作者以及其它类别资讯,方便众多乐曲的管理.缺少ID3标签并不会影响 MP3的播放,但若没有的话,管理音乐文件也会相当的麻烦.如果你在网上download MP3,里面多半已经写有预设的ID3讯息.如果你想要将其清除

ASP.NET页面进行GZIP压缩优化的几款压缩模块的使用简介及应用测试!(附源码)第1/2页_实用技巧

在介绍之前,先简单说一说ASP.NET服务端GZIP压缩模块的作用及工作原理,很多人编写网页的时候页面因为使用了大量的JS特效又或者放置很多大型动态广告导致了页面或脚本体积庞大,通常都会使用一些压缩工具本地对页面或脚本进行一定的压缩后再上传到服务器,但这样的压缩工具一般压缩率有限,优化自然也不明显,本文章介绍的压缩模块的作用就是对asp.net的页面或脚本等资源进行高强度GZIP压缩(一般能压缩到只有1/5的体积),而且压缩的过程是发生在客户端请求aspx页面的时候由服务端进行压缩处理后再传送给

lame音频压缩解码(二)之编译事例Demo

  简单步骤: 一.下载lame存文件. 二.创建android工程后,解压lame包,拷贝lame文件夹中的libmp3lame文件下的所有.c和.h文件到android工程的jni目录下. 三.从lame包中的include文件夹中,拷贝 lame.h到jni目录下. 四.定义natvie方法. 五.在jni目录下,编写android.mk文件. 六.进入工程的bin/classes目录下,通过javah生成.h文件. 七.根据生成的.h文件,创建相应的devchina.c文件.调用lame

最佳的开源网络视频会议音频编码库:Speex

在网络视频会议开发领域,我们有许多开源的音频编码库可以供选择,如G.7x系列.ACC.AC3.Speex等音频编码库,但由于网络视频会议可能会在无QOS保证的网络中传输数据,所以不仅要求音频编码器可以传输实时的高质量的音频数据,还需要保证带宽足够小,满足不同带宽的需求.因此一些宽带的编码器,如ACC.AC3等,其传输需要较大的带宽,就很难适应网络视频会议的低带宽传输要求.Speex是一个基于CELP算法的开源编码器,其可以支持从窄带(2kb)到宽带(44kbs)的音频传输,最少的音频传输带宽可以

【100分求助】音频 .speex格式的在网页中如何播放?

问题描述 最近做微信开发对于下载下来的音频文件不知如何去播放返回的格式有amr和speex格式1.amr格式的网上查了下,安装个quicktime可以播放对于speex格式的不知道如何去播放,求哪位大神支个招.顺便还想问下如何能获取到音频或者视频的播放时长. 解决方案 解决方案二:别沉啊别沉啊别沉啊解决方案三:Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式.Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用输入门槛.另外,相对于其它编解码器,Speex也很适

音频编码器

16 音频编码器 介绍当前可用的音频编码器 aac AAC(Advanced Audio Coding )编码器 当前原生(内置)编码器还处于实验阶段,而且只能支持AAC-LC(低复杂度AAC).要使用这个编码器,必须选择 'experimental'或者'lower' 因为当前还处于实验期,所以很多意外可能发生.如果需要一个更稳定的AAC编码器,参考libvo-aacenc,然而它也有一些负面报告. aac选项 b   设置码率,单位是bits/s,是自动恒定比特率(CBR)模式的码率 q  

JMF捕获音频和视频(二)

6.关于视频捕获 6.1VFW(Video for Windows) 在阐述如何识别视频采集设备之前,我们先引入VFW概念: VFW 是Microsoft公司为开发Windows平台下的视频应用程序提供的软件工具包,提供了一系列应用程序编程接口(API),用户可以通过它们很方便地实现视频捕获.视频编辑及视频播放等通用功能,还可利用回调函数开发更复杂的视频应用程序.它的特点是播放视频时不需要专用的硬件设备,而且应用灵活,可以满足视频应用程序开发的需要.Windows操作系统自身就携带了VFW,系统

采集音频和摄像头视频并实时H264编码及AAC编码

0. 前言 我在前两篇文章中写了DirectShow捕获音视频然后生成avi,再进行264编码的方法.那种方法有一些局限性,不适合实时性质的应用,如:视频会议.视频聊天.视频监控等.本文所使用的技术,适用于这种实时性的应用,通过处理采集出来的音视频的每一帧,实现实时编码,实时输出.这是我做直播系列应用的一部分,目前的情况是输入端采用DirectShow技术捕获音视频,然后对视频进行h.264编码,对音频进行aac编码,输出端则是生成文件,接下来还要进一步扩展输入端和输出端,以支持文件.桌面输入,

视音频编解码基本术语及解释

         整理了一些基本视音频术语,用于入门和查询使用. H264: H264是视频的标准,是MPEG4-10,基于内容的高效编码方式. H.264/MPEG-4第10部分,或称AVC(AdvancedVideo Coding,高级视频编码),是一种视频压缩标准,一种被广泛使用的高精度视频的录制.压缩和发布格式.第一版标准的最终草案于2003年5月完成. H.264/MPEG-4 AVC是一种面向块的基于运动补偿的编解码器标准.由ITU-T视频编码专家组与ISO/IEC联合工作组--即动