Android多媒体-音频-重要模块

1.概述
之前了解了安卓整个Audio系统的分层,整体架构是怎么样的,大概在心里也已经有了一些初步的轮廓。现在开始细化,挑选出几个重要的模块,逐个分析,找出其中的联系,并且知道其工作原理,目的就算达到了。

在这几个重要的类中,有提供给apk开发者使用的接口,还有核心的几个Audio系统服务。

当我们要使用Audio的API时,要回想一下第一章提到过的那几个音频参数的具体含义。

2.AudioTrack
AudioTrack是最灵活、高级的音频API,用它可以实现将原始音频数据直接送到硬件的操作。是管理和播放单一音频资源的类,仅仅能播放已经解码的PCM流,用于PCM音频流的回放。

想使用好这个API播放音频数据,可以分为五个步骤:
1、 配置基本参数
(1)StreamType音频流类型:最主要的几种STREAM

        AudioManager.STREAM_MUSIC:用于音乐播放的音频流。    
        AudioManager.STREAM_SYSTEM:用于系统声音的音频流。    
        AudioManager.STREAM_RING:用于电话铃声的音频流。
        AudioManager.STREAM_VOICE_CALL:用于电话通话的音频流。
        AudioManager.STREAM_ALARM:用于警报的音频流。
        AudioManager.STREAM_NOTIFICATION:用于通知的音频流。
        AudioManager.STREAM_BLUETOOTH_SCO:用于连接到蓝牙电话的手机音频流。
        AudioManager.STREAM_SYSTEM_ENFORCED:在某些国家实施的系统声音的音频流。
        AudioManager.STREAM_DTMF:DTMF音调的音频流。
        AudioManager.STREAM_TTS:文本到语音转换(TTS)的音频流。    


(2)MODE模式(static和stream两种)
AudioTrack.MODE_STREAM:
STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到AudioTrack中。

AudioTrack.MODE_STATIC:
STATIC就是数据一次性交付给接收方。

(3)采样率:mSampleRateInHz
采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。 设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)

(4)通道数目:mChannelConfig
声道设置:android支持双声道立体声和单声道。MONO单声道,STEREO立体声.

CHANNEL_OUT_MONO;
CHANNEL_OUT_STEREO。

(5)音频量化位数:mAudioFormat(只支持8bit和16bit两种)
ENCODING_PCM_16BIT;
ENCODING_PCM_8BIT;

采样大小越大,那么信息量越多,音质也越高,现在主流的采样大小都是16bit,在低质量的语音传输的时候8bit足够了。

2、获取最小缓冲区大小
根据采样率,采样精度,单双声道来得到frame的大小。

计算最小缓冲区:
AudioTrack.getMinBufferSize(mSampleRateInHz,mChannelConfig, mAudioFormat);

    注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。

3、创建AudioTrack对象
取到mMinBufferSize后,我们就可以创建一个AudioTrack对象了。它的构造函数原型是:
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode);

构造函数里面的参数我们之前都讲过了,所以创建时根据需求自行指定即可。

4、获取PCM文件,转成DataInputStream
根据存放PCM的路径获取到PCM文件。

    File file = new File(path);
    mDis = new DataInputStream(new FileInputStream(file));

5、开启/停止播放
(1)开始播放
先把音频数据放到AudioTrack获取到的缓冲中。
readCount = mDis.read(MinBufferSize);
先调用AudioTrack.play();
之后就可以写数据了,AudioTrack.write(MinBufferSize, 0, readCount);

(2)停止播放
停止播放音频数据,如果是STREAM模式,会等播放完最后写入buffer的数据才会停止。如果立即停止,要调用pause()方法,然后调用flush方法,会舍弃还没有播放的数据。

(3)释放资源
AudioTrack.release();

3.AudioRecord
看完AudioTrack了,再看AudioRecord的话就会非常简单。
一般情况下录音实现的简单流程如下:(实现不难,不具体说了)
1、创建一个数据流。
2、构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败。
3、初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小。
4、开始录音。
5、从AudioRecord中读取声音数据到初始化buffer,将buffer中数据导入数据流。
6、停止录音。
7、关闭数据流。

4.AudioManager
AudioManager实现了AudioSystem类与JAVA层系统服务AudioService的封装,主要作用为向应用程序层提供Audio系统的控制接口。提供了丰富的API让开发者对应用的音量和铃声模式等进行控制以及访问。主要内容涉及到音频流、声音、蓝牙、扩音器、耳机等等。

常用功能:
1、音量控制
调音量有两个比较重要的接口。

(1)adjustStreamVolume(int streamType, int direction, int flags);
这个方法主要是用于按键调音,音量+/-。按照规定好的步进值和方向调整指定流的音量大小。

streamType:是你要调节的流。比如说,音乐,铃声,闹钟等。

direction:调节的方向,也就是增大/减小,或者保持不变。(ADJUST_LOWER;
ADJUST_RAISE;ADJUST_SAME)。

flags:常用就是是否在调节音量时显示UI。

(2)setStreamVolume(int streamType, int index, int flags);

这函数是可以直接设置特定流的音量值,主要是用在系统设置中进行调节。

这个函数和上一个就一个参数不同,但是很简单。

index代表的就是设置的音量值。

2、静音控制

(1) setMasterMute(boolean mute, int flags);

这个函数的作用是设置全局静音,一直设置到BSP。

参数一个是mute的布尔值,另一个flags之前说过。

(2)setStreamMute(int streamType, boolean state);
设置指定流静音,其内部静音实现就是在FW把该流的音量设置为0。

3、音频模式/状态
AudioMode用于描述整个Audio部分的当前状态。

            MODE_NORMAL:正常状态(默认)。
            MODE_RINGTONE:响铃状态(来电)。
            MODE_IN_CALL:呼叫电话状态(拨出未接通)。
            MODE_IN_COMMUNICATION:电话状态(接通)。

这些状态的改变会影响音频数据在Audio各输出设备中的路由选择。
还有各种铃声状态也都很常用:

            RINGER_MODE_NORMAL:正常模式,可以听到铃声。
            RINGER_MODE_SILENT:静音模式,无铃声,无震动。
            RINGER_MODE_VIBRATE:震动模式,无铃声,有震动。

4、音频焦点
(1)目的:
AudioFocus机制是为解决系统中Audio资源的竞争问题而引入的。

(2)要求:
需要所有参与Audio竞争的竞争者主动去遵循,Audio Focus机制才能更好的工作。

(3)申请焦点:
现在只说安卓8.0以上的接口。
public int requestAudioFocus(@NonNull AudioFocusRequest focusRequest);

可以看到参数只有一个AudioFocusRequest,那么重点肯定就在这里边了,其中一定设置了许多参数,包括音源类型、监听器等。

所以先看这个AudioFocusRequest。

AudioFocusRequest类是一个封装有关音频焦点请求的信息的类。
看个例子更加直观:

         mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                            .setWillPauseWhenDucked(true)
                            .setAcceptsDelayedFocusGain(true)
                                .setOnAudioFocusChangeListener(mListener, mHandler)
                                .setAudioAttributes(mAttribute)
                            .build();
    AUDIOFOCUS_GAIN:当前声音的唯一的音频来源,持续多长时间未知,结束后也不希望恢复另一个音频流。
    AUDIOFOCUS_GAIN_TRANSIENT:应用程序暂时从当前所有者那里获取焦点,音频播放完后恢复以前的音频播放, 表示请求焦点时已经有人在持有焦点
    AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:和上面的一样临时获取焦点,但他可以在拥有焦点期间,另一个应用可以降低音量继续播放,俗称 躲闪,
    AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:也是临时请求焦点,在拥有焦点期间不希望设备播放任何内容。就是在持有焦点期间,在有请求焦点时禁止把焦点分给其他人。

setWillPauseWhenDucked():声明应用程序在音频回避方面的预期行为,也就是说如果我不希望系统自动给我降低音量,而是想自己暂停音频相关的工作,可以调这个函数取消系统的默认行为。这样通过监听音频焦点变化,来自己处理。

setAcceptsDelayedFocusGain():这个是为了能够延迟获取到焦点的必须条件,但是同时也必须要设置AudioManager.OnAudioFocusChangeListener才能得知何时获取到焦点。

setOnAudioFocusChangeListener():设置音频焦点变化监听器。值得一提的是这个方法有个重载的方法,有一个重载方法有两个参数,第二个参数为Handler对象,看到Handler应该明白了,是为了使用它的消息队列来顺序处理这个回调。

setAudioAttributes(): 这个方法是用来描述app的使用情况。这方法需要传入一个AudioAttributes对象,这个对象也是使用Builder模式来构造。

例如使用AudioAttributes.Builder.setUsage()来描述使用这个音频来干什么,我们可以传入一个AudioAttributes.USAGE_MEDIA来表明用这个音频来作为媒体文件来播放,也可以传入一个AudioAttributes.USAGE_ALARM来表明用这个来作为闹铃。

(4)释放焦点:
public int abandonAudioFocus(OnAudioFocusChangeListener l);
参数就是之前说过的音频焦状态变化监听器。

5.AudioService
AudioService这个系统服务包含或者使用了几乎所有与音频有关的内容,AudioService是音频系统在java层的大本营

1、介绍
(1)音频系统在java层中基本上不参与数据流的,AudioService这个系统服务包含或者使用了几乎所有与音频有关的内容,所以说AudioService是音频系统在java层的大本营。

(2)AudioManager拥有AudioService的Bp端,是AudioService在客户端的一个代理,几乎所有客户端对AudioManager进行的请求,最终都会交由AudioService实现。

(3)AudioService的功能实现依赖于AudioSystem类,AudioSystem无法实例化,它是java层到native层的代理,AudioService通过它与AudioPolicyService以及AudioFlinger进行通信。

2、音量管理
(1)AudioService音量管理的核心是VolumeStreamState,它保存了一个流类型所有的音量信息。

(2)VolumeStreamState保存了运行时的音量信息,而音量的生效则是在底层AudioFlinger完成的。

所以进行音量设置需要做两件事情:更新VolumeStreamState存储的音量值,设置音量到Audio底层系统。

6.AudioSystem
AudioSystem在Audio系统中属于一个承上启下的类,在java和native都有它,所以我们在上层调用的很多功能都经过它,再经过它流转到native服务。

1、介绍
(1)AudioSystem是Audio子系统面向framework的接口,这里面有很多一竿子戳到底的函数。同样,Audio子系统内部也往往使用AudioSystem进行通信,比如AF和APM。

(2)AudioService通过对AudioSystem进行函数调用与Audio系统进行通信。

(3)AudioSystem和AudioFlinger以及AudioPolicyService的双向通信机制。

7.AudioPolicyService
我们知道,在 Audio系统中,核心的两块是 AudioPolicy 和 AudioFlinger,其中 AudioPolicy 相当于军师的角色,专门来制定 Audio 播放时的相关的策略及设定相关的参数,而 AudioFlinger 相当于将军,会根据军师的策略来执行。
所以重点都在这两个服务上,那么就先看AudioPolicyService这个负责“指挥“的服务,最好的办法就是看它启动时都分别干了什么。

启动流程分析:
1、AudioServer
(1)main_audioserver.cpp会编译成可执行文件audioserver,在主线程中,主要是监控子进程的状态,而在它的子进程中分别调用 AudioFlinger::instantiate() 和 AudioPolicyService::instantiate(),分别对AudioFlinger 和 AudioPolicyService 进行初始化。

(2)同样会被它初始化的还有语音识别 SoundTriggerHwService::instantiate()和 VR 语音模块 VRAudioServiceNative::instantiate(),不过这些不是我们目前的重点,后续再看。

具体代码如下:
xref: /frameworks/av/media/audioserver/main_audioserver.cpp

int main(int argc __unused, char **argv)
{
    // TODO: update with refined parameters
    limitProcessMemory(
        "audio.maxmem", /* "ro.audio.maxmem", property that defines limit */
        (size_t)512 * (1 << 20), /* SIZE_MAX, upper limit in bytes */
        20 /* upper limit as percentage of physical RAM */);

    signal(SIGPIPE, SIG_IGN);

    bool doLog = (bool) property_get_bool("ro.test_harness", 0);

    pid_t childPid;
    // FIXME The advantage of making the process containing media.log service the parent process of
    // the process that contains the other audio services, is that it allows us to collect more
    // detailed information such as signal numbers, stop and continue, resource usage, etc.
    // But it is also more complex.  Consider replacing this by independent processes, and using
    // binder on death notification instead.
    if (doLog && (childPid = fork()) != 0) {
        // media.log service
        //prctl(PR_SET_NAME, (unsigned long) "media.log", 0, 0, 0);
        // unfortunately ps ignores PR_SET_NAME for the main thread, so use this ugly hack
        strcpy(argv[0], "media.log");
        sp<ProcessState> proc(ProcessState::self());
        MediaLogService::instantiate();
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
           ......
        sp<ProcessState> proc(ProcessState::self());
        sp<IServiceManager> sm = defaultServiceManager();
        ALOGI("ServiceManager: %p", sm.get());
        AudioFlinger::instantiate();
        AudioPolicyService::instantiate();

        // AAudioService should only be used in OC-MR1 and later.
        // And only enable the AAudioService if the system MMAP policy explicitly allows it.
        // This prevents a client from misusing AAudioService when it is not supported.
        aaudio_policy_t mmapPolicy = property_get_int32(AAUDIO_PROP_MMAP_POLICY,
                                                        AAUDIO_POLICY_NEVER);
        if (mmapPolicy == AAUDIO_POLICY_AUTO || mmapPolicy == AAUDIO_POLICY_ALWAYS) {
            AAudioService::instantiate();
        }

        SoundTriggerHwService::instantiate();
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    }

2、AudioPolicyService::instantiate()
首先注册一个名为 media.audio_policy 的服务,在注册服务过程中会首先调用 AudioPolicyService::onFirstRef() 函数,它是我们 AudioPolicySerivce 启动时的核心代码。

3、AudioPolicyService::onFirstRef()
在该代码中,主要工作如下:
(1)创建了三个AudioCommandThread,名字分别为"ApmTone",“ApmAudio”,“ApmOutput”。

(2)实例化 AudioPolicyClient 对象

(3)初始化 AudioPolicyManager ,传参就是 AudioPolicyClient对象。

(4)AudioPolicyEffects 音效初始化。

void AudioPolicyService::onFirstRef()
{
    {
        Mutex::Autolock _l(mLock);

        // start tone playback thread,用于播放tone音,tone是音调的意思
        mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this);
        // start audio commands thread,用于执行audio命令
        mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);
        // start output activity command thread,用于执行audio输出命令
        mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);
        //实例化AudioPolicyClient对象
        mAudioPolicyClient = new AudioPolicyClient(this);
        //实例化AudioPolicyManager对象
        mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
    }
    // load audio processing modules
    //初始化音效相关
    sp<AudioPolicyEffects>audioPolicyEffects = new AudioPolicyEffects();
    {
               mAudioPolicyEffects = audioPolicyEffects;
    }

}


4、在 AudioPolicyManager 初始化过程中
(1)解析Audio 配置文件,audio_policy.xml 和 audio_policy.conf,解析配置文件中当前系统所支持的输出设备mAvailableOutputDevices、输入设备mAvailableInputDevices、默认输出设备mDefaultOutputDevice。

(2)调用 config.setDeafult() 初始化 mHwModules ,配置 Module 的名字为 “primary”,配置默认输出设备为 AUDIO_DEVICE_OUT_SPEAKER,默认输入设备为 AUDIO_DEVICE_IN_BUILTIN_MIC。

(3)通过 getName() 加载模块,调用loadHwModule_l 函数初始化 HwModule 模块,调用 openDevice 初始化 hardware 层的audio配置,hardware 层会返回给上层 Audio 操作方法,及初始化 输入链表 streams_input_cfg_list 和输出流链表 streams_output_cfg_list ,

(4)初始化默认音量解级参数,包括 通话、系统、铃声、闹钟、通知、蓝牙、拨号盘等音量等级。

(5)给每一个输入 和 输出设备,都分配一个线程,及对应的输入 和 输出 stream 流,根据 audio类型调用不同的函数(Playback 、 OFFLOAD 、 DIRECT 类型等。

(6) 最终所有的信息保存在 HwModule[ ] 中,将所有的输出设备保存在 mDeviceForStrategy[ ] 中。

    至此,所有的输入设备和输出设备 都有对应的stream及单独的 thread 。

5、AudioPolicyEffects 音效初始化

解析audio_effects.conf 文件,得到并加载 系统支持的音效库。初始化各个音效对应的参数,将各音效和 对应的输入 和输出流绑定在一起,这样,当上层要初始化使用音效时,就会在对应的threadloop中调用process_l音效处理函数。创建一个AudioFlinger客户端,将 effect 和 AudioFlinger 客户端绑定在一起。

    注:
    声音通路配置,sources 是声音输信端口,sink 是声音输出端口    
    播放:source(MixerPort)->sink(DevicePort)
    录音:source(DevicePort)->sink(MixerPort)

(借用几张图片)
加载完系统定义的所有音频接口,并生成相应的数据对象,如图所示:

打开音频输出后,在AudioFlinger与AudioPolicyService中的表现形式如图:


6、总结
打开音频输出时创建一个audio_stream_out通道,并创建AudioStreamOut对象以及新建PlaybackThread播放线程。

打开音频输入时创建一个audio_stream_in通道,并创建AudioStreamIn对象以及创建RecordThread录音线程。

8.AudioFlinger
AudioFlinger 是音频系统策略的执行者,负责音频流设备的管理及音频流数据的处理传输,所以 AudioFlinger 也被认为是 Android 音频系统的引擎。

最近的版本AudioFlinger这一块变得更加模块化了,把其中的子类提取出为独立的文件,而AudioFlinger.cpp只包含对外提供的服务接口了。另外相比以前,增加更多的功能特性。先来看看这些被提取出来的独立的文件,有个印象。

    AudioResampler.cpp:
        重采样处理类,可进行采样率转换和声道转换;由录制线程 AudioFlinger::RecordThread 直接使用。
    AudioMixer.cpp:
        混音处理类,包括重采样、音量调节、声道转换等,其中的重采样复用了 AudioResampler;由回放线程 AudioFlinger::MixerThread 直接使用。
    Effects.cpp:
        音效处理类。
    Tracks.cpp:
        音频流管理类,可控制音频流的状态,如 start、stop、pause。
    Threads.cpp:
        回放线程和录制线程类;回放线程从 FIFO 读取回放数据并混音处理,然后写数据到输出流设备;录制线程从输入流设备读取录音数据并重采样处理,然后写数据到 FIFO。
    AudioFlinger.cpp:
        AudioFlinger 对外提供的服务接口。

AudioFlinger 对外提供的主要的服务接口如下:

可以归纳出 AudioFlinger 响应的服务请求主要有:
① 获取硬件设备的配置信息
② 音量调节
③ 静音操作
④ 音频模式切换
⑤ 音频参数设置
⑥ 输入输出流设备管理
⑦ 音频流管理

现在,我们介绍了AudioFlinger它的各个子类以及它作为服务自身对外提供的接口,之前在说AudioPolicyService的时候也知道了它们都是在audioserver里启动的,所以我们要看AudioFlinger这个服务时启动时都做了什么。

AudioFlinger::instantiate() 调用如下:
1、注册 AudioFlinger 服务名为 “media.audio_flinger”。

2、调用 AudioFlinger 构造函数。在构造函数中,主要是做一下初始化,初始化了 mDevicesFactoryHal 设备接口 和 mEffectsFactoryHal 音效接口。

3、调用 AudioFlinger::onFirstRef 函数。在其中构造一个 PatchPanel 对象;修改 mode 为 AUDIO_MODE_NORMAL。

AudioFlinger 服务启动后,其他进程可以通过 ServiceManager 来获取其代理对象 IAudioFlinger,通过 IAudioFlinger 可以向 AudioFlinger 发出各种服务请求,从而完成自己的音频业务。

看来它在初始化时做的工作没有AudioPolicyService那么复杂,但是AudioFlinger有着更重要的工作,我们继续来看。

AndioFlinger 作为 Android 的音频系统引擎,重任之一是负责输入输出流设备的管理及音频流数据的处理传输,这是由回放线程(PlaybackThread 及其派生的子类)和录制线程(RecordThread)进行的。

我们简单看看回放线程和录制线程类关系:

    ThreadBase:PlaybackThread 和 RecordThread 的基类。
    RecordThread:录制线程类,由 ThreadBase 派生。
    PlaybackThread:回放线程基类,同由 ThreadBase 派生。
    MixerThread:混音回放线程类,由 PlaybackThread 派生,负责处理标识为 AUDIO_OUTPUT_FLAG_PRIMARY、AUDIO_OUTPUT_FLAG_FAST、AUDIO_OUTPUT_FLAG_DEEP_BUFFER 的音频流,MixerThread 可以把多个音轨的数据混音后再输出。
    DirectOutputThread:直输回放线程类,由 PlaybackThread 派生,负责处理标识为 AUDIO_OUTPUT_FLAG_DIRECT 的音频流,不需要软件混音,直接输出到音频设备即可。
    DuplicatingThread:复制回放线程类,由 MixerThread 派生,负责复制音频流数据到其他输出设备,使用场景如主声卡设备、蓝牙耳机设备、USB 声卡设备同时输出。
    OffloadThread:硬解回放线程类,由 DirectOutputThread 派生,负责处理标识为 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音频流,这种音频流未经软件解码的(一般是 MP3、AAC 等格式的数据),需要输出到硬件解码器,由硬件解码器解码成 PCM 数据。

从 Audio HAL 中,我们通常看到如下 4 种输出流设备,分别对应着不同的播放场景:

    primary_out: 主输出流设备,用于铃声类声音输出,对应着标识为 AUDIO_OUTPUT_FLAG_PRIMARY 的音频流和一个 MixerThread 回放线程实例。
    low_latency: 低延迟输出流设备,用于按键音、游戏背景音等对时延要求高的声音输出,对应着标识为 AUDIO_OUTPUT_FLAG_FAST 的音频流和一个 MixerThread 回放线程实例。
    deep_buffer:音乐音轨输出流设备,用于音乐等对时延要求不高的声音输出,对应着标识为 AUDIO_OUTPUT_FLAG_DEEP_BUFFER 的音频流和一个 MixerThread 回放线程实例。
    compress_offload:硬解输出流设备,用于需要硬件解码的数据输出,对应着标识为 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音频流和一个 OffloadThread 回放线程实例。

其中 primary_out 设备是必须声明支持的,而且系统启动时就已经打开 primary_out 设备并创建好对应的 MixerThread 实例。其他类型的输出流设备并非必须声明支持的,主要是看硬件上有无这个能力。

下图简单描述 AudioTrack、PlaybackThread、输出流设备三者的对应关系:

输出流设备决定了它对应的 PlaybackThread 是什么类型,只有支持了该类型的输出流设备,那么该类型的 PlaybackThread 才有可能被创建。

应用进程与 AudioFlinger 并不在一个进程上,这就需要 AudioFlinger 提供音频流管理功能,并提供一套通讯接口可以让应用进程跨进程控制 AudioFlinger 中的音频流状态。
AudioFlinger 音频流管理由 AudioFlinger::PlaybackThread::Track 实现,Track 与 AudioTrack 是一对一的关系,一个 AudioTrack 创建后,那么 AudioFlinger 会创建一个 Track 与之对应,PlaybackThread 与 AudioTrack/Track 是一对多的关系,一个 PlaybackThread 可以挂着多个 Track。

AudioTrack 创建后,AudioPolicyManager 根据 AudioTrack 的输出标识和流类型,找到对应的输出流设备和 PlaybackThread(如果没有找到的话,则系统会打开对应的输出流设备并新建一个 PlaybackThread),然后创建一个 Track 并挂到这个 PlaybackThread 下面。

PlaybackThread 有两个私有成员向量与此强相关:

    mTracks:该 PlaybackThread 创建的所有 Track 均添加保存到这个向量中。
    mActiveTracks:只有需要播放(设置了 ACTIVE 状态)的 Track 会添加到这个向量中。

PlaybackThread 会从该向量上找到所有设置了 ACTIVE 状态的 Track,把这些 Track 数据混音后写到输出流设备。

音频流控制最常用的三个接口:

    AudioFlinger::PlaybackThread::Track::start:开始播放:把该 Track 置 ACTIVE 状态,然后添加到 mActiveTracks 向量中,最后调用 AudioFlinger::PlaybackThread::broadcast_l() 告知 PlaybackThread 情况有变。
    AudioFlinger::PlaybackThread::Track::stop:停止播放:把该 Track 置 STOPPED 状态,最后调用 AudioFlinger::PlaybackThread::broadcast_l() 告知 PlaybackThread 情况有变。
    AudioFlinger::PlaybackThread::Track::pause:暂停播放:把该 Track 置 PAUSING 状态,最后调用 AudioFlinger::PlaybackThread::broadcast_l() 告知 PlaybackThread 情况有变。
    AudioFlinger::PlaybackThread::threadLoop() 得悉情况有变后,调用 prepareTracks_l() 重新准备音频流和混音器:ACTIVE 状态的 Track 会添加到 mActiveTracks,此外的 Track 会从 mActiveTracks 上移除出来,然后重新准备 AudioMixer。

可见这三个音频流控制接口是非常简单的,主要是设置一下 Track 的状态,然后发个事件通知 PlaybackThread 就行,复杂的处理都在AudioFlinger::PlaybackThread::threadLoop() 中了。

总结:
AudioFlinger里面涉及和包含的东西很多很多,在这里只是冰山一角,没有深入到具体的代码中去看,只是给大家介绍了一下其工作原理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值