Android 音频系统

  • App
    音频应用软件

  • Framework
    MediaPlayer 和 MediaRecorder,以及 AudioTrack、AudioRecorder、AudioMannager、AudioService 以及 AudioSystem。

  • Libraries
    frameorks/av/media/libmedia、libaudioflinger、libmediaplayerservice

  • HAL
    AudioFlinger、AudioPolicyService。

  • AudioPolicyService:APS 是音频框架的服务,main_audioserver 启动,会创建 AudioCommandThread 和 AudioPolicyClient、AudioPolicyManager。它主要由 AudioSystem 通过 binder 调用,也可以由 AudioPolicyClient,AudioPolicyManager 直接调用。它的大部分操作都交给 AudioPolicyManager 来做

  • AudioPolicyClient:APC 是 AudioPolicyService 的内部类。它用于打开关闭输入输出,设置流音量,传递参数给 hal 层(如 audio_hw.cpp)等;它主要是通过 binder 跨进程调用 AudioFlinger 去完成真正的操作。可以由 AudioManager 通过 mpClientInterface 去调用它。

  • AudioPolicyManager:APM 是 AudioPolicyService 的主要工作类,AudioPolicyService 的大部分操作都由他来执行

  • AudioFlinger:AF 主要承担音频混合输出,是 Audio 系统的核心,从 AudioTrack 来的数据最终都会在这里处理,并被写入到 Audio 的 HAL。

  • DevicesFactoryHalLocal:根据名字加载对应的 hal module。比如传进去 a2dp 相关的名字,会加载到 audio.a2dp.default.so

  • DevicesFactoryHalHidl:跨进行加载 hidl hal module。

  • DevicesFactoryHalInterface:用于创建子类 DevicesFactoryHalHybrid。

  • DevicesFactoryHalHybrid:选择创建 DevicesFactoryHalLocal 或者 DevicesFactoryHalHidl,我这里只创建 DevicesFactoryHalLocal。

  • DeviceHalLocal:通过私有成员 audio_hw_device_t *mDev,直接调用 hal 代码,用来设置和获取底层参数,打开和关闭 stream。

  • StreamOutHalLocal:通过私有成员 audio_stream_out_t *mStream 直接调用 hal 代码,用于操作流,比如 start、stop、flush、puse 操作;还有调用 write 函数写音频数据到 hal 层。

  • AudioStreamOutSink:它其实是 StreamOutHalLocal 的一个 wrapper,它也有 write 函数,不过是通过 StreamOutHalLocal 来操作的。

    附上一张重要的类图:

Audio 服务的启动

  1. 创建 AudioFlinger 和 AudioPolicyService。
  2. 解析 Audio Config 文件(audio_policy_configuration.xml),获取支持的音频外设列表及各输入输出通路详细参数。
  3. 根据解析得到的外设列表,加载所有的 Audio HAL 库。
  4. 为所有 output 设备打开 outputStream 并创建 PlaybackThread 线程。
  5. 为所有 input 设备打开 inputStream 并创建 RecordThread 线程。

AudioTrack

Android 声音播放都是通过 AudioTrack 进行,包括 MediaPlayer 最终也是创建 AudioTrack 来播放的。通过 AudioTrack 播放声音主要包括下面几步:

  1. 创建 AudioTrack。
  2. 调用 AudioTrack 的 play() 方法。
  3. 调用 AudioTrack 的 write() 方法写入音频数据。

创建 AudioTrack 时重点是通过 AudioPolicyManager 分配了音频路由通路,同时通知服务端 AudioFlinger 创建对应的 Track,用于接收音频数据。

  • 调用 play() 方法主要是将创建的 Track 加到 mActiveTracks 并激活沉睡的 PlaybackThread 线程。
  • 调用 write() 方法通过共享内存将数据写入服务端 AudioFlinger,PlaybackThread 收到数据激活线程,将数据进行混音等处理再写入对应的 Audio HAL,Audio HAL 再将数据写入驱动或其它外设。

音频策略

音频调试参考 Android Audio

首先要搞清楚 stream_type,device,strategy 三者之间的关系:

  • AudioSystem::stream_type:音频流的类型
  • AudioSystem::audio_devices:音频输入输出设备,每一个 bit 代表一种设备。
  • AudioPolicyManagerBase::routing_strategy:音频路由策略

AudioPolicyManagerBase.getStrategy 根据 stream type,返回对应的 routing strategy 值,AudioPolicyManagerBase.getDeviceForStrategy() 则是根据 routing strategy,返回可用的 device。
首先需要的是加载音频设备,音频设备的配置在 system/etc/audio_policy.confvendor/etc/aduio_policy, 配置文件中表示了各种 audio interface,通过 AudioFlinger 加载音频设备。
按照一定的优先级选择符合要求的 Device,然后为 Device 选择合适的 Output 通道。
可以通过重载 getStrategy 自己划分 Strategy。

routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);

截取音频数据

有三个地方可以获取到 PCM 数据:

  • 第一个地方 frameworks/av/media/libmedia/AudioTrack.cpp
nsecs_t AudioTrack::processAudioBuffer(){  
{
//在 releaseBuffer 之前 dump
releaseBuffer(&audioBuffer);
}
  • 第二个地方 frameworks/av/services/audioflinger/Tracks.cpp
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
AudioBufferProvider::Buffer* buffer)
{
// add dump method
ServerProxy::Buffer buf;
size_t desiredFrames = buffer->frameCount;
buf.mFrameCount = desiredFrames;
status_t status = mServerProxy->obtainBuffer(&buf);
buffer->frameCount = buf.mFrameCount;
buffer->raw = buf.mRaw;
if (buf.mFrameCount == 0 && !isStopping() && !isStopped() && !isPaused()) {
ALOGV("underrun, framesReady(%zu) < framesDesired(%zd), state: %d",
buf.mFrameCount, desiredFrames, mState);
mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
} else {
mAudioTrackServerProxy->tallyUnderrunFrames(0);
}

return status;
}
  • 第三个地方hardware/xxx/audio/tinyalsa_hal/audio_hw.c
static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, size_t bytes)
{
//add dump method
}

音频数据流向

Android 系统 audio 框架中主要有三种播放模式:low latency playback、deep buffer playback 和 compressed offload playback。

  • low latency / deep buffer 模式下的音频数据流向

  • compressed offload 模式下的音频数据流向

  • 音频录制

  • 打电话

配置解析

module 下面有 mixPorts、devicePorts 和 routes 子段,它们下面又分别包含多个 mixPort、devicePort 和 route 的字段,这些字段内标识为 source 和 sink 两种角色:
devicePorts(source):为实际的硬件输入设备;
devicePorts(sink):为实际的硬件输出设备;
mixPorts(source):为经过 AudioFlinger 之后的流类型,也称“输出流设备”,是个逻辑设备而非物理设备,对应 AudioFlinger 里面的一个 PlayerThread;
mixPorts(sink):为进入 AudioFlinger 之前的流类型,也称“输入流设备”,是个逻辑设备而非物理设备,对应 AudioFlinger 里面的一个 RecordThread;
routes:定义 devicePort 和 mixPorts 的路由策略。

profile 参数包含音频流一些信息,比如位数、采样率、通道数,它将被构建为 AudioProfile 对象,保存到 mixPort,然后在存储到 module。对于 xml 里面的 devicePort,一般没有 profile 参数,则会创建一个默认的 profile。当把 mixPort 加入到 Moudle 时,会进行分类:

即 source 角色保存到 OutputProfileCollection mOutputProfiles,sink 角色保存到 InputProfileCollection mInputProfiles。
而 devicePort 则调用 HwModule::setDeclaredDevices() 保存到 module 的 mDeclaredDevices。

HAL

Audio HAL 大致的类图,hal 采用工厂模式,分为 Local 和 HIDL 模式,最后都会调用到 audio_stream_out 或者 audio_stream_in 中,对应调用到 audio_hw.c(由各个厂商实现)中。

蓝牙连接例子

AudioService 的 handleDeviceConnection 调用 AudioPolicyManager 的 setDeviceConnectionStateInt。

checkOutputsForDevice 会检测所有的 profile(output),查找每个 profile 是否都存在对应的线程,如果没有则进行创建 checkOutputForAllStrategies 切换 AudioTrack 的写入数据源。