音频延迟是指"音频数据产生→硬件发声"或"硬件拾音→数据上抛"的总耗时,传统AudioTrack/AudioRecord链路延迟普遍在100ms 以上,远超实时场景≤45ms的要求。结合前序全链路,延迟主要来源于这几处:
- 多层数据拷贝:App用户态→JNI→AudioFlinger内核态→HAL,跨进程+跨空间多次拷贝,CPU耗时高。
- 线程调度低效:AudioFlinger MixerThread采用普通优先级,系统高负载时易被抢占,导致缓冲区空转。
- 混音链路冗余:所有音频流必经系统混音,即使单一流播放也无法跳过,增加处理耗时。
- 缓冲区过大:传统API默认缓冲区偏大,保证流畅性的同时牺牲了延迟。
- Binder IPC开销:Framework与HAL、App与audioserver的跨进程通信,带来额外耗时。
要实现低延迟,必须解决减少拷贝、提升线程优先级、缩短链路、精简缓冲区 四大问题,AAudio正是围绕这几点设计的原生解决方案。AAudio 是在 Android O 版本中引入的全新 Android C API。此 API 专为需要低延迟的高性能音频应用而设计。AAudio低延迟关键机制:
- MMAP 模式:允许应用直接与音频硬件共享内存,绕过 AudioFlinger 混音器,显著降低延迟(可低至 10ms 以下)。
- NOIRQ 模式:在独占模式下,不使用中断机制,而是由客户端维护硬件计时模型,预测缓冲区读写时机,进一步减少上下文切换开销。
一、AAudio核心组件
- AAudioStream:核心数据流对象,分为输出流(播放)和输入流(录制),所有操作围绕Stream展开。
- AAudioStreamBuilder:Stream构造器,用于配置流参数、模式、回调、缓冲区大小。
- AAudioService:系统级Native服务,运行在audioserver进程,负责Stream管理、硬件调度、权限校验。
- Shared RingBuffer (共享环形缓冲区):App与AAudioService共享的内存区域,实现零拷贝数据传输。
- Hardware HAL 适配层:对接AIDL/HIDL音频HAL,兼容新旧HAL架构,数据直达硬件。
二、AAudio 两种工作模式
AAudio通过共享模式控制硬件占用,直接决定延迟高低,是低延迟的关键:
1、AAUDIO_SHARING_MODE_EXCLUSIVE (独占模式)
- 直接抢占音频硬件通道,绕过 AudioFlinger 混音,数据直达HAL;
- 延迟最低,是实时场景首选;
- 缺点:同一时间仅一个Stream占用,不支持多音频混音。
2、AAUDIO_SHARING_MODE_SHARED (共享模式)
- 经AudioFlinger混音,与其他音频流共享硬件通道;
- 延迟略高于独占模式,兼容性更好;
- 适用于需要多音频并发的低延迟场景。
三、AAudio 低延迟核心原理
**1.**零拷贝:共享内存环形缓冲区
这是AAudio低延迟的核心突破,彻底解决传统链路拷贝问题:
- AAudioService创建一块匿名共享内存,通过mmap映射到App进程和audioserver进程的虚拟地址空间;
- 播放场景:App直接将PCM数据写入共享缓冲区,AAudioService从缓冲区读取,无跨进程拷贝;
- 录制场景:HAL采集的数据写入共享缓冲区,App直接读取,全程零拷贝;
- 采用环形缓冲区设计,实现生产-消费异步解耦,避免数据阻塞。
**2.**实时线程调度
- AAudio内部线程采用SCHED_FIFO实时调度策略,优先级远高于普通应用线程;
- 系统CPU调度时,优先执行AAudio音频线程,避免线程切换、抢占导致的延迟;
- 严格限制线程内耗时操作,仅做数据读写,保证响应速度。
**3.**回调驱动机制
- 摒弃传统write/read轮询方式,采用硬件触发回调;
- 音频硬件准备好接收/发送数据时,立即触发AAudio的数据回调函数;
- App在回调中精准填充/读取数据,无轮询等待耗时,数据同步更及时。
**4.**精简链路,砍掉冗余环节
对比传统AudioTrack,AAudio播放链路大幅精简:
传统链路:App→Java层→JNI→Binder→AudioFlinger混音→Binder→HAL→驱动
AAudio 独占链路:App(Native)→共享内存→AAudioService→HAL→驱动
直接砍掉Binder通信、系统混音两大冗余环节,链路耗时骤减。
四、AAudio核心方法
AAudio_createStreamBuilder(&builder):初始化构建器对象。AAudioStreamBuilder_setDeviceId(builder, deviceId):指定音频设备 ID。通常可使用默认设备,但如需特定输入/输出设备(如蓝牙耳机),需通过 AudioManager 获取 ID 并设置。AAudioStreamBuilder_setDirection(builder, direction):设置流方向(输入AAUDIO_DIRECTION_INPUT或输出AAUDIO_DIRECTION_OUTPUT)。AAudioStreamBuilder_setSharingMode(builder, mode):设置共享模式。AAUDIO_SHARING_MODE_EXCLUSIVE:独占设备,延迟最低,但可能因设备被占用而创建失败。AAUDIO_SHARING_MODE_SHARED:允许与其他流混合,默认模式,兼容性更好。
AAudioStreamBuilder_setPerformanceMode(builder, mode):设置性能模式。AAUDIO_PERFORMANCE_MODE_LOW_LATENCY:关键设置。只有显式请求此模式,系统才会尝试启用 MMAP 路径。AAUDIO_PERFORMANCE_MODE_POWER_SAVING:以延迟换取节能。AAUDIO_PERFORMANCE_MODE_NONE:默认平衡模式。
AAudioStreamBuilder_setFormat(builder, format):设置样本格式(如AAUDIO_FORMAT_PCM_FLOAT或AAUDIO_FORMAT_PCM_I16)。若未指定,AAudio 会选择设备最优格式,打开流后需查询实际格式并进行必要转换。AAudioStreamBuilder_setSampleRate(builder, sampleRate)和AAudioStreamBuilder_setChannelCount(builder, channelCount):设置采样率和声道数。AAudioStreamBuilder_openStream(builder, &stream):根据配置创建并打开音频流。- 注意 :打开流后,应调用
AAudioStream_getDataFormat(stream)等查询函数确认实际使用的格式、采样率等,因为系统可能会调整请求的参数以匹配硬件能力。 - 完成后应调用
AAudioStreamBuilder_delete(builder)释放构建器资源。
- 注意 :打开流后,应调用
-
AAudioStreamBuilder_setDataCallback(builder, callback, userData):注册数据回调函数。系统在需要数据时自动调用此函数,适用于实时性要求高的场景。回调函数中应避免执行耗时操作(如内存分配、锁竞争),以确保低延迟。typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(
AAudioStream *stream,
///上下文环境
void userData,
///填充音频数据
void audioData,
///需要填充多少帧数据,具体的换算方式:dataSize = numFrameschannels(format == AAUDIO_FORMAT_PCM_I16 ? 2 : 1)
int32_t numFrames); -
AAudioStream_write(stream, buffer, numFrames, timeoutNanos):向输出流写入数据。 -
AAudioStream_read(stream, buffer, numFrames, timeoutNanos):从输入流读取数据。timeoutNanos设为 0 表示非阻塞,立即返回;设为正数表示阻塞等待指定时间。
流状态管理
音频流具有明确的状态机,需正确管理状态转换。
- 主要状态 :
AAUDIO_STREAM_STATE_OPEN:已创建但未启动。AAUDIO_STREAM_STATE_STARTED:正在传输数据。AAUDIO_STREAM_STATE_PAUSED:暂停,缓冲区数据保留。AAUDIO_STREAM_STATE_FLUSHED:刷新,缓冲区数据清空。AAUDIO_STREAM_STATE_STOPPED:停止。AAUDIO_STREAM_STATE_CLOSED:已关闭。
- 状态控制函数 :
AAudioStream_requestStart(stream):启动流,进入 STARTED 状态。AAudioStream_requestStop(stream):停止流。AAudioStream_requestPause(stream):暂停流。AAudioStream_requestFlush(stream):刷新缓冲区,通常在停止前调用以清除残留数据。AAudioStream_close(stream):关闭流,释放资源。**务必在不再使用时关闭流,尤其是独占模式流,以便其他应用访问设备。**
错误处理与查询
AAudioStream_getState(stream):获取当前流状态。AAudioStream_getBufferSizeInFrames(stream):获取当前缓冲区大小。AAudioStream_getXRunCount(stream):获取欠载(Underrun,输出时)或过载(Overrun,输入时)次数,用于监控性能问题。- API 调用返回值类型
aaudio_result_t,需检查返回值是否为AAUDIO_OK,否则通过AAudio_convertResultToText(result)获取错误描述。
五、AAudio播放&录制实战
1. AAudio****播放(输出流)完整流程
- 构建 Stream:通过AAudioStreamBuilder配置参数(方向、采样率、声道、格式、共享模式、回调)。
- 打开 Stream:AAudioService校验权限,分配硬件通道,创建共享环形缓冲区。
- 启动 Stream:切换流状态为Running,开启实时线程,等待硬件回调。
- 数据回调:硬件触发回调,App在回调函数中填充PCM数据到共享缓冲区。
- 数据传输:AAudioService从共享内存读取数据,直接下发至HAL层。
- 停止/关闭:播放结束,停止Stream,释放硬件通道与共享内存。
代码实现:
AAudioStream *playStream;
AAudioStreamBuilder *playBuilder;
//构建stream
aaudio_result_t result = AAudio_createStreamBuilder(&playBuilder);
if (result != AAUDIO_OK) {
//打印错误类型
printf("Failed to create stream builder: %s", AAudio_convertResultToText(result));
return;
}
//设置传输方向
AAudioStreamBuilder_setDirection(playBuilder, AAUDIO_DIRECTION_OUTPUT);
//设置分享模式
AAudioStreamBuilder_setSharingMode(playBuilder, AAUDIO_SHARING_MODE_EXCLUSIVE);
//设置性能模式
AAudioStreamBuilder_setPerformanceMode(playBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
//设置格式
AAudioStreamBuilder_setFormat(playBuilder, AAUDIO_FORMAT_PCM_I16); // 16位PCM
//设置声道
AAudioStreamBuilder_setChannelCount(playBuilder, 2); // 双声道
//设置采样率
AAudioStreamBuilder_setSampleRate(playBuilder, 48000);
//设置数据回调
AAudioStreamBuilder_setDataCallback(playBuilder, dataCallback, nullptr);
result = AAudioStreamBuilder_openStream(playBuilder, &playStream);
if (result != AAUDIO_OK) {
//打印错误类型
printf("Failed to openstream: %s", AAudio_convertResultToText(result));
return;
}
//释放构建器资源
AAudioStreamBuilder_delete(playBuilder);
//启动流
AAudioStream_requestStart(playStream);
/********流程结束,释放资源*****************/
AAudioStream_requestStop(playStream);
AAudioStream_close(playStream);
//数据回调
aaudio_data_callback_result_t dataCallback(AAudioStream *stream, void *userData,
void *audioData, int32_t numFrames) {
//播放流程中填充audioData数据
// 立体声 16-bit PCM:每帧 2 个样本,每个样本 2 字节
int16_t *samples = (int16_t *)audioData;
int32_t totalSamples = numFrames * 2; // 2 通道
for (int i = 0; i < totalSamples; ++i) {
samples[i] = generateNextSample(); // 自定义生成函数
}
return AAUDIO_CALLBACK_RESULT_CONTINUE;
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
⚠️ 注意 :播放流中不能 调用 AAudioStream_read() 或 AAudioStream_write(),否则会出错。所有数据必须通过回调写入 audioData
2、AAudio录制(输入流)完整流程
- 构建录制Stream,配置输入方向、录音参数、共享模式。
- 打开Stream,获取麦克风硬件独占权,创建共享缓冲区。
- 启动Stream,麦克风采集数据,经HAL写入共享缓冲区。
- 触发数据回调,App从共享内存读取PCM裸数据。
- 录制完成,停止Stream,释放麦克风资源。
代码实现:
AAudioStream *recordStream;
AAudioStreamBuilder *recordBuilder;
//构建stream
aaudio_result_t result = AAudio_createStreamBuilder(&recordBuilder);
if (result != AAUDIO_OK) {
//打印错误类型
printf("Failed to create stream builder: %s", AAudio_convertResultToText(result));
return;
}
//设置传输方向,录制为AAUDIO_DIRECTION_INPUT
AAudioStreamBuilder_setDirection(recordBuilder, AAUDIO_DIRECTION_INPUT);
//设置共享模式--设置为独占模式
AAudioStreamBuilder_setSharingMode(recordBuilder, AAUDIO_SHARING_MODE_EXCLUSIVE);
//设置性能模式--低延时
AAudioStreamBuilder_setPerformanceMode(recordBuilder,AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
//设置格式
AAudioStreamBuilder_setFormat(recordBuilder, AAUDIO_FORMAT_PCM_I16);
//设置通道
AAudioStreamBuilder_setChannelCount(recordBuilder, 2);
//设置采样率
AAudioStreamBuilder_setSampleRate(recordBuilder, 48000);
//设置数据回调
AAudioStreamBuilder_setDataCallback(recordBuilder, dataCallback, nullptr);
aaudio_result_t result = AAudioStreamBuilder_openStream(recordBuilder, &recordStream);
if (result != AAUDIO_OK) {
//打印错误类型
printf("Failed to open stream: %s", AAudio_convertResultToText(result));
return;
}
//释放构建器资源
AAudioStreamBuilder_delete(recordBuilder);
//启动流
AAudioStream_requestStart(recordStream);
/*******流程结束,释放资源**********/
AAudioStream_requestStop(recordStream);
AAudioStream_close(recordStream);
//数据回调
aaudio_data_callback_result_t dataCallback(AAudioStream *stream, void *userData,
void *audioData, int32_t numFrames) {
int16_t *recordedSamples = (int16_t *)audioData;
int32_t totalSamples = numFrames * 2; // 立体声
// 将录音数据复制到自定义缓冲区(如环形缓冲区)
memcpy(myBuffer + bufferOffset, recordedSamples, totalSamples * sizeof(int16_t));
bufferOffset += totalSamples;
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
⚠️ 注意 :录音流中不能 调用 AAudioStream_write() 向 audioData 写入数据,因为该缓冲区由系统填充。同样,不能 调用 AAudioStream_read()