`
20386053
  • 浏览: 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。


  1. AudioFlinger::AudioFlinger()
  2. :BnAudioFlinger(),
  3. mAudioHardware(0),mMasterVolume(1.0f),mMasterMute(false),mNextUniqueId(1)
  4. {
  5. mHardwareStatus=AUDIO_HW_IDLE;
  6. mAudioHardware=AudioHardwareInterface::create();
  7. ......
再看AudioHardwareInterface::create():
  1. AudioHardwareInterface*AudioHardwareInterface::create()
  2. {
  3. /*
  4. *FIXME:Thiscodeneedstoinstantiatethecorrectaudiodevice
  5. *interface.Fornow-weusecompile-timeswitches.
  6. */
  7. AudioHardwareInterface*hw=0;
  8. charvalue[PROPERTY_VALUE_MAX];
  9. #ifdefGENERIC_AUDIO
  10. hw=newAudioHardwareGeneric();
  11. #else
  12. //ifrunninginemulation-usetheemulatordriver
  13. if(property_get("ro.kernel.qemu",value,0)){
  14. LOGD("Runninginemulation-usinggenericaudiodriver");
  15. hw=newAudioHardwareGeneric();
  16. }
  17. else{
  18. LOGV("CreatingVendorSpecificAudioHardware");
  19. hw=createAudioHardware();
  20. }
  21. #endif
  22. if(hw->initCheck()!=NO_ERROR){
  23. LOGW("Usingstubbedaudiohardware.Nosoundwillbeproduced.");
  24. deletehw;
  25. hw=newAudioHardwareStub();
  26. }
  27. #ifdefWITH_A2DP
  28. hw=newA2dpAudioInterface(hw);
  29. #endif
  30. #ifdefENABLE_AUDIO_DUMP
  31. //ThiscodeaddsarecordofbuffersinafiletowritecallsmadebyAudioFlinger.
  32. //ItreplacesthecurrentAudioHardwareInterfaceobjectbyanintermediateonewhich
  33. //willrecordbuffersinafile(aftersendingthemtohardware)fortestingpurpose.
  34. //ThisfeatureisenabledbydefiningsymbolENABLE_AUDIO_DUMP.
  35. //TheoutputfileissetwithsetParameters("test_cmd_file_name=<name>").Pausearenotrecordedinthefile.
  36. LOGV("openingPCMdumpinterface");
  37. hw=newAudioDumpInterface(hw);//replaceinterface
  38. #endif
  39. returnhw;
  40. }
这个函数我在ANDROID2.3音频系统HAL有简要的分析,现在我们接着往下看看A2DP的注册:

hw = new A2dpAudioInterface(hw);

注意红色部分hw,为什么A2dpAudioInterface还需要createAudioHardware()打开的AudioHardwareInterface(我们假设这是ALSA设备接口)呢?如我们所知,BluetoothA2DP与ALSA设备并不走同一套接口,因此Android的设计者就把ALSA设备接口扔到A2DP接口里面管理了。这又是如何管理呢?简单来说,就是根据上层传下来的参数devices,判断devices是否是DEVICE_OUT_BLUETOOTH_A2DP,如果是则走A2DP接口,如果不是则走ALSA设备接口。例如需要打开一个音频输出流时:

  1. AudioStreamOut*A2dpAudioInterface::openOutputStream(
  2. uint32_tdevices,int*format,uint32_t*channels,uint32_t*sampleRate,status_t*status)
  3. {
  4. if(!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)){
  5. LOGV("A2dpAudioInterface::openOutputStream()openHWdevice:%x",devices);
  6. returnmHardwareInterface->openOutputStream(devices,format,channels,sampleRate,status);
  7. }
  8. status_terr=0;
  9. //onlyoneoutputstreamallowed
  10. if(mOutput){
  11. if(status)
  12. *status=-1;
  13. returnNULL;
  14. }
  15. //createnewoutputstream
  16. A2dpAudioStreamOut*out=newA2dpAudioStreamOut();
  17. if((err=out->set(devices,format,channels,sampleRate))==NO_ERROR){
  18. mOutput=out;
  19. mOutput->setBluetoothEnabled(mBluetoothEnabled);
  20. mOutput->setSuspended(mSuspended);
  21. }else{
  22. deleteout;
  23. }
  24. if(status)
  25. *status=err;
  26. returnmOutput;
  27. }
当上层传下来的devices不属于A2DP设备时,则return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);其中mHardwareInterface保存的是ALSA的hw。否则A2dpAudioStreamOut* out = new A2dpAudioStreamOut();为A2DP打开一个音频输出流。


liba2dp

到了A2dpAudioInterface这层,就是访问BlueZ的音频操作接口了,主要是external\bluetooth\bluez\audio\liba2dp.c。liba2dp.c代码或许很复杂,我也没有深入了解过,但是接口却非常简单易用。看liba2dp.h,仅仅只有几个接口:

  1. inta2dp_init(intrate,intchannels,a2dpData*dataPtr);
  2. voida2dp_set_sink(a2dpDatadata,constchar*address);
  3. inta2dp_write(a2dpDatadata,constvoid*buffer,intcount);
  4. inta2dp_stop(a2dpDatadata);
  5. voida2dp_cleanup(a2dpDatadata);
a2dp_init:根据传入来的采样率rate,声道数channels初始化一个a2dpData;

a2dp_set_sink:绑定一个蓝牙地址address到a2dpData上;

a2dp_write:往a2dp写入音频PCM数据;

a2dp_stop:停止a2dp播放。


例如,每当有音频PCM数据需要送入Bluetooth时:

  1. ssize_tA2dpAudioInterface::A2dpAudioStreamOut::write(constvoid*buffer,size_tbytes)
  2. {
  3. Mutex::Autolocklock(mLock);
  4. size_tremaining=bytes;
  5. status_tstatus=-1;
  6. if(!mBluetoothEnabled||mClosing||mSuspended){
  7. LOGV("A2dpAudioStreamOut::write(),butbluetoothdisabled\
  8. mBluetoothEnabled%d,mClosing%d,mSuspended%d",
  9. mBluetoothEnabled,mClosing,mSuspended);
  10. gotoError;
  11. }
  12. status=init();
  13. if(status<0)
  14. gotoError;
  15. while(remaining>0){
  16. status=a2dp_write(mData,buffer,remaining);
  17. if(status<=0){
  18. LOGE("a2dp_writefailederr:%d\n",status);
  19. gotoError;
  20. }
  21. remaining-=status;
  22. buffer=((char*)buffer)+status;
  23. }
  24. mStandby=false;
  25. returnbytes;
  26. Error:
  27. //Simulateaudiooutputtimingincaseoferror
  28. usleep(((bytes*1000)/frameSize()/sampleRate())*1000);
  29. returnstatus;
  30. }
核心语句:status = a2dp_write(mData, buffer, remaining); 只需要传入音频数据的首地址和大小就行了。

该函数在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。


  1. AudioFlinger::AudioFlinger()
  2. :BnAudioFlinger(),
  3. mAudioHardware(0),mMasterVolume(1.0f),mMasterMute(false),mNextUniqueId(1)
  4. {
  5. mHardwareStatus=AUDIO_HW_IDLE;
  6. mAudioHardware=AudioHardwareInterface::create();
  7. ......
再看AudioHardwareInterface::create():
  1. AudioHardwareInterface*AudioHardwareInterface::create()
  2. {
  3. /*
  4. *FIXME:Thiscodeneedstoinstantiatethecorrectaudiodevice
  5. *interface.Fornow-weusecompile-timeswitches.
  6. */
  7. AudioHardwareInterface*hw=0;
  8. charvalue[PROPERTY_VALUE_MAX];
  9. #ifdefGENERIC_AUDIO
  10. hw=newAudioHardwareGeneric();
  11. #else
  12. //ifrunninginemulation-usetheemulatordriver
  13. if(property_get("ro.kernel.qemu",value,0)){
  14. LOGD("Runninginemulation-usinggenericaudiodriver");
  15. hw=newAudioHardwareGeneric();
  16. }
  17. else{
  18. LOGV("CreatingVendorSpecificAudioHardware");
  19. hw=createAudioHardware();
  20. }
  21. #endif
  22. if(hw->initCheck()!=NO_ERROR){
  23. LOGW("Usingstubbedaudiohardware.Nosoundwillbeproduced.");
  24. deletehw;
  25. hw=newAudioHardwareStub();
  26. }
  27. #ifdefWITH_A2DP
  28. hw=newA2dpAudioInterface(hw);
  29. #endif
  30. #ifdefENABLE_AUDIO_DUMP
  31. //ThiscodeaddsarecordofbuffersinafiletowritecallsmadebyAudioFlinger.
  32. //ItreplacesthecurrentAudioHardwareInterfaceobjectbyanintermediateonewhich
  33. //willrecordbuffersinafile(aftersendingthemtohardware)fortestingpurpose.
  34. //ThisfeatureisenabledbydefiningsymbolENABLE_AUDIO_DUMP.
  35. //TheoutputfileissetwithsetParameters("test_cmd_file_name=<name>").Pausearenotrecordedinthefile.
  36. LOGV("openingPCMdumpinterface");
  37. hw=newAudioDumpInterface(hw);//replaceinterface
  38. #endif
  39. returnhw;
  40. }
这个函数我在ANDROID2.3音频系统HAL有简要的分析,现在我们接着往下看看A2DP的注册:

hw = new A2dpAudioInterface(hw);

注意红色部分hw,为什么A2dpAudioInterface还需要createAudioHardware()打开的AudioHardwareInterface(我们假设这是ALSA设备接口)呢?如我们所知,BluetoothA2DP与ALSA设备并不走同一套接口,因此Android的设计者就把ALSA设备接口扔到A2DP接口里面管理了。这又是如何管理呢?简单来说,就是根据上层传下来的参数devices,判断devices是否是DEVICE_OUT_BLUETOOTH_A2DP,如果是则走A2DP接口,如果不是则走ALSA设备接口。例如需要打开一个音频输出流时:

  1. AudioStreamOut*A2dpAudioInterface::openOutputStream(
  2. uint32_tdevices,int*format,uint32_t*channels,uint32_t*sampleRate,status_t*status)
  3. {
  4. if(!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)){
  5. LOGV("A2dpAudioInterface::openOutputStream()openHWdevice:%x",devices);
  6. returnmHardwareInterface->openOutputStream(devices,format,channels,sampleRate,status);
  7. }
  8. status_terr=0;
  9. //onlyoneoutputstreamallowed
  10. if(mOutput){
  11. if(status)
  12. *status=-1;
  13. returnNULL;
  14. }
  15. //createnewoutputstream
  16. A2dpAudioStreamOut*out=newA2dpAudioStreamOut();
  17. if((err=out->set(devices,format,channels,sampleRate))==NO_ERROR){
  18. mOutput=out;
  19. mOutput->setBluetoothEnabled(mBluetoothEnabled);
  20. mOutput->setSuspended(mSuspended);
  21. }else{
  22. deleteout;
  23. }
  24. if(status)
  25. *status=err;
  26. returnmOutput;
  27. }
当上层传下来的devices不属于A2DP设备时,则return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);其中mHardwareInterface保存的是ALSA的hw。否则A2dpAudioStreamOut* out = new A2dpAudioStreamOut();为A2DP打开一个音频输出流。


liba2dp

到了A2dpAudioInterface这层,就是访问BlueZ的音频操作接口了,主要是external\bluetooth\bluez\audio\liba2dp.c。liba2dp.c代码或许很复杂,我也没有深入了解过,但是接口却非常简单易用。看liba2dp.h,仅仅只有几个接口:

  1. inta2dp_init(intrate,intchannels,a2dpData*dataPtr);
  2. voida2dp_set_sink(a2dpDatadata,constchar*address);
  3. inta2dp_write(a2dpDatadata,constvoid*buffer,intcount);
  4. inta2dp_stop(a2dpDatadata);
  5. voida2dp_cleanup(a2dpDatadata);
a2dp_init:根据传入来的采样率rate,声道数channels初始化一个a2dpData;

a2dp_set_sink:绑定一个蓝牙地址address到a2dpData上;

a2dp_write:往a2dp写入音频PCM数据;

a2dp_stop:停止a2dp播放。


例如,每当有音频PCM数据需要送入Bluetooth时:

  1. ssize_tA2dpAudioInterface::A2dpAudioStreamOut::write(constvoid*buffer,size_tbytes)
  2. {
  3. Mutex::Autolocklock(mLock);
  4. size_tremaining=bytes;
  5. status_tstatus=-1;
  6. if(!mBluetoothEnabled||mClosing||mSuspended){
  7. LOGV("A2dpAudioStreamOut::write(),butbluetoothdisabled\
  8. mBluetoothEnabled%d,mClosing%d,mSuspended%d",
  9. mBluetoothEnabled,mClosing,mSuspended);
  10. gotoError;
  11. }
  12. status=init();
  13. if(status<0)
  14. gotoError;
  15. while(remaining>0){
  16. status=a2dp_write(mData,buffer,remaining);
  17. if(status<=0){
  18. LOGE("a2dp_writefailederr:%d\n",status);
  19. gotoError;
  20. }
  21. remaining-=status;
  22. buffer=((char*)buffer)+status;
  23. }
  24. mStandby=false;
  25. returnbytes;
  26. Error:
  27. //Simulateaudiooutputtimingincaseoferror
  28. usleep(((bytes*1000)/frameSize()/sampleRate())*1000);
  29. returnstatus;
  30. }
核心语句:status = a2dp_write(mData, buffer, remaining); 只需要传入音频数据的首地址和大小就行了。

该函数在AudioFlinger::MixerThread::threadLoop()调用,下面简要介绍音频数据从上层到底层硬件设备的传输流向过程。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics