DSP

Android Audio机制

2019-07-13 21:01发布



前言

这篇文章是最近自己学习android audio的学习心得,希望大牛提出宝贵意见。
本文内容基于android 5.0

目录

一. 硬件架构

(1).编解码器(codec)

二. 软件架构

(1). kernel里面关于Audio的driver机制 (2). Audio机制服务的核心AudioflingerAudioPolicyService启动流程 (3). AudioService在SystemServer里面的启动 (4). 应用程序接口MediaplayerAudioTrack和AudioRecoder。

一,硬件架构

audio硬件架构

1,上图所示为手机上audio的硬件架构

(1).其中PM8916里面主要是我们的hardware codec他的作用主要是将 模拟的音频信号采样转换为pcm格式 (2).MODEM DSP是我们的modem部分,可以看到我们采集到的PCM 数据可以通过modem的DSP处理,没有注意这里没有给我们的 audio单独集成DSP可以节省成本 (3).application processor是我们的主处理器目前手机上面主要 还是arm 总结: 架构图所示,我们的Audio数据主要通过I2s接口如图Audio Interface 和Digital codec core之间传输,而我们的命令通过spmi接口传输到hardware codec,注意这里我们只是一本图作为例子实际中不同的厂商可能使用不同的接口,这个主要在driver里面与硬件相关的部分,例如有些厂商接口是通过i2c或者spi接口

二,软件架构

audio的软件架构

软件架构模块:

(1).ALSA是kernel里面管理audio的核心,我们的audio driver 部分一般会调用snd_soc_register_card(), 将我们在软件层面抽象出来的声卡注册进audio核心 (2).在用户空间android同样给我们提供了tinyalsa方便 我们操作我们的driver (3).audio的hal层,这部分也是和具体的厂商有关,后面我会具体讲 (4).mediaservice,图中的media是在init.rc里面启动的native进程 在我们所用的android版本中这个service其实还启动了camera service,这里只讲解与audio相关的AudioFlinger和 AudioPolicyService (5).在mediaservice启动之后会zygote进程,zygote紧接着会启动 android的核心进程systemserver进程。在systemserver进程 里面会注册AudioService,其实这里AudioService虽然叫做 service,其实他是通过调用AudioSystem间接地获得 AudioFlinger的代理到具体的audio,之所以service是由于 我们一般在应用程序中又是通过获得AudioService的代理 操作的,可见虽然android的binder进程间通讯功能强大 但是效率可能并没有其他的进程间通讯机制高效。 比如我们一般在应用程序中通过AudioManager操作得到当前 的音量会首先通过binder机制先获得AudioService代理,然后 AudioService在通过AudioSystem获得AudioFlinger的代理才 能操作到实际的实现部分会发现这里一次调用却用了两次 binder通信,显然没有普通的进程间通信机制高效,优点确实功能很强大。 (6).应用程序中有AudioTrack,AudioRecoder,AudioManager, MediaPlayer等接口实际操作我们的audio的。

几个模块的单独分析

  1. ALSA kernel部分 代码位置:
    kernel/sound/sound_core.c(这部分暂时没有看完)
    kernel/sound/soc/soc-core.c(我们主要分析这里)
    kernel/sound/soc/soc-pcm.c
    kernel/sound/soc/soc-compress.c
    kernel/sound/core/pcm_lib.c
    kernel/sound/core/pcm_native.c
    1.soc-core.c里面注册了一个platform driver,这份code是与具体的硬件无关的主要是在内存里面建立几个重要的链表维护我们注册的关于audio的driver
    2.soc-pcm.c,soc-compress.c,pcm_lib.c,pcm_native.c等这些
    文件里面主要实现对于用户具体实现audio pcm读写操作
    注册框架(实际的注册有具体的driver实现,这个里面只是管理)。
    3.我们在这幅框架图里面看到的platform driver,cpu driver
    ,codec driver,machine driver里面才audio的具体实现操作
    (不同的厂商实现不一样的框架是一样的)
    4.在这幅图里能看到音量按键volumekey driver,这个是我自己假设
    出来的。实际中由于volume key 功能比较小,可能会和其他
    的driver写在一起,我们暂且这样理解,从图中,我们能看到当我们
    按下volume key时我们的按键事件会通过input子系统传到
    InputManagerService,进而传送到PhoneWindowManager。这个里面
    PhoneWindowManagerService和AudioService 都在
    systemserver进程里面,PhoneWindowManager又会通过binder得到
    AudioService的代理进而按照之前说的又往下实现真正的音量设置。
    我们发现这里我们会发现binder不仅可以用于进程间通信,即使是同一个进程中,只只要将service注册进servicemanager,也可以
    通过binder通信
    2.tinyalsa 代码位置:
    1.external/tinyalsa/
    这个目录底下主要的两个文件分别是pcm.c,mixer.c
    主要是我们pcm音频的播放和录制以及混音接口
    2.external/tinycompress/
    这个目录底下重要的一个文件compress.c
    主要是对于压缩格式的音频文件的播放。比如MP3,AAC。当然这个得硬件codec支持这种格式的解码才可以(硬解码)我们还会提到软解码
    3.Audio HAL
    这一层主要是调用tinyalsa以及一些厂商相关的具体实现。与具体的厂商有关所以我们也是略过。
4.mediaservice
1,代码在framework/av/media/mediaservice/main_mediaservice.c
在init进程中会解析init.rc 之后在on boot有一个
class_start main 在这个期间会启动mediaserver进程是我摘抄的
init.rc中的一段
service media /system/bin/mediaserver
class main
user media
。。。
进程启动了mediaservice进程,下面我们来看mediaservice的代码

块代码

```mediaservice int main(int argc __unused, char** argv) { signal(SIGPIPE, SIG_IGN); char value[PROPERTY_VALUE_MAX]; bool doLog = (property_get("ro.test_harness", value, "0") > 0) && (atoi(value) == 1); pid_t childPid; // FIXME The advantage of making the process containing media.log service the parent process of // the process that contains all the other real 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 hack strcpy(argv[0], "media.log"); sp proc(ProcessState::self()); MediaLogService::instantiate(); ProcessState::self()->startThreadPool(); 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 sm = defaultServiceManager(); sp binder = sm->getService(String16("media.log")); if (binder != 0) { Vector 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 services if (doLog) { prctl(PR_SET_PDEATHSIG, SIGKILL); // if parent media.log dies before me, kill me also setpgid(0, 0); // but if I die first, don't kill my parent } sp proc(ProcessState::self()); sp sm = defaultServiceManager(); ALOGI("ServiceManager: %p", sm.get()); AudioFlinger::instantiate(); MediaPlayerService::instantiate(); CameraService::instantiate(); #ifdef AUDIO_LISTEN_ENABLED ALOGI("ListenService instantiated"); ListenService::instantiate(); #endif AudioPolicyService::instantiate(); SoundTriggerHwService::instantiate(); registerExtensions(); ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); } } ``` 我们可以看到这个里面注册了MediaPlayerService,CameraService, 还有与我们audio相关的AudioFlinge和AudioPolicyService:
AudioFlinger::instantiate();
AudioPolicyService::instantiate();
之后下面两句等待binder进程间通信,是一个死循环,如果没有通信请求就休眠
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
这个里面关键的一句是AudioPolicyService::instantiate();
instantiate()和其他的没有区别就是创建service并且注册进
servicemanager。如果不懂先暂且这样理解,可以看我之后关于
binder进程间通信的博文,主要的是AudioPolicyService的
onFirstRef()函数,里面会创建AudioPolicyManager
而AudioPolicyManager里面又会加载我们的Hal层
(通过AudioPolicyClient的loadHwModule)AudioPolicyClient的
loadHwModule()又是通过AudioFlinger的loadHwModule()加载hal层
绕了这么一大圈。我们接下来分析AudioFlinger的loadHwModule()

AudioPolicyManager

``` AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface) 。。。 { 。。。 for (size_t i = 0; i < mHwModules.size(); i++) { //这里通过binder间接调用AudioFlinger的loadHwModule mHwModules[i]->mHandle = mpClientInterface->loadHwModule(mHwModules[i]->mName); if (mHwModules[i]->mHandle == 0) { ALOGW("could not open HW module %s", mHwModules[i]->mName); continue; } for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) { 。。。。 。。。。 //这里类似于loadHwModule调用AudioFinger //的openOutput status_t status = mpClientInterface->openOutput(outProfile->mModule->mHandle, &output, &config, &outputDesc->mDevice, String8(""), &outputDesc->mLatency, outputDesc->mFlags); 。。。。 。。。。 } for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++) { 。。。。 。。。。 //这里类似于loadHwModule调用AudioFinger //的openIntput status_t status = mpClientInterface->openInput(inProfile->mModule->mHandle, &input, &config, &inputDesc->mDevice, String8(""), AUDIO_SOURCE_MIC, AUDIO_INPUT_FLAG_NONE); 。。。。 。。。。 } } } ```

LoadHwModule

``` audio_module_handle_t AudioFlinger::loadHwModule(const char *name) { if (name == NULL) { return 0; } if (!settingsAllowed()) { return 0; } Mutex::Autolock _l(mLock); return loadHwModule_l(name); } // loadHwModule_l() must be called with AudioFlinger::mLock held audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name) { for (size_t i = 0; i < mAudioHwDevs.size(); i++) { if (strncmp(mAudioHwDevs.valueAt(i)->moduleName(), name, strlen(name)) == 0) { ALOGW("loadHwModule() module %s already loaded", name); return mAudioHwDevs.keyAt(i); } } audio_hw_device_t *dev; //这里会加载层 int rc = load_audio_interface(name, &dev); if (rc) { ALOGI("loadHwModule() error %d loading module %s ", rc, name); return 0; } 。。。。 。。。。 //将我们加载hal层之后创建的AudioHwDevice添加进mAudioHwDevs mAudioHwDevs.add(handle, new AudioHwDevice(handle,name,dev,flags)) return handle; } ``` 后面openInput与openOutput与加载hal层类似fenbie会创建
RecoderThread和PlaybackThread并分别添加进RecoderThreads和
mPlaybackThreads供之后应用使用
需要注意的是PlaybackThread有三个子类offloadThread,
MixerThread和DirectOutputThread。mPlaybackThreads里面add的就是这三种线程。应用程序会根据实际选择这些从里面查找使用哪一种thread播放。

AudioFlinger类视图

只列出刚才讲的thread和AudioHwDevice 这幅图我们只是将刚才讲的mAudioHwDevs,mPlaybackThreads
和mRecoderThread 到这里我们的mediaservice结束,Audio也已经完成
5.systemserver注册AudioService
一个java层的service虽然叫做service实质上是通过AudioSystem取得
AudioFlinger的binder代理让AudioFlinger来实现实际功能的。

systemserver之startOtherService()

```startOtherService private void startOtherServices() { 。。。。 。。。。 if (!disableMedia && !"0".equals(SystemProperties.get("system_init.startaudioservice"))) { try { Slog.i(TAG, "Audio Service"); //创建AudioService并且注册进servicemanager之后可以通//过binder通信调用 audioService = new AudioService(context); ServiceManager.addService(Context.AUDIO_SERVICE, audioService); } catch (Throwable e) { reportWtf("starting Audio Service", e); } } 。。。。 。。。。 } ``` 至此我们的Audio全部初始化完毕。
6.应用程序
应用程序中我们通常可以使用MediaPlayer,AudioManager,AudioTrack
和AudioRecoder。
如果我们的硬件上面的codec比较低端那么我们就不能使用硬件解码,只能通过软件解码。应用程序中通常使用MediaPlayer我在软件框架图中所画,先通过libstagefright软件解码,然后才会使用AudioTrack播放。当然如果有一种格式libstagefright也不支持,那么一般是先使用第三方库
解码之后再去播放。
AudioManager在应用程序中一般是需要权限的,他是通过binder使用
AudioService的来实现的
AudioRecode与AudioTrack类似只不过用于录音。
具体怎么实现逻辑关系图请看上面的软件架构图。由于篇幅原因这里就不详细讨论了。
其他的SDK接口这里就不讨论了 最后欢迎大神批评指正