天天看点

Android音频框架笔记 Android音频框架笔记 - 上篇

Android音频框架笔记 - 上篇

    本文转自:http://www.jianshu.com/p/9481b1482367

提醒一下,纯个人笔记,你完全可能看晕

一、音频数字化基础知识

见书,列出知识点如下:

  • 声音

    声波,声音频率、响度, 音调、音色。

    音响设备中的声道

    得翻翻初高中的课本了。

  • 声音数字化过程

    声源 -> mic -> ADC(模-数转换器) ,采样、量化 -> [可选项] 过滤、音效等特殊化处理 -> 编码 ->

    其中采样过程,涉及采样率、采样深度、声道等。

    奈奎斯特采样定律: 见书。

    音量调节与韦伯定律: 见书。

    这得看看大学课本:高数、模拟电路、数字电路、多媒体技术。

  • 音频编码解码格式

    是指对原始PCM音频数据的压缩与编码、解码格式。

  • 音频文件格式

    是指存储音频数据的文件存储格式。

二、架构层级图

音频框架全图

Android音频框架笔记 Android音频框架笔记 - 上篇

Paste_Image.png

Android音频框架笔记 Android音频框架笔记 - 上篇

Paste_Image.png

Android音频框架笔记 Android音频框架笔记 - 上篇

Paste_Image.png

三、代码与生成的库

3-1、代码位置

frameworks\av\media\

\frameworks\av\services\audioflinger

frameworks/base/media/

Audio 系统代码

  • 1、Audio 的Java 部分

    frameworks/base/media/java/android/media

    frameworks/base/media/jni

    生成framework.jar,media是里面的一部分。

与Audio 相关的Java包是android.media,主要包含AudioManager和Audio 系统的几个类。

提供了两套API,如:

AudioTrack.java/AudioRecord.java 更原始,更灵活,也更不方便,

MediaPlayer/MediaRecord包含了编解码,文件格式支持,更简便。

  • 2、Audio 的JNI 部分

frameworks/base/core/jni

生成库libandroid_runtime.so,Audio 的JNI是其中的一个部分。这部分屏蔽了对Audio本地框架调用细节,简化后,相当于JAVA接口的本地中转,本地接口转换本来就是JNI功能之一。

  • 3、Audio 的框架部分

总位置frameworks\av\media,里面都是多媒体相关的

3.1

libmedia.so

部分,Audio系统的核心框架,向上层Audio 的

JNI 部分提供接口,间接为Audio的Java部分提供接口。

frameworks\av\media\libmedia

frameworks\av\include\media

旧版本:

frameworks/base/include/media/

frameworks/base/media/libmedia/

这部分内容被编译成库libmedia.so,实现Audio系统的核心框架,同时提供了IAudioFlinger 类接口。在这个类中,可以获得IAudioTrack 和IAudioRecorder 两个接口,分别用于声音的播放和录制。

AudioTrack 和AudioRecorder

分别调用IAudioTrack 和IAudioRecorder 来实现。IAudioFlinger.h、IAudioTrack.h 和IAudioRecorder.h 这三个接口通过下层来实现。AudioSystem.h、AudioTrack.h 和AudioRecorder.h 是对上层提供的接口,它们既供本地程序调用,也可以通过JNI 向Java 层提供接口。从功能上看,

AudioSystem

负责的是Audio 系统的综合管理功能,而AudioTrack 和AudioRecorder 分别负责音频数据的输出和输入,即播放和录制。另外一个接口是IAudioFlingerClient,它作为向IAudioFlinger中注册的监听器,相当于使用回调函数获取IAudioFlinger运行时信息。

MediaScanner

负责媒体文件扫描。

3.2 mediaplayerservice

frameworks\av\media\libmediaplayerservice

生成 libmediaplayerservice.so

上层其中一套API的服务端实现,即MediaPlayer/MediaRecord的服务端实现。

其实最终实现,Audio部分是通过libmedia.so里的AudioTrack/AudioRecord实现。

此外,内部包括了编码、解码、文件格式、视频播放/录制等。

视频播放部分,有好几个播放器,如StagefrightPlayer/Recorder,NuPlayer,而早期还有用openCore。

3.3 mediaserver,媒体服务进程

frameworks\av\media\mediaserver

所有媒体服务端代码线程都在此进程被启动

生成/system/bin/mediaserver

3.4 libeffects 音效部分

frameworks\av\media\libeffects

生成

/system/lib/libeffects.so // 总入口

/system/lib/soundfx/ //具体各种音效 

  • 4、本地services

    部分,音频服务进程

4.1 Audio Flinger

frameworks\av\services\audioflinger

旧版

frameworks/base/libs/audioflinger

这部分内容被编译成库libaudioflinger.so,它是Audio系统的本地服务部分。(其实也有libaudioresampler.so)

4.2 audiopolicy本地框架服务部分

frameworks\av\services\audiopolicy

生成libaudiopolicymanager.so, libaudiopolicyservice.so , libaudiopolicymanagerdefault.so

音频策略的本地服务框架,

向上提供接口,即向AudioSystem.cpp/AudioService.cpp/AudioManager.cpp等提供接口,//我猜是这样,至少AudioSystem.cpp确实包装了AudioPolicyService.cpp

向下通过HAL层调用到厂商真正实现音频策略的库,默认实现为audio_policy.default.so,而最终又调用到具体实现类AudioPolicyManagerBase。

功能上只负责策略性处理,包括转给audio_policy.default.so后得到的结果,

而具体音频处理事务转到AudioFlinger最终去执行。

关键点是AudioPolicyManagerBase 会去加载audio_policy.conf , audio_effects.conf,从而实现音频设备、音效上的策略的可配置。所以一般只改配置,应该很少有厂商去改AudioManagerBase吧。

  • 5、Audio 的硬件抽象层接口

    Android对Audio HAL的框架定义及遗留实现

    hardware\libhardware\modules

    hardware/libhardware_legacy/include/hardware/

HAL厂商实现代码

device\samsung\manta\audio

device\asus\grouper\audio

1、Audio使用JNI和Java对上层提供接口,JNI部分通过调用libmedia库提供的接口来实现。

2、 Audio 本地框架类是libmedia.so的一个部分,这些Audio框架类对上层提供接口,由下层的本地代码去实现。

3、AudioFlinger继承libmeida中的接口,提供实现库libaudiofilnger.so。这部分内容没有自己的对外头文件,上层调用的只是libmedia本部分的接口,但实际调用的内容是libaudioflinger.so。

4、Audio的硬件抽象层提供到硬件的接口,供AudioFlinger调用。Audio的硬件抽象层实际上是各个平台开发过程中需要主要关注和独立完成的部分。

在Android的Audio系统中,无论上层还是下层,都使用一个管理类和输出输入两个类来表示整个Audio系统,输出输入两个类负责数据通道。在各个层次之间具有对应关系:

Android音频框架笔记 Android音频框架笔记 - 上篇

Paste_Image.png

  • 6、底层音频架构相关**

    system\core\include\system

    external/tinyalsa

    或 alsa版本

    external/alsa-lib

    external/alsa-utils

4.1后基本都用tinyalsa

  • 7、其它

    7.1 音频常量定义

    system\core\include\system\audio.h

    AudioSystem.java

    里面很多常量保持一致,一个给native引用,一个给java引用。

7.2 native层公用头文件定义

system\core\include\system\audio.h

这个头文件很重要,它的一些常量定义,需要上下均保持一致,所以Java层中的AudioSystem.java与此保持一致。

audio.h则是给native层的所有模块引用,

包括JNI层的各cpp文件,libaudioflinger.so,audio.xxx.so,audio_policy.so等。

(当然,废话一句,到alsalib库就不是了,它引用头文件是alsa标准的头文件,旨在按alsa架构看齐,

并向上提供统一的alsalib接口,反而是Android的HAL层要使用alsalib的头文件。)

音频库与代码位置

libmedia.so

frameworks\av\media\libmedia

音频服务框架代码

libaudioflinger.so**

\frameworks\av\services\audioflinger HAL框架代码

hardware\libhardware\modules HAL的官方标准实现代码? HAL厂商实现代码**

device\samsung\manta\audio

device\asus\grouper\audio

底层音频架构相关

system\core\include\system

external/tinyalsa

音频库与小机文件位置

音频服务框架库:** libaudioflinger.so libmedia.so

一般放在机器中的/system/lib目录。

音频设备HAL库:audio_xxx.so

具体由硬件厂商来实现和定制,audio_policy.conf的配置跟这些实现要对应。

这些库即音频HAL层框架的实现,音频HAL框架定义在:hardware\libhardware\include\hardware\audio.h

具体的实现代码需要厂商提供,而google默认的实现,代码在: ???

生成的库,一般会有:audio_primary.so,audio_a2dp.so等。

一般放在机器中的/system/lib/hw或vendor/lib/hw目录。

相关进程:

systemserver: AudioFlinger, AudioPolicyService等,即Audio服务端

APP所在进程:AudioTrack,AudioRecoder等,即Audio客户端

andorid.process.media: 多媒体管理类所在进程

3-2、运行时模型,进程线程关系

四、Linux中的音频框架

  • Linux原生三种:OSS, ALSA, TinyAlsa。

    目前Android使用的是TinyALSA音频库。TinyAlsa其实只是位于Linux应用层的一个音频库,依赖于ALSA驱动架构,即底层驱动必须是用ALSA驱动架构,它是Android用于替代ALSA架构在Linux应用层的庞大复杂的库,让其更适用于嵌入式设备。

    反正,它为Linux应用层提供了统一方便的音频接口。如pcm_read() / pcm_write()

    而在Linux内核中,使用的ALSA音频架构,驱动框架按ALSA框架接口来写,驱动那块的移植是SOC厂商的事情。

参考:

ALSA官方文档

http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/index.html

ALSA声卡驱动系列文章

http://blog.csdn.net/DroidPhone/article/category/1118446

各种音频硬件通路图

http://blog.csdn.net/qianjin0703/article/details/6387662

拼凑还算完整的一篇(部分来自以上的文章)

http://www.360doc.com/content/14/0414/10/97538_368733041.shtml

  • 支持的音频设备(输出)列表

    硬件音频输出设备的类型, 见书

在HAL层,一个音频设备,用一个 audio_hw_device_t 结构体来表示,而它支持的输出类型,在audio_policy.conf被指明。

当上层要打开一个OUT通道时,会寻找匹配的类型,进行打开。

  • Android支持的媒体格式

    见书

    可录制的音频来源

    可录制的视频来源

    CAMERA 或 DEFAULT

五、代码详解

5-1、 类的使用

AudioRecord.cpp / AudioRecord.java(只是前者的JAVA封装)

AudioRecord.java的使用步骤

  1. 创建一个数据流。
  2. 构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通过getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败。
  3. 初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小。
  4. 开始录音。
  5. 从AudioRecord中读取声音数据到初始化buffer,将buffer中数据导入数据流。
  6. 停止录音。
  7. 关闭数据流。

对应的AudioRecord.cpp的使用代码:

AudioRecord  pAudioRecord  = new android::AudioRecord();    
pAudioRecord->set( inputSource,
                                sampleRateInHz,
                                audioFormat,
                                channelConfig,
                                minFrameCount,
                                AudioRecordCallback,
                                NULL,
                                ,
                                true,
                                ,
 android::AudioRecord::TRANSFER_DEFAULT, 
 AUDIO_INPUT_FLAG_NONE, //AUDIO_INPUT_FLAG_NONE //audio_input_flags_t flags);
 NULL
 );
           

AudioRecord recordInstance = new AudioRecord(
                MediaRecorder.AudioSource.REMOTE_SUBMIX, frequency,
                AudioFormat.CHANNEL_IN_MONO, audioEncoding, bufferSize);
recordInstance.startRecording();
           

//AudioRecod.cpp中的set()函数,看来一些限制:

AudioRecod.set(){
  if (inputSource == AUDIO_SOURCE_DEFAULT) {** //默认录制音源,即是MIC
       inputSource = AUDIO_SOURCE_MIC; 
    }
if (sampleRate == 0) {
    ALOGE("Invalid sample rate %u", sampleRate);
    return BAD_VALUE;
}

// these below should probably come from the audioFlinger too...
if (format == AUDIO_FORMAT_DEFAULT) {**//默认数据格式,即是**AUDIO_FORMAT_PCM_16_BIT
    format = AUDIO_FORMAT_PCM_16_BIT;
}

// Temporary restriction: AudioFlinger currently supports 16-bit PCM only
if (format != AUDIO_FORMAT_PCM_16_BIT) {**//数据格式,其实只能是**AUDIO_FORMAT_PCM_16_BIT
    ALOGE("Format %#x is not supported", format);
    return BAD_VALUE;
}

}
           

一些问题:

有哪些音源,输入还是输出? 单双声道? 音源和后两者的参数,如何配对?

其中:

录制音源,*CPP中与JAVA层定义一致。有如下一些音源。

总结下,就是MIC,通话进出,REMOTE_SUBMIX,CAMCORDER(录像用的mic,一般没有独立,最终其实也是默认的MIC)。

所以有些东西还是录制不到的,比如主设备混音后输出的声音,这包括了各种可能的音乐、铃声、通知等声音。

也就是,Android给到上层应用的接口是,只能给你录制MIC,录制通话,录像。其余录音场景,不给。

typedef enum {

AUDIO_SOURCE_DEFAULT = 0,

AUDIO_SOURCE_MIC = 1,

AUDIO_SOURCE_VOICE_UPLINK = 2,

AUDIO_SOURCE_VOICE_DOWNLINK = 3,

AUDIO_SOURCE_VOICE_CALL = 4,

AUDIO_SOURCE_CAMCORDER = 5,

AUDIO_SOURCE_VOICE_RECOGNITION = 6,

AUDIO_SOURCE_VOICE_COMMUNICATION = 7,

AUDIO_SOURCE_REMOTE_SUBMIX = 8, / Source for the mix to be presented remotely.  /

/ An example of remote presentation is Wifi Display /

/ where a dongle attached to a TV can be used to  /

/ play the mix captured by this audio source.  /

AUDIO_SOURCE_CNT,

AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1,

AUDIO_SOURCE_HOTWORD = 1999, / A low-priority, preemptible audio source for

for background software hotword detection.

Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION.

Used only internally to the framework. Not exposed

at the audio HAL. */

} audio_source_t;

声道:

定义了输入和输出,分别又定义了单、双、前后、5.1声道、7.1声道的各个声道,然后又定义了更方便使用的各种组合声道。

意思是按人们对于声道的理解来定义,而不理会底层如何实现。

显然,有些音源,是不可能有5.1声道的,对吧,所以,还是有个有效匹配问题。

AudioTrack

见书,

  • 简单说来,播放流程如下:

    Java端发起调用,MediaPlayer会转至MediaPlayerService,然后会调用相应的解码工具解码后创建AudioTrack,所有待输出的AudioTrack在AudioFlinger::AudioMixer里合成,然后通过AudioHAL(AudioHardwareInterface的实际实现者)传至实际的硬件来实现播放。

如果直接使用AudioTrack.java-> AudioTrack.cpp, 则后续流程直接到AudioFlinger,然后往下。

AudioTrack总结

通过这一次的分析,我自己觉得有以下几个点:

l AudioTrack的工作原理,尤其是数据的传递这一块,做了比较细致的分析,包括共享内存,跨进程的同步等,也能解释不少疑惑了。

l 看起来,最重要的工作是在AudioFlinger中做的。通过AudioTrack的介绍,我们给后续深入分析AudioFlinger提供了一个切入点

工作原理和流程嘛,再说一次好了,JAVA层就看最前面那个例子吧,实在没什么说的。

l AudioTrack被new出来,然后set了一堆信息,同时会通过Binder机制调用另外一端的AudioFlinger,得到IAudioTrack对象,通过它和AudioFlinger交互。

l 调用start函数后,会启动一个线程专门做回调处理,代码里边也会有那种数据拷贝的回调,但是JNI层的回调函数实际并没有往里边写数据,大家只要看write就可以了

l 用户一次次得write,那AudioTrack无非就是把数据memcpy到共享buffer中咯

l 可想而知,AudioFlinger那一定有一个线程在memcpy数据到音频设备中去。我们拭目以待。

5-2、相关类说明

  • 多媒体管理类

    MediaScannerService

    三种广播的扫描流程

    三种广播的扫描路径

    MediaProvider

    MediaReceiver

    MediaStore

  • 播放与录制,JAVA层

    MediaPlayer

    封装给App层,用于播放音视频多媒体的类

    状态迁移图

    框架图

    MediaRecorder

    封装给App层,用于录制音视频多媒体的类。非常友好的封装,录制、编码、存储等所有细节都被封装起来。**

    状态迁移图

    框架图

AudioRecord.java

封装给APP层的音频录制类,提供非常友好,简化的接口和调用流程。

通过JNI封装了AudioRecord.cpp功能类的具体事务操作。

JNI文件:android_media_AudioRecord.cpp

AudioTrack.java

AudioTrack.cpp的java层封装类,播放音频

AudioTrack.cpp

用于音频数据的回放。

分两类数据传送模式:

主动push 和 被pull

分两类数据操作模式:

static

数据量小,时间短,如铃音、通知、音效。数据一次性交付,然后播放。

stream

数据量大,时间长,如音乐等。数据通过共享内存方式传送,以流形式传输播放。**

AudioRecord.cpp

音频录制功能类,client端,服务端才是真正执行录制工作的。

AudioRecord:**AudioRecordThread**

  • 框架层

    MediaPlayerService

    APP层MediaPlayer和MediaRecorder的服务端,播放和录制操作的服务类,向下调用真正播放和操作的执行类与功能类。

    MediaRecoderClient

    与客户端MediaRecorde**r对应的服务端的助理类,多实例,一一对应关系。StageFrightRecoder

    直接的录制器,录制操作的执行类。 Client

    与客户端MediaPlaye**r对应的服务端的助理类,多实例,一一对应关系。** 各种播放器类**

    类型有:NU_PLAYER, PV_PLAYER等

AudioSystem.cpp

包装类,隔离AudioTrack/AudioRecord类 <==> AudioPolicyService、AudioFlinger。

因此向上提供统一接口,隔离后者策略类、音频服务类的变化。

看其实现,就是中转调用,转给AudioPolicyService或AudioFlinger。

AudioService

AudioFlinger

AudioFlinger(下面简称AF)是整个音频系统的核心与难点。作为Android系统中的音频中枢,它同时也是一个系统服务,启到承上(为上层提供访问接口)启下(通过HAL来管理音频设备)的作用。AudioFlinger向下访问AudioHardware,实现输出音频数据,控制音频参数。

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

AudioFlinger-->PlaybackThread

负责回放音频数据工作的线程类

AudioFlinger-->MixerThread

负责混音工作的线程类 AudioFlinger-->MixerThread-->AudioMixer

负责混音具体事务的功能类

@ AudioFlinger的创建与初始化

创建见书,

TODO: 初始化

@ 对音频设备的管理

AudioPolicyManagerBase 构造函数中,AudioPolicyManagerBase->loadAudioPolicyConfig,对 audio_policy.conf加载、解析, 找出可用的音频设备 -> AudioFlinger加载模块

AudioPolicyService

AudioPolicyService是策略的制定者,比如什么时候打开音频接口设备、某种Stream类型的音频对应什么设备等等。而AudioFlinger则是策略的执行者,例如具体如何与音频设备通信,如何维护现有系统中的音频设备,以及多个音频流的混音如何处理等等都得由它来完成。AudioPolicyService根据用户配置来指导AudioFlinger加载设备接口,起到路由功能。

audio_policy.conf

不同的Android产品在音频(设备)的设计上通常是存在差异的,而这些差异可以同过Audio的配置文件audio_policy.conf来获得。在Android系统中音频配置文件存放路径有两处,存放地址可以从AudioPolicyManagerBase.cpp文件中知道:

#define AUDIO_POLICY_VENDOR_CONFIG_FILE  "/vendor/etc/audio_policy.conf"
 #define AUDIO_POLICY_CONFIG_FILE         "/system/etc/audio_policy.conf"                

  在AudioPolicyManager.cpp文件中可以知道系统会首先加载vendor/etc目录下的configure文件,再加载system/etc目录下的configure文件。若这两者加载都发生错误的话,系统会加载default配置文件,并命名为primary module,从这可以看出,音频系统中一定必须存在的module就是primary了。

TODO: 这些个配置,什么来由,在代码中如何加载,处理,生成对应的audio_hw_device_t,上层如何来用这些信息,干嘛用。

在HAL层,一个音频设备,用一个 audio_hw_device_t 结构体来表示,而它支持的输出类型,在audio_policy.conf被指明。

当上层要打开一个OUT通道时,会寻找匹配的类型,进行打开。

audio_effects.conf

音效配置文件,最终机器,一般存放在:

/system/etc/audio_effects.conf

/vendor/etc/audio_effects.conf

struct default_audio_policy {
struct audio_policy policy;

struct audio_policy_service_ops *aps_ops;
void *service;
};
           

AudioPolicyManagerBase

是什么,干什么用?

初始化过程: AudioPolicyService构造中: rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,

&mpAudioPolicy); -> .... -> AudioPolicyManagerBase

@ Audio策略类的路由功能?

何谓路由功能,路由过程是怎样的?

audio.h中的输出设备类型是怎么回事? 与audio_policy.conf有什么关系?

policy类是如何使用这些定义及配置的?

最终如何选择到合适的输出设备,即对应audio_deivce类,找到对应输出通道,即audio_deivce ->openOutputStream,具体打开哪一个?

Android上层定义的声音流类型,又是如何对应下来的?

据说上面调用下来,辗转libaudiopolicy.so 后,到AudioPolicyManagerBase.getDevicesForStream()->**.getDeviceForStrategy(), 里面正是AudioSystem.java/AudioSystemLegacy.h中定义的声音流类型与audio.h中定义的音频设备类型转换过程。