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

来源:互联网 发布:windows家庭版升级 编辑:程序博客网 时间:2024/06/03 15:46

现在很多游戏开始注重社交,因此语音成了游戏中不可或缺的部分。很多公司由于各种限制,可能使用第三方的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的 mkinclude silk_sources.mk  #加载silk 所有.c 的mkinclude opus_sources.mk #加载opus 所有.c 的mkMY_MODULE_DIR       := newopus  #库的名称LOCAL_MODULE        := $(MY_MODULE_DIR)SILK_SOURCES += $(SILK_SOURCES_FIXED)#编译的源代码.cCELT_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=64LOCAL_CFLAGS        += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT=1 -DDISABLE_FLOAT_API -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -O3 -fno-math-errnoLOCAL_CPPFLAGS      := -DBSD=1LOCAL_CPPFLAGS      += -ffast-math -O3 -funroll-loopsinclude $(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怎么使用,在下篇博客