据说Opus 比speex库,aac各方面性能更好,也很适合做网络语音通话。
采样率 :
音频取样频率, 指每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,声音的还原也就越真实,但同时它占的资源比较多。一般采样率有 8000HZ 16000HZ 44100HZ。
比特率:
每秒的传输速率(位速, 也叫比特率)。如256kbps 或256000bps, 其中的 b 是 bit, ps 是每秒的意思,表示每秒256000bit的容量。
8bit=1Byte
帧:
帧记录了一个声音单元,其长度为样本长度(采样位数)和通道数的乘积。采样率X通道数X采样位数=8000X1X16=128000bit=16000Byte。
Opus压缩的声音需要pcm编码.
Android Opus编译过程
编译好下载地址:http://download.csdn.net/download/undiif123/9854940
1.打开android studio,点击sdk-manager->android sdk勾选红色箭头进行安装ndk等库,ndk需要设置一下环境变量,设置后cmd就可以直接编译了。
2.下载Opus_1.1.4 解压后把本文件名命名jni,celt_headers.mk,celt_sources.mk,opus_headers.mk,opus_sources.mk,silk_headers.mk,silk_sources.mk 把这mk放在jni同级目录中
3.配置android.mk
LOCAL_PATH := $(call my-dir) #加载当前路径
include $(CLEAR_VARS)
include celt_sources.mk #加载celt 所有.c的 mk
include silk_sources.mk #加载silk 所有.c 的mk
include opus_sources.mk #加载opus 所有.c 的mk
MY_MODULE_DIR := newopus #库的名称
LOCAL_MODULE := $(MY_MODULE_DIR)
SILK_SOURCES += $(SILK_SOURCES_FIXED)
#编译的源代码.c
CELT_SOURCES += $(CELT_SOURCES_ARM)
SILK_SOURCES += $(SILK_SOURCES_ARM)
LOCAL_SRC_FILES := opusmain.c\ #封装接口opusmain.c
$(CELT_SOURCES) $(SILK_SOURCES) $(OPUS_SOURCES)
LOCAL_LDLIBS := -lm –llog #加载系统的库 日志库
LOCAL_C_INCLUDES := \ #包含头文件
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/silk \
$(LOCAL_PATH)/silk/fixed \
$(LOCAL_PATH)/celt
#附加编译选项
LOCAL_CFLAGS := -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
LOCAL_CFLAGS += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT=1 -DDISABLE_FLOAT_API -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -O3 -fno-math-errno
LOCAL_CPPFLAGS := -DBSD=1
LOCAL_CPPFLAGS += -ffast-math -O3 -funroll-loops
include $(BUILD_SHARED_LIBRARY) #编译动态库设置
4.配置Application.mk
APP_ABI := armeabi armeabi-v7a #编译运行的系统,平台
NDK_TOOLCHAIN_VERSION=4.9 #指定交叉编译器
APP_PLATFORM := android-19 #设定ndk编译的版本
5.以上配置完编译
Cmd 到项目中 有jni文件夹 进行ndk-build编译就ok了 把libopus复制到项目libs对应的armeabi armeabi-v7a 目录下
封装接口源代码:
Opusmain.c
#include
#include
#include
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include
#include
#include
#include
#include
#include
#if (!defined WIN32 && !defined _WIN32) || defined(__MINGW32__)
#include
#else
#include
#define getpid _getpid
#endif
#include "opus_multistream.h"
#include "opus.h"
#include "../src/opus_private.h"
#define MAX_PACKET (1500)
#define SAMPLES (48000*30)
#define SSAMPLES (SAMPLES/3)
#define MAX_FRAME_SAMP (5760)
//每采集一帧大小为160short=320个字节
//帧的计算,例如 采样率8000 单通道 采样位数16bit
//1秒采集到数据8000X1X16=16000Byte
//网络语音传输20ms一次 也就是说1秒发50次320Byte的包 320Byte=160Short
//采集一次是160Short
#define FRAME_SIZE 160
#define SAMPLE_RATE 8000
#define CHANNELS 1
#define APPLICATION OPUS_APPLICATION_AUDIO
//参数调整压缩倍数可设置7000 声音还能正常,压缩率160short 压缩成18short
#define BITRATE 8000
#define MAX_FRAME_SIZE 6*960
#define MAX_PACKET_SIZE (3*1276)
OpusEncoder *encoder;
OpusDecoder *decoder;
int bandwidth = OPUS_AUTO;
int use_vbr = 0;
int cvbr=1;
int complexity = 4;
int packet_loss_perc=0;
//初始化语音
//com_gauss_ opus _encode java层的包名,Opus类名,open函数名 这样定义好java就可以直接调用了
jint Java_com_gauss_opus_encode_Speex_open(JNIEnv *env,jobject thiz,jint compression){
int err;
encoder = opus_encoder_create(SAMPLE_RATE, CHANNELS, APPLICATION, &err);
if (err<0)
{
return 0;
}
err = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(BITRATE));
if (err<0)
{
return 0;
}
opus_encoder_ctl(encoder, OPUS_SET_BANDWIDTH(bandwidth));
opus_encoder_ctl(encoder, OPUS_SET_VBR(use_vbr));
opus_encoder_ctl(encoder, OPUS_SET_VBR_CONSTRAINT(cvbr));
opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(compression)); opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));
decoder = opus_decoder_create(SAMPLE_RATE, CHANNELS, &err);
if (err<0)
{
return 0;
}
return 1;
}
JNIEXPORT
jint Java_com_gauss_opus_encode_Opus_getFrameSize(JNIEnv *env,jobject thiz)
{
return FRAME_SIZE;
}
//压缩
JNIEXPORT
jint Java_com_gauss_opus_encode_Opus_encode(JNIEnv *env,jobject thiz, jshortArray lin, jint offset, jbyteArray encoded, jint size)
{
int err;
int i;
int nbBytes;
unsigned short cbits[480];
unsigned short out[160];
if(encoder == NULL){
return 0;
}
//读取参数 数组
(*env)->GetShortArrayRegion(env,lin,offset,size,out);
nbBytes = opus_encode(encoder,out , size, cbits, 480);//
//设定压缩完的数据到数组中
(*env)->SetByteArrayRegion(env,encoded, 0, nbBytes, (jshort*)cbits);
return nbBytes;
}
//解压
JNIEXPORT
jint Java_com_gauss_opus_encode_Opus_decode(JNIEnv *env,jobject thiz,
jbyteArray encoded, jshortArray lin, int size)
{
int err;
int i;
int frame_size;
int nbBytes;
unsigned char cbits[MAX_PACKET_SIZE];
unsigned short out[160];
unsigned short deout[160];
if(decoder == NULL){
return 0;
}
(*env)->GetByteArrayRegion(env, encoded,0,size,out);
frame_size = opus_decode(decoder, out, size, deout, MAX_FRAME_SIZE, 0);
if(frame_size <0){
return 0;
}
int nSize = frame_size*CHANNELS;
(*env)->SetShortArrayRegion(env,lin, 0, nSize, (jshort*)deout);
return nSize;
}
//释放语音对象
JNIEXPORT
void Java_com_gauss_opus_encode_Opus_close(JNIEnv *env,jobject thiz)
{
if(encoder !=NULL){
opus_encoder_destroy(encoder);
}
if(encoder !=NULL){
opus_decoder_destroy(decoder);
}
}
IOS编译
1.使用build-libopus.sh 脚本编译,修改一下参数
VERSION=1.1.4 #opus库的版本
SDKVERSION=”10.3” #ios sdk版本
MINIOSVERSION=”8.0” #最低版本8.0
2.然后使用终端编译build-libopus.sh
3.然后把libopus.a 和include 加入工程中。
#import "OpusCode1.h"
@implementation OpusCode1
- (id)init {
if (self = [super init]) {
codecOpenedTimes = 0;
}
return self;
}
/*
quality value 1 ~ 10
*/
#define BITRATE 8000
#define MAX_FRAME_SIZE 6*960
- (void)open:(int)quality {
if ((quality < 1) || (quality > 10)) {
return;
}
if (codecOpenedTimes++ != 0) {
return;
}
else {
encodeFrameSize = 160;
decodeFrameSize = 160;
int bandwidth = OPUS_AUTO;
int use_vbr = 0;
int cvbr=1;
int packet_loss_perc=0;
//opus
int err;
encoder = opus_encoder_create(8000, 1, OPUS_APPLICATION_AUDIO, &err);
err = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(BITRATE));
opus_encoder_ctl(encoder, OPUS_SET_BANDWIDTH(bandwidth));
opus_encoder_ctl(encoder, OPUS_SET_VBR(use_vbr));
opus_encoder_ctl(encoder, OPUS_SET_VBR_CONSTRAINT(cvbr));
opus_encoder_ctl(encoder, OPUS_SET_COMPLEXITY(quality));
opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));
decoder = opus_decoder_create(8000, 1, &err);
}
}
- (NSData *)encode:(short *)pcmBuffer length:(int)lengthOfShorts {
if (codecOpenedTimes == 0) {
return nil;
}
//opus
int nbBytes;
unsigned short cbits[480];
NSMutableData *decodedData = [NSMutableData dataWithCapacity:20];
short input_frame[encodeFrameSize];
memset((Byte*)input_frame,0,encodeFrameSize*2);
memcpy(input_frame, pcmBuffer, encodeFrameSize * sizeof(short));
nbBytes = opus_encode(encoder, input_frame, encodeFrameSize, cbits, 480);
[decodedData appendBytes:cbits length:nbBytes];
return decodedData;
}
- (int)decode:(unsigned char *)encodedBytes length:(int)lengthOfBytes output:(short *)decoded {
if ( ! codecOpenedTimes)
return 0;
opus_int16 out[MAX_FRAME_SIZE];
char cbits[200];
memcpy(cbits, encodedBytes, lengthOfBytes);
int frame_size;
frame_size = opus_decode(decoder, (short*)encodedBytes, lengthOfBytes, decoded, MAX_FRAME_SIZE, 0);
return decodeFrameSize;
}
- (void)close {
if (--codecOpenedTimes != 0) {
return;
}
if(encoder !=NULL){
opus_encoder_destroy(encoder);
}
if(decoder !=NULL){
opus_decoder_destroy(decoder);
}
}
- (void)dealloc {
[self close];
#if !__has_feature(objc_arc)
// [super dealloc];
#endif
}
@end