DSP

Android Audio 架构分析

2019-07-13 17:08发布

一个音频系统大概包括音频的管理、声音播放、声音录音和声音音效几个部分,这几个部分分工协作来完成音频的功能, ·音频管理:负责音量调节、音频设备选择、响铃模式选择等; ·声音播放:负责一个音频流的创建、参数设置、播放、暂停、释放; ·声音录音:负责一个录音音轨的创建、管理; ·声音音效:负责控制声音的效果。 Android 系统对audio的实现是比较复杂的,但实现的方法还是对音频系统的抽象,我们 先看一个从网上拉过来的android 的音频系统结构图: 音频系统在android各个层次的类和调用关系:       1 Audio系统综述 Audio系统在Android中负责音频方面的数据流传输和控制功能,也负责音频设备的管理。这个部分作为AndroidAudio系统的输入/输出层次,一般负责播放PCM声音输出和从外部获取PCM声音,以及管理声音设备和设置,注意,解码功能不在这里实现,在android系统里音频视频的解码是opencore或stagefright完成的,在解码之后才调用音频系统的接口,创建音频流并播放。 Audio系统主要分成如下几个层次: ·JAVA接口层提供APK编程的接口; ·JAVA服务层为JAVA客户端提供服务; ·JNI层向上为java层提供接口,向下调用media库; ·media库提供的Audio系统本地部分接口,作为本地部分的客户端; ·AudioFlinger作为Audio系统的中间层,是audio的本地服务,他会打开hal层的动态库; ·Audio的硬件抽象层提供底层支持,供AudioFlinger调用,他通过open、ioctl、close操作驱动的sysfs节点; ·Audio驱动,注册sysfs节点,并将所有的功能以ioctl 命令的方式实现。 Audio本地框架类是libmedia.so的一个部分,这些Audio接口对上层提供接口,由下层的本地代码去实现。 AudioFlinger继承libmeida中的接口,提供实现库libaudiofilnger.so。这部分内容没有自己的对外头文件,上层调用的只是libmedia本部分的接口,但实际调用的内容是libaudioflinger.so Audio使用JNIJava对上层提供接口,JNI部分通过调用libmedia库提供的接口来实现。 Audio的硬件抽象层提供到硬件的接口,供AudioFlinger调用。Audio的硬件抽象层实际上是各个平台开发过程中需要主要关注和独立完成的部分。 提示:AndroidAudio系统不涉及编解码环节,只是负责上层系统和底层Audio硬件的交互,一般以PCM作为输入/输出格式。 AndroidAudio系统中,无论上层还是下层,都使用一个管理类和输出输入两个类来表示整个Audio系统,输出输入两个类负责数据通道。在各个层次之间具有对应关系,如表7-1所示所示。 7-1 Android各个层次的对应关系 Audio管理环节 Audio输出 Audio输入 Java android.media.AudioSystem android.media.AudioTrack android.media.AudioRecorder 本地框架层 AudioSystem AudioTrack AudioRecorder AudioFlinger IAudioFlinger IAudioTrack IAudioRecorder 硬件抽象层 AudioHardwareInterface AudioStreamOut AudioStreamIn 2.Android系统的代码分布情况如下所示: 1AudioJava部分 代码路径:frameworks/base/media/java/android/media Audio相关的Java包是android.media,主要包含AudioManagerAudio系统的几个类。 2AudioJNI部分 代码路径:frameworks/base/core/jni 生成库libandroid_runtime.soAudioJNI是其中的一个部分。 3Audio的框架部分 头文件路径:frameworks/base/include/media/ 源代码路径:frameworks/base/media/libmedia/ Audio本地框架是media库的一部分,本部分内容被编译成库libmedia.so,提供Audio部分的接口(包括基于BinderIPC机制)。 4Audio Flinger 代码路径:frameworks/base/libs/audioflinger 这部分内容被编译成库libaudioflinger.so,它是Audio系统的本地服务部分。 5Audio的硬件抽象层接口 头文件路径:hardware/libhardware_legacy/include/hardware/ Audio硬件抽象层的实现在各个系统中可能是不同的,需要使用代码去继承相应的类并实现它们,作为Android系统本地框架层和驱动程序接口。 3.Audio系统的各层次介绍 3.1 JAVA接口层 ·AudioManager.java 为APP提供音量控制和响铃模式控制功能。 ·AudioTrack .java 为APP提供管理和播放一个音频流的功能(必须是PCM流,因为不提供解码功能)。 ·AudioRecord.java 为APP提供录音功能。 ·AudioEffect.java 为APP提供控制音效的功能。 3.2 JAVA服务层 音频在java层的服务只有AudioService,他实现了IAudioService接口,AudioManager 是这个服务的客户端。AudioService服务向下调用AudioSystem.java,AudioSystem.java再通过JNI调用到本地层。那AudioTrack 、AudioRecord和AudioEffect呢?他们是直接调用本地接口的,为什么他们没有java服务层呢?这是因为一般的音频文件比如mp3、ogg等都是压缩的音频流,需要经过解码才能得到pcm数据流,解码是opencore或stagefright完成的,一般是播放器的服务,比如MediaPlayer,先解码音频文件,然后去创建AudioTrack、设置音效等,然后播放音乐。这些内容的实现一般是在本地服务层,所以AudioTrack 、AudioRecord和AudioEffect的服务层在本地层的AudioFlinger里实现就够了。 3.3 JNI层 AndroidAudio部分通过JNIJava层提供接口,在Java层可以通过JNI接口完成Audio系统的大部分操作。Audio JNI部分的代码路径为:frameworks/base/core/jni 其中,主要实现的3个文件为:android_media_AudioSystem.cppandroid_media_Audio Track.cppandroid_media_AudioRecord.cpp,它们分别对应了Android Java框架中的3个类的支持: ·android.media.AudioSystem:负责Audio系统的总体控制; ·android.media.AudioTrack:负责Audio系统的输出环节; ·android.media.AudioRecorder:负责Audio系统的输入环节。 AndroidJava层中,可以对Audio系统进行控制和数据流操作,对于控制操作,和底层的处理基本一致;但是对于数据流操作,由于Java不支持指针,因此接口被封装成了另外的形式。 Java提供native_write_bytenative_write_short接口,它们一般是通过调用AudioTrackwrite()函数来完成的,只是在Java的数据类型和C++的指针中做了一步转换。 3.4 libmedia AndroidAudio系统的核心框架在media库中提供,对上面主要实现AudioSystemAudioTrackAudioRecorder三个类,这其实是Audio系统的本地接口层。 AudioFlinger提供了IAudioFlinger类接口,在这个类中,可以获得IAudioTrackIAudioRecorder两个接口,分别用于声音的播放和录制。AudioTrackAudioRecorder分别通过调用IAudioTrackIAudioRecorder来实现。 Audio系统的头文件在frameworks/base/include/media/目录中,主要的头文件如下: ·AudioSystem.hmedia库的Audio部分对上层的总管接口; ·IAudioFlinger.h:需要下层实现的总管接口; ·AudioTrack.h:放音部分对上接口; ·IAudioTrack.h:放音部分需要下层实现的接口; ·AudioRecorder.h:录音部分对上接口; ·IAudioRecorder.h:录音部分需要下层实现的接口。 IAudioFlinger.hIAudioTrack.hIAudioRecorder.h这三个接口通过下层的继承来实现(即 AudioFlinger)。AudioFlinger.hAudioTrack.hAudioRecorder.h是对上层提供的接口,它们既供本地程序调用(例如声音的播放器、录制器等),也可以通过JNIJava层提供接口。meida库中Audio部分的结构如图7-2所示。 7-2meida库中Audio部分的结构 从功能上看,AudioSystem负责的是Audio系统的综合管理功能,而AudioTrackAudioRecorder分别负责音频数据的输出和输入,即播放和录制。 AudioSystem.h中主要定义了一些枚举值和set/get等一系列接口,Audio系统的几个枚举值中,audio_routes是由单独的位来表示的,而不是由顺序的枚举值表示,因此这个值在使用过程中可以使用""的方式。例如,表示声音可以既从耳机(EARPIECE)输出,也从扬声器(SPEAKER)输出,这样是否能实现,由下层提供支持。在这个类中,set/get等接口控制的也是相关的内容,例如Audio声音的大小、Audio的模式、路径等。 AudioTrackAudio输出环节的类,其中最重要的接口是write(); AudioRecordAudio输入环节的类,其中最重要的接口为read(); AudioTrackAudioRecordread/write函数的参数都是内存的指针及其大小,内存中的内容一般表示的是Audio的原始数据(PCM数据)。这两个类还涉及Auido数据格式、通道数、帧数目等参数,可以在建立时指定,也可以在建立之后使用set()函数进行设置。 libmedia库中提供的只是一个Audio系统框架,AudioSystem AudioTrackAudioRecord分别调用下层的IAudioFlingerIAudioTrackIAudioRecord来实现。另外的一个接口是IAudioFlingerClient,它作为向IAudioFlinger中注册的监听器,相当于使用回调函数获取 IAudioFlinger运行时信息。 3.5 AudioFlinger AudioFlingerAudio系统的中间层,在系统中起到服务作用,它主要作为libmedia提供的Audio部分接口的实现,其代码路径为:frameworks/base/libs/audioflinger AudioFlinger的核心文件是AudioFlinger.hAudioFlinger.cpp,提供了类AudioFlinger,这个类是一个IAudioFlinger的实现 AudioFlinger主要提供createTrack()创建音频的输出设备IAudioTrackopenRecord()创建音频的输入设备IAudioRecord。另外包含的就是一个get/set接口,用于控制。 从工作的角度看,AudioFlinger在初始化之后,首先获得放音设备,然后为混音器(Mixer)建立线程,接着建立放音设备线程,在线程中获得放音设备。 AudioFlingerAudioResampler.h中定义了一个音频重取样器工具类,这个音频重取样工具包含3种质量:低等质量(LOW_QUALITY)将使用线性差值算法实现;中等质量(MED_QUALITY)将使用立方差值算法实现;高等质量(HIGH_ QUALITY)将使用FIR(有限阶滤波器)实现。AudioResampler中的AudioResamplerOrder1是线性实现,AudioResamplerCubic.*文件提供立方实现方式,AudioResamplerSinc.*提供FIR实现。 AudioMixer.hAudioMixer.cpp中实现的是一个Audio系统混音器,它被AudioFlinger调用,一般用于在声音输出之前的处理,提供多通道处理、声音缩放、重取样。AudioMixer调用了AudioResampler 提示: AudioFlinger本身的实现通过调用下层的Audio硬件抽象层的接口来实现具体的功能,各个接口之间具有对应关系。 3.6 Audio的硬件抽象层 3.6.1 Audio硬件抽象层的接口定义 Audio的硬件抽象层是AudioFlingerAudio硬件的接口,在各个系统的移植过程中可以有不同的实现方式。Audio硬件抽象层的接口路径为:hardware/libhardware_legacy/include/hardware/ 其中主要的文件为:AudioHardwareBase.hAudioHardwareInterface.h Android中的Audio硬件抽象层可以基于Linux标准的ALSAOSS音频驱动实现,也可以基于私有的Audio驱动接口来实现。 AudioHardwareInterface.h中定义了类:AudioStreamOutAudioStreamInAudioHardwareInterfaceAudioStreamOutAudioStreamIn分别对应了音频的输出环节和输入环节,其中负责数据流的接口分别是wirte()read(),参数是一块内存的指针和长度;另外还有一些设置和获取接口。在这个AudioHardwareInterface接口中,使用openOutputStream()openInputStream()函数分别获取AudioStreamOutAudioStreamIn两个类,它们作为音频输入/输出设备来使用。此外,AudioHardwareInterface.h定义了C语言的接口来获取一个AudioHardware Interface类型的指针。 extern "C" AudioHardwareInterface* createAudioHardware(void); 如果实现一个Android的硬件抽象层,则需要实现AudioHardwareInterfaceAudioStream OutAudioStreamIn三个类,将代码编译成动态库libauido.soAudioFlinger会连接这个动态库,并调用其中的 createAudioHardware()函数来获取接口。 AudioHardwareBase.h中定义了类:AudioHardwareBase,它继承了Audio HardwareInterface,显然继承这个接口也可以实现Audio的硬件抽象层。 提示:Android系统的Audio硬件抽象层可以通过继承类AudioHardwareInterface来实现,其中分为控制部分和输入/输出处理部分。 3.6.2 AudioFlinger中自带Audio硬件抽象层实现 AudioFlinger中可以通过编译宏的方式选择使用哪一个Audio硬件抽象层。这些Audio硬件抽象层既可以作为参考设计,也可以在没有实际的Audio硬件抽象层(甚至没有Audio设备)时使用,以保证系统的正常运行。AudioDumpInterface.hAudioDumpInterface.cpp是一个提供了Dump功能的Audio硬件抽象层,它所起到的作用就是将输出的Audio数据写入到文件中。AudioDumpInterface并不是为了实际的应用使用的,而是为了调试使用的类。当进行音频播放器调试时,有时无法确认是解码器的问题还是Audio输出单元的问题,这时就可以用这个类来替换实际的Audio硬件抽象层,将解码器输出的AudioPCM数据写入文件中,由此可以判断解码器的输出是否正确。 提示:使用AudioDumpInterface音频硬件抽象层,可以通过/data/FlingerOut.pcm文件找到PCM的输出数据。 3.6.3 Audio硬件抽象层的真正实现 实现一个真正的Audio硬件抽象层,需要完成的工作和实现以上的硬件抽象层类似。 对于OSS驱动程序,实现方式和前面的AudioHardwareGeneric类似,数据流的读/写操作通过对/dev/dsp设备的读/写来完成;区别在于OSS支持了更多的ioctl来进行设置,还涉及通过/dev/mixer设备进行控制,并支持更多不同的参数。 对于ALSA驱动程序,实现方式一般不是直接调用驱动程序的设备节点,而是先实现用户空间的alsa-lib,然后Audio硬件抽象层通过调用alsa-lib来实现。 在实现Audio硬件抽象层时,对于系统中有多个Audio设备的情况,可由硬件抽象层自行处理setRouting()函数设定,例如,可以选择支持多个设备的同时输出,或者有优先级输出。对于这种情况,数据流一般来自AudioStreamOut::write()函数,可由硬件抽象层确定输出方法。对于某种特殊的情况,也有可能采用硬件直接连接的方式,此时数据流可能并不来自上面的write(),这样就没有数据通道,只有控制接口。 Audio硬件抽象层也是可以处理这种情况的。 3.7 Audio驱动 Audio的驱动是基于Linux标准的音频驱动:OSSOpen Sound System)或者ALSAAdvanced Linux Sound Architecture)驱动程序来实现的。