音频

Android Audio框架

2018-08-31  本文已影响0人  gbmaotai
ASLA -Advanced Sound Linux Architecture
OSS -以前的Linux音频体系结构,被ASLA取代并兼容
I2S/PCM/AC97 - Codec与CPU间音频的通信协议/接口/总线
DAI - Digital Audio Interface 其实就是I2S/PCM/AC97
DAPM - Dynamic Audio Power Management

1) 播放音乐

2) 录音

3)打电话

clipboard2.png

4) 通过蓝牙打电话

clipboard4.png

Android系统上的音频框架

Framework

MediaPlayer和MediaRecorder
AudioTrack和AudioRecorder

Android系统还为我们控制音频系统提供了AudioManager、AudioService及AudioSystem类。这些都是framework为便利上层应用开发所设计的。

libraries (AudioFlinger )

除了上面的类库实现外,音频系统还需要一个“核心中控”,或者用Android中通用的实现来讲,需要一个系统服务(比如 ServiceManager、LocationManagerService、ActivityManagerService等等),这就是 AudioFlinger和AudioPolicyService。它们的代码放置在frameworks/av/services /audioflinger,生成的最主要的库叫做libaudioflinger。

音频体系中另一个重要的系统服务是MediaPlayerService,它的位置在frameworks/av/media/libmediaplayerservice。

其一,以库为线索。比如AudioPolicyService和AudioFlinger都是在libaudioflinger库中;而AudioTrack、AudioRecorder等一系列实现则在libmedia库中。

其二,以进程为线索。库并不代表一个进程,进程则依赖于库来运行。虽然有的类是在同一个库中实现的,但并不代表它们会在同一个进程中被调用。比如 AudioFlinger和AudioPolicyService都驻留于名为mediaserver的系统进程中;而 AudioTrack/AudioRecorder和MediaPlayer/MediaRecorder一样实际上只是应用进程的一部分,它们通过 binder服务来与其它系统进程通信。

HAL( audio_hw.cpp ,audio.primary.so)

从设计上来看,硬件抽象层是AudioFlinger直接访问的对象。这说明了两个问题,一方面AudioFlinger并不直接调用底层的驱动程 序;另一方面,AudioFlinger上层(包括和它同一层的MediaPlayerService)的模块只需要与它进行交互就可以实现音频相关的功 能了。因而我们可以认为AudioFlinger是Android音频系统中真正的“隔离板”,无论下面如何变化,上层的实现都可以保持兼容。

Audio HAL提供了统一的接口来定义它与AudioFlinger/AudioPolicyService之间的通信方式,这就是** audio_hw_device、audio_stream_in及audio_stream_out等等存在的目的,这些Struct数据类型内部大多 只是函数指针的定义,是一些“壳”。当AudioFlinger/AudioPolicyService初始化时,它们会去寻找系统中最匹配的实现(这些 实现驻留在以audio.primary.,audio.a2dp.为名的各种库中)来填充这些“壳”。

AudioFlinger

Android中的系统服务分为两类,分别是Java层和Native层的System Services。其中AudioFlinger和SurfaceFlinger一样,都属于后者。Java层服务通常在 SystemServer.java中启动,比如后面会看到的AudioService就是这种情况.

1.mediaserver

mediaserver的目录下只有一个文件,它的任务很简单,就是把所有媒体相关的native层服务(包括AudioFlinger,MediaPlayerService,CameraService和AudioPolicyService)启动起来.

int main(int argc, char** argv)
{
    sp<ProcessState>proc(ProcessState::self());
    sp<IServiceManager>sm = defaultServiceManager();
   ALOGI("ServiceManager: %p", sm.get());
   AudioFlinger::instantiate();
   MediaPlayerService::instantiate();
   CameraService::instantiate();
   AudioPolicyService::instantiate();
   ProcessState::self()->startThreadPool();
   IPCThreadState::self()->joinThreadPool();
}

AudioFlinger 继承BinderService

class AudioFlinger :
    public BinderService<AudioFlinger>,
    public BnAudioFlinger…
实际的初始化工作不在构造函数中,而在void AudioFlinger::onFirstRef()
2.音频设备的管理

AudioPolicyService是策略的制定者,比如什么时候打开音频接口设备、某种Stream类型的音频对应什么设备等等。

AudioFlinger则是策略的执行者,例如具体如何与音频设备通信,如何维护现有系统中的音频设备,以及多个音频流的混音如何处理等等都得由它来完 成。

Audio系统中支持的音频设备接口(Audio Interface)分为三大类,即:

/*frameworks/av/services/audioflinger/AudioFlinger.cpp*/
static const char * const audio_interfaces[] = {
   AUDIO_HARDWARE_MODULE_ID_PRIMARY, //主音频设备,必须存在
   AUDIO_HARDWARE_MODULE_ID_A2DP, //蓝牙A2DP音频
   AUDIO_HARDWARE_MODULE_ID_USB, //USB音频,早期的版本不支持
};

每种音频设备接口由一个对应的so库提供支持。

AudioFlinger::loadHwModule(const char *name)/*name就是前面audio_interfaces 数组
                                                            成员中的字符串*/
Step1@ loadHwModule_l. 首先查找mAudioHwDevs是否已经添加了变量name所指示的audio interface,如果是的话直接返回。第一次进入时mAudioHwDevs的size为0,所以还会继续往下执行。
Step2@ loadHwModule_l. 加载指定的audiointerface,比如“primary”、“a2dp”或者“usb”。函数load_audio_interface用来加载 设备所需的库文件,然后打开设备并创建一个audio_hw_device_t实例。音频接口设备所对应的库文件名称是有一定格式的,比如a2dp的模块 名可能是audio.a2dp.so或者audio.a2dp.default.so等等。查找路径主要有两个,即:
/** Base path of the hal modules */
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
Step3@ loadHwModule_l,进行初始化操作。其中init_check是为了确定这个audio interface是否已经成功初始化,0是成功,其它值表示失败。接下来如果这个device支持主音量,我们还需要通过 set_master_volume进行设置。在每次操作device前,都要先改变mHardwareStatus的状态值,操作结束后将其复原为 AUDIO_HW_IDLE(根据源码中的注释,这样做是为了方便dump时正确输出内部状态,这里我们就不去深究了)。
Step4@ loadHwModule_l. 把加载后的设备添加入mAudioHwDevs键值对中,其中key的值是由nextUniqueId生成的,这样做保证了这个audiointerface拥有全局唯一的id号。

AudioFlinger是如何打开一个Output通道

audio_io_handle_t  AudioFlinger::openOutput(audio_module_handle_tmodule, audio_devices_t *pDevices,
                                          uint32_t *pSamplingRate,audio_format_t *pFormat,
                                          audio_channel_mask_t *pChannelMask,
                                          uint32_t *pLatencyMs, audio_output_flags_t flags)
/*入参中的module是由前面的loadHwModule 获得的,它是一个audiointerface的id号,可以通过此id在mAudioHwDevs中查找到对应的AudioHwDevice对象*/

outHwDev用于记录一个打开的音频接口设备,它的数据类型是audio_hw_device_t,是由HAL规定的一个音频接口设备所应具有的属性集合
struct audio_hw_device {
    struct hw_device_t common;
     …
    int (*set_master_volume)(struct audio_hw_device *dev, float volume);
    int (*set_mode)(struct audio_hw_device *dev, audio_mode_t mode);
    int (*open_output_stream)(struct audio_hw_device *dev,
                             audio_io_handle_t handle,
                             audio_devices_t devices,
                             audio_output_flags_t flags,
                             struct audio_config *config,
}

Step1. 在openOutput中,设备outHwDev是通过查找当前系统来得到的

audio_hw_device_t*  AudioFlinger::findSuitableHwDev_l(audio_module_
clipboard.png
handle_t module, uint32_t devices)

一种情况就是前面看到的modules为0时,会load所有潜在设备,另一种情况就是AudioPolicyManagerBase在构造时会预加载所有audio_policy.conf中所描述的output。

Step2,调用open_output_stream打开一个audio_stream_out_t。

Step3,生成AudioStreamOut对象。这个变量没什么特别的,它把audio_hw_device_t和audio_stream_out_t做为一个整体来封装。

Step4. 既然通道已经打开,那么由谁来往通道里放东西呢?这就是PlaybackThread。

DirectOutput 如果不需要混音
Mixer 需要混音
这两种情况分别对应DirectOutputThread和MixerThread两种线程

Step5,到目前为止,我们已经成功的建立起一个音频通道,就等着AudioTrack往里丢数据了。

Audio Codec

1.内置

2.WM8978 欧胜微电子(Wolfson英国的爱丁堡)
3.ALC5623 (realtek)

tinymix

tinymix: 查看配置混音器

tinyplay: 播放音频

tinycap: 录音

查看当前系统的声卡

cat /proc/asound/cards

pcm设备

在Android中一个pcm设备最多可有

一个mixer设备"/dev/snd/controlC%u"(一般是controlC0)和

32个/dev/snd/pcmC%uD%uc(一般是pcmC0D0c)、/dev/snd/pcmC%uD%u%p(一般是pcmC0D0p),pcm设备中的C代表card,D代表device,c代表capture,p代表playback。

tinyalsa的对外提供的头文件一个"asoundlib.h",提供最基础的pcm和mixer操作。
实现文件为pcm.c(实现pcm api)和mixer.c(实现mixer api)

上一篇下一篇

猜你喜欢

热点阅读