安卓 Audio Thread 类型

本文解析了Android音频offload机制中offloadThread的创建条件及其与mixer线程的区别,强调offload的简单逻辑,以及它如何绕过audioflinger的设置。通过高通案例探讨了音频场景下的数据路径优化。

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

1
offloadThread线程的创建,其实依赖于audio_policy.conf中是否有配置,如果有配置,则才会有创建该线程的逻辑。
2
offload跟mixer线程的区别,其实主要是基本上没有mixer的处理,直接绕过audioflinger的采样率、format、采样精度、通道数等的转变,所以其实offload的逻辑是更简单的。

参考
高通audio offload学习
Android智能手机中各种音频场景下的audio data path

在 Android 音频系统中,MixerThreadDirectOutputThreadAudioFlinger 管理的两种核心播放线程,分别用于处理不同类型的音频输出场景。

1. MixerThread

1.1总结

定义与作用

MixerThread 是 Android 音频系统的默认混音线程,负责将多个应用程序(AudioTrack)的音频数据进行混合,最终输出到声卡。其特点包括:

  • 多路混音**:支持同时处理多个 AudioTrack 的数据,混合后发送至硬件。
  • 软件音量控制**:通过调整音频数据的幅值实现音量调节,不影响硬件音量设置。
  • 动态管理**:根据活跃的 AudioTrack 状态动态调整混音策略,优化资源使用。
创建机制
  • AudioFlinger::openOutput_l() 中,根据音频输出标志(如 AUDIO_OUTPUT_FLAG_DIRECT)判断是否创建 MixerThread。若未指定独占标志,默认创建 MixerThread
  • 每个物理或虚拟音频设备(如扬声器、蓝牙)对应一个 MixerThread,通过 audio_io_handle_t 标识。
核心功能
  1. 混音处理
    • 遍历所有活跃的 AudioTrack,读取其数据并进行混合。
    • 支持格式转换(如 16bit 转 8bit)和音量叠加计算(data_mix = data1 * volume1 + data2 * volume2)。
  2. 状态管理
    • 根据 mMixerStatus 判断混音状态(如 MIXER_IDLEMIXER_TRACKS_READY),动态调整线程行为。
  3. 低延迟优化
    • 使用非阻塞 I/O(NBAIO)框架写入数据,减少线程阻塞。

1.2 混音原理说明

多个应用程序播放,每个APP端都会创建AudioTrack,每个AudioTrack都会通过共享内存和播放线程的Track传递数据,每个应用发送的音频数据格式可能都不相同,而声卡仅支持固定的几种格式,除非发送的格式就是声卡本身支持的格式,否则这里都需要进行重采样/混音(playbackthread中使用mAudioMixer的一些操作把硬件不支持的音频格式转化为硬件支持的音频格式,这个过程叫做重采样)。这里我们对MixerThread中的mAudioMixer变量类型AudioMixer进行解读,AudioMixer的引用代码如下:

class MixerThread : public PlaybackThread {
{
public:
    MixerThread(const sp<AudioFlinger>& audioFlinger,
                AudioStreamOut* output,
                audio_io_handle_t id,
                audio_devices_t device,
                type_t type = MIXER);
    virtual             ~MixerThread();
    //...
    AudioMixer* mAudioMixer;    // normal mixer
    //...
};

这里的AudioMixer类实现如下:

class AudioMixer
{
public:
    AudioMixer(size_t frameCount, uint32_t sampleRate,uint32_t maxNumTracks = MAX_NUM_TRACKS);
    //...
    //给track使用的hook函数整理
    /*进行重采样*/
    static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
            int32_t* aux);
    /*不做任何处理*/
    static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
    static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
            int32_t* aux);
    static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
            int32_t* aux);
    //...
    // multi-format track hooks
    template <int MIXTYPE, typename TO, typename TI, typename TA>
    static void track__Resample(track_t* t, TO* out, size_t frameCount,
            TO* temp __unused, TA* aux);
    template <int MIXTYPE, typename TO, typename TI, typename TA>
    static void track__NoResample(track_t* t, TO* out, size_t frameCount,
            TO* temp __unused, TA* aux);
    //...
    //给mState使用的hook函数整理
    static void process__validate(state_t* state, int64_t pts);
    /* no-op,如果静音了不做任何处理*/
    static void process__nop(state_t* state, int64_t pts);
    /*如果传递过来的数据,是声卡直接支持的格式,则不需要重新采样*/
    static void process__genericNoResampling(state_t* state, int64_t pts);
    /*如果需要重新采样,则会调用该函数*/
    static void process__genericResampling(state_t* state, int64_t pts);    
    static void process__OneTrack16BitsStereoNoResampling(state_t* state,int64_t pts);
    //...
    // hook types
    enum {
        PROCESSTYPE_NORESAMPLEONETRACK,
    };
    enum {
        TRACKTYPE_NOP,
        TRACKTYPE_RESAMPLE,
        TRACKTYPE_NORESAMPLE,
        TRACKTYPE_NORESAMPLEMONO,
    };
    //...
    state_t mState __attribute__((aligned(32)));
    //...
}

mAudioMixer中存在成员mstate,state_t结构体的定义如下:

// pad to 32-bytes to fill cache line
struct state_t {
    uint32_t        enabledTracks;
    uint32_t        needsChanged;
    size_t          frameCount;
    process_hook_t  hook;   // one of process__*, never NULL
    int32_t         *outputTemp;
    int32_t         *resampleTemp;
    NBLog::Writer*  mLog;
    int32_t         reserved[1];
    // FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS
    track_t         tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
};

mState包含了一个hook函数,其会指向不同的处理函数。hook针对不同的情况,会指向不同的函数。如果hook函数的指针指向process__genericNoResampling,会有一个outputTemp的临时缓存区,同时这里还有一个tracks(mState成员中变量),每个track跟应用程序的AudioTrack相对应,track_t结构体的定义如下:

struct track_t {
    uint32_t    needs;
    union {
    int16_t     volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero)
    int32_t     volumeRL;
    };
 
    int32_t     prevVolume[MAX_NUM_VOLUMES];
    int32_t     volumeInc[MAX_NUM_VOLUMES];
    int32_t     auxInc;
    int32_t     prevAuxLevel;
    int16_t     auxLevel;       // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performance
    uint16_t    frameCount;
    uint8_t     channelCount;   // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK)
    uint8_t     unused_padding; // formerly format, was always 16
    uint16_t    enabled;        // actually bool
    audio_channel_mask_t channelMask;
    AudioBufferProvider*                bufferProvider;
 
    // 16-byte boundary
 
    mutable AudioBufferProvider::Buffer buffer; // 8 bytes
 
    hook_t      hook; //指向不同的处理函数
    const void* in;  // current location in buffer
 
    // 16-byte boundary
 
    AudioResampler*     resampler; //重采样器
    uint32_t            sampleRate;
    int32_t*           mainBuffer;//保存重采样之后的最终数据
    int32_t*           auxBuffer;
    //...
    bool        needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
    bool        setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
    bool        doesResample() const { return resampler != NULL; }
    void        resetResampler() { if (resampler != NULL) resampler->reset(); }
    void        adjustVolumeRamp(bool aux, bool useFloat = false);
    size_t      getUnreleasedFrames() const { return resampler != NULL ?
                                                resampler->getUnreleasedFrames() : 0; };
};

通过上面对AudioMixer、state_t、track_t等类型的了解,我们可以看到两种hook:

  1. state_t mState中的hook:总的hook。

  2. track_t track中的hook:单个的hook。

AudioTrack通过共享内存将数据传递给MixerThread(PlaybackThread子类),对MixerThread中的mTracks进行格式分析,看是否为硬件支持的格式,根据不同情况设定不同的hook函数(确定是否重采样),设置完成之后,再去设置总的hook函数(mState中),总结如下:

  1. 确定hook:逐个分析mState.tracks[x]的数据, 根据它的格式确定tracks[x].hook,再确定总的mState.hook

  2. 调用hook:调用总的mState.hook即可, 它会再去调用每一个mState.tracks[x].hook

重采样之后的数据去向说明:在共享内存之中有原始数据,前面提到过outputTemp,各个track其处理之后的数据进入临时缓存区(比如重采样)。所以outputTemp存放的是各个track处理完成之后叠加的数据。这些临时的数据,最终保存到mainBuffer中。每个tracks中的mainBuffer指向PlaybackThread中混合的mMixerBuffer,mMixerBuffer中的数据可以进行播放,但并没有直接发送给硬件,而是发送给mSinkbuffer。当然在android系统中还可以使用mEffectBuffer对声音进行音效处理。他们最后会把buffer发送给硬件mSinkbuffer,如果使用音效,其来源可能是mEffectBuffer与mMixerBuffer。

1.3.混音源码分析

这里主要以MixerThread为核心进行分析,从MixerThread的父类playbackthread线程开始分析,回顾 AudioPloicyService启动和AudioFlinger启动的源码,playbacktrhead的创建流程分析栈如下:

AudioPolicyService::onFirstRef(...)
->AudioPolicyService::createAudioPolicyManager(...);
-->AudioPolicyManager(...);
--->AudioFlinger::loadHwModule(...);
--->AudioFlinger::openOutput(...);
---->AudioFlinger::openOutput_l(...);
----->创建thread(MixerThread(这里要注意:因为第一次并未传递flag,因此不会创建OffloadThread、DirectOutputThread))
----->mPlaybackThreads.add(*output, thread);将thread加入到mPlaybackThreads中

即从AudioPolicyService创建之初就开始创建对应output的线程(创建MixerThread,这里要注意:因为第一次并未传递flag,所以不会创建OffloadThread和DirectOutputThread线程)因此这里从playbackthread线程开始分析,构造器代码如下:

AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger,
        //...
        mLatchDValid(false), mLatchQValid(false)
{
    //设置mName
    snprintf(mName, kNameLength, "AudioOut_%X", id);
    //...
}

在onFirstRef中代码实现如下:

void AudioFlinger::PlaybackThread::onFirstRef()
{
    run(mName, ANDROID_PRIORITY_URGENT_AUDIO);
}

在创建playbackthread类(以及子类MixerThread等…)这里就开始启动线程了。在android线程中threadloop为线程真正的执行体,代码实现如下:

bool AudioFlinger::PlaybackThread::threadLoop()
{
    //...
    while (!exitPending())
    {
        cpuStats.sample(myName);
        Vector< sp<EffectChain> > effectChains;
        { // scope for mLock
            Mutex::Autolock _l(mLock);
            /*处理配置信息*/
            processConfigEvents_l();
            //...
            //如果数据为0,则让声卡进入休眠状态
            if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
                                   isSuspended()) {
                // put audio hardware into standby after short delay
                if (shouldStandby_l()) {
                    //声卡休眠
                    threadLoop_standby();
                    mStandby = true;
                }
 
                if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
                    IPCThreadState::self()->flushCommands();
                    //...
                    //线程休眠点,直到AudioTrack发送广播唤醒
                    mWaitWorkCV.wait(mLock);
                    //...
 
                    continue;
                }
            }
            //关键点1:混音前的准备工作
            mMixerStatus = prepareTracks_l(&tracksToRemove);
            //...
            lockEffectChains_l(effectChains);
        } // mLock scope ends
 
        if (mBytesRemaining == 0) {
            mCurrentWriteLength = 0;
            if (mMixerStatus == MIXER_TRACKS_READY) {
                //关键点2:混音
                threadLoop_mix();
            } else if ((mMixerStatus != MIXER_DRAIN_TRACK)
                        && (mMixerStatus != MIXER_DRAIN_ALL)) {
                threadLoop_sleepTime();
                if (sleepTime == 0) {
                    mCurrentWriteLength = mSinkBufferSize;
                }
            }
            if (mMixerBufferValid) {
                void *buffer = mEffectBufferValid ? mEffectBuffer : mSinkBuffer;
                audio_format_t format = mEffectBufferValid ? mEffectBufferFormat : mFormat;
                //把数据从thread.mMixerBuffer复制到thread.mSinkBuffer
                memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat,
                        mNormalFrameCount * mChannelCount);
            }
            //...
        }
        //...
        if (mEffectBufferValid) {
            //把数据从thread.mEffectBuffer复制到thread.mSinkBuffer
            memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat,
                    mNormalFrameCount * mChannelCount);
        }
 
        // enable changes in effect chain
        unlockEffectChains(effectChains);
 
        if (!waitingAsyncCallback()) {
            // sleepTime == 0 means we must write to audio hardware
            if (sleepTime == 0) {
                if (mBytesRemaining) {
                    //关键点3:音频输出
                    ssize_t ret = threadLoop_write();
                    //...
                } else if ((mMixerStatus == MIXER_DRAIN_TRACK) ||
                        (mMixerStatus == MIXER_DRAIN_ALL)) {
                    threadLoop_drain();
                }
                //...
            } else {
                usleep(sleepTime);
            }
        }
        //...
    }
    threadLoop_exit();
 
    if (!mStandby) {
        threadLoop_standby();
        mStandby = true;
    }
    //...
    return false;
}

playbackthread负责创建线程,但这里接下来要分析的关键方法都在MixerThread中实现,分析三个关键方法为prepareTracks_l、 threadLoop_mix、threadLoop_write。

1.3.1 prepareTracks_l分析

MixerThread::prepareTracks_l()的代码实现如下:

AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
        Vector< sp<Track> > *tracksToRemove)
{
    //默认为空闲状态
    mixer_state mixerStatus = MIXER_IDLE;
    size_t count = mActiveTracks.size();
    //...
    //对于所有在mActiveTracks里面的Track,都需要进行设置
    for (size_t i=0 ; i<count ; i++) {
        const sp<Track> t = mActiveTracks[i].promote();
        if (t == 0) {
            continue;
        }
        // this const just means the local variable doesn't change
        Track* const track = t.get();
 
        // fastTrack不会在这里进行混音,略过
        if (track->isFastTrack()) {
            //...
        }
 
        {   // local variable scope to avoid goto warning
        audio_track_cblk_t* cblk = track->cblk();
        /* 获取track的name,这是个索引,AudioMixer会最多维护32个track,分别对应int的32个位,
         * 如果track的name还没定下来的话,会自行选择一个空位
         */
        int name = track->name();
        size_t desiredFrames;
        uint32_t sr = track->sampleRate();
        if (sr == mSampleRate) {
            desiredFrames = mNormalFrameCount;
        } else {
            desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1;
            desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
        }
        uint32_t minFrames = 1;
        if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
                (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
            minFrames = desiredFrames;
        }
        //混音的状态下,frameReady = 1,那么会进入下面的条件,进行AudioMixer参数设置
        size_t framesReady = track->framesReady();
        if ((framesReady >= minFrames) && track->isReady() &&
                !track->isPaused() && !track->isTerminated())
        {
            //音量参数设置
            //...
            //设置AudioMixer参数
            mAudioMixer->setBufferProvider(name, track);//源Buffer
            mAudioMixer->enable(name);//使能该track,可以混音
            //音轨 左 右 aux
            mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);
            mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf);
            mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, &vaf);
            //音频格式
            mAudioMixer->setParameter(
                name,
                AudioMixer::TRACK,
                AudioMixer::FORMAT, (void *)track->format());
            //音轨mask,哪个需要或者不需要混音
            mAudioMixer->setParameter(
                name,
                AudioMixer::TRACK,
                AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask());
            
            mAudioMixer->setParameter(
                name,
                AudioMixer::TRACK,
                AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mChannelMask);
            // limit track sample rate to 2 x output sample rate, which changes at re-configuration
            uint32_t maxSampleRate = mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX;
            uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate();
            if (reqSampleRate == 0) {
                reqSampleRate = mSampleRate;
            } else if (reqSampleRate > maxSampleRate) {
                reqSampleRate = maxSampleRate;
            }
            /*进行重采样
             *注意:安卓的MixerThread会对所有的track进行重采样
             *那么在混音的时候会调用重采样的混音方法。
             */
            mAudioMixer->setParameter(
                name,
                AudioMixer::RESAMPLE,
                AudioMixer::SAMPLE_RATE,
                (void *)(uintptr_t)reqSampleRate);
            if (mMixerBufferEnabled
                    && (track->mainBuffer() == mSinkBuffer
                            || track->mainBuffer() == mMixerBuffer)) {
                mAudioMixer->setParameter(
                        name,
                        AudioMixer::TRACK,
                        AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
                //目的buffer
                mAudioMixer->setParameter(
                        name,
                        AudioMixer::TRACK,
                        AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer);
                // TODO: override track->mainBuffer()?
                mMixerBufferValid = true;
            } else {
                //...
            }
            //aux buffer
            mAudioMixer->setParameter(
                name,
                AudioMixer::TRACK,
                AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());
 
            // reset retry count
            track->mRetryCount = kMaxTrackRetries;
            if (mMixerStatusIgnoringFastTracks != MIXER_TRACKS_READY ||
                    mixerStatus != MIXER_TRACKS_ENABLED) {
                //状态为 ready表示可以混音
                mixerStatus = MIXER_TRACKS_READY;
            }
        } else {
            //...
        }
 
        }   // local variable scope to avoid goto warning
track_is_ready: ;
 
    }
    //...
    //从mActiveTracks删除需要移除的track
    removeTracks_l(*tracksToRemove);
    //...
    if (fastTracks > 0) {
        //正常混音准备时,这里返回的是MIXER_TRACK_READY
        mixerStatus = MIXER_TRACKS_READY;
    }
    return mixerStatus;
}

在准备混音的过程主要做了几件事情:

  • 1.设置混音所需要的参数,包括:音量,混音的源buffer,目的buffer,音频格式,是否重采样等。
  • 2.删除被加入tracksToRemove的track。
  • 3.返回当前状态mMixerStatus。
1.3.2 threadLoop_mix分析

在prepareTrack_l返回的mMixerStatus值为MIXER_TRACK_READY时,才可以进入threadLoop_mix进行混音。代码实现如下:

void AudioFlinger::MixerThread::threadLoop_mix()
{
    int64_t pts;
    status_t status = INVALID_OPERATION;
    //获取timestamps,即输出时间戳,用于seek到源buffer的某个位置进行混音
    if (mNormalSink != 0) {
        status = mNormalSink->getNextWriteTimestamp(&pts);
    } else {
        status = mOutputSink->getNextWriteTimestamp(&pts);
    }
 
    if (status != NO_ERROR) {
        pts = AudioBufferProvider::kInvalidPTS;
    }
 
    //AudioMixer混音
    mAudioMixer->process(pts);
    //混音了多少音频数据
    mCurrentWriteLength = mSinkBufferSize;
    if ((sleepTime == 0) && (sleepTimeShift > 0)) {
        sleepTimeShift--;
    }
    //等不需要睡眠时直接输出音频
    sleepTime = 0;
    //待机时间更新
    standbyTime = systemTime() + standbyDelay;
}

有了prepareTrack_l设置的参数,在threadLoop_mix中需要做的就是调用AudioMixer的process方法进行混音。

1.3.3 threadLoop_write分析

threadLoop_write用于混音后的音频输出,代码实现如下:

ssize_t AudioFlinger::MixerThread::threadLoop_write()
{
    if (mFastMixer != 0) {
        //...fastMixer处理
    }
    return PlaybackThread::threadLoop_write();
}

继续分析PlaybackThread::threadLoop_write(),代码实现如下:

// shared by MIXER and DIRECT, overridden by DUPLICATING
ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
{
    // FIXME rewrite to reduce number of system calls
    mLastWriteTime = systemTime();
    mInWrite = true;
    ssize_t bytesWritten;
    const size_t offset = mCurrentWriteLength - mBytesRemaining;
 
    // If an NBAIO sink is present, use it to write the normal mixer's submix
    if (mNormalSink != 0) {
        //将Buffer写到声卡上
        ssize_t framesWritten = mNormalSink->write((char *)mSinkBuffer + offset, count);
        //...
    // otherwise use the HAL / AudioStreamOut directly
    } else {
        //如果用fastMixer的话其实会走该分支,先忽略
        // Direct output and offload threads
        bytesWritten = mOutput->stream->write(mOutput->stream,
                                                   (char *)mSinkBuffer + offset, mBytesRemaining);
        //...
    }
    //...
    mNumWrites++;
    mInWrite = false;
    mStandby = false;
    return bytesWritten;//返回输出的音频数据量
}

1.3.4 流程总结

整理下threadloop中所做的工作,如下所示:

1.3.4.1 prepareTracks_l
  1. 确定enabled track, disabled track;

  2. 对于enabled track, 设置mState.tracks[x]中的参数。

1.3.4.2 threadLoop_mix : 处理数据(比如重采样)、混音

确定hook:逐个分析mState.tracks[x]的数据, 根据它的格式确定tracks[x].hook,再确定总的mState.hook;

调用hook:调用总的mState.hook即可, 它会再去调用每一个mState.tracks[x].hook;

混音后的数据会放在mState.outputTemp临时BUFFER中;

然后转换格式后存入 thread.mMixerBuffer。

1.3.4.3 memcpy_by_audio_format

把数据从thread.mMixerBuffer或thread.mEffectBuffer复制到thread.mSinkBuffer

1.3.4.4 threadLoop_write

把thread.mSinkBuffer写到声卡上

1.3.4.5 threadLoop_exit

2. DirectOutputThread

2.1 总结

定义与作用

DirectOutputThread 用于处理独占式音频输出,特点包括:

  • 独占硬件通道**:同一时间仅允许一个 AudioTrack 使用,无需混音,直接写入硬件(如 HDMI、USB 声卡)。
  • 硬件音量控制**:直接设置声卡硬件音量,而非调整数据幅值。
创建条件
  • 当应用程序指定 AUDIO_OUTPUT_FLAG_DIRECT 标志时,AudioFlinger 会创建 DirectOutputThread
  • 常见场景包括高清音频传输(如 192kHz 采样率)或需要低延迟的独占设备。
核心功能
  1. 直接输出
    • 将单个 AudioTrack 的数据直接写入硬件,跳过混音步骤。
    • 支持高保真音频格式(如 PCM 24bit)和特殊声道配置(如 7.1 环绕声)。
  2. 硬件参数同步
    • 通过 HAL 层接口(如 audio_stream_out_t)直接控制硬件参数(如采样率、声道数)。

3. 区别与应用场景

特性MixerThreadDirectOutputThread
数据源数量多路 AudioTrack 混合单路 AudioTrack 独占
音量控制软件调节(数据幅值)硬件调节(直接设置声卡)
延迟较高(混音开销)较低(无混音步骤)
适用场景常规应用(音乐、通知等)高保真音频(HDMI)、低延迟游戏音效
标志位默认或无特殊标志AUDIO_OUTPUT_FLAG_DIRECT

4. 对比

  1. 线程启动
    • 两者均继承自 PlaybackThread,在首次强引用(onFirstRef)时启动线程循环(threadLoop)。
  2. 数据写入
    • MixerThread 使用 mOutputSink(如 AudioStreamOutSink)写入混合后的数据。
    • DirectOutputThread 直接调用 HAL 层的 write() 接口。
  3. 资源管理
    • MixerThread 动态管理 mActiveTracks,移除已完成播放的 Track
    • DirectOutputThread 无混音逻辑,仅维护单个活跃 Track
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值