Subsistema de audio Android Framework (07) Transferencia de datos AudioTrack

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 transferencia de datos de sub-rama de la parte AudioTrack del análisis de proceso en la esquina superior izquierda del mapa mental anterior. Este capítulo analiza principalmente los dos modos de AudioTrack y cómo establecer la memoria compartida entre AudioTrack de la aplicación y la pista de mTracks en el thread de reproducción.


1 Crear memoria compartida en AudioTrack

1.1 Dos modos de AudioTrack

La aplicación crea AudioTrack, que corresponderá a la pista creada por PlaybackThread en AudioFlinger. Hay dos modos para que la APLICACIÓN proporcione datos de audio a AudioTrack: provisión única (modo MODE_STATIC), mientras se reproduce (modo MODE_STREAM). Se adopta el método de memoria compartida. Los dos modos son diferentes de dos maneras:

@ 1 2 modos de memoria compartida

  1. Modo MODE_STATIC: Proporcione datos por adelantado una vez. Use la aplicación para crear memoria compartida, y la aplicación completará los datos de una vez. Los datos como el hilo de reproducción se construyen, y los datos se pueden usar directamente sin sincronización de datos. El trabajo correspondiente de reproducciónThread es: Obtención de los datos que contienen getBuffer (la aplicación puede enviar muchos datos en la memoria compartida al mismo tiempo, y el thread de reproducción debe reproducirse varias veces). Una vez completado, suelte el búfer.
  2. Modo MODE_STREAM: Proporciona mientras juegas. Utilice el hilo de reproducción para crear memoria compartida. La aplicación utiliza getBuffer para obtener el búfer, y después de llenar los datos, se usa releaseBuffer para liberar el búfer. playbackThread también debe reproducirse varias veces. Sin embargo, el mecanismo de memoria intermedia de anillo se usa aquí para transferir datos continuamente. Una vez completado, suelte el búfer.

1.2 Revisión del constructor de AudioTrack

A continuación, comenzamos a analizar el código basado en esto. Aquí comenzamos a analizar los dos modos por separado, comenzando con la creación del objeto AudioTrack en la capa Java anterior, que luego conduce a la creación del objeto AudioTrack en la capa Native. Analice el AudioTrack de la capa Java. De acuerdo con el análisis en la sección anterior, revisamos la pila de llamadas:

Java::AudioTrack->Java::native_setup->JNI转换->android_media_AudioTrack_setup

Continuamos analizando desde la implementación de android_media_AudioTrack_setup, el código es el siguiente:

static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
        jobject jaa,
        jint sampleRateInHertz, jint javaChannelMask,
        jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {
	//...
    //关键点1:创建native AudioTrack对象
    sp<AudioTrack> lpTrack = new AudioTrack();
	//...
    switch (memoryMode) {//这里开始针对 两种模式MODE_STREAM 和 MODE_STATIC进行不同参数的设置
    case MODE_STREAM:
        //关键点2.1:set方法,设置参数
        //注意:这里APP不分配内存而是在后面的playbackthread中分配
        //因此share memory的值为“空”
        status = lpTrack->set(
                AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
                sampleRateInHertz,
                format,// word length, PCM
                nativeChannelMask,
                frameCount,
                AUDIO_OUTPUT_FLAG_NONE,
                audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
                0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
                0,// shared mem,
                true,// thread can call Java
                sessionId,// audio session ID
                AudioTrack::TRANSFER_SYNC,
                NULL,                         // default offloadInfo
                -1, -1,                       // default uid, pid values
                paa);
        break;
    case MODE_STATIC:
        //应用端申请共享内存
        if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
            ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
            goto native_init_failure;
        }
        //关键点2.2:set方法,设置参数
        //因此share memory的值为 应用端申请共享内存 首地址
        status = lpTrack->set(
                AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
                sampleRateInHertz,
                format,// word length, PCM
                nativeChannelMask,
                frameCount,
                AUDIO_OUTPUT_FLAG_NONE,
                audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
                0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
                lpJniStorage->mMemBase,// shared mem
                true,// thread can call Java
                sessionId,// audio session ID
                AudioTrack::TRANSFER_SHARED,
                NULL,                         // default offloadInfo
                -1, -1,                       // default uid, pid values
                paa);
        break;
	//...
    default:
        ALOGE("Unknown mode %d", memoryMode);
        goto native_init_failure;
    }
	//...
    return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}

En resumen, la configuración del modo aquí se originó en la capa Java, al ingresar a la capa C ++:

  1. Si el modo es MODE_STATIC: primero solicite memoria (método allocSharedMem), y luego ejecute el método set.
  2. Si el modo es MODE_STREAM: ejecute directamente el método establecido, y el hilo de reproducción se aplicará detrás de la memoria.

1.3 Análisis de la operación de memoria compartida en AudioTrack

Según el análisis de la sección anterior, revisamos la pila de llamadas:

AudioTrack::set->AudioTrack::createTrack_l

En la función createTrack_l, las operaciones relacionadas con la memoria compartida son:

status_t AudioTrack::createTrack_l()
{
    const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
    //...
    // Starting address of buffers in shared memory.  If there is a shared buffer, buffers
    // is the value of pointer() for the shared buffer, otherwise buffers points
    // immediately after the control block.  This address is for the mapping within client
    // address space.  AudioFlinger::TrackBase::mBuffer is for the server address space.
    void* buffers;
    if (mSharedBuffer == 0) {
        //指向 playbackthread提供的Buffer
        buffers = (char*)cblk + sizeof(audio_track_cblk_t);
    } else {
        //指向应用端APP提供的Buffer
        buffers = mSharedBuffer->pointer();
    }
    //...
    /* update proxy,APP的AudioTack 与Thread中的Track建立共享内存,这里的
     * AudioTrackClientProxy和StaticAudioTrackClientProxy用来管理Buffer
     */
    if (mSharedBuffer == 0) {
        mStaticProxy.clear();
        mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
    } else {
        mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
        mProxy = mStaticProxy;
    }
    //...

    return NO_ERROR;
    }
    //...
    return status;
}

Según el análisis en la sección anterior, sabemos que la creación de AudioTrack en el lado de la aplicación necesariamente significa la creación de la pista en AudioFlinger :: PlaybackThread. Entonces, enfóquese en analizar el objeto Track de PlaybackThread.


2 PlaybackThread Track establece memoria compartida

El seguimiento aquí se hereda de TrackBase, y hay contenidos importantes sobre la administración de memoria compartida que necesitamos analizar en TrackBase. Aquí nos centramos principalmente en sharedBuffer, el código es el siguiente:

// TrackBase constructor must be called with AudioFlinger::mLock held
AudioFlinger::ThreadBase::TrackBase::TrackBase(
            ThreadBase *thread,
            const sp<Client>& client,
            //...
            alloc_type alloc,
            track_type type)
    :   RefBase(),
        mThread(thread),
        mClient(client),
        mCblk(NULL),
        //...
{
    // if the caller is us, trust the specified uid
    if (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) {
        int newclientUid = IPCThreadState::self()->getCallingUid();
        if (clientUid != -1 && clientUid != newclientUid) {
            ALOGW("uid %d tried to pass itself off as %d", newclientUid, clientUid);
        }
        clientUid = newclientUid;
    }
	
    mUid = clientUid;
    size_t size = sizeof(audio_track_cblk_t);//头部结构体
    size_t bufferSize = (buffer == NULL ? roundup(frameCount) : frameCount) * mFrameSize;
    if (buffer == NULL && alloc == ALLOC_CBLK) {
        size += bufferSize;
    }

    if (client != 0) {
        //若APP端提供Buffer,则这里只分配CBLK,否则分配CBLK+Buffer
        mCblkMemory = client->heap()->allocate(size);
        if (mCblkMemory == 0 ||
                (mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer())) == NULL) {
            client->heap()->dump("AudioTrack");
            mCblkMemory.clear();
            return;
        }
    } else {
        // this syntax avoids calling the audio_track_cblk_t constructor twice
        mCblk = (audio_track_cblk_t *) new uint8_t[size];
        // assume mCblk != NULL
    }

    // construct the shared structure in-place.
    if (mCblk != NULL) {
        new(mCblk) audio_track_cblk_t();
        switch (alloc) {
        case ALLOC_READONLY: {
            const sp<MemoryDealer> roHeap(thread->readOnlyHeap());
            if (roHeap == 0 ||
                    (mBufferMemory = roHeap->allocate(bufferSize)) == 0 ||
                    (mBuffer = mBufferMemory->pointer()) == NULL) {
                ALOGE("not enough memory for read-only buffer size=%zu", bufferSize);
                if (roHeap != 0) {
                    roHeap->dump("buffer");
                }
                mCblkMemory.clear();
                mBufferMemory.clear();
                return;
            }
            memset(mBuffer, 0, bufferSize);
            } break;
        case ALLOC_PIPE:
            mBufferMemory = thread->pipeMemory();
            mBuffer = NULL;
            break;
        case ALLOC_CBLK:
            // buffer的初始化
            if (buffer == NULL) {//指向playbackthread提供的Buffer
                mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
                memset(mBuffer, 0, bufferSize);
            } else {
                mBuffer = buffer;//指向APP提供的Buffer
            }
            break;
        case ALLOC_LOCAL:
            mBuffer = calloc(1, bufferSize);
            break;
        case ALLOC_NONE:
            mBuffer = buffer;
            break;
        }
    }
}

Lo anterior es principalmente la creación y distribución de Buffer. A continuación, analice la implementación de Track, el código es el siguiente:

AudioFlinger::PlaybackThread::Track::Track(
            PlaybackThread *thread,
            const sp<Client>& client,
            //...
            const sp<IMemory>& sharedBuffer,
            //...
            track_type type)
    :   TrackBase(thread, client, sampleRate, format, channelMask, frameCount,
                  (sharedBuffer != 0) ? sharedBuffer->pointer() : buffer,
                  sessionId, uid, flags, true /*isOut*/,
                  (type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK,
                  type),
    mFillingUpStatus(FS_INVALID),
    // mRetryCount initialized later when needed
    mSharedBuffer(sharedBuffer),
    mStreamType(streamType),
    //...
{
    //共享内存相关代码,这里的AudioTrackServerProxy和StaticAudioTrackServerProxy用来管理Buffer
    if (sharedBuffer == 0) {
        mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
                mFrameSize, !isExternalTrack(), sampleRate);
    } else {
        mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
                mFrameSize);
    }
    mServerProxy = mAudioTrackServerProxy;
    mName = thread->getTrackName_l(channelMask, format, sessionId);
    //...
    // only allocate a fast track index if we were able to allocate a normal track name
    if (flags & IAudioFlinger::TRACK_FAST) {
        mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads();
        int i = __builtin_ctz(thread->mFastTrackAvailMask);
        mFastIndex = i;
        // Read the initial underruns because this field is never cleared by the fast mixer
        mObservedUnderruns = thread->getFastTrackUnderruns(i);
        thread->mFastTrackAvailMask &= ~(1 << i);
    }
}

Para resumir:

  1. El objeto AudioTrackClientProxy y el objeto StaticAudioTrackClientProxy se usan en AudioTrack para administrar la memoria compartida.
  2. Track utiliza el objeto AudioTrackServerProxy y el objeto StaticAudioTrackServerProxy para administrar la memoria compartida.

3 Transmisión de datos de audio

La transmisión de datos de audio se logra a través del método de escritura de AudioTack, y el análisis del método de escritura de la pista continúa en función de la pila de análisis en el Capítulo 5. La pila de código anterior es la siguiente:

Java层AudioTrack.write->native_write_XXX->writeToTrack
->C++层track->sharedBuffer() 或 C++层track.write

El código de writeToTrack aquí se implementa de la siguiente manera:

jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data,
                  jint offsetInBytes, jint sizeInBytes, bool blocking = true) {
    ssize_t written = 0;
    //playbackthread提供共享内存,调用C++层track的write函数
    if (track->sharedBuffer() == 0) {
        written = track->write(data + offsetInBytes, sizeInBytes, blocking);
        if (written == (ssize_t) WOULD_BLOCK) {
            written = 0;
        }
    } else {//应用端 提供共享内存,直接执行memcpy
        const audio_format_t format = audioFormatToNative(audioFormat);
        switch (format) {

        default:
        case AUDIO_FORMAT_PCM_FLOAT:
        case AUDIO_FORMAT_PCM_16_BIT: {
            if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
                sizeInBytes = track->sharedBuffer()->size();
            }
            //这里将data数据拷贝给 共享内存
            memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes);
            written = sizeInBytes;
            } break;

        case AUDIO_FORMAT_PCM_8_BIT: {
            //功能同上,只是8位需要中间的数据转换环节
            if (((size_t)sizeInBytes)*2 > track->sharedBuffer()->size()) {
                sizeInBytes = track->sharedBuffer()->size() / 2;
            }
            int count = sizeInBytes;
            int16_t *dst = (int16_t *)track->sharedBuffer()->pointer();
            const uint8_t *src = (const uint8_t *)(data + offsetInBytes);
            memcpy_to_i16_from_u8(dst, src, count);
            written = sizeInBytes;
            } break;
        }
    }
    return written;
}

También revise las siguientes instrucciones:

  1. Si track-> sharedBuffer () == 0, es decir, el hilo de reproducción proporciona memoria compartida, se ejecuta el método de escritura de la pista de capa C ++.
  2. Si track-> sharedBuffer ()! = 0, es decir, la memoria compartida es proporcionada por el lado de la APLICACIÓN, luego ejecute directamente la operación memcpy y asigne un valor a track-> sharedBuffer ().

3.1 Proceso de transferencia de datos en modo MODE_STREAM

@ 1 Proceso de proxy del cliente

Continúe analizando el método de escritura de pista aquí, el código es el siguiente:

ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
{
    //...
    size_t written = 0;
    Buffer audioBuffer;

    while (userSize >= mFrameSize) {
        audioBuffer.frameCount = userSize / mFrameSize;
        //关键点1:获取共享内存Buffer
        status_t err = obtainBuffer(&audioBuffer,
                blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
        //...
        size_t toWrite;
        //buffer拷贝数据到audioBuffer中
        if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
            toWrite = audioBuffer.size >> 1;
            memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) buffer, toWrite);
        } else {
            toWrite = audioBuffer.size;
            memcpy(audioBuffer.i8, buffer, toWrite);
        }
        //计算剩余数据
        buffer = ((const char *) buffer) + toWrite;
        userSize -= toWrite;
        written += toWrite;
        //关键点2:释放Buffer
        releaseBuffer(&audioBuffer);
    }
    //释放共享内存
    return written;
}

Continúe analizando la realización de getBuffer, el código es el siguiente:

status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
    ...//参数转换跟计算
    return obtainBuffer(audioBuffer, requested);
}

Continúe analizando la realización de getBuffer después de los parámetros de conversión, el código es el siguiente:

status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
        struct timespec *elapsed, size_t *nonContig)
{
    ...//参数转换
    status = proxy->obtainBuffer(&buffer, requested, elapsed);
    ...//结果的填充
}

Para el modo MODE_STREAM, porque mSharedBuffer == 0, el proxy aquí es AudioTrackClientProxy

status_t AudioTrack::createTrack_l(){
   //...
    void* buffers;
    if (mSharedBuffer == 0) {
        buffers = (char*)cblk + sizeof(audio_track_cblk_t);
    } else {
        buffers = mSharedBuffer->pointer();
    }
	//...
    // update proxy
    if (mSharedBuffer == 0) {
        mStaticProxy.clear();
        mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
    } else {
        mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
        mProxy = mStaticProxy;
    }
    //...
}

No existe un método getBuffer en AudioTrackClientProxy, de hecho, aquí se llama al método getBuffer en su clase padre ClientProxy, es decir, para obtener un Buffer en blanco a través de ClientProxy, y luego escribir los datos de audio en Buffer, y finalmente liberar Buffer.

@ 2 proceso del servidor

Aquí el método getNextBuffer (Get Buffer) de la pista se utiliza como análisis de entrada:

// AudioBufferProvider interface
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
        AudioBufferProvider::Buffer* buffer, int64_t pts __unused)
{
    ServerProxy::Buffer buf;
    size_t desiredFrames = buffer->frameCount;
    buf.mFrameCount = desiredFrames;
    //这里调用mServerProxy的obtainBuffer方法
    status_t status = mServerProxy->obtainBuffer(&buf);
    buffer->frameCount = buf.mFrameCount;
    buffer->raw = buf.mRaw;
    if (buf.mFrameCount == 0) {
        mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
    }
    return status;
}

Aquí continúe analizando el método obtenerBuffer de mServerProxy (mServerProxy = AudioTrackServerProxy en modo MODE_STREAM), y no hay ningún método obtenerBuffer en AudioTrackServerProxy, aquí debe llamar al método obtenerBuffer en su clase primaria ServerProxy para obtener un Buffer con datos. Nota: Finalmente, se llama a releaseBuffer directamente a través del destructor de TrackBase. El código destructor de TrackBase es el siguiente:

AudioFlinger::ThreadBase::TrackBase::~TrackBase()
{
    // delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
    delete mServerProxy;
    if (mCblk != NULL) {
        if (mClient == 0) {
            delete mCblk;
        } else {
            mCblk->~audio_track_cblk_t();   // destroy our shared-structure.
        }
    }
    mCblkMemory.clear();    // free the shared memory before releasing the heap it belongs to
    if (mClient != 0) {
        // Client destructor must run with AudioFlinger client mutex locked
        Mutex::Autolock _l(mClient->audioFlinger()->mClientLock);
        // If the client's reference count drops to zero, the associated destructor
        // must run with AudioFlinger lock held. Thus the explicit clear() rather than
        // relying on the automatic clear() at end of scope.
        mClient.clear();
    }
    // flush the binder command buffer
    IPCThreadState::self()->flushCommands();
}

En otras palabras, no hay necesidad de liberar memoria compartida llamando a releaseBuffer.

@ 3 Sincronización de datos

El modo MODE_STREAM usará el buffer de anillo para sincronizar datos, datos de producción, datos de consumo, esta vez usar el buffer de anillo es el más confiable. De hecho, es la colaboración entre ClientProxy :: getBuffer, ClientProxy :: releaseBuffer, ServerProxy :: getBuffer, ServerProxy :: releaseBuffer. Aquí hay una breve descripción de la lógica del buffer de anillo para comprender los principios. Los datos de transmisión de audio se dividen en dos partes, el encabezado de datos y los datos en sí, de la siguiente manera:

Al mismo tiempo, para el búfer en anillo, hay varias variables clave: mFront (puntero de lectura R) , mRear (puntero de escritura W) , mFrameCount (longitud de datos LEN) , mFrameCountP2 (longitud de datos LEN, tome la enésima potencia de 2) . A continuación, la lógica del búfer en anillo se explicará en forma de ejemplos y pseudocódigo. Como se muestra a continuación:

El procesamiento lógico del buffer de anillo aquí es el siguiente:

环形缓冲区:初始R=0,W=0,buf长度为LEN
写入一个数据流程:w=W%LEN ; buf[w] = data ; W++ ;
读取一个数据流程:r=R%LEN ; buf[r] = data ; R++;
判断 环形缓冲区为空:R==W
判断 满:W-R == LEN

Nota aquí: matemáticamente cuando LEN es 2 a la enésima potencia, las siguientes operaciones son equivalentes:

w=W%LEN 等价于 w=W &(LEN-1)
r=R%LEN 等价于 r=R &(LEN-1)

3.2 Proceso de transferencia de datos en modo MODE_STATIC

@ 1 Proceso del cliente

El método anterior writeToTrack del código de capa nativo escribirá directamente los datos en track-> sharedBuffer () -> pointer (). Para el modo MODE_STATIC, porque mSharedBuffer! = 0, el proxy aquí es StaticAudioTrackClientProxy.

status_t AudioTrack::createTrack_l(){
   //...
    void* buffers;
    if (mSharedBuffer == 0) {
        buffers = (char*)cblk + sizeof(audio_track_cblk_t);
    } else {
        buffers = mSharedBuffer->pointer();
    }
	//...
    // update proxy
    if (mSharedBuffer == 0) {
        mStaticProxy.clear();
        mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
    } else {
        mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
        mProxy = mStaticProxy;
    }
    //...
}

No hay un método getBuffer en StaticAudioTrackClientProxy, de hecho, aquí es para llamar al método getBuffer en su clase padre ClientProxy, que es obtener un Buffer en blanco a través de ClientProxy, y luego escribir los datos de audio en Buffer, y finalmente liberar Buffer.

@ 2 proceso del servidor

Aquí todavía se usa el método getNextBuffer (Get Buffer) de Track anterior como análisis de entrada:

// AudioBufferProvider interface
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
        AudioBufferProvider::Buffer* buffer, int64_t pts __unused)
{
    ServerProxy::Buffer buf;
    size_t desiredFrames = buffer->frameCount;
    buf.mFrameCount = desiredFrames;
    //这里调用mServerProxy的obtainBuffer方法
    status_t status = mServerProxy->obtainBuffer(&buf);
    buffer->frameCount = buf.mFrameCount;
    buffer->raw = buf.mRaw;
    if (buf.mFrameCount == 0) {
        mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
    }
    return status;
}

Aquí continúe analizando el método obtenerBuffer de mServerProxy (mServerProxy = StaticAudioTrackServerProxy en modo MODE_STATIC), y StaticAudioTrackServerProxy reescribe el método obtenerBuffer de ServerProxy. Al mismo tiempo, la operación de releaseBuffer es la misma que la anterior, también se llama directamente a través del destructor de TrackBase, por lo que no es necesario liberar Buffer llamando.

@ 3 Sincronización de datos

No hay problema de sincronización de datos en modo MODE_STATIC.

3.3 Resumen de transferencia de datos

@ 1 Para diferentes MODOS, estos Proxy apuntan a diferentes objetos:

  1. AudioTrack contiene mProxy, que se utiliza para administrar la memoria compartida, y contiene las funciones bufferBuffer y releaseBuffer.
  2. Track contiene mServerProxy, que se utiliza para administrar la memoria compartida, y contiene las funciones bufferBuffer, releaseBuffer

@ 2 AudioTrack y AudioFlinger usan mCblkMemory para realizar la interacción de datos "productor-consumidor". Analicemos el principio del intercambio de datos entre ServerProxy y ClientProxy a través de la memoria compartida:

  1. Al crear una pista, AudioFlinger asignará memoria compartida de audio a cada pista. AudioTrack y AudioFlinger usan el búfer como parámetro para crear mClientProxy y mServerProxy a través de AudioTrackClientProxy y AudioTrackServerProxy.
  2. AudioTrack (lado de la aplicación APP) escribe datos en el búfer compartido a través de mClientProxy, y AudioFlinger (lado del servidor) lee datos de la memoria compartida a través de mServerProxy. De esta manera, el cliente y el servidor forman un modelo de productor y consumidor para la memoria compartida a través del proxy.

@ 3 AudioTrackClientProxy, AudioTrackServerProxy (ambas clases se encuentran en AudioTrackShared.cpp), respectivamente, encapsulan el uso de getBuffer y releaseBuffer en el lado del cliente y el lado del servidor, las funciones de estas interfaces son las siguientes:

@@ 3.1 Cliente:

  1. AudioTrackClientProxy :: getBuffer () obtiene búferes vacíos continuos del búfer de audio;
  2. AudioTrackClientProxy :: releaseBuffer () vuelve a colocar el búfer lleno de datos en el búfer de audio.

@@ 3.2 Lado del servidor:

  1. AudioTrackServerProxy :: getBuffer () obtiene un búfer continuo lleno de datos del búfer de audio;
  2. AudioTrackServerProxy :: releaseBuffer () pone el búfer vacío usado nuevamente en el búfer de audio.
Publicado 289 artículos originales · elogiados 47 · 30,000+ vistas

Supongo que te gusta

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