前言

  一直想总结下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 <ALSA与芯片相关的部分>,是建立在标准ALSA驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频Codec的一套软件体系,ASOC将音频系统分为3部分:Machine,Platform和Codec。

  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驱动方面的东西,感觉又是一条超级超级大工程啊!!由于作者内功有限,若文章中存在错误或不足的地方,还请给位大佬指出,不胜感激!

基于Allwinner的Audio子系统分析(Android-5.1)的更多相关文章

  1. (一)Audio子系统之AudioRecord.getMinBufferSize

    在文章<基于Allwinner的Audio子系统分析(Android-5.1)>中已经介绍了Audio的系统架构以及应用层调用的流程,接下来,继续分析AudioRecorder方法中的ge ...

  2. (二)Audio子系统之new AudioRecord()

    在上一篇文章<(一)Audio子系统之AudioRecord.getMinBufferSize>中已经介绍了AudioRecord如何获取最小缓冲区大小,接下来,继续分析AudioReco ...

  3. (二)Audio子系统之new AudioRecord()(Android4.4)

    在上一篇文章<(一)Audio子系统之AudioRecord.getMinBufferSize>中已经介绍了AudioRecord如何获取最小缓冲区大小,接下来,继续分析AudioReco ...

  4. Linux input子系统分析

    输入输出是用户和产品交互的手段,因此输入驱动开发在Linux驱动开发中很常见.同时,input子系统的分层架构思想在Linux驱动设计中极具代表性和先进性,因此对Linux input子系统进行深入分 ...

  5. (三)Audio子系统之AudioRecord.startRecording

    在上一篇文章<(二)Audio子系统之new AudioRecord()>中已经介绍了Audio系统如何创建AudioRecord对象以及输入流,并创建了RecordThread线程,接下 ...

  6. (四)Audio子系统之AudioRecord.read

      在上一篇文章<(三)Audio子系统之AudioRecord.startRecording>中已经介绍了AudioRecord如何开始录制音频,接下来,继续分析AudioRecord方 ...

  7. (五)Audio子系统之AudioRecord.stop

    在上一篇文章<(四)Audio子系统之AudioRecord.read>中已经介绍了AudioRecord如何获取音频数据,接下来,继续分析AudioRecord方法中的stop的实现 函 ...

  8. (六)Audio子系统之AudioRecord.release

      在上一篇文章<(五)Audio子系统之AudioRecord.stop>中已经介绍了AudioRecord如何暂停录制,接下来,继续分析AudioRecord方法中的release的实 ...

  9. 从CM刷机过程和原理分析Android系统结构

    前面101篇文章都是分析Android系统源代码,似乎不够接地气. 假设能让Android系统源代码在真实设备上跑跑看效果,那该多好.这不就是传说中的刷ROM吗?刷ROM这个话题是老罗曾经一直避免谈的 ...

随机推荐

  1. stl源码分析de练习

    // StlTest1.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <vector> #include & ...

  2. zz如何让你的婚姻天长地久?

    如果天长地久意味着一列永不出轨的火车,下面有关婚姻生活的战略就像制定一张准确的运行时刻表.因为成功的婚姻并非源于机运,所谓的七年之痒也不是空穴来风.对那些已婚男人来说,他们需要计划——为了一年比一年过 ...

  3. jFinal render为什么不跳转到指定的页面

    jFinal render为什么不跳转到指定的页面 1:需要在你自己的主配置文件里面配置所有页面的文件前缀,没配置默认是项目的根目录 //配置页面访问主路径 me.setBaseViewPath(&q ...

  4. 网络中的A、B、C类地址

    1.A类ip地址(1.0.0.0到126.255.255.255) A类地址只有第一个8位表示网络地址,最高位一定为0,所以A类地址的网络号范围可以为:64+32+16+8+4+2+1=127,也就是 ...

  5. docker-compose up启动又停止,需要加tty为true

    如果docker-compose.yml如下,则用docker-compose up -d启动起来的容器可能会立即停止. version: '2' services: mir-http-repo: i ...

  6. Work at a KFC fast food restaurant

    During the summer holiday of 2005,I thought I should do some meaningful instead of at home and watch ...

  7. C++11新标准学习

    <深入理解C++11:C++11新特性解析与应用> <华章科技:深入理解C++11:C++11新特性解析与应用>一共8章:第1章从设计思维和应用范畴两个维度对C++11新标准中 ...

  8. ZOJ3768 Continuous Login 2017-04-14 12:47 45人阅读 评论(0) 收藏

    Continuous Login Time Limit: 2 Seconds      Memory Limit: 131072 KB      Special Judge Pierre is rec ...

  9. TypeToken 是google提供的一个解析Json数据的类库中一个类

    Type listType = new TypeToken<LinkedList<User>>(){}.getType(); Type是java里的reflect包的Type ...

  10. 解决json结合struts2时,Class org.apache.struts2.json.JSONWriter can not access a member of * 的问题

    在使用json的时候,产生的一个错误,查了一下资料,原来是struts2和json一起使用的时候,才产生的问题,虽然不影响程序的运行,但是总是会有一些异常的日志产生,并且,这个也会增加程序的负担. 原 ...