基于Allwinner的Audio子系统分析(Android-5.1)
前言
一直想总结下Audio子系统的博客,但是各种原因(主要还是自己懒>_<),一直拖到现在才开始重新整理,期间看过H8(Android-4.4),T3(Android-4.4),A64(Android-5.1)上的AudioRecoder录音流程,总的来说,几个平台上的audio系统都差不多,这里还是主要以A64来分析。
=================================================== 我是分割线 ===============================================================================
对于Audio系统大概会分为AudioRecoder/AudioTrack方法,MediaRecoder/MediaPlayer方法,前者直接操作原始数据,后者与系统编解码结合对操作音频格式的文件,本文以AudioRecorder为切入点,来分析整个Audio系统的录音与播放,最终掌握Audio系统的架构,Audio数据的流向,以及怎么更改Audio的录音/播放通路等。
首先,先介绍下Android下Audio系统的一些备用资料,方便后面看代码的时候更好的理解
1.Audio数据是怎么存储的
每个声卡都有一个硬件缓存区来保存记录下来的样本。当缓存区足够满时,声卡将产生一个中断。内核声卡驱动然后使用直接内存(DMA)访问通道将样本传送到内存中的应用程序缓存区。类似地,对于回放,任何应用程序使用DMA将自己的缓存区数据传送到声卡的硬件缓存区中。这样硬件缓存区是环缓存。也就是说当数据到达缓存区末尾时将重新回到缓存区的起始位置。ALSA维护一个指针来指向硬件缓存以及应用程序缓存区中数据操作的当前位置。从内核外部看,我们只对应用程序的缓存区感兴趣,所以本文只讨论应用程序缓存区。
1、Audio系统架构
Audio系统架构图
Audio系统在Android中负责音频方面的数据流传输和控制功能,也负责音频设备的管理,Audio系统主要分成如下几个层次:
1.Application本地接口(frameworks/base/media/java/android/media)
AudioTrack/MediaPlayer是Audio输出环节的类;
AudioRecord/MediaRecorder是Audio输入环节的类;
其中AudioRecord可以提取pcm原始数据,AudioRecord也只能播放pcm原始数据;
而MediaRecorder则可以对音频数据进行编码存储为mp3等格式的文件,MediaPlayer则可以解码音频文件类型的文件。
他们都通过frameworks/base/media/jni下的JNI文件调用libmedia的方法。
2.libmedia库实现(frameworks/av/media/libmedia)
AudioTrack/MediaPlayer和AudioRecorder/MeidaRecorder分别负责音频数据的输出和输入,即播放和录制;
AudioSystem负责的是Audio系统的综合管理功能;
在libmedia库中提供的只是一个Audio系统框架,AudioSystem、 AudioTrack 和AudioRecord分别调用下层的IAudioFlinger、IAudioTrack和IAudioRecord来实现。
3.AudioService的实现 (frameworks/av/services/audioflinger)
AudioFlinger是Audio系统的中间层,在系统中起到服务作用,它主要实现了libmedia提供的Audio部分接口;AudioFlinger其中的createTrack()创建音频的输出设备IAudioTrack,openRecord()创建音频的输入设备IAudioRecord。
AudioMixer中实现的是一个Audio系统混音器,它被AudioFlinger调用,一般用于在声音输出之前的处理,提供多通道处理、声音缩放、重取样(AudioResampler)。
AudioPolicyService是Audio系统的策略控制中心,具有掌管系统中声音设备的选择和切换、音量控制等功能。
Threads与Tracks则继承AudioFlinger中的方法,同时在读写音频数据也是在这里面完成的。
4.Audio硬件抽象层
Audio硬件抽象层的接口路径为:hardware/libhardware_legacy/include/hardware/
函数实现:hardware/libhardware_legacy/audio/
其中主要的文件为:AudioHardwareBase.h和AudioHardwareInterface.h。
在AudioHardwareInterface.h中定义了类:AudioStreamOut、AudioStreamIn和AudioHardwareInterface。AudioStreamOut和AudioStreamIn分别对应了音频的输出环节和输入环节,这个AudioHardwareInterface接口中,使用openOutputStream()和openInputStream()函数分别获取AudioStreamOut和AudioStreamIn两个类,它们作为音频输入/输出设备来使用。
AudioHardwareGeneric.cpp则继承了AudioStreamOut与AudioStreamIn;
AudioHardwareInterface.cpp则继承了AudioHardwareInterface。
AudioHardware*最终会调用audio_hw.c中的函数来实现与驱动进行数据交换,audio_hw.c文件位置:hardware/aw/audio/tulip/audio_hw.c
2、ALSA驱动架构
ALSA架构图
Audio Codec驱动采用ALSA(Advanced Linux Sound Architecture)架构,驱动包括放音(play),录音(capture),以及对Codec进行各种控制和选择(mixer)功能。
ASoC--ALSA System on Chip
Codec驱动包含了一些音频的控件(Controls),音频接口,DAMP(动态音频电源管理)的定义和某些Codec IO功能,为了保证硬件无关性,任何特定于平台和机器的代码都要移到Platform和Machine驱动中;
Platform驱动包含了该SoC平台的音频DMA和音频接口的配置和控制(I2S,PCM,AC97等等),它也不能包含任何与板子或机器相关的代码;
Machine驱动整合Codec驱动和Platform驱动,单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作。
3、Audio实现流程分析
3.1.参数介绍
AudioSource:采样通道,一般为MediaRecorder.AudioSource.MIC;
SampleRate:采样率,一般在8000Hz~48000Hz之间,不同的硬件设备这个值不同;
Channel:采样声道数,一般为单通道或立体声,即 AudioFormat.CHANNEL_CONFIGURATION_MONO或 AudioFormat.CHANNEL_CONFIGURATION_STEREO;
AudioFormat:采样精度,一般为8位或16位,即AudioFormat.ENCODING_16BIT或8BIT;
bufferSize:缓冲区大小:可以通过getMinBufferSize来获取;
在Audio系统中,buffer的组成如图所示
buffer:DMA缓冲区大小
period: 周期,是每两个硬件中断之间的帧数,即传输多少个采样帧数据,DMA反馈一个中断,一般一个周期包含1024个采样帧,在HAL层中定义。
frame: 每个采样帧的大小。为一个采样点的字节数*声道数,因为对于多通道的话,用1个采样点的字节数表示不全,因为播放的时候肯定是多个声道的数据都要播放出来才行,所以为了方便,就说1秒钟有多少个frame,这样就能抛开声道数,把意思表达全了。
sample: 采样声道,一般为2个字节。
3.2.Audio实现流程分析
在APP中开启录音流程如下,包含了音频参数设定、数据采集、释放资源全过程。这里仅详细分析AudioRecorder的实现
3.2.1.AudioRecorder
3.2.2.MediaRecoder
- new MediaRecorder();
- MediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- MediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
- MediaRecorder.setAudioSamplingRate(44100);
- MediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
- MediaRecorder.setAudioEncodingBitRate(96000);
- MediaRecorder.setOutputFile(mRecorderFile.getAbsolutePath());
- MediaRecorder.prepare();
- MediaRecorder.start();
- MediaRecorder.stop();
- MediaRecorder.release();
3.2.3AudioTrack
- AudioTrack.getMinBufferSize(micRate,AudioFormat.CHANNEL_CONFIGURATION_STEREO,AudioFormat.ENCODING_PCM_16BIT);
- new AudioTrack(AudioManager.STREAM_MUSIC, micRate,AudioFormat.CHANNEL_CONFIGURATION_STEREO,AudioFormat.ENCODING_PCM_16BIT, m_out_buf_size,AudioTrack.MODE_STREAM);
- AudioTrack.play();
- AudioTrack.write(bytes_pkg, 0, bytes_pkg.length);
- AudioTrack.stop();
- AudioTrack.release();
4.总结
1.AudioRecord的链路流程
驱动层注册设备并挂载设备节点,HAL层提供底层读取函数的实现,然后AudioFlinger调用HAL接口把读取到的数据放入FIFO中,然后AudioRecord再从FIFO中把数据读取出来。
2.AudioTrack的链路流程
驱动层注册设备并挂载设备节点,HAL层提供底层读取函数的实现 ,然后AudioTrack把数据写入到FIFO中,然后AudioFlinger从FIFO中取出数据,经过AudioMixer混音处理之后调用HAL接口把数据写入到驱动中。
3.MediaRecord的链路流程
首先设置文件编码参数,通过AudioFlinger调用HAL接口把读取到的数据放入FIFO中,然后调用AudioRecord再从FIFO中把数据读取出来,然后把数据传给相应的编码器进行编码处理,最后写入到文件中。
陆陆续续花了两个月的碎片时间终于把这个专题写完了,今天一口气都发出来,接下来准备准备,搞一下display驱动方面的东西,感觉又是一条超级超级大工程啊!!由于作者内功有限,若文章中存在错误或不足的地方,还请给位大佬指出,不胜感激!