- 浏览: 432076 次
文章分类
最新评论
ANDROID音频系统散记之一:A2dpAudioInterface
写在之前
本来有打算写写Android音频系统的,但是仔细研究了如下链接的三篇文章,果断中断了我的想法。毫不夸张来说,这是我看过的最好的阐述Android音频系统的文章了,简练精辟,将音频系统各个方面的重要的脉络都描述出来了。有这三篇文章,理解Android音频系统何止加快了10倍。
Android Audio System 之一:AudioTrack如何与AudioFlinger交换音频数据
Android Audio System 之二:AudioFlinger
Android Audio System 之三: AudioPolicyService 和 AudioPolicyManager
A2dpAudioInterface
Android音频系统有两大服务:一是AudioFlinger,二是AudioPolicyService。AudioFlinger负责向下访问AudioHardwareInterface,实现音频PCM数据的混音/输入/输出,实现音量调节;AudioPolicyService负责音频输入输出设备的连接状态,音频策略调度即音频设备(如本地CODEC、Bluetooth A2DP、Headset)的切换策略(注意它只是负责策略,真正的切换操作是在AudioFlinger中的openOutput,毕竟AudioFlinger负责操作底层音频硬件)。AudioPolicyService在以后的章节详细分析,这里主要探讨A2DP-Audio是如何注册到AudioFlinger中,并简要提及音频PCM数据流向。
好的平台软件应有这样的一个抽象层:向下提供一套固定的接口,不同的硬件设备根据这些接口实现各自的方法,然后注册到这个抽象层中去。这样对于上层应用而言并没有任何区别,因为上层只需调用抽象层接口就行了,不管底层硬件的差异性。AudioFlinger就是这样的一个抽象层,无论底层是ALSA设备还是BluetoothHeadset,上层都只会看到AudioFlinger的接口。至于何时切换到ALSA设备何时切换到BluetoothHeadset,这就属于音频策略调度范畴了即AudioPolicyService。
- AudioFlinger::AudioFlinger()
- :BnAudioFlinger(),
- mAudioHardware(0),mMasterVolume(1.0f),mMasterMute(false),mNextUniqueId(1)
- {
- mHardwareStatus=AUDIO_HW_IDLE;
- mAudioHardware=AudioHardwareInterface::create();
- ......
- AudioHardwareInterface*AudioHardwareInterface::create()
- {
- /*
- *FIXME:Thiscodeneedstoinstantiatethecorrectaudiodevice
- *interface.Fornow-weusecompile-timeswitches.
- */
- AudioHardwareInterface*hw=0;
- charvalue[PROPERTY_VALUE_MAX];
- #ifdefGENERIC_AUDIO
- hw=newAudioHardwareGeneric();
- #else
- //ifrunninginemulation-usetheemulatordriver
- if(property_get("ro.kernel.qemu",value,0)){
- LOGD("Runninginemulation-usinggenericaudiodriver");
- hw=newAudioHardwareGeneric();
- }
- else{
- LOGV("CreatingVendorSpecificAudioHardware");
- hw=createAudioHardware();
- }
- #endif
- if(hw->initCheck()!=NO_ERROR){
- LOGW("Usingstubbedaudiohardware.Nosoundwillbeproduced.");
- deletehw;
- hw=newAudioHardwareStub();
- }
- #ifdefWITH_A2DP
- hw=newA2dpAudioInterface(hw);
- #endif
- #ifdefENABLE_AUDIO_DUMP
- //ThiscodeaddsarecordofbuffersinafiletowritecallsmadebyAudioFlinger.
- //ItreplacesthecurrentAudioHardwareInterfaceobjectbyanintermediateonewhich
- //willrecordbuffersinafile(aftersendingthemtohardware)fortestingpurpose.
- //ThisfeatureisenabledbydefiningsymbolENABLE_AUDIO_DUMP.
- //TheoutputfileissetwithsetParameters("test_cmd_file_name=<name>").Pausearenotrecordedinthefile.
- LOGV("openingPCMdumpinterface");
- hw=newAudioDumpInterface(hw);//replaceinterface
- #endif
- returnhw;
- }
hw = new A2dpAudioInterface(hw);
注意红色部分hw,为什么A2dpAudioInterface还需要createAudioHardware()打开的AudioHardwareInterface(我们假设这是ALSA设备接口)呢?如我们所知,BluetoothA2DP与ALSA设备并不走同一套接口,因此Android的设计者就把ALSA设备接口扔到A2DP接口里面管理了。这又是如何管理呢?简单来说,就是根据上层传下来的参数devices,判断devices是否是DEVICE_OUT_BLUETOOTH_A2DP,如果是则走A2DP接口,如果不是则走ALSA设备接口。例如需要打开一个音频输出流时:
- AudioStreamOut*A2dpAudioInterface::openOutputStream(
- uint32_tdevices,int*format,uint32_t*channels,uint32_t*sampleRate,status_t*status)
- {
- if(!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)){
- LOGV("A2dpAudioInterface::openOutputStream()openHWdevice:%x",devices);
- returnmHardwareInterface->openOutputStream(devices,format,channels,sampleRate,status);
- }
- status_terr=0;
- //onlyoneoutputstreamallowed
- if(mOutput){
- if(status)
- *status=-1;
- returnNULL;
- }
- //createnewoutputstream
- A2dpAudioStreamOut*out=newA2dpAudioStreamOut();
- if((err=out->set(devices,format,channels,sampleRate))==NO_ERROR){
- mOutput=out;
- mOutput->setBluetoothEnabled(mBluetoothEnabled);
- mOutput->setSuspended(mSuspended);
- }else{
- deleteout;
- }
- if(status)
- *status=err;
- returnmOutput;
- }
liba2dp
到了A2dpAudioInterface这层,就是访问BlueZ的音频操作接口了,主要是external\bluetooth\bluez\audio\liba2dp.c。liba2dp.c代码或许很复杂,我也没有深入了解过,但是接口却非常简单易用。看liba2dp.h,仅仅只有几个接口:
- inta2dp_init(intrate,intchannels,a2dpData*dataPtr);
- voida2dp_set_sink(a2dpDatadata,constchar*address);
- inta2dp_write(a2dpDatadata,constvoid*buffer,intcount);
- inta2dp_stop(a2dpDatadata);
- voida2dp_cleanup(a2dpDatadata);
a2dp_set_sink:绑定一个蓝牙地址address到a2dpData上;
a2dp_write:往a2dp写入音频PCM数据;
a2dp_stop:停止a2dp播放。
例如,每当有音频PCM数据需要送入Bluetooth时:
- ssize_tA2dpAudioInterface::A2dpAudioStreamOut::write(constvoid*buffer,size_tbytes)
- {
- Mutex::Autolocklock(mLock);
- size_tremaining=bytes;
- status_tstatus=-1;
- if(!mBluetoothEnabled||mClosing||mSuspended){
- LOGV("A2dpAudioStreamOut::write(),butbluetoothdisabled\
- mBluetoothEnabled%d,mClosing%d,mSuspended%d",
- mBluetoothEnabled,mClosing,mSuspended);
- gotoError;
- }
- status=init();
- if(status<0)
- gotoError;
- while(remaining>0){
- status=a2dp_write(mData,buffer,remaining);
- if(status<=0){
- LOGE("a2dp_writefailederr:%d\n",status);
- gotoError;
- }
- remaining-=status;
- buffer=((char*)buffer)+status;
- }
- mStandby=false;
- returnbytes;
- Error:
- //Simulateaudiooutputtimingincaseoferror
- usleep(((bytes*1000)/frameSize()/sampleRate())*1000);
- returnstatus;
- }
该函数在AudioFlinger::MixerThread::threadLoop()调用,下面简要介绍音频数据从上层到底层硬件设备的传输流向过程。
写在之前
本来有打算写写Android音频系统的,但是仔细研究了如下链接的三篇文章,果断中断了我的想法。毫不夸张来说,这是我看过的最好的阐述Android音频系统的文章了,简练精辟,将音频系统各个方面的重要的脉络都描述出来了。有这三篇文章,理解Android音频系统何止加快了10倍。
Android Audio System 之一:AudioTrack如何与AudioFlinger交换音频数据
Android Audio System 之二:AudioFlinger
Android Audio System 之三: AudioPolicyService 和 AudioPolicyManager
A2dpAudioInterface
Android音频系统有两大服务:一是AudioFlinger,二是AudioPolicyService。AudioFlinger负责向下访问AudioHardwareInterface,实现音频PCM数据的混音/输入/输出,实现音量调节;AudioPolicyService负责音频输入输出设备的连接状态,音频策略调度即音频设备(如本地CODEC、Bluetooth A2DP、Headset)的切换策略(注意它只是负责策略,真正的切换操作是在AudioFlinger中的openOutput,毕竟AudioFlinger负责操作底层音频硬件)。AudioPolicyService在以后的章节详细分析,这里主要探讨A2DP-Audio是如何注册到AudioFlinger中,并简要提及音频PCM数据流向。
好的平台软件应有这样的一个抽象层:向下提供一套固定的接口,不同的硬件设备根据这些接口实现各自的方法,然后注册到这个抽象层中去。这样对于上层应用而言并没有任何区别,因为上层只需调用抽象层接口就行了,不管底层硬件的差异性。AudioFlinger就是这样的一个抽象层,无论底层是ALSA设备还是BluetoothHeadset,上层都只会看到AudioFlinger的接口。至于何时切换到ALSA设备何时切换到BluetoothHeadset,这就属于音频策略调度范畴了即AudioPolicyService。
- AudioFlinger::AudioFlinger()
- :BnAudioFlinger(),
- mAudioHardware(0),mMasterVolume(1.0f),mMasterMute(false),mNextUniqueId(1)
- {
- mHardwareStatus=AUDIO_HW_IDLE;
- mAudioHardware=AudioHardwareInterface::create();
- ......
- AudioHardwareInterface*AudioHardwareInterface::create()
- {
- /*
- *FIXME:Thiscodeneedstoinstantiatethecorrectaudiodevice
- *interface.Fornow-weusecompile-timeswitches.
- */
- AudioHardwareInterface*hw=0;
- charvalue[PROPERTY_VALUE_MAX];
- #ifdefGENERIC_AUDIO
- hw=newAudioHardwareGeneric();
- #else
- //ifrunninginemulation-usetheemulatordriver
- if(property_get("ro.kernel.qemu",value,0)){
- LOGD("Runninginemulation-usinggenericaudiodriver");
- hw=newAudioHardwareGeneric();
- }
- else{
- LOGV("CreatingVendorSpecificAudioHardware");
- hw=createAudioHardware();
- }
- #endif
- if(hw->initCheck()!=NO_ERROR){
- LOGW("Usingstubbedaudiohardware.Nosoundwillbeproduced.");
- deletehw;
- hw=newAudioHardwareStub();
- }
- #ifdefWITH_A2DP
- hw=newA2dpAudioInterface(hw);
- #endif
- #ifdefENABLE_AUDIO_DUMP
- //ThiscodeaddsarecordofbuffersinafiletowritecallsmadebyAudioFlinger.
- //ItreplacesthecurrentAudioHardwareInterfaceobjectbyanintermediateonewhich
- //willrecordbuffersinafile(aftersendingthemtohardware)fortestingpurpose.
- //ThisfeatureisenabledbydefiningsymbolENABLE_AUDIO_DUMP.
- //TheoutputfileissetwithsetParameters("test_cmd_file_name=<name>").Pausearenotrecordedinthefile.
- LOGV("openingPCMdumpinterface");
- hw=newAudioDumpInterface(hw);//replaceinterface
- #endif
- returnhw;
- }
hw = new A2dpAudioInterface(hw);
注意红色部分hw,为什么A2dpAudioInterface还需要createAudioHardware()打开的AudioHardwareInterface(我们假设这是ALSA设备接口)呢?如我们所知,BluetoothA2DP与ALSA设备并不走同一套接口,因此Android的设计者就把ALSA设备接口扔到A2DP接口里面管理了。这又是如何管理呢?简单来说,就是根据上层传下来的参数devices,判断devices是否是DEVICE_OUT_BLUETOOTH_A2DP,如果是则走A2DP接口,如果不是则走ALSA设备接口。例如需要打开一个音频输出流时:
- AudioStreamOut*A2dpAudioInterface::openOutputStream(
- uint32_tdevices,int*format,uint32_t*channels,uint32_t*sampleRate,status_t*status)
- {
- if(!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)){
- LOGV("A2dpAudioInterface::openOutputStream()openHWdevice:%x",devices);
- returnmHardwareInterface->openOutputStream(devices,format,channels,sampleRate,status);
- }
- status_terr=0;
- //onlyoneoutputstreamallowed
- if(mOutput){
- if(status)
- *status=-1;
- returnNULL;
- }
- //createnewoutputstream
- A2dpAudioStreamOut*out=newA2dpAudioStreamOut();
- if((err=out->set(devices,format,channels,sampleRate))==NO_ERROR){
- mOutput=out;
- mOutput->setBluetoothEnabled(mBluetoothEnabled);
- mOutput->setSuspended(mSuspended);
- }else{
- deleteout;
- }
- if(status)
- *status=err;
- returnmOutput;
- }
liba2dp
到了A2dpAudioInterface这层,就是访问BlueZ的音频操作接口了,主要是external\bluetooth\bluez\audio\liba2dp.c。liba2dp.c代码或许很复杂,我也没有深入了解过,但是接口却非常简单易用。看liba2dp.h,仅仅只有几个接口:
- inta2dp_init(intrate,intchannels,a2dpData*dataPtr);
- voida2dp_set_sink(a2dpDatadata,constchar*address);
- inta2dp_write(a2dpDatadata,constvoid*buffer,intcount);
- inta2dp_stop(a2dpDatadata);
- voida2dp_cleanup(a2dpDatadata);
a2dp_set_sink:绑定一个蓝牙地址address到a2dpData上;
a2dp_write:往a2dp写入音频PCM数据;
a2dp_stop:停止a2dp播放。
例如,每当有音频PCM数据需要送入Bluetooth时:
- ssize_tA2dpAudioInterface::A2dpAudioStreamOut::write(constvoid*buffer,size_tbytes)
- {
- Mutex::Autolocklock(mLock);
- size_tremaining=bytes;
- status_tstatus=-1;
- if(!mBluetoothEnabled||mClosing||mSuspended){
- LOGV("A2dpAudioStreamOut::write(),butbluetoothdisabled\
- mBluetoothEnabled%d,mClosing%d,mSuspended%d",
- mBluetoothEnabled,mClosing,mSuspended);
- gotoError;
- }
- status=init();
- if(status<0)
- gotoError;
- while(remaining>0){
- status=a2dp_write(mData,buffer,remaining);
- if(status<=0){
- LOGE("a2dp_writefailederr:%d\n",status);
- gotoError;
- }
- remaining-=status;
- buffer=((char*)buffer)+status;
- }
- mStandby=false;
- returnbytes;
- Error:
- //Simulateaudiooutputtimingincaseoferror
- usleep(((bytes*1000)/frameSize()/sampleRate())*1000);
- returnstatus;
- }
该函数在AudioFlinger::MixerThread::threadLoop()调用,下面简要介绍音频数据从上层到底层硬件设备的传输流向过程。
相关推荐
北京散记(一)作文.doc
湘行散记 名著导读.doc
mtk散记1到3.docmtk散记1到3.doc mtk散记1到3.docmtk散记1到3.doc mtk散记1到3.docmtk散记1到3.doc
形散而神聚。因为是散文,所以没有框架结构的限制,作者写起来轻松,读者读起来方便,数千字成一文,每篇文章都是一个独立主题的探讨,又都紧密围绕ERP项目实施这一主题。
散记 关于 演示Android应用程序,用于。 谷歌认证 资源 。 用法 在preferences.xml将此值替换为自己的类似物: [APP_ID] Facebook身份验证 资源 用法 在preferences.xml将此值替换为自己的类似物: [APP_ID] ...
鄂教版七年级上语文:第17课《牧鹅散记》同步练习精选.doc
OBLOG 浮生散记
湘行散记名著导读.doc
北京散记(四)作文.doc
北京散记(十二)作文.doc
北京散记(十)作文.doc
北京散记(三)作文.doc
北京散记(六)作文.doc
北京散记(八)作文.doc
巴隆国际猎场散记[规整].pdf
《湘行散记》读后感.doc
【北戴河散记写景美文】-北戴河美文.docx
Maven游乐场一般信息什么时候怎样散记推荐链接: