您的位置:首页 > 移动开发 > Unity3D

Unity 工具-Opus音频压缩(安卓篇)

2017-09-02 17:55 716 查看
现在很多游戏开始注重社交,因此语音成了游戏中不可或缺的部分。很多公司由于各种限制,可能使用第三方的SDK。虽然方便使用,但是费用挺高,而且不开源,不方便控制细节。

Opus编码器 是一个有损声音编码的格式,由互联网工程任务组(IETF)近来开发Opus 格式是一个开放格式,使用上没有任何专利或限制,应该算是Speex的升级吧,在压缩质量和效率上有了很大的提升。

具体的对比:官网对比说明

在Unity中使用Opus,首先要自己编译成对应Win、Android、IOS的支持库,网上关于Win和Android的网上有很多。我是自己写了简单的接口函数,然后分别对应编译成.dll、.so和.o,导入到Unity中用C#调用。(过去没有接触过编译,因此说的都是入门的,可能有些也不对,大神可以不用看下面的)

其中有几个坑:

Opus源码在Win有对应的解决方案,因此直接VS打开就可以,写完接口函数,直接编译就好,但是如果编译IOS,就需要自己新建工程,然后导入Opus源码,注意只导入C文件和头文件,其他的都不要导进去,并且所有的X86文件夹都不要导入,IOS只有ARM的CPU。

IOS必须在真机上测试,Unity中不能调用静态库。

安卓的是按照网上的教程,使用NDK编译,感觉很好用,写好MK,就不用管了,我只把接口函数写入,它会自动将以来的方法编译进去。

其中安卓的编译,参考 前人的经验,接口函数不同,需要写C#调用的,而不是JAVA

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_SRC_FILES := opusmain.cpp\ #封装接口opusmain.cpp

接口文件按照自己写的改,而且要写成可以C#访问的,而不是JAVA,网上的很多都只是Android使用,要自己写接口函数。

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.cpp\   #封装接口opusmain.cpp
$(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)  #编译动态库设置


其中include celt_sources.mk include silk_sources.mk include opus_sources.mk为官方自带的MK,里面有需要的编译规则,cilt主要处理音乐,silk处理声音(skype开发)



4.配置Application.mk

APP_ABI := armeabi armeabi-v7a  #编译运行的系统,平台
NDK_TOOLCHAIN_VERSION=4.9 #指定交叉编译器
APP_PLATFORM := android-19   #设定ndk编译的版本


5.接口函数

头文件opusmain.h

int frame_size;
int channels = 1;
opus_int32 opus_num;
opus_int32 pcm_num;
float* pcm_dataFloat_encoder = NULL;
opus_int16* pcm_dataInt_encoder = NULL;
unsigned char* opus_dataInt_decoder = NULL;
unsigned char* opus_dataFloat_decoder = NULL;
OpusEncoder *enc = NULL;
OpusDecoder *dec = NULL;
int error;
//必须带有extern "C",否则在Unity中找不到以下方法
extern "C" {
int nMyOpus;
int fnMyOpus(void);
void  opusEncoderInit(int Fs, int _channels);
void  opusEncoderSet(int _frame_size, bool isFloat, int quality, int signal);
void  opusDecoderInit(int Fs, int _channels, int max_len, bool isFloat);
int  opusEncoder(opus_int16 *encoder_insrc, int in_offset, unsigned char *encoder_out, int max_len);
int  opusDecoder(int len, unsigned char *decoder_insrc, int in_offset, opus_int16 *decoder_out);
int  opusEncoderFloat(float *encoder_insrc, int in_offset, unsigned char *encoder_out, int max_len);
int  opusDecoderFloat(int len, unsigned char *decoder_insrc, int in_offset, float *decoder_out);
void  opusEncoderDispose();
void  opusDecoderDispose();
}


C++文件opusmain.cpp

#include <jni.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "arch.h"
#include "opus_multistream.h"

#include "opus.h"
#include "opusmain.h"
#include "../src/opus_private.h"
#ifdef VALGRIND
#include <valgrind/memcheck.h>
#define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y))
#define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y))
#endif

// 这是导出函数的一个示例。这个函数没逻辑,就是测试Unity是否能够连接到静态库
int MyOpus()
{
return 2;
}

OpusEncoder*  OpusEncoderInit(int Fs)
{
OpusEncoder *enc = NULL;
int complexity = 1;
int signal = OPUS_SIGNAL_VOICE;
int application = OPUS_APPLICATION_AUDIO;
int bitrate_bps = 32000;
int bandwidth = OPUS_AUTO;
int use_vbr = 1;
int cvbr = 0;
int packet_loss_perc = 0;
enc = opus_encoder_create(Fs, channels, application, &error);
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal));
opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate_bps));
opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(bandwidth));
opus_encoder_ctl(enc, OPUS_SET_VBR(use_vbr));
opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cvbr));
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));
return enc;
}

void  OpusEncoderSet(OpusEncoder *enc, int complexity, int signal, int bitrate, int frame_size, int cvbr)
{
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal));
opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
opus_encoder_ctl(enc, OPUS_SET_VBR(1 - cvbr));
opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cvbr));
}
OpusDecoder*  OpusDecoderInit(int Fs)
{
OpusDecoder *dec = opus_decoder_create(Fs, channels, &error);
return dec;
}

int  OpusEncoderInt(OpusEncoder *enc, int frame_size, opus_int16 *encoder_insrc, unsigned char *encoder_out, int max_len)
{
opus_int32  opus_num = opus_encode(
enc,
encoder_insrc,
frame_size,
encoder_out,
max_len);
return opus_num;
}
int  OpusDecoderInt(OpusDecoder *dec, int frame_size, int len, unsigned char *decoder_insrc, opus_int16 *decoder_out)
{
opus_int32 pcm_num = opus_decode(
dec,
decoder_insrc,
len,
decoder_out,
frame_size,
0);
return pcm_num;
}


编译过程很简单

进入到Opus文件夹,在里面打开命令行,输入ndk-build,就可以,他会自动找到MK文件,并按规则编译。

在生成Lib文件夹,在里面就能找到.so文件

安卓的先到这,IOS和Win的,以及Unity怎么使用,在下篇博客
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息