Mac系统opus Android编译集成

一、源码下载

opus官网
源码下载
我这里使用的是1.3.1版本
新建Android module opus

其目录结构如下


opus源码拷贝至src目录,在src目录下新建Android.mkApplication.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

image.png

新建encoder_jni.cppdecoder_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;
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352