音频框架
- 音频框架的组成:
a) 应用层:应用厂商根据特定需求袭击写的各种音频处理apk
b) 框架层:供开发音频相关产品时使用的java类
c) JNI层:屏蔽了对Audio本地框架调用细节,相当于Java接口本地中转
d) 库层:
i. client部分:JNI层调用对应的本地实现,通过binder与server交互;
ii. server部分:系统服务,是Android音频系统中最核心的部分;
e) HAL层:音频的硬件抽象层,不同类型的音频设备对应不同的HAL层。
f) tinyalsa:封装给用户态调用音频驱动ALSA架构接口的一个精简库。 - AudioFlinger:音频系统中的核心服务,是Android音频系统的中枢,是各功能的主要执行者。它同时也是一个系统服务,为上层提供访问接口,并直接与HAL(音频的硬件抽象层)进行交互。是音频策略的执行者,管理不同HAL层的打开和关闭,对不同的AudioTrack数据进行处理,包括混音、重采样、音效、音量控制。
- AudioPolicyService:音频系统中关于策略的重要服务,是Android音频策略的制定者。根据用户配置来指导AudioFlinger加载支持的设备HAL,对不同stream类型制定选择设备的优先级策略。
- MediaPlayerService:多媒体系统中的重要服务;
- CameraService:有关摄像/照相的重要服务;
- AudioTrack的两种数据加载模式:
a) MODE_STREAM:在这种模式下,通过write一次次把音频数据写到AudioTrack中,这和平时通过write系统调用往文件中写数据类似,但这种工作方式每次都需要把数据从用户提供的Buffer中拷贝到AudioTrack内部的Buffer中,这在一定程度上会使引起延时;
b) MODE_STATIC:这种模式下,在play之前只需要把所有的数据通过一次write调用传递到AudioTrack的内部缓冲区中,后续就不必再传递数据了。这种迷失适用于像铃声这种占用量较小,延时要求较高的文件。但它也有一个缺点,就是一次write的数据不能太多,否则系统无法分配足够的内存来存储全部数据。 - 共享内存:
a) 每个进程的内存空间是4GB,这个4GB是由指针长度决定的,如果指针长度为32位,那么地址的最大编号就是0xFFFFFFFFF,为4GB;
b) 上面说的内存空间是进程的虚拟地址空间,在应用程序中使用的指针其实是指向虚拟空间地址的。 - Linux平台创建和共享内存的步骤为:
a) 进程A创建并打开一个文件,得到一个文件描述符Id;
b) 通过mmap调用fd映射成内存映射文件,在mmap调用中指定特定的参数表示要创建进程间共享内存;
c) 进程B打开同一个文件,也得到一个文件描述符,这样A和B就打开了同一个文件;
d) 进程B也要用mmap调用指定参数表示想使用共享内存,并传递打开的fd。这样A和B就通过打开同一个文件并构造内存映射,实现了进程间的内存共享。 - MemoryHeapBase构造后得到以下结果:
a) mBase变量指向共享内存的起始位置;
b) mSize是所要求分配的内存大小;
c) mFd是ashmem_create_region返回的文件描述符; - AudioTrack在JNI层使用了Native的AudioTrack对象,总结一下调用Native对象的流程:
a) new一个AudioTrack,使用无参的构造函数;
b) 调用set函数,把Java层的参数传进去,另外还设置一个audiocallback函数;
c) 调用AudioTrack的start函数;
d) 调用AudioTrack的write函数;
e) 工作完毕后,调用stop;
f) 最后就是Native对象的delete; - flowControlFlag的总结:
a) 对于音频输出来说,flowControlFlag对应着underrun状态,underrun状态是指生产者提供数据的速度跟不上消费者使用数据的速度。这里的消费者指的是音频输出设备。由于音频输出设备采用环形缓冲方式管理,当生产者没有及时提供新数据时,输出设备就会循环使用缓冲中的数据,这样就会听到一段重复的声音。这种现象一般被称作“machinegun”。对于这种情况,一般的处理方法是暂停输出,等数据准备好后在恢复输出。
b) 对应音频输入来说,flowControlFlag对应着overrun状态,它的意思和underrun一样,只是这里的生产者变成了音频输入设备,而消费者则变成了Audio系统的AudioRecord. - AudioTrack的两种数据输入方式:
a) Push方式:用户主动调用write写数据,相当于数据被push到AudioTrack。
b) Pull方式:AudioTrackThread将利用这个回调函数,以EVENT_MORE_DATA为参数,主动从用户那pull数据。ToneGenerator则用这种方式为AudioTrack提供数据。 - write的工作方式:通过共享内存传递数据,通过控制结构协调生产者和消费者的步调。
- AT和AF交互流程:
a) AT调用createTrack,得到第一个IAudioTrack对象;
b) AT调用IAudioTrack对象的start,表示准备写数据了;
c) AT通过write写数据,这个过程和audio_track_cblk_t有着密切关系;
d) 最后AT调用IAudioTrack的stop或deleteIAudioTrack结束工作; - Android在这里使用了Proxy模式,即TrackHandle是Track的代理,Track没有基于Binder通信,它不能接收来自远端进程的请求。TrackHandle能基于Binder通信,它可以接收来自远端进程的请求,并且能调用Track对应的函数。
- PlaybackThread:回放线程,用于音频输出;它有一个成员变量mOutput,为AudioStreamOutput*类型,这表明PlaybackThread直接和Audio音频输出设备建立了联系;
- RecordThread:录音线程,用于音频输入;
- MixerThread:混音线程,它将来自多个源的音频数据混音后在输出;
- DirectoutputThread:直接输出线程,它会选择一路音频流后将数据直接输出,由于没有混音操作,这样就可以减少很多延时;
- DuplicatingThread:多路输出现象,它从MixerThread派生,意味着它也能混音。它最终会把混音后的数据写到多个输出中,也就是一份数据会有多个接收者。
- MixerThread的线程函数大致的工作流程:
a) 如果有通知信息或配置请求,则先完成这些工作。比如向监听者通知AF的一些信息,或者根据配置请求进行音量控制、声音设备切换等;
b) 调用prepareTracks_l函数,检查活跃的Tracks是否有数据已备好;
c) 调用混音器对象mAudioMixer的process,并且传入一个存储结果数据的缓冲,混音后的结果就存储在这个缓冲中;
d) 调用代表音频设备的AudioOutputStream对象的write,把结果数据写入设备。 - AF的使用流程:
a) 调用framesReady看是否有可读数据;
b) 获得可读数据的起始位置,这个和上面的buffer调用基本一样,都是根据offset和serverBase来获得可读数据块的首地址;
c) 调用stepServer更新读位置; - 系统有两个核心处理器,一个是应用处理的核心,叫AP,可把它当作是台式机上的CPU,在这上面可以运行操作系统。另一个和手机通信相关,一般叫做BP。
- AP和BP都能向音频DSP发送数据,它们在硬件的通道山峰互不干扰。于是就出现了一个问题,即如果两个P同时往DSP发送数据,而互相之间没有协调,那么就可能出现通话声和音乐声混杂的情况。
- AudioManager.java
方法:setStreamVolume(intstreamType,intindex,intflags)
作用:直接设置音量大小。
streamType,指定声音类型
STREAM_ALARM,手机闹铃,STREAM_MUSIC:手机音乐
Stream_RING:电话铃声,STREAM_SYSTEAM:手机系统
STREAM_DTMF:音调,STREAM_NOTIFICATION:系统提示 STREAM_VOICE_CALL:语音电话
index,调大或调小音量
flags,可选的标志位,比如AudioManager.FLAG_SHOW_UI,显示进度条,AudioManager.PLAY_SOUND:播放声音。
- AudioService:音频系统在java层中基本不参与数据流的,AudioService这个系统服务包含或者使用了几乎所有与音频有关的内容,所以说AUdioService是音频系统在Java层的大本营;AudioManager拥有AudioService的Bp端(应用芯片),是AudioService在客户端的一个代理,几乎所有客户对AudioManager进行的请求,最终都会交由AudioService实现;AudioService的功能依赖AudioSystem类,AudioSystem无法实例化,它是Java层到native层的代理,AudioService通过它与AudioPolicy以及AudioFlinger进行通信;
- RemoteException,通信异常;
- 方法:
adjustSuggestedStreamVolume(intdirection,intsuggestedStreamType,intflags)
作用:调整“推荐的流”的音量 - 方法:
setMasterMute(booleanmute,intflags)
作用:设置静音