转载请注明出处:http://blog.csdn.net/adits/article/details/8242146
开发环境简介
1. 主机系统: Unbuntu10.10
2. android系统版本: 4.0.3(Linux kernel 3.0.8)
综述
android的音频系统非常庞大复杂:涉及到java应用程序,java框架层,JNI,本地服务(AudioFlinger和AudioPolicyService),硬件抽象层HAL,ALSA-LIB和ALSA-DRIVER。
本文将先分析音频系统的启动与模块加载流程,并具体分析一个JAVA API的调用流程;最后在此基础上自然地为android系统添加USB AUDIO设备的放音和录音功能。
全文可分为如下几大部分:
1. 本地服务的启动流程分析。
1.1 AudioFlinger启动流程及其所涉及的HAL层模块启动流程分析。
1.2 AudioPolicyService启动流程及其所涉及的HAL层模块启动流程分析。
2. JAVA API setDeviceConnectionState()调用流程详解,同时为android系统添加USB AUDIO设备的放音和录音功能。
3. ALSA-LIB浅述以及asound.conf配置文件的书写。
4. 重新获取USB AUDIO设备的硬件参数。
详述
1. 本地服务的启动流程分析。
AudioFlinger和AudioPolicyService两大音频服务都是在android系统启动时就启动的。
当linux kenerl启动完成后,会启动Android的init进程(system/core/init/init.c)。
- <span style="font-size:24px;">int main(int argc, char **argv)
- {
- .....
-
- init_parse_config_file("/<span style="color:#ff0000;">init.rc</span>");
-
- .....
- }</span>
init.rc文件中保存了许多系统启动时需要启动的服务。其中就有多媒体服务mediaserver的启动:
- <span style="font-size:24px;">service media /system/bin/mediaserver</span>
此服务在文件frameworks/base/media/mediaserver/main_mediaserver.cpp中定义,而音频子系统的两大本地服务AudioFlinger和AudioPolicyService就是在此启动的。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">int main(int argc, char** argv)
- {
- .....
-
- <span style="color:#ff0000;">AudioFlinger::instantiate()</span>; <span style="color:#3333ff;">
- .....
-
- <span style="color:#ff0000;">AudioPolicyService::instantiate()</span>; <span style="color:#3333ff;">
-
- .....
- }</span>
1.1 AudioFlinger启动流程及其所涉及的HAL层模块启动流程分析。
根据上文分析,将调用AudioFlinger::instantiate()函数实例化AudioFlinger。但是AudioFlinger.cpp中并没有找到此函数,那必然在其父类中。AudioFlinger类有很多父类,一时难以确定instantiate()到底在哪个父类中定义的。直接搜索吧!
grep -rn "instantiate" frameworks/base/
很快找到instantiate()函数的定义处在./frameworks/base/include/binder/BinderService.h头文件中
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">template<typename SERVICE>
- class BinderService
- {
- public:
- static status_t publish() {
- sp<IServiceManager> sm(defaultServiceManager());
- return sm->addService(String16(<span style="color:#ff0000;">SERVICE::getServiceName</span>()), <span style="color:#ff0000;">new SERVICE</span>());
-
- ......
-
- static void instantiate() { publish(); }
-
- .....
- }
- }</span>
这里用到了模板,需要确定SERVICE是什么东东。
AudioFlinger类是在AudioFlinger.h中定义的,而恰好包含了头文件BinderService.h。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">class AudioFlinger :
- public <span style="color:#ff0000;">BinderService<AudioFlinger></span>,
- public BnAudioFlinger
- {
- friend class BinderService<AudioFlinger>;
- public:
- static char const* getServiceName() { return "media.audio_flinger"; }
-
- .....
-
- }</span>
原来AudioFlinger类继承了BinderService类,同时把自己(AudioFlinger)传递给SERVICE。而addService函数第一个参数调用了AudioFlinger类的静态成员函数getServiceName()获取AudioFlinger的服务名称;其第二个参数便是创建了一个AudioFlinger的实例。至此,明白了实例化函数instantiate()就是要向服务管理器注册的服务是AudioFlinger。
既然此时实例化了AudioFlinger,那么看看AudioFlinger类的构造函数具体做了哪些初始化工作。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">AudioFlinger::AudioFlinger()
- : BnAudioFlinger(),
- mPrimaryHardwareDev(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1),
- mBtNrecIsOff(false)
- {
- }</span>
此构造函数做了一些无关紧要的事情,不管它。既然AudioFlinger服务是第一次启动,则将调到函数AudioFlinger::onFirstRef(至于为什么,我还没有搞明白,可以通过log信息确信确实是这么回事)。
void AudioFlinger::onFirstRef()
{
......
for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) {
const hw_module_t *mod;
audio_hw_device_t *dev;
rc = load_audio_interface(audio_interfaces[i], &mod,&dev);
.....
mAudioHwDevs.push(dev); // 把通过load_audio_interface()函数获得的设备存入元素为audio_hw_device_t
// 类型的模板变量mAudioHwDevs中
.....
}
看到load_audio_interface()函数的名字,知晓,应当是加载音频接口的。
- <span style="font-size:24px;">static int load_audio_interface(const char *if_name, const hw_module_t **mod,
- audio_hw_device_t **dev)
- {
- ......
-
- rc = <span style="color:#ff0000;">hw_get_module_by_class</span>(<span style="color:#ff0000;">AUDIO_HARDWARE_MODULE_ID</span>, if_name, mod);
- if (rc)
- goto out;
-
- rc = <span style="color:#ff0000;">audio_hw_device_open</span>(*mod, <span style="color:#ff0000;">dev</span>);
-
- .....
-
- }</span>
首先通过函数hw_get_module_by_class获取ID号为AUDIO_HARDWARE_MODULE_ID的音频模块,此ID在头文件hardware/libhardware/include/hardware/audio.h中定义。此头文件中定义了一个十分重要的结构体struct audio_hw_device,其中包含了许多函数接口(函数指针):
- <span style="font-size:24px;">struct audio_hw_device {
- struct hw_device_t common;
-
-
-
-
-
-
-
- uint32_t (*<span style="color:#ff0000;">get_supported_devices</span>)(const struct audio_hw_device *dev);
-
-
-
-
-
- int (*<span style="color:#ff0000;">init_check</span>)(const struct audio_hw_device *dev);
-
- ......
-
-
- int (*<span style="color:#ff0000;">set_parameters</span>)(struct audio_hw_device *dev, const char *kv_pairs);
-
- .....
-
-
- int (*<span style="color:#ff0000;">open_output_stream</span>)(struct audio_hw_device *dev, uint32_t devices,
- int *format, uint32_t *channels,
- uint32_t *sample_rate,
- struct audio_stream_out **out);
-
- ......
-
-
- int (*open_input_stream)(struct audio_hw_device *dev, uint32_t devices,
- int *format, uint32_t *channels,
- uint32_t *sample_rate,
- audio_in_acoustics_t acoustics,
- struct audio_stream_in **stream_in);
-
- .....
- }</span>
ID为AUDIO_HARDWARE_MODULE_ID的音频模块到底在哪儿定义的那?既然是HAL层模块,必定在hardware目录下定义的
- <span style="font-size:24px;">$ grep -rn AUDIO_HARDWARE_MODULE_ID hardware/
- hardware/libhardware_legacy/audio/audio_hw_hal.cpp:602:id: AUDIO_HARDWARE_MODULE_ID,
- hardware/libhardware/modules/audio/audio_hw.c:435: .id = AUDIO_HARDWARE_MODULE_ID,
- hardware/libhardware/include/hardware/audio.h:37:
- #define AUDIO_HARDWARE_MODULE_ID "audio"</span>
从搜索结果发现,AUDIO_HARDWARE_MODULE_ID的音频模块有两处定义,具体用的是哪一个那?
先分析下这两个模块最终编译进哪些模块。
通过查阅Android.mk晓得,audio_hw.c先被编译进audio_policy.stub模块,而后被编译进libhardware模块;
同样的,audio_hw_hal.cpp先被编译进libaudiopolicy_legacy模块,而后被编译进libhardware_legacy模块;
而libhardware和libhardware_legacy模块都在audioFlinger中用到。
通过log信息,确认使用的是libaudiopolicy_legacy模块,即具体调到hardware/libhardware_legacy/audio/audio_hw_hal.cpp文件中所定义的模块了。
在获取到HAL层音频模块后,接下来执行audio_hw_device_open()函数,打开设备。此函数也在audio.h头文件中定义,函数体如下:
- <span style="font-size:24px;">
-
- static inline int audio_hw_device_open(const struct hw_module_t* module,
- struct audio_hw_device** device)
- {
- return module->methods->open(module, AUDIO_HARDWARE_INTERFACE,
- (struct hw_device_t**)device);
- }</span>
struct hw_module_t是在hardware/libhardware/include/hardware/hardware.h头文件中定义的,其中嵌套了struct hw_module_methods_t。此结构体很简单,只有一个函数指针open
- <span style="font-size:24px;">typedef struct hw_module_methods_t {
-
- int (*open)(const struct hw_module_t* module, const char* id,
- struct hw_device_t** device);
-
- } hw_module_methods_t;</span>
在确定具体模块后,很容易确定open函数指针的具体实现
- <span style="font-size:24px;">struct legacy_audio_module HAL_MODULE_INFO_SYM = {
- module: {
- common: {
- tag: HARDWARE_MODULE_TAG,
- version_major: 1,
- version_minor: 0,
- id: AUDIO_HARDWARE_MODULE_ID,
- name: "LEGACY Audio HW HAL",
- author: "The Android Open Source Project",
- methods: &<span style="color:#ff0000;">legacy_audio_module_methods</span>,
- dso : NULL,
- reserved : {0},
- },
- },
- };</span>
- <span style="font-size:24px;">static struct hw_module_methods_t legacy_audio_module_methods = {
- open: <span style="color:#ff0000;">legacy_adev_open</span>
- };</span>
open()的实现就是legacy_adev_open()函数了!
- <span style="font-size:24px;">static int legacy_adev_open(const hw_module_t* module, const char* name,
- hw_device_t** device)
- {
- struct legacy_audio_device *ladev;
- int ret;
-
- if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
- return -EINVAL;
-
- .....
-
- ladev->device.get_supported_devices = adev_get_supported_devices;
- ladev->device.init_check = <span style="color:#ff0000;">adev_init_check</span>;
-
- .....
-
- ladev->device.open_output_stream = <span style="color:#ff0000;">adev_open_output_stream</span>;
- ladev->device.close_output_stream = adev_close_output_stream;
- ladev->device.open_input_stream = adev_open_input_stream;
- ladev->device.close_input_stream = adev_close_input_stream;
-
- .....
-
- ladev->hwif = <span style="color:#ff0000;">createAudioHardware</span>();
-
- .....
- <span style="color:#ff0000;">*device = &ladev->device.common;</span> <span style="color:#3333ff;">
-
-
- </span>
- return 0;
- }</span>
这里主要做了一些初始化工作,即给函数指针提供具体实现函数;但createAudioHardware()应该做了更多的事情。
先从函数createAudioHardware()的返回值入手。struct legacy_audio_device的定义如下:
- <span style="font-size:24px;">struct legacy_audio_device {
- struct audio_hw_device device;
-
- struct AudioHardwareInterface *hwif;
- }; </span>
原来createAudioHardware()的返回值是一个硬件设备接口AudioHardwareInterface。
类AudioHardwareInterface正好在audio_hw_hal.cpp文件中所包含的头文件hardware_legacy/AudioHardwareInterface.h中定义的虚类(结构体能调到类,还是头一遭见到,虽然结构体和类长得很象)。那么我很想知道createAudioHardware()具体做了哪些事情。
首先需要确定函数createAudioHardware()的定义在哪儿?有几处定义?调用的具体是哪一个?
AudioHardwareInterface.h头文件中对createAudioHardware函数的声明,没有包含在任何类中,而仅仅包含在名字空间android_audio_legacy中,这和audio_hw_hal.cpp同在一个名字空间中。
- <span style="font-size:24px;">namespace android_audio_legacy {
-
- .....
-
- extern "C" AudioHardwareInterface* createAudioHardware(void);
- };
经搜索,发现createAudioHardware()函数有四处定义。
- <span style="font-size:24px;">$ grep -rn createAudioHardware hardware/ --exclude-dir=.svn
- hardware/alsa_sound/AudioHardwareALSA.cpp:45:
- android_audio_legacy::AudioHardwareInterface *createAudioHardware(void) {
- hardware/msm7k/libaudio-qsd8k/AudioHardware.cpp:2021:
- extern "C" AudioHardwareInterface* createAudioHardware(void) {
- hardware/msm7k/libaudio-qdsp5v2/AudioHardware.cpp:337:
- extern "C" AudioHardwareInterface* createAudioHardware(void) {
- hardware/msm7k/libaudio/AudioHardware.cpp:1132:
- extern "C" AudioHardwareInterface* createAudioHardware(void) {</span>
只有AudioHardwareALSA.cpp文件中包含了头文件hardware_legacy/AudioHardwareInterface.h,并且返回值是android_audio_legacy名字空间的AudioHardwareInterface类对象。则createAudioHardware函数的具体实现很可能是它了,通过log信息证明了这一点。
进入AudioHardwareALSA.cpp,不难看出,此函数,最后会通过执行代码如下代码创建AudioHardwareALSA类对象。
- <span style="font-size:24px;">return new AudioHardwareALSA();</span>
AudioHardwareALSA类的构造函数如下:
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">AudioHardwareALSA::AudioHardwareALSA() :
- mALSADevice(0),
- mAcousticDevice(0)
- {
- ......
-
- int err = <span style="color:#ff0000;">hw_get_module</span>(<span style="color:#ff0000;">ALSA_HARDWARE_MODULE_ID</span>,
- (hw_module_t const**)<span style="color:#ff0000;">&module</span>);
-
- if (err == 0) {
- hw_device_t* device;
- err = <span style="color:#ff0000;">module->methods->open</span>(module, <span style="color:#ff0000;">ALSA_HARDWARE_NAME</span>, &device);
- if (err == 0) {
- mALSADevice = (alsa_device_t *)device;
- <span style="color:#ff0000;">mALSADevice->init</span>(<span style="color:#ff0000;">mALSADevice</span>, <span style="color:#ff0000;">mDeviceList</span>);
-
- .....
-
- err = hw_get_module(ACOUSTICS_HARDWARE_MODULE_ID,
- (hw_module_t const**)&module);
-
- if (err == 0) {
- hw_device_t* device;
- err = module->methods->open(module, ACOUSTICS_HARDWARE_NAME, &device);
-
- .....
- }</span>
宏ALSA_HARDWARE_MODULE_ID是在头文件hardware/alsa_sound/AudioHardwareALSA.h中定义的。
模块所对应的结构体类型为hw_module_t,在头文件hardware/libhardware/include/hardware/hardware.h中定义。
在构造函数中,首先调用函数hw_get_module()获取ID为ALSA_HARDWARE_MODULE_ID的ALSA硬件模块,看来即将进入庞大而又功能强大的ALSA音频子系统了!
经过搜索,很快确定ID为ALSA_HARDWARE_MODULE_ID的ALSA硬件抽象层的具体实现在文件hardware/alsa_sound/alsa_default.cpp中。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">$ grep -rn ALSA_HARDWARE_MODULE_ID hardware/ --exclude-dir=.svn
- hardware/alsa_sound/AudioHardwareALSA.h:39:#define ALSA_HARDWARE_MODULE_ID "alsa"
- hardware/alsa_sound/alsa_default.cpp:59:
- id : ALSA_HARDWARE_MODULE_ID,
- hardware/alsa_sound/AudioHardwareALSA.cpp:150:
- int err = hw_get_module(ALSA_HARDWARE_MODULE_ID,</span>
则很快找到此模块的具体内容如下:
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">extern "C" const hw_module_t HAL_MODULE_INFO_SYM = {
- tag : HARDWARE_MODULE_TAG,
- version_major : 1,
- version_minor : 0,
- id : ALSA_HARDWARE_MODULE_ID,
- name : "ALSA module",
- author : "Wind River",
- methods : &<span style="color:#ff0000;">s_module_methods</span>,
- dso : 0,
- reserved : { 0, },
- };</span>
s_module_methods函数的实现如下:
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static hw_module_methods_t s_module_methods = {
- open : s_device_open
- };</span>
s_device_open函数的实现如下:
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static int s_device_open(const hw_module_t* module, const char* name, <span style="color:#3333ff;">//有些困惑,
-
- hw_device_t** device) <span style="color:#3333ff;"><span style="background-color: rgb(255, 255, 255);">
- {
- alsa_device_t *dev;
- dev = (alsa_device_t *) malloc(sizeof(*dev));
- if (!dev) return -ENOMEM;
-
- memset(dev, 0, sizeof(*dev));
-
-
- dev->common.tag = HARDWARE_DEVICE_TAG;
- dev->common.version = 0;
- dev->common.module = (hw_module_t *) module;
- dev->common.close = s_device_close;
- dev->init = <span style="color:#ff0000;">s_init</span>;
- dev->open = <span style="color:#ff0000;">s_open</span>;
- dev->close = s_close;
- dev->route = <span style="color:#ff0000;">s_route</span>;
-
- <span style="color:#ff0000;">*device = &dev->common;</span> <span style="color:#3333ff;">
- return 0;
- }</span>
经过上述分析,知道了module->methods->open函数具体调用流程了。
然后对ALSA硬件抽象层模块做了初始化的工作。
这里用到一个结构体变量mALSADevice,它在头文件hardware/alsa_sound/AudioHardwareALSA.h中定义的struct alsa_device_t变量。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">struct alsa_device_t {
- hw_device_t common;
-
- status_t (*init)(alsa_device_t *, ALSAHandleList &);
- status_t (*open)(alsa_handle_t *, uint32_t, int);
- status_t (*close)(alsa_handle_t *);
- status_t (*route)(alsa_handle_t *, uint32_t, int);
- }; </span>
此结构体仅仅提供了一些函数调用接口,在这里都有了具体的实现。则mALSADevice->init()将调到s_init()函数中。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static status_t s_init(alsa_device_t *module, <span style="color:#ff0000;">ALSAHandleList</span> &list)
- {
- list.clear();
-
- snd_pcm_uframes_t bufferSize = <span style="color:#ff0000;">_defaultsOut</span>.bufferSize;
-
- for (size_t i = 1; (bufferSize & ~i) != 0; i <<= 1)
- bufferSize &= ~i;
-
- _defaultsOut.module = module;
- _defaultsOut.bufferSize = bufferSize;
-
- <span style="color:#ff0000;">list.push_back(_defaultsOut); </span>
-
- bufferSize = <span style="color:#ff0000;">_defaultsIn</span>.bufferSize;
-
- .....
-
- <span style="color:#ff0000;">list.push_back(_defaultsIn);</span>
-
- .....
- }</span>
这里会把_defaultsOut和_defaultsIn东东保存在ALSA句柄列表ALSAHandleList中。
首先需要明确_defaultsOut和_defaultsIn具体是什么东东。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static alsa_handle_t _defaultsOut = {
- module : 0,
- devices : android_audio_legacy::AudioSystem::DEVICE_OUT_ALL, <span style="color:#3333ff;">
-
- curDev : 0,
- curMode : 0,
- handle : 0, <span style="color:#3333ff;">
- </span> format : SND_PCM_FORMAT_S16_LE,
- channels : 2,
- sampleRate : DEFAULT_SAMPLE_RATE,
- latency : 200000,
- bufferSize : DEFAULT_SAMPLE_RATE / 5,
- modPrivate : 0,
- };
-
- static alsa_handle_t _defaultsIn = {
- module : 0,
- devices : android_audio_legacy::AudioSystem::DEVICE_IN_ALL, <span style="color:#3333ff;">
-
- curDev : 0,
- curMode : 0,
- handle : 0, <span style="color:#3333ff;">
- format : SND_PCM_FORMAT_S16_LE,
- channels : 2, <span style="color:#3333ff;">
-
- sampleRate : DEFAULT_SAMPLE_RATE, <span style="color:#3333ff;">
-
- <span style="color:#000000;">latency : 250000,
- bufferSize : 2048,
- modPrivate : 0,
- };</span>
那ALSAHandleList又是什么东东?
ALSAHandleList在头文件hardware/alsa_sound/AudioHardwareALSA.h中定义的List模板变量。
typedef List<alsa_handle_t> ALSAHandleList;
原来就是struct asla_handle_t的一个列表而已,而_defaultsOut和_defaultsIn正是这样的结构体变量。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">struct alsa_handle_t {
- alsa_device_t * module;
- uint32_t devices;
- uint32_t curDev;
- int curMode;
- snd_pcm_t * handle; <span style="color:#3333ff;">
- </span> snd_pcm_format_t format;
- <span style="color:#ff0000;"> uint32_t channels;
- uint32_t sampleRate;</span>
- unsigned int latency;
- unsigned int bufferSize;
- void * modPrivate;
- };</span>
ALSA硬件抽象层正是这样获得了输出音频通道和输入音频通道的相关初始化硬件参数,以后在使用中并不试图改变这些硬件参数(针对真能手机和平板来说,也却是不需要改变)。因此,在扩展android系统功能,为其添加对USB AUDIO设备的支持时,就不得不考虑时事改变channels和sampleRate这两个硬件参数的值。
至此,AudioFlinger服务首次启动过程分析完毕!
1.2 AudioPolicyService启动流程及其所涉及的HAL层模块启动流程分析。
AudioPolicyService服务的启动流程类似于AudioFlinger服务的启动过程,将简要分析。
先看下AudioPolicyService类的定义(AudioPolicyService.h)(提供此类的定义,主要是为下面instantiate()函数服务的):
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">class AudioPolicyService :
- public <span style="color:#ff0000;">BinderService<AudioPolicyService></span>, <span style="color:#3333ff;">
-
-
- </span> public BnAudioPolicyService,
-
- public IBinder::DeathRecipient
- {
- friend class BinderService<AudioPolicyService>;
-
- public:
-
- static const char *<span style="color:#ff0000;">getServiceName</span>() { return "media.audio_policy"; }
-
- .....
- }</span>
根据前面的分析,晓得将通过调用如下代码启动AudioPolicyService服务。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">AudioPolicyService::instantiate();</span>
此代码最后将调到AudioPolicyService类的构造函数
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">AudioPolicyService::AudioPolicyService()
- : BnAudioPolicyService() , mpAudioPolicyDev(NULL) , mpAudioPolicy(NULL)
- {
- char value[PROPERTY_VALUE_MAX];
- const struct hw_module_t *module;
- int forced_val;
- int rc;
-
- Mutex::Autolock _l(mLock);
-
-
- mTonePlaybackThread = new AudioCommandThread(String8(""));
-
- mAudioCommandThread = new <span style="color:#ff0000;">AudioCommandThread</span>(String8("ApmCommandThread"));
-
-
- rc = <span style="color:#ff0000;">hw_get_module</span>(<span style="color:#ff0000;">AUDIO_POLICY_HARDWARE_MODULE_ID</span>, &module);
- if (rc)
- return;
-
- rc = <span style="color:#ff0000;">audio_policy_dev_open</span>(module, <span style="color:#ff0000;">&mpAudioPolicyDev</span>);
- LOGE_IF(rc, "couldn't open audio policy device (%s)", strerror(-rc));
- if (rc)
- return;
-
- rc = <span style="color:#ff0000;">mpAudioPolicyDev->create_audio_policy</span>(mpAudioPolicyDev, <strong style="background-color: rgb(192, 192, 192);"><span style="color:#ff0000;">&aps_ops</span></strong>, this,
- <span style="color:#ff0000;">&mpAudioPolicy</span>);
- LOGE_IF(rc, "couldn't create audio policy (%s)", strerror(-rc));
- if (rc)
- return;
-
- rc = <span style="color:#ff0000;">mpAudioPolicy->init_check</span>(mpAudioPolicy);
-
- .....
-
- }</span>
(1)首先开启了放音线程和音频命令线程。这些工作都是通过创建AudioCommandThread线程类对象完成。
AudioCommandThread类在头文件frameworks/base/services/audioflinger/AudioPolicyService.h中定义
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">class AudioCommandThread : public Thread {</span>
是AudioPolicyService类的私有子类。
AudioCommandThread线程类创建了对象后,将进入死循环中,等待要处理的事件传来。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">bool AudioPolicyService::AudioCommandThread::threadLoop()
- {
- nsecs_t waitTime = INT64_MAX;
-
- mLock.lock();
- while (!exitPending())
- {
- while(!mAudioCommands.isEmpty()) {
- .....
-
- switch (command->mCommand) {
-
- .....
-
- <span style="color:#ff0000;">case SET_PARAMETERS</span>: {
- ParametersData *data = (ParametersData *)command->mParam;
- LOGV("AudioCommandThread() processing set parameters string %s, io %d",
- data->mKeyValuePairs.string(), data->mIO);
- command->mStatus = <span style="color:#ff0000;">AudioSystem::setParameters</span>(data->mIO, data->mKeyValuePairs);
- if (command->mWaitStatus) {
- command->mCond.signal();
- mWaitWorkCV.wait(mLock);
- }
- delete data;
- }break;
-
- .....
- }</span>
这里只列出了switch语句中的一种情况的处理代码,因为后面分析setDeviceConnectionState()函数的调用流程时将用到。
当command->mCommand值为SET_PARAMETERS时,将调用libmedia库(frameworks/base/media/libmedia/AudioSystem.cpp)中的函数setParameters()做进一步处理。
(2)然后调用函数hw_get_module()获得ID号为AUDIO_POLICY_HARDWARE_MODULE_ID的硬件抽象层的音频策略模块。宏AUDIO_POLICY_HARDWARE_MODULE_ID在头文件hardware/libhardware/include/hardware/audio_policy.h中定义。
ID号为AUDIO_POLICY_HARDWARE_MODULE_ID的模块也有两处具体实现,同样通过log信息,确认调用的是libhardware_legacy模块中的AUDIO_POLICY_HARDWARE_MODULE_ID子模块的具体实现。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">$ grep -rn AUDIO_POLICY_HARDWARE_MODULE_ID hardware/ --exclude-dir=.svn
- hardware/libhardware_legacy/audio/audio_policy_hal.cpp:414:
- id: AUDIO_POLICY_HARDWARE_MODULE_ID,
- hardware/libhardware/modules/audio/audio_policy.c:318:
- .id = AUDIO_POLICY_HARDWARE_MODULE_ID,</span>
audio_policy_hal.cpp文件中定义的AUDIO_POLICY_HARDWARE_MODULE_ID模块如下:
struct legacy_ap_module HAL_MODULE_INFO_SYM = {
module: {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: AUDIO_POLICY_HARDWARE_MODULE_ID,
name: "LEGACY Audio Policy HAL",
author: "The Android Open Source Project",
methods: &legacy_ap_module_methods,
dso : NULL,
reserved : {0},
},
},
};
(3)再然后调用audio_policy_dev_open()函数(在头文件hardware/libhardware/include/hardware/audio_policy.h中定义)。
首先分析函数参数:第一个参数就是上面获取的模块,第二个参数mpAudioPolicyDev是struct audio_policy_device 指针变量,在头文件AudioPolicyService.h中定义。而struct audio_policy_device是在头文件audio_policy.h中定义的。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">struct audio_policy_device {
- struct hw_device_t common;
-
- int (*create_audio_policy)(const struct audio_policy_device *device,
- struct audio_policy_service_ops <span style="color:#ff0000;background-color: rgb(153, 153, 153);">*<strong>aps_ops</strong></span>,
- void *service,
- struct audio_policy **ap);
- .....
- }</span>
最后看下audio_policy_dev_open()函数的实现
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;"><span xmlns="http://www.w3.org/1999/xhtml" style="">
-
- static inline int audio_policy_dev_open(const hw_module_t* module,
- struct audio_policy_device** device)
- {
- return module->methods->open(module, AUDIO_POLICY_INTERFACE,
- (hw_device_t**)device);
- }</span></span>
由上述分析可知,open函数指针就指向legacy_ap_dev_open()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">static int legacy_ap_dev_open(const hw_module_t* module, const char* name,
- hw_device_t** device) <span style="color:#3333ff;">
- {
- struct legacy_ap_device *dev;
-
- if (strcmp(name, AUDIO_POLICY_INTERFACE) != 0)<span style="color:#3333ff;">
-
- </span> return -EINVAL;
-
- dev = (struct legacy_ap_device *)calloc(1, sizeof(*dev));
- if (!dev)
- return -ENOMEM;
-
- dev->device.common.tag = HARDWARE_DEVICE_TAG;
- dev->device.common.version = 0;
- dev->device.common.module = const_cast<hw_module_t*>(module);
- dev->device.common.close = legacy_ap_dev_close;
- dev->device.create_audio_policy = <span style="color:#ff0000;">create_legacy_ap</span>;
- dev->device.destroy_audio_policy = destroy_legacy_ap;
-
- <span style="color:#ff0000;">*device = &dev->device.common;</span> <span style="color:#3333ff;">
-
- return 0;
- }</span></span>
(4)再接下来调用的mpAudioPolicyDev->create_audio_policy()函数指针具体就是create_legacy_ap()。
第二个参数&aps_ops是struct audio_policy_service_ops变量,是APS(AudioPolicyService)的操作接口,并且传递的是aps_ops的地址,则被调用者使用的将是在APS中函数接口的实现。
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">namespace {
- struct audio_policy_service_ops aps_ops = {
- <span style="color:#ff0000;">open_output : aps_open_output,</span>
- open_duplicate_output : aps_open_dup_output,
- close_output : aps_close_output,
- suspend_output : aps_suspend_output,
- restore_output : aps_restore_output,
- open_input : aps_open_input,
- close_input : aps_close_input,
- set_stream_volume : aps_set_stream_volume,
- set_stream_output : aps_set_stream_output,
- <span style="color:#ff0000;">set_parameters : aps_set_parameters,</span>
- get_parameters : aps_get_parameters,
- start_tone : aps_start_tone,
- stop_tone : aps_stop_tone,
- set_voice_volume : aps_set_voice_volume,
- move_effects : aps_move_effects,
- };
- };
struct audio_policy_service_ops在头文件hardware/libhardware/include/hardware/audio_policy.h中定义,是包含音频相关控制函数的接口。可见aps_ops接口将为HAL层提供服务。
第四个参数是&mpAudioPolicy。mpAudioPolicy是struct audio_policy的指针变量(AudioPolicyService.h)。而struct audio_policy也是在audio_policy.h中定义,将要用到的接口如下:
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">struct audio_policy {
-
-
-
-
-
- int (*set_device_connection_state)(struct audio_policy *pol,
- audio_devices_t device,
- audio_policy_dev_state_t state,
- const char *device_address);
-
- .....
-
-
- int (*init_check)(const struct audio_policy *pol);
-
- .....
- }</span></span>
接下来看看create_audio_policy()函数指针的具体实现:
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">static int create_legacy_ap(const struct audio_policy_device *device,
- struct audio_policy_service_ops *aps_ops,
- void *service,
- struct audio_policy **ap)
- {
- struct legacy_audio_policy *lap;
- int ret;
-
- if (!service || !aps_ops)
- return -EINVAL;
-
- lap = (struct legacy_audio_policy *)calloc(1, sizeof(*lap));
- if (!lap)
- return -ENOMEM;
-
- lap->policy.set_device_connection_state = <span style="color:#ff0000;">ap_set_device_connection_state</span>;
-
- ......
-
- lap->policy.init_check = ap_init_check;
- lap->policy.get_output = ap_get_output;
- lap->policy.start_output = ap_start_output;
- lap->policy.stop_output = ap_stop_output;
- lap->policy.release_output = ap_release_output;
- lap->policy.get_input = ap_get_input;
- lap->policy.start_input = <span style="color:#ff0000;">ap_start_input</span>;
- lap->policy.stop_input = ap_stop_input;
- lap->policy.release_input = ap_release_input;
-
- .....
- <span style="color:#ff0000;">
- lap->service = service; <span style="color:#3333ff;">
- lap->aps_ops = aps_ops; <span style="color:#3333ff;">
- lap->service_client =
- new AudioPolicyCompatClient(aps_ops, service);</span>
- if (!lap->service_client) {
- ret = -ENOMEM;
- goto err_new_compat_client;
- }
- <span style="color:#ff0000;">
- lap->apm = createAudioPolicyManager(lap->service_client);</span>
-
- ......
-
- <span style="color:#ff0000;">*ap = &lap->policy;</span> <span style="color:#3333ff;">
-
- ......
- }</span></span>
此函数中创建了重要对象:AudioPolicyCompatClient类对象和createAudioPolicyManager函数创建音频策略管理器,需要分析下。
先分析AudioPolicyCompatClient类对象的创建。此类在头文件hardware/libhardware_legacy/audio/AudioPolicyCompatClient.h中定义。
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">namespace android_audio_legacy {
- class AudioPolicyCompatClient : public AudioPolicyClientInterface {
- <span style="color:#3333ff;">
-
-
- public:
- AudioPolicyCompatClient(struct audio_policy_service_ops *serviceOps,
- void *service) :
- <span style="color:#ff0000;">mServiceOps(serviceOps) , mService(service)</span> {} <span style="color:#3333ff;">
-
-
- ......
-
- private:
- struct audio_policy_service_ops* mServiceOps;
- void* mService;
-
- ......
- }</span></span>
此构造函数主要初始化两个私有化变量mServiceOps和mService,并把创建的对象作为函数createAudioPolicyManager的唯一参数,来创建音频策略管理器。
然而,函数createAudioPolicyManager有多个定义,搜索结果如下:
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">$ grep -rn createAudioPolicyManager hardware/ --exclude-dir=.svn
- hardware/alsa_sound/AudioPolicyManagerALSA.cpp:31:
- extern "C" android_audio_legacy::AudioPolicyInterface*
- createAudioPolicyManager(
- android_audio_legacy::AudioPolicyClientInterface *clientInterface)
- hardware/libhardware_legacy/audio/AudioPolicyManagerDefault.cpp:24:
- extern "C" AudioPolicyInterface*
- createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
- hardware/msm7k/libaudio-qsd8k/AudioPolicyManager.cpp:39:
- extern "C" AudioPolicyInterface*
- createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
- hardware/msm7k/libaudio-qdsp5v2/AudioPolicyManager.cpp:39:
- extern "C" AudioPolicyInterface*
- createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
- hardware/msm7k/libaudio/AudioPolicyManager.cpp:35:
- extern "C" AudioPolicyInterface*
- createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)</span></span>
函数createAudioPolicyManager虽然有多个定义,但是它们最后将创建AudioPolicyManagerBase类对象,以AudioPolicyManagerALSA类为例分析
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">AudioPolicyManagerALSA::AudioPolicyManagerALSA(
- android_audio_legacy::AudioPolicyClientInterface *<span style="color:#ff0000;">clientInterface</span>)
- : AudioPolicyManagerBase(<span style="color:#ff0000;">clientInterface</span>) <span style="color:#3333ff;">
-
-
- </span>{
- }
-
- AudioPolicyManagerBase::AudioPolicyManagerBase(
- AudioPolicyClientInterface *clientInterface)
- :
- #ifdef AUDIO_POLICY_TEST
- Thread(false),
- #endif //AUDIO_POLICY_TEST
- mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0),
- mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
- mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
- mA2dpSuspended(false)
- {
- <span style="color:#ff0000;">mpClientInterface = clientInterface;</span> <span style="color:#3333ff;">
-
- for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
- mForceUse[i] = AudioSystem::FORCE_NONE;
- }
-
- initializeVolumeCurves();
-
-
- mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
- AudioSystem::DEVICE_OUT_SPEAKER; <span style="color:#3333ff;">
- mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC; <span style="color:#3333ff;">
-
- ......
-
- <span style="color:#ff0000;">mHardwareOutput</span> = <span style="color:#cc0000;">mpClientInterface->openOutput</span>(&outputDesc->mDevice,
- &outputDesc->mSamplingRate,
- &outputDesc->mFormat,
- &outputDesc->mChannels,
- &outputDesc->mLatency,
- outputDesc->mFlags);
-
- ......
-
- <span style="color:#ff0000;">setOutputDevice</span>(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER,
- true);
-
- ......
-
- }
- </span></span>
mpClientInterface->openOutput()函数先回掉到AudioPolicyCompatClient类的openOutput()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">audio_io_handle_t AudioPolicyCompatClient::openOutput(uint32_t *pDevices,
- uint32_t *pSamplingRate,
- uint32_t *pFormat,
- uint32_t *pChannels,
- uint32_t *pLatencyMs,
- AudioSystem::output_flags flags)
- {
- return <span style="color:#ff0000;">mServiceOps->open_output</span>(mService, pDevices, pSamplingRate, pFormat,
- pChannels, pLatencyMs,
- (audio_policy_output_flags_t)flags);
- }</span></span>
由前面分析可知,在创建AudioPolicyCompatClient类对象时,mServiceOps被初始化为APS的struct audio_policy_service_ops变量aps_ops;则将回调到ops中的open_output()函数,具体调到aps_open_output()函数:
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static audio_io_handle_t aps_open_output(void *service,
- uint32_t *pDevices,
- uint32_t *pSamplingRate,
- uint32_t *pFormat,
- uint32_t *pChannels,
- uint32_t *pLatencyMs,
- audio_policy_output_flags_t flags)
- {
- sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
- if (af == NULL) {
- LOGW("%s: could not get AudioFlinger", __func__);
- return 0;
- }
-
- return <span style="color:#ff0000;">af->openOutput</span>(pDevices, pSamplingRate, pFormat, pChannels,
- pLatencyMs, flags);
- }</span>
不难看出,将调到AudioFlinger的openOutput()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">int AudioFlinger::openOutput(uint32_t *pDevices,
- uint32_t *pSamplingRate,
- uint32_t *pFormat,
- uint32_t *pChannels,
- uint32_t *pLatencyMs,
- uint32_t flags)
- {
- ......
-
- audio_hw_device_t *outHwDev;
-
- ......
-
- outHwDev = findSuitableHwDev_l(*pDevices);
- if (outHwDev == NULL)
- return 0;
-
- status = <span style="color:#ff0000;">outHwDev->open_output_stream</span>(outHwDev, *pDevices, (int *)&format,
- &channels, &samplingRate, &outStream);
-
- ......
-
- return 0;
- }</span>
struct audio_hw_device_t是在头文件hardware/libhardware/include/hardware/audio.h中定义的一个结构体。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">typedef struct audio_hw_device audio_hw_device_t;</span>
前面在分析AudioFlinger类的load_audio_interface函数时,已经分析过struct audio_hw_device。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(uint32_t devices)
- {
-
- for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
- audio_hw_device_t *dev = <span style="color:#ff0000;">mAudioHwDevs</span>[i];
- if ((<span style="color:#ff0000;">dev->get_supported_devices</span>(dev) & devices) == devices)
- return dev;
- }
- return NULL;
- }</span>
由前文分析可知,此处的get_supported_devices()函数指针将具体调到audio_hw_hal.cpp文件中的adev_get_supported_devices(),如下所示,这里列出了系统所支持的所有输出/输入音频设备。因此,我们要也要仿照此音频设备的定义名称,在这里添加USB AUDIO音频设备的名称,以及它们在别处的定义。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static uint32_t adev_get_supported_devices(const struct audio_hw_device *dev)
- {
-
-
-
-
-
-
- return (
- AUDIO_DEVICE_OUT_EARPIECE |
- AUDIO_DEVICE_OUT_SPEAKER |
- AUDIO_DEVICE_OUT_WIRED_HEADSET |
- AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
- AUDIO_DEVICE_OUT_AUX_DIGITAL |
- AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
- AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET |
- AUDIO_DEVICE_OUT_ALL_SCO |
- AUDIO_DEVICE_OUT_DEFAULT |
-
- AUDIO_DEVICE_IN_COMMUNICATION |
- AUDIO_DEVICE_IN_AMBIENT |
- AUDIO_DEVICE_IN_BUILTIN_MIC |
- AUDIO_DEVICE_IN_WIRED_HEADSET |
- AUDIO_DEVICE_IN_AUX_DIGITAL |
- AUDIO_DEVICE_IN_BACK_MIC |
- AUDIO_DEVICE_IN_ALL_SCO |
- AUDIO_DEVICE_IN_DEFAULT);
- }</span>
当找到合适的设备之后,将调用outHwDev->open_output_stream()函数打开相应设备的输出流;同样的道理,将具体调到audio_hw_hal.cpp文件中的adev_open_output_stream()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static int adev_open_output_stream(struct audio_hw_device *dev,
- uint32_t devices,
- int *format,
- uint32_t *channels,
- uint32_t *sample_rate,
- struct audio_stream_out **stream_out)
- {
- struct legacy_audio_device *ladev = to_ladev(dev);
- status_t status;
- struct legacy_stream_out *out;
- int ret;
-
- out = (struct legacy_stream_out *)calloc(1, sizeof(*out));
- if (!out)
- return -ENOMEM;
-
- out->legacy_out = <span style="color:#ff0000;">ladev->hwif->openOutputStream</span>(devices, format, channels,
- sample_rate, &status);
-
- ......
- }</span>
由前文分析可知,ladev->hwif具体指AudioHardwareALSA类;则ladev->hwif->openOutputStream()函数调到AudioHardwareALSA::openOutputStream()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">android_audio_legacy::AudioStreamOut *
- AudioHardwareALSA::openOutputStream(uint32_t devices,
- int *format,
- uint32_t *channels,
- uint32_t *sampleRate,
- status_t *status)
- {
- ......
-
-
- for(ALSAHandleList::iterator it = mDeviceList.begin(); <span style="color:#3333ff;">
-
-
-
- </span> it != mDeviceList.end(); ++it)
- if (it->devices & devices) {
- err = <span style="color:#ff0000;">mALSADevice->open</span>(&(*it), devices, mode()); <span style="color:#3333ff;">
-
-
-
- if (err) break;
- out = <span style="color:#ff0000;">new AudioStreamOutALSA</span>(this, &(*it)); <span style="color:#3333ff;">
-
-
- err = out->set(format, channels, sampleRate);
- break;
- }
-
- ......
- }</span>
由前文对AudioHardwareALSA类的启动流程分析可知,mDeviceList是用来存储输出/输入音频通道信息的句柄的。
mALSADevice表示在初始化ALSA设备时所指向的一个具体ALSA设备的操作接口。则mALSADevice->open()函数,将具体调到ALSA模块的s_open()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static status_t s_open(alsa_handle_t *handle, uint32_t devices, int mode)
- {
-
-
-
-
-
- s_close(handle); <span style="color:#3333ff;">
- </span>
- LOGD("open called for devices %08x in mode %d...", devices, mode);
-
- const char *stream = <span style="color:#ff0000;">streamName</span>(handle);
- const char *devName = <span style="color:#ff0000;">deviceName</span>(handle, devices, mode);
-
- int err;
-
- for (;;) {
-
-
-
- err = <span style="color:#ff0000;">snd_pcm_open</span>(<span style="color:#ff0000;">&handle->handle</span>, <span style="color:#ff0000;">devName</span>, <span style="color:#ff0000;">direction</span>(handle),
- <span style="color:#3333ff;">
- SND_PCM_ASYNC);
- if (err == 0) break;
-
-
-
- char *tail = strrchr(devName, '_');
- if (!tail) break;
- *tail = 0;
- }
-
- if (err < 0) {
-
- <span style="color:#ff0000;">devName = "default";</span>
- err = <span style="color:#ff0000;">snd_pcm_open</span>(&handle->handle, devName, direction(handle), 0);
- }
- if (err < 0) {
- LOGE("Failed to Initialize any ALSA %s device: %s",
- stream, strerror(err));
- return NO_INIT;
- }
-
- err = <span style="color:#ff0000;">setHardwareParams</span>(handle);
-
- if (err == NO_ERROR) err = setSoftwareParams(handle);
-
- LOGI("Initialized ALSA %s device %s", stream, devName);
-
- handle->curDev = devices;
- handle->curMode = mode;
-
- return err;
- }
- </span>
(1) 调用函数streamName()函数获取音频流名称。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">const char *streamName(alsa_handle_t *handle)
- {
- return snd_pcm_stream_name(direction(handle));
- } </span>
snd_pcm_stream_name()函数是ALSA-LIB API,在external/alsa-lib/src/pcm/pcm.c文件中定义。
要想获得音频流名称,不得不先分析direction()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">snd_pcm_stream_t direction(alsa_handle_t *handle)
- {
- return (handle->devices & android_audio_legacy::AudioSystem::DEVICE_OUT_ALL) ? SND_PCM_STREAM_PLAYBACK
- : SND_PCM_STREAM_CAPTURE;
- }</span>
原来direction()函数就是用来返回PCM流的方向(放音或者录音)。
direction()函数的返回值将作为snd_pcm_stream_name()的参数,
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">const char *snd_pcm_stream_name(snd_pcm_stream_t stream)
- {
- if (stream > SND_PCM_STREAM_LAST)
- return NULL;
- return <span style="color:#ff0000;">snd_pcm_stream_names</span>[stream];
- }</span>
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static const char *const snd_pcm_stream_names[] = {
- STREAM(PLAYBACK),
- STREAM(CAPTURE),
- };</span>
好吧,音频流的名称不是放音就是录音。
(2)接下来调用deviceName()函数获取设备名称。这点很重要,将为我们在asound.conf为新添加的USB AUDIO音频设备命名提供规则。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">const char *deviceName(alsa_handle_t *handle, uint32_t device, int mode)
- {
- static char <span style="color:#ff0000;">devString</span>[ALSA_NAME_MAX];
- int hasDevExt = 0;
-
- strcpy(devString, devicePrefix[direction(handle)]);
-
- for (int dev = 0; device && dev < deviceSuffixLen; dev++)
- if (device & <span style="color:#ff0000;">deviceSuffix</span>[dev].device) {
- ALSA_STRCAT (devString, deviceSuffix[dev].suffix);
- device &= ~deviceSuffix[dev].device;
- <span style="color:#ff0000;">hasDevExt = 1;</span>
- }
-
- if (hasDevExt) switch (mode) {
- case android_audio_legacy::AudioSystem::MODE_NORMAL:
- ALSA_STRCAT (devString, "_normal")
- ;
- break;
- case android_audio_legacy::AudioSystem::MODE_RINGTONE:
- ALSA_STRCAT (devString, "_ringtone")
- ;
- break;
- case android_audio_legacy::AudioSystem::MODE_IN_CALL:
- ALSA_STRCAT (devString, "_incall")
- ;
- break;
- };
-
- return devString;
- }</span>
用字符数组devString存储设备名称。
首先把设备前缀复制给devString。因此,作为输出音频通道设备的名称,必以AndroidPlayback为前缀;作为输入音频通道设备的名称,必以AndroidCapture为前缀。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static const char *devicePrefix[SND_PCM_STREAM_LAST + 1] = {
- "AndroidPlayback",
- "AndroidCapture",
- };</span>
接下来从deviceSuffix数组中查找合适的后缀,追加到devString字符数组中。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">
-
- static const device_suffix_t deviceSuffix[] = {
- {android_audio_legacy::AudioSystem::DEVICE_OUT_EARPIECE, "_Earpiece"},
- {android_audio_legacy::AudioSystem::DEVICE_OUT_SPEAKER, "_Speaker"},
- {android_audio_legacy::AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "_Bluetooth"},
- {android_audio_legacy::AudioSystem::DEVICE_OUT_WIRED_HEADSET, "_Headset"},
- {android_audio_legacy::AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "_Bluetooth-A2DP"},
- }; </span>
struct device_suffix_t的定义如下:
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">struct device_suffix_t {
- const android_audio_legacy::AudioSystem::audio_devices device;
- const char *suffix;
- };</span>
PS: 我们也要在此数组中添加USB AUDIO音频设备的相关信息。同时也要在定义了类似DEVICE_OUT_EARPIECE设备的类中定义USB AUDIO音频设备:
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;"> 1.frameworks/base/media/java/android/media/AudioSystem.java
- 2.frameworks/base/media/java/android/media/AudioManager.java
- 3.hardware/libhardware_legacy/include/hardware_legacy/AudioSystemLegacy.h
- 4.system/core/include/system/audio.h</span>
题外话:android系统中对音频设备的定义如下(AudioSystem.Java):
- <span xmlns="http://www.w3.org/1999/xhtml" style=""> <span style="font-size:24px;"> public static final int DEVICE_OUT_EARPIECE = 0x1; // 0x1 << 0
- public static final int DEVICE_OUT_SPEAKER = 0x2;
- public static final int DEVICE_OUT_WIRED_HEADSET = 0x4;
- public static final int DEVICE_OUT_WIRED_HEADPHONE = 0x8;
- public static final int DEVICE_OUT_BLUETOOTH_SCO = 0x10;
- public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20;
- public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40;
- public static final int DEVICE_OUT_BLUETOOTH_A2DP = 0x80;
- public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
-
- public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
- public static final int DEVICE_OUT_AUX_DIGITAL = 0x400;
- public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800;
- public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000;
- public static final int DEVICE_OUT_DEFAULT = 0x8000;
-
- public static final int DEVICE_IN_COMMUNICATION = 0x10000;
- public static final int DEVICE_IN_AMBIENT = 0x20000;
- public static final int DEVICE_IN_BUILTIN_MIC1 = 0x40000;
- public static final int DEVICE_IN_BUILTIN_MIC2 = 0x80000;
- public static final int DEVICE_IN_MIC_ARRAY = 0x100000;
- public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x200000;
-
- public static final int DEVICE_IN_WIRED_HEADSET = 0x400000;
- public static final int DEVICE_IN_AUX_DIGITAL = 0x800000;
当设备越来越多时,很难保证等号右边的数字中零的个数不写错。采用注释部分的定义方式较好。
当找到了设备后缀后,将对变量hasDevExt赋值为1,表示还会有扩展名称(_normal,_ringtone或者_incall)。
至此,一个设备的PCM节点名称就形成了!
(3)程序将执行到调用snd_pcm_open() ALSA-LIB API,并把刚才得到的设备名称devName作为参数之一,调到ALSA-LIB,进而调到ALSA-DRIVER,去打开所指定的音频设备。如果打开指定的音频设备失败了,将打开默认的音频设备。
(4)如果成功打开音频设备,程序继续往下执行,将调用setHardwareParams()函数设置硬件参数。这些硬件参数包括缓冲区大小,采样率,声道数和音频格式等。其实这些硬件参数都在struct alsa_handle_t中定义,在分析初始化函数s_init()函数时已有分析,在默认音频设备配置_defaultsOut和_defaultsIn中已经指定。
但是,在使用USB AUDIO输入音频设备时,默认的输入音频配置中的声道数和采样率很有可能与实际使用的USB AUDIO的不一致,导致USB AUDIO设备不可用或者音频失真。因此,需要在执行setHardwareParams()函数前,并且知道是要打开输入音频通道时(输出音频通道的硬件参数配置可用),需要检测时间使用的USB AUDIO音频设备的这两个硬件参数,重新对_defaultsIn中声道数和采样率进行赋值。将在后面做详细分析。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">status_t setHardwareParams(alsa_handle_t *handle)
- {
- ......
-
- unsigned int requestedRate = <span style="color:#ff0000;">handle->sampleRate</span>;
-
- ......
-
- <span style="color:#ff0000;"> err = </span>snd_pcm_hw_params_set_channels(handle->handle, hardwareParams,
- <span style="color:#ff0000;">handle->channels</span>);
-
- ......
-
- err = <span style="color:#ff0000;">snd_pcm_hw_params_set_rate_near</span>(handle->handle, hardwareParams,
- <span style="color:#ff0000;">&requestedRate</span>, 0);
-
- ......
- }
- </span>
(5)最后程序会执行到设置软件参数的函数setHardwareParams()中。在添加USB AUDIO音频设备时,这里没有遇到问题,不再分析。
2. JAVA API setDeviceConnectionState()调用流程详解。
把最复杂的两大本地服务分析完后,后面的任务就很轻了!
JAVA API setDeviceConnectionState()在文件frameworks/base/media/java/android/media/AudioSystem.java中定义。
public static native int setDeviceConnectionState(int device, int state, String device_address);
第一个参数就是要打开的音频设备的标识符,将一路传递下去,直到ALSA模块(alsa_default.cpp);
第二个参数表示第一个参数所指的音频设备是否可用;
第三个参数表示设备地址,一般为空。
看到java关键字native,晓得将调到对应的JNI(frameworks/base/core/jni/android_media_AudioSystem.cpp)代码。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static int
- android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz,
- jint device, jint state, jstring device_address)
- {
- const char *c_address = env->GetStringUTFChars(device_address, NULL);
- int status = check_AudioSystem_Command(
- <span style="color:#ff0000;">AudioSystem::setDeviceConnectionState</span>(static_cast <audio_devices_t>(device),
- static_cast <audio_policy_dev_state_t>(state),
- c_address));
- env->ReleaseStringUTFChars(device_address, c_address);
- return status;
- }</span>
显然,又调到libmedia库(frameworks/base/media/libmedia/AudioSystem.cpp)中setDeviceConnectionState()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">status_t AudioSystem::setDeviceConnectionState(audio_devices_t device,
- audio_policy_dev_state_t state,
- const char *device_address)
- {
- const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
- const char *address = "";
-
- if (aps == 0) return PERMISSION_DENIED;
-
- if (device_address != NULL) {
- address = device_address;
- }
-
- return <span style="color:#ff0000;">aps->setDeviceConnectionState</span>(device, state, address);
- }</span>
显然,调到APS的setDeviceConnectionState()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device,
- audio_policy_dev_state_t state,
- const char *device_address)
- {
- ......
-
- return <span style="color:#ff0000;">mpAudioPolicy->set_device_connection_state</span>(mpAudioPolicy, device,
- state, device_address);
- }
- </span>
根据前面对AudioPolicyService服务的启动流程分析可知,mpAudioPolicy指向在文件audio_policy_hal.cpp中定义的音频策略模块。则mpAudioPolicy->set_device_connection_state()函数具体调到函数ap_set_device_connection_state()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static int ap_set_device_connection_state(struct audio_policy *pol,
- audio_devices_t device,
- audio_policy_dev_state_t state,
- const char *device_address)
- {
- struct legacy_audio_policy *lap = to_lap(pol);
- return <span style="color:#ff0000;">lap->apm->setDeviceConnectionState</span>(
- (AudioSystem::audio_devices)device,
- (AudioSystem::device_connection_state)state,
- device_address);
- }</span>
同样,由前面对AudioPolicyService服务的启动流程分析可知,lap->apm指向AudioPolicyManagerBase类对象。则lap->apm->setDeviceConnectionState()函数将调到AudioPolicyManagerBase::setDeviceConnectionState()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size: 24px;"><span style="font-size:18px;">status_t AudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_devices device,
- AudioSystem::device_connection_state state,
- const char *device_address)
- {
- ......
-
-
- if (AudioSystem::isOutputDevice(device)) { </span><span style="font-size:18px;color:#3333ff;">
- </span><span style="font-size:18px;">
- ......
-
- switch (state)
- {
-
- case AudioSystem::DEVICE_STATE_AVAILABLE:
- if (mAvailableOutputDevices & device) {
- LOGW("setDeviceConnectionState() device already connected: %x", device);
- return INVALID_OPERATION;
- }
- LOGV("setDeviceConnectionState() connecting device %x", device);
-
-
- mAvailableOutputDevices |= device; <span style="color:#3333ff;">
-
- .....
-
-
- case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
- if (!(mAvailableOutputDevices & device)) {
- LOGW("setDeviceConnectionState() device not connected: %x", device);
- return INVALID_OPERATION;
- }
-
-
- LOGV("setDeviceConnectionState() disconnecting device %x", device);
-
- mAvailableOutputDevices &= ~device; <span style="color:#3333ff;">
-
- ......
- }
-
-
- uint32_t newDevice = <span style="color:#ff0000;">getNewDevice</span>(mHardwareOutput, false);
-
- ......
-
- <span style="color:#ff0000;">updateDeviceForStrategy</span>();
- <span style="font-size:24px;"><strong style="background-color: rgb(153, 153, 153);"><span style="color:#ff0000;">setOutputDevice</span></strong></span>(mHardwareOutput, newDevice);
-
- <span style="color:#3333ff;">
- if (device == AudioSystem::DEVICE_OUT_WIRED_HEADSET) {
- device = AudioSystem::DEVICE_IN_WIRED_HEADSET;
- } else if (device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO ||
- device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
- device == AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
- device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
- } else {
- return NO_ERROR;
- }
- }
-
- if (AudioSystem::isInputDevice(device)) { <span style="color:#3333ff;">
-
- switch (state)
- {
-
- case AudioSystem::DEVICE_STATE_AVAILABLE: {
- if (mAvailableInputDevices & device) {
- LOGW("setDeviceConnectionState() device already connected: %d", device);
- return INVALID_OPERATION;
- }
- mAvailableInputDevices |= device;
- }
- break;
-
-
- case AudioSystem::DEVICE_STATE_UNAVAILABLE: {
- if (!(mAvailableInputDevices & device)) {
- LOGW("setDeviceConnectionState() device not connected: %d", device);
- return INVALID_OPERATION;
- }
- mAvailableInputDevices &= ~device;
- } break;
-
- default:
- LOGE("setDeviceConnectionState() invalid state: %x", state);
- return BAD_VALUE;
- }
-
- audio_io_handle_t activeInput = getActiveInput();
- if (activeInput != 0) {
- AudioInputDescriptor *inputDesc = mInputs.valueFor(activeInput);
- uint32_t newDevice = getDeviceForInputSource(inputDesc->mInputSource);
- if (newDevice != inputDesc->mDevice) {
- LOGV("setDeviceConnectionState() changing device from %x to %x for input %d",
- inputDesc->mDevice, newDevice, activeInput);
- inputDesc->mDevice = newDevice;
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), (int)newDevice);
- mpClientInterface->setParameters(activeInput, param.toString());
- }
- }
-
- return NO_ERROR;
- }
-
- LOGW("setDeviceConnectionState() invalid device: %x", device);
- return BAD_VALUE;
- }</span>
- </span>
(1) 当前设备是输出设备时,程序执行到getNewDevice()函数,将获得新设备,作为设置输出设备函数setOutputDevice()的第二个参数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="">uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fromCache)
- {
- uint32_t device = 0;
-
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
-
-
-
-
-
-
-
-
-
-
-
- if (outputDesc-><span style="color:#ff0000;">isUsedByStrategy</span>(STRATEGY_ENFORCED_AUDIBLE)) { <span style="color:#3333ff;">
-
- device = <span style="color:#ff0000;">getDeviceForStrategy</span>(STRATEGY_ENFORCED_AUDIBLE, fromCache); <span style="color:#3333ff;">
-
- } else if (isInCall() ||
- outputDesc->isUsedByStrategy(STRATEGY_PHONE)) {
- device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
- } else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) {
- device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
- } else if (outputDesc->isUsedByStrategy(STRATEGY_MEDIA)) {
- device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache);
- } else if (outputDesc->isUsedByStrategy(STRATEGY_DTMF)) {
- device = getDeviceForStrategy(STRATEGY_DTMF, fromCache);
- }
-
- ......
-
- return device;
- }
-
- ......
-
-
- uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
- {
- uint32_t device = 0;
-
- if (fromCache) {
- LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]);
- return mDeviceForStrategy[strategy];
- }
-
- switch (strategy) {
- case STRATEGY_DTMF:
- if (!isInCall()) {
-
- device = getDeviceForStrategy(STRATEGY_MEDIA, false);
- break;
- }
-
-
-
- case STRATEGY_PHONE: <span style="color:#3333ff;">
- </span>
-
-
- ......
-
- break;
-
- case STRATEGY_SONIFICATION: <span style="color:#3333ff;">
-
-
-
-
- ......
-
- case STRATEGY_ENFORCED_AUDIBLE:
-
-
-
- ......
-
- case STRATEGY_MEDIA: { <span style="color:#3333ff;">
- uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
- if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
- }
-
- .......
-
- <span style="color:#3333ff;">
- <span style="color:#33cc00;"> if (device2 == 0) {
- device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_USB_AUDIO;
- }</span>
- <span style="color:#3333ff;">
-
- .......
- }</span>
(2)获取到新设备后,程序继续向下执行到updateDeviceForStrategy()函数,根据音频策略更新了相应的设备。
- <span xmlns="http://www.w3.org/1999/xhtml" style="">void AudioPolicyManagerBase::updateDeviceForStrategy()
- {
- for (int i = 0; i < NUM_STRATEGIES; i++) {
- mDeviceForStrategy[i] = <span style="color:#ff0000;">getDeviceForStrategy</span>((routing_strategy)i, false);
- }
- }</span>
由于此函数仍将调到刚刚分析的函数getDeviceForStrategy(),故不再深入分析。
(3)更新完设备后,程序继续向下执行到setOutputDevice()函数,用新获取到的设备名称作为第二个参数(第一个参数是在创建AudioPolicyManagerBase类对象时获得的输出音频通道),来设置输出设备。此函数很重要,它将调到ALSA模块(alsa_default.cpp)。
- <span xmlns="http://www.w3.org/1999/xhtml" style="">void AudioPolicyManagerBase::setOutputDevice(audio_io_handle_t output, uint32_t device, bool force, int delayMs)
- {
- LOGV("setOutputDevice() output %d device %x delayMs %d", output, device, delayMs);
- AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
-
- ......
-
-
- AudioParameter param = AudioParameter();
- param.addInt(String8(AudioParameter::keyRouting), (int)device); <span style="color:#3333ff;">
-
- </span> mpClientInterface->setParameters(mHardwareOutput, param.toString(), delayMs); <span style="color:#3333ff;">
-
- ......
- }</span>
调用函数setOutputDevice()时,明明只有两个参数,怎么函数原型却有4个参数了那。有点儿诡异吧!我也困惑了好大会儿。最后才发现此函数的声明(AudioPolicyManagerBase.h)中,最后两个参数(第三个和第四个参数)已经有了默认值。原来,C++中有了默认值的参数,在调用时可以不写出来!!!
void setOutputDevice(audio_io_handle_t output, uint32_t device, bool force = false, int delayMs = 0);
此函数的重点在于调用了函数mpClientInterface->setParameters()。
第一个参数mHardwareOutput:表示输出音频通道,
第三个参数delayMs:表示等待时间,值为默认值0。
通过前文分析可知,mpClientInterface就是AudioPolicyCompatClient类的对象,则mpClientInterface->setParameters()函数将调到AudioPolicyCompatClient类的setParameters()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">void AudioPolicyCompatClient::setParameters(audio_io_handle_t ioHandle,
- const String8& keyValuePairs,
- int delayMs)
- {
- <span style="color:#ff0000;">mServiceOps->set_parameters</span>(mService, ioHandle, keyValuePairs.string(),
- <span style="color:#3333ff;">
-
- delayMs);
- }</span>
而mServiceOps在创建AudioPolicyCompatClient类对象时,指向APS的struct audio_policy_service_ops变量aps_ops。则mServiceOps->set_parameters()函数将调到APS的aps_set_parameters()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">static void aps_set_parameters(void *service, audio_io_handle_t io_handle,
- const char *kv_pairs, int delay_ms)
- {
- AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;
-
- audioPolicyService->setParameters(io_handle, kv_pairs, delay_ms);
- }</span>
显然将调到APS类的setParameters()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="font-size:24px;">void AudioPolicyService::setParameters(audio_io_handle_t ioHandle,
- const char *keyValuePairs,
- int delayMs)
- {
- <span style="color:#ff0000;">mAudioCommandThread->parametersCommand</span>((int)ioHandle, keyValuePairs,
- delayMs);
- }</span>
mAudioCommandThread就是在创建APS类对象时,启动的一个音频命令线程。函数mAudioCommandThread->parametersCommand()将根据第二个参数产生一个设置音频参数的命令,并发给此线程的处理函数threadLoop()。
- <span xmlns="http://www.w3.org/1999/xhtml" style="">status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle,
- const char *keyValuePairs,
- int delayMs)
- {
- status_t status = NO_ERROR;
-
- AudioCommand *command = new AudioCommand();
- <span style="color:#ff0000;">command->mCommand = SET_PARAMETERS;
- </span> ParametersData *data = new ParametersData();
- data->mIO = ioHandle;
- <span style="color:#ff0000;">data->mKeyValuePairs = String8(keyValuePairs); <span style="color:#3333ff;">
- </span> command->mParam = data;
- if (delayMs == 0) {
- command->mWaitStatus = true;
- } else {
- command->mWaitStatus = false;
- }
- Mutex::Autolock _l(mLock);
- insertCommand_l(command, delayMs); <span style="color:#3333ff;">
-
- ......
-
- }
- </span>
- <span xmlns="http://www.w3.org/1999/xhtml" style="">// insertCommand_l() must be called with mLock held
- void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
- {
- ......
-
-
- if (mName != "" && <span style="color:#ff0000;">mAudioCommands</span>.isEmpty()) {
- acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string());
- }
-
-
- for (i = mAudioCommands.size()-1; i >= 0; i--) {
- <span style="color:#ff0000;">AudioCommand *command2 = mAudioCommands[i];</span>
-
- ......
-
- switch (command->mCommand) {
- <span style="color:#ff0000;">case SET_PARAMETERS</span>: {
- ParametersData *data = (ParametersData *)command->mParam;
- ParametersData *data2 = (ParametersData *)command2->mParam;
- if (data->mIO != data2->mIO) break;
- LOGV("Comparing parameter command %s to new command %s",
- data2->mKeyValuePairs.string(), data->mKeyValuePairs.string());
- AudioParameter param = AudioParameter(data->mKeyValuePairs);
- AudioParameter param2 = AudioParameter(data2->mKeyValuePairs);
- for (size_t j = 0; j < param.size(); j++) {
- String8 key;
- String8 value;
- param.getAt(j, key, value);
- for (size_t k = 0; k < param2.size(); k++) {
- String8 key2;
- String8 value2;
- param2.getAt(k, key2, value2);
- if (key2 == key) {
- param2.remove(key2);
- LOGV("Filtering out parameter %s", key2.string());
- break;
- }
- }
- }
-
-
- if (param2.size() == 0) {
- <span style="color:#ff0000;">removedCommands.add(command2);</span>
- } else {
- data2->mKeyValuePairs = param2.toString();
- }
- } break;
-
- ......
- }</span>
插入命令函数AudioCommandThread::insertCommand_l()和命令处理函数AudioCommandThread::threadLoop()的衔接点就是mAudioCommands列表。
- <span xmlns="http://www.w3.org/1999/xhtml" style="">bool AudioPolicyService::AudioCommandThread::threadLoop()
- {
- nsecs_t waitTime = INT64_MAX;
-
- mLock.lock();
- while (!exitPending())
- {
- while(!mAudioCommands.isEmpty()) {
- nsecs_t curTime = systemTime();
-
- if (mAudioCommands[0]->mTime <= curTime) {
- <span style="color:#ff0000;">AudioCommand *command = mAudioCommands[0];
- </span> mAudioCommands.removeAt(0);
- mLastCommand = *command;
-
- switch (command->mCommand) {
- ......
-
- case SET_PARAMETERS: {
- ParametersData *data = (ParametersData *)command->mParam;
- LOGV("AudioCommandThread() processing set parameters string %s, io %d",
- data->mKeyValuePairs.string(), data->mIO);
- command->mStatus = <span style="color:#ff0000;">AudioSystem::setParameters</span>(data->mIO, data->mKeyValuePairs);
- if (command->mWaitStatus) {
- command->mCond.signal();
- mWaitWorkCV.wait(mLock);
- }
- delete data;
- }break;
- ......
-
- }</span>
此线程处理函数并没有真正去设置参数,而是把设置参数的实际操作交给了函数AudioSystem::setParameters()。
- <span xmlns="http://www.w3.org/1999/xhtml" style="">status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs) {
- const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
- return <span style="color:#ff0000;">af->setParameters</span>(ioHandle, keyValuePairs);
- }</span>
又调到AudioFlinger了!
- <span xmlns="http://www.w3.org/1999/xhtml" style="">status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
- {
-
- ......
-
-
- if (!settingsAllowed()) {
- return PERMISSION_DENIED;
- }
-
-
- if (ioHandle == 0) { <span style="color:#ff0000;"><span style="color:#3333ff;">
-
-
-
- </span> AutoMutex lock(mHardwareLock);
- mHardwareStatus = AUDIO_SET_PARAMETER;
- status_t final_result = NO_ERROR;
- for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
- audio_hw_device_t *dev = <span style="color:#ff0000;">mAudioHwDevs</span>[i];
- result = <span style="color:#ff0000;">dev->set_parameters</span>(dev, keyValuePairs.string());
- final_result = result ?: final_result;
- }
-
- ......
-
- }</span>
由前面对AudioFlinger服务和AudioPolicyService服务的启动流程分析可知,mAudioHwDevs具体指在文件audio_hw_hal.cpp所实现的音频模块中定义的设备。则dev->set_parameters()函数就是adev_set_parameters()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style="">static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
- {
- struct legacy_audio_device *ladev = to_ladev(dev);
- return <span style="color:#ff0000;">ladev->hwif->setParameters</span>(String8(kvpairs));
- }</span>
同样根据前面AudioFlinger服务分析可知,ladev->hwif具体指ALSA音频类AudioHardwareALSA的对象。则函数ladev->hwif->setParameters()就是函数AudioHardwareALSA类的setParameters()函数。此函数在头文件hardware/alsa_sound/AudioHardwareALSA.h中定义。
- <span xmlns="http://www.w3.org/1999/xhtml" style="">class ALSAStreamOps
- {
- public:
- ALSAStreamOps(AudioHardwareALSA *parent, alsa_handle_t *handle);
- virtual ~ALSAStreamOps();
-
- status_t set(int *format, uint32_t *channels, uint32_t *rate);
-
- status_t <span style="color:#ff0000;">setParameters</span>(const String8& keyValuePairs);
-
- ......
- }</span>
此setParameters()函数的实现在提供了ALSA流操作接口的文件hardware/alsa_sound/ALSAStreamOps.cpp中。
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">status_t ALSAStreamOps::setParameters(const String8& keyValuePairs)
- {
- AudioParameter param = AudioParameter(keyValuePairs);
- String8 key = String8(AudioParameter::keyRouting);
- status_t status = NO_ERROR;
- int device;
- LOGV("setParameters() %s", keyValuePairs.string());
-
- if (param.getInt(key, device) == NO_ERROR) {
- AutoMutex lock(mLock);
- <span style="color:#ff0000;">mParent->mALSADevice->route</span>(mHandle, (uint32_t)device, mParent->mode());
- param.remove(key);
- }
-
- if (param.size()) {
- status = BAD_VALUE;
- }
- return status;
- }</span></span>
看到route()函数,有点儿面熟吧?!
mParent是在AudioHardwareALSA.h头文件中定义的AudioHardwareALSA类的指针对象,是在创建ALSAStreamOps类实例时初始化的。没有看到什么时候直接创建了ALSAStreamOps类的对象。不过,ALSAStreamOps类同时被AudioStreamOutALSA类和AudioStreamInALSA类继承了,则在创建这两个子类的同时将创建ALSAStreamOps类的对象。 ALSAStreamOps类的子类之一AudioStreamOutALSA类在AudioPolicyManagerBase类被创建时,打开输出音频通道的操作一路调到AudioHardwareALSA类的打开音频通道时被创建。因此,此时mParent将指向AudioHardwareALSA类,不为空。
AudioHardwareALSA * mParent;
mALSADevice也在创建AudioHardwareALSA类的对象时有了struct alsa_device_t类型的值
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">struct alsa_device_t {
- hw_device_t common;
-
- status_t (*init)(alsa_device_t *, ALSAHandleList &);
- status_t (*open)(alsa_handle_t *, uint32_t, int);
- status_t (*close)(alsa_handle_t *);
- status_t (*route)(alsa_handle_t *, uint32_t, int);
- };</span></span>
而这些ALSA函数接口已经指向了具体的函数实现(alsa_default.cpp)。则调到HAL层ALSA音频模块(alsa_default.cpp)中s_route()函数。
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">static status_t s_route(alsa_handle_t *handle, uint32_t devices, int mode)
- {
- LOGD("route called for devices %08x in mode %d...", devices, mode);
-
-
-
-
- if (handle->handle && handle->curDev == devices ) return NO_ERROR;
-
- return s_open(handle, devices, mode);
- }</span></span>
进而调到s_open()函数。前面已经对s_open()函数做过分析,这里不再重述。
3. ALSA-LIB浅述以及asound.conf配置文件的书写。
ALSA(Advanced Linux Sound Architecture)音频驱动是一个十分庞大,复杂和功能强大的音频系统,应用领域远远高于先前的OSS (Open Sound System)音频驱动。由于ALSA-DRIVER过于庞杂,给开发者使用ALSA-DRIVER带来许多不变,故ALSA-DRIVER的API ALSA-LIB应运而生。我们可以通过调用ALSA-LIB的API间接与ALSA-DRIVER打交道。
关于ALSA的更多更详尽的介绍请参阅其官网(http://www.alsa-project.org/main/index.php/Main_Page)。
前文在分析ALSA音频模块(alsa_default.cpp)时,已经提到会在s_open()函数中调到ALSA-LIB的API函数snd_pcm_open()函数。就是此函数来实际实现音频通道的打开的!
snd_pcm_open(&handle->handle, devName, direction(handle), SND_PCM_ASYNC);
函数snd_pcm_open()在文件external/alsa-lib/src/pcm/pcm.c中定义。
/**
* \brief Opens a PCM
* \param pcmp Returned PCM handle
* \param name ASCII identifier of the PCM handle
* \param stream Wanted stream
* \param mode Open mode (see #SND_PCM_NONBLOCK, #SND_PCM_ASYNC)
* \return 0 on success otherwise a negative error code
*/
int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_stream_t stream, int mode)
{
int err;
assert(pcmp && name);
err = snd_config_update();
if (err < 0)
return err;
return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 0);
}
(1)更新配置文件函数snd_config_update()。
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">
-
-
-
-
-
-
-
-
-
-
-
-
- int snd_config_update(void)
- {
- int err;
-
- #ifdef HAVE_LIBPTHREAD
- pthread_mutex_lock(&snd_config_update_mutex);
- #endif
- err = <span style="color:#ff0000;">snd_config_update_r</span>(<span style="color:#ff0000;">&snd_config</span>, <span style="color:#ff0000;">&snd_config_global_update</span>, <span style="color:#ff0000;">NULL</span>);
- #ifdef HAVE_LIBPTHREAD
- pthread_mutex_unlock(&snd_config_update_mutex);
- #endif
- return err;
- }</span></span>
又调到snd_config_update_r()函数了。
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, const char *cfgs) <span style="color:#3333ff;">
- {
- ......
-
- configs = cfgs;
- if (!configs) {
- configs = getenv(ALSA_CONFIG_PATH_VAR); <span style="color:#3333ff;">
- if (!configs || !*configs) {
- configs = ALSA_CONFIG_PATH_DEFAULT; <span style="color:#3333ff;">
-
- .......</span>
- }</span></span>
这里提到要读取配置文件/usr/share/alsa/alsa.conf,即是在源码中的文件external/alsa-lib/src/conf/alsa.conf。在解析alsa.conf配置文件的同时,将解析alsa.conf文件中所包含的文件/etc/asound.conf。据ALSA官方网站介绍,asound.conf是全局配置文件。
网上关于asound.conf介绍很丰富了,其官网网址如下:http://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html。
在android的源码中,也有现成的例子可以参考,比如三星的一个音频配置文件device/samsung/crespo/asound.conf。
pcm节点真是对具体的音频设备的硬件参数的配置。可以配置的硬件参数如下:
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">pcm.name {
- type hw # Kernel PCM
- card INT/STR # Card name (string) or number (integer)
- [device INT] # Device number (default 0)
- [subdevice INT] # Subdevice number (default -1: first available)
- [sync_ptr_ioctl BOOL] # Use SYNC_PTR ioctl rather than the direct mmap access for control structures
- [nonblock BOOL] # Force non-blocking open mode
- [format STR] # Restrict only to the given format
- [channels INT] # Restrict only to the given channels
- [rate INT] # Restrict only to the given rate
- }</span></span>
仅举一例
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">pcm.AndroidCapture_Usb-audio_normal { <span style="color:#3333ff;">// 此PCM节点为录音设备的PCM节点
- </span> type hooks
- slave.pcm "hw:1,0" <span style="color:#3333ff;">
- }</span></span>
AndroidCapture_Usb-audio_normal:前缀"AndroidCapture"和扩展"_normal"是固定的,后缀"_Usb-audio"是要在alsa_default.cpp中设置的。
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">
-
- static const device_suffix_t deviceSuffix[] = {
- {android_audio_legacy::AudioSystem::DEVICE_OUT_EARPIECE, "_Earpiece"},
- {android_audio_legacy::AudioSystem::DEVICE_OUT_SPEAKER, "_Speaker"},
- {android_audio_legacy::AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "_Bluetooth"},
- {android_audio_legacy::AudioSystem::DEVICE_OUT_WIRED_HEADSET, "_Headset"},
- {android_audio_legacy::AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "_Bluetooth-A2DP"},
- <span style="color:#33cc00;"> {android_audio_legacy::AudioSystem::DEVICE_OUT_USB_AUDIO, "_Usb-audio"},
- {android_audio_legacy::AudioSystem::DEVICE_IN_USB_AUDIO, "_Usb-audio"},
- </span>};</span></span>
在此配置文件中,不要指定USB AUDIO音频设备的声道数和采样率。因为不同的USB AUDIO音频设备具有不同的声道数channels或采样率rate。当要打开输入音频设备进行录音时。就需要根据实际使用的USB AUDIO音频设备的声道数或采样率这两个硬件参数进行重新设置(如果不改变channels,当channels值与实际设备的不一致时,将打不开音频设备;如果不改变rate,当rate值与实际设备的不一致时,声音将失真)。而打开输出音频通道时,采用默认值是没有问题的。
4. 重新获取USB AUDIO设备的硬件参数。
前文已经提到,可在alsa_default.cpp文件中的s_open()函数在调用setHardwareParams()函数设置硬件参数之前重设声道数channels或采样率rate。
4.1 有三种方法获得实际使用的USB AUDIO设备的硬件参数信息。
第一种方法: 自己写个字符串处理函数,从文件/proc/asound/card1/stream0中读取(只需读取录音部分的)。
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">$ cat /proc/asound/card1/stream0
- SAGE Technology SAGE AirMouse at usb-0000:00:1d.3-2, full speed : USB Audio
-
- Playback:
- Status: Stop
- Interface 2
- Altset 1
- Format: S16_LE
- Channels: 1
- Endpoint: 6 OUT (NONE)
- Rates: 16000
-
- Capture:
- Status: Stop
- Interface 1
- Altset 1
- Format: S16_LE
- Channels: 1 <span style="color:#3333ff;">
- </span> Endpoint: 5 IN (NONE)
- Rates: 16000 <span style="color:#3333ff;">
- </span></span>
第二种方法:自己写代码,从内核USB驱动中读取。主要涉及到如下文件(也是我们项目所采用的方法)
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">sound/usb/card.c
- drivers/hid/hid-input.c</span></span>
第三种方法:因为是直接调用系统现有接口,是比较可靠的方法:调用ALSA-LIB库的API。主要涉及到如下文件
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">external/alsa-lib/src/pcm/pcm_rate.c</span></span>
4.2 把输入音频通道的声道数和采样率改回默认值
当打开指定PCM节点的音频设备失败后,系统会去打开默认的音频通道(alsa_default.cpp)。
- <span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style=""> for (;;) {
-
-
-
- err = snd_pcm_open(&handle->handle, devName, direction(handle),
-
-
- .......
-
- }
-
- if (err < 0) {
- <span style="color:#3333ff;">
- </span>
-
- devName = "default";
- err = snd_pcm_open(&handle->handle, devName, direction(handle), 0);
- }</span></span>
PS:只需调用setDeviceConnectionState()函数打开输出音频通道,不要去打开输入音频通道。因为用户使用的音频聊天工具(如Skypee)会自动打开。我们只需在打开USB AUDIO输出音频通道时,强制设置USB MIC为输入音频通道。这样就能实现用USB MIC 进行语音聊天了!
结贴~~~~~~~