Android 9.0 AAudio源码分析
Posted ……蓦然回首
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 9.0 AAudio源码分析相关的知识,希望对你有一定的参考价值。
android 9.0 AAudio源码分析(一)
提示:再读文章之前可以先学习一下Binder和MMAP的知识
前言
因为网上目前还没有关于安卓AAudio方面的原理分析,所以笔者通过研究安卓9.0源码,总结了一些偏向于底层的一些东西,希望可以帮助到大家
一、AAudio是什么
AAudio 是在 Android O 版本中引入的全新 Android C API。此 API 专为需要低延迟的高性能音频应用而设计。应用通过读取数据并将数据写入流来与 AAudio 进行通信
二、AAudio源码解析
1.启动
(1)系统服务启动main_audioserver.cpp
xref: /frameworks/av/media/audioserver/main_audioserver.cpp
// AAudioService should only be used in OC-MR1 and later.
// And only enable the AAudioService if the system MMAP policy explicitly allows it.
// This prevents a client from misusing AAudioService when it is not supported.
//AAudioService只能在OC-MR1及更高版本中使用
//并且只有在系统MMAP策略明确允许的情况下才启用AAudioService
//这可以防止客户端在不支持AAudioService时误用它
aaudio_policy_t mmapPolicy = property_get_int32(AAUDIO_PROP_MMAP_POLICY,
AAUDIO_POLICY_NEVER);
if (mmapPolicy == AAUDIO_POLICY_AUTO || mmapPolicy == AAUDIO_POLICY_ALWAYS) {
AAudioService::instantiate();
}
可以看到是有一个AAudioService来初始化的。
(2)AAudioService是什么?
先来看它都继承了什么:
binder不用多说,BnAAudioService和AAudioServiceInterface要看一下
xref: /frameworks/av/services/oboeservice/AAudioService.h
class AAudioService :
public BinderService<AAudioService>,
public BnAAudioService,
public aaudio::AAudioServiceInterface
{
friend class BinderService<AAudioService>;
先来看BnAAudioService:
从名字来看就是一个服务
这个类写在了IAAudioService.h的头文件中
而client的各种使用AAudio的操作,都通过层层调用及参数包装汇聚到onTransact(),再由onTransact()分发到真正的目标方法执行
xref: /frameworks/av/media/libaaudio/src/binding/IAAudioService.h
class BnAAudioService : public BnInterface<IAAudioService> {
public:
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags = 0);
};
具体都有哪些方法呢?
class IAAudioService : public IInterface {
public:
DECLARE_META_INTERFACE(AAudioService);
// Register an object to receive audio input/output change and track notifications.
// For a given calling pid, AAudio service disregards any registrations after the first.
// Thus the IAAudioClient must be a singleton per process.
virtual void registerClient(const sp<IAAudioClient>& client) = 0;
/**
* @param request info needed to create the stream
* @param configuration contains information about the created stream
* @return handle to the stream or a negative error
*/
virtual aaudio::aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) = 0;
virtual aaudio_result_t closeStream(aaudio::aaudio_handle_t streamHandle) = 0;
/* Get an immutable description of the in-memory queues
* used to communicate with the underlying HAL or Service.
*/
virtual aaudio_result_t getStreamDescription(aaudio::aaudio_handle_t streamHandle,
aaudio::AudioEndpointParcelable &parcelable) = 0;
/**
* Start the flow of data.
* This is asynchronous. When complete, the service will send a STARTED event.
*/
virtual aaudio_result_t startStream(aaudio::aaudio_handle_t streamHandle) = 0;
/**
* Stop the flow of data such that start() can resume without loss of data.
* This is asynchronous. When complete, the service will send a PAUSED event.
*/
virtual aaudio_result_t pauseStream(aaudio::aaudio_handle_t streamHandle) = 0;
/**
* Stop the flow of data such that the data currently in the buffer is played.
* This is asynchronous. When complete, the service will send a STOPPED event.
*/
virtual aaudio_result_t stopStream(aaudio::aaudio_handle_t streamHandle) = 0;
/**
* Discard any data held by the underlying HAL or Service.
* This is asynchronous. When complete, the service will send a FLUSHED event.
*/
virtual aaudio_result_t flushStream(aaudio::aaudio_handle_t streamHandle) = 0;
/**
* Manage the specified thread as a low latency audio thread.
*/
virtual aaudio_result_t registerAudioThread(aaudio::aaudio_handle_t streamHandle,
pid_t clientThreadId,
int64_t periodNanoseconds) = 0;
virtual aaudio_result_t unregisterAudioThread(aaudio::aaudio_handle_t streamHandle,
pid_t clientThreadId) = 0;
};
这些函数能够让用户很方便的使用AAudio实现播放或者录制等等
顺便看一下IAAudioService.cpp文件都干了啥?
xref: /frameworks/av/media/libaaudio/src/binding/IAAudioService.cpp
/**
* This is used by the AAudio Client to talk to the AAudio Service.
*
* The order of parameters in the Parcels must match with code in AAudioService.cpp.
*/
class BpAAudioService : public BpInterface<IAAudioService>
{
public:
explicit BpAAudioService(const sp<IBinder>& impl)
: BpInterface<IAAudioService>(impl)
{
}
void registerClient(const sp<IAAudioClient>& client) override
{
Parcel data, reply;
data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
data.writeStrongBinder(IInterface::asBinder(client));
remote()->transact(REGISTER_CLIENT, data, &reply);
}
aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) override {
Parcel data, reply;
// send command
data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
// request.dump();
request.writeToParcel(&data);
status_t err = remote()->transact(OPEN_STREAM, data, &reply);
if (err != NO_ERROR) {
ALOGE("BpAAudioService::client openStream transact failed %d", err);
return AAudioConvert_androidToAAudioResult(err);
}
// parse reply
aaudio_handle_t stream;
err = reply.readInt32(&stream);
if (err != NO_ERROR) {
ALOGE("BpAAudioService::client transact(OPEN_STREAM) readInt %d", err);
return AAudioConvert_androidToAAudioResult(err);
} else if (stream < 0) {
ALOGE("BpAAudioService::client OPEN_STREAM passed stream %d", stream);
return stream;
}
err = configurationOutput.readFromParcel(&reply);
if (err != NO_ERROR) {
ALOGE("BpAAudioService::client openStream readFromParcel failed %d", err);
closeStream(stream);
return AAudioConvert_androidToAAudioResult(err);
}
return stream;
}
...
};
// Implement an interface to the service.
// This is here so that you don't have to link with libaaudio static library.
IMPLEMENT_META_INTERFACE(AAudioService, "IAAudioService");
// The order of parameters in the Parcels must match with code in BpAAudioService
status_t BnAAudioService::onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags) {
aaudio_handle_t streamHandle;
aaudio::AAudioStreamRequest request;
aaudio::AAudioStreamConfiguration configuration;
pid_t tid;
int64_t nanoseconds;
aaudio_result_t result;
status_t status = NO_ERROR;
ALOGV("BnAAudioService::onTransact(%i) %i", code, flags);
switch(code) {
case REGISTER_CLIENT: {
CHECK_INTERFACE(IAAudioService, data, reply);
sp<IAAudioClient> client = interface_cast<IAAudioClient>(
data.readStrongBinder());
registerClient(client);
return NO_ERROR;
} break;
case OPEN_STREAM: {
CHECK_INTERFACE(IAAudioService, data, reply);
request.readFromParcel(&data);
result = request.validate();
if (result != AAUDIO_OK) {
streamHandle = result;
} else {
//ALOGD("BnAAudioService::client openStream request dump --------------------");
//request.dump();
// Override the uid and pid from the client in case they are incorrect.
request.setUserId(IPCThreadState::self()->getCallingUid());
request.setProcessId(IPCThreadState::self()->getCallingPid());
streamHandle = openStream(request, configuration);
//ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X",
// streamHandle);
}
reply->writeInt32(streamHandle);
configuration.writeToParcel(reply);
return NO_ERROR;
} break;
...
default:
// ALOGW("BnAAudioService::onTransact not handled %u", code);
return BBinder::onTransact(code, data, reply, flags);
}
}
} /* namespace android */
其中声明了BpAAudioService类,实现了这些函数
最终还是经过transact调到了BnAAudioService,然后根据参数分发处理
整个过程用到了跨进程通信的方法:Binder
再来看AAudioService的另一个继承:AAudioServiceInterface
xref: /frameworks/av/media/libaaudio/src/binding/AAudioServiceInterface.h
/**
* This has the same methods as IAAudioService but without the Binder features.
*
* It allows us to abstract the Binder interface and use an AudioStreamInternal
* both in the client and in the service.
*/
只看注释,因为定义的方法和IAAudioService里面几乎一样,就是少了Binder
所以也就可以单纯的把它看作一个接口,客户端和服务端都有继承AAudioServiceInterface,统一了客户端和服务端的接口
(3)其他几个重要的类的介绍
IAAudioClient:
xref: /frameworks/av/media/libaaudio/src/binding/AAudioBinderClient.h
通过绑定器与服务对话来实现AAudioServiceInterface里面的方法,就是我们上面说到的客户端
在其内部通过从SM(ServiceManager)里面拿到AAudioService的引用,绑定之后使用
class AAudioBinderClient : public virtual android::RefBase
, public AAudioServiceInterface
, public android::Singleton<AAudioBinderClient> {
加上它的继承的一些类来看,会更加一目了然。
AAudioBinderClient
xref: /frameworks/av/media/libaaudio/src/binding/IAAudioClient.h
这个类是用来service与client通信的,其中主要内容就是流 状态的变化
// Interface (our AIDL) - client methods called by service
class IAAudioClient : public IInterface {
public:
DECLARE_META_INTERFACE(AAudioClient);
virtual void onStreamChange(aaudio::aaudio_handle_t handle, int32_t opcode, int32_t value) = 0;
};
class BnAAudioClient : public BnInterface<IAAudioClient> {
public:
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags = 0);
};
} /* namespace android */
总结:
关于AAudioSevice,里面用了很多Binder的知识,把Binder看透了也就了解了这整个调用的流程
Binder流程如图:
2.工作原理
AAudioService提供了很多供我们使用它的方法,接下来就是对其中几个重要的方法进行剖析,了解其工作原理
(1)openStream:
先来看一张时序图:
打开流的过程如图所示,其中主要会分两种情况:
共享模式:多个音频流可以同时访问该音频设备,该模式下音频的延迟略高于独占模式
独占模式:只有该音频流能访问该音频设备,其它音频流拒绝访问,该模式下 音频流 性能高 , 延迟低
注意:如果不再使用该音频设备,需要马上释放音频流,以免影响其它音频流访问该音频设备(独占模式)
接下来来看代码:
xref: /frameworks/av/services/oboeservice/AAudioService.cpp
aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) {
aaudio_result_t result = AAUDIO_OK;
sp<AAudioServiceStreamBase> serviceStream;
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
bool sharingModeMatchRequired = request.isSharingModeMatchRequired();
aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
// Enforce limit on client processes.
//对客户端进程实施限制
//这里面规定每个进程都有最大音频流数
//当超过这个最大音频流数量时,将不能继续执行函数
pid_t pid = request.getProcessId();
if (pid != mAudioClient.clientPid) {
int32_t count = AAudioClientTracker::getInstance().getStreamCount(pid);
if (count >= MAX_STREAMS_PER_PROCESS) {
ALOGE("openStream(): exceeded max streams per process %d >= %d",
count, MAX_STREAMS_PER_PROCESS);
return AAUDIO_ERROR_UNAVAILABLE;
}
}
//传输模式检测,只允许共享模式或者独占模式
if (sharingMode != AAUDIO_SHARING_MODE_EXCLUSIVE && sharingMode != AAUDIO_SHARING_MODE_SHARED) {
ALOGE("openStream(): unrecognized sharing mode = %d", sharingMode);
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
}
//独占模式的情况
if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
// only trust audioserver for in service indication
//这个inService变量代表着这个流是不是被AAudioService单独打开的(没有客户端)
bool inService = false;
if (mAudioClient.clientPid == IPCThreadState::self()->getCallingPid() &&
mAudioClient.clientUid == IPCThreadState::self()->getCallingUid()) {
inService = request.isInService();
}
//之后就可以new一个独占模式专用的对象来打开流
//传递了两个参数:一个是AAudioService的指针,一个就是inService
//在之后对流进行其他操作时会用到inService这个参数
serviceStream = new AAudioServiceStreamMMAP(*this, inService);
result = serviceStream->open(request);
if (result != AAUDIO_OK) {
// Clear it so we can possibly fall back to using a shared stream.
ALOGW("openStream(), could not open in EXCLUSIVE mode");
serviceStream.clear();
}
}
// if SHARED requested or if EXCLUSIVE failed
//接下来是共享模式打开流
//当创建独占模式的流失败并且传输模式与请求的模式不同时,会以共享模式打开流
if (sharingMode == AAUDIO_SHARING_MODE_SHARED
|| (serviceStream.get() == nullptr && !sharingModeMatchRequired)) {
serviceStream = new AAudioServiceStreamShared(*this);
result = serviceStream->open(request);
}
//创建不成功
if (result != AAUDIO_OK) {
serviceStream.clear();
ALOGE("openStream(): failed, return %d = %s",
result, AAudio_convertResultToText(result));
return result;
} else {
//创建成功
//生成这个AAduio流服务的句柄返回供客户端调用
aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get());
ALOGD("openStream(): handle = 0x%08X", handle);
serviceStream->setHandle(handle);
pid_t pid = request.getProcessId();
//将这个创建好的音频流注册为客户端用来发送数据
AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
configurationOutput.copyFrom(*serviceStream);
return handle;
}
}
接下来分析独占模式下是什么情况
xref: /frameworks/av/services/oboeservice/AAudioServiceStreamMMAP.cpp
//这些对应于独占模式MMAP客户端流。
//它独占使用一个AAudioServiceEndpointMMAP与底层设备或端口通信。
//这个服务流的核心就是使用MMAP缓冲区
//继承了AAudioServiceStreamBase
class AAudioServiceStreamMMAP : public AAudioServiceStreamBase
// Open stream on HAL and pass information about the shared memory buffer back to the client.
//打开HAL上的流并将有关共享内存缓冲区的信息传递回客户端。
aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request) {
sp<AAudioServiceStreamMMAP> keep(this);
//带着独占模式的参数,调用AAudioServiceStreamBase的open函数
aaudio_result_t result = AAudioServiceStreamBase::open(request,
AAUDIO_SHARING_MODE_EXCLUSIVE);
if (result != AAUDIO_OK) {
return result;
}
//把这个mServiceEndpointWeak升级为强指针,看它是不是还存活
//mServiceEndpoint变量可以由多个线程访问
//因此,我们通过本地向智能指针升级弱指针来访问它,这是线程安全的
sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
if (endpoint == nullptr) {
ALOGE("%s() has no endpoint", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
//注册这个AAudioServiceStreamMMAP流
//AAudioServiceEndpoint(endpoint)由AAudioServiceStreamBase的子类使用,与底层音频设备或端口通信
//保证流的操作是线程安全的
result = endpoint->registerStream(keep);
if (result != AAUDIO_OK) {
return result;
}
setState(AAUDIO_STREAM_STATE_OPEN);
return AAUDIO_OK;
}
继续分析AAudioServiceStreamBase::open
xref: /frameworks/av/services/oboeservice/AAudioServiceStreamBase.cpp
//AAudioServiceStreamBase的每个实例都对应于一个客户机流。
//它使用AAudioServiceEndpoint的子类与底层设备或端口通信。
aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
aaudio_sharing_mode_t sharingMode) {
AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
aaudio_result_t result = AAUDIO_OK;
mMmapClient.clientUid = request.getUserId();
mMmapClient.clientPid = request.getProcessId();
mMmapClient.packageName.setTo(String16("")); // TODO What should we do here?
// Limit scope of lock to avoid recursive lock in close().
//限制锁的作用域以避免close()中的递归锁。
//判断mUpMessageQueueLock是否为空,也就是说看它是否是第一次打开流
{
std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
if (mUpMessageQueue != nullptr) {
ALOGE("%s() called twice", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
//创建共享内存
mUpMessageQueue = new SharedRingBuffer();
result = mUpMessageQueue->allocate(sizeof(AAudioServiceMessage),
QUEUE_UP_CAPACITY_COMMANDS);
if (result != AAUDIO_OK) {
goto error;
}
// This is not protected by a lock because the stream cannot be
// referenced until the service returns a handle to the client.
// So only one thread can open a stream.
//这不受锁的保护,因为在服务向客户端返回句柄之前,流不能被引用。
//所以只有一个线程可以打开一个流。
mServiceEndpoint = mEndpointManager.openEndpoint(mAudioServiceAndroid 9.0 AAudio源码分析
错误记录Oboe / AAudio 播放器报错 ( onEventFromServer - AAUDIO_SERVICE_EVENT_DISCONNECTED - FIFO cleared )(代码片
Android Init进程分析番外篇:9.0的init进程
Android 9.0 SQLiteCantOpenDatabaseException SQLITE_CANTOPEN(不支持WAL模式)源码分析定位