Android Framework Audio Subsystem (08) Processus PlaybackThread mélangeant le flux de données

Cette série d'articles lien Master: sous-répertoire thématique cadre Android classe Sous - système audio


Résumé et description des points clés de ce chapitre:

Ce chapitre se concentre principalement sur l'analyse de PlaybackThread dans le coin supérieur gauche de la carte mentale ci-dessus. Il analyse principalement le principe de mélange et le processus de mélange.


1 Explication du principe de mélange

Lorsque plusieurs applications sont lues, chaque application crée un AudioTrack et chaque AudioTrack transfère des données via la piste de la mémoire partagée et le fil de lecture. Le format des données audio envoyé par chaque application peut être différent et la carte son ne prend en charge qu'un nombre fixe de Format, sauf si le format envoyé est le format pris en charge par la carte son elle-même, sinon il doit être rééchantillonné / mixé (certaines opérations de mAudioMixer en lecture-lecture convertissent le format audio non pris en charge par le matériel au format audio pris en charge par le matériel. Ce processus est appelé lourd Échantillonnage). Ici, nous interprétons le type de variable AudioMixer de mAudioMixer dans MixerThread. Le code de référence d'AudioMixer est le suivant:

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
    //...
};

La classe AudioMixer ici est implémentée comme suit:

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)));
    //...
}

Il existe un membre mstate dans mAudioMixer, et la structure state_t est définie comme suit:

// 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 contient une fonction hook qui pointe vers différentes fonctions de traitement. Pour différentes situations, les crochets pointeront vers différentes fonctions. Si le pointeur de la fonction hook pointe vers process__genericNoResampling, il y aura une zone de cache temporaire de outputTemp, et il y a aussi des pistes (variables dans le membre mState). Chaque piste correspond au AudioTrack de l'application. La définition de la structure track_t est la suivante:

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; };
};

Grâce à la compréhension ci-dessus d'AudioMixer, state_t, track_t et d'autres types, nous pouvons voir deux types de crochets:

  1. hook in state_t mState: total hook
  2. hook in track_t track: crochet simple

AudioTrack transfère les données vers MixerThread (sous-classe PlaybackThread) via la mémoire partagée, analyse le format des mTracks dans MixerThread pour voir s'il s'agit d'un format pris en charge par le matériel, définit différentes fonctions de raccordement (déterminez s'il faut rééchantillonner) en fonction des différentes situations et le réglage est terminé Après cela, définissez la fonction de hook total (dans mState), résumée comme suit:

  1. Déterminez le hook: analysez les données de mState.tracks [x] une par une, déterminez les pistes [x] .hook en fonction de son format, puis déterminez le mState.hook total
  2. Appeler le crochet: il suffit d'appeler le mState.hook total, il appellera chaque mState.tracks [x] .hook

La destination des données après rééchantillonnage: il y a des données originales dans la mémoire partagée Comme mentionné précédemment dans outputTemp, les données traitées de chaque piste entrent dans la zone tampon temporaire (comme le rééchantillonnage). OutputTemp stocke donc les données superposées après le traitement de chaque piste. Ces données temporaires sont finalement enregistrées dans mainBuffer. Le mainBuffer de chaque piste pointe vers le mMixerBuffer mixte dans PlaybackThread. Les données dans mMixerBuffer peuvent être lues, mais elles ne sont pas envoyées directement au matériel, mais à mSinkbuffer. Bien sûr, mEffectBuffer peut également être utilisé pour traiter les effets sonores dans le système Android. Ils enverront finalement le tampon au mSinkbuffer matériel.Si l'effet sonore est utilisé, la source peut être mEffectBuffer et mMixerBuffer.


2 Analyse du mélange du code source

Ici, l'analyse est principalement basée sur MixerThread, en commençant par le thread de lecture de la classe parent de MixerThread, en examinant le code source du démarrage AudioPloicyService et du démarrage AudioFlinger, et la pile d'analyse du processus de création de playbacktrhead est la suivante:

AudioPolicyService::onFirstRef(...)
->AudioPolicyService::createAudioPolicyManager(...);
-->AudioPolicyManager(...);
--->AudioFlinger::loadHwModule(...);
--->AudioFlinger::openOutput(...);
---->AudioFlinger::openOutput_l(...);
----->创建thread(MixerThread(多数)、OffloadThread、DirectOutputThread中的一个)
----->mPlaybackThreads.add(*output, thread);将thread加入到mPlaybackThreads中

Autrement dit, dès le début de la création d'AudioPolicyService, les threads correspondant à la sortie (l'un parmi MixerThread (la plupart), OffloadThread et DirectOutputThread) sont créés. Par conséquent, à partir de l'analyse du thread playbackthread, le code constructeur est le suivant:

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

L'implémentation du code dans onFirstRef est la suivante:

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

Après avoir créé la classe playbackthread (ainsi que la sous-classe MixerThread, etc.), le thread commence ici. Dans le thread Android, threadloop est le véritable corps d'exécution du thread, et le code est implémenté comme suit:

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 est responsable de la création de threads, mais les méthodes clés à analyser ici sont toutes implémentées dans MixerThread. Les trois méthodes clés analysées sont prepareTracks_l, threadLoop_mix et threadLoop_write.

2.1 analyse de prepareTracks_l

L'implémentation du code de MixerThread :: prepareTracks_l est la suivante:

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;
}

Dans le processus de préparation du mélange, il y a principalement quelques éléments:

  1. Définissez les paramètres requis pour le mixage, notamment: volume, tampon source, tampon de destination, format audio, rééchantillonnage, etc.    
  2. Supprimer les pistes ajoutées à tracksToRemove
  3. Renvoie le statut actuel mMixerStatus

2.2 Analyse de threadLoop_mix

Ce n'est que lorsque la valeur mMixerStatus retournée par prepareTrack_l est MIXER_TRACK_READY, que nous pouvons entrer threadLoop_mix pour le mixage. Le code est implémenté comme suit:

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;
}

Avec les paramètres définis par prepareTrack_l, tout ce que vous avez à faire dans threadLoop_mix est d'appeler la méthode de processus AudioMixer pour mixer.

2.3 threadLoop_write 分析

threadLoop_write est utilisé pour la sortie audio après le mixage, et le code est implémenté comme suit:

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

Continuez d'analyser PlaybackThread :: threadLoop_write (), l'implémentation du code est la suivante:

// 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;//返回输出的音频数据量
}

2.4 Résumé du processus

Organisez le travail effectué dans threadloop comme suit:

@ 1 prepareTracks_l: 

  1. 确定 piste activée, piste désactivée
  2. Pour la piste activée, définissez les paramètres dans mState.tracks [x]

@ 2 threadLoop_mix: traitement des données (comme le rééchantillonnage), mixage

  1.    Déterminez le hook: analysez les données de mState.tracks [x] une par une, déterminez les pistes [x] .hook en fonction de son format, puis déterminez le mState.hook total
  2.    Appeler le crochet: il suffit d'appeler le mState.hook total, il appellera chaque mState.tracks [x] .hook
  3.    Les données mixtes seront placées dans le TAMPON temporaire de mState.outputTemp
  4.    Convertissez ensuite le format et stockez-le dans thread.mMixerBuffer

@ 3 memcpy_by_audio_format: copier les données de thread.mMixerBuffer ou thread.mEffectBuffer vers thread.mSinkBuffer
@ 4 threadLoop_write: écrire thread.mSinkBuffer sur la carte son
@ 5 threadLoop_exit

Publié 289 articles originaux · loué 47 · 30 000+ vues

Je suppose que tu aimes

Origine blog.csdn.net/vviccc/article/details/105341277
conseillé
Classement