转载请注明出处:/adits/article/details/8242146
开发环境简介
1. 主机系统: Unbuntu10.102. android系统版本: 4.0.3(Linux kernel 3.0.8)
综述
android的音频系统非常庞大复杂:涉及到java应用程序,java框架层,JNI,本地服务(AudioFlinger和AudioPolicyService),硬件抽象层HAL,ALSA-LIB和ALSA-DRIVER。本文将先分析音频系统的启动与模块加载流程,并具体分析一个JAVA API的调用流程;最后在此基础上自然地为android系统添加USBAUDIO设备的放音和录音功能。
全文可分为如下几大部分:
1. 本地服务的启动流程分析。
1.1 AudioFlinger启动流程及其所涉及的HAL层模块启动流程分析。
1.2 AudioPolicyService启动流程及其所涉及的HAL层模块启动流程分析。
2. JAVA API setDeviceConnectionState()调用流程详解,同时为android系统添加USBAUDIO设备的放音和录音功能。
3. ALSA-LIB浅述以及asound.conf配置文件的书写。
4. 重新获取USBAUDIO设备的硬件参数。
详述
1. 本地服务的启动流程分析。
AudioFlinger和AudioPolicyService两大音频服务都是在android系统启动时就启动的。当linux kenerl启动完成后,会启动Android的init进程(system/core/init/init.c)。
[cpp]view plaincopy print? <spanstyle="font-size:24px;">intmain(intargc,char**argv) { ..... init_parse_config_file("/<spanstyle="color:#ff0000;">init.rc</span>"); ..... }</span>
init.rc文件中保存了许多系统启动时需要启动的服务。其中就有多媒体服务mediaserver的启动:
[plain]view plaincopy print? <spanstyle="font-size:24px;">servicemedia/system/bin/mediaserver</span>
此服务在文件frameworks/base/media/mediaserver/main_mediaserver.cpp中定义,而音频子系统的两大本地服务AudioFlinger和AudioPolicyService就是在此启动的。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">intmain(intargc,char**argv) { ..... <spanstyle="color:#ff0000;">AudioFlinger::instantiate()</span>;<spanstyle="color:#3333ff;">//实例化AudioFlinger</span> ..... <spanstyle="color:#ff0000;">AudioPolicyService::instantiate()</span>;<spanstyle="color:#3333ff;">//实例化AudioPolicyService</span> ..... }</span>
1.1 AudioFlinger启动流程及其所涉及的HAL层模块启动流程分析。
根据上文分析,将调用AudioFlinger::instantiate()函数实例化AudioFlinger。但是AudioFlinger.cpp中并没有找到此函数,那必然在其父类中。AudioFlinger类有很多父类,一时难以确定instantiate()到底在哪个父类中定义的。直接搜索吧!
grep -rn "instantiate" frameworks/base/
很快找到instantiate()函数的定义处在./frameworks/base/include/binder/BinderService.h头文件中
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">template<typenameSERVICE> classBinderService { public: staticstatus_tpublish(){ sp<IServiceManager>sm(defaultServiceManager()); returnsm->addService(String16(<spanstyle="color:#ff0000;">SERVICE::getServiceName</span>()),<spanstyle="color:#ff0000;">newSERVICE</span>()); ...... staticvoidinstantiate(){publish();} ..... } }</span>
这里用到了模板,需要确定SERVICE是什么东东。
AudioFlinger类是在AudioFlinger.h中定义的,而恰好包含了头文件BinderService.h。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">classAudioFlinger: public<spanstyle="color:#ff0000;">BinderService<AudioFlinger></span>, publicBnAudioFlinger { friendclassBinderService<AudioFlinger>; public: staticcharconst*getServiceName(){return"media.audio_flinger";} ..... }</span>
原来AudioFlinger类继承了BinderService类,同时把自己(AudioFlinger)传递给SERVICE。而addService函数第一个参数调用了AudioFlinger类的静态成员函数getServiceName()获取AudioFlinger的服务名称;其第二个参数便是创建了一个AudioFlinger的实例。至此,明白了实例化函数instantiate()就是要向服务管理器注册的服务是AudioFlinger。
既然此时实例化了AudioFlinger,那么看看AudioFlinger类的构造函数具体做了哪些初始化工作。
[cpp]view plaincopy print? <spanxmlns="/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()函数的名字,知晓,应当是加载音频接口的。
[cpp]view plaincopy print? <spanstyle="font-size:24px;">staticintload_audio_interface(constchar*if_name,consthw_module_t**mod, audio_hw_device_t**dev) { ...... rc=<spanstyle="color:#ff0000;">hw_get_module_by_class</span>(<spanstyle="color:#ff0000;">AUDIO_HARDWARE_MODULE_ID</span>,if_name,mod); if(rc) gotoout; rc=<spanstyle="color:#ff0000;">audio_hw_device_open</span>(*mod,<spanstyle="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,其中包含了许多函数接口(函数指针):
[cpp]view plaincopy print? <spanstyle="font-size:24px;">structaudio_hw_device{ structhw_device_tcommon; /** *usedbyaudioflingertoenumeratewhatdevicesaresupportedby *eachaudio_hw_deviceimplementation. * *Returnvalueisabitmaskof1ormorevaluesofaudio_devices_t */ uint32_t(*<spanstyle="color:#ff0000;">get_supported_devices</span>)(conststructaudio_hw_device*dev); /** *checktoseeiftheaudiohardwareinterfacehasbeeninitialized. *returns0onsuccess,-ENODEVonfailure. */ int(*<spanstyle="color:#ff0000;">init_check</span>)(conststructaudio_hw_device*dev); ...... /*set/getglobalaudioparameters*/ int(*<spanstyle="color:#ff0000;">set_parameters</span>)(structaudio_hw_device*dev,constchar*kv_pairs); ..... /**Thismethodcreatesandopenstheaudiohardwareoutputstream*/ int(*<spanstyle="color:#ff0000;">open_output_stream</span>)(structaudio_hw_device*dev,uint32_tdevices, int*format,uint32_t*channels, uint32_t*sample_rate, structaudio_stream_out**out); ...... /**Thismethodcreatesandopenstheaudiohardwareinputstream*/ int(*open_input_stream)(structaudio_hw_device*dev,uint32_tdevices, int*format,uint32_t*channels, uint32_t*sample_rate, audio_in_acoustics_tacoustics, structaudio_stream_in**stream_in); ..... }</span>
ID为AUDIO_HARDWARE_MODULE_ID的音频模块到底在哪儿定义的那?既然是HAL层模块,必定在hardware目录下定义的
[plain]view plaincopy print? <spanstyle="font-size:24px;">$grep-rnAUDIO_HARDWARE_MODULE_IDhardware/ 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: #defineAUDIO_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头文件中定义,函数体如下:[cpp]view plaincopy print? <spanstyle="font-size:24px;">/**convenienceAPIforopeningandclosingasupporteddevice*/ staticinlineintaudio_hw_device_open(conststructhw_module_t*module, structaudio_hw_device**device) { returnmodule->methods->open(module,AUDIO_HARDWARE_INTERFACE, (structhw_device_t**)device); }</span>
struct hw_module_t是在hardware/libhardware/include/hardware/hardware.h头文件中定义的,其中嵌套了struct hw_module_methods_t。此结构体很简单,只有一个函数指针open
[cpp]view plaincopy print? <spanstyle="font-size:24px;">typedefstructhw_module_methods_t{ /**Openaspecificdevice*/ int(*open)(conststructhw_module_t*module,constchar*id, structhw_device_t**device); }hw_module_methods_t;</span>
在确定具体模块后,很容易确定open函数指针的具体实现
[cpp]view plaincopy print? <spanstyle="font-size:24px;">structlegacy_audio_moduleHAL_MODULE_INFO_SYM={ module:{ common:{ tag:HARDWARE_MODULE_TAG, version_major:1, version_minor:0, id:AUDIO_HARDWARE_MODULE_ID, name:"LEGACYAudioHWHAL", author:"TheAndroidOpenSourceProject", methods:&<spanstyle="color:#ff0000;">legacy_audio_module_methods</span>, dso:NULL, reserved:{0}, }, }, };</span>
[cpp]view plaincopy print? <spanstyle="font-size:24px;">staticstructhw_module_methods_tlegacy_audio_module_methods={ open:<spanstyle="color:#ff0000;">legacy_adev_open</span> };</span>
open()的实现就是legacy_adev_open()函数了!
[cpp]view plaincopy print? <spanstyle="font-size:24px;">staticintlegacy_adev_open(consthw_module_t*module,constchar*name, hw_device_t**device) { structlegacy_audio_device*ladev; intret; if(strcmp(name,AUDIO_HARDWARE_INTERFACE)!=0)//看来对应宏AUDIO_HARDWARE_INTERFACE,除了用来判断希望打开的是否音频硬件接口外,并没有做什么更多的事情 return-EINVAL; ..... ladev->device.get_supported_devices=adev_get_supported_devices; ladev->device.init_check=<spanstyle="color:#ff0000;">adev_init_check</span>; ..... ladev->device.open_output_stream=<spanstyle="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=<spanstyle="color:#ff0000;">createAudioHardware</span>(); ..... <spanstyle="color:#ff0000;">*device=&ladev->mon;</span><spanstyle="color:#3333ff;">//将当前设备信息层层返回给回调函数,最后返回到 //load_audio_interface()函数,并保存在 //mAudioHwDevs模板变量中,供AudioFlinger类使用。 </span> return0; }</span>
这里主要做了一些初始化工作,即给函数指针提供具体实现函数;但createAudioHardware()应该做了更多的事情。
先从函数createAudioHardware()的返回值入手。struct legacy_audio_device的定义如下:
[cpp]view plaincopy print? <spanstyle="font-size:24px;">structlegacy_audio_device{ structaudio_hw_devicedevice; structAudioHardwareInterface*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同在一个名字空间中。
[cpp]view plaincopy print? <spanstyle="font-size:24px;">namespaceandroid_audio_legacy{ ..... extern"C"AudioHardwareInterface*createAudioHardware(void); };//namespaceandroid</span>
经搜索,发现createAudioHardware()函数有四处定义。
[cpp]view plaincopy print? <spanstyle="font-size:24px;">$grep-rncreateAudioHardwarehardware/--exclude-dir=.svn hardware/alsa_sound/AudioHardwareALSA.cpp:45: android_audio_legacy::AudioHardwareInterface*createAudioHardware(void){ hardware/msm7k/libaudio-qsd8k/AudioHardware.cpp:: 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类对象。
[cpp]view plaincopy print? <spanstyle="font-size:24px;">returnnewAudioHardwareALSA();</span>
AudioHardwareALSA类的构造函数如下:
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">AudioHardwareALSA::AudioHardwareALSA(): mALSADevice(0), mAcousticDevice(0) { ...... interr=<spanstyle="color:#ff0000;">hw_get_module</span>(<spanstyle="color:#ff0000;">ALSA_HARDWARE_MODULE_ID</span>, (hw_module_tconst**)<spanstyle="color:#ff0000;">&module</span>); if(err==0){ hw_device_t*device; err=<spanstyle="color:#ff0000;">module->methods->open</span>(module,<spanstyle="color:#ff0000;">ALSA_HARDWARE_NAME</span>,&device); if(err==0){ mALSADevice=(alsa_device_t*)device; <spanstyle="color:#ff0000;">mALSADevice->init</span>(<spanstyle="color:#ff0000;">mALSADevice</span>,<spanstyle="color:#ff0000;">mDeviceList</span>); ..... err=hw_get_module(ACOUSTICS_HARDWARE_MODULE_ID, (hw_module_tconst**)&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中。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">$grep-rnALSA_HARDWARE_MODULE_IDhardware/--exclude-dir=.svn hardware/alsa_sound/AudioHardwareALSA.h:39:#defineALSA_HARDWARE_MODULE_ID"alsa" hardware/alsa_sound/alsa_default.cpp:59: id:ALSA_HARDWARE_MODULE_ID, hardware/alsa_sound/AudioHardwareALSA.cpp:150: interr=hw_get_module(ALSA_HARDWARE_MODULE_ID,</span>
则很快找到此模块的具体内容如下:
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">extern"C"consthw_module_tHAL_MODULE_INFO_SYM={ tag:HARDWARE_MODULE_TAG, version_major:1, version_minor:0, id:ALSA_HARDWARE_MODULE_ID, name:"ALSAmodule", author:"WindRiver", methods:&<spanstyle="color:#ff0000;">s_module_methods</span>, dso:0, reserved:{0,}, };</span>
s_module_methods函数的实现如下:
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">statichw_module_methods_ts_module_methods={ open:s_device_open };</span>
s_device_open函数的实现如下:
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticints_device_open(consthw_module_t*module,constchar*name,<spanstyle="color:#3333ff;">//有些困惑, //此open函数实现中并没有对调用者传递下来的name(ALSA_HARDWARE_NAME)作如何处理。</span> hw_device_t**device)<spanstyle="color:#3333ff;"><spanstyle="background-color:rgb(255,255,255);">//device存储返回的模块信息</span></span> { alsa_device_t*dev; dev=(alsa_device_t*)malloc(sizeof(*dev)); if(!dev)return-ENOMEM; memset(dev,0,sizeof(*dev)); /*initializetheprocs*/ 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=<spanstyle="color:#ff0000;">s_init</span>; dev->open=<spanstyle="color:#ff0000;">s_open</span>; dev->close=s_close; dev->route=<spanstyle="color:#ff0000;">s_route</span>; <spanstyle="color:#ff0000;">*device=&dev->common;</span><spanstyle="color:#3333ff;">//把此模块信息返回给调用者</span> return0; }</span>
经过上述分析,知道了module->methods->open函数具体调用流程了。
然后对ALSA硬件抽象层模块做了初始化的工作。
这里用到一个结构体变量mALSADevice,它在头文件hardware/alsa_sound/AudioHardwareALSA.h中定义的struct alsa_device_t变量。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">structalsa_device_t{ hw_device_tcommon; 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()函数中。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticstatus_ts_init(alsa_device_t*module,<spanstyle="color:#ff0000;">ALSAHandleList</span>&list) { list.clear(); snd_pcm_uframes_tbufferSize=<spanstyle="color:#ff0000;">_defaultsOut</span>.bufferSize; for(size_ti=1;(bufferSize&~i)!=0;i<<=1) bufferSize&=~i; _defaultsOut.module=module; _defaultsOut.bufferSize=bufferSize; <spanstyle="color:#ff0000;">list.push_back(_defaultsOut);</span> bufferSize=<spanstyle="color:#ff0000;">_defaultsIn</span>.bufferSize; ..... <spanstyle="color:#ff0000;">list.push_back(_defaultsIn);</span> ..... }</span>
这里会把_defaultsOut和_defaultsIn东东保存在ALSA句柄列表ALSAHandleList中。
首先需要明确_defaultsOut和_defaultsIn具体是什么东东。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticalsa_handle_t_defaultsOut={ module:0, devices:android_audio_legacy::AudioSystem::DEVICE_OUT_ALL,<spanstyle="color:#3333ff;">//支持的所有 //输出音频设备</span> curDev:0, curMode:0, handle:0,<spanstyle="color:#3333ff;">//PCM节点 </span>format:SND_PCM_FORMAT_S16_LE,//AudioSystem::PCM_16_BIT channels:2, sampleRate:DEFAULT_SAMPLE_RATE, latency:200000,//DesiredDelayinusec bufferSize:DEFAULT_SAMPLE_RATE/5,//DesiredNumberofsamples modPrivate:0, }; staticalsa_handle_t_defaultsIn={ module:0, devices:android_audio_legacy::AudioSystem::DEVICE_IN_ALL,<spanstyle="color:#3333ff;">//支持的所有 //输入音频设备</span> curDev:0, curMode:0, handle:0,<spanstyle="color:#3333ff;">//PCM节点</span> format:SND_PCM_FORMAT_S16_LE,//AudioSystem::PCM_16_BIT channels:2,<spanstyle="color:#3333ff;">//声道数:1表示单声道,2表示立体声。如果与实际使用的USBAUDIO设备参数 //的不一致,将导致USBAUDIO设备不能使用。</span> sampleRate:DEFAULT_SAMPLE_RATE,<spanstyle="color:#3333ff;">//采样率,如果与实际使用的USBAUDIO设备参数 //的不一致,将导致声音失真 <spanstyle="color:#000000;">latency:250000,//DesiredDelayinusec</span></span> bufferSize:2048,//DesiredNumberofsamples modPrivate:0, };</span>
那ALSAHandleList又是什么东东?
ALSAHandleList在头文件hardware/alsa_sound/AudioHardwareALSA.h中定义的List模板变量。
typedef List<alsa_handle_t> ALSAHandleList;
原来就是struct asla_handle_t的一个列表而已,而_defaultsOut和_defaultsIn正是这样的结构体变量。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">structalsa_handle_t{ alsa_device_t*module; uint32_tdevices; uint32_tcurDev; intcurMode; snd_pcm_t*handle;<spanstyle="color:#3333ff;">//PCM节点 </span>snd_pcm_format_tformat; <spanstyle="color:#ff0000;">uint32_tchannels; uint32_tsampleRate;</span> unsignedintlatency;//Delayinusec unsignedintbufferSize;//Sizeofsamplebuffer void*modPrivate; };</span>
ALSA硬件抽象层正是这样获得了输出音频通道和输入音频通道的相关初始化硬件参数,以后在使用中并不试图改变这些硬件参数(针对真能手机和平板来说,也却是不需要改变)。因此,在扩展android系统功能,为其添加对USB AUDIO设备的支持时,就不得不考虑时事改变channels和sampleRate这两个硬件参数的值。
至此,AudioFlinger服务首次启动过程分析完毕!
1.2 AudioPolicyService启动流程及其所涉及的HAL层模块启动流程分析。
AudioPolicyService服务的启动流程类似于AudioFlinger服务的启动过程,将简要分析。
先看下AudioPolicyService类的定义(AudioPolicyService.h)(提供此类的定义,主要是为下面instantiate()函数服务的):
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">classAudioPolicyService: public<spanstyle="color:#ff0000;">BinderService<AudioPolicyService></span>,<spanstyle="color:#3333ff;">//继承了BinderService类, //并把自己(AudioPolicyService)传递给 //BinderService。 </span>publicBnAudioPolicyService, //publicAudioPolicyClientInterface, publicIBinder::DeathRecipient { friendclassBinderService<AudioPolicyService>; public: //forBinderService staticconstchar*<spanstyle="color:#ff0000;">getServiceName</span>(){return"media.audio_policy";} ..... }</span>
根据前面的分析,晓得将通过调用如下代码启动AudioPolicyService服务。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">AudioPolicyService::instantiate();</span>
此代码最后将调到AudioPolicyService类的构造函数
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">AudioPolicyService::AudioPolicyService() :BnAudioPolicyService(),mpAudioPolicyDev(NULL),mpAudioPolicy(NULL) { charvalue[PROPERTY_VALUE_MAX]; conststructhw_module_t*module; intforced_val; intrc; Mutex::Autolock_l(mLock); //starttoneplaybackthread mTonePlaybackThread=newAudioCommandThread(String8("")); //startaudiocommandsthread mAudioCommandThread=new<spanstyle="color:#ff0000;">AudioCommandThread</span>(String8("ApmCommandThread")); /*instantiatetheaudiopolicymanager*/ rc=<spanstyle="color:#ff0000;">hw_get_module</span>(<spanstyle="color:#ff0000;">AUDIO_POLICY_HARDWARE_MODULE_ID</span>,&module); if(rc) return; rc=<spanstyle="color:#ff0000;">audio_policy_dev_open</span>(module,<spanstyle="color:#ff0000;">&mpAudioPolicyDev</span>); LOGE_IF(rc,"couldn'topenaudiopolicydevice(%s)",strerror(-rc)); if(rc) return; rc=<spanstyle="color:#ff0000;">mpAudioPolicyDev->create_audio_policy</span>(mpAudioPolicyDev,<strongstyle="background-color:rgb(192,192,192);"><spanstyle="color:#ff0000;">&aps_ops</span></strong>,this, <spanstyle="color:#ff0000;">&mpAudioPolicy</span>); LOGE_IF(rc,"couldn'tcreateaudiopolicy(%s)",strerror(-rc)); if(rc) return; rc=<spanstyle="color:#ff0000;">mpAudioPolicy->init_check</span>(mpAudioPolicy); ..... }</span>
(1)首先开启了放音线程和音频命令线程。这些工作都是通过创建AudioCommandThread线程类对象完成。
AudioCommandThread类在头文件frameworks/base/services/audioflinger/AudioPolicyService.h中定义
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">classAudioCommandThread:publicThread{</span>
是AudioPolicyService类的私有子类。
AudioCommandThread线程类创建了对象后,将进入死循环中,等待要处理的事件传来。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">boolAudioPolicyService::AudioCommandThread::threadLoop() { nsecs_twaitTime=INT64_MAX; mLock.lock(); while(!exitPending()) { while(!mAudioCommands.isEmpty()){ ..... switch(command->mCommand){ ..... <spanstyle="color:#ff0000;">caseSET_PARAMETERS</span>:{ ParametersData*data=(ParametersData*)command->mParam; LOGV("AudioCommandThread()processingsetparametersstring%s,io%d", data->mKeyValuePairs.string(),data->mIO); command->mStatus=<spanstyle="color:#ff0000;">AudioSystem::setParameters</span>(data->mIO,data->mKeyValuePairs); if(command->mWaitStatus){ command->mCond.signal(); mWaitWorkCV.wait(mLock); } deletedata; }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子模块的具体实现。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">$grep-rnAUDIO_POLICY_HARDWARE_MODULE_IDhardware/--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中定义的。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">structaudio_policy_device{ structhw_device_tcommon; int(*create_audio_policy)(conststructaudio_policy_device*device, structaudio_policy_service_ops<spanstyle="color:#ff0000;background-color:rgb(153,153,153);">*<strong>aps_ops</strong></span>, void*service, structaudio_policy**ap); ..... }</span>
最后看下audio_policy_dev_open()函数的实现
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;"><spanxmlns="/1999/xhtml"style="">/**convenienceAPIforopeningandclosingasupporteddevice*/ staticinlineintaudio_policy_dev_open(consthw_module_t*module, structaudio_policy_device**device) { returnmodule->methods->open(module,AUDIO_POLICY_INTERFACE, (hw_device_t**)device); }</span></span>
由上述分析可知,open函数指针就指向legacy_ap_dev_open()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">staticintlegacy_ap_dev_open(consthw_module_t*module,constchar*name, hw_device_t**device)<spanstyle="color:#3333ff;">//参数device保存返回的模块信息</span> { structlegacy_ap_device*dev; if(strcmp(name,AUDIO_POLICY_INTERFACE)!=0)<spanstyle="color:#3333ff;">//参数name(AUDIO_POLICY_INTERFACE) //就这点用处 </span>return-EINVAL; dev=(structlegacy_ap_device*)calloc(1,sizeof(*dev)); if(!dev) return-ENOMEM; dev->mon.tag=HARDWARE_DEVICE_TAG; dev->mon.version=0; dev->mon.module=const_cast<hw_module_t*>(module); dev->mon.close=legacy_ap_dev_close; dev->device.create_audio_policy=<spanstyle="color:#ff0000;">create_legacy_ap</span>; dev->device.destroy_audio_policy=destroy_legacy_ap; <spanstyle="color:#ff0000;">*device=&dev->mon;</span><spanstyle="color:#3333ff;">//将当前模块具体信息赋值给device,并回馈给调用者</span> return0; }</span></span>
(4)再接下来调用的mpAudioPolicyDev->create_audio_policy()函数指针具体就是create_legacy_ap()。
第二个参数&aps_ops是struct audio_policy_service_ops变量,是APS(AudioPolicyService)的操作接口,并且传递的是aps_ops的地址,则被调用者使用的将是在APS中函数接口的实现。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">namespace{ structaudio_policy_service_opsaps_ops={ <spanstyle="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, <spanstyle="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, }; };//namespace<unnamed></span></span>
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中定义,将要用到的接口如下:
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">structaudio_policy{ /* *configurationfunctions */ /*indicateachangeindeviceconnectionstatus*/ int(*set_device_connection_state)(structaudio_policy*pol, audio_devices_tdevice, audio_policy_dev_state_tstate, constchar*device_address); ..... /*checkproperinitialization*/ int(*init_check)(conststructaudio_policy*pol); ..... }</span></span>
接下来看看create_audio_policy()函数指针的具体实现:
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">staticintcreate_legacy_ap(conststructaudio_policy_device*device, structaudio_policy_service_ops*aps_ops, void*service, structaudio_policy**ap) { structlegacy_audio_policy*lap; intret; if(!service||!aps_ops) return-EINVAL; lap=(structlegacy_audio_policy*)calloc(1,sizeof(*lap)); if(!lap) return-ENOMEM; lap->policy.set_device_connection_state=<spanstyle="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=<spanstyle="color:#ff0000;">ap_start_input</span>; lap->policy.stop_input=ap_stop_input; lap->policy.release_input=ap_release_input; ..... <spanstyle="color:#ff0000;"> lap->service=service;<spanstyle="color:#3333ff;">//APS</span> lap->aps_ops=aps_ops;<spanstyle="color:#3333ff;">//在APS中实现</span> lap->service_client= newAudioPolicyCompatClient(aps_ops,service);</span> if(!lap->service_client){ ret=-ENOMEM; gotoerr_new_compat_client; } <spanstyle="color:#ff0000;"> lap->apm=createAudioPolicyManager(lap->service_client);</span> ...... <spanstyle="color:#ff0000;">*ap=&lap->policy;</span><spanstyle="color:#3333ff;">//将当前音频策略的配置的地址赋值给*ap,并返回给mpAudioPolicy</span> ...... }</span></span>
此函数中创建了重要对象:AudioPolicyCompatClient类对象和createAudioPolicyManager函数创建音频策略管理器,需要分析下。
先分析AudioPolicyCompatClient类对象的创建。此类在头文件hardware/libhardware_legacy/audio/AudioPolicyCompatClient.h中定义。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">namespaceandroid_audio_legacy{ classAudioPolicyCompatClient:publicAudioPolicyClientInterface{ <spanstyle="color:#3333ff;">//父类是AudioPolicyClientInterface,与下文中提到的 //AudioPolicyManagerBase:: //AudioPolicyManagerBase(AudioPolicyClientInterface*clientInterface)类型一致</span> public: AudioPolicyCompatClient(structaudio_policy_service_ops*serviceOps, void*service): <spanstyle="color:#ff0000;">mServiceOps(serviceOps),mService(service)</span>{}<spanstyle="color:#3333ff;">//serviceOps=aps_ops, //service=this(APS)</span> ...... private: structaudio_policy_service_ops*mServiceOps; void*mService; ...... }</span></span>
此构造函数主要初始化两个私有化变量mServiceOps和mService,并把创建的对象作为函数createAudioPolicyManager的唯一参数,来创建音频策略管理器。
然而,函数createAudioPolicyManager有多个定义,搜索结果如下:
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">$grep-rncreateAudioPolicyManagerhardware/--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类为例分析
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">AudioPolicyManagerALSA::AudioPolicyManagerALSA( android_audio_legacy::AudioPolicyClientInterface*<spanstyle="color:#ff0000;">clientInterface</span>) :AudioPolicyManagerBase(<spanstyle="color:#ff0000;">clientInterface</span>)<spanstyle="color:#3333ff;">//clientInterface正是 //AudioPolicyCompatClient类对象,而AudioPolicyCompatClient类具体实现了 //AudioPolicyClientInterface类的需函数接口 </span>{ } AudioPolicyManagerBase::AudioPolicyManagerBase( AudioPolicyClientInterface*clientInterface) : #ifdefAUDIO_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) { <spanstyle="color:#ff0000;">mpClientInterface=clientInterface;</span><spanstyle="color:#3333ff;">//mpClientInterface:将用它回调到APS</span> for(inti=0;i<AudioSystem::NUM_FORCE_USE;i++){ mForceUse[i]=AudioSystem::FORCE_NONE; } initializeVolumeCurves(); //devicesavailablebydefaultarespeaker,earpieceandmicrophone mAvailableOutputDevices=AudioSystem::DEVICE_OUT_EARPIECE| AudioSystem::DEVICE_OUT_SPEAKER;<spanstyle="color:#3333ff;">//可用的输出音频设备</span> mAvailableInputDevices=AudioSystem::DEVICE_IN_BUILTIN_MIC;<spanstyle="color:#3333ff;">//可用的输入音频设备</span> ...... <spanstyle="color:#ff0000;">mHardwareOutput</span>=<spanstyle="color:#cc0000;">mpClientInterface->openOutput</span>(&outputDesc->mDevice, &outputDesc->mSamplingRate, &outputDesc->mFormat, &outputDesc->mChannels, &outputDesc->mLatency, outputDesc->mFlags); ...... <spanstyle="color:#ff0000;">setOutputDevice</span>(mHardwareOutput,(uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true); ...... } </span></span>
mpClientInterface->openOutput()函数先回掉到AudioPolicyCompatClient类的openOutput()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">audio_io_handle_tAudioPolicyCompatClient::openOutput(uint32_t*pDevices, uint32_t*pSamplingRate, uint32_t*pFormat, uint32_t*pChannels, uint32_t*pLatencyMs, AudioSystem::output_flagsflags) { return<spanstyle="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()函数:
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticaudio_io_handle_taps_open_output(void*service, uint32_t*pDevices, uint32_t*pSamplingRate, uint32_t*pFormat, uint32_t*pChannels, uint32_t*pLatencyMs, audio_policy_output_flags_tflags) { sp<IAudioFlinger>af=AudioSystem::get_audio_flinger(); if(af==NULL){ LOGW("%s:couldnotgetAudioFlinger",__func__); return0; } return<spanstyle="color:#ff0000;">af->openOutput</span>(pDevices,pSamplingRate,pFormat,pChannels, pLatencyMs,flags); }</span>
不难看出,将调到AudioFlinger的openOutput()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">intAudioFlinger::openOutput(uint32_t*pDevices, uint32_t*pSamplingRate, uint32_t*pFormat, uint32_t*pChannels, uint32_t*pLatencyMs, uint32_tflags) { ...... audio_hw_device_t*outHwDev; ...... outHwDev=findSuitableHwDev_l(*pDevices); if(outHwDev==NULL) return0; status=<spanstyle="color:#ff0000;">outHwDev->open_output_stream</span>(outHwDev,*pDevices,(int*)&format, &channels,&samplingRate,&outStream); ...... return0; }</span>
struct audio_hw_device_t是在头文件hardware/libhardware/include/hardware/audio.h中定义的一个结构体。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">typedefstructaudio_hw_deviceaudio_hw_device_t;</span>
前面在分析AudioFlinger类的load_audio_interface函数时,已经分析过struct audio_hw_device。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">audio_hw_device_t*AudioFlinger::findSuitableHwDev_l(uint32_tdevices) { /*firstmatchingHWdeviceisreturned*/ for(size_ti=0;i<mAudioHwDevs.size();i++){//前文分析过,mAudioHwDevs变量保存了HAL层可用音频设备 audio_hw_device_t*dev=<spanstyle="color:#ff0000;">mAudioHwDevs</span>[i]; if((<spanstyle="color:#ff0000;">dev->get_supported_devices</span>(dev)&devices)==devices) returndev; } returnNULL; }</span>
由前文分析可知,此处的get_supported_devices()函数指针将具体调到audio_hw_hal.cpp文件中的adev_get_supported_devices(),如下所示,这里列出了系统所支持的所有输出/输入音频设备。因此,我们要也要仿照此音频设备的定义名称,在这里添加USB AUDIO音频设备的名称,以及它们在别处的定义。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticuint32_tadev_get_supported_devices(conststructaudio_hw_device*dev) { /*XXX:TheoldAudioHardwareInterfaceinterfaceisnotsmartenoughto *tellusthis,sowe'lllieandbasicallytellAFthatwesupportthe *belowinput/outputdevicesandcrossourfingers.Todothingsproperly, *audiohardwareinterfacesthatneedadvancedfeatures(likethis)should *converttothenewHALinterfaceandnotusethiswrapper.*/ return(/*OUT*/ 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| /*IN*/ 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()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticintadev_open_output_stream(structaudio_hw_device*dev, uint32_tdevices, int*format, uint32_t*channels, uint32_t*sample_rate, structaudio_stream_out**stream_out) { structlegacy_audio_device*ladev=to_ladev(dev); status_tstatus; structlegacy_stream_out*out; intret; out=(structlegacy_stream_out*)calloc(1,sizeof(*out)); if(!out) return-ENOMEM; out->legacy_out=<spanstyle="color:#ff0000;">ladev->hwif->openOutputStream</span>(devices,format,channels, sample_rate,&status); ...... }</span>
由前文分析可知,ladev->hwif具体指AudioHardwareALSA类;则ladev->hwif->openOutputStream()函数调到AudioHardwareALSA::openOutputStream()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">android_audio_legacy::AudioStreamOut* AudioHardwareALSA::openOutputStream(uint32_tdevices, int*format, uint32_t*channels, uint32_t*sampleRate, status_t*status) { ...... //Findtheappropriatealsadevice for(ALSAHandleList::iteratorit=mDeviceList.begin();<spanstyle="color:#3333ff;">//mDeviceList是用来存储 //输出/输入音频通道信息的 //句柄的,总共两个句柄, //分别对应输出和输入音频通道 </span>it!=mDeviceList.end();++it) if(it->devices&devices){ err=<spanstyle="color:#ff0000;">mALSADevice->open</span>(&(*it),devices,mode());<spanstyle="color:#3333ff;">//当调用open()函数时, //就已经知道要打开的是 //输入音频通道还是输出 //音频通道</span> if(err)break; out=<spanstyle="color:#ff0000;">newAudioStreamOutALSA</span>(this,&(*it));<spanstyle="color:#3333ff;">//此类对象的创建本可不必理会, //但它的父类ALSAStreamOps类的对象也会随之创建;而ALSAStreamOps //类将在后面用到(mParent=this(AudioHardwareALSA))</span> err=out->set(format,channels,sampleRate); break; } ...... }</span>
由前文对AudioHardwareALSA类的启动流程分析可知,mDeviceList是用来存储输出/输入音频通道信息的句柄的。
mALSADevice表示在初始化ALSA设备时所指向的一个具体ALSA设备的操作接口。则mALSADevice->open()函数,将具体调到ALSA模块的s_open()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticstatus_ts_open(alsa_handle_t*handle,uint32_tdevices,intmode) { //Closeoffpreviouslyopeneddevice. //Itwouldbenicetodetermineiftheunderlyingdeviceactually //changes,butwemightberecoveringfromanerrorormanipulating //mixersettings(seeasound.conf). // s_close(handle);<spanstyle="color:#3333ff;">//先关闭先前打开的音频通道 </span> LOGD("opencalledfordevices%08xinmode%d...",devices,mode); constchar*stream=<spanstyle="color:#ff0000;">streamName</span>(handle); constchar*devName=<spanstyle="color:#ff0000;">deviceName</span>(handle,devices,mode); interr; for(;;){ //ThePCMstreamisopenedinblockingmode,perALSAdefaults.The //AudioFlingerseemstoassumeblockingmodetoo,soasynchronousmode //shouldnotbeused. err=<spanstyle="color:#ff0000;">snd_pcm_open</span>(<spanstyle="color:#ff0000;">&handle->handle</span>,<spanstyle="color:#ff0000;">devName</span>,<spanstyle="color:#ff0000;">direction</span>(handle), <spanstyle="color:#3333ff;">//handle->handle:保存从ALSA-LIB中获得的PCM节点</span> SND_PCM_ASYNC); if(err==0)break; //Seeifthereisalessspecificnamewecantry. //Note:Wearechangingthecontentsofaconstchar*here. char*tail=strrchr(devName,'_'); if(!tail)break; *tail=0; } if(err<0){ //NoneoftheAndroiddefinedaudiodevicesexist.Openagenericone. <spanstyle="color:#ff0000;">devName="default";</span> err=<spanstyle="color:#ff0000;">snd_pcm_open</span>(&handle->handle,devName,direction(handle),0); } if(err<0){ LOGE("FailedtoInitializeanyALSA%sdevice:%s", stream,strerror(err)); returnNO_INIT; } err=<spanstyle="color:#ff0000;">setHardwareParams</span>(handle); if(err==NO_ERROR)err=setSoftwareParams(handle); LOGI("InitializedALSA%sdevice%s",stream,devName); handle->curDev=devices; handle->curMode=mode; returnerr; } </span>
(1) 调用函数streamName()函数获取音频流名称。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">constchar*streamName(alsa_handle_t*handle) { returnsnd_pcm_stream_name(direction(handle)); }</span>
snd_pcm_stream_name()函数是ALSA-LIB API,在external/alsa-lib/src/pcm/pcm.c文件中定义。
要想获得音频流名称,不得不先分析direction()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">snd_pcm_stream_tdirection(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()的参数,
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">constchar*snd_pcm_stream_name(snd_pcm_stream_tstream) { if(stream>SND_PCM_STREAM_LAST) returnNULL; return<spanstyle="color:#ff0000;">snd_pcm_stream_names</span>[stream]; }</span>
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticconstchar*constsnd_pcm_stream_names[]={ STREAM(PLAYBACK), STREAM(CAPTURE), };</span>
好吧,音频流的名称不是放音就是录音。
(2)接下来调用deviceName()函数获取设备名称。这点很重要,将为我们在asound.conf为新添加的USB AUDIO音频设备命名提供规则。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">constchar*deviceName(alsa_handle_t*handle,uint32_tdevice,intmode) { staticchar<spanstyle="color:#ff0000;">devString</span>[ALSA_NAME_MAX]; inthasDevExt=0; strcpy(devString,devicePrefix[direction(handle)]); for(intdev=0;device&&dev<deviceSuffixLen;dev++) if(device&<spanstyle="color:#ff0000;">deviceSuffix</span>[dev].device){ ALSA_STRCAT(devString,deviceSuffix[dev].suffix); device&=~deviceSuffix[dev].device; <spanstyle="color:#ff0000;">hasDevExt=1;</span> } if(hasDevExt)switch(mode){ caseandroid_audio_legacy::AudioSystem::MODE_NORMAL: ALSA_STRCAT(devString,"_normal") ; break; caseandroid_audio_legacy::AudioSystem::MODE_RINGTONE: ALSA_STRCAT(devString,"_ringtone") ; break; caseandroid_audio_legacy::AudioSystem::MODE_IN_CALL: ALSA_STRCAT(devString,"_incall") ; break; }; returndevString; }</span>
用字符数组devString存储设备名称。
首先把设备前缀复制给devString。因此,作为输出音频通道设备的名称,必以AndroidPlayback为前缀;作为输入音频通道设备的名称,必以AndroidCapture为前缀。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticconstchar*devicePrefix[SND_PCM_STREAM_LAST+1]={ /*SND_PCM_STREAM_PLAYBACK:*/"AndroidPlayback", /*SND_PCM_STREAM_CAPTURE:*/"AndroidCapture", };</span>
接下来从deviceSuffix数组中查找合适的后缀,追加到devString字符数组中。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">/*Thefollowingtable(s)needtomatchinorderoftheroutebits */ staticconstdevice_suffix_tdeviceSuffix[]={ {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的定义如下:
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">structdevice_suffix_t{ constandroid_audio_legacy::AudioSystem::audio_devicesdevice; constchar*suffix; };</span>
PS: 我们也要在此数组中添加USB AUDIO音频设备的相关信息。同时也要在定义了类似DEVICE_OUT_EARPIECE设备的类中定义USB AUDIO音频设备:
[cpp]view plaincopy print? <spanxmlns="/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):
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanstyle="font-size:24px;">publicstaticfinalintDEVICE_OUT_EARPIECE=0x1;//0x1<<0 publicstaticfinalintDEVICE_OUT_SPEAKER=0x2;//0x1<<1 publicstaticfinalintDEVICE_OUT_WIRED_HEADSET=0x4;//0x1<<2 publicstaticfinalintDEVICE_OUT_WIRED_HEADPHONE=0x8;//0x1<<3 publicstaticfinalintDEVICE_OUT_BLUETOOTH_SCO=0x10;//0x1<<4 publicstaticfinalintDEVICE_OUT_BLUETOOTH_SCO_HEADSET=0x20;//0x1<<5 publicstaticfinalintDEVICE_OUT_BLUETOOTH_SCO_CARKIT=0x40;//0x1<<6 publicstaticfinalintDEVICE_OUT_BLUETOOTH_A2DP=0x80;//0x1<<7 publicstaticfinalintDEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES=0x100; //0x100<<0 publicstaticfinalintDEVICE_OUT_BLUETOOTH_A2DP_SPEAKER=0x200;//0x100<<1 publicstaticfinalintDEVICE_OUT_AUX_DIGITAL=0x400;//0x100<<2 publicstaticfinalintDEVICE_OUT_ANLG_DOCK_HEADSET=0x800;//0x100<<3 publicstaticfinalintDEVICE_OUT_DGTL_DOCK_HEADSET=0x1000;//0x1000<<0 publicstaticfinalintDEVICE_OUT_DEFAULT=0x8000;//0x1000<<3 //inputdevices publicstaticfinalintDEVICE_IN_COMMUNICATION=0x10000;//0x10000<<0 publicstaticfinalintDEVICE_IN_AMBIENT=0x20000;//0x10000<<1 publicstaticfinalintDEVICE_IN_BUILTIN_MIC1=0x40000;//0x10000<<2 publicstaticfinalintDEVICE_IN_BUILTIN_MIC2=0x80000;//0x10000<<3 publicstaticfinalintDEVICE_IN_MIC_ARRAY=0x100000;//0x100000<<0 publicstaticfinalintDEVICE_IN_BLUETOOTH_SCO_HEADSET=0x200000; //0x100000<<1 publicstaticfinalintDEVICE_IN_WIRED_HEADSET=0x400000;//0x100000<<2 publicstaticfinalintDEVICE_IN_AUX_DIGITAL=0x800000;//0x100000<<3</span></span>
当设备越来越多时,很难保证等号右边的数字中零的个数不写错。采用注释部分的定义方式较好。
当找到了设备后缀后,将对变量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中声道数和采样率进行赋值。将在后面做详细分析。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">status_tsetHardwareParams(alsa_handle_t*handle) { ...... unsignedintrequestedRate=<spanstyle="color:#ff0000;">handle->sampleRate</span>; ...... <spanstyle="color:#ff0000;">err=</span>snd_pcm_hw_params_set_channels(handle->handle,hardwareParams, <spanstyle="color:#ff0000;">handle->channels</span>); ...... err=<spanstyle="color:#ff0000;">snd_pcm_hw_params_set_rate_near</span>(handle->handle,hardwareParams, <spanstyle="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)代码。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticint android_media_AudioSystem_setDeviceConnectionState(JNIEnv*env,jobjectthiz, jintdevice,jintstate,jstringdevice_address) { constchar*c_address=env->GetStringUTFChars(device_address,NULL); intstatus=check_AudioSystem_Command( <spanstyle="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); returnstatus; }</span>
显然,又调到libmedia库(frameworks/base/media/libmedia/AudioSystem.cpp)中setDeviceConnectionState()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">status_tAudioSystem::setDeviceConnectionState(audio_devices_tdevice, audio_policy_dev_state_tstate, constchar*device_address) { constsp<IAudioPolicyService>&aps=AudioSystem::get_audio_policy_service(); constchar*address=""; if(aps==0)returnPERMISSION_DENIED; if(device_address!=NULL){ address=device_address; } return<spanstyle="color:#ff0000;">aps->setDeviceConnectionState</span>(device,state,address); }</span>
显然,调到APS的setDeviceConnectionState()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">status_tAudioPolicyService::setDeviceConnectionState(audio_devices_tdevice, audio_policy_dev_state_tstate, constchar*device_address) { ...... return<spanstyle="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()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticintap_set_device_connection_state(structaudio_policy*pol, audio_devices_tdevice, audio_policy_dev_state_tstate, constchar*device_address) { structlegacy_audio_policy*lap=to_lap(pol); return<spanstyle="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()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;"><spanstyle="font-size:18px;">status_tAudioPolicyManagerBase::setDeviceConnectionState(AudioSystem::audio_devicesdevice, AudioSystem::device_connection_statestate, constchar*device_address) { ...... //handleoutputdevices if(AudioSystem::isOutputDevice(device)){</span><spanstyle="font-size:18px;color:#3333ff;">//如果是输出设备 </span><spanstyle="font-size:18px;"> ...... switch(state) { //handleoutputdeviceconnection caseAudioSystem::DEVICE_STATE_AVAILABLE: if(mAvailableOutputDevices&device){ LOGW("setDeviceConnectionState()devicealreadyconnected:%x",device); returnINVALID_OPERATION; } LOGV("setDeviceConnectionState()connectingdevice%x",device); //registernewdeviceasavailable mAvailableOutputDevices|=device;<spanstyle="color:#3333ff;">//把当前设备加入到可用设备变量中</span> ..... //handleoutputdevicedisconnection caseAudioSystem::DEVICE_STATE_UNAVAILABLE:{ if(!(mAvailableOutputDevices&device)){ LOGW("setDeviceConnectionState()devicenotconnected:%x",device); returnINVALID_OPERATION; } LOGV("setDeviceConnectionState()disconnectingdevice%x",device); //removedevicefromavailableoutputdevices mAvailableOutputDevices&=~device;<spanstyle="color:#3333ff;">//把当前设备从可用设备变量中去除</span> ...... } //requestroutingchangeifnecessary uint32_tnewDevice=<spanstyle="color:#ff0000;">getNewDevice</span>(mHardwareOutput,false); ...... <spanstyle="color:#ff0000;">updateDeviceForStrategy</span>(); <spanstyle="font-size:24px;"><strongstyle="background-color:rgb(153,153,153);"><spanstyle="color:#ff0000;">setOutputDevice</span></strong></span>(mHardwareOutput,newDevice); <spanstyle="color:#3333ff;">//如果输出音频设备是USBAUDIO(USB放音),那么应该知道输入音频设备为SUBMIC</span> if(device==AudioSystem::DEVICE_OUT_WIRED_HEADSET){ device=AudioSystem::DEVICE_IN_WIRED_HEADSET; }elseif(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{ returnNO_ERROR; } } //handleinputdevices if(AudioSystem::isInputDevice(device)){<spanstyle="color:#3333ff;">//如果是输入设备</span> switch(state) { //handleinputdeviceconnection caseAudioSystem::DEVICE_STATE_AVAILABLE:{ if(mAvailableInputDevices&device){ LOGW("setDeviceConnectionState()devicealreadyconnected:%d",device); returnINVALID_OPERATION; } mAvailableInputDevices|=device; } break; //handleinputdevicedisconnection caseAudioSystem::DEVICE_STATE_UNAVAILABLE:{ if(!(mAvailableInputDevices&device)){ LOGW("setDeviceConnectionState()devicenotconnected:%d",device); returnINVALID_OPERATION; } mAvailableInputDevices&=~device; }break; default: LOGE("setDeviceConnectionState()invalidstate:%x",state); returnBAD_VALUE; } audio_io_handle_tactiveInput=getActiveInput(); if(activeInput!=0){ AudioInputDescriptor*inputDesc=mInputs.valueFor(activeInput); uint32_tnewDevice=getDeviceForInputSource(inputDesc->mInputSource); if(newDevice!=inputDesc->mDevice){ LOGV("setDeviceConnectionState()changingdevicefrom%xto%xforinput%d", inputDesc->mDevice,newDevice,activeInput); inputDesc->mDevice=newDevice; AudioParameterparam=AudioParameter(); param.addInt(String8(AudioParameter::keyRouting),(int)newDevice); mpClientInterface->setParameters(activeInput,param.toString()); } } returnNO_ERROR; } LOGW("setDeviceConnectionState()invaliddevice:%x",device); returnBAD_VALUE; }</span> </span>
(1) 当前设备是输出设备时,程序执行到getNewDevice()函数,将获得新设备,作为设置输出设备函数setOutputDevice()的第二个参数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="">uint32_tAudioPolicyManagerBase::getNewDevice(audio_io_handle_toutput,boolfromCache) { uint32_tdevice=0; AudioOutputDescriptor*outputDesc=mOutputs.valueFor(output); //checkthefollowingbyorderofprioritytorequestaroutingchangeifnecessary: //1:thestrategyenforcedaudibleisactiveontheoutput: //usedeviceforstrategyenforcedaudible //2:weareincallorthestrategyphoneisactiveontheoutput: //usedeviceforstrategyphone //3:thestrategysonificationisactiveontheoutput: //usedeviceforstrategysonification //4:thestrategymediaisactiveontheoutput: //usedeviceforstrategymedia //5:thestrategyDTMFisactiveontheoutput: //usedeviceforstrategyDTMF if(outputDesc-><spanstyle="color:#ff0000;">isUsedByStrategy</span>(STRATEGY_ENFORCED_AUDIBLE)){<spanstyle="color:#3333ff;">//判断是否使用了这五种音频策略之一的 //STRATEGY_ENFORCED_AUDIBLE</span> device=<spanstyle="color:#ff0000;">getDeviceForStrategy</span>(STRATEGY_ENFORCED_AUDIBLE,fromCache);<spanstyle="color:#3333ff;">//若使用了STRATEGY_ENFORCED_AUDIBLE, //并获得相关设备,需要增加对USBAUDIO设备的支持</span> }elseif(isInCall()|| outputDesc->isUsedByStrategy(STRATEGY_PHONE)){ device=getDeviceForStrategy(STRATEGY_PHONE,fromCache); }elseif(outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)){ device=getDeviceForStrategy(STRATEGY_SONIFICATION,fromCache); }elseif(outputDesc->isUsedByStrategy(STRATEGY_MEDIA)){ device=getDeviceForStrategy(STRATEGY_MEDIA,fromCache); }elseif(outputDesc->isUsedByStrategy(STRATEGY_DTMF)){ device=getDeviceForStrategy(STRATEGY_DTMF,fromCache); } ...... returndevice; } ...... uint32_tAudioPolicyManagerBase::getDeviceForStrategy(routing_strategystrategy,boolfromCache) { uint32_tdevice=0; if(fromCache){ LOGV("getDeviceForStrategy()fromcachestrategy%d,device%x",strategy,mDeviceForStrategy[strategy]); returnmDeviceForStrategy[strategy]; } switch(strategy){ caseSTRATEGY_DTMF: if(!isInCall()){ //whenoffcall,DTMFstrategyfollowsthesamerulesasMEDIAstrategy device=getDeviceForStrategy(STRATEGY_MEDIA,false); break; } //whenincall,DTMFandPHONEstrategiesfollowthesamerules //FALLTHROUGH caseSTRATEGY_PHONE:<spanstyle="color:#3333ff;">//由于我BOX不支持通话功能,所以可以不在此策略下添加对USBAUDIO设备的支持 </span>//forphonestrategy,wefirstconsidertheforceduseandthentheavailabledevicesbyorder //ofpriority ...... break; caseSTRATEGY_SONIFICATION:<spanstyle="color:#3333ff;">//由于我BOX不支持通话功能,所以可以不在此策略下添加对USBAUDIO设备的支持</span> //Ifincall,justselecttheSTRATEGY_PHONEdevice:Therestofthebehaviorishandledby //handleIncallSonification(). ...... caseSTRATEGY_ENFORCED_AUDIBLE: //strategySTRATEGY_ENFORCED_AUDIBLEusessameroutingpolicyasSTRATEGY_SONIFICATION //exceptwhenincallwhereitdoesn'tdefaulttoSTRATEGY_PHONEbehavior ...... caseSTRATEGY_MEDIA:{<spanstyle="color:#3333ff;">//多媒体播放策略,需要添加对USBAUDIO设备的支持</span> uint32_tdevice2=mAvailableOutputDevices&AudioSystem::DEVICE_OUT_WIRED_HEADPHONE; if(device2==0){ device2=mAvailableOutputDevices&AudioSystem::DEVICE_OUT_WIRED_HEADSET; } ....... <spanstyle="color:#3333ff;">//添加对USBAUDIO设备支持的代码</span> <spanstyle="color:#33cc00;">if(device2==0){ device2=mAvailableOutputDevices&AudioSystem::DEVICE_OUT_USB_AUDIO; }</span> <spanstyle="color:#3333ff;">//end</span> ....... }</span>
(2)获取到新设备后,程序继续向下执行到updateDeviceForStrategy()函数,根据音频策略更新了相应的设备。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="">voidAudioPolicyManagerBase::updateDeviceForStrategy() { for(inti=0;i<NUM_STRATEGIES;i++){ mDeviceForStrategy[i]=<spanstyle="color:#ff0000;">getDeviceForStrategy</span>((routing_strategy)i,false); } }</span>
由于此函数仍将调到刚刚分析的函数getDeviceForStrategy(),故不再深入分析。
(3)更新完设备后,程序继续向下执行到setOutputDevice()函数,用新获取到的设备名称作为第二个参数(第一个参数是在创建AudioPolicyManagerBase类对象时获得的输出音频通道),来设置输出设备。此函数很重要,它将调到ALSA模块(alsa_default.cpp)。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="">voidAudioPolicyManagerBase::setOutputDevice(audio_io_handle_toutput,uint32_tdevice,boolforce,intdelayMs) { LOGV("setOutputDevice()output%ddevice%xdelayMs%d",output,device,delayMs); AudioOutputDescriptor*outputDesc=mOutputs.valueFor(output); ...... //dotherouting AudioParameterparam=AudioParameter(); param.addInt(String8(AudioParameter::keyRouting),(int)device);<spanstyle="color:#3333ff;">//第一个参数,暗示将重新选择路由, //第二个参数就是要打开的音频设备的标识符 </span>mpClientInterface->setParameters(mHardwareOutput,param.toString(),delayMs);<spanstyle="color:#3333ff;">//将从HAL层调回到frameworks层</span> ...... }</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()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">voidAudioPolicyCompatClient::setParameters(audio_io_handle_tioHandle, constString8&keyValuePairs, intdelayMs) { <spanstyle="color:#ff0000;">mServiceOps->set_parameters</span>(mService,ioHandle,keyValuePairs.string(), <spanstyle="color:#3333ff;">//在创建APS类对象时,调用create_audio_policy()第三个参数this(即APS)一路传递下来,并在创建 //AudioPolicyCompatClient类对象时对mService进行了初始化</span> delayMs); }</span>
而mServiceOps在创建AudioPolicyCompatClient类对象时,指向APS的struct audio_policy_service_ops变量aps_ops。则mServiceOps->set_parameters()函数将调到APS的aps_set_parameters()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">staticvoidaps_set_parameters(void*service,audio_io_handle_tio_handle, constchar*kv_pairs,intdelay_ms) { AudioPolicyService*audioPolicyService=(AudioPolicyService*)service; audioPolicyService->setParameters(io_handle,kv_pairs,delay_ms); }</span>
显然将调到APS类的setParameters()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="font-size:24px;">voidAudioPolicyService::setParameters(audio_io_handle_tioHandle, constchar*keyValuePairs, intdelayMs) { <spanstyle="color:#ff0000;">mAudioCommandThread->parametersCommand</span>((int)ioHandle,keyValuePairs, delayMs); }</span>
mAudioCommandThread就是在创建APS类对象时,启动的一个音频命令线程。函数mAudioCommandThread->parametersCommand()将根据第二个参数产生一个设置音频参数的命令,并发给此线程的处理函数threadLoop()。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="">status_tAudioPolicyService::AudioCommandThread::parametersCommand(intioHandle, constchar*keyValuePairs, intdelayMs) { status_tstatus=NO_ERROR; AudioCommand*command=newAudioCommand(); <spanstyle="color:#ff0000;">command->mCommand=SET_PARAMETERS; </span>ParametersData*data=newParametersData(); data->mIO=ioHandle; <spanstyle="color:#ff0000;">data->mKeyValuePairs=String8(keyValuePairs);<spanstyle="color:#3333ff;">//keyValuePairs保存了从AudioPolicyManagerBase类传递的音频参数param</span> </span>command->mParam=data; if(delayMs==0){ command->mWaitStatus=true; }else{ command->mWaitStatus=false; } Mutex::Autolock_l(mLock); insertCommand_l(command,delayMs);<spanstyle="color:#3333ff;">//调用函数insertCommand_l()把命令插入到此线程处理函数threadLoop()中</span> ...... } </span>
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="">//insertCommand_l()mustbecalledwithmLockheld voidAudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand*command,intdelayMs) { ...... //acquirewakelocktomakesuredelayedcommandsareprocessed if(mName!=""&&<spanstyle="color:#ff0000;">mAudioCommands</span>.isEmpty()){ acquire_wake_lock(PARTIAL_WAKE_LOCK,mName.string()); } //checksamependingcommandswithlatertimestampsandeliminatethem for(i=mAudioCommands.size()-1;i>=0;i--){ <spanstyle="color:#ff0000;">AudioCommand*command2=mAudioCommands[i];</span> ...... switch(command->mCommand){ <spanstyle="color:#ff0000;">caseSET_PARAMETERS</span>:{ ParametersData*data=(ParametersData*)command->mParam; ParametersData*data2=(ParametersData*)command2->mParam; if(data->mIO!=data2->mIO)break; LOGV("Comparingparametercommand%stonewcommand%s", data2->mKeyValuePairs.string(),data->mKeyValuePairs.string()); AudioParameterparam=AudioParameter(data->mKeyValuePairs); AudioParameterparam2=AudioParameter(data2->mKeyValuePairs); for(size_tj=0;j<param.size();j++){ String8key; String8value; param.getAt(j,key,value); for(size_tk=0;k<param2.size();k++){ String8key2; String8value2; param2.getAt(k,key2,value2); if(key2==key){ param2.remove(key2); LOGV("Filteringoutparameter%s",key2.string()); break; } } } //ifallkeyshavebeenfilteredout,removethecommand. //otherwise,updatethekeyvaluepairs if(param2.size()==0){ <spanstyle="color:#ff0000;">removedCommands.add(command2);</span> }else{ data2->mKeyValuePairs=param2.toString(); } }break; ...... }</span>
插入命令函数AudioCommandThread::insertCommand_l()和命令处理函数AudioCommandThread::threadLoop()的衔接点就是mAudioCommands列表。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="">boolAudioPolicyService::AudioCommandThread::threadLoop() { nsecs_twaitTime=INT64_MAX; mLock.lock(); while(!exitPending()) { while(!mAudioCommands.isEmpty()){ nsecs_tcurTime=systemTime(); //commandsaresortedbyincreasingtimestamp:executethemfromindex0andup if(mAudioCommands[0]->mTime<=curTime){ <spanstyle="color:#ff0000;">AudioCommand*command=mAudioCommands[0]; </span>mAudioCommands.removeAt(0); mLastCommand=*command; switch(command->mCommand){ ...... caseSET_PARAMETERS:{ ParametersData*data=(ParametersData*)command->mParam; LOGV("AudioCommandThread()processingsetparametersstring%s,io%d", data->mKeyValuePairs.string(),data->mIO); command->mStatus=<spanstyle="color:#ff0000;">AudioSystem::setParameters</span>(data->mIO,data->mKeyValuePairs); if(command->mWaitStatus){ command->mCond.signal(); mWaitWorkCV.wait(mLock); } deletedata; }break; ...... }</span>
此线程处理函数并没有真正去设置参数,而是把设置参数的实际操作交给了函数AudioSystem::setParameters()。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="">status_tAudioSystem::setParameters(audio_io_handle_tioHandle,constString8&keyValuePairs){ constsp<IAudioFlinger>&af=AudioSystem::get_audio_flinger(); if(af==0)returnPERMISSION_DENIED; return<spanstyle="color:#ff0000;">af->setParameters</span>(ioHandle,keyValuePairs); }</span>
又调到AudioFlinger了!
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="">status_tAudioFlinger::setParameters(intioHandle,constString8&keyValuePairs) { ...... //checkcallingpermissions if(!settingsAllowed()){ returnPERMISSION_DENIED; } //ioHandle==0meanstheparametersareglobaltotheaudiohardwareinterface if(ioHandle==0){<spanstyle="color:#ff0000;"><spanstyle="color:#3333ff;">//ioHandle就是在函数AudioPolicyManagerBase::setOutputDevice()内调用 //mpClientInterface->setParameters()函数时, //传递的第一个参数mHardwareOutput,其值有其中可能;经查看log信息确认,当前值是0。 //将改变全局输入/输出音频设备</span> </span>AutoMutexlock(mHardwareLock); mHardwareStatus=AUDIO_SET_PARAMETER; status_tfinal_result=NO_ERROR; for(size_ti=0;i<mAudioHwDevs.size();i++){//将对所有的音频设备的参数都重新设置 audio_hw_device_t*dev=<spanstyle="color:#ff0000;">mAudioHwDevs</span>[i]; result=<spanstyle="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()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="">staticintadev_set_parameters(structaudio_hw_device*dev,constchar*kvpairs) { structlegacy_audio_device*ladev=to_ladev(dev); return<spanstyle="color:#ff0000;">ladev->hwif->setParameters</span>(String8(kvpairs)); }</span>
同样根据前面AudioFlinger服务分析可知,ladev->hwif具体指ALSA音频类AudioHardwareALSA的对象。则函数ladev->hwif->setParameters()就是函数AudioHardwareALSA类的setParameters()函数。此函数在头文件hardware/alsa_sound/AudioHardwareALSA.h中定义。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style="">classALSAStreamOps { public: ALSAStreamOps(AudioHardwareALSA*parent,alsa_handle_t*handle); virtual~ALSAStreamOps(); status_tset(int*format,uint32_t*channels,uint32_t*rate); status_t<spanstyle="color:#ff0000;">setParameters</span>(constString8&keyValuePairs); ...... }</span>
此setParameters()函数的实现在提供了ALSA流操作接口的文件hardware/alsa_sound/ALSAStreamOps.cpp中。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">status_tALSAStreamOps::setParameters(constString8&keyValuePairs) { AudioParameterparam=AudioParameter(keyValuePairs); String8key=String8(AudioParameter::keyRouting); status_tstatus=NO_ERROR; intdevice; LOGV("setParameters()%s",keyValuePairs.string()); if(param.getInt(key,device)==NO_ERROR){ AutoMutexlock(mLock); <spanstyle="color:#ff0000;">mParent->mALSADevice->route</span>(mHandle,(uint32_t)device,mParent->mode()); param.remove(key); } if(param.size()){ status=BAD_VALUE; } returnstatus; }</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类型的值
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">structalsa_device_t{ hw_device_tcommon; 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()函数。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">staticstatus_ts_route(alsa_handle_t*handle,uint32_tdevices,intmode) { LOGD("routecalledfordevices%08xinmode%d...",devices,mode); //@eric:0316 //WhenRingCallcome,AudioHardWareALSASetRINGMODEandwillcallthisfunc. //FIXME:IthinkOurAudioDeviceonlyhasonehandle,sowecannotreopenit if(handle->handle&&handle->curDev==devices/*&&handle->curMode==mode*/)returnNO_ERROR; returns_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-/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()。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">/** *\briefUpdates#snd_configbyrereadingtheglobalconfigurationfiles(ifneeded). *\returnAnon-negativevalueifsuccessful,otherwiseanegativeerrorcode. *\retval0Noactionisneeded. *\retval1Theconfigurationtreehasbeenrebuilt. * *Theglobalconfigurationfilesarespecifiedintheenvironmentvariable *\cALSA_CONFIG_PATH.Ifthisisnotset,thedefaultvalueis *"/usr/share/alsa/alsa.conf". * *\warningIftheconfigurationtreeisreread,allstringpointersand *configurationnodehandlespreviouslyobtainedfromthistreebecomeinvalid. */ intsnd_config_update(void) { interr; #ifdefHAVE_LIBPTHREAD pthread_mutex_lock(&snd_config_update_mutex); #endif err=<spanstyle="color:#ff0000;">snd_config_update_r</span>(<spanstyle="color:#ff0000;">&snd_config</span>,<spanstyle="color:#ff0000;">&snd_config_global_update</span>,<spanstyle="color:#ff0000;">NULL</span>); #ifdefHAVE_LIBPTHREAD pthread_mutex_unlock(&snd_config_update_mutex); #endif returnerr; }</span></span>
又调到snd_config_update_r()函数了。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">/** *\briefUpdatesaconfigurationtreebyrereadingtheconfigurationfiles(ifneeded). *\param_topAddressofthehandletothetoplevelnode. *\param_updateAddressofapointertoprivateupdateinformation. *\paramcfgsAlistofconfigurationfilenames,delimitedwith':'. *If\pcfgsissetto\cNULL,thedefaultglobalconfiguration *fileisused("/usr/share/alsa/alsa.conf"). *\returnAnon-negativevalueifsuccessful,otherwiseanegativeerrorcode. *\retval0Noactionisneeded. *\retval1Theconfigurationtreehasbeenrebuilt. * *Theglobalconfigurationfilesarespecifiedintheenvironmentvariable *\cALSA_CONFIG_PATH. * *\warningIftheconfigurationtreeisreread,allstringpointersand *configurationnodehandlespreviouslyobtainedfromthistreebecomeinvalid. */ intsnd_config_update_r(snd_config_t**_top,snd_config_update_t**_update,constchar*cfgs)<spanstyle="color:#3333ff;">//cfgs=NULL</span> { ...... configs=cfgs; if(!configs){ configs=getenv(ALSA_CONFIG_PATH_VAR);<spanstyle="color:#3333ff;">//???</span> if(!configs||!*configs){ configs=ALSA_CONFIG_PATH_DEFAULT;<spanstyle="color:#3333ff;">//宏ALSA_CONFIG_PATH_DEFAULT //便是/usr/share/alsa/alsa.conf .......</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-/alsa-doc/alsa-lib/pcm_plugins.html。
在android的源码中,也有现成的例子可以参考,比如三星的一个音频配置文件device/samsung/crespo/asound.conf。
pcm节点真是对具体的音频设备的硬件参数的配置。可以配置的硬件参数如下:
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">pcm.name{ typehw#KernelPCM cardINT/STR#Cardname(string)ornumber(integer) [deviceINT]#Devicenumber(default0) [subdeviceINT]#Subdevicenumber(default-1:firstavailable) [sync_ptr_ioctlBOOL]#UseSYNC_PTRioctlratherthanthedirectmmapaccessforcontrolstructures [nonblockBOOL]#Forcenon-blockingopenmode [formatSTR]#Restrictonlytothegivenformat [channelsINT]#Restrictonlytothegivenchannels [rateINT]#Restrictonlytothegivenrate }</span></span>
仅举一例
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">pcm.AndroidCapture_Usb-audio_normal{<spanstyle="color:#3333ff;">//此PCM节点为录音设备的PCM节点 </span>typehooks slave.pcm"hw:1,0"<spanstyle="color:#3333ff;">//1:表示声卡1(声卡0就是系统内嵌的声卡),0表示声卡1上的设备编号为0的设备</span> }</span></span>
AndroidCapture_Usb-audio_normal:前缀"AndroidCapture"和扩展"_normal"是固定的,后缀"_Usb-audio"是要在alsa_default.cpp中设置的。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">/*Thefollowingtable(s)needtomatchinorderoftheroutebits */ staticconstdevice_suffix_tdeviceSuffix[]={ {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"}, <spanstyle="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. 重新获取USBAUDIO设备的硬件参数。
前文已经提到,可在alsa_default.cpp文件中的s_open()函数在调用setHardwareParams()函数设置硬件参数之前重设声道数channels或采样率rate。
4.1 有三种方法获得实际使用的USB AUDIO设备的硬件参数信息。
第一种方法: 自己写个字符串处理函数,从文件/proc/asound/card1/stream0中读取(只需读取录音部分的)。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">$cat/proc/asound/card1/stream0 SAGETechnologySAGEAirMouseatusb-0000:00:1d.3-2,fullspeed:USBAudio Playback: Status:Stop Interface2 Altset1 Format:S16_LE Channels:1 Endpoint:6OUT(NONE) Rates:16000 Capture: Status:Stop Interface1 Altset1 Format:S16_LE Channels:1<spanstyle="color:#3333ff;">//这个声道数与默认值2不一致 </span>Endpoint:5IN(NONE) Rates:16000<spanstyle="color:#3333ff;">//这个采样率与默认值48000不一致</span> </span></span>
第二种方法:自己写代码,从内核USB驱动中读取。主要涉及到如下文件(也是我们项目所采用的方法)
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">sound/usb/card.c drivers/hid/hid-input.c</span></span>
第三种方法:因为是直接调用系统现有接口,是比较可靠的方法:调用ALSA-LIB库的API。主要涉及到如下文件
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">external/alsa-lib/src/pcm/pcm_rate.c</span></span>
4.2 把输入音频通道的声道数和采样率改回默认值
当打开指定PCM节点的音频设备失败后,系统会去打开默认的音频通道(alsa_default.cpp)。
[cpp]view plaincopy print? <spanxmlns="/1999/xhtml"style=""><spanxmlns="/1999/xhtml"style="">for(;;){ //ThePCMstreamisopenedinblockingmode,perALSAdefaults.The //AudioFlingerseemstoassumeblockingmodetoo,soasynchronousmode //shouldnotbeused. err=snd_pcm_open(&handle->handle,devName,direction(handle), ....... } if(err<0){ <spanstyle="color:#3333ff;">//要在此处把输入音频通道的声道数和采样率改回默认值 </span> //NoneoftheAndroiddefinedaudiodevicesexist.Openagenericone. devName="default"; err=snd_pcm_open(&handle->handle,devName,direction(handle),0); }</span></span>
PS:只需调用setDeviceConnectionState()函数打开输出音频通道,不要去打开输入音频通道。因为用户使用的音频聊天工具(如Skypee)会自动打开。我们只需在打开USB AUDIO输出音频通道时,强制设置USB MIC为输入音频通道。这样就能实现用USB MIC 进行语音聊天了!
结贴~~~~~~~
如果觉得《为android系统添加USB AUDIO设备的放音和录音功能》对你有帮助,请点赞、收藏,并留下你的观点哦!