Android音频框架之一 详解audioPolicy流程及HAL驱动加载与配置

一、 AndroidRuntime.so 引发思考

android 系统 framework 代码起点, frameworks/base/core/jni/AndroidRuntime.cpp 文件,
此文件是android系统主线程代码,代码内容涉及系统很多模块,此程序主要是注册模块的JNI接口方法。其中涉及到模块 native、sensorHal、media、audioflinger、displayflinger、camera、serialport、binder等,
从各模块名称上可看出是 android 系统核心组件内容,由此可见 AndroidRuntime 是系统框架的入口。
启动注册 audioRecord、audioSystem、audioTrack 模块,此模块注册是在 mediaService 模块启动之前。

defaultServiceManager 定义在 frameworks\native\libs\binder\IServiceManager.cpp

sp<IServiceManager> defaultServiceManager()
{std::call_once(gSmOnce, []() {sp<AidlServiceManager> sm = nullptr;while (sm == nullptr) {sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));if (sm == nullptr) {ALOGE("Waiting 1s on context object on %s.", ProcessState::self()->getDriverName().c_str());sleep(1);}}gDefaultServiceManager = sp<ServiceManagerShim>::make(sm);});return gDefaultServiceManager;
}
sp<IServiceManager> sm(defaultServiceManager());

安卓源码中大量使用 sm 对象来管理系统的各种服务,添加、释放服务。用户 app 通过反射的方式获取系统的各种服务,
由此可以对安卓系统服务概念理解会更加深刻。

Binder学习之获取ServiceManager对象_sp<iservicemanager> sm = defaultservicemanager();-CSDN博客

二、 Android 音频框架

Android音频系统有两大服务:一是AudioFlinger,二是AudioPolicyService。 AudioFlinger负责向下 访问AudioHardwareInterface,
实现音频PCM数据的混音/输入/输出,实现音量调节;
AudioPolicyService负责音 频输入输出设备的连接状态,音频策略调度即音频设备(如本地CODEC、Bluetooth A2DP、Headset)的切换
策略(注意它只是负责策略,真正的切换操作是在AudioFlinger中的openOutput,毕竟 AudioFlinger负责操作底层音频硬件)。

2.0> mediaserver 和 audioserver 本地服务

在 android 系统初始化 frameworks\av\media\mediaserver\mediaserver.rc 中,启动 mediaserver 服务, 线程名称: /system/bin/mediaserver ,启动多媒体服务。

on property:init.svc.media=*setprop init.svc.mediadrm ${init.svc.media}service media /system/bin/mediaserverclass mainuser mediagroup audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrmioprio rt 4task_profiles ProcessCapacityHigh HighPerformance

frameworks/av/media/mediaserver/main_mediaserver.cpp 是入口函数, 

int main(int argc __unused, char **argv __unused)
{signal(SIGPIPE, SIG_IGN);sp<ProcessState> proc(ProcessState::self());sp<IServiceManager> sm(defaultServiceManager());ALOGI("ServiceManager: %p", sm.get());MediaPlayerService::instantiate();ResourceManagerService::instantiate();registerExtensions();::android::hardware::configureRpcThreadpool(16, false);ProcessState::self()->startThreadPool();IPCThreadState::self()->joinThreadPool();::android::hardware::joinRpcThreadpool();
}

 

system/core/rootdir/init.zygote32.rc 中启动 audioserver 服务,内容如下:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-serverclass mainsocket zygote stream 660 root systemonrestart write /sys/android_power/request_state wakeonrestart write /sys/power/state ononrestart restart audioserver          ## 启动 audioserver 服务onrestart restart cameraserver         ## 启动 cameraserver 服务onrestart restart mediaonrestart restart netdwritepid /dev/cpuset/foreground/tasks

frameworks/av/media/audioserver/main_audioserver.cpp 此程序是入口函数 

int main(int argc __unused, char **argv)
{// TODO: update with refined parameterslimitProcessMemory("audio.maxmem", /* "ro.audio.maxmem", property that defines limit */(size_t)512 * (1 << 20), /* SIZE_MAX, upper limit in bytes */20 /* upper limit as percentage of physical RAM */);signal(SIGPIPE, SIG_IGN);#if 1// FIXME See bug 165702394 and bug 168511485const bool doLog = false;
#elsebool doLog = (bool) property_get_bool("ro.test_harness", 0);
#endifpid_t childPid;// FIXME The advantage of making the process containing media.log service the parent process of// the process that contains the other audio services, is that it allows us to collect more// detailed information such as signal numbers, stop and continue, resource usage, etc.// But it is also more complex.  Consider replacing this by independent processes, and using// binder on death notification instead.if (doLog && (childPid = fork()) != 0) {// media.log service//prctl(PR_SET_NAME, (unsigned long) "media.log", 0, 0, 0);// unfortunately ps ignores PR_SET_NAME for the main thread, so use this ugly hackstrcpy(argv[0], "media.log");sp<ProcessState> proc(ProcessState::self());MediaLogService::instantiate();ProcessState::self()->startThreadPool();IPCThreadState::self()->joinThreadPool();for (;;) {siginfo_t info;int ret = waitid(P_PID, childPid, &info, WEXITED | WSTOPPED | WCONTINUED);if (ret == EINTR) {continue;}if (ret < 0) {break;}char buffer[32];const char *code;switch (info.si_code) {case CLD_EXITED:code = "CLD_EXITED";break;case CLD_KILLED:code = "CLD_KILLED";break;case CLD_DUMPED:code = "CLD_DUMPED";break;case CLD_STOPPED:code = "CLD_STOPPED";break;case CLD_TRAPPED:code = "CLD_TRAPPED";break;case CLD_CONTINUED:code = "CLD_CONTINUED";break;default:snprintf(buffer, sizeof(buffer), "unknown (%d)", info.si_code);code = buffer;break;}struct rusage usage;getrusage(RUSAGE_CHILDREN, &usage);ALOG(LOG_ERROR, "media.log", "pid %d status %d code %s user %ld.%03lds sys %ld.%03lds",info.si_pid, info.si_status, code,usage.ru_utime.tv_sec, usage.ru_utime.tv_usec / 1000,usage.ru_stime.tv_sec, usage.ru_stime.tv_usec / 1000);sp<IServiceManager> sm = defaultServiceManager();sp<IBinder> binder = sm->getService(String16("media.log"));if (binder != 0) {Vector<String16> args;binder->dump(-1, args);}switch (info.si_code) {case CLD_EXITED:case CLD_KILLED:case CLD_DUMPED: {ALOG(LOG_INFO, "media.log", "exiting");_exit(0);// not reached}default:break;}}} else {// all other servicesif (doLog) {prctl(PR_SET_PDEATHSIG, SIGKILL);   // if parent media.log dies before me, kill me alsosetpgid(0, 0);                      // but if I die first, don't kill my parent}android::hardware::configureRpcThreadpool(4, false /*callerWillJoin*/);sp<ProcessState> proc(ProcessState::self());sp<IServiceManager> sm = defaultServiceManager();ALOGI("ServiceManager: %p", sm.get());AudioFlinger::instantiate();AudioPolicyService::instantiate();// 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.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();}ProcessState::self()->startThreadPool();IPCThreadState::self()->joinThreadPool();}
}

此线程启动服务有:AudioFlinger、AudioPolicyService、RadioService、SoundTriggerHwService 服务;其中 RadioService 是电话服务,不是本次讨论内容略过。

2.1> audioflinger 本地混音管理框架

AudioFlinger(下面简称AF)是整个音频系统的核心与难点。作为 Android 系统中的音频中枢,它同时也是一个系统服务,启到承上(为上层提供访问接口)启下(通过HAL来管理音频设备)的作用。 AudioFlinger 向下访问 AudioHardware,实现输出音频数据,控制音频参数。
首先看 AudioFlinger 的继承关系及父类内容,在 audioserver 函数中调用 AudioFlinger::instantiate() 函数.

void AudioFlinger::instantiate() {sp<IServiceManager> sm(defaultServiceManager());sm->addService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME),new AudioFlingerServerAdapter(new AudioFlinger()), false,IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
}

在 系统服务中添加 audioService 服务内容,供用户通过服务的方式使用音频设备

AudioFlinger 构造函数内容如下:

AudioFlinger::AudioFlinger(): mMediaLogNotifier(new AudioFlinger::MediaLogNotifier()),mPrimaryHardwareDev(NULL),mAudioHwDevs(NULL),mHardwareStatus(AUDIO_HW_IDLE),mMasterVolume(1.0f),mMasterMute(false),// mNextUniqueId(AUDIO_UNIQUE_ID_USE_MAX),mMode(AUDIO_MODE_INVALID),mBtNrecIsOff(false),mIsLowRamDevice(true),mIsDeviceTypeKnown(false),mTotalMemory(0),mClientSharedHeapSize(kMinimumClientSharedHeapSizeBytes),mGlobalEffectEnableTime(0),mPatchPanel(this),mDeviceEffectManager(this),mSystemReady(false)
{// Move the audio session unique ID generator start base as time passes to limit risk of// generating the same ID again after an audioserver restart.// This is important because clients will reuse previously allocated audio session IDs// when reconnecting after an audioserver restart and newly allocated IDs may conflict with// active clients.// Moving the base by 1 for each elapsed second is a good compromise between avoiding overlap// between allocation ranges and not reaching wrap around too soon.timespec ts{};clock_gettime(CLOCK_MONOTONIC, &ts);// zero ID has a special meaning, so start allocation at least at AUDIO_UNIQUE_ID_USE_MAXuint32_t movingBase = (uint32_t)std::max((long)1, ts.tv_sec);// unsigned instead of audio_unique_id_use_t, because ++ operator is unavailable for enumfor (unsigned use = AUDIO_UNIQUE_ID_USE_UNSPECIFIED; use < AUDIO_UNIQUE_ID_USE_MAX; use++) {mNextUniqueIds[use] =((use == AUDIO_UNIQUE_ID_USE_SESSION || use == AUDIO_UNIQUE_ID_USE_CLIENT) ?movingBase : 1) * AUDIO_UNIQUE_ID_USE_MAX;}#if 1// FIXME See bug 165702394 and bug 168511485const bool doLog = false;
#elseconst bool doLog = property_get_bool("ro.test_harness", false);
#endifif (doLog) {mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters",MemoryHeapBase::READ_ONLY);(void) pthread_once(&sMediaLogOnce, sMediaLogInit);}// reset battery stats.// if the audio service has crashed, battery stats could be left// in bad state, reset the state upon service start.BatteryNotifier::getInstance().noteResetAudio();mDevicesFactoryHal = DevicesFactoryHalInterface::create();mEffectsFactoryHal = EffectsFactoryHalInterface::create();mMediaLogNotifier->run("MediaLogNotifier");std::vector<pid_t> halPids;mDevicesFactoryHal->getHalPids(&halPids);TimeCheck::setAudioHalPids(halPids);// Notify that we have started (also called when audioserver service restarts)mediametrics::LogItem(mMetricsId).set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR).record();
}

 C++ 中智能指针创建对象时,第一次创建会调用onFirstRef()函数,在本例中将调用 AudioFlinger::onFirstRef() 函数,
内容如下:frameworks/av/services/audioflinger/audioFlinger.cpp

void AudioFlinger::onFirstRef()
{Mutex::Autolock _l(mLock);/* TODO: move all this work into an Init() function */char val_str[PROPERTY_VALUE_MAX] = { 0 };if (property_get("ro.audio.flinger_standbytime_ms", val_str, NULL) >= 0) {uint32_t int_val;if (1 == sscanf(val_str, "%u", &int_val)) {mStandbyTimeInNsecs = milliseconds(int_val);ALOGI("Using %u mSec as standby time.", int_val);} else {mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs;ALOGI("Using default %u mSec as standby time.",(uint32_t)(mStandbyTimeInNsecs / 1000000));}}mMode = AUDIO_MODE_NORMAL;gAudioFlinger = this;  // we are already refcounted, store into atomic pointer.mDevicesFactoryHalCallback = new DevicesFactoryHalCallbackImpl;mDevicesFactoryHal->setCallbackOnce(mDevicesFactoryHalCallback);
}

 至此 audioFlinger 对象构建完成

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/342636.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用pandas按照商品和下单人统计下单数据

目录 一&#xff1a;需求描述 二&#xff1a;代码实现 三&#xff1a;注意事项 一&#xff1a;需求描述 最近运营那边给到一个excel表格&#xff0c;是一个小程序用户的下单数据&#xff0c;要以商品为维度&#xff0c;统计用户下单情况&#xff0c;主要是下单的商品总金额&…

两周掌握Vue3(四):计算属性、监听属性、事件处理

文章目录 一、计算属性1.什么是计算属性2.代码示例 二、监听属性三、事件处理 代码仓库&#xff1a;跳转 当前分支&#xff1a;04 一、计算属性 1.什么是计算属性 Vue 中的计算属性具有以下作用&#xff1a; 数据处理&#xff1a;计算属性可以用于对数据进行处理和计算&…

开源笔记工具AFFiNE本地部署并结合内网穿透

前言 本篇文章讲解Notion开源平替全能知识库工具AFFINE如何本地部署&#xff0c;并实现公网远程访问。AFFiNE 是一个全新的开源项目&#xff0c;旨在克服 Notion 和 Miro 在安全和隐私方面的一些局限性。它的设计目标是帮助用户将会议记录、待办事项、文档中的目标、视频会议白…

如何运用自养号测评策略在Lazada、Shopee上轻松提升销售和排名

卖家们常常会为Lazada、Shopee店铺销量不佳而感到困惑。然而&#xff0c;仅仅感叹并不能解决问题。作为卖家&#xff0c;我们需要深入分析问题&#xff0c;并采取有效的措施来解决它们。基本功是提升销量的基石&#xff0c;但仅仅依靠基本功是不够的。我们需要将运营和测评结合…

【Java】正则表达式( Pattern 和 Matcher 类)

快速入门 Java 提供了 java.util.regex 包&#xff0c;它包含了 Pattern 和 Matcher 类&#xff0c;用于处理正则表达式的匹配操作。 java.util.regex 包主要包括以下三个类&#xff1a; Pattern 类&#xff1a; pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共…

程序员必知!组合模式的实战应用与案例分析

组合模式是一种设计模式&#xff0c;允许将对象组合成树形结构并像单个对象一样使用它们&#xff0c;这种模式在处理类似公司组织结构这样的树形数据时非常有用&#xff0c;通过组合模式&#xff0c;我们可以将公司和部门视为同一类型的对象&#xff0c;从而以统一的方式处理发…

SV-9001 壁挂式网络采播终端

SV-9001 壁挂式网络采播终端 一、描述 SV-9001是深圳锐科达电子有限公司的一款壁挂式网络采播终端&#xff0c;具有10/100M以太网接口&#xff0c;配置一路线路输入和一组麦克风输入&#xff0c;可以直接连接音源输出设备或麦克风&#xff0c;将采集音源编码后发送至网络播放终…

仲晶同志简历

女&#xff0c;汉族。1972年出生&#xff0c;国防大学科技与装备教研室教官。1992年&#xff0c;仲晶毕业于军事气象学院&#xff0c;1996年成为国防大学国防科技发展战略学硕士研究生&#xff0c;毕业后留校任教。曾出版过9部军事专著&#xff0c;先后发表学术论文100多万字。…

CANoe中最常见的文件类型

文件类型图标说明文件说明保存步骤附加说明example.cfg此文件相当于一个集成的可执行文件&#xff0c;双击该图标就能打开工程Flie-》savepanel.xvp是vxp&#xff0c;控制盘文件home界面。打开panel 图标。在专门编辑界面保存。trace界面 导出的报文 报文存在多种格式&#xff…

Vue3+Vite项目搭建

为什么选择vite而不是vue-cli&#xff1a; vite下一代前端开发与构建工具 vite创建的项目默认vue3 优势&#xff1a; 开发环境中&#xff0c;无需打包&#xff0c;可快速的冷启动 轻量快速的热重载&#xff08;HMR&#xff09; 真正的按需编译&#xff0c;不在等待整个应用…

Elasticsearch:是时候离开了! - 在 Elasticsearch 文档上使用 TTL

作者&#xff1a;来自 Elastic David Pilato 想象一下&#xff0c;圣诞老人必须向世界上所有的孩子们分发礼物。 他有很多工作要做&#xff0c;他需要保持高效。 他有一份所有孩子的名单&#xff0c;并且知道他们住在哪里。 他很可能会将礼物按区域分组&#xff0c;然后再交付。…

递增的三元子序列

题目链接 递增的三元子序列 题目描述 注意点 1 < nums.length < 500000子序列的三个元素在原数组中可以不是连续的实现时间复杂度为 O(n) &#xff0c;空间复杂度为 O(1) 的解决方案 解答思路 使用贪心算法&#xff0c;在遍历数组时&#xff0c;要记录当前数组中的最…