Android音频系统之AudioFlinger

1.1 AudioFlinger

在上面的框架图中,我们可以看到AudioFlinger(下面简称AF)是整个音频系统的核心与难点。作为Android系统中的音频中枢,它同时也是一个系统服务,启到承上(为上层提供访问接口)启下(通过HAL来管理音频设备)的作用。只有理解了AudioFlinger,才能以此为基础更好地深入到其它模块,因而我们把它放在前面进行分析。

 

1.1.1 AudioFlinger服务的启动和运行

我们知道,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 audio camera inetnet_bt net_bt_admin net_bw_acct drmrpc

    ioprio rt 4

值得一提的是,这个AudioFlinger::instantiate()并不是AudioFlinger内部的静态类,而是BinderService类的一个实现。包括AudioFlinger、AudioPolicyService等在内的几个服务都继承自这个统一的Binder服务类,比如:

class AudioFlinger :

    public BinderService<AudioFlinger>,

    public BnAudioFlinger…

从名称上看,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()), new SERVICE(),allowIsolated);

    }

    static void instantiate(){ 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执行音频处理操作,我们在后面章节会陆续讲解到。


1.1.1 音频设备的管理

虽然AudioFlinger实体已经成功创建并初始化,但到目前为止它还是一块静态的内存空间,没有涉及到具体的工作。

从职能分布上来讲,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怎么会知道当前设备中支持上述的哪些接口,每种接口又支持哪些具体的音频设备呢?这是AudioPolicyService的责任之一,即根据用户配置来指导AudioFlinger加载设备接口。

当AudioPolicyManagerBase(AudioPolicyService中持有的Policy管理者,后面小节有详细介绍)构造时,它会读取厂商关于音频设备的描述文件(audio_policy.conf),然后据此来打开以上三类音频接口(如果存在的话)。这一过程最终会调用loadHwModule@AudioFlinger,如下所示:

/*frameworks/av/services/audioflinger*/

audio_module_handle_t AudioFlinger::loadHwModule(const char *name)/*name就是前面audio_interfaces 数组

                                                            成员中的字符串*/

{

    if (!settingsAllowed()) {

        return 0;

    }

    Mutex::Autolock _l(mLock);

    returnloadHwModule_l(name);

}

这个函数没有做实质性的工作,只是执行了加锁动作,然后接着调用下面的函数:

audio_module_handle_t AudioFlinger::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. 加载audio interface*/

    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)) {

        AutoMutex lock(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是为了确定这个audio interface是否已经成功初始化,0是成功,其它值表示失败。接下来如果这个device支持主音量,我们还需要通过set_master_volume进行设置。在每次操作device前,都要先改变mHardwareStatus的状态值,操作结束后将其复原为AUDIO_HW_IDLE(根据源码中的注释,这样做是为了方便dump时正确输出内部状态,这里我们就不去深究了)。

Step4@ loadHwModule_l. 把加载后的设备添加入mAudioHwDevs键值对中,其中key的值是由nextUniqueId生成的,这样做保证了这个audiointerface拥有全局唯一的id号。

完成了audiointerface的模块加载只是万里长征的第一步。因为每一个interface包含的设备通常不止一个,Android系统目前支持的音频设备如下列表所示:

表格 13‑4 Android系统支持的音频设备(输出)

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 {

    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,

                             struct audio_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_t devices)

{

    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 to find 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的情况,是为了兼容之前的Audio Policy而特别做的处理。当module等于0时,首先加载所有已知的音频接口设备,然后再根据devices来确定其中符合要求的。入参devices的值实际上来源于“表格 13‑4 Android系统支持的音频设备(输出)”所示的设备。可以看到,enum中每个设备类型都对应一个特定的比特位,因而上述代码段中可以通过“与运算”来找到匹配的设备。

当modules为非0值时,说明Audio Policy指定了具体的设备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的任何一个audio interface。

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)

{

    struct tuna_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 = new AudioMixer(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 public RefBase

{…

根据强指针的特性,目标对象在第一次被引用时是会调用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传递数据,整个音频回放就开始了。当然,这其中还涉及很多状态的管理、路由切换、以及数据的跨进程交互等等,这些都是我们后面内容所要解决的。

1.1.1 PlaybackThread的循环主体

当一个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("Track left volume out of range: %04X", vl);

                    vl =MAX_GAIN_INT;

                }

                if (vr >MAX_GAIN_INT) {//对vr进行合理值判断

                   ALOGV("Track right 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中,从而逐步写入到硬件设备中。

void AudioFlinger::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-blocking AudioI/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有交互的部分,我们还将在后续小节进行阐述。

1.1.1 AudioMixer

每一个MixerThread都有一个唯一对应的AudioMixer(在MixerThread中用mAudioMixer表示),它的作用如其名所表示的,就是为了完成音频的混音操作。


图 13‑14 MixerThread示意图

 

如上图,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__*, never NULL

        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 { return resampler!= NULL; }

        void        resetResampler() { if (resampler !=NULL) resampler->reset(); }

        void        adjustVolumeRamp(bool aux);

        size_t     getUnreleasedFrames() const { return resampler != 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的代码实现如下:

void AudioMixer::process__validate(state_t* state, int64_t pts)

{  …

    int countActiveTracks = 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函数执行具体工作。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值