Android 9.0 AAudio源码分析
Posted ……蓦然回首
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 9.0 AAudio源码分析相关的知识,希望对你有一定的参考价值。
android AAudio源码分析(二)
前言
在之前的章节中我们分析了AAudioservice的启动以及openStream这个方法
本章将继续分析AAdioService之中其他几个重要的方法
一、Start
经过前一节对AAudioService里面openStream的分析,相信已经对这个服务不那么陌生了
1.startStream分析
开始
xref: /frameworks/av/services/oboeservice/AAudioService.cpp
//它的功能就是启动数据流
//可以看出来这这个操作是异步的,完成后,服务将发送一个已启动的事件
aaudio_result_t AAudioService::startStream(aaudio_handle_t streamHandle) {
//根据之前创建好的流的句柄,来获取到serviceStream
sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
if (serviceStream.get() == nullptr) {
ALOGE("startStream(), illegal stream handle = 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
//然后根据拿到的serviceStream来执行start
aaudio_result_t result = serviceStream->start();
return checkForPendingClose(serviceStream, result);
}
又到了我们之前看过的AAudioServiceStreamBase这个类中
每一个AAudioServiceStreamBase相当于一个client
xref: /frameworks/av/services/oboeservice/AAudioServiceStreamBase.cpp
/**
* Start the flow of audio data.
*
* An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
*/
aaudio_result_t AAudioServiceStreamBase::start() {
aaudio_result_t result = AAUDIO_OK;
//已经运行,直接返回就可
if (isRunning()) {
return AAUDIO_OK;
}
//流启动时设置为false
//首次从流中读取数据时设置为true
setFlowing(false);
// Start with fresh presentation timestamps.
//计时器重置
mAtomicTimestamp.clear();
mClientHandle = AUDIO_PORT_HANDLE_NONE;
//内部调用startDevice
result = startDevice();
if (result != AAUDIO_OK) goto error;
// This should happen at the end of the start.
//在start完之后应该发送一个AAUDIO_SERVICE_EVENT_STARTED事件给客户端
//设置状态AAUDIO_STREAM_STATE_STARTED
sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
setState(AAUDIO_STREAM_STATE_STARTED);
mThreadEnabled.store(true);
result = mTimestampThread.start(this);
if (result != AAUDIO_OK) goto error;
return result;
error:
disconnect();
return result;
}
aaudio_result_t AAudioServiceStreamBase::startDevice() {
mClientHandle = AUDIO_PORT_HANDLE_NONE;
//获取到可以操作的AAudioServiceEndpoint对象
sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
if (endpoint == nullptr) {
ALOGE("%s() has no endpoint", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
return endpoint->startStream(this, &mClientHandle);
}
前面这两个部分的调用很简单,没有涉及到什么复杂的逻辑
startStream是AAudioServiceEndpoint.h定义的虚函数
AAudioServiceEndpointShared和AAudioServiceEndpointMMAP都继承了这个类
所以也就都实现了这个startStream方法
所以这里面的startStream会通过这两个类来实现
我们这里主要看AAudioServiceEndpointMMAP也就是独占模式下startStream
xref: /frameworks/av/services/oboeservice/AAudioServiceEndpointMMAP.cpp
aaudio_result_t AAudioServiceEndpointMMAP::startStream(sp<AAudioServiceStreamBase> stream,
audio_port_handle_t *clientHandle __unused) {
// Start the client on behalf of the AAudio service.
// Use the port handle that was provided by openMmapStream().
//利用之前打开流时得到的mPortHandle来作为参数传入startClient方法
audio_port_handle_t tempHandle = mPortHandle;
aaudio_result_t result = startClient(mMmapClient, &tempHandle);
// When AudioFlinger is passed a valid port handle then it should not change it.
//当AudioFlinger被传递一个有效的端口句柄时,它不应该改变它。
LOG_ALWAYS_FATAL_IF(tempHandle != mPortHandle,
"%s() port handle not expected to change from %d to %d",
__func__, mPortHandle, tempHandle);
ALOGV("%s(%p) mPortHandle = %d", __func__, stream.get(), mPortHandle);
return result;
}
aaudio_result_t AAudioServiceEndpointMMAP::startClient(const android::AudioClient& client,
audio_port_handle_t *clientHandle) {
if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
ALOGD("%s(%p(uid=%d, pid=%d))", __func__, &client, client.clientUid, client.clientPid);
audio_port_handle_t originalHandle = *clientHandle;
//这个mMmapStream可以当成AudioFlinger
//在这里面根据启动流的客户端和保存的句柄为参数来调用start
status_t status = mMmapStream->start(client, clientHandle);
aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
ALOGD("%s() , portHandle %d => %d, returns %d", __func__, originalHandle, *clientHandle, result);
return result;
}
继续看AudioFlinger里面
xref: /frameworks/av/services/audioflinger/Threads.cpp
status_t AudioFlinger::MmapThread::start(const AudioClient& client,
audio_port_handle_t *handle)
{
ALOGV("%s clientUid %d mStandby %d mPortId %d *handle %d", __FUNCTION__,
client.clientUid, mStandby, mPortId, *handle);
if (mHalStream == 0) {
return NO_INIT;
}
status_t ret;
if (*handle == mPortId) {
// for the first track, reuse portId and session allocated when the stream was opened
return exitStandby();
}
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
audio_io_handle_t io = mId;
//判断是输出流还是输入流
if (isOutput()) {
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = mSampleRate;
config.channel_mask = mChannelMask;
config.format = mFormat;
audio_stream_type_t stream = streamType();
audio_output_flags_t flags =
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
audio_port_handle_t deviceId = mDeviceId;
//获取到之前创建好的句柄
ret = AudioSystem::getOutputForAttr(&mAttr, &io,
mSessionId,
&stream,
client.clientPid,
client.clientUid,
&config,
flags,
&deviceId,
&portId);
} else {
audio_config_base_t config;
config.sample_rate = mSampleRate;
config.channel_mask = mChannelMask;
config.format = mFormat;
audio_port_handle_t deviceId = mDeviceId;
ret = AudioSystem::getInputForAttr(&mAttr, &io,
mSessionId,
client.clientPid,
client.clientUid,
client.packageName,
&config,
AUDIO_INPUT_FLAG_MMAP_NOIRQ,
&deviceId,
&portId);
}
// APM should not chose a different input or output stream for the same set of attributes
// and audo configuration
if (ret != NO_ERROR || io != mId) {
ALOGE("%s: error getting output or input from APM (error %d, io %d expected io %d)",
__FUNCTION__, ret, io, mId);
return BAD_VALUE;
}
bool silenced = false;
//开始start了
if (isOutput()) {
ret = AudioSystem::startOutput(mId, streamType(), mSessionId);
} else {
ret = AudioSystem::startInput(portId, &silenced);
}
Mutex::Autolock _l(mLock);
// abort if start is rejected by audio policy manager
if (ret != NO_ERROR) {
ALOGE("%s: error start rejected by AudioPolicyManager = %d", __FUNCTION__, ret);
if (mActiveTracks.size() != 0) {
mLock.unlock();
if (isOutput()) {
AudioSystem::releaseOutput(mId, streamType(), mSessionId);
} else {
AudioSystem::releaseInput(portId);
}
mLock.lock();
} else {
mHalStream->stop();
}
return PERMISSION_DENIED;
}
//音量设置
if (isOutput()) {
// force volume update when a new track is added
mHalVolFloat = -1.0f;
} else if (!silenced) {
for (const sp<MmapTrack> &track : mActiveTracks) {
if (track->isSilenced_l() && track->uid() != client.clientUid)
track->invalidate();
}
}
// Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ?
sp<MmapTrack> track = new MmapTrack(this, mAttr, mSampleRate, mFormat, mChannelMask, mSessionId,
client.clientUid, client.clientPid, portId);
track->setSilenced_l(silenced);
mActiveTracks.add(track);
sp<EffectChain> chain = getEffectChain_l(mSessionId);
if (chain != 0) {
chain->setStrategy(AudioSystem::getStrategyForStream(streamType()));
chain->incTrackCnt();
chain->incActiveTrackCnt();
}
*handle = portId;
broadcast_l();
ALOGV("%s DONE handle %d stream %p", __FUNCTION__, *handle, mHalStream.get());
return NO_ERROR;
}
看完上一章的open函数之后,再看start就没那么困难了
其实内部和audioTrack和audioRecord的方法实现差不多
只不过AAudio这边到底层数据通信时用的是MMAP
因此可以被称为高性能音频
其他的方法就不予在这边分析了
相信读者可以再看完这两章后自己去深入研究
会更有收获呦!
总结
关于AAudioService
AAudioService这个服务属于安卓原生后来才出现的一个服务
其存在的意义就是为了满足我们安卓手机中低延迟音频的需要
在苹果手机中好像早就有这样的功能了
安卓目前占据主流,但是目前我却已经嗅到了一丝丝不安
或许有些已经坐好了准备,有些已经吹起了号角
安卓作为一个开源项目,而且其底层是linux内核
其内部还是有非常多的编码精髓值得我们去学习
未来仍旧可期
觉得看完有帮助的请一键三连吧!
您的支持将是我继续下去的动力
以上是关于Android 9.0 AAudio源码分析的主要内容,如果未能解决你的问题,请参考以下文章
错误记录Oboe / AAudio 播放器报错 ( onEventFromServer - AAUDIO_SERVICE_EVENT_DISCONNECTED - FIFO cleared )(代码片
Android Init进程分析番外篇:9.0的init进程
Android 9.0 SQLiteCantOpenDatabaseException SQLITE_CANTOPEN(不支持WAL模式)源码分析定位