【Android多媒体(重采样与混频)】从playback流程分析AudioResampler,AudioMixer

本文详细分析了Android多媒体播放流程,重点探讨了AudioMixer的Downmix和Reformat过程,以及AudioResampler在44100 Hz到48000 Hz重采样中的作用。通过log调用流程,展示了MixerThread如何处理音轨的混音和格式转换,强调了防止溢出的处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章大致内容介绍

本文主要讲述播放音乐流程,AudioResampler是如何生效的

  • 播放流程
  • AudioMixer分析
  • 从MixerThread分析

播放流程

在Android中间,如果使用硬解码OffloadThread是不会出现混频和重采样的,但是播放需要软解的音频,则会按需要重采样。
为了方便研究,这里修改软件配置文件,让primary compress_offload仅仅支持8000的采样率播放,音乐播放会去跑MixerThread线程,这个线程上面才会出现重采样。
configs/msm8909/audio_policy_configuration.xml修改如下:

@@ -69,7 +69,7 @@
                 <mixPort name="compressed_offload" role="source"
                          flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD|AUDIO_OUTPUT_FLAG_NON_BLOCKING">
                     <profile name="" format="AUDIO_FORMAT_MP3"
-                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+                             samplingRates="8000"
                              channelMasks="AUDIO_CHANNEL_OUT_STEREO,AUDIO_CHANNEL_OUT_MONO"/>

使用播放流程如下:

这里写图片描述

AudioMixer分析:

我们从log出发,查看代码调用流程

AudioMixer: add track (0)
AudioMixer::prepareForDownmix(0xb4f7d060) with mask 0x3
AudioMixer::prepareForReformat(0xb4f7d060) with format 0x1
AudioMixer: enable(0) AudioMixer: setParameter(VOLUME, VOLUME0: 0000)
AudioMixer: setParameter(VOLUME, VOLUME1: 0000)
AudioMixer: Creating resampler from track 44100 Hz to device 48000 Hz
AudioResampler: resampler load 0 -> 6 MHz due to delta +6 MHz from quality 6
AudioResampler: Create dynamic Resampler = 6
AudioMixer: setParameter(RESAMPLE, SAMPLE_RATE, 44100) AudioMixer: setParameter(TRACK, MIXER_FORMAT, 0x5) AudioMixer: setParameter(TRACK, MAIN_BUFFER, 0xb4f7a000) AudioMixer: mixer configuration change: 1
AudioMixer:activeTracks (00000001) all16BitsStereoNoResample=0, resampling=1, volumeRamp=0
AudioMixer: track__Resample

这段log的调用流程

1,AudioFlinger::MixerThread::checkForNewParameter_l,
2,AudioFlinger::MixerThread::getTrackName_l
3, AudioMixer::getTrackName(audio_channel_mask_t
channelMask,audio_format_t format, int sessionId)
4,调用prepare处理

prepareForDownmix这个函数,是用来处理多通道,将多通道转化,按照条件选择DownMixer或者Remixer,函数如下:



status_t AudioMixer::track_t::prepareForDownmix()
{
    ALOGV("AudioMixer::prepareForDownmix(%p) with mask 0x%x",
            this, channelMask);

    // discard the previous downmixer if there was one
    unprepareForDownmix();
    // MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks
    // are not the same and not handled internally, as mono -> stereo currently is.
    if (channelMask == mMixerChannelMask
            || (channelMask == AUDIO_CHANNEL_OUT_MONO
                    && mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) {
    //mMixerChannelMask 在getTrackName中间被赋值为双声道,判断是否需要downMixer
        return NO_ERROR;
    }
    // DownmixerBufferProvider is only used for position masks.
    if (audio_channel_mask_get_representation(channelMask)
                == AUDIO_CHANNEL_REPRESENTATION_POSITION
            && DownmixerBufferProvider::isMultichannelCapable()) {
     //条件满足channelMask&0x3为0,即声道数大于2,并且系统支持兼容多声道,则初始化相应的bufferprovider用来改变通道数
        DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(channelMask,
                mMixerChannelMask,
                AUDIO_FORMAT_PCM_16_BIT /* TODO: use mMixerInFormat, now only PCM 16 */,
                sampleRate, sessionId, kCopyBufferFrameCount);

        if (pDbp->isValid()) { // if constructor completed properly
            mDownmixRequiresFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix
            downmixerBufferProvider = pDbp;
            reconfigureBufferProviders();
            return NO_ERROR;
        }
        delete pDbp;
    }
    //使用remixer的情况

    // Effect downmixer does not accept the channel conversion.  Let's use our remixer.
    RemixBufferProvider* pRbp = new RemixBufferProvider(channelMask,
            mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount);
    // Remix always finds a conversion whereas Downmixer effect above may fail.
    downmixerBufferProvider = pRbp;
    reconfigureBufferProviders();
    return NO_ERROR;
}

prepareForReformat()判断是否需要格式转换:


status_t AudioMixer::track_t::prepareForReformat()
{
    ALOGV("AudioMixer::prepareForReformat(%p) with format %#x", this, mFormat);
    // discard previous reformatters
    unprepareForReformat();
    // only configure reformatters as needed
    const audio_format_t targetFormat = mDownmixRequiresFormat != AUDIO_FORMAT_INVALID
            ? mDownmixRequiresFormat : mMixerInFormat;
    bool requiresReconfigure = false;
    if (mFormat != targetFormat) {
        mReformatBufferProvider = new ReformatBufferProvider(
                audio_channel_count_from_out_mask(channelMask),
                mFormat,
                targetFormat,
                kCopyBufferFrameCount);
        requiresReconfigure = true;
    }
    if (targetFormat != mMixerInFormat) {
        mPostDownmixReformatBufferProvider = new ReformatBufferProvider(
                audio_channel_count_from_out_mask(mMixerChannelMask),
                targetFormat,
                mMixerInFormat,
                kCopyBufferFrameCount);
        requiresReconfigure = true;
    }
    if (requiresReconfigure) {
        reconfigureBufferProviders();
    }
    return NO_ERROR;
}

从AudioMixer到AudioResampler

调用的时序图如下:
这里写图片描述

voidAudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
它是把各个track的声音数据相加。所谓声音数据,可以认为是一个个的采样点,Android默认支持的采样精度是16bit的,格式为signedPCM,所以每个采样点用有符号的16位数int16_t表示。如果直接加16bit的数据,肯定会造成16bit的值溢出,Android的做法是强转成int32_t,相加,并把和赋值给了32bit的数。注意,相加前乘上了音量,而表达音量的数据类型也是int32_t。这样,就能保证在这个过程中是不会溢出的。

void ditherAndClamp(int32_t* out, constint32_t *sums, size_t c) 将32bit的输出转成16bit,再把右声道放高16bit,左声道放低16bit输出

AudioMixer->AudioResampler传递设置参数处理主要的函数:prepareTracks_l()

这里写图片描述




这个函数中设置重采样参数等操作
// prepareTracks_l() must be called with ThreadBase::mLock held
AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
        Vector< sp<Track> > *tracksToRemove)
{

    mixer_state mixerStatus = MIXER_IDLE;
    // find out which tracks need to be processed
    size_t count = mActiveTracks.size();
    size_t mixedTracks = 0;
    size_t tracksWithEffect = 0;
    // counts only _active_ fast tracks
    size_t fastTracks = 0;
    uint32_t resetMask = 0; // bit mask of fast tracks that need to be reset

    float masterVolume = mMasterVolume;
    bool masterMute = mMasterMute;
#ifdef DOLBY_ENABLE // DOLBY_DAP_PREGAIN
    // The maximum volume of left channel and right channel for pregain calculation.
    uint32_t max_vol = 0;
    float dvlf = 0.0f;
    float dvrf = 0.0f;
#endif // DOLBY_END

    if (masterMute) {
        masterVolume = 0;
    }
    // Delegate master volume control to effect in output mix effect chain if needed
    sp<EffectChain> chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
    if (chain != 0) {
        uint32_t v = (uint32_t)(masterVolume * (1 << 24));
        chain->setVolume_l(&v, &v);
        masterVolume = (float)((v + (
<think>嗯,用户想了解AndroidAudioMixer,我需要先回忆一下相关知识。首先,AudioMixerAndroid音频系统中负责混音的部分,对吧?它应该是在AudioFlinger里面,处理不同音频流的混合。那它的主要功能是什么呢?比如,混合多个音频源,调整音量,应用音效,还有格式转换之类的。 用户可能想知道它的工作原理,比如怎么处理不同的音频流,如何管理缓冲区,还有性能优化方面的问题。比如,AudioMixer支持多声道吗?有没有使用硬件加速?可能还需要提到它如何处理重采样,因为不同音频源的采样率可能不同。 对了,还有AudioMixer的结构,可能分为输入端口、混音核心和输出端口。输入端口对应各个音频流,混音核心执行实际的混合操作,输出端口生成最终的音频数据。需要解释一下每个部分的作用。 另外,AudioMixer的一些高级特性,比如音效处理,音量均衡,动态范围控制,这些也是用户可能感兴趣的。还有性能优化方面,比如NEON指令优化,低延迟处理,这些对开发者来说很重要。 用户可能还想知道在应用开发中如何AudioMixer交互,或者遇到音频问题时如何调试。不过用户的问题主要是介绍,所以重点放在原理和结构上。需要确保用中文解释清楚,避免术语混乱,同时保持技术准确性。可能还需要举一些例子,比如混音的过程,或者音量调整的具体实现方式。</think>Android AudioMixerAndroid 音频系统中的核心组件,负责将多个音频流混合成统一的输出信号。它属于系统级音频处理模块,主要运行在 `AudioFlinger` 服务中。以下是其核心功能实现逻辑的分步解析: --- ### **1. 核心功能** - **多路混音** 同时处理来自不同应用/进程的音频流(如音乐、通知声、通话语音),支持 16/32 位定点或浮点 PCM 格式。 - **动态路由** 根据音频属性(`usage`, `content type`)自动选择输出设备(扬声器/耳机/蓝牙)。 - **音效处理链** 集成均衡器、重低音、虚拟化等音效模块(通过 `AudioEffect` API)。 - **采样率适配** 通过多相滤波器实现 44.1kHz ↔ 48kHz 等采样率转换。 - **通道映射** 支持单声道→立体声上混(Upmix)和立体声→单声道下混(Downmix)。 --- ### **2. 硬件抽象层(HAL)交互** - **Buffer 管理** 使用环形缓冲区 (`FastMixer`) 实现低延迟,通过 `AudioStreamOut` 向 HAL 提交数据。 - **功耗优化** 根据激活的音频流动态调整 CPU 频率,休眠非活跃 DSP 模块。 --- ### **3. 关键数据结构** ```cpp // 音频流描述符 struct AudioBufferProvider::Buffer { void* raw; // PCM 数据指针 size_t frameCount; // 帧数 int64_t timestamp; // 时间戳(纳秒) }; // 混音器配置 struct AudioMixer::Configuration { uint32_t sampleRate; // 输出采样率 audio_channel_mask_t channelMask; // 输出通道掩码 audio_format_t format; // 输出格式(AUDIO_FORMAT_PCM_16BIT 等) }; ``` --- ### **4. 处理流水线** 1. **输入准备** 每个音频流通过 `Track` 对象注册到 `AudioMixer`,设置音量、声道增益、音效链。 2. **重采样阶段** 使用 `AudioResampler` 进行采样率转换(例如 Sinc 插值算法): ```cpp resampler->resample(tempBuffer, outFrameCount, bufferProvider); ``` 3. **格式归一化** 将不同位深的 PCM 统一转换为 32 位浮点,便于后续处理。 4. **声道处理** 执行上混/下混逻辑,例如单声道→立体声扩展: ```cpp for (size_t i = 0; i < frames; i++) { output[2*i] = input[i]; // 左声道 output[2*i+1] = input[i]; // 右声道 } ``` 5. **音效应用** 按顺序调用 `Visualizer`、`BassBoost` 等效果的 `process()` 方法。 6. **混合求和** 使用 SIMD 指令(如 NEON)并行累加所有音轨: ```cpp // 伪代码:四路音频混合 float32x4_t acc = vld1q_f32(output); for (int i = 0; i < numTracks; i++) { float32x4_t data = vld1q_f32(track[i]->data); acc = vmlaq_f32(acc, data, track[i]->volume); } vst1q_f32(output, acc); ``` 7. **限制抖动处理** 防止混音后 clipping,应用 soft limiter: $$ y = \frac{x}{1 + |x|} \quad (\text{其中 } x \text{ 为输入信号}) $$ --- ### **5. 延迟优化策略** - **异步混合** 音频数据准备硬件传输并行,通过双缓冲 (`FrontBuffer`/`BackBuffer`) 实现。 - **实时性保障** 使用 SCHED_FIFO 调度策略,确保混音线程抢占式执行。 - **内存预分配** 启动时预分配内存池,避免运行时动态分配引入延迟。 --- ### **6. 调试手段** - **Audio Policy Log** 通过 `adb shell dumpsys media.audio_policy` 查看活跃混音配置。 - **Perfetto 跟踪** 使用系统级性能分析工具抓取混音线程的 CPU 占用和时序。 --- ### **典型问题场景** 1. **音频卡顿** 检查 `AudioMixer` 线程是否因高优先级任务被抢占。 2. **声道错位** 验证 `audio_channel_mask_t` 设备物理通道的映射关系。 3. **混音失真** 确认输入音轨的增益未超过动态范围(例如浮点 1.0 对应 0dBFS)。 --- 这种设计使得 Android 能够在复杂的多任务环境下,保持低延迟、高保真的音频输出,同时平衡功耗性能。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值