Android Audio框架

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)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容

  • Android音频系统详解 参考好文: Android 音频系统:从 AudioTrack 到 AudioFlin...
    爱雨520阅读 13,573评论 2 7
  • 提醒一下,纯个人笔记,你完全可能看晕 一、音频数字化基础知识 见书,列出知识点如下: 声音声波,声音频率、响度, ...
    YY17阅读 31,265评论 6 48
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,070评论 25 707
  • 提醒一下,纯个人笔记,你完全可能看晕 六、HAL层 6-1、Audio HAL层,其实包括了audio.xxx.s...
    YY17阅读 12,106评论 2 19
  • 已是入了黄梅时节,雾气氤氲的扬州城倒是比平时多了几分冷清。浮岚飘渺,像极了阆苑瑶台。 叶长亭行走在泥泞的阡陌小路上...
    北何三阅读 258评论 0 0