Frame 帧数,1单位的Frame等于一个采样点的字数*声道数(r如PCM16,双声道的1个Frame为2*2=4字节),一般音频系统都是用它为单位来分配和管理的
AudioManager.STREAM_MUSIC 音频流的类型
8000采样频率,每秒8000个点
AudioFormat.CHANNEL_CONFIGUATION_STEREO 双声道
AudioFormat.ENCODING_PCN_16BIT 采样精度:一个采样点16bit,相当于2个字节
bufsize以帧为单位的缓冲区大小
AudioTrack.MODE_STREAM数据加载模式
Audio系统对外提供的API,构造AudioTrack的参数:最后一个参数为数据加载模式,有Mode_STREAM和MODE_STATIC,第一种需要把用户提供的数据
Buffer拷贝到AudioTrack的Buffer中,一定程度上会引起延时,第二种由于是静态的,可直接调用,但这时的Buffer不能太多,否则系统无法分配足够的内存来存储全部数据。
调用native层的native_setup函数,调用AudioTrackJniStorage,new 一个AudioTrack,调用无参的构造函数,再调用Set方法填充,更新AudioTrack的参数。
AudioTrack.getMinBufferSize():调用Native函数,通过JNI查询HAL层硬件的相关信息,比如该硬件支持的最高采样率,硬件内部缓冲的大小(以Frame为单位),会综合考虑硬件的情况,得出一个最小缓冲区的大小,一般分配的缓冲大小是它的整数倍。
AudioTrackJniStorage用到了Android对共享内存的封装类,MemoryHeapBase打开/dev/ashmem设备,mapfd(fd,size)创建共享内存。MemoryBase通过MemoryHeapBase构造,即两个类都可以共享这块内存。
调用AudioTrack的start函数,通过native调用
调用AudioTrack的write函数,然后stop,最后调用native的delete
AudioTrack的set方法,利用Binder机制,实现IAudioTrack,在IAudioTrack中有一块共享内存,头部是一个audio_track_cblk_t(CB)对象,协调和管理AudioTrack和AudioFlinger的步伐。AudioTrack支持两种数据输入方式,push方式:用户主动write写数据;pull方式:主动从用户那pull数据。
write:先obtainBuffer 从共享内存中得到一块空闲的数据块,通过memcpy将得到的数据赋值到该数据块。releaseBuffer在使用完这块控件后更新写指针的位置。
AudioFlinger的构造中定义了AudioHardwareInterface,是android对代表audio硬件的封装,属于HAL层,管理了AudioStreamOut(音频输出设备对象)和
AudioStreamIn(音频输入设备对象),设置了音频系统的一些参数,由SetParameters完成,功能由硬件提供。
在AudioTrack创建共享内存的时候,先调用createTrack,得到一个IAudioTrack对象,分析createTrack:
1、选择工作线程,checkPlaybackThread_1,内部封装类PlaybackThread::createTrack_1,new 出一个Track,Track的构造函数中
new(mcblk) audio_track_cblk_t(),利用C++中的placement new ,在括号中指定的内存中创建一个对象。
createTrack函数返回的是Trackhandle对象,而TrackHandle是基于Binder通信的
2、AudioFlinger的工作线程: PlaybackThread 回放线程,音频输出的数据最后由这个线程处理,而这个线程实际上是一个MixerThread,Track也定义在这个线程中。
3、MixerThead(混音线程),在选择工作线程checkPlaybackThread _1中,根据output值找到相应的回放线程,是如何找到的?答案在AudioPolicyService中,调用
new 一个AudioPolicyManagerbase,调用AudioPolicyService的openoutput,再调用AudioFlinger的openoutput函数,创建MixerThread线程,并且加入PlaybackThread 线程组。AudioPolicyService控制整个音频系统,AudioFlinger管理音频的输入和输出。
4、PlaybackThread的启动,即AudioTrack的start,将Track加入到活跃组,触发一个同步事件,该事件让线程启动,启动后先完成通知的消息或匹配的请求,再检查活跃 的Tracks数据,调用混音对象mAudioMixer的process函数,参数为存储结果的缓冲,混音后的结果就存在这个缓存里,最后启动代表音频输出设备的AudioOutputStream 的对象(HAL层的AudioOutputStream)的write,将缓存结果写入设备。
关键:Track 调用Track的getnextBuffer,得到可读数据缓冲,releaseBuffer,释放缓冲。主要是修改CB记录的读写位置等计算可读的缓冲位置。
5、AudioTrack的stop会通过TrackHandle这个Proxy函数交给Track的stop处理。
a:调用framesAvailable,看看是否有空余的可写空间
b:调用buffer,获得写空间的起始地址
c:调用strepUser,更新user的位置
a:调用framesReady 看是否有可读数据
b:获得可读数据的起始位置,读完后,触发一个同步信号,因为AT端可能在等待可写的数据缓冲。
c:调用stepServer更新读位置
在构造函数中,使用了AudioPolicyInterface的createAudioPolicyManager函数,该接口的具体实现由硬件提供,android提供了一个通用的实现类AudioPolicyManagerBase,而AudioPolicyInterface的参数在AudioSystem中,AudioSystem是一个Native类,如:stream type(音频流类型)、audio_mode(声音模式)等。
创建了AudioOutputDescriptor对象,从硬件上看,代表的是DSP的设备。
openoutput函数,导致AF创建了线程,函数实现由AudioPolicyManagerBase完成。最终在AF中创建一个混音线程,返回该线程在AF中的索引号。
setOutputDevice,为DSP选择一个合适的输出设备
Audio Strategy: UpdateDeviceForStrategy更新路由策略,与stream type有关
AudioTrack在调用createTrack时,传入一个audio_handle_t output,该值表示AF中某个工作线程的索引,它是怎么来的呢?
在AudioSystem的getOutput中,根据流类型找到对应的Audio Strategy,根据策略找到适合输出的设备,根据device找到AF中合适的工作线程,根据AF线程索引号createTrack。AudioTrack->AudioSystem->AudioPolicyService->AudioPolicyManagerBase
耳机插拔事件,系统会发一个广播,java层的AudioService会接受这个广播,在内部AudioServiceBoardcastReceive中处理该事件,会调用AudioSystem的
setDeviceConnectionState函数,设置设备连接状态,该函数实现在Native层,最终调用AudioPolicyManagerBase实现。实现的3个关键函数:
a:getNewDevice(audio_io_handle_t,bool)通过传入的output,找到对应的设备(AudioOutoutDescriptor),调用getDeviceForStrategy,通过策略获取设备,
b:updateDeviceStragety更新各种策略适用的设备
c:setOutputDevice设置新的输出设备,最终调用AudioPolicyService的setParameters,在AudioPolicyService的创建时有两个线程,一个处理各种请求,位于AudioCommandThread中,调用AudioSystem->AudioFlinger->MixerThread的setParameters,最后调用HAL层的setParameters
多路输出线程,从MixerThread派生,能够混音。最终把混音数据写到多个输出中,一份数据有多个接收者。主要用于蓝牙的A2DP输出。
对于A2DP设备,有专门函数handleA2dpConnection函数处理,调用openDuplicateOutput,在AF中处理,AF中构造了一个DuplicatingThread以及addOutputTrack,使DT构造了两个OutputTrack,一个对应蓝牙的MT,另一个对应DSP的MT(MixerThread)。