本文共 54427 字,大约阅读时间需要 181 分钟。
在上面的框架图中,我们可以看到AudioFlinger(下面简称AF)是整个音频系统的核心与难点。作为系统中的音频中枢,它同时也是一个系统服务,启到承上(为上层提供访问接口)启下(通过HAL来管理音频设备)的作用。只有理解了AudioFlinger,才能以此为基础更好地深入到其它模块,因而我们把它放在前面进行分析。
1.1.1AudioFlinger服务的启动和运行
我们知道,Android中的系统服务分为两类,分别是Java层和Native层的System Services。其中AudioFlinger和SurfaceFlinger一样,都属于后者。Java层服务通常在SystemServer.java中启动,比如后面会看到的AudioService就是这种情况。而Native层服务则通常是各服务方按照自己的特定部署来决定何时启动、如何启动。例如AudioFlinger就是利用一个Linux程序来间接创建的,如下所示:
/*frameworks/av/media/mediaserver/Main_mediaserver.cpp*/
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();
}
这个mediaserver的目录下只有一个文件,它的任务很简单,就是把所有媒体相关的native层服务(包括AudioFlinger,MediaPlayerService,CameraService和AudioPolicyService)启动起来,可以参考其Android.mk:
LOCAL_SRC_FILES:= \
main_mediaserver.cpp
LOCAL_SHARED_LIBRARIES := \
libaudioflinger\
libcameraservice\
libmediaplayerservice\
libutils \
libbinder
…
LOCAL_MODULE:= mediaserver
根据前面的分析,AudioFlinger的源码实现是放在libaudioflinger库中的,因而在编译mediaserver时要引用这个库,其它服务也是一样的做法。编译生成的mediaserver将被烧录到设备的/system/bin/mediaserver路径中,然后由系统启动时的init进程启动,其在Init.rc中的配置是:
service media/system/bin/mediaserver
class main
user media
group audiocamera inetnet_bt net_bt_admin net_bw_acct drmrpc
ioprio rt 4
值得一提的是,这个AudioFlinger::instantiate()并不是AudioFlinger内部的静态类,而是BinderService类的一个实现。包括AudioFlinger、AudioPolicyService等在内的几个服务都继承自这个统一的Binder服务类,比如:
class AudioFlinger :
public BinderService<AudioFlinger>,
publicBnAudioFlinger…
从名称上看,BinderService应该是实现了binder跨进程通信相关的功能,它是一个模板类,其中的函数instantiate将把模板指定的服务创建出来,并添加到ServiceManager中:
/*frameworks/native/include/binder/BinderService.h*/
template<typename SERVICE> …
static status_t publish(bool allowIsolated = false) {
sp<IServiceManager>sm(defaultServiceManager());
returnsm->addService(String16(SERVICE::getServiceName()), newSERVICE(),allowIsolated);
}
static voidinstantiate(){ publish(); } //调用publish
回头看下AudioFlinger的构造函数,发现它只是简单地为内部一些变量做了初始化,除此之外就没有任何代码了:
AudioFlinger::AudioFlinger()
:BnAudioFlinger(),mPrimaryHardwareDev(NULL),
mHardwareStatus(AUDIO_HW_IDLE),// see alsoonFirstRef()
mMasterVolume(1.0f),mMasterVolumeSupportLvl(MVS_NONE), mMasterMute(false),
mNextUniqueId(1),mMode(AUDIO_MODE_INVALID), mBtNrecIsOff(false)
{
}
大家可能会觉得疑惑,那么AudioFlinger在什么情况下会开始执行实际的工作呢?没错,是在onFirstRef()中。BnAudioFlinger是由RefBase层层继承而来的,并且IServiceManager::addService的第二个参数实际上是一个强指针引用(constsp<IBinder>&),因而AudioFlinger具备了强指针被第一次引用时调用onFirstRef的程序逻辑。如果大家不是很清楚这些细节的话,可以参考下本书的强指针章节,这里不再赘述。
void AudioFlinger::onFirstRef()
{
int rc = 0;
Mutex::Autolock_l(mLock);
charval_str[PROPERTY_VALUE_MAX] = { 0 };
if(property_get("ro.audio.flinger_standbytime_ms", val_str, NULL)>=0) {
uint32_t int_val;
if (1 ==sscanf(val_str, "%u", &int_val)) {
mStandbyTimeInNsecs= milliseconds(int_val);
ALOGI("Using%u mSec as standby time.", int_val);
} else {
mStandbyTimeInNsecs= kDefaultStandbyTimeInNsecs;
…
}
}
mMode = AUDIO_MODE_NORMAL;
mMasterVolumeSW =1.0;
mMasterVolume = 1.0;
mHardwareStatus=AUDIO_HW_IDLE;
}
属性ro.audio.flinger_standbytime_ms为用户调整standby时间提供了一个接口,早期版本中这个时间值是固定的。接下来初始化几个重要的内部变量,和构造函数的做法不同的是,这里赋予的都是有效的值了。
从这时开始,AudioFlinger就是一个“有意义”的实体了,因为有人使用到了它。接下来其它进程可以通过ServiceManager来访问,并调用createTrack、openOutput等一系列接口来驱使AudioFlinger执行音频处理操作,我们在后面章节会陆续讲解到。
音频设备的管理
虽然AudioFlinger实体已经成功创建并初始化,但到目前为止它还是一块静态的内存空间,没有涉及到具体的工作。
从职能分布上来讲,AudioPolicyService是策略的制定者,比如什么时候打开音频接口设备、某种Stream类型的音频对应什么设备等等。而AudioFlinger则是策略的执行者,例如具体如何与音频设备通信,如何维护现有系统中的音频设备,以及多个音频流的混音如何处理等等都得由它来完成。
目前Audio系统中支持的音频设备接口(Audio Interface)分为三大类,即:
/*frameworks/av/services/audioflinger/AudioFlinger.cpp*/
static const char * constaudio_interfaces[] = {
AUDIO_HARDWARE_MODULE_ID_PRIMARY,//主音频设备,必须存在
AUDIO_HARDWARE_MODULE_ID_A2DP,//蓝牙A2DP音频
AUDIO_HARDWARE_MODULE_ID_USB,//USB音频,早期的版本不支持
};
每种音频设备接口由一个对应的so库提供支持。那么AudioFlinger怎么会知道当前设备中支持上述的哪些接口,每种接口又支持哪些具体的音频设备呢?这是AudioPolicyService的责任之一,即根据用户配置来指导AudioFlinger加载设备接口。
当(AudioPolicyService中持有的Policy管理者,后面小节有详细介绍)构造时,它会读取厂商关于音频设备的描述文件(audio_policy.conf),然后据此来打开以上三类音频接口(如果存在的话)。这一过程最终会调用loadHwModule@AudioFlinger,如下所示:
/*frameworks/av/services/audioflinger*/
audio_module_handle_tAudioFlinger::loadHwModule(const char *name)/*name就是前面audio_interfaces 数组
成员中的字符串*/
{
if(!settingsAllowed()) {
return 0;
}
Mutex::Autolock_l(mLock);
returnloadHwModule_l(name);
}
这个函数没有做实质性的工作,只是执行了加锁动作,然后接着调用下面的函数:
audio_module_handle_tAudioFlinger::loadHwModule_l(const char *name)
{
/*Step 1. 是否已经添加了这个interface?*/
for (size_t i =0; i <mAudioHwDevs.size(); i++) {
if(strncmp(mAudioHwDevs.valueAt(i)->moduleName(), name, strlen(name)) == 0){
ALOGW("loadHwModule()module %s already loaded", name);
returnmAudioHwDevs.keyAt(i);
}
}
/*Step 2. 加载audiointerface*/
audio_hw_device_t*dev;
int rc=load_audio_interface(name, &dev);
…
/*Step 3. 初始化*/
mHardwareStatus = AUDIO_HW_INIT;
rc = dev->init_check(dev);
mHardwareStatus = AUDIO_HW_IDLE;
…
if ((mMasterVolumeSupportLvl!=MVS_NONE) && (NULL != dev->set_master_volume)) {
AutoMutexlock(mHardwareLock);
mHardwareStatus= AUDIO_HW_SET_MASTER_VOLUME;
dev->set_master_volume(dev,mMasterVolume);
mHardwareStatus= AUDIO_HW_IDLE;
}
/*Step 4. 添加到全局变量中*/
audio_module_handle_t handle = nextUniqueId();
mAudioHwDevs.add(handle,new AudioHwDevice(name, dev));
return handle;
}
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"
当然,因为Android是完全开源的,各开发商可以根据自己的需要来进行相应的修改,比如下面是某手机设备的音频库截图:
图 13‑12 音频库实例
Step3@ loadHwModule_l,进行初始化操作。其中init_check是为了确定这个audiointerface是否已经成功初始化,0是成功,其它值表示失败。接下来如果这个device支持主音量,我们还需要通过set_master_volume进行设置。在每次操作device前,都要先改变mHardwareStatus的状态值,操作结束后将其复原为AUDIO_HW_IDLE(根据源码中的注释,这样做是为了方便dump时正确输出内部状态,这里我们就不去深究了)。
Step4@ loadHwModule_l. 把加载后的设备添加入mAudioHwDevs键值对中,其中key的值是由nextUniqueId生成的,这样做保证了这个audiointerface拥有全局唯一的id号。
完成了audiointerface的模块加载只是万里长征的第一步。因为每一个interface包含的设备通常不止一个,Android系统目前支持的音频设备如下列表所示:
13‑4Android系统支持的音频设备(输出)
Device Name | Description |
AUDIO_DEVICE_OUT_EARPIECE | 听筒 |
AUDIO_DEVICE_OUT_SPEAKER | 喇叭 |
AUDIO_DEVICE_OUT_WIRED_HEADSET | 带话筒的耳机 |
AUDIO_DEVICE_OUT_WIRED_HEADPHONE | 耳机 |
AUDIO_DEVICE_OUT_BLUETOOTH_SCO | SCO 蓝牙 |
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | SCO 蓝牙耳机 |
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT | SCO 车载套件 |
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | A2DP 蓝牙 |
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | A2DP 蓝牙耳机 |
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | A2DP 蓝牙喇叭 |
AUDIO_DEVICE_OUT_AUX_DIGITAL | AUX IN |
AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | 模拟dock headset |
AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | 数字dock headset |
AUDIO_DEVICE_OUT_USB_ACCESSORY | USB配件 |
AUDIO_DEVICE_OUT_USB_DEVICE | USB设备 |
AUDIO_DEVICE_OUT_DEFAULT | 默认设备 |
AUDIO_DEVICE_OUT_ALL | 上述每种设备只占int值一个bit位,这里是指上述设备的集合 |
AUDIO_DEVICE_OUT_ALL_A2DP | 上述设备中与A2DP蓝牙相关的设备集合 |
AUDIO_DEVICE_OUT_ALL_SCO | 上述设备中与SCO蓝牙相关的设备集合 |
AUDIO_DEVICE_OUT_ALL_USB | 上述设备中与USB相关的设备集合 |
大家可能会有疑问:
Ø 这么多的输出设备,那么当我们回放音频流(录音也是类似的情况)时,该选择哪一种呢?
Ø 而且当前系统中audio interface也很可能不止一个,应该如何选择?
显然这些决策工作将由AudioPolicyService来完成,我们会在下一小节做详细阐述。这里先给大家分析下,AudioFlinger是如何打开一个Output通道的(一个audiointerface可能包含若干个output)。
打开音频输出通道(output)在AF中对应的接口是openOutput(),即:
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对象*/
status_t status;
PlaybackThread*thread =NULL;
…
audio_stream_out_t *outStream= NULL;
audio_hw_device_t*outHwDev;
…
/*Step 1. 查找相应的audio interface
outHwDev = findSuitableHwDev_l(module, *pDevices);
…
/*Step 2. 为设备打开一个输出流*/
mHardwareStatus=AUDIO_HW_OUTPUT_OPEN;
status =outHwDev->open_output_stream(outHwDev,id, *pDevices,(audio_output_flags_t)flags,
&config, &outStream);
mHardwareStatus=AUDIO_HW_IDLE;
…
if (status == NO_ERROR&&outStream != NULL) {
/*Step 3.生成AudioStreamOut*/
AudioStreamOut *output = newAudioStreamOut(outHwDev, outStream);
/*Step 4.创建PlaybackThread*/
if((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||(config.format !=AUDIO_FORMAT_PCM_16_BIT) ||
(config.channel_mask!= AUDIO_CHANNEL_OUT_STEREO)) {
thread = new DirectOutputThread(this,output, id, *pDevices);
}else {
thread = new MixerThread(this, output, id,*pDevices);
}
mPlaybackThreads.add(id,thread); //添加播放线程
…
/*Step 5.Primary output情况下的处理*/
if((mPrimaryHardwareDev== NULL) &&flags & AUDIO_OUTPUT_FLAG_PRIMARY)){
ALOGI("Usingmodule %d has the primary audio interface", module);
mPrimaryHardwareDev= outHwDev;
AutoMutexlock(mHardwareLock);
mHardwareStatus =AUDIO_HW_SET_MODE;
outHwDev->set_mode(outHwDev,mMode);
…
float initialVolume = 1.0;
mMasterVolumeSupportLvl= MVS_NONE;
mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME; //测试设备是否支持主音量获取
if ((NULL !=outHwDev->get_master_volume)&&
(NO_ERROR ==outHwDev->get_master_volume (outHwDev, &initialVolume))) {
mMasterVolumeSupportLvl= MVS_FULL;
} else {
mMasterVolumeSupportLvl= MVS_SETONLY;
initialVolume= 1.0;
}
mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME;//测试是否支持主音量设置
if ((NULL ==outHwDev->set_master_volume)||
(NO_ERROR !=outHwDev->set_master_volume (outHwDev, initialVolume))) {
mMasterVolumeSupportLvl= MVS_NONE;
}
for (size_t i = 0; i<mAudioHwDevs.size(); i++) {
audio_hw_device_t*dev = mAudioHwDevs.valueAt(i)->hwDevice();
if ((dev !=mPrimaryHardwareDev) &&
(NULL !=dev->set_master_volume)) {
dev->set_master_volume(dev,initialVolume);
}
}
mHardwareStatus =AUDIO_HW_IDLE;
mMasterVolumeSW =(MVS_NONE == mMasterVolumeSupportLvl)? initialVolume: 1.0;
mMasterVolume = initialVolume;
}
return id;
}
return 0;
}
上面这段代码中,颜色加深的部分是我们接下来分析的重点,主要还是围绕outHwDev这个变量所做的一系列操作,即:
· 查找合适的音频接口设备(findSuitableHwDev_l)
· 创建音频输出流(通过open_output_stream获得一个audio_stream_out_t)
· 利用AudioStreamOut来封装audio_stream_out_t与audio_hw_device_t
· 创建播放线程(PlaybackThread)
· 如果当前设备是主设备,则还需要进行相应的设置,包括模式、主音量等等
显然,outHwDev用于记录一个打开的音频接口设备,它的数据类型是audio_hw_device_t,是由HAL规定的一个音频接口设备所应具有的属性集合,如下所示:
struct audio_hw_device {
structhw_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_thandle,
audio_devices_tdevices,
audio_output_flags_tflags,
structaudio_config *config,
structaudio_stream_out **stream_out);
…}
其中common代表了HAL层所有设备的共有属性;set_master_volume、set_mode、open_output_stream分别为我们设置audio interface的主音量、设置音频模式类型(比如AUDIO_MODE_RINGTONE、AUDIO_MODE_IN_CALL等等)、打开输出数据流提供了接口。
接下来我们分步来阐述。
Step1@ AudioFlinger::openOutput. 在openOutput中,设备outHwDev是通过查找当前系统来得到的,代码如下:
audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(audio_module_handle_tmodule, uint32_tdevices)
{
if (module == 0){
for (size_t i = 0; i< ARRAY_SIZE(audio_interfaces); i++) {
loadHwModule_l(audio_interfaces[i]);
}
} else {
AudioHwDevice*audioHwdevice = mAudioHwDevs.valueFor(module);
if (audioHwdevice !=NULL) {
returnaudioHwdevice->hwDevice();
}
}
// then try tofind amodule supporting the requested device.
for (size_t i = 0;i <mAudioHwDevs.size(); i++) {
audio_hw_device_t *dev= mAudioHwDevs.valueAt(i)->hwDevice();
if((dev->get_supported_devices(dev) & devices) == devices)
return dev;
}
return NULL;
}
变量module值为0的情况,是为了兼容之前的AudioPolicy而特别做的处理。当module等于0时,首先加载所有已知的音频接口设备,然后再根据devices来确定其中符合要求的。入参devices的值实际上来源于“表格 13‑4 Android系统支持的音频设备(输出)”所示的设备。可以看到,enum中每个设备类型都对应一个特定的比特位,因而上述代码段中可以通过“与运算”来找到匹配的设备。
当modules为非0值时,说明AudioPolicy指定了具体的设备id号,这时就通过查找全局的mAudioHwDevs变量来确认是否存在符合要求的设备。
DefaultKeyedVector<audio_module_handle_t,AudioHwDevice*> mAudioHwDevs;
变量mAudioHwDevs是一个Vector,以audio_module_handle_t为key,每一个handle值唯一确定了已经添加的音频设备。那么在什么时候添加设备呢?
一种情况就是前面看到的modules为0时,会load所有潜在设备,另一种情况就是AudioPolicyManagerBase在构造时会预加载所有audio_policy.conf中所描述的output。不管是哪一种情况,最终都会调用loadHwModuleàloadHwModule_l,这个函数我们开头就分析过了。
如果modules为非0,且从mAudioHwDevs中也找不到符合要求的设备,程序并不会就此终结——它会退而求其次,遍历数组中的所有元素寻找支持devices的任何一个audiointerface。
Step2@ AudioFlinger::openOutput,调用open_output_stream打开一个audio_stream_out_t。如果直接讲解这个函数的作用,大家可能觉得很抽象,所以这里我们提供一个具体硬件方案上的实现。原生态代码中就包括了一些具体音频设备的实现,如samsung的tuna,其源码实现如下:
/*device/samsung/tuna/audio/Audio_hw.c*/
static int adev_open_output_stream(…structaudio_stream_out **stream_out)
{
structtuna_audio_device*ladev = (struct tuna_audio_device *)dev;
struct tuna_stream_out *out;
…
*stream_out =NULL;
out =(structtuna_stream_out *)calloc(1, sizeof(struct tuna_stream_out));
…
out->stream.common.set_parameters = out_set_parameters;…
*stream_out=&out->stream;
…
}
我们去掉了其中的大部分代码,只留下核心部分。可以看到,tuna_stream_out类型包含了audio_stream_out,后者就是最后要返回的结果。这种方式在HAL层实现中非常多见,读者应该要熟悉这样的写法。而对于audio_stream_out的操作,无非就是根据入参需要,为它的函数指针做初始化,比如set_parameters的实现就最终指向了out_set_parameters。接下来的实现就涉及linux驱动了,我们这里先不往下分析,后面音量调节小节还会再遇到这个函数。
Step3@ AudioFlinger::openOutput,生成AudioStreamOut对象。这个变量没什么特别的,它把audio_hw_device_t和audio_stream_out_t做为一个整体来封装。
Step4@ AudioFlinger::openOutput. 既然通道已经打开,那么由谁来往通道里放东西呢?这就是PlaybackThread。这里分两种不同的情况:
· DirectOutput
如果不需要混音
· Mixer
需要混音
这两种情况分别对应DirectOutputThread和MixerThread两种线程。我们以后者为例来分析下PlaybackThread的工作模式,也会后面小节打下基础。
图 13‑13 Playback各线程类关系
如上图所示,用于Playback的线程种类不少,它们的基类都是Thread。
AudioFlinger中用于记录Record和Playback线程的有两个全局变量,如下:
DefaultKeyedVector<audio_io_handle_t, sp<PlaybackThread>> mPlaybackThreads;
DefaultKeyedVector<audio_io_handle_t, sp<RecordThread>> mRecordThreads;
在openOutput中,加入mPlaybackThreads的是一个新建的线程类实例,比如MixerThread。它的构造函数如下:
AudioFlinger::MixerThread::MixerThread(…):PlaybackThread(audioFlinger,output, id, device, type),…
{ …
mAudioMixer = newAudioMixer(mNormalFrameCount,mSampleRate);
if (mChannelCount== 1) {
ALOGE("Invalidaudio hardware channel count");
}
mOutputSink =newAudioStreamOutSink(output->stream);
…
if(initFastMixer) {
…
} else {
mFastMixer = NULL;
}
…
}
首先生成一个AudioMixer对象,这是混音处理的关键,我们会在后面有详细介绍。然后检查声道数量,在Mixer情况下肯定不止一个声道。接着创建一个NBAIO(Non-blockingaudio I/O interface) sink(即AudioStreamOutSink),并进行negotiate。最后根据配置(initFastMixer)来判断是否使用fast mixer。
可以想象一下,一个放音线程的任务就是不断处理上层的数据请求,然后将其传递到下一层,最终写入硬件设备。但是在上面这个函数中,似乎并没有看到程序去启动一个新线程,也没有看到进入线程循环的地方,或者去调用其它可能引起线程创建的函数。那么究竟在什么情况下MixerThread才会真正进入线程循环呢?
不知大家有没有注意到之前mPlaybackThreads的定义,我们再次列出如下:
DefaultKeyedVector<audio_io_handle_t, sp<PlaybackThread>> mPlaybackThreads;
它实际上是由audio_io_handle_t和PlaybackThread强指针所组成的键值对。同时也可以判断出,PlaybackThread类的祖先中一定会有RefBase。具体来说,就是它的父类Thread继承自RefBase:
/*frameworks/native/include/utils/Thread.h*/
class Thread : virtual publicRefBase
{…
根据强指针的特性,目标对象在第一次被引用时是会调用onFirstRef的,这点在前面小节分析AudioFlinger时我们也见过。这个函数实现如下:
void AudioFlinger::PlaybackThread::onFirstRef()
{
run(mName,ANDROID_PRIORITY_URGENT_AUDIO);
}
很简单,只是调用了run方法,从而启动一个新线程并间接调用threadLoop,不断地处理Mix业务。这样我们就明白了一个PlaybackThread是如何进入线程循环的了,至于循环中需要做些什么,留在下一小节做详细介绍。
Step5@ AudioFlinger::openOutput,到目前为止,我们已经成功的建立起一个音频通道,就等着AudioTrack往里丢数据了。不过假如当前的output是“primary”的,则还有一些额外的工作要做。程序接着会对此音频设备设置主音量,前提是mMasterVolumeSupportLvl不为MVS_NONE(表示既不支持主音量的设置和获取。另外MVS_SETONLY表示只支持设置不能获取,MVS_FULL表示同时支持设置和获取)。
“测试设备是否支持主音量设置/获取”部分的代码很简单,我们就不详细说明了。不过要注意的是,当确定了主音量后,需要主动为系统当前已经存在的音频设置主音量(也就是openOutput最后的for循环部分)。这和loadHwModule_l中的设置主音量并不矛盾,试想一下主音量在什么时候被设置是不确定的,因而一旦设定后就先将系统已有的设备先做主音量设置,而后加的设备则由loadHwModule_l来完成。
我们来整理下这个小节所阐述的内容。
· 当AudioPolicyManagerBase构造时,它会根据用户提供的audio_policy.conf来分析系统中有哪些audio interface(primary,a2dp以及usb),然后通过AudioFlinger::loadHwModule加载各audio interface对应的库文件,并依次打开其中的output(openOutput)和input(openInput)
· 我们详细分析了openOutput所做的工作,包括打开一个audio_stream_out_t通道,生成AudioStreamOut对象,以及新建PlaybackThread等等。此时“万事俱备,只欠东风”,只要AudioTrack不断和AudioFlinger传递数据,整个音频回放就开始了。当然,这其中还涉及很多状态的管理、路由切换、以及数据的跨进程交互等等,这些都是我们后面内容所要解决的。
当一个PlaybackThread进入主循环后(threadLoop),音频事务就正式开启了。仔细观察的话,我们会发现这个循环中会不断地调用以“threadLoop_”开头的若干接口,比如threadLoop_mix、threadLoop_sleepTime、threadLoop_standby等等。以这样的前缀开头,是因为这些函数都是在threadLoop这个主体里被调用的,可以说代表了这个PlaybackThread所需要完成的各个操作步骤。
从上一小节可以了解到,当程序执行到PlaybackThread::onFirstRef时会去真正启动一个线程承载运行threadLoop,接下来我们具体看下这个循环体的处理流程。
bool AudioFlinger::PlaybackThread::threadLoop()
{ …
while(!exitPending())/*Step1.*/
{ …
processConfigEvents();/*Step 2. */
{/*把这段代码框起来的目的是限制自动锁变量_l的生命期,
从而灵活地实现了自动锁的控制范围*/
Mutex::Autolock _l(mLock);
…
/*Step 3. Standby判断*/
if(CC_UNLIKELY((!mActiveTracks.size() && systemTime() >standbyTime)|| mSuspended> 0)){
if (!mStandby){
threadLoop_standby();//调用设备的
mStandby =true;
mBytesWritten= 0;
}
…
}
/*Step 4.*/
mMixerStatus =prepareTracks_l(&tracksToRemove);
…
}
/*Step5.*/
if(CC_LIKELY(mMixerStatus == MIXER_TRACKS_READY)) {
threadLoop_mix();
}else {
threadLoop_sleepTime();
}
…
/*Step 6.*/
if(sleepTime == 0) {
threadLoop_write();//不需要休眠,有数据要写
…
mStandby = false;
}else {
usleep(sleepTime);//进入休眠,时间长短是sleepTime
}
/*Step 7.*/
threadLoop_removeTracks(tracksToRemove);//移除相关Track
tracksToRemove.clear();…
}//while (!exitPending())结束
…
releaseWakeLock();
return false;
}
Step1@ PlaybackThread::threadLoop, 循环的条件是!exitPending()为true。这个函数属于Thread类,它主要通过判断内部变量mExitPending的值来得出是否要结束线程。变量mExitPending在Thread初始化时为fasle,如果后面有人通过requestExit()、requestExitAndWait等等来请求退出,这个值就会改变,从而使得PlaybackThread结束循环。
Step2@ PlaybackThread::threadLoop, 处理config事件。当有配置改变的事件发生时,可以通过sendConfigEvent来通知PlaybackThread。这个函数将把事件添加到mConfigEvents全局变量中,以供processConfigEvents进行处理。配置事件包括如下几种:
enum io_config_event {
OUTPUT_OPENED,//Output打开
OUTPUT_CLOSED,//Output关闭
OUTPUT_CONFIG_CHANGED,//Output配置改变
INPUT_OPENED, //Input打开
INPUT_CLOSED, //Input关闭
INPUT_CONFIG_CHANGED,//Input配置改变
STREAM_CONFIG_CHANGED,//Stream配置改变
NUM_CONFIG_EVENTS
};
Step3@ PlaybackThread::threadLoop,判断当前是否符合Standby的条件,如果是的话就调用threadLoop_standby。这个函数最终还是通过HAL层的接口来实现,如下:
mOutput->stream->common.standby(&mOutput->stream->common);
Step4@ PlaybackThread::threadLoop, 进行数据准备,prepareTracks_l这个函数非常长,我们先用伪代码的形式整理一下它所做的工作,如下所示:
AudioFlinger::PlaybackThread::mixer_stateAudioFlinger::MixerThread::prepareTracks_l(…)
{
/*Step 1. 当前活跃的Track数量*/
size_t count =mActiveTracks.size();
/*Step 2. 循环处理每个Track,这是函数的核心*/
for (size_t i=0; i<count ; i++) {
Track* track =mActiveTracks[i];//伪代码没有考虑强指针
/*Step 3. FastTrack下的处理*/
if(track is FastTrack)
{
//dosomething;
}
/*Step 4. 准备数据,分为以下几个小部来完成*/
audio_track_cblk_t*cblk = track->cblk(); //Step 4.1 数据块准备
/*Step 4.2 要回放音频前,至少需要准备多少帧数据?*/
uint32_t minFrames = 1;//初始化
//具体计算minFrames…
/*Step 4.3 如果数据已经准备完毕*/
//调整音量
//其它参数设置
}//for循环结束
/*Step 5. 后续判断*/
//返回结果,指明当前状态是否已经ready
}
现在我们针对上面的步骤来做“填空”。
Step1@ MixerThread::prepareTracks_l,mActiveTracks的数据类型是SortedVector,用于记录当前活跃的Track。它会随着新的AudioTrack的加入而扩大,也会在必要的情况下(AudioTrack工作结束、或者出错等等)remove相应的Track。
Step2&3@MixerThread::prepareTracks_l, 循环的条件就是要逐个处理该PlaybackThread中包含的Track。假如当前是一个FastTrack,我们还要做一些其它准备,这里就暂时不去涉及具体细节了。
Step4@ MixerThread::prepareTracks_l, 这一步是准备工作中最重要的,那就是缓冲数据。在学习代码细节前,我们先来了解数据传输时容易出现的underrun情况。
什么是BufferUnderrun呢?
当两个设备或进程间形成“生产者-消费者”关系时,如果生产的速度不及消费者消耗的速度,就会出现Underrun。以音频回放为例,此时用户听到的声音就可能是断断续续的,或者是重复播放当前buffer中的数据(取决于具体的实现)。
如何避免这种异常的发生?这也是Step4所要解决的问题,以下分为几个小步骤来看AudioFlinger是如何做到的。
Ø Step4.1,取得数据块
audio_track_cblk_t*cblk =track->cblk();
关于audio_track_cblk_t的更多描述,可以参见后面数据流小节。
Ø Step4.2 计算正确回放音频所需的最少帧数,初始值为1。
uint32_tminFrames = 1;
if((track->sharedBuffer() == 0) && !track->isStopped()&&!track->isPausing() &&
(mMixerStatusIgnoringFastTracks== MIXER_TRACKS_READY)) {
if(t->sampleRate() == (int)mSampleRate) {
minFrames =mNormalFrameCount;
} else {
minFrames =(mNormalFrameCount * t->sampleRate()) / mSampleRate + 1 +1;
minFrames+=mAudioMixer->getUnreleasedFrames(track->name());
ALOG_ASSERT(minFrames<= cblk->frameCount);
}
}
当track->sharedBuffer()为0时,说明AudioTrack不是STATIC模式的,否则数据就是一次性传送的,可以参见AudioTrack小节的描述。全局变量mSampleRate 是通过mOutput->stream->common.get_sample_rate获得的,它是由HAL提供的,代表的是设备的Sampling rate。
如果两者一致的话,就采用mNormalFrameCount,这个值在readOutputParameters函数中进行初始化。如果两者不一致的话,就要预留多余的量做rounding(+1)和interpolation(+1)。另外,还需要考虑未释放的空间大小,也就是getUnreleasedFrames得到的。得出的minFrames必需小于数据块的总大小,因而最后有个ASSERT。通常情况下frameCount分配的是一个buffer的两倍,可以参见AudioTrack小节的例子。
Ø Step4.3 数据是否准备就绪了?
上一步我们计算出了数据的最小帧值,即minFrames,接下来就该判断目前的情况是否符合这一指标了,代码如下所示:
if ((track->framesReady() >=minFrames) &&track->isReady() &&!track->isPaused()&&!track->isTerminated())
{//数据准备就绪,并处于ready状态
mixedTracks++; //需要mix的Track数量增加1
…
/*计算音量值*/
uint32_t vl, vr,va; //三个变量分别表示左、右声道、Aux level音量
if(track->isMuted() ||track->isPausing()||mStreamTypes[track->streamType()].mute) {
vl = vr = va =0; //当静音时,变量直接赋0
if (track->isPausing()) {
track->setPaused();
}
} else{
/*这里获得的是针对每个stream类型设置的音量值,也就是后面“音量调节”小节里最
后执行到的地方,在这里就起到作用了*/
float typeVolume =mStreamTypes[track->streamType()].volume;
float v =masterVolume * typeVolume; //主音量和类型音量的乘积
uint32_t vlr = cblk->getVolumeLR(); //这里得到的vlr必须经过验证是否在合理范围内
vl = vlr &0xFFFF; //vlr的高低位分别表示vr和vl
vr = vlr>> 16;
if (vl >MAX_GAIN_INT) { //对vl进行合理值判断
ALOGV("Trackleft volume out of range: %04X", vl);
vl =MAX_GAIN_INT;
}
if (vr >MAX_GAIN_INT) {//对vr进行合理值判断
ALOGV("Trackright volume out of range: %04X", vr);
vr =MAX_GAIN_INT;
}
// now applythe master volume and stream type volume
vl =(uint32_t)(v * vl) << 12;
vr =(uint32_t)(v * vr) << 12;
uint16_tsendLevel = cblk->getSendLevel_U4_12();
// send levelcomes from shared memory and so may be corrupt
if (sendLevel> MAX_GAIN_INT) {
ALOGV("Track send level out of range:%04X",sendLevel);
sendLevel= MAX_GAIN_INT;
}
va =(uint32_t)(v * sendLevel);
} …
mAudioMixer->setParameter(name,param, AudioMixer::VOLUME0, (void*)vl);
mAudioMixer->setParameter(name,param, AudioMixer::VOLUME1, (void*)vr);
mAudioMixer->setParameter(name,param, AudioMixer::AUXLEVEL, (void*)va);
…
}else {//数据未准备就绪,略过。。。
对于音量的设置还有很多细节,大家有兴趣的可以深入研究下。在得到vl、vr和va的值后,还需要把它们应用到AudioMixer中去,不过在prepareTracks_l中还只是调用mAudioMixer->setParameter设置了这些参数,真正的实现是在threadLoop_mix中,我们后面会讲到这个函数。
Step5@ MixerThread::prepareTracks_l, 通过对每个Track执行上述的处理后,最后要返回一个结果,这通常取决于:
①是否有activetrack
②active track的数据是否已经准备就绪
返回的最终值将影响到threadLoop的下一步操作。
完成了prepareTracks_l的分析,我们再回到前面的threadLoop。
Step5@ PlaybackThread::threadLoop, 如果上一步的数据准备工作已经完成(即返回值是MIXER_TRACKS_READY),就开始进行真正的混音操作,即threadLoop_mix,否则会休眠一定的时间——如此循环往复直到退出循环体。
void AudioFlinger::MixerThread::threadLoop_mix()
{
int64_t pts;
…
mAudioMixer->process(pts);
…
}
这样就进入AudioMixer的处理了,我们放在下一小节做统一分析。
假如数据还没有准备就绪,那么AudioFlinger将调用threadLoop_sleepTime来计算需要休眠多长时间(变量sleepTime),并在threadLoop主循环的末尾(在remove track之前)执行usleep进入休眠。
Step6@ PlaybackThread::threadLoop, 将数据写到HAL中,从而逐步写入到硬件设备中。
voidAudioFlinger::PlaybackThread::threadLoop_write()
{
mLastWriteTime=systemTime();
mInWrite = true;
int bytesWritten;
if (mNormalSink != 0) {
…
ssize_t framesWritten= mNormalSink->write(mMixBuffer, count);
…
} else {
bytesWritten =(int)mOutput->stream->write(mOutput->stream,mMixBuffer,mixBufferSize);
}
if (bytesWritten >0)mBytesWritten += mixBufferSize;
mNumWrites++;
mInWrite = false;
}
分为两种情况:
Ø 如果是采用了NBAIO(Non-blockingAudioI/O),即mNormalSink不为空,则通过它写入HAL
Ø 否则使用普通的AudioStreamOut(即mOutput变量)将数据输出
Step7@ PlaybackThread::threadLoop, 移除tracksToRemove中指示的Tracks。是否移除一个Track是在prepareTracks_l中判断中,可以概括为以下几种情况:
Ø 对于Fast Track,如果它的状态(mState)是STOPPING_2、PAUSED、TERMINATED、STOPPED、FLUSHED,或者状态是ACTIVE但underrun的次数超过限额(mRetryCount),则会被加入tracksToRemove列表中
Ø 当前的Track数据未准备就绪的情况下,且是STATICTRACK或者已经停止/暂停,也会被加入tracksToRemove列表中
在tracksToRemove列表中的Track,与其相关的output将收到stop请求(由AudioSystem::stopOutput发起)。
关于AudioFlinger中与AudioTrack、AudioPolicyService有交互的部分,我们还将在后续小节进行阐述。
每一个MixerThread都有一个唯一对应的AudioMixer(在MixerThread中用mAudioMixer表示),它的作用如其名所表示的,就是为了完成音频的混音操作。
|
图 13‑14 MixerThread示意图
如上图,对外开放的接口主要涉及到Parameter(比如setParameter)、Resampler(比如setResampler)、Volume(比如adjustVolumeRamp)、Buffer(比如setBufferProvider)及Track(比如getTrackName)五个部分。
在内部的实现中,MixerThread的核心是一个mState变量(state_t类型),所有的混音工作都会在这个变量中体现出来——特别是其中的tracks数组,如下所示:
struct state_t {
uint32_t enabledTracks;
uint32_t needsChanged;
size_t frameCount;
void (*hook)(state_t* state, int64_tpts); // one of process__*, neverNULL
int32_t *outputTemp;
int32_t *resampleTemp;
int32_t reserved[2];
track_t tracks[MAX_NUM_TRACKS];__attribute__((aligned(32)));
};
MAX_NUM_TRACKS=32,也就是说最多支持32路同时混音,这对于大部分情况肯定是足够了。数据类型track_t是对每个Track的描述,可想而知类似Parameter这种设置,最终影响的就是Track的属性。
struct track_t {
…
union {
int16_t volume[MAX_NUM_CHANNELS];
int32_t volumeRL;
};//音量相关的属性
int32_t prevVolume[MAX_NUM_CHANNELS];
int32_t volumeInc[MAX_NUM_CHANNELS];
…
uint8_t channelCount; //只能是1或2
uint8_t format; // 总是16
uint16_t enabled; // 实际是布尔类型
audio_channel_mask_t channelMask;
AudioBufferProvider* bufferProvider;
mutableAudioBufferProvider::Buffer buffer; // 8 bytes
hook_t hook;
const void* in; //buffer中的当前位置
AudioResampler* resampler;
uint32_t sampleRate;
int32_t* mainBuffer;
int32_t* auxBuffer;
…
bool setResampler(uint32_t sampleRate,uint32_t devSampleRate);
bool doesResample() const { returnresampler!= NULL; }
void resetResampler() { if (resampler!=NULL) resampler->reset(); }
void adjustVolumeRamp(bool aux);
size_t getUnreleasedFrames() const { returnresampler != NULL ?resampler->getUnreleasedFrames() : 0; };
};
AudioFlinger的threadLoop中,通过不断调用prepareTracks_l来准备数据,而每次prepare实际上都是对所有Track的一次调整。如果属性有变化,就会通过setParamter来告知AudioMixer。
在上一个小节中,threadLoop_mix在内部就是通过AudioMixer来实现混音的,我们这里具体来看下:
void AudioMixer::process(int64_t pts)
{
mState.hook(&mState,pts);
}
“hook”是钩子的意思,为什么取这个名字?一个原因可能是hook指向的实体是变化的,就好像钩子一样,它可以灵活的依附于各种物体之上。从代码层面上看,hook是一个函数指针,它根据当前具体情况会分别指向以下几个函数实现:
process__validate:根据当前具体情况,将hook导向下面的几个实现
process__nop:初始化值
process__OneTrack16BitsStereoNoResampling:只有一路Track,16比特立体声,不重采样
process__genericNoResampling:两路(包含)以上Track,不重采样
process__genericResampling:两路(包含)以上Track,重采样
hook在以下几种情况下会重新赋值
Ø AudioMixer初始化时,hook指向process_nop
Ø 当状态改变或者参数变化时(比如setParameter),调用invalidateState。此时hook指向process__validate
Ø AudioMixer::process是外部调用hook的入口
我们以下面的图来描述一下,大家会看得更清楚些:
图 13‑15 hook的使用
其中process_validate的代码实现如下:
voidAudioMixer::process__validate(state_t* state, int64_t pts)
{ …
intcountActiveTracks = 0;
boolall16BitsStereoNoResample = true;
bool resampling =false;…
uint32_t en=state->enabledTracks;
while (en) {
const int i = 31 -__builtin_clz(en);
en &= ~(1<<i);
countActiveTracks++;//enabled 状态的Track计数
…
}
state->hook = process__nop;
if(countActiveTracks) {
if (resampling) {
…
state->hook = process__genericResampling;
} else {
…
state->hook = process__genericNoResampling;
if(all16BitsStereoNoResample && !volumeRamp) {
if(countActiveTracks == 1) {
state->hook= process__OneTrack16BitsStereoNoResampling;
}
}
}
}
state->hook(state,pts);
…
}
这个函数先通过while循环逐个分析处于enabled状态的Track,统计其内部各状态位(比如NEEDS_AUX__MASK、NEEDS_RESAMPLE__MASK等等)情况,得出countActiveTracks、resampling、volumeRamp及all16BitsStereoNoResample的合理值,最后根据这几个变量判断选择正确的hook实现,并调用这个hook函数执行具体工作。
关于AudioMixer是如何处理音频数据的,我们将在后续音频流小节做统一分析。
在AudioFlinger小节,我们反复强调它只是策略的执行者,而AudioPolicyService则是策略的制定者。这种分离方式有效地降低了整个系统的藕合性,而且为各个模块独立扩展功能提供了保障。
概述
汉语中有很多与策略有关联的俗语,比如“因地制宜”、“具体问题具体分析”;战争中只遵照兵书制定战术的行为也被我们称为是“纸上谈兵”、死读书。这些都告诉我们,了解策略的执行环境是非常重要的,只有清晰地界定出“问题是什么”,才能有的放矢的制定出正确的Policy来解决问题。
Android系统中声音的种类有很多种,具体分类如下所示:
/*AudioManager.java*/
public staticfinal intSTREAM_VOICE_CALL = 0; /* 通话声音*/
public staticfinal intSTREAM_SYSTEM = 1; /* 系统声音*/
public staticfinal int STREAM_RING = 2; /* 电话铃声和短信提示*/
public staticfinal intSTREAM_MUSIC = 3; /* 音乐播放*/
public staticfinal intSTREAM_ALARM = 4; /* 闹铃*/
public static finalintSTREAM_NOTIFICATION = 5; /* 通知声音 */
/*下面几个是隐藏类型,不对上层应用开放*/
public staticfinal intSTREAM_BLUETOOTH_SCO = 6; /*当连接蓝牙时的通话*/
public static finalintSTREAM_SYSTEM_ENFORCED = 7; /* 强制的系统声音,比如有的国家强制要求
摄像头拍照时有声音,以防止偷拍*/
public staticfinal intSTREAM_DTMF = 8; /* DTMF声音 */
public staticfinal intSTREAM_TTS = 9; /* 即texttospeech (TTS) */
针对这么多类型的音频,AudioPolicyService至少面临着如下几个问题:
l 上述类型的声音需要输出到哪些对应的硬件设备
比如一部典型的手机,它既有听筒、耳机接口,还有蓝牙设备。假设默认情况下播放音乐是通过听筒喇叭输出的,那么当用户插入耳机时,这个策略就会改变——从耳机输出,而不再是听筒;又比如在机器插着耳机时,播放音乐不应该从喇叭输出,但是当有来电铃声时,就需要同时从喇叭和耳机输出音频。这些“音频策略”的制定,主导者就是AudioPolicyService
l 声音的路由策略
如果把一个音乐播放实例(比如用MediaPlayer播放一首SD卡中的歌曲)比作源IP,那么上一步中找到的音频播放设备就是目标IP。在TCP/IP体系中,从源IP最终到达目标IP通常需要经过若干个路由器节点,由各路由器根据一定的算法来决定下一个匹配的节点是什么,从而制定出一条最佳的路由路径,如下图所示:
|
图 13‑16 路由器示意图
AudioPolicyService所要解决的问题与路由器类似。因为系统中很可能存在多个audiointerface,每一个audiointerface包含若干output,而每个output又同时支持若干种音频设备,这就意味着从播放实例到终端设备,需要经过audiointerface和output的选择,我们称之为AudioPolicyService的路由功能。
l 每种类型音频的音量调节
不同类型的音频,其音量的可调节范围是不一样的,比如有的是0-15,而有的则是1-20。而且它们的默认值也是有差别的,我们看AudioManager中的定义:
public staticfinal int[] DEFAULT_STREAM_VOLUME = new int[] {
4, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
5, // STREAM_RING
11, // STREAM_MUSIC
6, // STREAM_ALARM
5, // STREAM_NOTIFICATION
7, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
11, // STREAM_DTMF
11 // STREAM_TTS
};
音量的调节部分后面我们有专门的小节来介绍。
为了让大家对AudioPolicyService有个感性的认识,我们以下图来形象地表示它与AudioTrack及AudioFlinger间的关系:
|
图 13‑17 AudioPolicyService与AudioTrack和AudioFlinger的关系
这个图中的元素包括AudioPolicyService、AudioTrack、AudioFlinger、PlaybackThread以及两音频设备(喇叭、耳机)。它们之间的关系如下(特别注意,本例的目的只是说明这些元素的关系,并不代表图中的策略就是Android系统所采用的):
l 一个PlaybackThread的输出对应了一种设备
比如图中有两个设备,就有两个PlaybackThread与之对应。左边的Thread最终混音后输出到喇叭,而右边的则输出到耳机
l 在特定的时间,同一类型音频对应的输出设备是统一的
也就是说,如果当前STREAM_MUSIC对应的是喇叭,那么所有该类型的音频都会输出到喇叭。结合上一点,我们还可以得出一个结论,即同一类型音频对应的PlaybackThread也是一样的
l AudioPolicyService起到了路由的作用
AudioPolicyService在整个选择过程中的作用有点类似于网络路由器,它有权决定某一个AudioTrack所产生的音频流最终会走向哪个设备,就像路由器可以根据一定的算法来决定发送者的包应该传递给哪个节点一样
接下来我们从三个方面来了解AudioPolicyService。
首先,从启动过程来看AudioPolicyService的工作方式。
其次,我们结合上面的关系图详细分析AudioPolicyService是如何完成“路由功能”的。
最后,我们来分析Android系统下默认的“路由策略”是怎样的。
1.1.2AudioPolicyService的启动过程
还记得前面我们在分析AudioFlinger的启动时,曾经看到过AudioPolicyService的影子吗?没错,它和AudioFlinger是驻留在同一个程序中的,如下所示:
/*frameworks/av/media/mediaserver/Main_mediaserver.cpp*/
int main(int argc, char** argv)
{ …
AudioFlinger::instantiate();
…
AudioPolicyService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
因而从理论上来讲,AudioFlinger和AudioPolicyService是可以直接进行函数调用的。不过实际上它们仍然采用标准的Binder进行通信。
AudioPolicyService的启动方式和AudioFlinger也是类似的,我们这里就不赘述,直接来看它的构造函数:
AudioPolicyService::AudioPolicyService()
:BnAudioPolicyService() ,mpAudioPolicyDev(NULL) , mpAudioPolicy(NULL)
{
charvalue[PROPERTY_VALUE_MAX];
const structhw_module_t*module;
int forced_val;
int rc;
…
rc=hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);//Step 1.
…
rc=audio_policy_dev_open(module, &mpAudioPolicyDev);//Step 2.
…
rc=mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,
&mpAudioPolicy);//Step3.
…
rc=mpAudioPolicy->init_check(mpAudioPolicy); //Step 4.
…
//Step 5
property_get("ro.camera.sound.forced",value, "0");
forced_val =strtol(value,NULL, 0);
mpAudioPolicy->set_can_mute_enforced_audible(mpAudioPolicy,!forced_val);
//Step 6.
if(access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
loadPreProcessorConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
} elseif(access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
loadPreProcessorConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
}
}
我们将上述代码段分为6个步骤来讲解。
Step1@ AudioPolicyService::AudioPolicyService.得到Audio Policy的hw_module_t,原生态系统中Policy的实现有两个地方,即Audio_policy.c和Audio_policy_hal.cpp,默认情况下系统选择的是后者(对应的库是libaudiopolicy_legacy)
Step2@AudioPolicyService::AudioPolicyService. 通过上一步得到的hw_module_t打开AudioPolicy设备(这并不是一个传统意义的硬件设备,而是把Policy虚拟成了一种设备。这样子的实现方式让音频硬件厂商在制定自己的音频策略时多了不少灵活性)。原生态代码中audio_policy_dev_open调用的是legacy_ap_dev_open@Audio_policy_hal.cpp,最终生成的Policy Device实现是legacy_ap_device
Step 3@AudioPolicyService::AudioPolicyService. 通过上述的Audio Policy设备来产生一个策略,其对应的具体实现方法是create_legacy_ap@Audio_policy_hal.cpp。这个函数首先生成的一个legacy_audio_policy@Audio_policy_hal.cpp,而mpAudioPolicy对应的则是legacy_audio_policy::policy。除此之外,legacy_audio_policy还包含如下重要成员变量:
struct legacy_audio_policy {
structaudio_policy policy;
void *service;
structaudio_policy_service_ops*aps_ops;
AudioPolicyCompatClient*service_client;
AudioPolicyInterface*apm;
};
其中aps_ops是由AudioPolicyService提供的函数指针(aps_ops),这里面的函数是AudioPolicyService与外界沟通的接口,后面还会经常遇到。
最后一个apm是AudioPolicyManager的简写,AudioPolicyInterface是其基类,apm在原生态实现上是一个AudioPolicyManagerDefault对象,它是在create_legacy_ap中创建的:
static int create_legacy_ap(conststruct audio_policy_device*device,
structaudio_policy_service_ops*aps_ops,
void*service,
structaudio_policy **ap)
{
structlegacy_audio_policy*lap;
…
lap->apm =createAudioPolicyManager(lap->service_client);
…}
函数createAudioPolicyManager默认情况下对应的是AudioPolicyManagerDefault.cpp中的实现,所以它将返回一个AudioPolicyManagerDefault。
是不是觉得Policy相关的类越来越多了?那为什么需要这么多类呢?我们先来看一下它们之间的关系:
图 13‑18 Audio Policy相关类的关系
看起来很复杂,其实概况起来就以下几点:
l AudioPolicyService持有的只是一个类似于接口类的对象,即audio_policy。换句话说,AudioPolicyService是一个“壳”,而audio_policy则是一个符合要求的插件。插件与壳之间的接口是固定不变的,而内部实现却可以根据厂商自己的需求来做
l 我们知道,audio_policy实际上是一个C语言中的struct类型,内部包含了各种函数指针,比如get_output、start_output等等。这些函数指针在初始化时,需要指向具体的函数实现,这就是Audio_policy_hal中的ap_get_output、ap_start_output等等
l 上面提到的各数据类型更多的只是一个“壳”,而真正的实现者是AudioPolicyManager。与此相关的又有三个类:AudioPolicyInterface是它们的基类,AudioPolicyManagerBase实现了一些基础的策略,而AudioPolicyManagerDefault则是最终的实现类。除了AudioPolicyService,后面这两个类也是我们研究Audio Policy的重点
Step 4@AudioPolicyService::AudioPolicyService. 进行初始化检测,原生态的实现直接返回0
Step 5@AudioPolicyService::AudioPolicyService. 判断是否强制执行相机拍照声音
Step 6@ AudioPolicyService::AudioPolicyService.加载音频效果文件(如果存在的话),文件路径如下:
AUDIO_EFFECT_DEFAULT_CONFIG_FILE"/system/etc/audio_effects.conf"
AUDIO_EFFECT_VENDOR_CONFIG_FILE"/vendor/etc/audio_effects.conf"
这样AudioPolicyService就完成了构造,它在ServiceManager中的注册名称为"media.audio_policy"。其中包含的mpAudioPolicy变量是实际的策略制定者,而它也是由HAL层创建的,换句话说是根据硬件厂商自己的“意愿”来执行策略的。
1.1.3AudioPolicyService加载音频设备
在AudioFlinger的“设备管理”小节,我们曾简单提及AudioPolicyService将通过解析配置文件来加载当前系统中的音频设备。具体而言,当AudioPolicyService构造时创建了一个Audio PolicyDevice(mpAudioPolicyDev)并由此打开一个AudioPolicy(mpAudioPolicy)——这个Policy默认情况下的实现是legacy_audio_policy::policy(数据类型audio_policy)。同时legacy_audio_policy还包含了一个AudioPolicyInterface成员变量,它会被初始化为一个AudioPolicyManagerDefault,这些都是我们在前一个小节分析过的。
那么AudioPolicyService在什么时候去加载音频设备呢?
除了后期的动态添加外,另外一个重要途径是通过AudioPolicyManagerDefault的父类,即AudioPolicyManagerBase的构造函数。
AudioPolicyManagerBase::AudioPolicyManagerBase(AudioPolicyClientInterface*clientInterface)…
{ mpClientInterface= clientInterface;
…
if (loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE)!= NO_ERROR){
if (loadAudioPolicyConfig(AUDIO_POLICY_CONFIG_FILE)!= NO_ERROR) {
defaultAudioPolicyConfig();
}
}
for (size_t i =0; i < mHwModules.size();i++) {
mHwModules[i]->mHandle= mpClientInterface->loadHwModule(mHwModules[i]->mName);
if(mHwModules[i]->mHandle == 0) {
continue;
}
for (size_t j = 0; j< mHwModules[i]->mOutputProfiles.size(); j++)
{
const IOProfile*outProfile = mHwModules[i]->mOutputProfiles[j];
if(outProfile->mSupportedDevices & mAttachedOutputDevices) {
AudioOutputDescriptor*outputDesc = new AudioOutputDescriptor(outProfile);
outputDesc->mDevice= (audio_devices_t)(mDefaultOutputDevice &
outProfile->mSupportedDevices);
audio_io_handle_t output =mpClientInterface->openOutput(…);
…
}
不同的Android产品在音频的设计上通常是有差异的,利用配置文件的形式(audio_policy.conf)可以使厂商方便地描述其产品中所包含的音频设备,这个文件的存放路径有两处:
#defineAUDIO_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_policy.conf"
#defineAUDIO_POLICY_CONFIG_FILE"/system/etc/audio_policy.conf"
如果audio_policy.conf不存在的话,则系统将使用默认的配置,具体实现在defaultAudioPolicyConfig中。通过配置文件可以读取如下信息:
· 有哪些audiointerface,比如有没有“primary”、“a2dp”、“usb”
· 每个audiointerface的属性。比如支持的sampling_rates、formats、支持哪些device等等。这些属性是在loadOutput@AudioPolicyManagerBase中读取的,并存储到HwModule->mOutputProfiles中。每一个audiointerface下可能有若干个output和input,而每个output/input下又有若干具体的支持属性,关系如下图所示:
|
图 13‑19 audio_policy.conf中各元素关系图
大家可以自己打开一个audio_policy.conf来具体了解这个文件的格式要求,我们这里就不做深入讲解了。
读取了相关配置后,接下来就要打开这些设备了。AudioPolicyService只是策略制定者,而非执行者,那么是由谁来完成这些具体的工作呢?没错,一定是AudioFlinger。我们可以看到上述函数段中有一个mpClientInterface变量,它是否和AudioFlinger有联系?可以先来分析下这个变量是如何来的。
很明显的mpClientInterface这个变量在AudioPolicyManagerBase构造函数的第一行进行了初始化,再回溯追踪,可以发现它的根源在AudioPolicyService的构造函数中,对应的代码语句如下:
rc=mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,&mpAudioPolicy);
在这个场景下,函数create_audio_policy对应的是create_legacy_ap,并将传入的aps_ops组装到一个AudioPolicyCompatClient对象中,也就是mpClientInterface所指向的那个对象。
换句话说,mpClientInterface->loadHwModule实际上调用的就是aps_ops->loadHwModule,即:
static audio_module_handle_t aps_load_hw_module(void*service,const char *name)
{
sp<IAudioFlinger> af= AudioSystem::get_audio_flinger();
…
returnaf->loadHwModule(name);
}
AudioFlinger终于出现了,同样的情况也适用于mpClientInterface->openOutput,代码如下:
static audio_io_handle_t aps_open_output(…)
{
sp<IAudioFlinger> af= AudioSystem::get_audio_flinger();
…
return af->openOutput((audio_module_handle_t)0,pDevices, pSamplingRate,pFormat, pChannelMask,
pLatencyMs,flags);
}
再回到AudioPolicyManagerBase的构造函数中来,for循环的目标有两个:
Ø 利用loadHwModule来加载从audio_policy.conf中解析出的audio interface,即mHwModules数组中的元素
Ø 利用openOutput来打开各audiointerface中包含的所有Output
关于AudioFlinger中这两个函数的实现,我们在前一个小节已经分析过了,这里终于把它们串起来了。通过AudioPolicyManagerBase,AudioPolicyService解析出了设置中的音频配置,并利用AudioFlinger提供的接口完成了整个音频系统的部署,从而为后面上层应用使用音频设备提供了底层支撑。下一小节我们就看下上层应用具体是如何使用这一框架来播放音频的。
应用实例
对于Android应用开发人员来讲,音频回放最熟悉的莫过于MediaPlayer,而AudioTrack相信用的人相对会少很多。这是因为MediaPlayer提供了更完整的封装和状态控制,使得我们用很少的代码就可以实现一个简单的音乐播放器。而相比MediaPlayer,AudioTrack更为精练、高效,实际上MediaPlayerService的内部实现就是使用了AudioTrack。
AudioTrack被用于PCM音频流的回放,在数据传送上它有两种方式:
Ø 调用write(byte[],int,int)或write(short[],int,int)把音频数据“push”到AudioTrack中。
Ø 与之相对的,当然就是“pull”形式的数据获取,即数据接收方主动索取的过程,如下图所示:
|
图 13‑20 “push”和“pull”两种数据传送模式
除此之外,AudioTrack还同时支持static和streaming两种模式:
§ static
静态的言下之意就是数据一次性交付给接收方。好处是简单高效,只需要进行一次操作就完成了数据的传递;缺点当然也很明显,对于数据量较大的音频回放,显然它是无法胜任的,因而通常只用于播放铃声、系统提醒等对内存小的操作
§ streaming
流模式和网络上播放视频是类似的,即数据是按照一定规律不断地传递给接收方的。理论上它可用于任何音频播放的场景,不过我们一般在以下情况下采用:
Ø 音频文件过大
Ø 音频属性要求高,比如采样率高、深度大的数据
Ø 音频数据是实时产生的,这种情况就只能用流模式了
下面我们选取AudioTrackTest.java为例来讲解,先从使用者的角度来了解下AudioTrack。
/*cts/tests/tests/media/src/android/media/cts*/
publicvoidtestSetStereoVolumeMax() throwsException {
final String TEST_NAME= "testSetStereoVolumeMax";
final int TEST_SR =22050;
final int TEST_CONF =AudioFormat.CHANNEL_CONFIGURATION_STEREO;
final int TEST_FORMAT= AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE =AudioTrack.MODE_STREAM;
final intTEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
// --------initialization --------------
/*Step1.*/
int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR,TEST_CONF, TEST_FORMAT);
/*Step 2.*/
AudioTrack track = newAudioTrack(TEST_STREAM_TYPE,TEST_SR, TEST_CONF,
TEST_FORMAT, 2 * minBuffSize,TEST_MODE);
byte data[] = newbyte[minBuffSize];
// -------- test--------------
track.write(data, OFFSET_DEFAULT,data.length);
track.write(data, OFFSET_DEFAULT,data.length);
track.play();
float maxVol =AudioTrack.getMaxVolume();
assertTrue(TEST_NAME, track.setStereoVolume(maxVol,maxVol) == AudioTrack.SUCCESS);
// -------- tear down--------------
track.release();
}
这个TestCase是测试立体声左右声道最大音量的。顺便提一下,关于自动化测试的更多描述,可以参照本书最后一个篇章。用例中涉及到AudioTrack的常规操作都用高显标示出来了。可见大概是这么几个步骤:
Step1@testSetStereoVolumeMax,getMinBufferSize
字面意思就是获取最小的buffer大小,这个buffer将用于后面AudioTrack的构造函数。它是AudioTrack可以正常使用的一个最低保障,根据官方的建议如果音频文件本身要求较高的话,最好可以采用比MinBufferSize更大的数值。这个函数的实现相对简单:
static public intgetMinBufferSize(int sampleRateInHz, int channelConfig,int audioFormat) {
int channelCount = 0;
switch(channelConfig){
caseAudioFormat.CHANNEL_OUT_MONO:
caseAudioFormat.CHANNEL_CONFIGURATION_MONO:
channelCount = 1;
break;
case AudioFormat.CHANNEL_OUT_STEREO:
caseAudioFormat.CHANNEL_CONFIGURATION_STEREO:
channelCount = 2;
break;
default:
…
}
首先得出声道数,目前最多只支持双声道。
if ((audioFormat !=AudioFormat.ENCODING_PCM_16BIT)
&& (audioFormat !=AudioFormat.ENCODING_PCM_8BIT)) {
returnAudioTrack.ERROR_BAD_VALUE;
}
得出音频采样深度,只支持8bit和16bit两种。
// sample rate, notethese values are subject to change
if ( (sampleRateInHz< 4000) || (sampleRateInHz > 48000) ) {
loge("getMinBufferSize():" + sampleRateInHz +"Hz is nota supported sample rate.");
returnAudioTrack.ERROR_BAD_VALUE;
}
得出采样频率,支持的范围是4k-48kHZ.
int size =native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
…
}
也就是说最小buffer大小取决于采样率、声道数和采样深度三个属性。那么具体是如何计算的呢?我们接着看下native层代码实现:
frameworks/base/core/jni/android_media_AudioTrack.cpp
static jintandroid_media_AudioTrack_get_min_buff_size(JNIEnv*env, jobject thiz,
jintsampleRateInHertz,jint nbChannels, jint audioFormat) {
int frameCount =0;
if(AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
sampleRateInHertz) != NO_ERROR) {
return -1;
}
return frameCount * nbChannels * (audioFormat ==javaAudioTrackFields.PCM16 ? 2 :1);
}
这里又调用了getMinFrameCount,这个函数用于确定至少需要多少Frame才能保证音频正常播放。那么Frame代表了什么意思呢?可以想象一下视频中帧的概念,它代表了某个时间点的一幅图像。这里的Frame也是类似的,它应该是指某个特定时间点时的音频数据量,所以android_media_AudioTrack_get_min_buff_size中最后采用的计算公式就是:
至少需要多少帧*每帧数据量
= frameCount * nbChannels *(audioFormat ==javaAudioTrackFields.PCM16 ? 2 : 1);
公式中frameCount就是需要的帧数,每一帧的数据量又等于:
Channel数*每个Channel数据量= nbChannels * (audioFormat ==javaAudioTrackFields.PCM16 ? 2: 1)
层层返回getMinBufferSize就得到了保障AudioTrack正常工作的最小缓冲区大小了。
Step2@ testSetStereoVolumeMax,创建AudioTrack实例
有了minBuffSize后,我们就可以创建一个AudioTrack对象了。它的构造函数原型是:
public AudioTrack (int streamType,int sampleRateInHz, intchannelConfig, int audioFormat, int bufferSizeInBytes,int mode)
除了倒数第二个参数是计算出来的,其它入参在这个TestCase中都是直接指定的。比如streamType是STREAM_MUSIC,sampleRateInHz是22050等等。如果是编写一个音乐播放器,这些参数自然都是需要通过分析音频文件得出来的,所幸的是Android提供了更加易用的MediaPlayer,使得我们不需要理会这些琐碎细节。
创建AudioTrack的一个重要任务就是和AudioFlinger建立联系,它是由native层的代码来实现的:
publicAudioTrack(intstreamType, int sampleRateInHz, int channelConfig, intaudioFormat,
intbufferSizeInBytes, int mode, int sessionId)
throwsIllegalArgumentException {
…
int initResult = native_setup(new WeakReference<AudioTrack>(this),
mStreamType,mSampleRate, mChannels, mAudioFormat,
mNativeBufferSizeInBytes,mDataLoadMode, session);
…
}
这里调用了native_setup来创建一个本地AudioTrack对象,如下:
/*frameworks/base/core/jni/android_media_AudioTrack.cpp*/
static int android_media_AudioTrack_native_setup(JNIEnv*env, jobject thiz, jobjectweak_this,
jint streamType, jintsampleRateInHertz, jint javaChannelMask,
jint audioFormat, jintbuffSizeInBytes, jint memoryMode, jintArray jSession)
{
…
sp<AudioTrack>lpTrack = newAudioTrack();
…
AudioTrackJniStorage* lpJniStorage=new AudioTrackJniStorage();
创建一个Storage对象,直觉告诉我们这可能是存储音频数据的地方,后面我们再详细分析。
…
if (memoryMode== javaAudioTrackFields.MODE_STREAM) {
lpTrack->set(…
audioCallback, //回调函数
&(lpJniStorage->mCallbackData),//回调数据
0,
0,//shared mem
true,// thread cancall Java
sessionId);//audio session ID
} else if(memoryMode ==javaAudioTrackFields.MODE_STATIC) {
…
lpTrack->set(…
audioCallback, &(lpJniStorage->mCallbackData),0,
lpJniStorage->mMemBase,// shared mem
true,// thread cancall Java
sessionId);//audio session ID
}
…// native_setup结束
函数native_setup首先新建了一个AudioTrack(native)对象,然后进行各种属性的计算,最后调用set函数为AudioTrack设置这些属性——我们只保留两种内存模式(STATIC和STREAM)有差异的地方,便于大家比对理解。对于静态数据,入参中的倒数第三个是lpJniStorage->mMemBase,而STREAM类型时为null(0)。
到目前为止我们还没有看到它与AudioFlinger有交互的地方,看来谜底应该就在这个set函数中了。
status_t AudioTrack::set(…
callback_t cbf, void* user, int notificationFrames,constsp<IMemory>& sharedBuffer,
boolthreadCanCallJava, int sessionId)
{
AutoMutexlock(mLock);
…
if (streamType==AUDIO_STREAM_DEFAULT) {
streamType =AUDIO_STREAM_MUSIC;
}
当不设置streamType时,会被改为默认的AUDIO_STREAM_MUSIC。
…
if (format==AUDIO_FORMAT_DEFAULT) {
format =AUDIO_FORMAT_PCM_16_BIT;
}
if (channelMask== 0) {
channelMask =AUDIO_CHANNEL_OUT_STEREO;
}
采样深度和声道数默认为16bit和立体声
…
if (format==AUDIO_FORMAT_PCM_8_BIT && sharedBuffer != 0) {
ALOGE("8-bit datain shared memory is not supported");
return BAD_VALUE;
}
当sharedBuffer!=0时表明是STATIC模式,也就是说静态数据模式下只支持16bit深度否则就报错直接返回,这点要特别注意。
…
audio_io_handle_t output =AudioSystem::getOutput(streamType,sampleRate, format, channelMask, flags);
通过上述的有效性检查后,AudioTrack接着就可以使用底层的音频服务了。那么是直接调用AudioFlinger服务提供的接口吗?理论上这样子做也是可以的,但Android系统考虑得更细,它在AudioTrack与底层服务间又提供了AudioSystem和AudioService。其中前者同时提供了Java和native两层的实现,而AudioService则只有native层的实现。这样子就降低了使用者(AudioTrack)与底层实现(AudioPolicyService、AudioFlinger等等)间的藕合。换句话说,不同版本的Android音频系统通常改动很大,但只要AudioSystem和AudioService向上的接口不变,那么AudioTrack就不需要做修改。
所以上面的getOutput是由AudioSystem来提供的,可以猜测到其内部只是做了些简单的中转功能,最终还是得由AudioPolicyService/AudioFlinger来实现。这个getOutput寻找最适合当前AudioTrack的audio interface以及Output输出(也就是前面通过openOutput打开的通道),然后AudioTrack会向这个Output申请一个Track。
…
mVolume[LEFT] =1.0f;
mVolume[RIGHT] = 1.0f; /*左右声道的初始值都是最大,另外还有其它成员变量都会在这里赋值,
我们直接省略掉了*/
…
if (cbf != NULL){
mAudioTrackThread =new AudioTrackThread(*this, threadCanCallJava);
mAudioTrackThread->run("AudioTrack",ANDROID_PRIORITY_AUDIO,0 /*stack*/);
}
status_t status = createTrack_l(…sharedBuffer, output);
…
} //AudioTrack::set函数结束
因为cbf是audioCallback不为空,所以这里会启动一个AudioTrack线程。这个线程是用于AudioTrack(native)与AudioTrack(java)间的数据事件通知的,这就为上层应用处理事件提供了一个入口,包括:
EVENT_MORE_DATA = 0, /*请求写入更多数据*/
EVENT_UNDERRUN = 1, /*PCM 缓冲发生了underrun*/
EVENT_LOOP_END = 2, /*到达loop end,如果loopcount不为空的话
将从loop start重新开始回放*/
EVENT_MARKER = 3, /*Playback head在指定的位置,参考setMarkerPosition*/
EVENT_NEW_POS = 4, /*Playback head在一个新的位置,参考setPositionUpdatePeriod*/
EVENT_BUFFER_END = 5 /*Playback head在buffer末尾*/
AudioTrack在AudioFlinger中是以Track来管理的。不过因为它们之间是跨进程的关系,自然需要一个“桥梁”来维护,这个沟通的媒介是IAudioTrack(这有点类似于显示系统中的IWindow)。函数createTrack_l除了为AudioTrack在AudioFlinger中申请一个Track外,还会建立两者间IAudioTrack桥梁:
status_t AudioTrack::createTrack_l(
audio_stream_type_t streamType,uint32_t sampleRate, audio_format_tformat,uint32_t channelMask,
int frameCount,audio_output_flags_t flags, const sp<IMemory>&sharedBuffer,
audio_io_handle_t output)
{
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
获得AudioFlinger服务,还记得上一小节的介绍吗?AudioFlinger在ServiceManager中注册,以“media.audio_flinger”为服务名。
…
IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
…
sp<IAudioTrack> track=audioFlinger->createTrack(getpid(),streamType,sampleRate,format,
channelMask,frameCount, trackFlags, sharedBuffer, output, tid,&mSessionId,&status);
//未完待续…
利用AudioFlinger创建一个IAudioTrack,这是它与AudioTrack之间的跨进程通道。除此之处,AudioFlinger还做了什么?我们深入AudioFlinger分析下。
sp<IAudioTrack>AudioFlinger::createTrack(…
constsp<IMemory>& sharedBuffer, audio_io_handle_t output,
pid_t tid, int*sessionId, status_t *status)
{
sp<PlaybackThread::Track> track;
sp<TrackHandle>trackHandle;
…
PlaybackThread*thread = checkPlaybackThread_l(output);
PlaybackThread*effectThread = NULL;
…
track =thread->createTrack_l(client,streamType, sampleRate, format,
channelMask,frameCount, sharedBuffer, lSessionId, flags, tid, &lStatus);
…
if (lStatus ==NO_ERROR) {
trackHandle = new TrackHandle(track);
} else {
client.clear();
track.clear();
}
returntrackHandle;
}
我们只留下createTrack中最重要的几个步骤,即:
· AudioFlinger::checkPlaybackThread_l
在AudioFlinger::openOutput时,产生了全局唯一的audio_io_handle_t值,这个值是与PlaybackThread相对应的,它作为mPlaybackThreads键值对的key值存在。
当AudioTrack调用createTrack时,需要传入这个全局标记值,checkPlaybackThread_l借此找到匹配的PlaybackThread
· PlaybackThread::createTrack_l
找到匹配的PlaybackThread后,还需要在其内部创建一个PlaybackThread::Track对象(所有Track都由PlaybackThread::mTracks全局变量管理),这些工作由PlaybackThread::createTrack_l完成。我们以下图表示它们之间的关系:
|
图 13‑21 PlaybackThread:Track的管理
· newTrackHandle
TrackHandle实际上就是IAudioTrack,后续AudioTrack将利用这个binder服务来调用getCblk等接口
我们再接着前面AudioTrack::createTrack_l“未完待续”的部分往下看。
…
sp<IMemory>cblk =track->getCblk();
…
mAudioTrack =track;
mCblkMemory = cblk;
mCblk=static_cast<audio_track_cblk_t*>(cblk->pointer());
…
if (sharedBuffer== 0) {
mCblk->buffers =(char*)mCblk + sizeof(audio_track_cblk_t);
} else {
mCblk->buffers =sharedBuffer->pointer();
mCblk->stepUser(mCblk->frameCount);
}
…
return NO_ERROR;
}
事实上当PlaybackThread创建一个PlaybackThread::Track对象时,所需的缓冲区空间就已经分配了。这块空间是可以跨进程共享的,所以AudioTrack可以通过track->getCblk()来获取。看起来很简单的一句话,但其中涉及到很多的细节,我们会在后面的数据流小节做集中分析。
到目前为止,AudioTrack已经可以通过IAudioTrack(即上面代码段中的track变量)来调用AudioFlinger提供的服务了。我们以序列图来总结这一小节:
图 13‑22 AudioTrack的创建流程
创建了AudioTrack后,应用实例通过不断写入(AudioTrack::write)数据来回放音频,这部分代码与音频数据流有关,我们也放在后面小节中分析。
的路由实现
我们在AudioPolicyService小节曾将其比作是一个“路由器”,不过还没有深入解析它是如何完成路由选择的。这部分的功能与使用者——AudioTrack有很大关联,所以我们特别将它的实现原理剖析放在这里,以使读者可以综合起来理解。
路由器功能由如下几个部分组成:
l 与发送方(AudioTrack)的接口
就好像路由器首先要接收到一个IP数据包,它才会去做路由处理,否则AudioPolicyService就成了“无源之水”了
l 与接收方(AudioFlinger)的接口
道理和上面是类似的,AudioPolicyService内部拥有当前系统中所有音频设备的信息,就好比一个路由器也需要预先知道它有多少个节点,才可能把音频数据发送到正确的终点一样
l 路由路径的选择策略
路径选择策略是AudioPolicyService的重点。和传统的路由器不同,它的路径选择算法并不是固定的,而是通过灵活的方式先产生一个策略制定者,然后再由它来生成具体的策略
大家应该还记得前面AudioTrack小节中,我们调用了AudioSystem::getOutput,即:
status_t AudioTrack::set(…)
{…
audio_io_handle_toutput =AudioSystem::getOutput(streamType, sampleRate, format, channelMask,flags);
…}
AudioSystem只是一个中介,其中的实现还是由AudioPolicyService完成的:
audio_io_handle_tAudioSystem::getOutput(…)
{
constsp<IAudioPolicyService>& aps=AudioSystem::get_audio_policy_service();
if (aps == 0)return 0;
returnaps->getOutput(stream, samplingRate, format, channels, flags);
}
显然是直接调用了AudioPolicyService的服务接口:
audio_io_handle_t AudioPolicyService::getOutput(...)
{ …
Mutex::Autolock_l(mLock);
return mpAudioPolicy->get_output(mpAudioPolicy,stream, samplingRate, format,channels, flags);
}
变量mpAudioPolicy便是由策略制定者“生产”出来的Policy。在原生态的实现中它代表的是legacy_audio_policy::policy@Audio_policy_hal.cpp,因而上面实际上调用的是如下函数:
static audio_io_handle_t ap_get_output(struct audio_policy *pol,…)
{
structlegacy_audio_policy*lap = to_lap(pol);
returnlap->apm->getOutput((AudioSystem::stream_type)stream,sampling_rate,(int) format, channels,
(AudioSystem::output_flags)flags);
}
也就是说,前面的apm->getOutput的接口实现最终是落在getOutput @ AudioPolicyManagerBase(AudioPolicyManagerDefault继承自AudioPolicyManagerBase,而后者又继承自AudioPolicyInterface)。
我们先来看下AudioPolicyManagerBase的getOutput实现。
/*hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp*/
audio_io_handle_t AudioPolicyManagerBase::(AudioSystem::stream_typestream,
uint32_tsamplingRate, uint32_t format,
uint32_tchannelMask, AudioSystem::output_flags flags)
{
audio_io_handle_t output = 0;
uint32_t latency = 0;
/*Step 1. 获取stream类型对应的Strategy*/
routing_strategystrategy= getStrategy((AudioSystem::stream_type)stream);
audio_devices_t device = getDeviceForStrategy(strategy, false/*fromCache*/);
…
/*Step 2. 应用策略,判断哪些Output符合用户传入的Stream类型*/
SortedVector<audio_io_handle_t>outputs= getOutputsForDevice(device);
/*Step 3. 选择一个最适合的Output*/
output=selectOutput(outputs, flags);
return output;
}
我们将这个函数分为三个步骤。
Step1@AudioPolicyManagerBase::getOutput.每种Stream类型都有对应的strategy,比如AudioSystem::TTS和AudioSystem::MUSIC对应的是STRATEGY_MEDIA,AudioSystem::NOTIFICATION对应的是STRATEGY_SONIFICATION_RESPECTFUL。具体的对应关系如下表所示:
表格 13‑5 Stream类型与Strategy对照表
STREAM_TYPE | STRATEGY |
VOICE_CALL | STRATEGY_PHONE |
BLUETOOTH_SCO | |
RING | STRATEGY_SONIFICATION |
ALARM | |
NOTIFICATION | STRATEGY_SONIFICATION_RESPECTFUL |
DTMF | STRATEGY_DTMF |
SYSTEM | STRATEGY_MEDIA |
TTS | |
MUSIC | |
ENFORCED_AUDIBLE | STRATEGY_ENFORCED_AUDIBLE |
不同的Stream类型有可能会被划归同一个Strategy,比如TTS、MUSIC及SYSTEM类型的音频,它们在路由策略上都遵循STRATEGY_MEDIA。当然我们也可以通过重载getStrategy来按自己的要求划分Strategy。
当找到某Stream类型对应的Strategy后,接下来getDeviceForStrategy进一步为这一Strategy查找最佳匹配的音频设备(以STRATEGY_MEDIA为例):
audio_devices_tAudioPolicyManagerBase::getDeviceForStrategy(routing_strategystrategy, boolfromCache)
{
uint32_t device =0;
…
switch(strategy) {
case STRATEGY_MEDIA: {
uint32_t device2 = 0;
if (mHasA2dp&& (mForceUse[AudioSystem::FOR_MEDIA] !=
AudioSystem::FORCE_NO_BT_A2DP)&& (getA2dpOutput() != 0) &&!mA2dpSuspended) {
device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
if (device2 == 0){
device2 =mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
}
if (device2 == 0){
device2 =mAvailableOutputDevices &
AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
}
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_ACCESSORY;
}
if (device2 == 0) {
device2 =mAvailableOutputDevices & AUDIO_DEVICE_OUT_USB_DEVICE;
}
…
device |= device2;
if (device) break;
device =mDefaultOutputDevice;
if (device == 0) {
ALOGE("getDeviceForStrategy()no device found forSTRATEGY_MEDIA");
}
} break;
…
上面的代码看上去很长,但逻辑比较简单——按照一定的优先级来匹配系统中已经存在的音频设备。这个优先级的设定因Strategy不同而有所差异。在STRATEGY_MEDIA这种情况下,其优先级如下所示:
² 在有蓝牙A2dp的平台上,且设备可以正常打开,没有挂起,当前也没有强制不使用A2dp,那么通过匹配mAvailableOutputDevices来寻找合适的A2dp设备,比如A2dp_headphone、A2dp_Speaker
² 要注意的是,只有在上一步匹配失败(即找不到合适的设备,变量device2为0)的情况下,才会继续执行下一优先级的判断。这里处于第二等级的是wired headphone
² 继续寻找是否有wired headset
² 寻找是否有usb accessory
² 寻找是否有usb device
等等。。。
正常情况下getDeviceForStrategy都能获得符合要求的device。我们再回到前面的getOutput,看下接下来步骤的执行。
Step2@AudioPolicyManagerBase::getOutput
SortedVector<audio_io_handle_t>AudioPolicyManagerBase::getOutputsForDevice(audio_devices_tdevice)
{
SortedVector<audio_io_handle_t>outputs;
for (size_t i =0; i <mOutputs.size(); i++) {
if ((device &mOutputs.valueAt(i)->supportedDevices()) == device) {
outputs.add(mOutputs.keyAt(i));
}
}
return outputs;
}
这个函数用于获得所有支持device设备的Output,并添加到outputs中。Output是AudioFlinger::openOutput得到的结果,AudioPolicyService会把它们存储到mOutputs键值对中。因为每个Output通常都支持若干种音频设备,不同的Output支持的音频设备类型也是不限的,所以系统中很可能存在多个支持device的Output。
Step3@AudioPolicyManagerBase::getOutput. 到目前为止,符合要求的Output可能不止一个,所以要选择一个最适合的。
audio_io_handle_t AudioPolicyManagerBase::selectOutput(constSortedVector<audio_io_handle_t>&outputs,
AudioSystem::output_flags flags)
{
/*Step 1. 处理一些特殊情况*/
if(outputs.size() == 0) {
return 0;
}
if(outputs.size() == 1) {
return outputs[0];
}
先处理一些特殊情况,比如没有任何output存在的情况下只能返回空;同样的如果只有一个output的情况也没得选择,直接返回该output。
/*Step 2. 开始判断择优*/
intmaxCommonFlags = 0;
audio_io_handle_toutputFlags = 0;
audio_io_handle_t outputPrimary = 0;
for (size_t i =0; i <outputs.size(); i++) {
AudioOutputDescriptor*outputDesc = mOutputs.valueFor(outputs[i]);
if(!outputDesc->isDuplicated()) {
int commonFlags =(int)AudioSystem::popCount(outputDesc->mProfile->mFlags& flags);
if (commonFlags > maxCommonFlags) {
outputFlags =outputs[i];
maxCommonFlags= commonFlags;
ALOGV("selectOutput() commonFlags foroutput %d,%04x", outputs[i], commonFlags);
}
if(outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
outputPrimary= outputs[i];
}
}
}
这个循环是整个函数的核心,我们来分析下它的决策准绳是什么。大家只要仔细看下循环中的判断语句,就可以发现它实际上是在寻找最大值maxCommonFlags,所以问题就转化为,什么东西的最大值?
int commonFlags=(int)AudioSystem::popCount(outputDesc->mProfile->mFlags & flags);
上面这句代码用通俗的话来讲,就是计算outputDesc->mProfile->mFlags与入参flags的相似度有多大。可选的Flags如下所示:
AUDIO_OUTPUT_FLAG_NONE = 0x0,
AUDIO_OUTPUT_FLAG_DIRECT = 0x1, //output直接把track导向一个outputstream,没有混音器
AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, //primary output,它是唯一的并且必需存在
AUDIO_OUTPUT_FLAG_FAST = 0x4, //支持fasttracks的output
AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8 //使用deepaudiobuffer的output
音频系统中名为output_flags的数据类型非常多,不过仔细回溯的话,可以发现这个flags是在AudioTrack的set函数中指定的。另外,如果在查找过程中发现primaryoutput,则用outputPrimary表示,这在后面会用到。
/*Step 3. 根据优先级做出选择*/
if (outputFlags != 0) {
return outputFlags;
}
if (outputPrimary!= 0) {
return outputPrimary;
}
returnoutputs[0];
}
优先级的排列很简单,即:
² Flags与要求相似度高的output
² Primaryoutput
² 如果上面两种都找不到,则默认返回第一个output
这样子AudioPolicyService就完成了整个路由路径的选择,AudioTrack则是通过AudioSystem::getOutput间接调用到的这一功能。