Android 音频系统(二)之 AudioFlinger

接着前面的文章《Android 音频框架概述(一)之 AudioTrack

3. AudioFlinger 概述

AudioPolicyService 与 AudioFlinger 是 Android 音频系统的两大基本服务。
AudioPolicyService 是音频系统策略的制定者,负责音频设备切换的策略抉择、音量调节策略等;
AudioFlinger 是音频系统策略的执行者,负责音频流设备的管理及音频流数据的处理传输,
所以 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 对外提供的主要的服务接口如下:

Interface Description
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)进行的,
我们简单看看回放线程和录制线程类关系:
在这里插入图片描述
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 种输出流设备,分别对应着不同的播放场景:

  1. primary_out: 主输出流设备,用于铃声类声音输出,
    对应着标识为 AUDIO_OUTPUT_FLAG_PRIMARY 的音频流和一个 MixerThread 回放线程实例

  2. low_latency: 低延迟输出流设备,用于按键音、游戏背景音等对时延要求高的声音输出,
    对应着标识为 AUDIO_OUTPUT_FLAG_FAST 的音频流和一个 MixerThread 回放线程实例

  3. deep_buffer:音乐音轨输出流设备,用于音乐等对时延要求不高的声音输出,
    对应着标识为 AUDIO_OUTPUT_FLAG_DEEP_BUFFER 的音频流和一个 MixerThread 回放线程实例

  4. compress_offload:硬解输出流设备,用于需要硬件解码的数据输出,
    对应着标识为 AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音频流和一个 OffloadThread 回放线程实例

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

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

下图简单描述 AudioTrack、PlaybackThread、输出流设备三者的对应关系:
在这里插入图片描述
我们可以这么说: 输出流设备决定了它对应的 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_DIRECT) != 0) {
                continue;
            }
            // ......
            audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
            status_t status = mpClientInterface->openOutput(outProfile->getModuleHandle(),
                                                            &output,
                                                            &config,
                                                            &outputDesc->mDevice,
                                                            address,
                                                            &outputDesc->mLatency,
                                                            outputDesc->mFlags);

            // ......
        }
        // open input streams needed to access attached devices to validate
        // mAvailableInputDevices list
        for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++)
        {
            // ......
            status_t status = mpClientInterface->openInput(inProfile->getModuleHandle(),
                                                           &input,
                                                           &config,
                                                           &inputDesc->mDevice,
                                                           address,
                                                           AUDIO_SOURCE_MIC,
                                                           AUDIO_INPUT_FLAG_NONE);

            // ......
        }
    }
    // ......

    updateDevicesAndOutputs();
}

其中 mpClientInterface->openOutput() 最终会调用到 AudioFlinger::openOutput():打开输出流设备,并创建 PlaybackThread 对象:

status_t AudioFlinger::openOutput(audio_module_handle_t module,
                                  audio_io_handle_t *output,
                                  audio_config_t *config,
                                  audio_devices_t *devices,
                                  const String8& address,
                                  uint32_t *latencyMs,
                                  audio_output_flags_t flags)
{
    // ......

    Mutex::Autolock _l(mLock);

    sp<PlaybackThread> thread = openOutput_l(module, output, config, *devices, address, flags);
    // ......
}

sp<AudioFlinger::PlaybackThread> AudioFlinger::openOutput_l(audio_module_handle_t module,
                                                            audio_io_handle_t *output,
                                                            audio_config_t *config,
                                                            audio_devices_t devices,
                                                            const String8& address,
                                                            audio_output_flags_t flags)
{
    // ......
    // 分配全局唯一的 audio_io_handle_t,可以理解它是回放线程的索引号
    if (*output == AUDIO_IO_HANDLE_NONE) {
        *output = nextUniqueId(); 
    }

    mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;

    //......

    // 打开音频输出流设备,HAL 层根据 flags 选择打开相关类型的输出流设备
    AudioStreamOut *outputStream = NULL;
    status_t status = outHwDev->openOutputStream(
            &outputStream,
            *output,
            devices,
            flags,
            config,
            address.string());

    mHardwareStatus = AUDIO_HW_IDLE;

    if (status == NO_ERROR) {

        PlaybackThread *thread;
        if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) 
        {
            // AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 音频流,创建 OffloadThread 实例
            thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady);
            ALOGV("openOutput_l() created offload output: ID %d thread %p", *output, thread);
        } 
        else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)
                || !isValidPcmSinkFormat(config->format)
                || !isValidPcmSinkChannelMask(config->channel_mask))
        {
            // AUDIO_OUTPUT_FLAG_DIRECT 音频流,创建 DirectOutputThread 实例
            thread = new DirectOutputThread(this, outputStream, *output, devices, mSystemReady);
            ALOGV("openOutput_l() created direct output: ID %d thread %p", *output, thread);
        } 
        else 
        {
            // 其他标识的音频流,创建 MixerThread 实例
            thread = new MixerThread(this, outputStream, *output, devices, mSystemReady);
            ALOGV("openOutput_l() created mixer output: ID %d thread %p", *output, thread);
        }
        
        // 把 audio_io_handle_t 和 PlaybackThread 添加到键值对向量 mPlaybackThreads 中
        // 键值对向量 mPlaybackThreads 中,由于 audio_io_handle_t 和 PlaybackThread 是一
        // 一对应的关系,所以拿到一个 audio_io_handle_t,就能找到它对应的 PlaybackThread
        // 所以可以理解 audio_io_handle_t 为 PlaybackThread 的索引号
        mPlaybackThreads.add(*output, thread);
        return thread;
    }

    return 0;
}

3.5. AudioFlinger OffloadThread

系统启动时,就已经打开 primary_out、low_latency、deep_buffer 这三种输出流设备,并创建对应的 MixerThread 了;
而此时 DirectOutputThread 与 OffloadThread 不会被创建,
直到标识为 AUDIO_OUTPUT_FLAG_DIRECT/AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD 的音频流需要输出时,
才开始创建 DirectOutputThread/OffloadThread 和打开 direct_out/compress_offload 设备。

这里不知大家有无疑问:为什么 DirectOutputThread 与 OffloadThread 会被单独对待?

DirectOuputThread 使用率较低,尚可以理解,
但 OffloadThread 的使用率还是很高的,为什么不让 OffloadThread/compress_offload 设备也进入待命状态呢?

要回答这个问题:
我们首先得明白 compress_offload 设备是什么东东,与其他输出流设备有什么不同。

先看个图:
在这里插入图片描述
红色的是 Offload 音频流,它与其他音频流有什么本质的不同?
Offload 音频流是未经 NuPlayerDecoder 进行解码的(NuPlayerDecoder 设置了 Passthrough 模式),
所以必须把这些音频流数据送到 DSP,让 DSP 对其解码,解码后的 PCM 数据再送到 Codec 输出。

compress_offload 设备,说白了,就是驱动 DSP 解码数据、Codec 输出声音。
而 DSP 要解码数据,首先得知道数据的编码信息,如编码器 codec_id、采样率 sample_rate、声道数 channel_mask、比特率 bit_rate 等信息,由于 DSP 并没有实现 DataSource/Parser 部件,不能自己解析数据的编码信息,所以得有“人”告诉它,这个“人”无疑是 compress_offload 设备。

AudioTrack 构造函数有个 offloadInfo 的参数,参数原型定义如下:

/* Additional information about compressed streams offloaded to
 * hardware playback
 * The version and size fields must be initialized by the caller by using
 * one of the constants defined here.
 */
typedef struct {
    uint16_t version;                   // version of the info structure
    uint16_t size;                      // total size of the structure including version and size
    uint32_t sample_rate;               // sample rate in Hz
    audio_channel_mask_t channel_mask;  // channel mask
    audio_format_t format;              // audio format
    audio_stream_type_t stream_type;    // stream type
    uint32_t bit_rate;                  // bit rate in bits per second
    int64_t duration_us;                // duration in microseconds, -1 if unknown
    bool has_video;                     // true if stream is tied to a video stream
    bool is_streaming;                  // true if streaming, false if local playback
} audio_offload_info_t;

NuPlayer DataSource/Parser 解析 mp3、flac 等文件得到数据编码信息,并在构造 AudioTrack 实例时作为参数传入,
AudioFlinger 将基于这些编码信息打开 compress_offload 设备。

每个 mp3/flac 文件的编码信息可能是不一样的,比如 a.mp3 文件的编码信息是 mp3&44.1KHZ&16bit… ,
而 b.flac 文件的编码信息是 flac&48KHz&24bit…;
播放 a.mp3 时,AudioFlinger 打开一个配置为 mp3&44.1KHz&16bit… 的 compress_offload 设备,
接着播放 b.flac,就需要关闭之前的 compress_offload 设备,重新打开一个配置为 flac&48KHz&24bit… 的 compress_offload 设备。
所以系统不会提前打开 compress_offload 设备,只有等到播放 mp3、flac 时取到明确的数据编码信息,才基于这些编码信息打开 compress_offload 设备。

编码信息包含很多条目,切换音源时,是否编码信息有一点点不一样,都需要重新打开 compress_offload 设备呢?不能运行时更新信息到 DSP 吗?
其实 stagefright 和 compress_offload 是支持运行期更新某些信息的,也就是无缝切换,
至于是哪些信息,依赖于 DSP 算法实现;有兴趣深入的可以参考 sendMetaDataToHal() 和 compress_set_gapless_metadata()


最后附上 NuPlayer Offload Playback 初始化流程,摘自《Linux Audio Compress Offload Playback and PCM Offload Playback》

1. 	NuPlayer gets the file format metadata based on the type created by the source parser and decoder. 
	For compress offload, DecoderPassThrough is created, causing the software decoder to enter Passthrough mode and not perform decoding.

2. 	NuPlayer calls CanOffloadStream, which in turn calls the APM API isOffloadSupported to decide if a stream can be offloaded.

3. 	NuPlayer creates a new NuPlayerRenderer, which then creates a new AudioTrack. 
	Based on the information NuPlayer has received from the APM, it sets the flag FLAG_OFFLOAD_AUDIO, which passes to NuPlayerRenderer.

4. 	AudioTrack then calls getOutput(), which gets the profile information from the APM. 
	The APM then calls openOutput(), which goes to AudioFlinger. 
	Based on the AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD flag previously set by AudioPlayer, 
	AudioFlinger creates the offload thread.
	AudioFlinger also calls open_output_stream() which goes to the HAL. 
	The HAL then creates the offload callback thread used to notify the write and drain completion events.

5. 	AudioTrack creates the track to be used for this stream.

6. 	NuPlayerRenderer issues a start command to AudioSink, which then calls start_output_stream(). 
	The HAL sends the appropriate mixer control to the back-end (BE) platform driver to configure the output device.

7. 	The HAL opens the compress driver and sends the codec and stream parameters to the compress driver though the IOCTL SNDRV_COMPRESS_SET_PARAMS. 
	The compress driver opens an ASM session accordingly and requests the ASM driver to allocate the buffers to be used for this session.

3.6. AudioFlinger 音频流管理

从 AudioTrack、PlaybackThread、输出流设备三者的关系图中,
我们看到 AudioTrack 把音频流数据送入到对应的 PlaybackThread 中,那么应用进程想控制这些音频流的话,
比如开始播放 start()、停止播放 stop()、暂停播放 pause(),怎么办呢?

注意应用进程与 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 数据混音后写到输出流设备

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

  1. AudioFlinger::PlaybackThread::Track::start:开始播放
    把该 Track 置 ACTIVE 状态,然后添加到 mActiveTracks 向量中,
    最后调用AudioFlinger::PlaybackThread::broadcast_l() 告知 PlaybackThread 情况有变

  2. AudioFlinger::PlaybackThread::Track::stop:停止播放
    把该 Track 置 STOPPED 状态,最后调用 AudioFlinger::PlaybackThread::broadcast_l() 告知 PlaybackThread 情况有变

  3. 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() 中了。

3.7. AudioFlinger 混音器处理

// TODO: …

本文学习自: https://blog.csdn.net/zyuanyun/article/details/60890534

发布了325 篇原创文章 · 获赞 58 · 访问量 9万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 黑客帝国 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览