Android Framework Audio Subsystem (08) Proceso de PlaybackThread que mezcla el flujo de datos

Esta serie de artículos enlace Maestro: sub-directorio temático Android Framework Subsistema de audio


Resumen y descripción de puntos clave en este capítulo:

Este capítulo se centra principalmente en el análisis de PlaybackThread en la esquina superior izquierda del mapa mental de arriba. Analiza principalmente el principio de mezcla y el proceso de mezcla.


1 Explicación del principio de mezcla

Cuando se reproducen varias aplicaciones, cada aplicación creará un AudioTrack, y cada AudioTrack transferirá datos a través de la pista de la memoria compartida y el hilo de reproducción. El formato de datos de audio enviado por cada aplicación puede ser diferente, y la tarjeta de sonido solo admite un número fijo de Formato, a menos que el formato enviado sea el formato admitido por la propia tarjeta de sonido, de lo contrario, debe volver a muestrearse / mezclarse (algunas operaciones de mAudioMixer en el hilo de reproducción convierten el formato de audio que no es compatible con el hardware al formato de audio compatible con el hardware. Este proceso se llama pesado. Muestreo). Aquí interpretamos el tipo de audio AudioMixer de la variable mAudioMixer en MixerThread. El código de referencia de AudioMixer es el siguiente:

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 clase AudioMixer aquí se implementa de la siguiente manera:

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

Hay un miembro mstate en mAudioMixer, y la estructura state_t se define de la siguiente manera:

// 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 contiene una función de enlace que apunta a diferentes funciones de procesamiento. Para diferentes situaciones, los ganchos apuntarán a diferentes funciones. Si el puntero de la función de enlace apunta a process__genericNoResampling, habrá un área de caché temporal de outputTemp, y también hay pistas (variables en el miembro mState), cada pista corresponde al AudioTrack de la aplicación, y la definición de la estructura track_t es la siguiente:

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

A través de la comprensión anterior de AudioMixer, state_t, track_t y otros tipos, podemos ver dos tipos de ganchos:

  1. gancho en estado_t mState: gancho total
  2. gancho en track_t track: solo gancho

AudioTrack transfiere datos a MixerThread (subclase PlaybackThread) a través de la memoria compartida, analiza el formato de mTracks en MixerThread para ver si es un formato compatible con el hardware, establece diferentes funciones de enlace (determina si se volverá a muestrear) de acuerdo con diferentes situaciones, y la configuración se completa Después de eso, configure la función de enlace total (en mState), resumida de la siguiente manera:

  1. Determine el enlace: analice los datos de mState.tracks [x] uno por uno, determine las pistas [x] .hook según su formato y luego determine el mState.hook total
  2. Enlace de llamada: simplemente llame al mState.hook total, llamará a cada mState.tracks [x] .hook

El destino de los datos después del remuestreo: hay datos originales en la memoria compartida. Como se mencionó anteriormente en outputTemp, los datos procesados ​​de cada pista ingresan al área de almacenamiento temporal (como el remuestreo). Entonces outputTemp almacena los datos superpuestos después de que se procesa cada pista. Estos datos temporales finalmente se guardan en mainBuffer. MainBuffer en cada pista apunta al mMixerBuffer mixto en PlaybackThread. Los datos en mMixerBuffer se pueden reproducir, pero no se envían directamente al hardware, sino a mSinkbuffer. Por supuesto, mEffectBuffer también se puede usar para procesar efectos de sonido en el sistema Android. Finalmente enviarán el búfer al hardware mSinkbuffer. Si se utiliza el efecto de sonido, la fuente puede ser mEffectBuffer y mMixerBuffer.


2 Análisis de mezcla de código fuente

Aquí, el análisis se basa principalmente en MixerThread, comenzando desde el hilo de reproducción de la clase principal de MixerThread, revisando el código fuente del inicio de AudioPloicyService y el inicio de AudioFlinger, y la pila de análisis del proceso de creación deheadtrhead es la siguiente:

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

Es decir, desde el comienzo de la creación de AudioPolicyService, se crea el hilo de salida correspondiente (MixerThread (most), OffloadThread, DirectOutputThread). Por lo tanto, a partir del análisis del hilo del hilo de reproducción, el código del constructor es el siguiente:

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

La implementación del código en onFirstRef es la siguiente:

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

Después de crear la clase de hilo de reproducción (así como la subclase MixerThread, etc.), el hilo comienza aquí. En el hilo de Android, threadloop es el cuerpo de ejecución real del hilo, y el código se implementa de la siguiente manera:

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

playthread es responsable de crear hilos, pero los métodos clave a analizar aquí se implementan en MixerThread. Los tres métodos clave analizados son prepareTracks_l, threadLoop_mix y threadLoop_write.

2.1 análisis de prepareTracks_l

La implementación del código de MixerThread :: prepareTracks_l es la siguiente:

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

En el proceso de preparación de la mezcla, existen principalmente algunas cosas:

  1. Establezca los parámetros necesarios para la mezcla, incluidos: volumen, búfer de origen, búfer de destino, formato de audio, si desea volver a muestrear, etc.    
  2. Eliminar pistas agregadas a tracksToRemove
  3. Devuelve el estado actual mMixerStatus

2.2 análisis threadLoop_mix

Solo cuando el valor mMixerStatus devuelto por prepareTrack_l es MIXER_TRACK_READY, podemos ingresar threadLoop_mix para mezclar. El código se implementa de la siguiente manera:

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

Con los parámetros establecidos por prepareTrack_l, todo lo que necesita hacer en threadLoop_mix es llamar al método de proceso AudioMixer para mezclar.

2.3 threadLoop_write 分析

threadLoop_write se usa para la salida de audio después de la mezcla, y el código se implementa de la siguiente manera:

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

Continúe analizando PlaybackThread :: threadLoop_write (), la implementación del código es la siguiente:

// 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 Resumen del proceso

Organice el trabajo realizado en threadloop de la siguiente manera:

@ 1 prepareTracks_l: 

  1. 确定 pista habilitada, pista deshabilitada
  2. Para la pista habilitada, configure los parámetros en mState.tracks [x]

@ 2 threadLoop_mix: procesamiento de datos (como remuestreo), mezcla

  1.    Determine el enlace: analice los datos de mState.tracks [x] uno por uno, determine las pistas [x] .hook según su formato y luego determine el mState.hook total
  2.    Enlace de llamada: simplemente llame al mState.hook total, llamará a cada mState.tracks [x] .hook
  3.    Los datos mixtos se colocarán en el BUFFER temporal de mState.outputTemp
  4.    Luego convierta el formato y guárdelo en thread.mMixerBuffer

@ 3 memcpy_by_audio_format: copie datos de thread.mMixerBuffer o thread.mEffectBuffer a thread.mSinkBuffer
@ 4 threadLoop_write: escriba thread.mSinkBuffer a la tarjeta de sonido
@ 5 threadLoop_exit

Publicado 289 artículos originales · elogiados 47 · 30,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/vviccc/article/details/105341277
Recomendado
Clasificación