AAudio 架构讲解以及实现范例
1、Google官方说明
Audio Hal 适配: https://source.android.google.cn/devices/audio/aaudio
AAudio 上层应用使用说明:https://developer.android.google.cn/ndk/guides/audio/aaudio/aaudio
AAudio API说明文档:https://developer.android.com/ndk/reference/group/audio
“AAudio 是在 Android O 版本中引入的全新 Android C API。此 API 专为需要低延迟的高性能音频应用而设计。”相对于之前的AudioFlinger音频引擎来说,AAudio整个通路的缓存都设置很小,同时也对我们vendor的音频处理实时性要求更高,最终降低整个音频通路的延时,为对实时性要求高的音频流的第三方apk提供帮助。
特征:
低延时、仅支持PCM流、不支持自动化路由
2、AAudio内部架构图
Code路径:
libaaudio.so: frameworks/av/media/libaaudio (运行在客户端进程)
libaaudioservice.so: frameworks/av/services/oboeservice (运行在audioserver进程的服务)
原流程图下载地址:https://download.csdn.net/download/u013120422/11937875
2.1、控制流
2.2、数据流
2.2.1、共享buffer分配顺序
- 分配AAudioServiceStreamShared的up msg buffer;(AAudioServiceStreamBase::open)
- 分配AAudioServiceStreamMMAP的up msg buffer; (AAudioServiceStreamBase::open)
- get到alsa的pcm_open mmap的fd;(AAudioServiceEndpointMMAP::open)
- 分配AAudioServiceStreamShared的audioData buffer;(AAudioServiceStreamShared::open)
upMessageQueue; // server to client
downMessageQueue; // client to server
DataQueue; // capture or playback
2.2.2、客户端和oboeservice共享buffer大小设置
函数:AAudioServiceStreamShared::calculateBufferCapacity
未指定BufferCapacity时,AAudioStream_getBufferCapacityInFrames值是AAudioStream_getFramesPerBurst值的16倍(DEFAULT_BURSTS_PER_BUFFER)
2.2.2、client和oboeservice 的FifoBuffer实例构造顺序
①.service端 service-client msg管理buffer (AAudioServiceStreamShared父类open中分配mUpMessageQueue[SharedRingBuffer]->mFifoBuffer);
②.MMAP端MMAP-service msg管理buffer (AAudioServiceStreamMMAP父类open中分配mUpMessageQueue[SharedRingBuffer]->mFifoBuffer);③.service端service-MMAP msg管理buffer (mUpCommandQueue);
④.service端service-ION data管理buffer (mDataQueue);
⑤.service端service-client data管理 buffer (AAudioServiceStreamShared::open中分配mAudioDataQueue[SharedRingBuffer]->mFifoBuffer);
⑥.client端client-service msg管理buffer (mUpCommandQueue);
⑦.client端client-service data管理buffer (mDataQueue);
2.2.3、共享和独占模式
一个AAudioServiceStreamShared实例为一路Track,AAudioServiceStreamShared.cpp可为多个实例;
AAudioServiceStreamMMAP.cpp为专有模式,只能拥有一个实例
2.3、AAudio 共享ring buffer
3、AAudio audio hal实现实例
-
Audio Hal 需适配一下4个接口
int (*start)(const struct audio_stream_out stream);
int (*stop)(const struct audio_stream_out stream);
int (*create_mmap_buffer)(const struct audio_stream_out *stream,
int32_t min_size_frames,
struct audio_mmap_buffer_info *info);
int (*get_mmap_position)(const struct audio_stream_out *stream,
struct audio_mmap_position *position);
3.1、软件实现代码
- 无DSP、无ms12的软件AAudio实现之后的框架
3.1.1、hippo_mmap_audio.c
/*
* Copyright (C) 2019 hippo Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "hippo_mmap_audio"
#include <sys/mman.h>
#include <sys/prctl.h>
#include <stdlib.h>
#include <cutils/log.h>
#include "audio_virtual_buf.h"
#include "audio_hw.h"
#include "hippo_android_utils.h"
#include "hippo_volume_utils.h"
#include "audio_hw_utils.h"
#include "hippo_mmap_audio.h"
#include "audio_hw_ms12.h"
#include "hippo_audio_timer.h"
#define MMAP_FRAME_SIZE_BYTE (4)
#define MMAP_SAMPLE_RATE_HZ (48000)
#define MMAP_BUFFER_SIZE_BYTE (MMAP_SAMPLE_RATE_HZ * MMAP_FRAME_SIZE_BYTE * 32 / 1000)
#define MMAP_WRITE_SIZE_BYTE (MMAP_SAMPLE_RATE_HZ * MMAP_FRAME_SIZE_BYTE * 8 / 1000) // every time to write 8ms data
#define MMAP_WRITE_SIZE_FRAME (MMAP_WRITE_SIZE_BYTE / MMAP_FRAME_SIZE_BYTE)
#define MMAP_WRITE_PERIOD_TIME_NANO (MMAP_WRITE_SIZE_FRAME * 1000000000LL / MMAP_SAMPLE_RATE_HZ)
enum {
MMAP_INIT,
MMAP_START,
MMAP_START_DONE,
MMAP_STOP,
MMAP_STOP_DONE
};
static FILE *g_pFile = NULL;
static void *outMmapThread(void *pArg) {
struct hippo_stream_out *out = (struct hippo_stream_out *) pArg;
hippo_mmap_audio_param_st *pstParam = (hippo_mmap_audio_param_st *)out->pstMmapAudioParam;
struct audio_virtual_buf *pstVirtualBuffer = NULL;
unsigned char *pu8CurReadAddr = pstParam->pu8MmapAddr;
unsigned char *pu8StartAddr = pstParam->pu8MmapAddr;
unsigned char *pu8TempBufferAddr = NULL;
hippo_mmap_thread_param_st *pstThread = &pstParam->stThreadParam;
struct timespec timestamp;
ALOGI("[%s:%d] enter threadloop bExitThread:%d, bStopPlay:%d, mmap addr:%p, out:%p", __func__, __LINE__,
pstThread->bExitThread, pstThread->bStopPlay, pu8StartAddr, out);
if (NULL == pu8StartAddr) {
ALOGE("[%s:%d] pu8MmapAddr is null", __func__, __LINE__);
return NULL;
}
prctl(PR_SET_NAME, "outMmapThread");
hippo_set_thread_priority("outMmapThread", pstThread->threadId);
pu8TempBufferAddr = (unsigned char *)malloc(MMAP_WRITE_SIZE_BYTE);
while (false == pstThread->bExitThread) {
if (false == pstThread->bStopPlay) {
if (pstThread->status == MMAP_START) {
ALOGI("MMAP status: start");
pu8CurReadAddr = pu8StartAddr;
pstParam->u32FramePosition = 0;
pstThread->status = MMAP_START_DONE;
if (pstVirtualBuffer) {
audio_virtual_buf_reset(pstVirtualBuffer);
audio_virtual_buf_process((void *)pstVirtualBuffer, MMAP_WRITE_PERIOD_TIME_NANO * 4);
}
}
if (pstVirtualBuffer == NULL) {
audio_virtual_buf_open((void **)&pstVirtualBuffer, "aaudio mmap",
MMAP_WRITE_PERIOD_TIME_NANO * 4, MMAP_WRITE_PERIOD_TIME_NANO * 4, 0);
audio_virtual_buf_process((void *)pstVirtualBuffer, MMAP_WRITE_PERIOD_TIME_NANO * 4);
}
unsigned int u32RemainSizeByte = (MMAP_BUFFER_SIZE_BYTE + pu8StartAddr) - pu8CurReadAddr;
if (u32RemainSizeByte >= MMAP_WRITE_SIZE_BYTE) {
memcpy(pu8TempBufferAddr, pu8CurReadAddr, MMAP_WRITE_SIZE_BYTE);
memset(pu8CurReadAddr, 0, MMAP_WRITE_SIZE_BYTE);
pu8CurReadAddr += MMAP_WRITE_SIZE_BYTE;
} else {
memcpy(pu8TempBufferAddr, pu8CurReadAddr, u32RemainSizeByte);
memset(pu8CurReadAddr, 0, u32RemainSizeByte);
memcpy(pu8TempBufferAddr + u32RemainSizeByte, pu8StartAddr, MMAP_WRITE_SIZE_BYTE - u32RemainSizeByte);
memset(pu8StartAddr, 0, MMAP_WRITE_SIZE_BYTE - u32RemainSizeByte);
pu8CurReadAddr = pu8StartAddr + MMAP_WRITE_SIZE_BYTE - u32RemainSizeByte;
}
apply_volume(out->volume_l, pu8TempBufferAddr, 2, MMAP_WRITE_SIZE_BYTE);
if (out->dev->useSubMix) {
out->stream.write(&out->stream, pu8TempBufferAddr, MMAP_WRITE_SIZE_BYTE);
} else