一、源码下载
opus官网
源码下载
我这里使用的是1.3.1
版本
新建Android module opus
其目录结构如下
将
opus
源码拷贝至src
目录,在src
目录下新建Android.mk
与Application.mk
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(LOCAL_PATH)/celt_sources.mk
include $(LOCAL_PATH)/silk_sources.mk
include $(LOCAL_PATH)/opus_sources.mk
LOCAL_MODULE := opus
# Fixed point sources
SILK_SOURCES += $(SILK_SOURCES_FIXED)
# ARM build
CELT_SOURCES += $(CELT_SOURCES_ARM)
SILK_SOURCES += $(SILK_SOURCES_ARM)
LOCAL_SRC_FILES := \
$(CELT_SOURCES) $(SILK_SOURCES) $(OPUS_SOURCES) $(OPUS_SOURCES_FLOAT)
LOCAL_SRC_FILES += \
logger.c \
jni/encoder_jni.cpp \
jni/decoder_jni.cpp
LOCAL_LDLIBS := -lm -llog
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
$(LOCAL_PATH)/silk \
$(LOCAL_PATH)/silk/fixed \
$(LOCAL_PATH)/celt \
$(LOCAL_PATH)/jni
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 -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -O3 -fno-math-errno
LOCAL_CPPFLAGS := -DBSD=1
LOCAL_CPPFLAGS += -ffast-math -O3 -funroll-loops
LOCAL_LDLIBS += -llog
LOCAL_LDLIBS += -landroid
LOCAL_CFLAGS += -DNPT_CONFIG_ENABLE_LOGGING
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_ABI := armeabi armeabi-v7a
APP_PLATFORM := android-21
APP_ALLOW_MISSING_DEPS=true
添加编译脚本build.sh
#!/usr/bin/env bash
cd ../src
ndk-build APP_BUILD_SCRIPT=Android.mk NDK_APPLICATION_MK=Application.mk NDK_PROJECT_PATH=.
cd ../build
cp -f ../src/libs/armeabi-v7a/* ../../libs/armeabi-v7a/
编译可得libopus.so
二、jni调用
在java
部分添加编解码类
public class Encoder {
public Encoder() {
Opus.getInstance();
}
public int initEncoder(Parameter parameter) {
return _initEncoder(parameter);
}
public int encode(byte[] opus, short[] pcm) {
return _encode(opus, pcm);
}
public int destroyEncoder() {
return _destroyEncoder();
}
private native int _initEncoder(Parameter parameter);
private native int _encode(byte[] opus, short[] pcm);
private native int _destroyEncoder();
}
public class Decoder {
public Decoder() {
Opus.getInstance();
}
public int initDecoder(Parameter parameter) {
return _initDecoder(parameter);
}
public int decode(short[] pcm, byte[] opus) {
return _decode(pcm, opus);
}
public int destroyDecoder() {
return _destroyDecoder();
}
private native int _initDecoder(Parameter parameter);
private native int _decode(short[] pcm, byte[] opus);
private native int _destroyDecoder();
}
添加jni
脚本jni.sh
,用于生成头文件
#!/usr/bin/env bash
# Mirror
javah -o ../core/src/jni/decoder_jni.h -classpath ../build/intermediates/classes/release/ com.cast.opus.decoder.Decoder
javah -o ../core/src/jni/encoder_jni.h -classpath ../build/intermediates/classes/release/ com.cast.opus.encoder.Encoder
新建
encoder_jni.cpp
与 decoder_jni.cpp
分别实现编解码部分的jni
调用
encoder_jni.cpp
#include "encoder_jni.h"
#include "logger.h"
#include "opus.h"
#define TAG "encoder-jni"
OpusEncoder *gOpusEnc;
JNIEXPORT jint JNICALL Java_com_cast_opus_encoder_Encoder__1initEncoder
(JNIEnv *env, jobject obj, jobject paraObj) {
int error;
gOpusEnc = opus_encoder_create(48000, 2,
OPUS_APPLICATION_RESTRICTED_LOWDELAY,
&error);
if (gOpusEnc) {
opus_encoder_ctl(gOpusEnc, OPUS_SET_VBR(0));//0:CBR, 1:VBR
opus_encoder_ctl(gOpusEnc, OPUS_SET_VBR_CONSTRAINT(true));
opus_encoder_ctl(gOpusEnc, OPUS_SET_BITRATE(32000));
opus_encoder_ctl(gOpusEnc, OPUS_SET_COMPLEXITY(8));//8 0~10
opus_encoder_ctl(gOpusEnc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
opus_encoder_ctl(gOpusEnc, OPUS_SET_LSB_DEPTH(16));
opus_encoder_ctl(gOpusEnc, OPUS_SET_DTX(0));
opus_encoder_ctl(gOpusEnc, OPUS_SET_INBAND_FEC(0));
opus_encoder_ctl(gOpusEnc, OPUS_SET_PACKET_LOSS_PERC(0));
}
OpusLogI(TAG,"initEncoder %d ", error);
return error;
}
JNIEXPORT jint JNICALL Java_com_cast_opus_encoder_Encoder__1encode
(JNIEnv *env, jobject obj, jbyteArray opus,jshortArray pcm) {
if (!gOpusEnc || !pcm || !opus) {
OpusLogW(TAG,"encode ignore 1");
return -1;
}
jshort *pcmArray = env->GetShortArrayElements(pcm, 0);
jsize pcmSize = env->GetArrayLength(pcm);
jbyte *opusArray = env->GetByteArrayElements(opus, 0);
jsize opusSize = env->GetArrayLength(opus);
//if (opusSize < 320 || pcmSize <= 0) {
if (opusSize <= 0 || pcmSize <= 0) {
OpusLogW(TAG,"encode ignore 2");
return -1;
}
OpusLogI(TAG,"encode %d %d",pcmSize,opusSize);
//
// int nRet = opus_encode(gOpusEnc, pcmArray, pcmSize, (unsigned char *) opusArray,
// opusSize);
int nRet = opus_encode(gOpusEnc, pcmArray, pcmSize, (unsigned char *) opusArray,
opusSize);
env->ReleaseShortArrayElements(pcm, pcmArray, 0);
env->ReleaseByteArrayElements(opus, opusArray, 0);
OpusLogI(TAG,"encode %d ",nRet);
return nRet;
}
JNIEXPORT jint JNICALL Java_com_cast_opus_encoder_Encoder__1destroyEncoder
(JNIEnv *env, jobject obj) {
OpusLogI(TAG,"destroyEncoder");
if (!gOpusEnc)
return 0;
opus_encoder_destroy(gOpusEnc);
return 0;
}
decoder_jni.cpp
//
// Created by don on 2020/5/20.
//
#include "decoder_jni.h"
#include "logger.h"
#include "opus.h"
#define TAG "decoder-jni"
OpusDecoder *gOpusDec;
JNIEXPORT jint JNICALL Java_com_cast_opus_decoder_Decoder__1initDecoder
(JNIEnv *env, jobject obj, jobject paraObj){
int error;
gOpusDec = opus_decoder_create(48000, 2, &error);
OpusLogI(TAG,"initDecoder %d",error);
return error;
}
JNIEXPORT jint JNICALL Java_com_cast_opus_decoder_Decoder__1decode
(JNIEnv *env, jobject obj, jshortArray pcm,jbyteArray opus){
OpusLogI(TAG,"decode");
if (!gOpusDec || !opus || !pcm)
return -1;
jbyte *opusArray = env->GetByteArrayElements(opus, 0);
jsize opusSize = env->GetArrayLength(opus);
jshort *pcmArray = env->GetShortArrayElements(pcm, 0);
jsize pcmSize = env->GetArrayLength(pcm);
if (opusSize <= 0 || pcmSize <= 0) {
return -1;
}
int nRet = opus_decode(gOpusDec, (unsigned char *) opusArray, opusSize, pcmArray, pcmSize, 0);
env->ReleaseShortArrayElements(pcm, pcmArray, 0);
env->ReleaseByteArrayElements(opus, opusArray, 0);
return nRet;
}
JNIEXPORT jint JNICALL Java_com_cast_opus_decoder_Decoder__1destroyDecoder
(JNIEnv *env, jobject obj){
OpusLogI(TAG,"destroyDecoder");
if (!gOpusDec)
return 0;
opus_decoder_destroy(gOpusDec);
gOpusDec = NULL;
return 0;
}
三、编解码调用
pcm编码为opus
private byte[] encode(short[] buf) {
short[] newShortBuf = new short[1920];
System.arraycopy(buf, 0, newShortBuf, 0, newShortBuf.length);
Logger.i(TAG, "opus---- origin: " + Arrays.toString(newShortBuf));
byte[] opus = new byte[newShortBuf.length / 8];
if (mOpusEncoder == null) {
mOpusEncoder = new Encoder();
mOpusEncoder.initEncoder(null);
}
int result = mOpusEncoder.encode(opus, newShortBuf);
//Logger.i(TAG, "opus---- encode opus: " + Arrays.toString(opus));
byte[] newOpus = new byte[result];
System.arraycopy(opus, 0, newOpus, 0, newOpus.length);
return newOpus;
}
opus解码为pcm
private short[] decode(byte[] buf) {
short[] pcm = new short[1920];
if (mOpusDecoder == null) {
mOpusDecoder = new Decoder();
mOpusDecoder.initDecoder(null);
}
int result = mOpusDecoder.decode(pcm, buf);
Logger.i(TAG, "decode " + result);
short[] newPcm = new short[result];
System.arraycopy(pcm,0,newPcm,0,newPcm.length);
return newPcm;
}