在Android中使用Opus 1.3.1(Ndk编译使用Opus so库)

本文介绍如何在Android环境中使用Opus 1.3.1进行声音编码和解码,涵盖Opus特性、编译步骤及JNI接口调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android中使用Opus 1.3.1

Opus是一个开放格式的有损声音编码的格式,并在其使用上没有任何专利或限制。还可以处理各种音频应用,包括IP语音、视频会议、游戏内聊天、流音乐、甚至远程现场音乐表演。它可以从低比特率窄带语音扩展到非常高清音频的立体声音乐。支持的功能包括:

6 kb/秒到510 kb/秒的比特率;单一频道最高256 kb/秒

采样率从8 kHz(窄带)到48 kHz(全频)

帧大小从2.5毫秒到60毫秒

支持恒定比特率(CBR)、受约束比特率(CVBR)和可变比特率(VBR)

支持语音(SILK层)和音乐(CELT层)的单独或混合模式

支持单声道和立体声;支持多达255个音轨(多数据流的帧)

可动态调节比特率,音频带宽和帧大小

良好的鲁棒性丢失率和数据包丢失隐藏(PLC)

浮点和定点实现

Ndk编译产生Opus so库

快捷获取so库传送门:https://download.youkuaiyun.com/download/fepengwang/18623320

下载Opus源码

https://download.youkuaiyun.com/download/fepengwang/18623164

使用NDK-Build编译

解压下载好的opus-1.3.1.tar.gz

编写Android.mk文件到opus-1.3.1\

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
#我使用的是NDK 18
#NDK 17及以上不再支持ABIs [mips64, armeabi, mips]
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
APP_CPPFLAGS += -std=c++11
APP_STL := gnustl_shared
APP_PLATFORM := android-16

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_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 -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)

编译

$ cd opus-1.3.1
$ ndk-build APP_BUILD_SCRIPT=Android.mk NDK_PROJECT_PATH=.

注意:如果失败,看一下是否配置了ndk-build的环境变量

在这里插入图片描述
在libs中查看自己配置的平台是否完全产生,这里需要等2分钟左右哦!

转载请注明出处:https://blog.youkuaiyun.com/fepengwang/article/details/116714006#comments_16390157

在Android中使用so库

拷贝文件至Android项目中
opus-1.3.1/include —> app\src\main\cpp
opus-1.3.1/libs/* —> app\src\main\jniLibs\

在这里插入图片描述

以下为JNI文件内容

#include <jni.h>
#include <opus.h>

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jlong JNICALL Java_com_wpf_opususedemo_utils_OpusUtils_createEncoder
        (JNIEnv *env, jobject thiz, jint sampleRateInHz, jint channelConfig, jint complexity) {
    int error;
    OpusEncoder *pOpusEnc = opus_encoder_create(sampleRateInHz, channelConfig,
                                                OPUS_APPLICATION_RESTRICTED_LOWDELAY,
                                                &error);
    if (pOpusEnc) {
        opus_encoder_ctl(pOpusEnc, OPUS_SET_VBR(0));//0:CBR, 1:VBR
        opus_encoder_ctl(pOpusEnc, OPUS_SET_VBR_CONSTRAINT(true));
        opus_encoder_ctl(pOpusEnc, OPUS_SET_BITRATE(32000));
        opus_encoder_ctl(pOpusEnc, OPUS_SET_COMPLEXITY(complexity));//8    0~10
        opus_encoder_ctl(pOpusEnc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
        opus_encoder_ctl(pOpusEnc, OPUS_SET_LSB_DEPTH(16));
        opus_encoder_ctl(pOpusEnc, OPUS_SET_DTX(0));
        opus_encoder_ctl(pOpusEnc, OPUS_SET_INBAND_FEC(0));
        opus_encoder_ctl(pOpusEnc, OPUS_SET_PACKET_LOSS_PERC(0));
    }
    return (jlong) pOpusEnc;
}
JNIEXPORT jlong JNICALL Java_com_wpf_opususedemo_utils_OpusUtils_createDecoder
        (JNIEnv *env, jobject thiz, jint sampleRateInHz, jint channelConfig) {
    int error;
    OpusDecoder *pOpusDec = opus_decoder_create(sampleRateInHz, channelConfig, &error);
    return (jlong) pOpusDec;
}
JNIEXPORT jint JNICALL Java_com_wpf_opususedemo_utils_OpusUtils_encode
        (JNIEnv *env, jobject thiz, jlong pOpusEnc, jshortArray samples, jint offset,
         jbyteArray bytes) {
    OpusEncoder *pEnc = (OpusEncoder *) pOpusEnc;
    if (!pEnc || !samples || !bytes)
        return 0;
    jshort *pSamples = env->GetShortArrayElements(samples, 0);
    jsize nSampleSize = env->GetArrayLength(samples);
    jbyte *pBytes = env->GetByteArrayElements(bytes, 0);
    jsize nByteSize = env->GetArrayLength(bytes);
    if (nSampleSize - offset < 320 || nByteSize <= 0)
        return 0;
    int nRet = opus_encode(pEnc, pSamples + offset, nSampleSize, (unsigned char *) pBytes,
                           nByteSize);
    env->ReleaseShortArrayElements(samples, pSamples, 0);
    env->ReleaseByteArrayElements(bytes, pBytes, 0);
    return nRet;
}
JNIEXPORT jint JNICALL Java_com_wpf_opususedemo_utils_OpusUtils_decode
        (JNIEnv *env, jobject thiz, jlong pOpusDec, jbyteArray bytes,
         jshortArray samples) {
    OpusDecoder *pDec = (OpusDecoder *) pOpusDec;
    if (!pDec || !samples || !bytes)
        return 0;
    jshort *pSamples = env->GetShortArrayElements(samples, 0);
    jbyte *pBytes = env->GetByteArrayElements(bytes, 0);
    jsize nByteSize = env->GetArrayLength(bytes);
    jsize nShortSize = env->GetArrayLength(samples);
    if (nByteSize <= 0 || nShortSize <= 0) {
        return -1;
    }
    int nRet = opus_decode(pDec, (unsigned char *) pBytes, nByteSize, pSamples, nShortSize, 0);
    env->ReleaseShortArrayElements(samples, pSamples, 0);
    env->ReleaseByteArrayElements(bytes, pBytes, 0);
    return nRet;
}
JNIEXPORT void JNICALL Java_com_wpf_opususedemo_utils_OpusUtils_destroyEncoder
        (JNIEnv *env, jobject thiz, jlong pOpusEnc) {
    OpusEncoder *pEnc = (OpusEncoder *) pOpusEnc;
    if (!pEnc)
        return;
    opus_encoder_destroy(pEnc);
}
JNIEXPORT void JNICALL Java_com_wpf_opususedemo_utils_OpusUtils_destroyDecoder
        (JNIEnv *env, jobject thiz, jlong pOpusDec) {
    OpusDecoder *pDec = (OpusDecoder *) pOpusDec;
    if (!pDec)
        return;
    opus_decoder_destroy(pDec);
}
#ifdef __cplusplus
}
#endif

注意注意
CMakeLists.txt内容的编写!!!受自己本地NDK开发环境的影响,3.4.3版本中必须添加${CMAKE_CURRENT_SOURCE_DIR}否则会报错

这里记录一下我的踩坑:missing and no known rule to make it

解决方式:

CMakeLists.txt中配置的路径都是默认相对于CMakeLists.txt,但是在路径前还是要添加CMakeLists.txt的相对位置标识符${CMAKE_CURRENT_SOURCE_DIR}

CMakeList.tex内容:

cmake_minimum_required(VERSION 3.10.2)

#set(libs_DIR src/main/jniLibs/${ANDROID_ABI}/libopus.so)
set(libs_include_DIR src/main/cpp/opus/include)
#支持-std=gnu++11
set(CMAKE_VERBOSE_MAKEFILE on)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")


find_library( # Sets the name of the path variable.
              log-lib
              log )
add_library( # Sets the name of the library.
        opus-use
        SHARED
        ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/opus-use.cpp)


#关联头文件目录到库名
target_include_directories(
        opus-use # 库名
        PRIVATE # 库属性
        ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/opus/include # 头文件目录
)


#[[
每个库要单独添加一次
add_library(第三方库名称 共享动态库 导入)
set_target_properties(
        第三方库名称
        配置导入库
        具体第三方库路径
)
]]
add_library(opus_my SHARED IMPORTED)
set_target_properties(
        opus_my
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopus.so
)




target_link_libraries( # Specifies the target library.
        opus-use
        ${log-lib}
        opus_my)



完整版代码链接
https://download.youkuaiyun.com/download/fepengwang/18631244

### Android 平台下的 OPUS 编解码器使用 #### 介绍 `opus_android` 是一个移植自官方 Opus 编解码Android 。通过这个,在应用程序级别上能够轻松操作 Opus 格式的音频文件,功能涵盖了录音、播放以及编解码等功能[^1]。 #### 使用方法 为了在 Android 中集成并使用 `opus_android` 进行编解码工作,开发者需遵循如下指南: - **引入依赖**:首先应当将此作为项目的依赖项加入到工程配置中。 - **初始化环境**:确保设备具备必要的权限(如读取/写入外部存储),以便于处理多媒体数据流。 - **调用 API 接口**:依据具体需求选用合适的类和函数完成相应的任务逻辑编写。 下面给出一段简单的 Java 实现示例用于说明如何基于该执行基本的操作——录制 PCM 数据并通过 Opus 编码保存为 .opus 文件;之后再将其解码回原始 PCM 流进行播放。 ```java // 导入中的必要组件 import com.github.opus.android.Opus; import java.io.File; import java.nio.ShortBuffer; public class AudioProcessor { private static final String OUTPUT_FILE_PATH = "/path/to/output/file"; public void recordAndEncode() throws Exception { // 创建输出文件实例 File outputFile = new File(OUTPUT_FILE_PATH); try (Opus.Encoder encoder = new Opus.Encoder()) { ShortBuffer pcmData; // 假设这里已经获取到了PCM格式的数据 byte[] encodedBytes = encoder.encode(pcmData); // 将编码后的字节数组写入指定路径的目标文件内 Files.write(outputFile.toPath(), encodedBytes); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } public short[] decodeAndPlay(String filePath) throws IOException { try (Opus.Decoder decoder = new Opus.Decoder(filePath)) { return decoder.decode(); } } } ``` 上述代码片段展示了怎样创建一个名为 `AudioProcessor` 的处理器类来进行音轨记录与转换的工作流程。其中包含了两个主要的方法:一个是负责收集来自麦克风或其他声源处未经压缩的声音信号,并经过 Opus 算法加工成适合网络传输的小型包体形式存盘;另一个则是相反的过程,即从本地加载已有的 Opus 音频资源还原出可被扬声器重放出来的线性脉冲序列。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

feipengwang6666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值