Android移动端高性能、低延迟音频库,OpenSL ES、AAudio到Oboe

Android移动端高性能、低延迟音频库,OpenSL ES、AAudio到Oboe

移动端上的很多音频场景都会有低延迟的要求,比如k歌应用,会有耳返需求。耳返主要实现监听的功能,在低延时的情况下可以给主播一个比较真实音频的反馈,技术实现上来说就是要实时地把录制进的音频数据立刻播放出去,当然这个过程要低延迟。

再比如现在很火的空间音频应用,需要让声场随着耳机的转动而变化,这中间需要接受耳机的陀螺仪数据,然后依据陀螺仪数据调整音效,如果延迟过高,会有“不跟头”的感觉。

以Android平台为例,要实现这些低延迟的需求,我们并不能直接使用系统的AudioRecord、AudioTrack。这两个API都有很大的延迟,尤其是AudioTrack,不同的系统延迟范围在40ms到200ms之间。延迟感是不能忍受的。

那么不使用系统API实现,要怎么实现呢?

从OpenSL ES、AAudio到Oboe

OpenSL ES

OpenSL ES 是 Khronos Group 开发的 OpenSL ES™ API 规范的实现,专用于 Android。

NDK 软件包中OpenSL ES™ API 对应 Android 特定实现。利用这个库,不论是编写合成器、卡拉 OK、游戏还是其他实时应用,都可以使用 C 或 C++ 实现高性能、低延迟时间音频。

AAudio

AAudio 是作为 OpenSL ES 库的轻量级原生 Android 替代项而开发。与 OpenSL ES 相比,AAudio API 不仅较小,而且容易使用。

AAudio 是在 Android O 版本中引入的全新 Android C API。此 API 是专为需要低延迟的高性能音频应用而设计。应用通过读取并将数据写入流来与 AAudio 进行通信。

使用android系统底层的OpenSL ES或者AAudio都可以实现一个高性能的音频程序,尤其是AAudio更是简单易用,性能上,功能上都更佳,但是AAudio 是在 Android O 版本中才引入的全新 Android C API,在以前的系统版本中只能使用OpenSL ES。

那么我们需要做的是在新版本系统中使用AAudio,在不支持AAudio的系统版本中使用OpenSL ES,两套API同时使用。

不要怕困难,因为这是一个即面向未来,又兼顾现在,历史的good idea。不要害怕这有多么困难,Google已经帮我们实现了。

相关视频推荐:

LinuxC++音视频开发视频 免费】FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发

【文章福利】:小编整理了一些相关的音视频开发学习资料 (资料包括C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等), qun994289133 免费分享 ,有需要的可以加群领取哦! ~点击裙994289133加入领取资料

企鹅群994289133领取资料
企鹅群994289133领取资料

Oboe

Oboe是一个C ++库,可以轻松地在Android上构建高性能音频应用程序。内部有OpenSL ES和AAudio两大音频引擎,一行代码随意切换使用,我们可以通过JNI在android应用上使用它。

在使用Oboe做录制和播放引擎后,明显改善了延迟问题。之前 120Ms 的回路延迟设备,优化后延时可以做到 25Ms 左右,对于用户来说已经几乎没有延迟感觉了。

如何使用Oboe

官方指导文档在这里,可以通过两种方式使用oboe,这里是把源码拉下来编译到项目。

  1. 下载oboe库-> github.com/google/oboe
  2. 修改项目CMakeLists.txt 添加oboe源文件路径
# Set the path to the Oboe directory.
set (OBOE_DIR ***PATH TO OBOE***)
# Add the Oboe library as a subdirectory in your project.
# add_subdirectory tells CMake to look in this directory to
# compile oboe source files using oboe's CMake file.
# ./oboe specifies where the compiled binaries will be stored
add_subdirectory (${OBOE_DIR} ./oboe)
# Specify the path to the Oboe header files.
# This allows targets compiled with this CMake (application code)
# to see public Oboe headers, in order to access its API.
include_directories (${OBOE_DIR}/include)

添加oboe到target_link_libraries命令 target_link_libraries(native-lib oboe)

使用oboe进行录音或者播放

成功添加oboe到项目中编译通过后,很容易就能使用oboe的api实现播放或者录音,编译遇到问题的话,可以从下载的oboe源码demo中参考CMakeLists.txt。

第一步:导入oboe头文件

#include <oboe/Oboe.h>

第二步:创建builder并设置参数

oboe::AudioStreamBuilder builder;
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency) // 性能优先级
  ->setSharingMode(oboe::SharingMode::Exclusive) // 共享模式,独占
  ->setCallback(myCallback)
  ->setFormat(oboe::AudioFormat::Float); // 采样格式
  ->setDirection(oboe::Direction::Output); // 音频流方向

由于每个方法返回的是builder本身,所以可以链式调用,具体的参数意义可以查询官方文档,这里指定direction为Output意味着用于播放音频,录制音频则对应Input。

第三步:设置回调接口

上面代码中setCallback方法设置的是AudioStreamCallback对象。在音频流请求数据时,会回调onAudioReady方法

class MyCallback : public oboe::AudioStreamCallback {
public:
    oboe::DataCallbackResult
    onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) {
        // We requested AudioFormat::Float so we assume we got it.
        // For production code always check what format
        // the stream has and cast to the appropriate type.
        auto *outputData = static_cast<float *>(audioData);
        // Generate random numbers (white noise) centered around zero.
        const float amplitude = 0.2f;
        for (int i = 0; i < numFrames; ++i){
            outputData[i] = ((float)drand48() - 0.5f) * 2 * amplitude;
        return oboe::DataCallbackResult::Continue;