Android 音频应用框架

frameworks\av\media\libmedia
--------AudioRecord.cpp
--------------|
              |_ sp<IAudioRecord> record = audioFlinger->openRecord(input,
              |_  mAudioRecord = record;
              
--------AudioSystem.cpp
--------AudioTrack.cpp

 

frameworks\av\services\audioflinger
--------------AudioFlinger.cpp
              |_ AudioFlinger::openRecord(
              |_ recordTrack = thread->createRecordTrack_l
              |_ recordHandle = new RecordHandle(recordTrack);
              |_ 

              |_ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
              |_ load_audio_interface
              |_ hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name,
              |_ audio_hw_device_open(mod, dev);
              |_

--------------AudioHwDevice.cpp
--------------Threads.cpp
              |_ AudioFlinger::RecordThread::createRecordTrack_l(
              |_ track = new RecordTrack(this, client, sampleRate,
              |_

--------------Tracks.cpp

Audio Application Framework:音频应用框架

  |---AudioTrack:负责回放数据的输出,属 Android 应用框架 API 类

  |---AudioRecord:负责录音数据的采集,属 Android 应用框架 API 类

  |---AudioSystem: 负责音频事务的综合管理,属 Android 应用框架 API 类

 

Audio Native Framework:音频本地框架

  |---AudioTrack:负责回放数据的输出,属 Android 本地框架 API 类

  |---AudioRecord:负责录音数据的采集,属 Android 本地框架 API 类

  |---AudioSystem: 负责音频事务的综合管理,属 Android 本地框架 API 类

 

Audio Services:音频服务

   |----AudioPolicyService:音频策略的制定者,负责音频设备切换的策略抉择、音量调节策略等

   |----AudioFlinger:音频策略的执行者,负责输入输出流设备的管理及音频流数据的处理传输

 

Audio HAL:音频硬件抽象层,负责与音频硬件设备的交互,由 AudioFlinger 直接调用

AudioTrack API 概述
   播放声音可以使用 MediaPlayer 和 AudioTrack,两者都提供 Java API 给应用开发者使用。
   两者的差别在于:
         MediaPlayer 可以播放多种格式的音源,如 mp3、flac、wma、ogg、wav 等,
         而 AudioTrack 只能播放解码后的 PCM 数据流。

了解下音频重采样:音频重采样是指这样的一个过程——把一个采样率的数据转换为另一个采样率的数据。
Android 原生系统上,音频硬件设备一般都工作在一个固定的采样率上(如 48 KHz),因此所有音轨数据
都需要重采样到这个固定的采样率上,然后再输出。
  为什么这么做?系统中可能存在多个音轨同时播放,而每个音轨的采样率可能是不一致的;
  比如在播放音乐的过程中,来了一个提示音,这时需要把音乐和提示音混音并输出到硬件设备,
  而音乐的采样率和提示音的采样率不一致,问题来了,如果硬件设备工作的采样率设置为音乐的采样率的话,
  那么提示音就会失真;因此最简单见效的解决方法是:硬件设备工作的采样率固定一个值,所有音轨在
   AudioFlinger 都重采样到这个采样率上,混音后输出到硬件设备,保证所有音轨听起来都不失真

Google 把多个子类抽取出来独立成文件,比如 Threads.cpp、Tracks.cpp、Effects.cpp,
而 AudioFlinger.cpp 只包含对外提供的服务接口了
  |----AudioResampler.cpp:重采样处理类,可进行采样率转换和声道转换;由录制线程 
         AudioFlinger::RecordThread 直接使用

  |----AudioMixer.cpp:混音处理类,包括重采样、音量调节、声道转换等,其中的重采样复用了 AudioResampler;
          由回放线程 AudioFlinger::MixerThread 直接使用

  |----Effects.cpp:音效处理类

|----Tracks.cpp:音频流管理类,可控制音频流的状态,如 start、stop、pause

  |-----Threads.cpp:回放线程和录制线程类;回放线程从 FIFO 读取回放数据并混音处理,然后写数据到输出流设备;
          录制线程从输入流设备读取录音数据并重采样处理,然后写数据到 FIFO

|-----AudioFlinger.cpp:AudioFlinger 对外提供的服务接口

 

AudioFlinger 服务启动

从 Android 7.0 开始,AudioFlinger 在系统启动时由 audioserver 加载(之前版本由 mediaserver 加载),
详见 frameworks/av/media/audioserver/main_audioserver.cpp:
main_audioserver.cpp 编译生成的可执行文件存放在 /system/bin/audioserver,系统启动时由 init 进程运行,
详见 frameworks/av/media/audioserver/audioserver.rc:

ThreadBase:PlaybackThread 和 RecordThread 的基类

RecordThread:录制线程类,由 ThreadBase 派生

PlaybackThread:回放线程基类,同由 ThreadBase 派生

MixerThread:混音回放线程类,由 PlaybackThread 派生,负责处理标识为 AUDIO_OUTPUT_FLAG_PRIMARY、AUDIO_OUTPUT_FLAG_FAST、   AUDIO_OUTPUT_FLAG_DEEP_BUFFER 的音频流,MixerThread 可以把多个音轨的数据混音后再输出

DirectOutputThread:直输回放线程类,由 PlaybackThread 派生,负责处理标识为 AUDIO_OUTPUT_FLAG_DIRECT 的音频流,  这种音频流数据不需要软件混音,直接输出到音频设备即可

DuplicatingThread:复制回放线程类,由 MixerThread 派生,负责复制音频流数据到其他输出设备,使用场景如主声卡设备、
   蓝牙耳机设备、USB 声卡设备同时输出

OffloadThread:硬解回放线程类,由 DirectOutputThread 派生,负责处理标识为 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音频流,
   这种音频流未经软件解码的(一般是 MP3、AAC 等格式的数据),需要输出到硬件解码器,由硬件解码器解码成 PCM 数据  

从 Audio HAL 中,我们通常看到如下 4 种输出流设备,分别对应着不同的播放场景:

|----primary_out:主输出流设备,用于铃声类声音输出,对应着标识为 AUDIO_OUTPUT_FLAG_PRIMARY 的音频流和一个 MixerThread  回放线程实例

|----low_latency:低延迟输出流设备,用于按键音、游戏背景音等对时延要求高的声音输出,对应着标识为 AUDIO_OUTPUT_FLAG_FAST   的音频流和一个 MixerThread 回放线程实例

|----deep_buffer:音乐音轨输出流设备,用于音乐等对时延要求不高的声音输出,对应着标识为 AUDIO_OUTPUT_FLAG_DEEP_BUFFER 的音频流和一个 MixerThread 回放线程实例

|----compress_offload:硬解输出流设备,用于需要硬件解码的数据输出,对应着标识为 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD  的音频流和一个 OffloadThread 回放线程实例

可能有人产生这样的疑问:既然 primary_out 设备一直保持打开,那么能耗岂不是很大?这里阐释一个概念:输出流设备属于逻辑设备,  并不是硬件设备。所以即使输出流设备一直保持打开,只要硬件设备不工作,那么就不会影响能耗。那么硬件设备什么时候才会打开呢?  答案是 PlaybackThread 将音频数据写入到输出流设备时

系统启动时,就已经打开 primary_out、low_latency、deep_buffer 这三种输出流设备,并创建对应的 MixerThread 了;
 而此时 DirectOutputThread 与 OffloadThread 不会被创建,直到标识为 AUDIO_OUTPUT_FLAG_DIRECT/AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 
 的音频流需要输出时,才开始创建 DirectOutputThread/OffloadThread 和打开 direct_out/compress_offload 设备。

NuPlayer DataSource/Parser 解析 mp3、flac 等文件得到数据编码信息,并在构造 AudioTrack 实例时作为参数传入,AudioFlinger   将基于这些编码信息打开 compress_offload 设备。到这里,大家明白了吗?每个 mp3/flac 文件的编码信息可能是不一样的,比如 a.mp3 文件的编码信息是 mp3&44.1KHZ&16bit… ,
而 b.flac 文件的编码信息是 flac&48KHz&24bit…; 播放 a.mp3 时,AudioFlinger 打开一个配置为 mp3&44.1KHz&16bit… 的 
compress_offload 设备,接着播放 b.flac,就需要关闭之前的 compress_offload 设备,重新打开一个配置为 flac&48KHz&24bit… 的 compress_offload 设备。所以系统不会提前打开 compress_offload 设备,只有等到播放 mp3、flac 时取到明确的数据编码信息, 才基于这些编码信息打开 compress_offload 设备。

编码信息包含很多条目,切换音源时,是否编码信息有一点点不一样,都需要重新打开 compress_offload 设备呢?不能运行时更新信息到  DSP 吗?其实 stagefright 和 compress_offload 是支持运行期更新某些信息的,也就是无缝切换,至于是哪些信息,依赖于 DSP 算法  实现;有兴趣深入的可以参考 sendMetaDataToHal() 和 compress_set_gapless_metadata() 。

AudioFlinger 音频流管理
从 AudioTrack、PlaybackThread、输出流设备三者的关系图中,我们看到 AudioTrack 把音频流数据送入到对应的 PlaybackThread 中,那么应用进程想控制这些音频流的话,比如开始播放 start()、停止播放 stop()、暂停播放 pause(),怎么办呢?注意应用进程与 AudioFlinger 并不在一个进程上。这就需要 AudioFlinger 提供音频流管理功能,并提供一套通讯接口可以让应用进程跨进程控制 AudioFlinger 中的音频流状态  

AudioFlinger 音频流管理由 AudioFlinger::PlaybackThread::Track 实现,Track 与 AudioTrack 是一对一的关系,一个 AudioTrack 创建后,那么 AudioFlinger 会创建一个 Track 与之对应;PlaybackThread 与 AudioTrack/Track 是一对多的关系,一个 PlaybackThread 可以挂着多个 Track。

音频流控制最常用的三个接口:
   |----AudioFlinger::PlaybackThread::Track::start:开始播放:把该 Track 置 ACTIVE 状态,然后添加到 mActiveTracks 向量中,
      最后调用 AudioFlinger::PlaybackThread::broadcast_l() 告知 PlaybackThread 情况有变

|---AudioFlinger::PlaybackThread::Track::stop:停止播放:把该 Track 置 STOPPED 状态,最后调用 
       AudioFlinger::PlaybackThread::broadcast_l() 告知 PlaybackThread 情况有变

|----AudioFlinger::PlaybackThread::Track::pause:暂停播放:把该 Track 置 PAUSING 状态,最后调用
       AudioFlinger::PlaybackThread::broadcast_l() 告知 PlaybackThread 情况有变

AudioFlinger::PlaybackThread::threadLoop() 得悉情况有变后,调用 prepareTracks_l() 重新准备音频流和混音器:ACTIVE 状态的 Track 会添加到 mActiveTracks,此外的 Track 会从 mActiveTracks 上移除出来,然后重新准备 AudioMixer。

可见这三个音频流控制接口是非常简单的,主要是设置一下 Track 的状态,然后发个事件通知 PlaybackThread 就行,复杂的处理都在  AudioFlinger::PlaybackThread::threadLoop() 中了。

AudioFlinger::TrackHandle:Track 对象只负责音频流管理业务,对外并没有提供跨进程的 Binder 调用接口,而应用进程又需要对音频流进行控制,所以需要一个对象来代理 Track 的跨进程通讯,这个角色就是 TrackHandle,AudioTrack 通过它与 Track 交互

AudioTrack:Android 音频系统对外提供的一个 API 类,负责音频流数据输出;每个音频流对应着一个 AudioTrack 实例,不同输出标识的 AudioTrack 会匹配到不同的 AudioFlinger::PlaybackThread;AudioTrack 与 AudioFlinger::PlaybackThread 之间通过 FIFO 来交换音 频数据,AudioTrack 是 FIFO 生产者,AudioFlinger::PlaybackThread 是 FIFO 消费者

IAudioTrack:IAudioTrack 是链结 AudioTrack 与 AudioFlinger 的桥梁;它在 AudioTrack 端的对象是 BpAudioTrack,在 AudioFlinger 端的 对象是 BnAudioTrack,从图中不难看出,AudioFlinger::TrackHandle 继承自 BnAudioTrack,而 AudioFlinger::TrackHandle 恰恰是 AudioFlinger::PlaybackThread::Track 的代理对象,所以 AudioTrack 得到 IAudioTrack 实例后,就可以调用 IAudioTrack 的接口与 AudioFlinger::PlaybackThread::Track 交互

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值