<一>Android Audio音频框架

Posted 王二の黄金时代

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了<一>Android Audio音频框架相关的知识,希望对你有一定的参考价值。

目录

1.0 设备驱动

2.0  android hal层

3.0  选择设备的暗箱策略AudioPolicy

4.0  软件层面的混音,AudioFlinger

5.0 完整的对外接口 AudioSystem.cpp

6.0 换壳java形似的对外接口AudioSystem.java

7.0 java层的服务供应AudioService.java

8.0 应用层的一对一服务AudioManager

9.0 为了支持和兼容多音频设备的car, 提供的动态策略。  

10.0 CarAudioService CarAudioManager

11.0 AudioTrack OpenslEs AAudio

Aaudiotrack:

opensl: 

AAudio: 

12.0 oboe 库


总览:个人对于Android 音频模块的整体理解,分析各个子模块存在的意义,领悟设计者架构思想,探讨未来可能的发展趋势。篇幅有限,码字不易,部分子模块的详细分析还为上blog
放一张自制图:

图中有一个小的拼写错误,为AlSA ,非ALSO。
全文涉及到 AudioFlinger  AudioPolicy  Audiosystem  AudioService  AudioManager
以及用于音频流数据传输的 AudioTrack  OpenSLes  AAudio。 部分子篇还为编写,先预留占坑。

1.0 设备驱动

        linux 系统上一个应用要播放一个音频,原理是open一个音频播放设备的设备节点,然后往节点写入数据即可。设备节点由内核里面的驱动提供,往该设备节点写数据就是输入到了驱动中,对应音频设备的驱动,linux 提供的是名为ALSA 的框架,通常对音频来说都是pcm设备,所以里面的驱动也就是pcm设备驱动。属于字符设备,串行。(linux 设备类型:字符设备,块设备,网络设备)   由该pcm设备的驱动,处理获得的pcm数据给到硬件信号达到播放声音的目的。
        驱动要处理的问题:竞争,阻塞,可重入。
         pcm设备当然也是支持多个进程同时使用的,那么内核的pcm驱动就需要处理这个并发的情况   (内部也应该是作了混合音频的处理的,待后续补充详细)
        android 也有相应的工具,tinyalsa.  就是直接通过和linux 内核的alsa框架进行交互,可以不经过hal层。当然这个机制只是为了方便调试的,android并不打算把它给应用层使用,应用层的接口还需要经过android系统的层层包装和管理。
详细参考:
《  1.有待补充blog linux pcm设备驱动  》
《 
1. android tinyalsa 理解_王二の黄金时代的博客-CSDN博客

2.0  android hal层

        既然是基于linux内核,硬件厂商完全可以实现自己的驱动,然后系统启动insmod加载进去,普通的应用程序即可通过驱动的设备节点,来open write/read iocontrol  完成对设备的操作。但是为了厂家并不乐意公开自己的驱动全部,于是安卓整了一套硬件抽象层 hal.( 关于hal,介绍的资料不少,不再赘述)
       一个普通的c++的层序,就已经可以直接调用hal层的接口,打开指定的设备,把pcm数据流写入进行播放了。
2..1 android 直接使用hal库播放pcm demo
但是设备众多,总不能都需要开发者自己选择具体的输出设备,于是android又为我们提供了一个自动确定具体设备的模块:AudioPolicy

3.0  选择设备的暗箱策略AudioPolicy

         AudioPolicy   根据有限的固定的流类型,来确定最终的设备。之所以说它是暗箱操作,应为应用层不会参与也不清楚具体选择那个输出设备。很好地将应用开发者和设备制作者再进行了一次分离,通过这个相对不会有太多变化的流类型,让应用开发者不需要再往下关注不同android设备具体音频设备的组成。 而到底这个流类型的数据是输出给听筒,还是扬声器,还是一起都输出,这就需要设备制作者来暗箱操作了,这个就是AudioPolicyServe的用途。
详细参考:
《3. 待后续补充blog  AudioPolicy 选择设备策略的源码分析》

4.0  软件层面的混音,AudioFlinger

        每一个应用都需要自己去调用hal驱动,这看起来是linux上面的开发模式,移动端的系统总是期望什么功能都有系统的一个统一管理,Android系统音频也就是这么干的,这就是AudioFlinger.   从linux的角度来看, android 系统只有一个进程在播放声音,就是AudioFlinger所在的进程。  
        关于AudioFlinger的角色,从其命名就可以直接看出来,Flinger的原本意思就是将多个汇合成一个的意思,同样命名的还有SurfaceFlinger,所以它的作用就是汇合所有的应用的音频输入,合成一个音频流再输入hal层。故而android应用想要播放生音,就需要通过跨进程的方式将自己的pcm数据传递给AudioFlinger,由AudioFlinger汇合,AudioFlinger顺便在汇合的时候可以根据需要做一个音效处理,然后输出给对应的hal层设备。
        当然,如果我们应用不想这么做,而是想直接自行通过hal层输出数据,也是可以的,只要你能访问到hal层接口,有足够的权限,那就不走AudioFlinger中间商,当然也就没办法享用到AudiFlinger提供的增值服务比如音效。
       要说明的是,输入给AudioFlinger的数据也不全是经过解码的pcm数据,有些特殊的设备它可以直接自己解码特殊格式的音频,这些音频数据就不能被AudioFlinger进行混合,AudioFlinger对这些数据就不会处理,直接交给对应的dsp设备,这算是特殊通道,这样的数据流类型被标记为offload  顾名思义就是包袱,打包的数据,一般用于dsp硬件解码的流数据
详细参考:
《4. 待补充blog AudioFlinger 混音及线程模型分析》
4. Android native层直接使用AudioFlinger播放pcm

5.0 完整的对外接口 AudioSystem.cpp

        到此已经完全具备完整的音频能力了,如果是开发一个c++的程序,完全可以使用AudioPolicyServe 和AudioFlingerServer 的接口,从AudioPolicy获取到合适的输出设备,然后把pcm数据给到AudioFlinger让其帮忙播放。这个AudioSystem正是对内部AudioPolicyServe 和AudioFlingerServer的接口的一个外壳包装,让它门两个看起来成为一个整体,已经具备完整的音频能力,取名System也无可厚非。


6.0 换壳java形似的对外接口AudioSystem.java

        为了给java使用,同样再换了个外壳,AudioSystem.java ,AudioSystem.java的作用就完全是为了通过java ->jni-> cpp了。没什么逻辑,只是个桥梁。

7.0 java层的服务供应AudioService.java

        如何让应用层使用到AudioSystem.java? Android还是不想直接将AudioSystem的接口暴露给应用,毕竟还涉及到权限管理,音量的控制,焦点的控制等等,这些逻辑就都被放在了java sdk层统一处理,所以从java 层面来看,android 将音频相关的控制,都放在了 AudioService.java  可以说这是android 音频java 层的大本营, 由它来唯一和AudioSystem同底层控制交互。(也不完全是唯一和AudioSystem交互的模块,比如AudioManager在列举所有音频设备时,没有经过AudioService.java ,而是直接调用的AudioSystem.java. 但是总的设计上的思想,应该是都要经过AudioService的)
AudioService.java 源码有相关的注释:

The implementation of the audio service for volume, audio focus, device management...

详细参考《7. 待后续补充blog  AudioService 如何同AudioManager AudioSystem关联》

8.0 应用层的一对一服务AudioManager

        为了更加方便应用层的使用,AudioService.java 服务为每一个应用派遣了一个一对一的接待AudioManager 。 每一个应用可以申请到一个只为自己服务的AudioManager对象。 但是AudioManager 的实际业务基本上是去跨进程调用AudioService服务, 应用获得AudioManager对象也是通过getSystemService(Context.AUDIO_SERVICE) 得到。 

9.0 为了支持和兼容多音频设备的car, 提供的动态策略。  

        原来的android只是用于音频固件相对比较少的手机,一般只有 扬声器、听筒、有线耳机和蓝牙耳机,以及虚拟的用于无线投屏远程播放的 REMOTE_SUBMIX。 应用层只需要表明音频流的类型,由AudioPolicy来决定该流类型应该使用具体是扬声器设备还是听筒设备,这个AudioPolicy就提自定义策略的实现,由Android设备制作厂家来定制自己的策略,当然这个策略是在 底层AudioPolicy内部的。  对于音频输出设备比较多的car 而言,动不动就6个到10个的喇叭,光靠几个流类型显得不太够用,并且策略完全在底层的AudioPolicy ,修改也很不灵活,于是从 Android 9 开始,  对AudioPolicy进行了修改, 他定义了一个策略的接口 AudioMix ,可以由外部实现,然后注册进来覆盖原来的传统策略模式,这个外部策略甚至可以更加灵活,一直对外暴露到AudioSystem -> AudioService.java->AudioManager.java .  在java层这个策略的定义为AudioPolicy.java 。
        这样连应用层都可以使用AudioManager.java来定义注册自己的AudioPolicy,这就是动态音频策略。当没有app来注册动态策略的时候,系统默认就使用传统的内置在AudioPolicy的策略。当有应用向系统注册了音频策略的时候,就优先使用注册的动态外部策略。当然并不是所有的应用都能随意注册,需要SystemApi级别, 需要android.Manifest.permission.MODIFY_AUDIO_ROUTING权限。
        于是对于传统的手机,可以默认使用原来的策略,对于汽车,只需要外挂一个系统级别的app,即源码中的 packages/services/Car  然后在里面实现自己的AudioPolicy ,用AudioManager.registerAudioPolicy注入即可。
详细参考《 9. 待后续补充blog  android 动态音频策略的原理》

10.0 CarAudioService CarAudioManager

        这就是对于car 设备,外挂的系统级别app, 在应用层 java 实现了定制的音频策略,比如多音区.
       安卓中很多这种manager+service的结构,类似于AudioManager+AudioService的结构,就像我们在8.0 AudioManager中提到的那样, manager是给应用层一对一专门服务的接待对象,而manager内部实现则是经过统一的跨进程到service中去完成功能。
  详细参考:《10. 待后续补充 blog  android CarAudio》

11.0 AudioTrack OpenslEs AAudio

         以上全部在讨论关于设备的选择策略,音量的控制等等,对于应用如何把pcm数据流交给系统,就需要看AudioTrack了。

Aaudiotrack:

        Track原本的意思,是轨迹,轨道,这个名词在安卓系统中有多处使用。 在AudioFlinger中, 每一路音频输入,都是一个 track.    这个就是AudioFlinger 内部创建的track, 为了让外部应用层可以访问到,于是专门抽象出一个类AudioTrack.cpp (为了方便区分是java还是c++ 故意在对象后面加上后缀名), 专门负责和AudioFlinger内部的track进行流数据的拷贝交互。 AudioTrack.cpp 负责直接和AudioFlinger进行流数据的交互, 它不负责具体使用那个音频设备。  它被暴露给应用层,也就是java层的AudioTrack.java  , app 可以用AudioTrack来输出一个音频流到AudioFlinger. 然后AudioFlinger将其输送到具体的音频输出设备。
native层为了配合媒体播放,利用AudioTrack.cpp 的接口编写了一个TrackPlayer,让他符合plaer播放器的特性。 
11. Android native层使用TrackPlayer播放pcm

opensl: 

        同时为了支持opensl 这套公共api标准, 又基于TrackPlayer  实现了Wilhelm project这个工程,所以android 上使用 opensl 实际上就是通过native层的AudioTrack 来和AudioFlinger交互数据流。  至于opensles 相对于AudioTrack.java的性能提升,其实就是少了一层从java 到 native的拷贝延时,两者最后都会通过AudioTrack.cpp -> AudioFlinger -> hal

AAudio: 

        其实就是Android Audio的缩写,android 在java层提供了AudioTrack.java 这个api,但是在native层却没有相应的独立API, 而对 opensles 的支持只是为了适应一套通用规范的接口,并不能提供一些android 自己特性的音频接口,于是在Android O(8.0版本,API-26 ) 版本中引入的全新 Android C API , 取名AAudio (Android Audio)  其中还支持 mmap通路,内存映射的方式直接通到内核层,以减少拷贝,降低延迟。
详细参考:
        《11. Android native层使用TrackPlayer播放pcm_》
        《11. 有待补充 blog android opensl 源码原理分析》
        《11. 有待补充 blog android AAudio分析》

12.0 oboe 库

        为了简化调用流程,自动选择是使用 opensl 还是AAudio (毕竟在低于8.0的设备上是没有AAudio的支持的),谷歌整合了一个c++ 库,叫做oboe, 目前是以第三方库的方式提供,应用层可以嵌入使用,相比于opensl 确实调用起来简洁多了。

官方源码 https://github.com/google/oboe

Oboe audio library | Android Developers (google.cn)

读源码不容易! 码字更是费时费力!  原创!!!发扬共享精神! 

Android 音频系统:从 AudioTrack 到 AudioFlinger

1. Android 音频框架概述

Android_Audio_Architecture

Audio 是整个 Android 平台非常重要的一个组成部分,负责音频数据的采集和输出、音频流的控制、音频设备的管理、音量调节等,主要包括如下部分:

  • Audio Application Framework:音频应用框架
    • AudioTrack:负责回放数据的输出,属 Android 应用框架 API 类
    • AudioRecord:负责录音数据的采集,属 Android 应用框架 API 类
    • AudioSystem: 负责音频事务的综合管理,属 Android 应用框架 API 类
  • Audio Native Framework:音频本地框架
    • AudioTrack:负责回放数据的输出,属 Android 本地框架 API 类
    • AudioRecord:负责录音数据的采集,属 Android 本地框架 API 类
    • AudioSystem: 负责音频事务的综合管理,属 Android 本地框架 API 类
  • Audio Services:音频服务
    • AudioPolicyService:音频策略的制定者,负责音频设备切换的策略抉择、音量调节策略等
    • AudioFlinger:音频策略的执行者,负责输入输出流设备的管理及音频流数据的处理传输
  • Audio HAL:音频硬件抽象层,负责与音频硬件设备的交互,由 AudioFlinger 直接调用

与 Audio 强相关的有 MultiMedia,MultiMedia 负责音视频的编解码,MultiMedia 将解码后的数据通过 AudioTrack 输出,而 AudioRecord 采集的录音数据交由 MultiMedia 进行编码。

本文分析基于 Android 7.0 - Nougat

//
// 声明:本文由 http://blog.csdn.net/zyuanyun 原创,转载请注明出处,谢谢!
//

2. AudioTrack API 概述

播放声音可以使用 MediaPlayer 和 AudioTrack,两者都提供 Java API 给应用开发者使用。两者的差别在于:MediaPlayer 可以播放多种格式的音源,如 mp3、flac、wma、ogg、wav 等,而 AudioTrack 只能播放解码后的 PCM 数据流。从上面 Android 音频系统架构图来看:MediaPlayer 在 Native 层会创建对应的音频解码器和一个 AudioTrack,解码后的数据交由 AudioTrack 输出。所以 MediaPlayer 的应用场景更广,一般情况下使用它也更方便;只有一些对声音时延要求非常苛刻的应用场景才需要用到 AudioTrack。

2.1. AudioTrack Java API

AudioTrack Java API 两种数据传输模式:

Transfer ModeDescription
MODE_STATIC应用进程将回放数据一次性付给 AudioTrack,适用于数据量小、时延要求高的场景
MODE_STREAM用进程需要持续调用 write() 写数据到 FIFO,写数据时有可能遭遇阻塞(等待 AudioFlinger::PlaybackThread 消费之前的数据),基本适用所有的音频场景


AudioTrack Java API 音频流类型:

Stream TypeDescription
STREAM_VOICE_CALL电话语音
STREAM_SYSTEM系统声音
STREAM_RING铃声声音,如来电铃声、闹钟铃声等
STREAM_MUSIC音乐声音
STREAM_ALARM警告音
STREAM_NOTIFICATION通知音
STREAM_DTMFDTMF 音(拨号盘按键音)


Android 为什么要定义这么多的流类型?这与 Android 的音频管理策略有关,例如:

  • 音频流的音量管理,调节一个类型的音频流音量,不会影响到其他类型的音频流
  • 根据流类型选择合适的输出设备;比如插着有线耳机期间,音乐声(STREAM_MUSIC)只会输出到有线耳机,而铃声(STREAM_RING)会同时输出到有线耳机和外放

这些属于 AudioPolicyService 的内容,本文不展开分析了。应用开发者应该根据应用场景选择相应的流类型,以便系统为这道流选择合适的输出设备。

一个 AudioTrack Java API 的测试例子(MODE_STREAM 模式):

    //Test case 1: setStereoVolume() with max volume returns SUCCESS
    @LargeTest
    public void testSetStereoVolumeMax() throws Exception {
        // constants for test
        final String TEST_NAME = "testSetStereoVolumeMax";
        final int TEST_SR = 22050;
        final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
        final int TEST_MODE = AudioTrack.MODE_STREAM;
        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;

        //-------- initialization --------------
        // 稍后详细分析 getMinBufferSize
        int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
        // 创建一个 AudioTrack 实例
        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, 
                minBuffSize, TEST_MODE);
        byte data[] = new byte[minBuffSize/2];
        //--------    test        --------------
        // 调用 write() 写入回放数据
        track.write(data, 0, data.length);
        track.write(data, 0, data.length);
        // 调用 play() 开始播放
        track.play();
        float maxVol = AudioTrack.getMaxVolume();
        assertTrue(TEST_NAME, track.setStereoVolume(maxVol, maxVol) == AudioTrack.SUCCESS);
        //-------- tear down      --------------
        // 播放完成后,调用 release() 释放 AudioTrack 实例
        track.release();
    }

详细说明下 getMinBufferSize() 接口,字面意思是返回最小数据缓冲区的大小,它是声音能正常播放的最低保障,从函数参数来看,返回值取决于采样率、采样深度、声道数这三个属性。MODE_STREAM 模式下,应用程序重点参考其返回值然后确定分配多大的数据缓冲区。如果数据缓冲区分配得过小,那么播放声音会频繁遭遇 underrun,underrun 是指生产者(AudioTrack)提供数据的速度跟不上消费者(AudioFlinger::PlaybackThread)消耗数据的速度,反映到现实的后果就是声音断续卡顿,严重影响听觉体验。

// AudioTrack.java
/**
     * Returns the estimated minimum buffer size required for an AudioTrack
     * object to be created in the {@link #MODE_STREAM} mode.
     * The size is an estimate because it does not consider either the route or the sink,
     * since neither is known yet.  Note that this size doesn't
     * guarantee a smooth playback under load, and higher values should be chosen according to
     * the expected frequency at which the buffer will be refilled with additional data to play.
     * For example, if you intend to dynamically set the source sample rate of an AudioTrack
     * to a higher value than the initial source sample rate, be sure to configure the buffer size
     * based on the highest planned sample rate.
     * @param sampleRateInHz the source sample rate expressed in Hz.
     *   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted.
     * @param channelConfig describes the configuration of the audio channels.
     *   See {@link AudioFormat#CHANNEL_OUT_MONO} and
     *   {@link AudioFormat#CHANNEL_OUT_STEREO}
     * @param audioFormat the format in which the audio data is represented.
     *   See {@link AudioFormat#ENCODING_PCM_16BIT} and
     *   {@link AudioFormat#ENCODING_PCM_8BIT},
     *   and {@link AudioFormat#ENCODING_PCM_FLOAT}.
     * @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed,
     *   or {@link #ERROR} if unable to query for output properties,
     *   or the minimum buffer size expressed in bytes.
     */
    static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
        int channelCount = 0;
        switch(channelConfig) {
        case AudioFormat.CHANNEL_OUT_MONO:
        case AudioFormat.CHANNEL_CONFIGURATION_MONO:
            channelCount = 1; // 单声道
            break;
        case AudioFormat.CHANNEL_OUT_STEREO:
        case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
            channelCount = 2; // 双声道
            break;
        default:
            if (!isMultichannelConfigSupported(channelConfig)) {
                loge("getMinBufferSize(): Invalid channel configuration.");
                return ERROR_BAD_VALUE;
            } else {
                channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
            }
        }

        if (!AudioFormat.isPublicEncoding(audioFormat)) {
            loge("getMinBufferSize(): Invalid audio format.");
            return ERROR_BAD_VALUE;
        }

        // sample rate, note these values are subject to change
        // Note: AudioFormat.SAMPLE_RATE_UNSPECIFIED is not allowed
        if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||
                (sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) {
            loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate.");
            return ERROR_BAD_VALUE; // 采样率支持:4KHz~192KHz
        }

        // 调用 JNI 方法,下面分析该函数
        int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
        if (size <= 0) {
            loge("getMinBufferSize(): error querying hardware");
            return ERROR;
        }
        else {
            return size;
        }
    }

// android_media_AudioTrack.cpp
// ----------------------------------------------------------------------------
// returns the minimum required size for the successful creation of a streaming AudioTrack
// returns -1 if there was an error querying the hardware.
static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,
    jint sampleRateInHertz, jint channelCount, jint audioFormat) {

    size_t frameCount;
    // 调用 AudioTrack::getMinFrameCount,这里不深究,到 native 层再分析
    // 这个函数用于确定至少设置多少个 frame 才能保证声音正常播放,也就是最低帧数
    const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
            sampleRateInHertz);
    if (status != NO_ERROR) {
        ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",
                sampleRateInHertz, status);
        return -1;
    }
    const audio_format_t format = audioFormatToNative(audioFormat);
    if (audio_has_proportional_frames(format)) {
        const size_t bytesPerSample = audio_bytes_per_sample(format);
        return frameCount * channelCount * bytesPerSample; // PCM 数据最小缓冲区大小
    } else {
        return frameCount;
    }
}

可见最小缓冲区的大小 = 最低帧数 * 声道数 * 采样深度,(采样深度以字节为单位),到这里大家应该有所明悟了吧,在视频中,如果帧数过低,那么画面会有卡顿感,对于音频,道理也是一样的。最低帧数如何求得,我们到 native 层再解释。

关于 MediaPlayer、AudioTrack,更多更详细的 API 接口说明请参考 Android Developer:

2.2. AudioTrack Native API

AudioTrack Native API 四种数据传输模式:

Transfer ModeDescription
TRANSFER_CALLBACK在 AudioTrackThread 线程中通过 audioCallback 回调函数主动从应用进程那里索取数据,ToneGenerator 采用这种模式
TRANSFER_OBTAIN应用进程需要调用 obtainBuffer()/releaseBuffer() 填充数据,目前我还没有见到实际的使用场景
TRANSFER_SYNC应用进程需要持续调用 write() 写数据到 FIFO,写数据时有可能遭遇阻塞(等待 AudioFlinger::PlaybackThread 消费之前的数据),基本适用所有的音频场景;对应于 AudioTrack Java API 的 MODE_STREAM 模式
TRANSFER_SHARED应用进程将回放数据一次性付给 AudioTrack,适用于数据量小、时延要求高的场景;对应于 AudioTrack Java API 的 MODE_STATIC 模式


AudioTrack Native API 音频流类型:

Stream TypeDescription
AUDIO_STREAM_VOICE_CALL电话语音
AUDIO_STREAM_SYSTEM系统声音
AUDIO_STREAM_RING铃声声音,如来电铃声、闹钟铃声等
AUDIO_STREAM_MUSIC音乐声音
AUDIO_STREAM_ALARM警告音
AUDIO_STREAM_NOTIFICATION通知音
AUDIO_STREAM_DTMFDTMF 音(拨号盘按键音)


AudioTrack Native API 输出标识:

AUDIO_OUTPUT_FLAGDescription
AUDIO_OUTPUT_FLAG_DIRECT表示音频流直接输出到音频设备,不需要软件混音,一般用于 HDMI 设备声音输出
AUDIO_OUTPUT_FLAG_PRIMARY表示音频流需要输出到主输出设备,一般用于铃声类声音
AUDIO_OUTPUT_FLAG_FAST表示音频流需要快速输出到音频设备,一般用于按键音、游戏背景音等对时延要求高的场景
AUDIO_OUTPUT_FLAG_DEEP_BUFFER表示音频流输出可以接受较大的时延,一般用于音乐、视频播放等对时延要求不高的场景
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD表示音频流没有经过软件解码,需要输出到硬件解码器,由硬件解码器进行解码


我们根据不同的播放场景,使用不同的输出标识,如按键音、游戏背景音对输出时延要求很高,那么就需要置 AUDIO_OUTPUT_FLAG_FAST,具体可以参考 ToneGenerator、SoundPool 和 OpenSL ES。

一个 AudioTrack Natvie API 的测试例子(MODE_STATIC/TRANSFER_SHARED 模式),代码文件位置:frameworks/base/media/tests/audiotests/shared_mem_test.cpp:

int AudioTrackTest::Test01() {

    sp<MemoryDealer> heap;
    sp<IMemory> iMem;
    uint8_t* p;

    short smpBuf[BUF_SZ];
    long rate = 44100;
    unsigned long phi;
    unsigned long dPhi;
    long amplitude;
    long freq = 1237;
    float f0;

    f0 = pow(2., 32.) * freq / (float)rate;
    dPhi = (unsigned long)f0;
    amplitude = 1000;
    phi = 0;
    Generate(smpBuf, BUF_SZ, amplitude, phi, dPhi);  // fill buffer

    for (int i = 0; i < 1024; i++) {
        // 分配一块匿名共享内存
        heap = new MemoryDealer(1024*1024, "AudioTrack Heap Base");

        iMem = heap->allocate(BUF_SZ*sizeof(short));

        // 把音频数据拷贝到这块匿名共享内存上
        p = static_cast<uint8_t*>(iMem->pointer());
        memcpy(p, smpBuf, BUF_SZ*sizeof(short));

        // 构造一个 AudioTrack 实例,该 AudioTrack 的数据方式是 MODE_STATIC
        // 音频数据已经一次性拷贝到共享内存上了,不用再调用 track->write() 填充数据了
        sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type
               rate,
               AUDIO_FORMAT_PCM_16_BIT,// word length, PCM
               AUDIO_CHANNEL_OUT_MONO,
               iMem);

        // 检查 AudioTrack 实例是否构造成功饿了
        status_t status = track->initCheck();
        if(status != NO_ERROR) {
            track.clear();
            ALOGD("Failed for initCheck()");
            return -1;
        }

        // start play
        ALOGD("start");
        track->start(); // 开始播放

        usleep(20000);

        ALOGD("stop");
        track->stop(); // 停止播放
        iMem.clear();
        heap.clear();
        usleep(20000);
    }

    return 0;
}

上个小节还存在一个问题:AudioTrack::getMinFrameCount() 如何计算最低帧数呢?

首先要了解音频领域中,帧(frame)的概念:帧表示一个完整的声音单元,所谓的声音单元是指一个采样样本;如果是双声道,那么一个完整的声音单元就是 2 个样本,如果是 5.1 声道,那么一个完整的声音单元就是 6 个样本了。帧的大小(一个完整的声音单元的数据量)等于声道数乘以采样深度,即 frameSize = channelCount * bytesPerSample。帧的概念非常重要,无论是框架层还是内核层,都是以帧为单位去管理音频数据缓冲区的。

其次还得了解音频领域中,传输延迟(latency)的概念:传输延迟表示一个周期的音频数据的传输时间。可能有些读者一脸懵逼,一个周期的音频数据,这又是啥?我们再引入周期(period)的概念:Linux ALSA 把数据缓冲区划分为若干个块,dma 每传输完一个块上的数据即发出一个硬件中断,cpu 收到中断信号后,再配置 dma 去传输下一个块上的数据;一个块即是一个周期,周期大小(periodSize)即是一个数据块的帧数。再回到传输延迟(latency),传输延迟等于周期大小除以采样率,即 latency = periodSize / sampleRate

最后了解下音频重采样:音频重采样是指这样的一个过程——把一个采样率的数据转换为另一个采样率的数据。Android 原生系统上,音频硬件设备一般都工作在一个固定的采样率上(如 48 KHz),因此所有音轨数据都需要重采样到这个固定的采样率上,然后再输出。为什么这么做?系统中可能存在多个音轨同时播放,而每个音轨的采样率可能是不一致的;比如在播放音乐的过程中,来了一个提示音,这时需要把音乐和提示音混音并输出到硬件设备,而音乐的采样率和提示音的采样率不一致,问题来了,如果硬件设备工作的采样率设置为音乐的采样率的话,那么提示音就会失真;因此最简单见效的解决方法是:硬件设备工作的采样率固定一个值,所有音轨在 AudioFlinger 都重采样到这个采样率上,混音后输出到硬件设备,保证所有音轨听起来都不失真。

sample、frame、period、latency 这些概念与 Linux ALSA 及硬件设备的关系非常密切,这里点到即止,如有兴趣深入了解的话,可参考:Linux ALSA 音频系统:逻辑设备篇

了解这些前置知识后,我们再分析 AudioTrack::getMinFrameCount() 这个函数:

status_t AudioTrack::getMinFrameCount(
        size_t* frameCount,
        audio_stream_type_t streamType,
        uint32_t sampleRate)
{
    if (frameCount == NULL) {
        return BAD_VALUE;
    }

    // 通过 binder 调用到 AudioFlinger::sampleRate(),取得硬件设备的采样率
    uint32_t afSampleRate;
    status_t status;
    status = AudioSystem::getOutputSamplingRate(&afSampleRate, streamType);
    if (status != NO_ERROR) {
        ALOGE("Unable to query output sample rate for stream type %d; status %d",
                streamType, status);
        return status;
    }
    // 通过 binder 调用到 AudioFlinger::frameCount(),取得硬件设备的周期大小
    size_t afFrameCount;
    status = AudioSystem::getOutputFrameCount(&afFrameCount, streamType);
    if (status != NO_ERROR) {
        ALOGE("Unable to query output frame count for stream type %d; status %d",
                streamType, status);
        return status;
    }
    // 通过 binder 调用到 AudioFlinger::latency(),取得硬件设备的传输延迟
    uint32_t afLatency;
    status = AudioSystem::getOutputLatency(&afLatency, streamType);
    if (status != NO_ERROR) {
        ALOGE("Unable to query output latency for stream type %d; status %d",
                streamType, status);
        return status;
    }

    // When called from createTrack, speed is 1.0f (normal speed).
    // This is rechecked again on setting playback rate (TODO: on setting sample rate, too).
    // 根据 afSampleRate、afFrameCount、afLatency 计算出一个最低帧数
    *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f);

    // The formula above should always produce a non-zero value under normal circumstances:
    // AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX.
    // Return error in the unlikely event that it does not, as that's part of the API contract.
    if (*frameCount == 0) {
        ALOGE("AudioTrack::getMinFrameCount failed for streamType %d, sampleRate %u",
                streamType, sampleRate);
        return BAD_VALUE;
    }
    ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u",
            *frameCount, afFrameCount, afSampleRate, afLatency);
    return NO_ERROR;
}

// 有兴趣的可以研究 calculateMinFrameCount() 的实现,需大致了解重采样算法原理
static size_t calculateMinFrameCount(
        uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
        uint32_t sampleRate, float speed)
{
    // Ensure that buffer depth covers at least audio hardware latency
    uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
    if (minBufCount < 2) {
        minBufCount = 2;
    }
    ALOGV("calculateMinFrameCount afLatency %u  afFrameCount %u  afSampleRate %u  "
            "sampleRate %u  speed %f  minBufCount: %u",
            afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount);
    return minBufCount * sourceFramesNeededWithTimestretch(
            sampleRate, afFrameCount, afSampleRate, speed);
}

static inline size_t sourceFramesNeededWithTimestretch(
        uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate,
        float speed) {
    // required is the number of input frames the resampler needs
    size_t required = sourceFramesNeeded(srcSampleRate, dstFramesRequired, dstSampleRate);
    // to deliver this, the time stretcher requires:
    return required * (double)speed + 1 + 1; // accounting for rounding dependencies
}

// Returns the source frames needed to resample to destination frames.  This is not a precise
// value and depends on the resampler (and possibly how it handles rounding internally).
// Nevertheless, this should be an upper bound on the requirements of the resampler.
// If srcSampleRate and dstSampleRate are equal, then it returns destination frames, which
// may not be true if the resampler is asynchronous.
static inline size_t sourceFramesNeeded(
        uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate) {
    // +1 for rounding - always do this even if matched ratio (resampler may use phases not ratio)
    // +1 for additional sample needed for interpolation
    return srcSampleRate == dstSampleRate ? dstFramesRequired :
            size_t((uint64_t)dstFramesRequired * srcSampleRate / dstSampleRate + 1 + 1);
}

我们不深入分析 calculateMinFrameCount() 函数了,并不是说这个函数的流程有多复杂,而是它涉及到音频重采样的背景原理,说清楚 how 很容易,但说清楚 why 就很困难了。目前我们只需要知道:这个函数根据硬件设备的配置信息(采样率、周期大小、传输延迟)和音轨的采样率,计算出一个最低帧数(应用程序至少设置多少个帧才能保证声音正常播放)。

说点题外话,Anroid 2.2 时,AudioTrack::getMinFrameCount() 的处理很简单:

status_t AudioTrack::getMinFrameCount(
        int* frameCount,
        int streamType,
        uint32_t sampleRate)
{
    int afSampleRate;
    if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
        return NO_INIT;
    }
    int afFrameCount;
    if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
        return NO_INIT;
    }
    uint32_t afLatency;
    if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
        return NO_INIT;
    }

    // Ensure that buffer depth covers at least audio hardware latency
    uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
    if (minBufCount < 2) minBufCount = 2;

    *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount :
              afFrameCount * minBufCount * sampleRate / afSampleRate;
    return NO_ERROR;
}

从这段来看,最低帧数也是基于重采样来计算的,只不过这里的处理很粗糙:afFrameCount 是硬件设备处理单个数据块的帧数,afSampleRate 是硬件设备配置的采样率,sampleRate 是音轨的采样率,如果要把音轨数据重采样到 afSampleRate 上,那么反推算出应用程序最少传入的帧数为 afFrameCount * sampleRate / afSampleRate,而为了播放流畅,实际上还要大一点,所以再乘以一个系数(可参照 framebuffer 双缓冲,一个缓冲缓存当前的图像,一个缓冲准备下一幅的图像,这样图像切换更流畅),然后就得出一个可以保证播放流畅的最低帧数 minFrameCount = (afFrameCount * sampleRate / afSampleRate) * minBufCount

为什么 Android 7.0 这方面的处理比 Android 2.2 复杂那么多呢?我想是两个原因:

  1. Android 7.0 充分考虑了边界处理
  2. Android 2.2 只支持采样率 4~48 KHz 的音轨,但 Android 7.0 支持采样率 4~192 KHz 的音轨,因此现在对重采样处理提出更严格的要求

3. AudioFlinger 概述

AudioPolicyService 与 AudioFlinger 是 Android 音频系统的两大基本服务。前者是音频系统策略的制定者,负责音频设备切换的策略抉择、音量调节策略等;后者是音频系统策略的执行者,负责音频流设备的管理及音频流数据的处理传输,所以 AudioFlinger 也被认为是 Android 音频系统的引擎。

3.1. AudioFlinger 代码文件结构

$ tree ./frameworks/av/services/audioflinger/
./frameworks/av/services/audioflinger/
├── Android.mk
├── AudioFlinger.cpp
├── AudioFlinger.h
├── AudioHwDevice.cpp
├── AudioHwDevice.h
├── AudioMixer.cpp
├── AudioMixer.h
├── AudioMixerOps.h
├── audio-resampler
│   ├── Android.mk
│   ├── AudioResamplerCoefficients.cpp
│   └── filter_coefficients.h
├── AudioResampler.cpp
├── AudioResamplerCubic.cpp
├── AudioResamplerCubic.h
├── AudioResamplerDyn.cpp
├── AudioResamplerDyn.h
├── AudioResamplerFirGen.h
├── AudioResamplerFirOps.h
├── AudioResamplerFirProcess.h
├── AudioResamplerFirProcessNeon.h
├── AudioResampler.h
├── AudioResamplerSinc.cpp
├── AudioResamplerSincDown.h
├── AudioResamplerSinc.h
├── AudioResamplerSincUp.h
├── AudioStreamOut.cpp
├── AudioStreamOut.h
├── AudioWatchdog.cpp
├── AudioWatchdog.h
├── BufferProviders.cpp
├── BufferProviders.h
├── Configuration.h
├── Effects.cpp
├── Effects.h
├── FastCapture.cpp
├── FastCaptureDumpState.cpp
├── FastCaptureDumpState.h
├── FastCapture.h
├── FastCaptureState.cpp
├── FastCaptureState.h
├── FastMixer.cpp
├── FastMixerDumpState.cpp
├── FastMixerDumpState.h
├── FastMixer.h
├── FastMixerState.cpp
├── FastMixerState.h
├── FastThread.cpp
├── FastThreadDumpState.cpp
├── FastThreadDumpState.h
├── FastThread.h
├── FastThreadState.cpp
├── FastThreadState.h
├── MODULE_LICENSE_APACHE2
├── NOTICE
├── PatchPanel.cpp
├── PatchPanel.h
├── PlaybackTracks.h
├── RecordTracks.h
├── ServiceUtilities.cpp
├── ServiceUtilities.h
├── SpdifStreamOut.cpp
├── SpdifStreamOut.h
├── StateQueue.cpp
├── StateQueue.h
├── StateQueueInstantiations.cpp
├── test-resample.cpp
├── tests
│   ├── Android.mk
│   ├── build_and_run_all_unit_tests.sh
│   ├── mixer_to_wav_tests.sh
│   ├── resampler_tests.cpp
│   ├── run_all_unit_tests.sh
│   ├── test-mixer.cpp
│   └── test_utils.h
├── Threads.cpp
├── Threads.h
├── TrackBase.h
└── Tracks.cpp

2 directories, 77 files

记得刚接触 Android 时,版本是 Android 2.2-Froyo,AudioFlinger 只有 3 个源文件:AudioFlinger.cpp、AudioMixer.cpp、AudioResampler.cpp。

现在文件多了许多,代码量就不用说了。但是接口及其基本流程一直没有改变的,只是更加模块化了,Google 把多个子类抽取出来独立成文件,比如 Threads.cpp、Tracks.cpp、Effects.cpp,而 AudioFlinger.cpp 只包含对外提供的服务接口了。另外相比以前,增加更多的功能特性,如 teesink、Offload、FastMixer、FastCapture、FastThread、PatchPanel 等,这里不对这些功能特性扩展描述,有兴趣的可以自行分析。

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

本文内容主要涉及 AudioFlinger.cpp、Threads.cpp、Tracks.cpp 这三个文件。

3.2. AudioFlinger 服务启动

从 Android 7.0 开始,AudioFlinger 在系统启动时由 audioserver 加载(之前版本由 mediaserver 加载),详见 frameworks/av/media/audioserver/main_audioserver.cpp:

int main(int argc __unused, char **argv)
{
    // ......

    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm = defaultServiceManager();
    ALOGI("ServiceManager: %p", sm.get());
    AudioFlinger::instantiate();
    AudioPolicyService::instantiate();
    RadioService::instantiate();
    SoundTriggerHwService::instantiate();
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}

可见 audioserver 把音频相关的服务都加载了,包括 AudioFlinger、AudioPolicyService、RadioService、SoundTriggerHwService。

main_audioserver.cpp 编译生成的可执行文件存放在 /system/bin/audioserver,系统启动时由 init 进程运行,详见 frameworks/av/media/audioserver/audioserver.rc:

service audioserver /system/bin/audioserver
    class main
    user audioserver
    # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
    group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct
    ioprio rt 4
    writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks

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

3.3. AudioFlinger 服务接口

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

InterfaceDescription
sampleRate获取硬件设备的采样率
format获取硬件设备的音频格式
frameCount获取硬件设备的周期帧数
latency获取硬件设备的传输延迟
setMasterVolume调节主输出设备的音量
setMasterMute静音主输出设备
setStreamVolume调节指定类型的音频流的音量,这种调节不影响其他类型的音频流的音量
setStreamMute静音指定类型的音频流
setVoiceVolume调节通话音量
setMicMute静音麦克风输入
setMode切换音频模式:音频模式有 4 种,分别是 Normal、Ringtone、Call、Communicatoin
setParameters设置音频参数:往下调用 HAL 层相应接口,常用于切换音频通道
getParameters获取音频参数:往下调用 HAL 层相应接口
openOutput打开输出流:打开输出流设备,并创建 PlaybackThread 对象
closeOutput关闭输出流:移除并销毁 PlaybackThread 上面挂着的所有的 Track,退出 PlaybackThread,关闭输出流设备
openInput打开输入流:打开输入流设备,并创建 RecordThread 对象
closeInput关闭输入流:退出 RecordThread,关闭输入流设备
createTrack新建输出流管理对象: 找到对应的 PlaybackThread,创建输出流管理对象 Track,然后创建并返回该 Track 的代理对象 TrackHandle
openRecord新建输入流管理对象:找到 RecordThread,创建输入流管理对象 RecordTrack,然后创建并返回该 RecordTrack 的代理对象 RecordHandle

可以归纳出 AudioFlinger 响应的服务请求主要有:

  • 获取硬件设备的配置信息
  • 音量调节
  • 静音操作
  • 音频模式切换
  • 音频参数设置
  • 输入输出流设备管理
  • 音频流管理

就本文范围而言,主要涉及 openOutput() 和 createTrack() 这两个接口,后面也会详细分析这两个接口的流程。

3.4. AudioFlinger 回放录制线程

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

Threads

  • 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 数据

PlaybackThread 中有个极为重要的函数 threadLoop(),当 PlaybackThread 被强引用时,threadLoop() 会真正运行起来进入循环主体,处理音频流数据相关事务,threadLoop() 大致流程如下(以 MixerThread 为例):

bool AudioFlinger::PlaybackThread::threadLoop()
{
    // ......

    while (!exitPending())
    {
        // ......

        { // scope for mLock

            Mutex::Autolock _l(mLock);

            processConfigEvents_l();

            // ......

            if ((!mActiveTracks.size() && systemTime() > mStandbyTimeNs) ||
                                   isSuspended()) {
                // put audio hardware into standby after short delay
                if (shouldStandby_l()) {

                    threadLoop_standby();

                    mStandby = true;
                }

                // ......
            }
            // mMixerStatusIgnoringFastTracks is also updated internally
            mMixerStatus = prepareTracks_l(&tracksToRemove);

            // ......
        } // mLock scope ends

        // ......

        if (mBytesRemaining == 0) {
            mCurrentWriteLength = 0;
            if (mMixerStatus == MIXER_TRACKS_READY) {
                // threadLoop_mix() sets mCurrentWriteLength
                threadLoop_mix();
            }
            // ......
        }

        // ......

        if (!waitingAsyncCallback()) {
            // mSleepTimeUs == 0 means we must write to audio hardware
            if (mSleepTimeUs == 0) {
                // ......
                if (mBytesRemaining) {
                    // FIXME rewrite to reduce number of system calls
                    ret = threadLoop_write();
                    lastWriteFinished = systemTime();
                    delta = lastWriteFinished - mLastWriteTime;
                    if (ret < 0) {
                        mBytesRemaining = 0;
                    } else {
                        mBytesWritten += ret;
                        mBytesRemaining -= ret;
                        mFramesWritten += ret / mFrameSize;
                    }
                }
                // ......
            }
            // ......
        }

        // Finally let go of removed track(s), without the lock held
        // since we can't guarantee the destructors won't acquire that
        // same lock.  This will also mutate and push a new fast mixer state.
        threadLoop_removeTracks(tracksToRemove);
        tracksToRemove.clear();

        // ......
    }

    threadLoop_exit();

    if (!mStandby) {
        threadLoop_standby();
        mStandby = true;
    }

    // ......
    return false;
}
  1. threadLoop() 循环的条件是 exitPending() 返回 false,如果想要 PlaybackThread 结束循环,则可以调用 requestExit() 来请求退出;
  2. processConfigEvents_l() :处理配置事件;当有配置改变的事件发生时,需要调用 sendConfigEvent_l() 来通知 PlaybackThread,这样 PlaybackThread 才能及时处理配置事件;常见的配置事件是切换音频通路;
  3. 检查此时此刻是否符合 standby 条件,比如当前并没有 ACTIVE 状态的 Track(mActiveTracks.size() = 0),那么调用 threadLoop_standby() 关闭音频硬件设备以节省能耗;
  4. prepareTracks_l(): 准备音频流和混音器,该函数非常复杂,这里不详细分析了,仅列一下流程要点:
    • 遍历 mActiveTracks,逐个处理 mActiveTracks 上的 Track,检查该 Track 是否为 ACTIVE 状态;
    • 如果 Track 设置是 ACTIVE 状态,则再检查该 Track 的数据是否准备就绪了;
    • 根据音频流的音量值、格式、声道数、音轨的采样率、硬件设备的采样率,配置好混音器参数;
    • 如果 Track 的状态是 PAUSED 或 STOPPED,则把该 Track 添加到 tracksToRemove 向量中;
  5. threadLoop_mix():读取所有置了 ACTIVE 状态的音频流数据,混音器开始处理这些数据;
  6. threadLoop_write(): 把混音器处理后的数据写到输出流设备;
  7. threadLoop_removeTracks(): 把 tracksToRemove 上的所有 Track 从 mActiveTracks 中移除出来;这样下一次循环时就不会处理这些 Track 了。

这里说说 PlaybackThread 与输出流设备的关系:PlaybackThread 实例与输出流设备是一一对应的,比方说 OffloadThread 只会将音频数据输出到 compress_offload 设备中,MixerThread(with FastMixer) 只会将音频数据输出到 low_latency 设备中。

从 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 实例。其他类型的输出流设备并非必须声明支持的,主要是看硬件上有无这个能力。

可能有人产生这样的疑问:既然 primary_out 设备一直保持打开,那么能耗岂不是很大?这里阐释一个概念:输出流设备属于逻辑设备,并不是硬件设备。所以即使输出流设备一直保持打开,只要硬件设备不工作,那么就不会影响能耗。那么硬件设备什么时候才会打开呢?答案是 PlaybackThread 将音频数据写入到输出流设备时。

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

PlaybackThread&StreamDevice

我们可以这么说:输出流设备决定了它对应的 PlaybackThread 是什么类型。怎么理解呢?意思是说:只有支持了该类型的输出流设备,那么该类型的 PlaybackThread 才有可能被创建。举个例子:只有硬件上具备硬件解码器,系统才建立 compress_offload 设备,然后播放 mp3 格式的音乐文件时,才会创建 OffloadThread 把数据输出到 compress_offload 设备上;反之,如果硬件上并不具备硬件解码器,系统则不应该建立 compress_offload 设备,那么播放 mp3 格式的音乐文件时,通过 MixerThread 把数据输出到其他输出流设备上。

那么有无可能出现这种情况:底层并不支持 compress_offload 设备,但偏偏有个标识为 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音频流送到 AudioFlinger 了呢?这是不可能的。系统启动时,会检查并保存输入输出流设备的支持信息;播放器在播放 mp3 文件时,首先看 compress_offload 设备是否支持了,如果支持,那么不进行软件解码,直接把数据标识为 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;如果不支持,那么先进行软件解码,然后把解码好的数据标识为 AUDIO_OUTPUT_FLAG_DEEP_BUFFER,前提是 deep_buffer 设备是支持了的;如果 deep_buffer 设备也不支持,那么把数据标识为 AUDIO_OUTPUT_FLAG_PRIMARY。

系统启动时,就已经打开 primary_out、low_latency、deep_buffer 这三种输出流设备,并创建对应的 MixerThread 了;而此时 DirectOutputThread 与 OffloadThread 不会被创建,直到标识为 AUDIO_OUTPUT_FLAG_DIRECT/AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音频流需要输出时,才开始创建 DirectOutputThread/OffloadThread 和打开 direct_out/compress_offload 设备。这一点请参考如下代码,注释非常清晰:

AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
    // ......
{
    // ......

    // mAvailableOutputDevices and mAvailableInputDevices now contain all attached devices
    // open all output streams needed to access attached devices
    // ......
    for (size_t i = 0; i < mHwModules.size(); i++) {
        // ......
        // open all output streams needed to access attached devices
        // except for direct output streams that are only opened when they are actually
        // required by an app.
        // This also validates mAvailableOutputDevices list
        for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
        {
            // ......
            if ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_DIREC

以上是关于<一>Android Audio音频框架的主要内容,如果未能解决你的问题,请参考以下文章

Android智能手机中各种音频场景下的audio data path

Android 音频系统:从 AudioTrack 到 AudioFlinger

Unity SKFramework框架Audio音频管理器

Android系统Audio框架介绍

Android 音频系统:从 AudioTrack 到 AudioFlinger

Android -- Audio Native服务之启动流程分析