http://www.rosoo.net/a/201202/15644.html ANDROID音频系统散记之一:A2dpAudioInterface 本来有打算写写Android音频系统的,但是仔细研究了如下链接的三篇文章,果断中断了我的想法。毫不夸张来说,这是我看过的最好的阐述 Android音频系统的文章了,简练精辟,将音频系统各个方面的重要的脉络都描述出来了。有这三篇文章,理解Android音频系统何止加快了10倍 TAG: 蓝牙 音频系统 A2dp

写在之前

本来有打算写写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>")
  36. //.Pausearenotrecordedinthefile.
  37. LOGV("openingPCMdumpinterface");
  38. hw=newAudioDumpInterface(hw);//replaceinterface
  39. #endif
  40. returnhw;
  41. }

这个函数我在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
  3. ,uint32_t*sampleRate,status_t*status)
  4. {
  5. if(!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)){
  6. LOGV("A2dpAudioInterface::openOutputStream()openHWdevice:%x",devices);
  7. returnmHardwareInterface->openOutputStream(devices,format
  8. ,channels,sampleRate,status);
  9. }
  10. status_terr=0;
  11. //onlyoneoutputstreamallowed
  12. if(mOutput){
  13. if(status)
  14. *status=-1;
  15. returnNULL;
  16. }
  17. //createnewoutputstream
  18. A2dpAudioStreamOut*out=newA2dpAudioStreamOut();
  19. if((err=out->set(devices,format,channels,sampleRate))==NO_ERROR){
  20. mOutput=out;
  21. mOutput->setBluetoothEnabled(mBluetoothEnabled);
  22. mOutput->setSuspended(mSuspended);
  23. }else{
  24. deleteout;
  25. }
  26. if(status)
  27. *status=err;
  28. returnmOutput;
  29. }

当上层传下来的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 Audio System 之一:AudioTrack如何与AudioFlinger交换

落鹤生 发布于 2012-04-01 13:50 点击: 1143次

Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFlinger把所有的AudioTrack进行混合(Mixer),然后输送到AudioHardware中 进行播放

TAG: 音频系统 AudioFlinger AudioTrack

引子

Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFlinger把所有的AudioTrack进行混合(Mixer),然后输送到AudioHardware中 进行播放,目前Android的Froyo版本设定了同时最多可以创建32个音频流,也就是说,Mixer最多会同时处理32个AudioTrack的数 据流。

如何使用AudioTrack

AudioTrack的主要代码位于 frameworks/base/media/libmedia/audiotrack.cpp中。现在先通过一个例子来了解一下如何使用 AudioTrack,ToneGenerator是android中产生电话拨号音和其他音调波形的一个实现,我们就以它为例子:

ToneGenerator的初始化函数:

                                                
  1. boolToneGenerator::initAudioTrack(){
  2. //Openaudiotrackinmono,PCM16bit
  3. //,defaultsamplingrate,defaultbuffersize
  4. mpAudioTrack=newAudioTrack();
  5. mpAudioTrack->set(mStreamType,
  6. 0,
  7. AudioSystem::PCM_16_BIT,
  8. AudioSystem::CHANNEL_OUT_MONO,
  9. 0,
  10. 0,
  11. audioCallback,
  12. this,
  13. 0,
  14. 0,
  15. mThreadCanCallJava);
  16. if(mpAudioTrack->initCheck()!=NO_ERROR){
  17. LOGE("AudioTrack->initCheckfailed");
  18. gotoinitAudioTrack_exit;
  19. }
  20. mpAudioTrack->setVolume(mVolume,mVolume);
  21. mState=TONE_INIT;
  22. ......
  23. }

可见,创建步骤很简单,先new一个AudioTrack的实例,然后调用set成员函数完成参数的设置并注册到AudioFlinger中,然后可以调 用其他诸如设置音量等函数进一步设置音频参数。其中,一个重要的参数是audioCallback,audioCallback是一个回调函数,负责响应 AudioTrack的通知,例如填充数据、循环播放、播放位置触发等等。回调函数的写法通常像这样:

                                                
  1. voidToneGenerator::audioCallback(intevent,void*user,void*info){
  2. if(event!=AudioTrack::EVENT_MORE_DATA)return;
  3. AudioTrack::Buffer*buffer=static_cast<AudioTrack::Buffer*>(info);
  4. ToneGenerator*lpToneGen=static_cast<ToneGenerator*>(user);
  5. short*lpOut=buffer->i16;
  6. unsignedintlNumSmp=buffer->size/sizeof(short);
  7. constToneDescriptor*lpToneDesc=lpToneGen->mpToneDesc;
  8. if(buffer->size==0)return;
  9. //Clearoutputbuffer:WaveGeneratoraccumulatesintolpOutbuffer
  10. memset(lpOut,0,buffer->size);
  11. ......
  12. //以下是产生音调数据的代码,略....
  13. }

该函数首先判断事件的类型是否是EVENT_MORE_DATA,如果是,则后续的代码会填充相应的音频数据后返回,当然你可以处理其他事件,以下是可用的事件类型:

                                                
  1. enumevent_type{
  2. EVENT_MORE_DATA=0,
  3. //RequesttowritemoredatatoPCMbuffer.
  4. EVENT_UNDERRUN=1,
  5. //PCMbufferunderrunoccured.
  6. EVENT_LOOP_END=2,
  7. //Sampleloopendwasreached;playbackrestartedfromloopstartifloopcountwasnot0.
  8. EVENT_MARKER=3,
  9. //Playbackheadisatthespecifiedmarkerposition(SeesetMarkerPosition()).
  10. EVENT_NEW_POS=4,
  11. //Playbackheadisatanewposition(SeesetPositionUpdatePeriod()).
  12. EVENT_BUFFER_END=5
  13. //Playbackheadisattheendofthebuffer.
  14. };

开始播放:

                                                
  1. mpAudioTrack->start();

停止播放:

                                                
  1. mpAudioTrack->stop();

只要简单地调用成员函数start()和stop()即可。

AudioTrack和AudioFlinger的通信机制

通常,AudioTrack和AudioFlinger并不在同一个进程中,它们通过android中的binder机制建立联系。

AudioFlinger是android中的一个service,在android启动时就已经被加载。下面这张图展示了他们两个的关系:

ANDROID音频系统散记之一:A2dpAudioInterface && Android Audio System_第1张图片

图一 AudioTrack和AudioFlinger的关系

我们可以这样理解这张图的含义:

  • audio_track_cblk_t实现了一个环形FIFO;
  • AudioTrack是FIFO的数据生产者;
  • AudioFlinger是FIFO的数据消费者。

建立联系的过程

下面的序列图展示了AudioTrack和AudioFlinger建立联系的过程:

ANDROID音频系统散记之一:A2dpAudioInterface && Android Audio System_第2张图片

图二 AudioTrack和AudioFlinger建立联系

解释一下过程:

  • Framework或者Java层通过JNI,new AudioTrack();
  • 根据StreamType等参数,通过一系列的调用getOutput();
  • 如有必要,AudioFlinger根据StreamType打开不同硬件设备;
  • AudioFlinger为该输出设备创建混音线程: MixerThread(),并把该线程的id作为getOutput()的返回值返回给AudioTrack;
  • AudioTrack通过binder机制调用AudioFlinger的createTrack();
  • AudioFlinger注册该AudioTrack到MixerThread中;
  • AudioFlinger创建一个用于控制的TrackHandle,并以IAudioTrack这一接口作为createTrack()的返回值;
  • AudioTrack通过IAudioTrack接口,得到在AudioFlinger中创建的FIFO(audio_track_cblk_t);
  • AudioTrack创建自己的监控线程:AudioTrackThread;

自此,AudioTrack建立了和AudioFlinger的全部联系工作,接下来,AudioTrack可以:

  • 通过IAudioTrack接口控制该音轨的状态,例如start,stop,pause等等;
  • 通过对FIFO的写入,实现连续的音频播放
  • 监控线程监控事件的发生,并通过audioCallback回调函数与用户程序进行交互;

FIFO的管理

audio_track_cblk_t

audio_track_cblk_t这个结构是FIFO实现的关键,该结构是在createTrack的时候,由AudioFlinger申请相 应的内存,然后通过IMemory接口返回AudioTrack的,这样AudioTrack和AudioFlinger管理着同一个 audio_track_cblk_t,通过它实现了环形FIFO,AudioTrack向FIFO中写入音频数据,AudioFlinger从FIFO 中读取音频数据,经Mixer后送给AudioHardware进行播放。

audio_track_cblk_t的主要数据成员:

user --AudioTrack当前的写位置的偏移
userBase -- AudioTrack写偏移的基准位置,结合user的值方可确定真实的FIFO地址指针
server --AudioFlinger当前的读位置的偏移
serverBase -- AudioFlinger读偏移的基准位置,结合server的值方可确定真实的FIFO地址指针

frameCount-- FIFO的大小,以音频数据的帧为单位,16bit的音频每帧的大小是2字节

buffers -- 指向FIFO的起始地址

out -- 音频流的方向,对于AudioTrack,out=1,对于AudioRecord,out=0

audio_track_cblk_t的主要成员函数:

framesAvailable_l()和framesAvailable()用于获取FIFO中可写的空闲空间的大小,只是加锁和不加锁的区别。

                                                
  1. uint32_taudio_track_cblk_t::framesAvailable_l()
  2. {
  3. uint32_tu=this->user;
  4. uint32_ts=this->server;
  5. if(out){
  6. uint32_tlimit=(s<loopStart)?s:loopStart;
  7. returnlimit+frameCount-u;
  8. }else{
  9. returnframeCount+u-s;
  10. }
  11. }

framesReady()用于获取FIFO中可读取的空间大小。

                                                
  1. uint32_taudio_track_cblk_t::framesReady()
  2. {
  3. uint32_tu=this->user;
  4. uint32_ts=this->server;
  5. if(out){
  6. if(u<loopEnd){
  7. returnu-s;
  8. }else{
  9. Mutex::Autolock_l(lock);
  10. if(loopCount>=0){
  11. return(loopEnd-loopStart)*loopCount+u-s;
  12. }else{
  13. returnUINT_MAX;
  14. }
  15. }
  16. }else{
  17. returns-u;
  18. }
  19. }

我们看看下面的示意图:

_____________________________________________

^ ^ ^ ^

buffer_start server(s) user(u) buffer_end

很明显,frameReady = u - s,frameAvalible = frameCount - frameReady = frameCount -u + s

可能有人会问,应为这是一个环形的buffer,一旦user越过了buffer_end以后,应该会发生下面的情况:

_____________________________________________

^^ ^ ^

buffer_startuser(u)server(s) buffer_end

这时候u在s的前面,用上面的公式计算就会错误,但是android使用了一些技巧,保证了上述公式一直成立。我们先看完下面三个函数的代码再分析:

                                                
  1. uint32_taudio_track_cblk_t::stepUser(uint32_tframeCount)
  2. {
  3. uint32_tu=this->user;
  4. u+=frameCount;
  5. ......
  6. if(u>=userBase+this->frameCount){
  7. userBase+=this->frameCount;
  8. }
  9. this->user=u;
  10. ......
  11. returnu;
  12. }
                                                
  1. boolaudio_track_cblk_t::stepServer(uint32_tframeCount)
  2. {
  3. //thecodebelowsimulateslock-with-timeout
  4. //weMUSTdothistoprotecttheAudioFlingerserver
  5. //asthislockissharedwiththeclient.
  6. status_terr;
  7. err=lock.tryLock();
  8. if(err==-EBUSY){//justwaitabit
  9. usleep(1000);
  10. err=lock.tryLock();
  11. }
  12. if(err!=NO_ERROR){
  13. //probably,theclientjustdied.
  14. returnfalse;
  15. }
  16. uint32_ts=this->server;
  17. s+=frameCount;
  18. //省略部分代码
  19. //......
  20. if(s>=serverBase+this->frameCount){
  21. serverBase+=this->frameCount;
  22. }
  23. this->server=s;
  24. cv.signal();
  25. lock.unlock();
  26. returntrue;
  27. }
                                                
  1. void*audio_track_cblk_t::buffer(uint32_toffset)const
  2. {
  3. return(int8_t*)this->buffers+(offset-userBase)*this->frameSize;
  4. }

stepUser()和stepServer的作用是调整当前偏移的位置,可以看到,他们仅仅是把成员变量user或server的值加上需要移动 的数量,user和server的值并不考虑FIFO的边界问题,随着数据的不停写入和读出,user和server的值不断增加,只要处理得 当,user总是出现在server的后面,因此frameAvalible()和frameReady()中的算法才会一直成立。根据这种算 法,user和server的值都可能大于FIFO的大小:framCount,那么,如何确定真正的写指针的位置呢?这里需要用到userBase这一 成员变量,在stepUser()中,每当user的值越过(userBase+frameCount),userBase就会增加 frameCount,这样,映射到FIFO中的偏移总是可以通过(user-userBase)获得。因此,获得当前FIFO的写地址指针可以通过成员 函数buffer()返回:

p = mClbk->buffer(mclbk->user);

在AudioTrack中,封装了两个函数:obtainBuffer()和releaseBuffer()操作 FIFO,obtainBuffer()获得当前可写的数量和写指针的位置,releaseBuffer()则在写入数据后被调用,它其实就是简单地调用 stepUser()来调整偏移的位置。

IMemory接口

在createTrack的过程中,AudioFlinger会根据传入的frameCount参数,申请一块内存,AudioTrack可以通过 IAudioTrack接口的getCblk()函数获得指向该内存块的IMemory接口,然后AudioTrack通过该IMemory接口的 pointer()函数获得指向该内存块的指针,这块内存的开始部分就是audio_track_cblk_t结构,紧接着是大小为frameSize的 FIFO内存。

IMemory->pointer() ---->|_______________________________________________________

|__audio_track_cblk_t__|_______buffer of FIFO(size==frameCount)____|

看看AudioTrack的createTrack()的代码就明白了:

                                                
  1. sp<IAudioTrack>track=audioFlinger->createTrack(getpid(),
  2. streamType,
  3. sampleRate,
  4. format,
  5. channelCount,
  6. frameCount,
  7. ((uint16_t)flags)<<16,
  8. sharedBuffer,
  9. output,
  10. &status);
  11. //得到IMemory接口
  12. sp<IMemory>cblk=track->getCblk();
  13. mAudioTrack.clear();
  14. mAudioTrack=track;
  15. mCblkMemory.clear();
  16. mCblkMemory=cblk;
  17. //得到audio_track_cblk_t结构
  18. mCblk=static_cast<audio_track_cblk_t*>(cblk->pointer());
  19. //该FIFO用于输出
  20. mCblk->out=1;
  21. //UpdatebuffersizeincaseithasbeenlimitedbyAudioFlingerduringtrackcreation
  22. mFrameCount=mCblk->frameCount;
  23. if(sharedBuffer==0){
  24. //给FIFO的起始地址赋值
  25. mCblk->buffers=(char*)mCblk+sizeof(audio_track_cblk_t);
  26. }else{
  27. ..........
  28. }

Android Audio System 之二:AudioFlinger

落鹤生 发布于 2012-04-08 19:42 点击: 293次
本文主要介绍AudioFlinger,AudioFlinger向下访问AudioHardware,实现输出音频数据,控制音频参数。同时,AudioFlinger向上通过IAudioFinger接口提供服务。所以,AudioFlinger在Android的音频系统框架中起着承上启下的作用,地位相当重要。 TAG: 音频系统 AudioFlinger

引言

AudioFlinger是Android音频系统的两大服务之一,另一个服务是AudioPolicyService,这两大服务都在系统启动时有 MediaSever加载,加载的代码位于:frameworks/base/media/mediaserver /main_mediaserver.cpp。AudioPolicyService的相关内容请参考另一编文章:《Android Audio System 之三: AudioPolicyService 和 AudioPolicyManager 》http://www.rosoo.net/a/201204/15904.html

本文主要介绍AudioFlinger,AudioFlinger向下访问AudioHardware,实现输出音频数据,控制音频参数。同 时,AudioFlinger向上通过IAudioFinger接口提供服务。所以,AudioFlinger在Android的音频系统框架中起着承上 启下的作用,地位相当重要。AudioFlinger的相关代码主要在:frameworks/base/libs/audioflinger,也有部分 相关的代码在frameworks/base/media/libmedia里。

AudioFlinger的类结构

下面的图示描述了AudioFlinger类的内部结构和关系:

ANDROID音频系统散记之一:A2dpAudioInterface && Android Audio System_第3张图片

图一 AudioFlinger的类结构

不知道各位是否和我一样,第一次看到AudioFlinger类的定义的时候都很郁闷--这个类实在是庞大和臃肿,可是当你理清他的关系以后,你会觉得相当合理。下面我们一一展开讨论。

  • IAudioFlinger接口

这是AudioFlinger向外提供服务的接口,例如openOutput,openInput,createTrack,openRecord等等, 应用程序或者其他service通过ServiceManager可以获得该接口。该接口通过继承BnAudioFlinger得到。

  • ThreadBase

在AudioFlinger中,Android为每一个放音/录音设备均创建一个处理线程,负责音频数据的I/O和合成,ThreadBase是这些线程的基类,所有的播放和录音线程都派生自ThreadBase

  • TrackBase

应用程序每创建一个音轨(AudioTrack/AudioRecord),在AudioFlinger中都会创建一个对应的Track实例,TrackBase就是这些Track的基类,他的派生类有:

    • PlaybackTread::Track // 用于普通播放,对应于应用层的AudioTrack
    • PlaybackThread::OutputTrack // 用于多重设备输出,当蓝牙播放开启时使用
    • RecordThread::RecordTrack // 用于录音,对应于应用层的AudioRecord
  • 播放

默认的播放线程是MixerThread,它由AudioPolicyManager创建,在AudioPolicyManager的构造函数中,有以下代码:

                                                
  1. mHardwareOutput=mpClientInterface->openOutput(&outputDesc->mDevice,
  2. &outputDesc->mSamplingRate,
  3. &outputDesc->mFormat,
  4. &outputDesc->mChannels,
  5. &outputDesc->mLatency,
  6. outputDesc->mFlags);

最终会进入AudioFlinger的openOut函数:

                                                
  1. ......
  2. thread=newMixerThread(this,output,++mNextThreadId);
  3. ......
  4. mPlaybackThreads.add(mNextThreadId,thread);
  5. ......
  6. returnmNextThreadId;

可以看到,创建好的线程会把该线程和它的Id保存在AudioFlinger的成员变量mPlaybackThreads 中,mPlaybackThreads是一个Vector,AudioFlinger创建的线程都会保存在里面,最后,openOutput返回该线程的 Id,该Id也就是所谓的audio_io_handle_t,AudioFlinger的调用者这能看到这个audio_io_handle_t,当需 要访问时传入该audio_io_handle_t,AudioFlinger会通过mPlaybackThreads,得到该线程的指针。

要播放声音,应用程序首先要通过IAudioFlinger接口,调用createTrack(),关于createTrack的流程,可以参看我的另一篇文章:

http://www.rosoo.net/a/201204/15898.html

createTrack会调用PlaybackThread类的createTrack_l函数:

                                                
  1. track=thread->createTrack_l(client,streamType,sampleRate,format,
  2. channelCount,frameCount,sharedBuffer,&lStatus);

再跟入createTrack_l函数中,可以看到创建了PlaybackThread::Track类,然后加入播放线程的track列表mTracks中。

                                                
  1. track=thread->createTrack_l(client,streamType,sampleRate,format,
  2. channelCount,frameCount,sharedBuffer,&lStatus);
  3. ......
  4. mTracks.add(track);

在createTrack的最后,创建了TrackHandle类并返回,TrackHandle继承了IAudioTrack接口,以后,createTrack的调用者可以通过IAudioTrack接口与AudioFlinger中对应的Track实例交互。

                                                
  1. trackHandle=newTrackHandle(track);
  2. ......
  3. returntrackHandle;

最后,在系统运行时,AudioFlinger中的线程和Track的结构大致如下图所示:它会拥有多个工作线程,每个线程拥有多个Track。

图二 AudioFlinger的线程结构

播放线程实际上是MixerThread的一个实例,MixerThread的threadLoop()中,会把该线程中的各个Track进行混 合,必要时还要进行ReSample(重采样)的动作,转换为统一的采样率(44.1K),然后通过音频系统的AudioHardware层输出音频数 据。

  • 录音

录音的流程和放音差不多,只不过数据流动的方向相反,录音线程变成RecordThread,Track变成了RecordTrack,openRecord返回RecordHandle,详细的暂且不表。

  • DuplicatingThread

AudioFlinger中有一个特殊的线程类:DuplicatingThread,从图一可以知道,它是MixerThread的子类。当系统中有两 个设备要同时输出时,DuplicatingThread将被创建,通过IAudioFlinger的openDuplicateOutput方法创建 DuplicatingThread。

                                                
  1. intAudioFlinger::openDuplicateOutput(intoutput1,intoutput2)
  2. {
  3. Mutex::Autolock_l(mLock);
  4. MixerThread*thread1=checkMixerThread_l(output1);
  5. MixerThread*thread2=checkMixerThread_l(output2);
  6. ......
  7. DuplicatingThread*thread=newDuplicatingThread(this,thread1,++mNextThreadId);
  8. thread->addOutputTrack(thread2);
  9. mPlaybackThreads.add(mNextThreadId,thread);
  10. returnmNextThreadId;
  11. }

创建DuplicatingThread时,传入2个需要同时输出的目标线程Id,openDuplicateOutput先从 mPlaybackThreads中根据Id取得相应输出线程的实例,然后为每个线程创建一个虚拟的AudioTrack---- OutputTrack,然后把这个虚拟的AudioTrack加入到目标线程的mTracks列表中,DuplicatingThread在它的 threadLoop()中,把Mixer好的数据同时写入两个虚拟的OutputTrack中,因为这两个OutputTrack已经加入到目标线程的 mTracks列表,所以,两个目标线程会同时输出DuplicatingThread的声音。

实际上,创建DuplicatingThread的工作是有AudioPolicyService中的AudioPolicyManager里发起的。主要是当蓝牙耳机和本机输出都开启时,AudioPolicyManager会做出以下动作:

  • 首先打开(或创建)蓝牙输出线程A2dpOutput
  • 以HardwareOutput和A2dpOutput作为参数,调用openDuplicateOutput,创建DuplicatingThread
  • 把属于STRATEGY_MEDIA类型的Track移到A2dpOutput中
  • 把属于STRATEGY_DTMF类型的Track移到A2dpOutput中
  • 把属于STRATEGY_SONIFICATION类型的Track移到DuplicateOutput中

结果是,音乐和DTMF只会在蓝牙耳机中输出,而按键音和铃声等提示音会同时在本机和蓝牙耳机中输出。

ANDROID音频系统散记之一:A2dpAudioInterface && Android Audio System_第4张图片

图三 本机播放时的Thread和Track

ANDROID音频系统散记之一:A2dpAudioInterface && Android Audio System_第5张图片

图四蓝牙播放时的Thread和Track

Android Audio System 之三: AudioPolicyService 和 AudioPolic

落鹤生 发布于 2012-04-08 19:31 点击: 275次
AudioPolicyService是Android音频系统的两大服务之一,另一个服务是AudioFlinger,这两大服务都在系统启动时有 MediaSever加载。AudioFlinger主要负责管理音频数据处理以及和硬件抽象层相关的工作。本文主要介绍 AudioPolicyService。 TAG:

引言

AudioPolicyService是Android音频系统的两大服务之一,另一个服务是AudioFlinger,这两大服务都在系统启动时有 MediaSever加载,加载的代码位于:frameworks/base/media/mediaserver /main_mediaserver.cpp。AudioFlinger主要负责管理音频数据处理以及和硬件抽象层相关的工作。本文主要介绍 AudioPolicyService。

AudioPolicyService

AudioPolicyService主要完成以下任务:

  • JAVA应用层通过JNI,经由IAudioPolicyService接口,访问AudioPolicyService提供的服务
  • 输入输出设备的连接状态
  • 系统的音频策略(strategy)的切换
  • 音量/音频参数的设置

AudioPolicyService的构成

下面这张图描述了AudioPolicyService的静态结构:

进一步说明:

1. AudioPolicyService继承了IAudioPolicyService接口,这样AudioPolicyService就可以基于Android的Binder机制,向外部提供服务;

2. AudioPolicyService同时也继承了AudioPolicyClientInterface类,他有一个AudioPolicyInterface类的成员指针mpPolicyManager,实际上就是指向了AudioPolicyManager;

3. AudioPolicyManager类继承了AudioPolicyInterface类以便向AudioPolicyService提供服务,反过来 同时还有一个AudioPolicyClientInterface指针,该指针在构造函数中被初始化,指向了AudioPolicyService,实 际上,AudioPolicyService是通过成员指针mpPolicyManager访问AudioPolicyManager,而 AudioPolicyManager则通过AudioPolicyClientInterface(mpClientInterface)访问 AudioPolicyService;

4. AudioPolicyService有一个内部线程类AudioCommandThread,顾名思义,所有的命令(音量控制,输入、输出的切换等)最终都会在该线程中排队执行;

AudioPolicyManager

AudioPolicyService的很大一部分管理工作都是在AudioPolicyManager中完成的。包括音量管理,音频策略(strategy)管理,输入输出设备管理。

输入输出设备管理

音频系统为音频设备定义了一个枚举:AudioSystem::audio_devices,例 如:

DEVICE_OUT_SPEAKER,

DEVICE_OUT_WIRED_HEADPHONE,

DEVICE_OUT_BLUETOOTH_A2DP,

DEVICE_IN_BUILTIN_MIC,

DEVICE_IN_VOICE_CALL 等等。

每一个枚举值其实对应一个32bit整数的某一个位,所以这些值是可以进行位或操作的,例如我希望同时打开扬声器和耳机,那么可以这样:

                                                
  1. newDevice=DEVICE_OUT_SPEAKER|DEVICE_OUT_WIRED_HEADPHONE;
  2. setOutputDevice(mHardwareOutput,newDevice);

AudioPolicyManager中有两个成员变量:mAvailableOutputDevices和 mAvailableInputDevices,他们记录了当前可用的输入和输出设备,当系统检测到耳机或者蓝牙已连接好时,会调用 AudioPolicyManager的成员函数:

                                                
  1. status_tAudioPolicyManager::setDeviceConnectionState(
  2. AudioSystem::audio_devicesdevice,
  3. AudioSystem::device_connection_statestate,
  4. constchar*device_address)

该函数根据传入的device值和 state(DEVICE_STATE_AVAILABLE/DEVICE_STATE_UNAVAILABLE)设置 mAvailableOutputDevices或者mAvailableInputDevices,然后选择相应的输入或者输出设备。

其他一些相关的函数:

  • setForceUse() 设置某种场合强制使用某一设备,例如setForceUse(FOR_MEDIA, FORCE_SPEAKER)会在播放音乐时打开扬声器
  • startOutput()/stopOutput()
  • startInput()/stopInput()

音量管理

AudioPolicyManager提供了一下几个与音量相关的函数:

  • initStreamVolume(AudioSystem::stream_type stream, int indexMin, int indexMax)
  • setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
  • getStreamVolumeIndex(AudioSystem::stream_type stream)

AudioService.java中定义了每一种音频流的最大音量级别:

                                                
  1. /**@hideMaximumvolumeindexvaluesforaudiostreams*/
  2. privateint[]MAX_STREAM_VOLUME=newint[]{
  3. 5,//STREAM_VOICE_CALL
  4. 7,//STREAM_SYSTEM
  5. 7,//STREAM_RING
  6. 15,//STREAM_MUSIC
  7. 7,//STREAM_ALARM
  8. 7,//STREAM_NOTIFICATION
  9. 15,//STREAM_BLUETOOTH_SCO
  10. 7,//STREAM_SYSTEM_ENFORCED
  11. 15,//STREAM_DTMF
  12. 15//STREAM_TTS
  13. };

由此可见,电话铃声可以有7个级别的音量,而音乐则可以有15个音量级别,java的代码通过jni,最后调用 AudioPolicyManager的initStreamVolume(),把这个数组的内容传入AudioPolicyManager中,这样 AudioPolicyManager也就记住了每一个音频流的音量级别。应用程序可以调用setStreamVolumeIndex设置各个音频流的音 量级别,setStreamVolumeIndex会把这个整数的音量级别转化为适合人耳的对数级别,然后通过AudioPolicyService的 AudioCommandThread,最终会将设置应用到AudioFlinger的相应的Track中。

音频策略管理

我想首先要搞清楚stream_type,device,strategy三者之间的关系:

  • AudioSystem::stream_type 音频流的类型,一共有10种类型
  • AudioSystem::audio_devices 音频输入输出设备,每一个bit代表一种设备,见前面的说明
  • AudioPolicyManager::routing_strategy 音频路由策略,可以有4种策略

getStrategy(stream_type)根据stream type,返回对应的routing strategy值,getDeviceForStrategy()则是根据routing strategy,返回可用的device。Android把10种stream type归纳为4种路由策略,然后根据路由策略决定具体的输出设备。

成员变量mOutputs

                                                
  1. KeyedVector<audio_io_handle_t,AudioOutputDescriptor*>mOutputs;//listofoutputdescriptors

这是AudioPolocyManager用管理输出的键值对向量(数组),通常AudioPolocyManager会打开3个输出句柄(audio_io_handle_t),关于audio_io_handle_t,请参考另一编博客:http://www.rosoo.net/a/201204/15898.html,它实际上就是AudioFlinger中某个PlaybackTread的ID。这3个句柄分别是:

  • mHardwareOutput // hardware output handler
  • mA2dpOutput // A2DP output handler
  • mDuplicatedOutput // duplicated output handler: outputs to hardware and A2DP

可以通过startOutput()把某一个stream type放入到相应的输出中。

popCount()

这个函数主要用来计算device变量中有多少个非0位(计算32位数种1的个数),例如该函数返回2,代表同时有两个device要处理。之所以特别介绍它,是因为这个函数的实现很有意思:

                                                
  1. uint32_tAudioSystem::popCount(uint32_tu)
  2. {
  3. u=((u&0x55555555)+((u>>1)&0x55555555));
  4. u=((u&0x33333333)+((u>>2)&0x33333333));
  5. u=((u&0x0f0f0f0f)+((u>>4)&0x0f0f0f0f));
  6. u=((u&0x00ff00ff)+((u>>8)&0x00ff00ff));
  7. u=(u&0x0000ffff)+(u>>16);
  8. returnu;
  9. }

不知道各位看懂了么?

AudioCommandThread

这是AudioPolicyService中的一个线程,主要用于处理音频设置相关的命令。包括:

  • START_TONE
  • STOP_TONE
  • SET_VOLUME
  • SET_PARAMETERS
  • SET_VOICE_VOLUME

每种命令的参数有相应的包装:

  • class ToneData
  • class VolumeData
  • class ParametersData
  • class VoiceVolumeData

START_TONE/STOP_TONE:播放电话系统中常用的特殊音调,例如:TONE_DTMF_0,TONE_SUP_BUSY等等。

SET_VOLUME:最终会调用AudioFlinger进行音量设置

SET_VOICE_VOLUME:最终会调用AudioFlinger进行电话音量设置

SET_PARAMETERS:通过一个KeyValuePairs形式的字符串进行参数设置,KeyValuePairs的格式可以这样:

  • "sampling_rate=44100"
  • "channels=2"
  • "sampling_rate=44100;channels=2" // 组合形式

这些KeyValuePairs可以通过AudioPolicyService的成员函数setParameters()传入。

更多相关文章

  1. C语言函数以及函数的使用
  2. 浅析Android线程模型
  3. android进阶4step2:Android音视频处理——音频管理
  4. android > 调用拨打电话 并子线程监控然后返回跳转
  5. android音频架构
  6. Android MediaRecorder录制音频
  7. Android基础系列-----------Android进程/线程管理应用示例(Androi
  8. Android 音频 OpenSL ES PCM数据播放
  9. Android音频开发(1):基础知识

随机推荐

  1. android 插件化 与一个轻量级框架
  2. 热更新预言
  3. popwindow动画显示消失,activity切换动画
  4. Android(安卓)Handler leak 分析及解决办
  5. onRetainNonConfigurationInstance和getL
  6. Android(安卓)Material Design 进度条 自
  7. Android(安卓)RingtoneManager铃声管理
  8. Android获取本机Mac地址及IP地址方法
  9. android UI开发之RecyclerView(一)简单实现
  10. 获取Android状态栏的高度