热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:Android9.0AAudio源码分析

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Android9.0AAudio源码分析相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了Android 9.0 AAudio源码分析相关的知识,希望对你有一定的参考价值。






android AAudio源码分析(二)



文章目录


  • Android AAudio源码分析(二)
  • 前言
  • 一、Start
    • 1.startStream分析

  • 总结





前言

在之前的章节中我们分析了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 &#61; convertHandleToServiceStream(streamHandle);
if (serviceStream.get() &#61;&#61; nullptr) {
ALOGE("startStream(), illegal stream handle &#61; 0x%0x", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
//然后根据拿到的serviceStream来执行start
aaudio_result_t result &#61; 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 &#61; AAUDIO_OK;
//已经运行&#xff0c;直接返回就可
if (isRunning()) {
return AAUDIO_OK;
}
//流启动时设置为false
//首次从流中读取数据时设置为true
setFlowing(false);
// Start with fresh presentation timestamps.
//计时器重置
mAtomicTimestamp.clear();
mClientHandle &#61; AUDIO_PORT_HANDLE_NONE;
//内部调用startDevice
result &#61; startDevice();
if (result !&#61; 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 &#61; mTimestampThread.start(this);
if (result !&#61; AAUDIO_OK) goto error;
return result;
error:
disconnect();
return result;
}
aaudio_result_t AAudioServiceStreamBase::startDevice() {
mClientHandle &#61; AUDIO_PORT_HANDLE_NONE;
//获取到可以操作的AAudioServiceEndpoint对象
sp<AAudioServiceEndpoint> endpoint &#61; mServiceEndpointWeak.promote();
if (endpoint &#61;&#61; nullptr) {
ALOGE("%s() has no endpoint", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
return endpoint->startStream(this, &mClientHandle);
}

前面这两个部分的调用很简单&#xff0c;没有涉及到什么复杂的逻辑
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 &#61; mPortHandle;
aaudio_result_t result &#61; startClient(mMmapClient, &tempHandle);
// When AudioFlinger is passed a valid port handle then it should not change it.
//当AudioFlinger被传递一个有效的端口句柄时&#xff0c;它不应该改变它。
LOG_ALWAYS_FATAL_IF(tempHandle !&#61; mPortHandle,
"%s() port handle not expected to change from %d to %d",
__func__, mPortHandle, tempHandle);
ALOGV("%s(%p) mPortHandle &#61; %d", __func__, stream.get(), mPortHandle);
return result;
}
aaudio_result_t AAudioServiceEndpointMMAP::startClient(const android::AudioClient& client,
audio_port_handle_t *clientHandle) {
if (mMmapStream &#61;&#61; nullptr) return AAUDIO_ERROR_NULL;
ALOGD("%s(%p(uid&#61;%d, pid&#61;%d))", __func__, &client, client.clientUid, client.clientPid);
audio_port_handle_t originalHandle &#61; *clientHandle;
//这个mMmapStream可以当成AudioFlinger
//在这里面根据启动流的客户端和保存的句柄为参数来调用start
status_t status &#61; mMmapStream->start(client, clientHandle);
aaudio_result_t result &#61; AAudioConvert_androidToAAudioResult(status);
ALOGD("%s() , portHandle %d &#61;> %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 &#61;&#61; 0) {
return NO_INIT;
}
status_t ret;
if (*handle &#61;&#61; mPortId) {
// for the first track, reuse portId and session allocated when the stream was opened
return exitStandby();
}
audio_port_handle_t portId &#61; AUDIO_PORT_HANDLE_NONE;
audio_io_handle_t io &#61; mId;
//判断是输出流还是输入流
if (isOutput()) {
audio_config_t config &#61; AUDIO_CONFIG_INITIALIZER;
config.sample_rate &#61; mSampleRate;
config.channel_mask &#61; mChannelMask;
config.format &#61; mFormat;
audio_stream_type_t stream &#61; streamType();
audio_output_flags_t flags &#61;
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
audio_port_handle_t deviceId &#61; mDeviceId;
//获取到之前创建好的句柄
ret &#61; AudioSystem::getOutputForAttr(&mAttr, &io,
mSessionId,
&stream,
client.clientPid,
client.clientUid,
&config,
flags,
&deviceId,
&portId);
} else {
audio_config_base_t config;
config.sample_rate &#61; mSampleRate;
config.channel_mask &#61; mChannelMask;
config.format &#61; mFormat;
audio_port_handle_t deviceId &#61; mDeviceId;
ret &#61; 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 !&#61; NO_ERROR || io !&#61; 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 &#61; false;
//开始start了
if (isOutput()) {
ret &#61; AudioSystem::startOutput(mId, streamType(), mSessionId);
} else {
ret &#61; AudioSystem::startInput(portId, &silenced);
}
Mutex::Autolock _l(mLock);
// abort if start is rejected by audio policy manager
if (ret !&#61; NO_ERROR) {
ALOGE("%s: error start rejected by AudioPolicyManager &#61; %d", __FUNCTION__, ret);
if (mActiveTracks.size() !&#61; 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 &#61; -1.0f;
} else if (!silenced) {
for (const sp<MmapTrack> &track : mActiveTracks) {
if (track->isSilenced_l() && track->uid() !&#61; client.clientUid)
track->invalidate();
}
}
// Given that MmapThread::mAttr is mutable, should a MmapTrack have attributes ?
sp<MmapTrack> track &#61; new MmapTrack(this, mAttr, mSampleRate, mFormat, mChannelMask, mSessionId,
client.clientUid, client.clientPid, portId);
track->setSilenced_l(silenced);
mActiveTracks.add(track);
sp<EffectChain> chain &#61; getEffectChain_l(mSessionId);
if (chain !&#61; 0) {
chain->setStrategy(AudioSystem::getStrategyForStream(streamType()));
chain->incTrackCnt();
chain->incActiveTrackCnt();
}
*handle &#61; portId;
broadcast_l();
ALOGV("%s DONE handle %d stream %p", __FUNCTION__, *handle, mHalStream.get());
return NO_ERROR;
}

看完上一章的open函数之后&#xff0c;再看start就没那么困难了
其实内部和audioTrack和audioRecord的方法实现差不多
只不过AAudio这边到底层数据通信时用的是MMAP
因此可以被称为高性能音频

其他的方法就不予在这边分析了
相信读者可以再看完这两章后自己去深入研究
会更有收获呦&#xff01;




总结

关于AAudioService

AAudioService这个服务属于安卓原生后来才出现的一个服务
其存在的意义就是为了满足我们安卓手机中低延迟音频的需要
在苹果手机中好像早就有这样的功能了
安卓目前占据主流&#xff0c;但是目前我却已经嗅到了一丝丝不安
或许有些已经坐好了准备&#xff0c;有些已经吹起了号角

安卓作为一个开源项目&#xff0c;而且其底层是linux内核
其内部还是有非常多的编码精髓值得我们去学习
未来仍旧可期



觉得看完有帮助的请一键三连吧&#xff01;
您的支持将是我继续下去的动力






推荐阅读
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
author-avatar
mobiledu2502907897
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有