gbmaotai关注
0.3592018.08.31 11:22:30字数 1,353阅读 6,308
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)
{
spproc(ProcessState::self());
spsm = 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,
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)