Análisis de implementación de Android AAudio

AAudio es una nueva API de Android C introducida en Android O. Está diseñado principalmente para aplicaciones de audio de alto rendimiento que requieren baja latencia. Las aplicaciones se comunican con AAudio leyendo o escribiendo datos directamente en la transmisión, pero solo contiene capacidades básicas de entrada y salida de audio. El documento oficial de Android AAudio ofrece una buena introducción a la API de AAudio y las ideas de diseño. Aquí se presenta un vistazo a la implementación de AAudio. El siguiente análisis de código se basa en android-12.1.0_r27.

AAudio mueve datos de audio entre aplicaciones y entrada y salida de audio en el dispositivo Android. La aplicación pasa la transmisión de audio y lee datos de la transmisión de audio para lograr esta transferencia de datos de audio. La implementación de AAudio gira en torno a la transferencia de datos de audio y la transmisión de audio.

Hay dos modos de trabajo principales para la transferencia de datos de AAudio. Uno es el modo MMap. En este momento, la biblioteca de AAudio del cliente media.aaudioobtiene una memoria compartida del servicio y transmite datos al dispositivo a través de esta memoria compartida; el otro es el llamado modo tradicional.En este momento, la interfaz del cliente AAudio se basa en la implementación tradicional android::AudioTrack, utilizando los mismos componentes que la implementación android::AudioRecordde Java . Lo que realmente puede lograr las ventajas de rendimiento y latencia descritas en la documentación de AAudio es el modo de trabajo de la memoria compartida basada en mmap y la transferencia de datos del dispositivo.AudioTrackAudioRecord

La implementación de la biblioteca AAudio incluye principalmente los siguientes módulos, y su estructura de directorio de código fuente también está organizada según módulos:

  • Interfaces API, que implementan directamente la interfaz AAudio C llamada en la aplicación, y el código relevante se encuentra en libaaudio/src/coreel directorio;
  • Legacy, una secuencia de audio implementada basada en la interfaz tradicional android::AudioTrack, el código relevante se encuentra en el directorio;android::AudioRecordlibaaudio/src/legacy
  • El enlace, el enlace del cliente, se utiliza principalmente para media.aaudiocomunicarse con servicios, como media.aaudioenviar solicitudes o recibir media.aaudionotificaciones de eventos de los servicios.El código relevante se encuentra en libaaudio/src/bindingel directorio;
  • fifo se utiliza principalmente para media.aaudiola transferencia de datos con servicios o dispositivos. AAudio obtiene el fd vinculado a la memoria compartida a través del módulo de enlace. Este fd necesita ser mapeado nuevamente en el proceso actual. Las clases relacionadas del módulo fifo se utilizan para compartir el memoria a través de estos mmaps media.aaudioPara la transferencia de datos servicios o dispositivos, el código correspondiente se encuentra en libaaudio/src/fifoel directorio;
  • Diagrama de flujo, cuando se usa AAudio para reproducir datos, se realizará un procesamiento simple en los datos, como la conversión del número de canal, etc. El módulo de diagrama de flujo se usa para completar estos procesos y el código relevante se encuentra en el directorio libaaudio/src/flowgraph;
  • cliente, una secuencia de audio implementada en base a módulos como vinculación, fifo y diagrama de flujo. El código relevante se encuentra en libaaudio/src/clientel directorio;
  • La utilidad, algunos programas de utilidad y los códigos relacionados se encuentran en libaaudio/src/utilityel directorio.

Desde la perspectiva del módulo, la estructura de implementación de la biblioteca AAudio se muestra a continuación:

Componentes de audio

El análisis de la implementación de Android AAudio aquí se centra en la transmisión de audio basada en la memoria compartida mmap para transferir datos de audio.

Abrir secuencia de audio

Los usuarios de la interfaz AAudio abren una transmisión de audio AAudioStreamBuilder_openStream()según la configuración de la transmisión de audio en la interfaz . AAudioStreamBuilderLa definición de esta función (ubicada en frameworks/av/media/libaaudio/src/flowgraph/AudioProcessorBase.h ) es la siguiente:

AAUDIO_API aaudio_result_t  AAudioStreamBuilder_openStream(AAudioStreamBuilder* builder,
                                                     AAudioStream** streamPtr)
{
    AudioStream *audioStream = nullptr;
    aaudio_stream_id_t id = 0;
    // Please leave these logs because they are very helpful when debugging.
    ALOGI("%s() called ----------------------------------------", __func__);
    AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(streamPtr);
    aaudio_result_t result = streamBuilder->build(&audioStream);
    if (result == AAUDIO_OK) {
        *streamPtr = (AAudioStream*) audioStream;
        id = audioStream->getId();
    } else {
        *streamPtr = nullptr;
    }
    ALOGI("%s() returns %d = %s for s#%u ----------------",
        __func__, result, AAudio_convertResultToText(result), id);
    return result;
}

AAudio en realidad AudioStreamBuilder::build()abre la transmisión de audio, lo que crea e inicializa el objeto de transmisión de audio AudioStreamy lo devuelve a la persona que llama. Por cierto, y que se ve en el archivo de encabezado de la interfaz AAudio aaudio/AAudio.h no son estructuras reales. Estas dos estructuras probablemente no sean diferentes de y las estructuras reales son y .struct AAudioStreamStructstruct AAudioStreamBuilderStructvoidaaudio::AudioStreamaaudio::AudioStreamBuilder

AudioStreamBuilder::build(AudioStream** streamPtr)La definición de la función (ubicada en frameworks/av/media/libaaudio/src/core/AudioStreamBuilder.cpp ) es la siguiente:

static aaudio_result_t builder_createStream(aaudio_direction_t direction,
                                         aaudio_sharing_mode_t sharingMode,
                                         bool tryMMap,
                                         android::sp<AudioStream> &stream) {
    aaudio_result_t result = AAUDIO_OK;

    switch (direction) {

        case AAUDIO_DIRECTION_INPUT:
            if (tryMMap) {
                stream = new AudioStreamInternalCapture(AAudioBinderClient::getInstance(),
                                                                 false);
            } else {
                stream = new AudioStreamRecord();
            }
            break;

        case AAUDIO_DIRECTION_OUTPUT:
            if (tryMMap) {
                stream = new AudioStreamInternalPlay(AAudioBinderClient::getInstance(),
                                                              false);
            } else {
                stream = new AudioStreamTrack();
            }
            break;

        default:
            ALOGE("%s() bad direction = %d", __func__, direction);
            result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
    }
    return result;
}

// Try to open using MMAP path if that is allowed.
// Fall back to Legacy path if MMAP not available.
// Exact behavior is controlled by MMapPolicy.
aaudio_result_t AudioStreamBuilder::build(AudioStream** streamPtr) {

    if (streamPtr == nullptr) {
        ALOGE("%s() streamPtr is null", __func__);
        return AAUDIO_ERROR_NULL;
    }
    *streamPtr = nullptr;

    logParameters();

    aaudio_result_t result = validate();
    if (result != AAUDIO_OK) {
        return result;
    }

    // The API setting is the highest priority.
    aaudio_policy_t mmapPolicy = AudioGlobal_getMMapPolicy();
    // If not specified then get from a system property.
    if (mmapPolicy == AAUDIO_UNSPECIFIED) {
        mmapPolicy = AAudioProperty_getMMapPolicy();
    }
    // If still not specified then use the default.
    if (mmapPolicy == AAUDIO_UNSPECIFIED) {
        mmapPolicy = AAUDIO_MMAP_POLICY_DEFAULT;
    }

    int32_t mapExclusivePolicy = AAudioProperty_getMMapExclusivePolicy();
    if (mapExclusivePolicy == AAUDIO_UNSPECIFIED) {
        mapExclusivePolicy = AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT;
    }

    aaudio_sharing_mode_t sharingMode = getSharingMode();
    if ((sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE)
        && (mapExclusivePolicy == AAUDIO_POLICY_NEVER)) {
        ALOGD("%s() EXCLUSIVE sharing mode not supported. Use SHARED.", __func__);
        sharingMode = AAUDIO_SHARING_MODE_SHARED;
        setSharingMode(sharingMode);
    }

    bool allowMMap = mmapPolicy != AAUDIO_POLICY_NEVER;
    bool allowLegacy = mmapPolicy != AAUDIO_POLICY_ALWAYS;

    // TODO Support other performance settings in MMAP mode.
    // Disable MMAP if low latency not requested.
    if (getPerformanceMode() != AAUDIO_PERFORMANCE_MODE_LOW_LATENCY) {
        ALOGD("%s() MMAP not used because AAUDIO_PERFORMANCE_MODE_LOW_LATENCY not requested.",
              __func__);
        allowMMap = false;
    }

    // SessionID and Effects are only supported in Legacy mode.
    if (getSessionId() != AAUDIO_SESSION_ID_NONE) {
        ALOGD("%s() MMAP not used because sessionId specified.", __func__);
        allowMMap = false;
    }

    if (!allowMMap && !allowLegacy) {
        ALOGE("%s() no backend available: neither MMAP nor legacy path are allowed", __func__);
        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
    }

    setPrivacySensitive(false);
    if (mPrivacySensitiveReq == PRIVACY_SENSITIVE_DEFAULT) {
        // When not explicitly requested, set privacy sensitive mode according to input preset:
        // communication and camcorder captures are considered privacy sensitive by default.
        aaudio_input_preset_t preset = getInputPreset();
        if (preset == AAUDIO_INPUT_PRESET_CAMCORDER
                || preset == AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION) {
            setPrivacySensitive(true);
        }
    } else if (mPrivacySensitiveReq == PRIVACY_SENSITIVE_ENABLED) {
        setPrivacySensitive(true);
    }

    android::sp<AudioStream> audioStream;
    result = builder_createStream(getDirection(), sharingMode, allowMMap, audioStream);
    if (result == AAUDIO_OK) {
        // Open the stream using the parameters from the builder.
        result = audioStream->open(*this);
        if (result != AAUDIO_OK) {
            bool isMMap = audioStream->isMMap();
            if (isMMap && allowLegacy) {
                ALOGV("%s() MMAP stream did not open so try Legacy path", __func__);
                // If MMAP stream failed to open then TRY using a legacy stream.
                result = builder_createStream(getDirection(), sharingMode,
                                              false, audioStream);
                if (result == AAUDIO_OK) {
                    result = audioStream->open(*this);
                }
            }
        }
        if (result == AAUDIO_OK) {
            audioStream->registerPlayerBase();
            audioStream->logOpenActual();
            *streamPtr = startUsingStream(audioStream);
        } // else audioStream will go out of scope and be deleted
    }

    return result;
}

La política MMap es uno de los parámetros más importantes al crear y abrir una transmisión de audio. AudioStreamBuilder::build()El proceso principal para abrir una transmisión de audio es el siguiente:

  • Para obtener la política MMap, primero obtengala de la configuración de API, si no está configurada, obtengala de las propiedades del sistema;
  • Obtenga la política exclusiva de MMap de las propiedades del sistema;
  • Corrija el modo de compartir de acuerdo con la política exclusiva de MMap y el modo de compartir establecido;
  • Compruebe el modo de rendimiento y el ID de sesión. El ID de sesión y los efectos de sonido solo se admiten en el modo tradicional. Si el modo de rendimiento no es el modo de baja latencia, utilice el modo tradicional;
  • Cree una transmisión de audio a través de builder_createStream()la función, que creará diferentes tipos de objetos de transmisión de audio según el modo MMap y los tipos de entrada y salida;
  • Ejecute open()la operación del objeto de transmisión de audio y abra la transmisión de audio;
  • Si falla una solicitud para abrir una transmisión de audio en modo MMap, se intenta crear y abrir una transmisión de audio en modo heredado.

La implementación de la interfaz de AAudio es relativamente robusta: cuando el modo de baja latencia no puede funcionar correctamente por diversas razones, volverá al modo tradicional.

La estructura jerárquica de los objetos de flujo de audio en AAudio es la siguiente:

AAudio AudioStream

No es difícil ver que AudioStreamInternalPlayy AudioStreamInternalCaptureson, respectivamente, los tipos de flujo de audio de reproducción y de flujo de audio de colección en el modo MMap, AudioStreamTracky AudioStreamRecordson respectivamente los tipos de flujo de audio de reproducción y de flujo de audio de colección en el modo tradicional.

AAudio proporciona interfaces para configurar y obtener políticas MMap. Las definiciones de estas interfaces (ubicadas en frameworks/av/media/libaaudio/src/core/AudioGlobal.cpp ) son las siguientes:

static aaudio_policy_t g_MMapPolicy = AAUDIO_UNSPECIFIED;

aaudio_policy_t AudioGlobal_getMMapPolicy() {
  return g_MMapPolicy;
}

aaudio_result_t AudioGlobal_setMMapPolicy(aaudio_policy_t policy) {
    aaudio_result_t result = AAUDIO_OK;
    switch(policy) {
        case AAUDIO_UNSPECIFIED:
        case AAUDIO_POLICY_NEVER:
        case AAUDIO_POLICY_AUTO:
        case AAUDIO_POLICY_ALWAYS:
            g_MMapPolicy = policy;
            break;
        default:
            result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
            break;
    }
    return result;
}

La propiedad del sistema que define la política MMap es aaudio.mmap_policyy la propiedad del sistema que define la política exclusiva de MMap es aaudio.mmap_exclusive_policy. Puede ver esto en el archivo frameworks/av/media/libaaudio/src/utility/AAudioUtilities.h :

int32_t AAudioProperty_getMMapPolicy();
#define AAUDIO_PROP_MMAP_POLICY           "aaudio.mmap_policy"

/**
 * Read system property.
 * @return AAUDIO_UNSPECIFIED, AAUDIO_POLICY_NEVER or AAUDIO_POLICY_AUTO or AAUDIO_POLICY_ALWAYS
 */
int32_t AAudioProperty_getMMapExclusivePolicy();
#define AAUDIO_PROP_MMAP_EXCLUSIVE_POLICY "aaudio.mmap_exclusive_policy"

El método para obtener las propiedades del sistema (ubicado en frameworks/av/media/libaaudio/src/utility/AAudioUtilities.cpp ) es el siguiente:

static int32_t AAudioProperty_getMMapProperty(const char *propName,
                                              int32_t defaultValue,
                                              const char * caller) {
    int32_t prop = property_get_int32(propName, defaultValue);
    switch (prop) {
        case AAUDIO_UNSPECIFIED:
        case AAUDIO_POLICY_NEVER:
        case AAUDIO_POLICY_ALWAYS:
        case AAUDIO_POLICY_AUTO:
            break;
        default:
            ALOGE("%s: invalid = %d", caller, prop);
            prop = defaultValue;
            break;
    }
    return prop;
}

int32_t AAudioProperty_getMMapPolicy() {
    return AAudioProperty_getMMapProperty(AAUDIO_PROP_MMAP_POLICY,
                                          AAUDIO_UNSPECIFIED, __func__);
}

int32_t AAudioProperty_getMMapExclusivePolicy() {
    return AAudioProperty_getMMapProperty(AAUDIO_PROP_MMAP_EXCLUSIVE_POLICY,
                                          AAUDIO_UNSPECIFIED, __func__);
}

Esta propiedad del sistema se puede definir en el archivo mk. Por ejemplo, para permitir que la versión AAOS del simulador inicie el media.aaudioservicio, debe device/generic/car/emulator/audio/car_emulator_audio.mkagregar las siguientes líneas al archivo:

PRODUCT_PROPERTY_OVERRIDES += aaudio.mmap_policy=2

Aunque en la implementación de la biblioteca AAudio, la política MMap establecida por la API tiene la máxima prioridad, esta configuración en el proceso del cliente no puede afectar media.aaudiosi se inicia el servicio, pero sí aaudio.mmap_policyla configuración de las propiedades del sistema.

Los objetos de diferentes tipos de flujo de audio open()tienen diferentes operaciones. A continuación se muestra AudioStreamInternalPlay::open()la definición de la función (ubicada en frameworks/av/media/libaaudio/src/client/AudioStreamInternalPlay.cpp ):

aaudio_result_t AudioStreamInternalPlay::open(const AudioStreamBuilder &builder) {
    aaudio_result_t result = AudioStreamInternal::open(builder);
    if (result == AAUDIO_OK) {
        result = mFlowGraph.configure(getFormat(),
                             getSamplesPerFrame(),
                             getDeviceFormat(),
                             getDeviceChannelCount());

        if (result != AAUDIO_OK) {
            safeReleaseClose();
        }
        // Sample rate is constrained to common values by now and should not overflow.
        int32_t numFrames = kRampMSec * getSampleRate() / AAUDIO_MILLIS_PER_SECOND;
        mFlowGraph.setRampLengthInFrames(numFrames);
    }
    return result;
}

AudioStreamInternalPlay::open()Ejecute AudioStreamInternal::open(builder)y configure la canalización de procesamiento de datos para procesar datos de reproducción de audio AAudioFlowGraph.

AudioStreamInternal::open(builder)La definición de la función (ubicada en frameworks/av/media/libaaudio/src/client/AudioStreamInternal.cpp ) es la siguiente:

aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) {

    aaudio_result_t result = AAUDIO_OK;
    int32_t framesPerBurst;
    int32_t framesPerHardwareBurst;
    AAudioStreamRequest request;
    AAudioStreamConfiguration configurationOutput;

    if (getState() != AAUDIO_STREAM_STATE_UNINITIALIZED) {
        ALOGE("%s - already open! state = %d", __func__, getState());
        return AAUDIO_ERROR_INVALID_STATE;
    }

    // Copy requested parameters to the stream.
    result = AudioStream::open(builder);
    if (result < 0) {
        return result;
    }

    const int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros();
    int32_t burstMicros = 0;

    const audio_format_t requestedFormat = getFormat();
    // We have to do volume scaling. So we prefer FLOAT format.
    if (requestedFormat == AUDIO_FORMAT_DEFAULT) {
        setFormat(AUDIO_FORMAT_PCM_FLOAT);
    }
    // Request FLOAT for the shared mixer or the device.
    request.getConfiguration().setFormat(AUDIO_FORMAT_PCM_FLOAT);

    // TODO b/182392769: use attribution source util
    AttributionSourceState attributionSource;
    attributionSource.uid = VALUE_OR_FATAL(android::legacy2aidl_uid_t_int32_t(getuid()));
    attributionSource.pid = VALUE_OR_FATAL(android::legacy2aidl_pid_t_int32_t(getpid()));
    attributionSource.packageName = builder.getOpPackageName();
    attributionSource.attributionTag = builder.getAttributionTag();
    attributionSource.token = sp<android::BBinder>::make();

    // Build the request to send to the server.
    request.setAttributionSource(attributionSource);
    request.setSharingModeMatchRequired(isSharingModeMatchRequired());
    request.setInService(isInService());

    request.getConfiguration().setDeviceId(getDeviceId());
    request.getConfiguration().setSampleRate(getSampleRate());
    request.getConfiguration().setDirection(getDirection());
    request.getConfiguration().setSharingMode(getSharingMode());
    request.getConfiguration().setChannelMask(getChannelMask());

    request.getConfiguration().setUsage(getUsage());
    request.getConfiguration().setContentType(getContentType());
    request.getConfiguration().setSpatializationBehavior(getSpatializationBehavior());
    request.getConfiguration().setIsContentSpatialized(isContentSpatialized());
    request.getConfiguration().setInputPreset(getInputPreset());
    request.getConfiguration().setPrivacySensitive(isPrivacySensitive());

    request.getConfiguration().setBufferCapacity(builder.getBufferCapacity());

    mDeviceChannelCount = getSamplesPerFrame(); // Assume it will be the same. Update if not.

    mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput);
    if (mServiceStreamHandle < 0
            && (request.getConfiguration().getSamplesPerFrame() == 1
                    || request.getConfiguration().getChannelMask() == AAUDIO_CHANNEL_MONO)
            && getDirection() == AAUDIO_DIRECTION_OUTPUT
            && !isInService()) {
        // if that failed then try switching from mono to stereo if OUTPUT.
        // Only do this in the client. Otherwise we end up with a mono mixer in the service
        // that writes to a stereo MMAP stream.
        ALOGD("%s() - openStream() returned %d, try switching from MONO to STEREO",
              __func__, mServiceStreamHandle);
        request.getConfiguration().setChannelMask(AAUDIO_CHANNEL_STEREO);
        mServiceStreamHandle = mServiceInterface.openStream(request, configurationOutput);
    }
    if (mServiceStreamHandle < 0) {
        return mServiceStreamHandle;
    }

    // This must match the key generated in oboeservice/AAudioServiceStreamBase.cpp
    // so the client can have permission to log.
    if (!mInService) {
        // No need to log if it is from service side.
        mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM)
                     + std::to_string(mServiceStreamHandle);
    }

    android::mediametrics::LogItem(mMetricsId)
            .set(AMEDIAMETRICS_PROP_PERFORMANCEMODE,
                 AudioGlobal_convertPerformanceModeToText(builder.getPerformanceMode()))
            .set(AMEDIAMETRICS_PROP_SHARINGMODE,
                 AudioGlobal_convertSharingModeToText(builder.getSharingMode()))
            .set(AMEDIAMETRICS_PROP_ENCODINGCLIENT,
                 android::toString(requestedFormat).c_str()).record();

    result = configurationOutput.validate();
    if (result != AAUDIO_OK) {
        goto error;
    }
    // Save results of the open.
    if (getChannelMask() == AAUDIO_UNSPECIFIED) {
        setChannelMask(configurationOutput.getChannelMask());
    }

    mDeviceChannelCount = configurationOutput.getSamplesPerFrame();

    setSampleRate(configurationOutput.getSampleRate());
    setDeviceId(configurationOutput.getDeviceId());
    setSessionId(configurationOutput.getSessionId());
    setSharingMode(configurationOutput.getSharingMode());

    setUsage(configurationOutput.getUsage());
    setContentType(configurationOutput.getContentType());
    setSpatializationBehavior(configurationOutput.getSpatializationBehavior());
    setIsContentSpatialized(configurationOutput.isContentSpatialized());
    setInputPreset(configurationOutput.getInputPreset());

    // Save device format so we can do format conversion and volume scaling together.
    setDeviceFormat(configurationOutput.getFormat());

    result = mServiceInterface.getStreamDescription(mServiceStreamHandle, mEndPointParcelable);
    if (result != AAUDIO_OK) {
        goto error;
    }

    // Resolve parcelable into a descriptor.
    result = mEndPointParcelable.resolve(&mEndpointDescriptor);
    if (result != AAUDIO_OK) {
        goto error;
    }

    // Configure endpoint based on descriptor.
    mAudioEndpoint = std::make_unique<AudioEndpoint>();
    result = mAudioEndpoint->configure(&mEndpointDescriptor, getDirection());
    if (result != AAUDIO_OK) {
        goto error;
    }

    framesPerHardwareBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;

    // Scale up the burst size to meet the minimum equivalent in microseconds.
    // This is to avoid waking the CPU too often when the HW burst is very small
    // or at high sample rates.
    framesPerBurst = framesPerHardwareBurst;
    do {
        if (burstMicros > 0) {  // skip first loop
            framesPerBurst *= 2;
        }
        burstMicros = framesPerBurst * static_cast<int64_t>(1000000) / getSampleRate();
    } while (burstMicros < burstMinMicros);
    ALOGD("%s() original HW burst = %d, minMicros = %d => SW burst = %d\n",
          __func__, framesPerHardwareBurst, burstMinMicros, framesPerBurst);

    // Validate final burst size.
    if (framesPerBurst < MIN_FRAMES_PER_BURST || framesPerBurst > MAX_FRAMES_PER_BURST) {
        ALOGE("%s - framesPerBurst out of range = %d", __func__, framesPerBurst);
        result = AAUDIO_ERROR_OUT_OF_RANGE;
        goto error;
    }
    setFramesPerBurst(framesPerBurst); // only save good value

    mBufferCapacityInFrames = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
    if (mBufferCapacityInFrames < getFramesPerBurst()
            || mBufferCapacityInFrames > MAX_BUFFER_CAPACITY_IN_FRAMES) {
        ALOGE("%s - bufferCapacity out of range = %d", __func__, mBufferCapacityInFrames);
        result = AAUDIO_ERROR_OUT_OF_RANGE;
        goto error;
    }

    mClockModel.setSampleRate(getSampleRate());
    mClockModel.setFramesPerBurst(framesPerHardwareBurst);

    if (isDataCallbackSet()) {
        mCallbackFrames = builder.getFramesPerDataCallback();
        if (mCallbackFrames > getBufferCapacity() / 2) {
            ALOGW("%s - framesPerCallback too big = %d, capacity = %d",
                  __func__, mCallbackFrames, getBufferCapacity());
            result = AAUDIO_ERROR_OUT_OF_RANGE;
            goto error;

        } else if (mCallbackFrames < 0) {
            ALOGW("%s - framesPerCallback negative", __func__);
            result = AAUDIO_ERROR_OUT_OF_RANGE;
            goto error;

        }
        if (mCallbackFrames == AAUDIO_UNSPECIFIED) {
            mCallbackFrames = getFramesPerBurst();
        }

        const int32_t callbackBufferSize = mCallbackFrames * getBytesPerFrame();
        mCallbackBuffer = std::make_unique<uint8_t[]>(callbackBufferSize);
    }

    // For debugging and analyzing the distribution of MMAP timestamps.
    // For OUTPUT, use a NEGATIVE offset to move the CPU writes further BEFORE the HW reads.
    // For INPUT, use a POSITIVE offset to move the CPU reads further AFTER the HW writes.
    // You can use this offset to reduce glitching.
    // You can also use this offset to force glitching. By iterating over multiple
    // values you can reveal the distribution of the hardware timing jitter.
    if (mAudioEndpoint->isFreeRunning()) { // MMAP?
        int32_t offsetMicros = (getDirection() == AAUDIO_DIRECTION_OUTPUT)
                ? AAudioProperty_getOutputMMapOffsetMicros()
                : AAudioProperty_getInputMMapOffsetMicros();
        // This log is used to debug some tricky glitch issues. Please leave.
        ALOGD_IF(offsetMicros, "%s() - %s mmap offset = %d micros",
                __func__,
                (getDirection() == AAUDIO_DIRECTION_OUTPUT) ? "output" : "input",
                offsetMicros);
        mTimeOffsetNanos = offsetMicros * AAUDIO_NANOS_PER_MICROSECOND;
    }

    setBufferSize(mBufferCapacityInFrames / 2); // Default buffer size to match Q

    setState(AAUDIO_STREAM_STATE_OPEN);

    return result;

error:
    safeReleaseClose();
    return result;
}

AudioStreamInternal::open(builder)El proceso de ejecución de la función es aproximadamente el siguiente:

  1. La AudioStream::open(builder)función de ejecución copia principalmente AudioStreamBuilderla configuración de la transmisión de audio realizada por el usuario, como la frecuencia de muestreo, el número de canal, el formato de datos, el modo de compartir y el modo de rendimiento, etc. Para algunos elementos de configuración que el usuario no ha configurado, se establecerán los valores predeterminados apropiados. ser establecido;
  2. Construya el objeto de solicitud de flujo AAudio, es decir, AAudioStreamRequestobjeto;
  3. Solicite al servicio que abra la transmisión de audio a través de la interfaz del servicio AAudio media.aaudio. El valor de retorno de la operación de abrir la transmisión de audio a través de la interfaz del servicio AAudio incluye dos partes. Una es el valor de retorno de la operación, que es el media.aaudioidentificador del servidor -Un lado del flujo de audio se abre para el servicio y el otro se utiliza como parámetro de salida.El AAudioStreamConfigurationobjeto, que contiene principalmente la información de configuración real del flujo de audio;
  4. Al solicitar al servicio que abra el stream a través de la interfaz del servicio AAudio media.aaudiofalla, si el número de canales de stream solicitados es mono, intentará abrirlo nuevamente con una configuración de stream de audio estéreo de dos canales;
  5. Establezca el estado de la transmisión de audio actual de acuerdo con la información de configuración de la transmisión de audio real devuelta al abrir la transmisión de audio;
  6. Obtenga la descripción de la transmisión de audio a través de la interfaz del servicio AAudio, pase el identificador de la transmisión de audio del servidor y obtenga la AudioEndpointParcelabledescripción de la transmisión de audio de un tipo de objeto. La información relacionada con la memoria compartida requerida para que el cliente AAudio se comunique con el servicio no es pasa a través del objeto media.aaudioal abrir la secuencia, se devuelve aquí a través del objeto;AAudioStreamConfigurationAudioEndpointParcelable
  7. AudioEndpointParcelableLa información de la memoria compartida relacionada con la comunicación del objeto se convierte en EndpointDescriptoruna descripción en el formulario;
  8. Cree y configure AudioEndpointel objeto, que se utiliza principalmente para media.aaudiointercambiar comandos y datos de audio con el servicio;
  9. De acuerdo con la configuración de las propiedades del sistema, es decir, aaudio.hw_burst_min_usecla duración mínima de cada transmisión de datos, corrija la configuración de la cantidad mínima de datos de cada transmisión de datos;
  10. Verifique la capacidad de cuadros de audio del búfer;
  11. Establecer el modelo de reloj;
  12. Devolución de llamada de datos de configuración;
  13. Establezca el tamaño del búfer y establezca el estado de la transmisión de audio para abrir.

Como se puede ver en AudioStreamInternal::open(builder)la función, la implementación del flujo de audio de la comunicación de memoria compartida basada en mmap con dispositivos y media.aaudioservicios tiene una estructura de nivel de clase como se muestra a continuación:

Arquitectura de audio

Aunque el directorio de código relacionado con la implementación de la interfaz API se denomina core, en la biblioteca AAudio, el flujo de audio es sin duda el centro de toda la implementación.

vinculante

El módulo de enlace se utiliza principalmente para media.aaudiocomunicarse con servicios. AAudio define la interfaz del servicio AAudio AAudioServiceInterface. AudioStreamInternal::open(builder)Al abrir la transmisión de audio, solicite media.aaudioal servicio que abra la transmisión de audio a través de esta interfaz y obtenga la descripción de la transmisión. La definición de esta interfaz (ubicada en frameworks/av/media/libaaudio/src/binding/ AAudioServiceInterface.h ) es el siguiente:

class AAudioServiceInterface {
public:

    AAudioServiceInterface() {};
    virtual ~AAudioServiceInterface() = default;

    virtual void registerClient(const android::sp<IAAudioClient>& client) = 0;

    /**
     * @param request info needed to create the stream
     * @param configuration contains information about the created stream
     * @return handle to the stream or a negative error
     */
    virtual aaudio_handle_t openStream(const AAudioStreamRequest &request,
                                       AAudioStreamConfiguration &configuration) = 0;

    virtual aaudio_result_t closeStream(aaudio_handle_t streamHandle) = 0;

    /* Get an immutable description of the in-memory queues
    * used to communicate with the underlying HAL or Service.
    */
    virtual aaudio_result_t getStreamDescription(aaudio_handle_t streamHandle,
                                                 AudioEndpointParcelable &parcelable) = 0;

    /**
     * Start the flow of data.
     */
    virtual aaudio_result_t startStream(aaudio_handle_t streamHandle) = 0;

    /**
     * Stop the flow of data such that start() can resume without loss of data.
     */
    virtual aaudio_result_t pauseStream(aaudio_handle_t streamHandle) = 0;

    /**
     * Stop the flow of data after data currently inthe buffer has played.
     */
    virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle) = 0;

    /**
     *  Discard any data held by the underlying HAL or Service.
     */
    virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle) = 0;

    /**
     * Manage the specified thread as a low latency audio thread.
     */
    virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
                                                pid_t clientThreadId,
                                                int64_t periodNanoseconds) = 0;

    virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
                                                  pid_t clientThreadId) = 0;

    virtual aaudio_result_t startClient(aaudio_handle_t streamHandle,
                                        const android::AudioClient& client,
                                        const audio_attributes_t *attr,
                                        audio_port_handle_t *clientHandle) = 0;

    virtual aaudio_result_t stopClient(aaudio_handle_t streamHandle,
                                       audio_port_handle_t clientHandle) = 0;
};

} /* namespace aaudio */

AAudioBinderAdapterEncapsule media.aaudioel objeto proxy del cliente del servicio e implemente AAudioServiceInterfacela interfaz basada en el objeto proxy del cliente. AAudioBinderAdapterLa implementación (ubicada en frameworks/av/media/libaaudio/src/binding/AAudioBinderAdapter.cpp ) es la siguiente:

namespace aaudio {

using android::aidl_utils::statusTFromBinderStatus;
using android::binder::Status;

AAudioBinderAdapter::AAudioBinderAdapter(IAAudioService* delegate)
        : mDelegate(delegate) {}

void AAudioBinderAdapter::registerClient(const android::sp<IAAudioClient>& client) {
    mDelegate->registerClient(client);
}

aaudio_handle_t AAudioBinderAdapter::openStream(const AAudioStreamRequest& request,
                                                AAudioStreamConfiguration& config) {
    aaudio_handle_t result;
    StreamParameters params;
    Status status = mDelegate->openStream(request.parcelable(),
                                          &params,
                                          &result);
    if (!status.isOk()) {
        result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
    }
    config = params;
    return result;
}

aaudio_result_t AAudioBinderAdapter::closeStream(aaudio_handle_t streamHandle) {
    aaudio_result_t result;
    Status status = mDelegate->closeStream(streamHandle, &result);
    if (!status.isOk()) {
        result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
    }
    return result;
}

aaudio_result_t AAudioBinderAdapter::getStreamDescription(aaudio_handle_t streamHandle,
                                                          AudioEndpointParcelable& endpointOut) {
    aaudio_result_t result;
    Endpoint endpoint;
    Status status = mDelegate->getStreamDescription(streamHandle,
                                                    &endpoint,
                                                    &result);
    if (!status.isOk()) {
        result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
    }
    endpointOut = std::move(endpoint);
    return result;
}

aaudio_result_t AAudioBinderAdapter::startStream(aaudio_handle_t streamHandle) {
    aaudio_result_t result;
    Status status = mDelegate->startStream(streamHandle, &result);
    if (!status.isOk()) {
        result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
    }
    return result;
}

aaudio_result_t AAudioBinderAdapter::pauseStream(aaudio_handle_t streamHandle) {
    aaudio_result_t result;
    Status status = mDelegate->pauseStream(streamHandle, &result);
    if (!status.isOk()) {
        result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
    }
    return result;
}

aaudio_result_t AAudioBinderAdapter::stopStream(aaudio_handle_t streamHandle) {
    aaudio_result_t result;
    Status status = mDelegate->stopStream(streamHandle, &result);
    if (!status.isOk()) {
        result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
    }
    return result;
}

aaudio_result_t AAudioBinderAdapter::flushStream(aaudio_handle_t streamHandle) {
    aaudio_result_t result;
    Status status = mDelegate->flushStream(streamHandle, &result);
    if (!status.isOk()) {
        result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
    }
    return result;
}

aaudio_result_t AAudioBinderAdapter::registerAudioThread(aaudio_handle_t streamHandle,
                                                         pid_t clientThreadId,
                                                         int64_t periodNanoseconds) {
    aaudio_result_t result;
    Status status = mDelegate->registerAudioThread(streamHandle, clientThreadId, periodNanoseconds, &result);
    if (!status.isOk()) {
        result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
    }
    return result;
}

aaudio_result_t AAudioBinderAdapter::unregisterAudioThread(aaudio_handle_t streamHandle,
                                                           pid_t clientThreadId) {
    aaudio_result_t result;
    Status status = mDelegate->unregisterAudioThread(streamHandle, clientThreadId, &result);
    if (!status.isOk()) {
        result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status));
    }
    return result;
}

}  // namespace aaudio

AAudioBinderAdapterBásicamente lo que hace es transferir la operación al media.aaudioobjeto proxy del cliente del servicio y convertir el valor de retorno de forma adecuada.

AAudioClientEl servicio utiliza la interfaz de carpeta media.aaudiopara notificar al cliente sobre algunos eventos e AAudioBinderClient::AAudioClientimplementar BnAAudioClientla interfaz. AAudioBinderClient::AAudioClientDespués de recibir la notificación del evento, el evento se transmitirá AAudioBinderClient.

AAudioBinderClient::AdapterHerencia AAudioBinderAdapter, que AAudioBinderAdapteragrega la lógica de cerrar sesión en la notificación de muerte cuando se destruye.

AAudioBinderClientEs el núcleo del módulo de enlace, mantiene el media.aaudioobjeto proxy del cliente, el objeto, etc. del servicio AAudioClient, AAudioBinderAdaptery el módulo del cliente accede media.aaudioal servicio a través de él.

AAudioBinderClientLa implementación de la función utilizada para obtener AAudioServiceInterfaceel objeto de interfaz getAAudioService()(ubicado en frameworks/av/media/libaaudio/src/binding/AAudioBinderClient.cpp ) es la siguiente:

// TODO Share code with other service clients.
// Helper function to get access to the "AAudioService" service.
// This code was modeled after frameworks/av/media/libaudioclient/AudioSystem.cpp
std::shared_ptr<AAudioServiceInterface> AAudioBinderClient::getAAudioService() {
    std::shared_ptr<AAudioServiceInterface> result;
    sp<IAAudioService> aaudioService;
    bool needToRegister = false;
    {
        Mutex::Autolock _l(mServiceLock);
        if (mAdapter == nullptr) {
            sp<IBinder> binder;
            sp<IServiceManager> sm = defaultServiceManager();
            // Try several times to get the service.
            int retries = 4;
            do {
                binder = sm->getService(String16(AAUDIO_SERVICE_NAME)); // This will wait a while.
                if (binder.get() != nullptr) {
                    break;
                }
            } while (retries-- > 0);

            if (binder.get() != nullptr) {
                // Ask for notification if the service dies.
                status_t status = binder->linkToDeath(mAAudioClient);
                // TODO review what we should do if this fails
                if (status != NO_ERROR) {
                    ALOGE("%s() - linkToDeath() returned %d", __func__, status);
                }
                aaudioService = interface_cast<IAAudioService>(binder);
                mAdapter.reset(new Adapter(aaudioService, mAAudioClient));
                needToRegister = true;
                // Make sure callbacks can be received by mAAudioClient
                ProcessState::self()->startThreadPool();
            } else {
                ALOGE("AAudioBinderClient could not connect to %s", AAUDIO_SERVICE_NAME);
            }
        }
        result = mAdapter;
    }
    // Do this outside the mutex lock.
    if (needToRegister && aaudioService.get() != nullptr) { // new client?
        aaudioService->registerClient(mAAudioClient);
    }
    return result;
}

AAudioBinderClient::getAAudioService()media.aaudioLa función obtiene el objeto proxy del cliente del servicio del administrador de servicios y crea AAudioServiceInterfaceun objeto de interfaz basado en el objeto, y luego registra la notificación de muerte y AAudioClient.

Al AAudioBinderClientrecibir media.aaudiouna notificación de suspensión del servicio, destruirá los recursos que mantiene, como media.aaudioel objeto proxy del cliente del servicio, etc. Esto se hace a través de dropAAudioService()la función (ubicada en frameworks/av/media/libaaudio/src/binding/AAudioBinderClient. cpp ):

void AAudioBinderClient::dropAAudioService() {
    Mutex::Autolock _l(mServiceLock);
    mAdapter.reset();
}

El intercambio de datos con media.aaudioservicios es la parte central del módulo de enlace. El intercambio de datos específicos ocurre principalmente en las siguientes operaciones:

  1. Al abrir la transmisión de audio, envíe AAudioStreamRequestuna solicitud representada por un objeto, reciba AAudioStreamConfigurationuna respuesta que represente la información de configuración de la transmisión representada por un objeto y el identificador de la transmisión de audio del lado del servidor;
  2. Obtenga la descripción del flujo de audio, envíe el identificador del flujo de audio del servidor y reciba AudioEndpointParcelablela información de descripción del flujo de audio representada por, principalmente la media.aaudioinformación relacionada con la memoria compartida utilizada para la comunicación posterior con el servicio;
  3. Lectura y escritura de datos de audio.

Los objetos de datos utilizados para representar solicitudes y respuestas al abrir una transmisión de audio tienen esta estructura:

Estructura de datos en Open Stream

android::content::AttributionSourceStateEl objeto contiene principalmente información del proceso actual, como nombre del paquete, ID de usuario, ID de proceso, etc.; el AAudioStreamConfigurationobjeto contiene principalmente información de configuración de la transmisión de audio, como ID del dispositivo, frecuencia de muestreo, número de canales, contenido. tipo, modo de rendimiento, etc. Los datos que se pueden pasar a través de procesos a través de Binder IPC son solo Parcelableobjetos que admiten mecanismos de serialización. AAudioStreamConfigurationLos datos del objeto StreamParametersse pasan a través del objeto AIDL, AAudioStreamConfigurationlo que proporciona parcelable()operaciones para crear StreamParametersobjetos según el estado del objeto actual y admite StreamParametersla creación de objetos basados ​​en AAudioStreamConfigurationobjetos. Los datos del objeto se pasan AAudioStreamRequesta través de objetos AIDL .StreamRequest

El objeto de datos relacionado con la descripción del flujo de audio tiene la siguiente estructura:

Estructura de datos para la descripción de la transmisión de audio

Los objetos en el cuadro rectangular amarillo en la figura son objetos AIDL, que se utilizan directamente para el intercambio de datos de Binder IPC.

La figura anterior SharedMemoryParcelablese utiliza para representar un espacio de memoria compartida vinculado a un determinado descriptor de archivo, como un mmap en un dispositivo de audio. Puede dividirse en múltiples áreas diferentes. Esta memoria se puede compartir usando Binder o entre procesos. La definición de esta clase (ubicada en frameworks/av/media/libaaudio/src/binding/SharedMemoryParcelable.h ) es la siguiente:

class SharedMemoryParcelable {
public:
    SharedMemoryParcelable() = default;

    // Ctor from a parcelable representation.
    // Since the parcelable object owns a unique FD, move semantics are provided to avoid the need
    // to dupe.
    explicit SharedMemoryParcelable(android::media::SharedFileRegion&& parcelable);

    /**
     * Make a dup() of the fd and store it for later use.
     *
     * @param fd
     * @param sizeInBytes
     */
    void setup(const android::base::unique_fd& fd, int32_t sizeInBytes);

    // mmap() shared memory
    aaudio_result_t resolve(int32_t offsetInBytes, int32_t sizeInBytes, void **regionAddressPtr);

    // munmap() any mapped memory
    aaudio_result_t close();

    int32_t getSizeInBytes();

    void dump();

    // Extract a parcelable representation of this object.
    // Since we own a unique FD, move semantics are provided to avoid the need to dupe.
    android::media::SharedFileRegion parcelable() &&;

    // Copy this instance. Duplicates the underlying FD.
    SharedMemoryParcelable dup() const;

private:
#define MMAP_UNRESOLVED_ADDRESS    reinterpret_cast<uint8_t*>(MAP_FAILED)

    android::base::unique_fd   mFd;
    int64_t                    mSizeInBytes = 0;
    int64_t                    mOffsetInBytes = 0;
    uint8_t                   *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;

    aaudio_result_t resolveSharedMemory(const android::base::unique_fd& fd);
    aaudio_result_t validate() const;
};

} /* namespace aaudio */

SharedMemoryParcelableContiene la memoria compartida y su descriptor de archivo asociado, el tamaño del bloque de memoria, el desplazamiento del bloque de memoria y la dirección del bloque de memoria en el proceso actual. En términos generales, la descripción del archivo es el índice de la tabla de descriptores de archivos del proceso. Solo es válido para el proceso actual que tiene el archivo abierto. Es un recurso que pertenece al proceso. Al pasar el descriptor de archivos, es necesario se creará en la tabla de descriptores de archivos del proceso de destino. El elemento correspondiente apunta al archivo original y el mecanismo Binder de Android proporciona la capacidad de pasar descriptores de archivos entre procesos. El descriptor de archivo que se ve aquí puede entenderse como algo cuyo valor real puede ser diferente del descriptor de archivo original en otro proceso, pero apunta al mismo archivo real.

SharedMemoryParcelableCree a partir del objeto AIDL utilizado directamente para la comunicación Binder IPC SharedFileRegion, o cree el objeto basándose en el objeto actual (ubicado en frameworks/av/media/libaaudio/src/binding/SharedMemoryParcelable.cpp ) de la siguiente manera:

SharedMemoryParcelable::SharedMemoryParcelable(SharedFileRegion&& parcelable) {
    mFd = parcelable.fd.release();
    mSizeInBytes = parcelable.size;
    mOffsetInBytes = parcelable.offset;
}

SharedFileRegion SharedMemoryParcelable::parcelable() && {
    SharedFileRegion result;
    result.fd.reset(std::move(mFd));
    result.size = mSizeInBytes;
    result.offset = mOffsetInBytes;
    return result;
}

SharedMemoryParcelable::resolve()La función se utiliza para analizar y obtener una determinada área de memoria compartida en el bloque de memoria compartida. Realizará el mapeo de memoria cuando sea necesario. La definición de esta función (ubicada en frameworks/av/media/libaaudio/src/binding/SharedMemoryParcelable.cpp ) es el siguiente:

aaudio_result_t SharedMemoryParcelable::resolveSharedMemory(const unique_fd& fd) {
    mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ | PROT_WRITE,
                                        MAP_SHARED, fd.get(), 0);
    if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
        ALOGE("mmap() failed for fd = %d, nBytes = %" PRId64 ", errno = %s",
              fd.get(), mSizeInBytes, strerror(errno));
        return AAUDIO_ERROR_INTERNAL;
    }
    return AAUDIO_OK;
}

aaudio_result_t SharedMemoryParcelable::resolve(int32_t offsetInBytes, int32_t sizeInBytes,
                                              void **regionAddressPtr) {
    if (offsetInBytes < 0) {
        ALOGE("illegal offsetInBytes = %d", offsetInBytes);
        return AAUDIO_ERROR_OUT_OF_RANGE;
    } else if ((offsetInBytes + sizeInBytes) > mSizeInBytes) {
        ALOGE("out of range, offsetInBytes = %d, "
                      "sizeInBytes = %d, mSizeInBytes = %" PRId64,
              offsetInBytes, sizeInBytes, mSizeInBytes);
        return AAUDIO_ERROR_OUT_OF_RANGE;
    }

    aaudio_result_t result = AAUDIO_OK;

    if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
        if (mFd.get() != -1) {
            result = resolveSharedMemory(mFd);
        } else {
            ALOGE("has no file descriptor for shared memory.");
            result = AAUDIO_ERROR_INTERNAL;
        }
    }

    if (result == AAUDIO_OK && mResolvedAddress != MMAP_UNRESOLVED_ADDRESS) {
        *regionAddressPtr = mResolvedAddress + offsetInBytes;
        ALOGV("mResolvedAddress = %p", mResolvedAddress);
        ALOGV("offset by %d, *regionAddressPtr = %p", offsetInBytes, *regionAddressPtr);
    }
    return result;
}

SharedRegionParcelableDescribe SharedMemoryParcelableuna región de memoria compartida en el bloque de memoria compartida descrito por. La definición de esta clase (ubicada en frameworks/av/media/libaaudio/src/binding/SharedRegionParcelable.h ) es la siguiente:

namespace aaudio {

class SharedRegionParcelable {
public:
    SharedRegionParcelable() = default;

    // Construct based on a parcelable representation.
    explicit SharedRegionParcelable(const SharedRegion& parcelable);

    void setup(int32_t sharedMemoryIndex, int32_t offsetInBytes, int32_t sizeInBytes);

    aaudio_result_t resolve(SharedMemoryParcelable *memoryParcels, void **regionAddressPtr);

    bool isFileDescriptorSafe(SharedMemoryParcelable *memoryParcels);

    void dump();

    // Extract a parcelable representation of this object.
    SharedRegion parcelable() const;

private:
    int32_t mSharedMemoryIndex = -1;
    int32_t mOffsetInBytes     = 0;
    int32_t mSizeInBytes       = 0;

    aaudio_result_t validate() const;
};

} /* namespace aaudio */

Como se puede ver en SharedMemoryParcelable::validate()la función, el rango de tamaño del bloque de memoria compartida es (0 kB, 256 kB):

aaudio_result_t SharedMemoryParcelable::validate() const {
    if (mSizeInBytes < 0 || mSizeInBytes >= MAX_MMAP_SIZE_BYTES) {
        ALOGE("invalid mSizeInBytes = %" PRId64, mSizeInBytes);
        return AAUDIO_ERROR_OUT_OF_RANGE;
    }
    if (mOffsetInBytes != 0) {
        ALOGE("invalid mOffsetInBytes = %" PRId64, mOffsetInBytes);
        return AAUDIO_ERROR_OUT_OF_RANGE;
    }
    return AAUDIO_OK;
}

SharedRegionParcelableContiene SharedMemoryParcelableinformación que apunta a un área de memoria compartida en un bloque de memoria compartida en una matriz. Al ejecutar, SharedRegionParcelable::resolve()obtenga la dirección de esta área de memoria compartida en el proceso actual:

aaudio_result_t SharedRegionParcelable::resolve(SharedMemoryParcelable *memoryParcels,
                                              void **regionAddressPtr) {
    if (mSizeInBytes == 0) {
        *regionAddressPtr = nullptr;
        return AAUDIO_OK;
    }
    if (mSharedMemoryIndex < 0) {
        ALOGE("invalid mSharedMemoryIndex = %d", mSharedMemoryIndex);
        return AAUDIO_ERROR_INTERNAL;
    }
    SharedMemoryParcelable *memoryParcel = &memoryParcels[mSharedMemoryIndex];
    return memoryParcel->resolve(mOffsetInBytes, mSizeInBytes, regionAddressPtr);
}

El sistema operativo proporciona la infraestructura de memoria compartida subyacente, pero para comunicarse verdaderamente a través de la memoria compartida entre dos procesos, se requiere más diseño de protocolo, como por ejemplo cómo sincronizar el acceso al área de memoria compartida entre los dos procesos. RingBufferParcelableEncapsula varias áreas de memoria compartida e implementa una ruta de datos para intercambiar grandes bloques de datos a través de la memoria compartida. RingBufferParcelableLa definición de la clase (ubicada en frameworks/av/media/libaaudio/src/binding/RingBufferParcelable.h ) es la siguiente:

namespace aaudio {

class RingBufferParcelable  {
public:
    RingBufferParcelable() = default;

    // Construct based on a parcelable representation.
    explicit RingBufferParcelable(const RingBuffer& parcelable);

    // TODO This assumes that all three use the same SharedMemoryParcelable
    void setupMemory(int32_t sharedMemoryIndex,
                     int32_t dataMemoryOffset,
                     int32_t dataSizeInBytes,
                     int32_t readCounterOffset,
                     int32_t writeCounterOffset,
                     int32_t counterSizeBytes);

    void setupMemory(int32_t sharedMemoryIndex,
                     int32_t dataMemoryOffset,
                     int32_t dataSizeInBytes);

    int32_t getBytesPerFrame();

    void setBytesPerFrame(int32_t bytesPerFrame);

    int32_t getFramesPerBurst();

    void setFramesPerBurst(int32_t framesPerBurst);

    int32_t getCapacityInFrames();

    void setCapacityInFrames(int32_t capacityInFrames);

    bool isFileDescriptorSafe(SharedMemoryParcelable *memoryParcels);

    aaudio_result_t resolve(SharedMemoryParcelable *memoryParcels, RingBufferDescriptor *descriptor);

    void dump();

    // Extract a parcelable representation of this object.
    RingBuffer parcelable() const;

private:
    SharedRegionParcelable  mReadCounterParcelable;
    SharedRegionParcelable  mWriteCounterParcelable;
    SharedRegionParcelable  mDataParcelable;
    int32_t                 mBytesPerFrame = 0;     // index is in frames
    int32_t                 mFramesPerBurst = 0;    // for ISOCHRONOUS queues
    int32_t                 mCapacityInFrames = 0;  // zero if unused
    RingbufferFlags         mFlags = RingbufferFlags::NONE;

    aaudio_result_t validate() const;
};

} /* namespace aaudio */

Específicamente, RingBufferParcelablese encapsulan tres áreas de memoria compartida, que se utilizan para contadores de lectura, contadores de escritura y transferencia de datos de audio, respectivamente. El método para crear y crear objetos AIDL RingBufferParcelablebasados ​​​​en objetos AIDL (ubicados en frameworks/av/media/libaaudio/src/binding/RingBufferParcelable.cpp ) es el siguiente:RingBufferRingBuffer

RingBufferParcelable::RingBufferParcelable(const RingBuffer& parcelable)
        : mReadCounterParcelable(std::move(parcelable.readCounterParcelable)),
          mWriteCounterParcelable(std::move(parcelable.writeCounterParcelable)),
          mDataParcelable(std::move(parcelable.dataParcelable)),
          mBytesPerFrame(parcelable.bytesPerFrame),
          mFramesPerBurst(parcelable.framesPerBurst),
          mCapacityInFrames(parcelable.capacityInFrames),
          mFlags(static_cast<RingbufferFlags>(parcelable.flags)) {
    static_assert(sizeof(mFlags) == sizeof(parcelable.flags));
}

RingBuffer RingBufferParcelable::parcelable() const {
    RingBuffer result;
    result.readCounterParcelable = std::move(mReadCounterParcelable).parcelable();
    result.writeCounterParcelable = std::move(mWriteCounterParcelable).parcelable();
    result.dataParcelable = std::move(mDataParcelable).parcelable();
    result.bytesPerFrame = mBytesPerFrame;
    result.framesPerBurst = mFramesPerBurst;
    result.capacityInFrames = mCapacityInFrames;
    static_assert(sizeof(mFlags) == sizeof(result.flags));
    result.flags = static_cast<int32_t>(mFlags);
    return result;
}

RingBufferParcelable::setupMemory()La función configura tres áreas de memoria en la misma memoria:

void RingBufferParcelable::setupMemory(int32_t sharedMemoryIndex,
                 int32_t dataMemoryOffset,
                 int32_t dataSizeInBytes,
                 int32_t readCounterOffset,
                 int32_t writeCounterOffset,
                 int32_t counterSizeBytes) {
    mReadCounterParcelable.setup(sharedMemoryIndex, readCounterOffset, counterSizeBytes);
    mWriteCounterParcelable.setup(sharedMemoryIndex, writeCounterOffset, counterSizeBytes);
    mDataParcelable.setup(sharedMemoryIndex, dataMemoryOffset, dataSizeInBytes);
}

void RingBufferParcelable::setupMemory(int32_t sharedMemoryIndex,
                 int32_t dataMemoryOffset,
                 int32_t dataSizeInBytes) {
    mReadCounterParcelable.setup(sharedMemoryIndex, 0, 0);
    mWriteCounterParcelable.setup(sharedMemoryIndex, 0, 0);
    mDataParcelable.setup(sharedMemoryIndex, dataMemoryOffset, dataSizeInBytes);
}

RingBufferParcelable::resolve()La función se utiliza para obtener las direcciones de tres áreas de memoria:

aaudio_result_t RingBufferParcelable::resolve(SharedMemoryParcelable *memoryParcels, RingBufferDescriptor *descriptor) {
    aaudio_result_t result;

    result = mReadCounterParcelable.resolve(memoryParcels,
                                            (void **) &descriptor->readCounterAddress);
    if (result != AAUDIO_OK) {
        return result;
    }

    result = mWriteCounterParcelable.resolve(memoryParcels,
                                             (void **) &descriptor->writeCounterAddress);
    if (result != AAUDIO_OK) {
        return result;
    }

    result = mDataParcelable.resolve(memoryParcels, (void **) &descriptor->dataAddress);
    if (result != AAUDIO_OK) {
        return result;
    }

    descriptor->bytesPerFrame = mBytesPerFrame;
    descriptor->framesPerBurst = mFramesPerBurst;
    descriptor->capacityInFrames = mCapacityInFrames;
    descriptor->flags = mFlags;
    return AAUDIO_OK;
}

Un flujo de audio necesita media.aaudiomantener múltiples rutas de datos basadas en la memoria compartida AudioEndpointParcelablecon el servicio y mantener información relevante en un flujo de audio media.aaudioque se comunica con el servicio a través de la memoria compartida, incluida SharedMemoryParcelableinformación sobre múltiples bloques de memoria compartida y rutas de datos para diferentes propósitos RingBufferParcelable. . AudioEndpointParcelableLa definición de la clase (ubicada en frameworks/av/media/libaaudio/src/binding/AudioEndpointParcelable.h ) es la siguiente:

namespace aaudio {

/**
 * Container for information about the message queues plus
 * general stream information needed by AAudio clients.
 * It contains no addresses, just sizes, offsets and file descriptors for
 * shared memory that can be passed through Binder.
 */
class AudioEndpointParcelable {
public:
    AudioEndpointParcelable() = default;

    // Ctor/assignment from a parcelable representation.
    // Since the parcelable object owns unique FDs (for shared memory blocks), move semantics are
    // provided to avoid the need to dupe.
    AudioEndpointParcelable(Endpoint&& parcelable);
    AudioEndpointParcelable& operator=(Endpoint&& parcelable);

    /**
     * Add the file descriptor to the table.
     * @return index in table or negative error
     */
    int32_t addFileDescriptor(const android::base::unique_fd& fd, int32_t sizeInBytes);

    aaudio_result_t resolve(EndpointDescriptor *descriptor);

    aaudio_result_t close();

    void dump();

    // Extract a parcelable representation of this object.
    // Since our shared memory objects own a unique FD, move semantics are provided to avoid the
    // need to dupe.
    Endpoint parcelable()&&;

public: // TODO add getters
    // Set capacityInFrames to zero if Queue is unused.
    RingBufferParcelable    mUpMessageQueueParcelable;   // server to client
    RingBufferParcelable    mDownMessageQueueParcelable; // to server
    RingBufferParcelable    mUpDataQueueParcelable;      // eg. record, could share same queue
    RingBufferParcelable    mDownDataQueueParcelable;    // eg. playback

private:
    aaudio_result_t         validate() const;

    int32_t                 mNumSharedMemories = 0;
    SharedMemoryParcelable  mSharedMemories[MAX_SHARED_MEMORIES];
};

} /* namespace aaudio */

AudioEndpointParcelableLa información del objeto proviene del objeto AIDL Endpoint. AudioEndpointParcelable::resolve()La función analiza la información de la memoria compartida, establece la asignación de memoria para la memoria compartida en el proceso actual, etc.:

aaudio_result_t AudioEndpointParcelable::resolve(EndpointDescriptor *descriptor) {
    aaudio_result_t result = mUpMessageQueueParcelable.resolve(mSharedMemories,
                                                           &descriptor->upMessageQueueDescriptor);
    if (result != AAUDIO_OK) return result;
    result = mDownMessageQueueParcelable.resolve(mSharedMemories,
                                        &descriptor->downMessageQueueDescriptor);
    if (result != AAUDIO_OK) return result;

    result = mDownDataQueueParcelable.resolve(mSharedMemories,
                                              &descriptor->dataQueueDescriptor);
    return result;
}

AudioEndpointParcelable, RingBufferParcelable, SharedRegionParcelableetc. SharedMemoryParcelableno se utilizan directamente para media.aaudiola comunicación con el servicio. AudioEndpointParcelable::resolve()La información relacionada con la memoria compartida para la resolución de funciones es EndpointDescriptormantenida por . EndpointDescriptorLa definición (ubicada en frameworks/av/media/libaaudio/src/binding/AAudioServiceDefinitions.h ) es la siguiente:

// This must be a fixed width so it can be in shared memory.
enum RingbufferFlags : uint32_t {
    NONE = 0,
    RATE_ISOCHRONOUS = 0x0001,
    RATE_ASYNCHRONOUS = 0x0002,
    COHERENCY_DMA = 0x0004,
    COHERENCY_ACQUIRE_RELEASE = 0x0008,
    COHERENCY_AUTO = 0x0010,
};

// This is not passed through Binder.
// Client side code will convert Binder data and fill this descriptor.
typedef struct RingBufferDescriptor_s {
    uint8_t* dataAddress;       // offset from read or write block
    int64_t* writeCounterAddress;
    int64_t* readCounterAddress;
    int32_t  bytesPerFrame;     // index is in frames
    int32_t  framesPerBurst;    // for ISOCHRONOUS queues
    int32_t  capacityInFrames;  // zero if unused
    RingbufferFlags flags;
} RingBufferDescriptor;

// This is not passed through Binder.
// Client side code will convert Binder data and fill this descriptor.
typedef struct EndpointDescriptor_s {
    // Set capacityInFrames to zero if Queue is unused.
    RingBufferDescriptor upMessageQueueDescriptor;   // server to client
    RingBufferDescriptor downMessageQueueDescriptor; // client to server
    RingBufferDescriptor dataQueueDescriptor;    // playback or capture
} EndpointDescriptor;

Una transmisión y media.aaudioun servicio de audio en realidad tienen tres canales de datos, que se utilizan para enviar mensajes, recibir mensajes y transmitir datos de audio.

quinceañera

No es difícil ver que no es muy conveniente comunicarse directamente EndpointDescriptorcon media.aaudioel servicio. El módulo FIFO encapsula bloques de memoria compartida utilizados para la comunicación entre procesos y proporciona operaciones más convenientes. La relación estructural de cada clase en el módulo quince es la siguiente:

Audio FIFO

FifoBufferRepresenta un canal de datos, FifoControllerBaseque se utiliza para mantener FifoBuffercontadores de lectura y contadores de escritura. FifoBufferIndirectImplementado para bloques de memoria compartida basados ​​en la comunicación entre procesos FifoBuffere FifoBufferAllocatedimplementado en función de la memoria asignada en el montón FifoBuffer.

La transmisión de audio de AAudio AudioEndpointmantiene estos canales de datos. AudioEndpoint::configure()La función (ubicada en frameworks/av/media/libaaudio/src/client/AudioEndpoint.cpp ) EndpointDescriptorcrea FifoBufferun objeto basado en:

// TODO Consider moving to a method in RingBufferDescriptor
static aaudio_result_t AudioEndpoint_validateQueueDescriptor(const char *type,
                                                  const RingBufferDescriptor *descriptor) {
    if (descriptor == nullptr) {
        ALOGE("AudioEndpoint_validateQueueDescriptor() NULL descriptor");
        return AAUDIO_ERROR_NULL;
    }

    if (descriptor->capacityInFrames < 1
        || descriptor->capacityInFrames > RIDICULOUSLY_LARGE_BUFFER_CAPACITY) {
        ALOGE("AudioEndpoint_validateQueueDescriptor() bad capacityInFrames = %d",
              descriptor->capacityInFrames);
        return AAUDIO_ERROR_OUT_OF_RANGE;
    }

    // Reject extreme values to catch bugs and prevent numeric overflows.
    if (descriptor->bytesPerFrame < 1
        || descriptor->bytesPerFrame > RIDICULOUSLY_LARGE_FRAME_SIZE) {
        ALOGE("AudioEndpoint_validateQueueDescriptor() bad bytesPerFrame = %d",
              descriptor->bytesPerFrame);
        return AAUDIO_ERROR_OUT_OF_RANGE;
    }

    if (descriptor->dataAddress == nullptr) {
        ALOGE("AudioEndpoint_validateQueueDescriptor() NULL dataAddress");
        return AAUDIO_ERROR_NULL;
    }
    ALOGV("AudioEndpoint_validateQueueDescriptor %s, dataAddress at %p ====================",
          type,
          descriptor->dataAddress);
    ALOGV("AudioEndpoint_validateQueueDescriptor  readCounter at %p, writeCounter at %p",
          descriptor->readCounterAddress,
          descriptor->writeCounterAddress);

    // Try to READ from the data area.
    // This code will crash if the mmap failed.
    uint8_t value = descriptor->dataAddress[0];
    ALOGV("AudioEndpoint_validateQueueDescriptor() dataAddress[0] = %d, then try to write",
        (int) value);
    // Try to WRITE to the data area.
    descriptor->dataAddress[0] = value * 3;
    ALOGV("AudioEndpoint_validateQueueDescriptor() wrote successfully");

    if (descriptor->readCounterAddress) {
        fifo_counter_t counter = *descriptor->readCounterAddress;
        ALOGV("AudioEndpoint_validateQueueDescriptor() *readCounterAddress = %d, now write",
              (int) counter);
        *descriptor->readCounterAddress = counter;
        ALOGV("AudioEndpoint_validateQueueDescriptor() wrote readCounterAddress successfully");
    }

    if (descriptor->writeCounterAddress) {
        fifo_counter_t counter = *descriptor->writeCounterAddress;
        ALOGV("AudioEndpoint_validateQueueDescriptor() *writeCounterAddress = %d, now write",
              (int) counter);
        *descriptor->writeCounterAddress = counter;
        ALOGV("AudioEndpoint_validateQueueDescriptor() wrote writeCounterAddress successfully");
    }

    return AAUDIO_OK;
}

aaudio_result_t AudioEndpoint_validateDescriptor(const EndpointDescriptor *pEndpointDescriptor) {
    aaudio_result_t result = AudioEndpoint_validateQueueDescriptor("messages",
                                    &pEndpointDescriptor->upMessageQueueDescriptor);
    if (result == AAUDIO_OK) {
        result = AudioEndpoint_validateQueueDescriptor("data",
                                                &pEndpointDescriptor->dataQueueDescriptor);
    }
    return result;
}

aaudio_result_t AudioEndpoint::configure(const EndpointDescriptor *pEndpointDescriptor,
                                         aaudio_direction_t   direction)
{
    aaudio_result_t result = AudioEndpoint_validateDescriptor(pEndpointDescriptor);
    if (result != AAUDIO_OK) {
        return result;
    }

    // ============================ up message queue =============================
    const RingBufferDescriptor *descriptor = &pEndpointDescriptor->upMessageQueueDescriptor;
    if(descriptor->bytesPerFrame != sizeof(AAudioServiceMessage)) {
        ALOGE("configure() bytesPerFrame != sizeof(AAudioServiceMessage) = %d",
              descriptor->bytesPerFrame);
        return AAUDIO_ERROR_INTERNAL;
    }

    if(descriptor->readCounterAddress == nullptr || descriptor->writeCounterAddress == nullptr) {
        ALOGE("configure() NULL counter address");
        return AAUDIO_ERROR_NULL;
    }

    // Prevent memory leak and reuse.
    if(mUpCommandQueue != nullptr || mDataQueue != nullptr) {
        ALOGE("configure() endpoint already used");
        return AAUDIO_ERROR_INTERNAL;
    }

    mUpCommandQueue = std::make_unique<FifoBufferIndirect>(
            descriptor->bytesPerFrame,
            descriptor->capacityInFrames,
            descriptor->readCounterAddress,
            descriptor->writeCounterAddress,
            descriptor->dataAddress
    );

    // ============================ data queue =============================
    descriptor = &pEndpointDescriptor->dataQueueDescriptor;
    ALOGV("configure() data framesPerBurst = %d", descriptor->framesPerBurst);
    ALOGV("configure() data readCounterAddress = %p",
          descriptor->readCounterAddress);

    // An example of free running is when the other side is read or written by hardware DMA
    // or a DSP. It does not update its counter so we have to update it.
    int64_t *remoteCounter = (direction == AAUDIO_DIRECTION_OUTPUT)
                             ? descriptor->readCounterAddress // read by other side
                             : descriptor->writeCounterAddress; // written by other side
    mFreeRunning = (remoteCounter == nullptr);
    ALOGV("configure() mFreeRunning = %d", mFreeRunning ? 1 : 0);

    int64_t *readCounterAddress = (descriptor->readCounterAddress == nullptr)
                                  ? &mDataReadCounter
                                  : descriptor->readCounterAddress;
    int64_t *writeCounterAddress = (descriptor->writeCounterAddress == nullptr)
                                  ? &mDataWriteCounter
                                  : descriptor->writeCounterAddress;

    // Clear buffer to avoid an initial glitch on some devices.
    size_t bufferSizeBytes = descriptor->capacityInFrames * descriptor->bytesPerFrame;
    memset(descriptor->dataAddress, 0, bufferSizeBytes);

    mDataQueue = std::make_unique<FifoBufferIndirect>(
            descriptor->bytesPerFrame,
            descriptor->capacityInFrames,
            readCounterAddress,
            writeCounterAddress,
            descriptor->dataAddress
    );
    uint32_t threshold = descriptor->capacityInFrames / 2;
    mDataQueue->setThreshold(threshold);
    return result;
}

Cada flujo de audio en realidad utiliza dos canales de datos, uno se usa para que media.aaudioel servicio envíe mensajes al cliente AAudio; el otro se usa para la transmisión de datos de audio.

AudioEndpoint::configure()El proceso de ejecución es aproximadamente el siguiente:

  1. Verifique el objeto pasado EndpointDescriptor;
  2. Cree un canal de datos para que media.aaudioel servicio envíe mensajes al cliente AAudio FifoBufferIndirect, el formato del mensaje será AAudioServiceMessage;
  3. Creado para el canal de datos utilizado para la transferencia de datos de audio FifoBufferIndirect.

FifoBufferProporciona principalmente operaciones de lectura y escritura en el canal de datos. El proceso general de operaciones de lectura y escritura es obtener primero FifoBufferel paquete del área de memoria disponible y luego leer y escribir el área de memoria disponible. El ajuste del área de memoria disponible se struct WrappingBufferdescribe en y la definición de esta estructura (ubicada en frameworks/av/media/libaaudio/src/fifo/FifoBuffer.h ) es la siguiente:

struct WrappingBuffer {
    enum {
        SIZE = 2
    };
    void *data[SIZE];
    int32_t numFrames[SIZE];
};

Dado que el bloque de memoria del canal de datos es un búfer en anillo, el área de memoria disponible puede abarcar el límite del búfer, por lo que las áreas de memoria en la cola y el cabezal del búfer se describen por separado. getFullDataAvailable(WrappingBuffer *wrappingBuffer)Se utiliza para obtener un área de datos legibles y getEmptyRoomAvailable(WrappingBuffer *wrappingBuffer)un área de datos grabables. Las definiciones de estas dos funciones (ubicadas en frameworks/av/media/libaaudio/src/fifo/FifoBuffer.cpp ) son las siguientes:

void FifoBuffer::fillWrappingBuffer(WrappingBuffer *wrappingBuffer,
                                    int32_t framesAvailable,
                                    int32_t startIndex) {
    wrappingBuffer->data[1] = nullptr;
    wrappingBuffer->numFrames[1] = 0;
    uint8_t *storage = getStorage();
    if (framesAvailable > 0) {
        fifo_frames_t capacity = mFifo->getCapacity();
        uint8_t *source = &storage[convertFramesToBytes(startIndex)];
        // Does the available data cross the end of the FIFO?
        if ((startIndex + framesAvailable) > capacity) {
            wrappingBuffer->data[0] = source;
            fifo_frames_t firstFrames = capacity - startIndex;
            wrappingBuffer->numFrames[0] = firstFrames;
            wrappingBuffer->data[1] = &storage[0];
            wrappingBuffer->numFrames[1] = framesAvailable - firstFrames;
        } else {
            wrappingBuffer->data[0] = source;
            wrappingBuffer->numFrames[0] = framesAvailable;
        }
    } else {
        wrappingBuffer->data[0] = nullptr;
        wrappingBuffer->numFrames[0] = 0;
    }
}

fifo_frames_t FifoBuffer::getFullDataAvailable(WrappingBuffer *wrappingBuffer) {
    // The FIFO might be overfull so clip to capacity.
    fifo_frames_t framesAvailable = std::min(mFifo->getFullFramesAvailable(),
                                             mFifo->getCapacity());
    fifo_frames_t startIndex = mFifo->getReadIndex();
    fillWrappingBuffer(wrappingBuffer, framesAvailable, startIndex);
    return framesAvailable;
}

fifo_frames_t FifoBuffer::getEmptyRoomAvailable(WrappingBuffer *wrappingBuffer) {
    // The FIFO might have underrun so clip to capacity.
    fifo_frames_t framesAvailable = std::min(mFifo->getEmptyFramesAvailable(),
                                             mFifo->getCapacity());
    fifo_frames_t startIndex = mFifo->getWriteIndex();
    fillWrappingBuffer(wrappingBuffer, framesAvailable, startIndex);
    return framesAvailable;
}

FifoBufferLos usuarios de pueden obtener los buffers disponibles a través getFullDataAvailable(WrappingBuffer *wrappingBuffer)de y , y luego pasar datos o mensajes a través de los buffers obtenidos, o pueden usar directamente las operaciones y Las definiciones de estas dos funciones (ubicadas en frameworks/av/media/libaaudio/src/fifo /FifoBuffer.cpp ) de la siguiente manera:getEmptyRoomAvailable(WrappingBuffer *wrappingBuffer)FifoBufferread()write()

fifo_frames_t FifoBuffer::read(void *buffer, fifo_frames_t numFrames) {
    WrappingBuffer wrappingBuffer;
    uint8_t *destination = (uint8_t *) buffer;
    fifo_frames_t framesLeft = numFrames;

    getFullDataAvailable(&wrappingBuffer);

    // Read data in one or two parts.
    int partIndex = 0;
    while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
        fifo_frames_t framesToRead = framesLeft;
        fifo_frames_t framesAvailable = wrappingBuffer.numFrames[partIndex];
        if (framesAvailable > 0) {
            if (framesToRead > framesAvailable) {
                framesToRead = framesAvailable;
            }
            int32_t numBytes = convertFramesToBytes(framesToRead);
            memcpy(destination, wrappingBuffer.data[partIndex], numBytes);

            destination += numBytes;
            framesLeft -= framesToRead;
        } else {
            break;
        }
        partIndex++;
    }
    fifo_frames_t framesRead = numFrames - framesLeft;
    mFifo->advanceReadIndex(framesRead);
    return framesRead;
}

fifo_frames_t FifoBuffer::write(const void *buffer, fifo_frames_t numFrames) {
    WrappingBuffer wrappingBuffer;
    uint8_t *source = (uint8_t *) buffer;
    fifo_frames_t framesLeft = numFrames;

    getEmptyRoomAvailable(&wrappingBuffer);

    // Read data in one or two parts.
    int partIndex = 0;
    while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
        fifo_frames_t framesToWrite = framesLeft;
        fifo_frames_t framesAvailable = wrappingBuffer.numFrames[partIndex];
        if (framesAvailable > 0) {
            if (framesToWrite > framesAvailable) {
                framesToWrite = framesAvailable;
            }
            int32_t numBytes = convertFramesToBytes(framesToWrite);
            memcpy(wrappingBuffer.data[partIndex], source, numBytes);

            source += numBytes;
            framesLeft -= framesToWrite;
        } else {
            break;
        }
        partIndex++;
    }
    fifo_frames_t framesWritten = numFrames - framesLeft;
    mFifo->advanceWriteIndex(framesWritten);
    return framesWritten;
}

FifoBufferEl proceso general de las operaciones read()y write()es el siguiente:

  1. Obtenga el buffer disponible a través de getFullDataAvailable(WrappingBuffer *wrappingBuffer)o ;getEmptyRoomAvailable(WrappingBuffer *wrappingBuffer)
  2. Leer algunos datos;
  3. Actualizar el contador de lectura o el contador de escritura.

Tanto el contador de lectura como el contador de escritura aumentan monótonamente. La cantidad de datos legibles en el buffer es el contador de escritura menos el contador de lectura. La cantidad de datos grabables es el umbral menos la cantidad de datos legibles. El puntero de lectura y el puntero de escritura pasar por el contador de lectura y escritura El contador se obtiene dividiendo el resto por la capacidad del búfer, como se muestra en el siguiente código (ubicado en frameworks/av/media/libaaudio/src/fifo/FifoControllerBase.cpp ):

FifoControllerBase::FifoControllerBase(fifo_frames_t capacity, fifo_frames_t threshold)
        : mCapacity(capacity)
        , mThreshold(threshold)
{
}

FifoControllerBase::~FifoControllerBase() {
}

fifo_frames_t FifoControllerBase::getFullFramesAvailable() {
    fifo_frames_t temp = 0;
    __builtin_sub_overflow(getWriteCounter(), getReadCounter(), &temp);
    return temp;
}

fifo_frames_t FifoControllerBase::getReadIndex() {
    // % works with non-power of two sizes
    return (fifo_frames_t) ((uint64_t)getReadCounter() % mCapacity);
}

void FifoControllerBase::advanceReadIndex(fifo_frames_t numFrames) {
   fifo_counter_t temp = 0;
    __builtin_add_overflow(getReadCounter(), numFrames, &temp);
    setReadCounter(temp);
}

fifo_frames_t FifoControllerBase::getEmptyFramesAvailable() {
    return (int32_t)(mThreshold - getFullFramesAvailable());
}

fifo_frames_t FifoControllerBase::getWriteIndex() {
    // % works with non-power of two sizes
    return (fifo_frames_t) ((uint64_t)getWriteCounter() % mCapacity);
}

void FifoControllerBase::advanceWriteIndex(fifo_frames_t numFrames) {
    fifo_counter_t temp = 0;
    __builtin_add_overflow(getWriteCounter(), numFrames, &temp);
    setWriteCounter(temp);
}

void FifoControllerBase::setThreshold(fifo_frames_t threshold) {
    if (threshold > mCapacity) {
        threshold = mCapacity;
    } else if (threshold < 0) {
        threshold = 0;
    }
    mThreshold = threshold;
}

FifoBufferLos usuarios de no pueden operarlo directamente FifoControllerBase, y FifoBufferlas operaciones de lectura y escritura están estrictamente controladas por él, por lo que la lectura y la escritura no cruzarán el límite.

Procesamiento de datos en AAudio

El objetivo principal de AAudio es la recopilación y reproducción de audio de alto rendimiento, pero también puede realizar un procesamiento simple de los datos que se van a reproducir. Específicamente, puede recibir datos de muestra de audio en los formatos de float, int16_t, int 24y int32_ty convertirlos en datos de muestra de audio en estos formatos para su entrega. Entre la recepción de datos y su transmisión, puede realizar aumentos de volumen, limitación del rango de muestra de audio y procesamiento mono a multicanal de los datos. El procesamiento de datos entre la recepción de los datos y su transferencia solo se floatrealiza para los datos en el formato. AudioSourceEl componente utilizado para recibir los datos convierte los datos en cualquier formato a floatlos datos en el formato. El componente utilizado para pasar los datos Convierta AudioSinklos floatdatos en el formato al formato de datos deseado.

La estructura de las clases relacionadas con el procesamiento de datos en AAudio es la siguiente:

Componente de procesamiento de audio

AAudio está definido AudioProcessorBasepara representar componentes del nodo de procesamiento de datos de audio. La definición de esta clase (ubicada en frameworks/av/media/libaaudio/src/flowgraph/AudioProcessorBase.h ) es la siguiente:

class AudioProcessorBase {
public:
    virtual ~AudioProcessorBase() = default;

    /**
     * Perform custom function.
     *
     * @param framePosition index of first frame to be processed
     * @param numFrames maximum number of frames requested for processing
     * @return number of frames actually processed
     */
    virtual int32_t onProcess(int64_t framePosition, int32_t numFrames) = 0;

    /**
     * If the framePosition is at or after the last frame position then call onProcess().
     * This prevents infinite recursion in case of cyclic graphs.
     * It also prevents nodes upstream from a branch from being executed twice.
     *
     * @param framePosition
     * @param numFrames
     * @return
     */
    int32_t pullData(int64_t framePosition, int32_t numFrames);

protected:
    int64_t  mLastFramePosition = -1; // Start at -1 so that the first pull works.

private:
    int32_t  mFramesValid = 0; // num valid frames in the block
};

AudioProcessorBaseLa onProcess()función realiza operaciones de procesamiento de datos personalizadas pullData()para impulsar AudioProcessorBaseel procesamiento de datos. AudioProcessorBaseLa implementación es la siguiente:

int32_t AudioProcessorBase::pullData(int64_t framePosition, int32_t numFrames) {
    if (framePosition > mLastFramePosition) {
        mLastFramePosition = framePosition;
        mFramesValid = onProcess(framePosition, numFrames);
    }
    return mFramesValid;
}

pullData()El método se llama solo cuando el parámetro entrante framePositiones posterior al último cuadro de audio procesado onProcess(), lo que evita la recursividad infinita cuando el gráfico de procesamiento de datos de audio forma un bucle. También evita que los nodos anteriores a la rama se ejecuten dos veces.

AAudio define AudioPortconectores para permitir que los datos AudioProcessorBasefluyan entre diferentes módulos. Las definiciones de estas clases (ubicadas en frameworks/av/media/libaaudio/src/flowgraph/AudioProcessorBase.h ) son las siguientes:

class AudioPort {
public:
    AudioPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
            : mParent(parent)
            , mSamplesPerFrame(samplesPerFrame) {
    }

    // Ports are often declared public. So let's make them non-copyable.
    AudioPort(const AudioPort&) = delete;
    AudioPort& operator=(const AudioPort&) = delete;

    int32_t getSamplesPerFrame() const {
        return mSamplesPerFrame;
    }

protected:
    AudioProcessorBase &mParent;

private:
    const int32_t    mSamplesPerFrame = 1;
};

/***************************************************************************/
/**
 * This port contains a float type buffer.
 * The size is framesPerBlock * samplesPerFrame).
 */
class AudioFloatBlockPort  : public AudioPort {
public:
    AudioFloatBlockPort(AudioProcessorBase &mParent,
                   int32_t samplesPerFrame,
                   int32_t framesPerBlock = kDefaultBlockSize
                );

    virtual ~AudioFloatBlockPort();

    int32_t getFramesPerBlock() const {
        return mFramesPerBlock;
    }

protected:

    /**
     * @return buffer internal to the port or from a connected port
     */
    virtual float *getBlock() {
        return mSampleBlock;
    }


private:
    const int32_t    mFramesPerBlock = 1;
    float           *mSampleBlock = nullptr; // allocated in constructor
};

/***************************************************************************/
/**
  * The results of a module are stored in the buffer of the output ports.
  */
class AudioFloatOutputPort : public AudioFloatBlockPort {
public:
    AudioFloatOutputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
            : AudioFloatBlockPort(parent, samplesPerFrame) {
    }

    virtual ~AudioFloatOutputPort() = default;

    using AudioFloatBlockPort::getBlock;

    /**
     * Call the parent module's onProcess() method.
     * That may pull data from its inputs and recursively
     * process the entire graph.
     * @return number of frames actually pulled
     */
    int32_t pullData(int64_t framePosition, int32_t numFrames);

    /**
     * Connect to the input of another module.
     * An input port can only have one connection.
     * An output port can have multiple connections.
     * If you connect a second output port to an input port
     * then it overwrites the previous connection.
     *
     * This not thread safe. Do not modify the graph topology form another thread while running.
     */
    void connect(AudioFloatInputPort *port);

    /**
     * Disconnect from the input of another module.
     * This not thread safe.
     */
    void disconnect(AudioFloatInputPort *port);
};

/***************************************************************************/
class AudioFloatInputPort : public AudioFloatBlockPort {
public:
    AudioFloatInputPort(AudioProcessorBase &parent, int32_t samplesPerFrame)
            : AudioFloatBlockPort(parent, samplesPerFrame) {
    }

    virtual ~AudioFloatInputPort() = default;

    /**
     * If connected to an output port then this will return
     * that output ports buffers.
     * If not connected then it returns the input ports own buffer
     * which can be loaded using setValue().
     */
    float *getBlock() override;

    /**
     * Pull data from any output port that is connected.
     */
    int32_t pullData(int64_t framePosition, int32_t numFrames);

    /**
     * Write every value of the float buffer.
     * This value will be ignored if an output port is connected
     * to this port.
     */
    void setValue(float value) {
        int numFloats = kDefaultBlockSize * getSamplesPerFrame();
        float *buffer = getBlock();
        for (int i = 0; i < numFloats; i++) {
            *buffer++ = value;
        }
    }

    /**
     * Connect to the output of another module.
     * An input port can only have one connection.
     * An output port can have multiple connections.
     * This not thread safe.
     */
    void connect(AudioFloatOutputPort *port) {
        assert(getSamplesPerFrame() == port->getSamplesPerFrame());
        mConnected = port;
    }

    void disconnect(AudioFloatOutputPort *port) {
        assert(mConnected == port);
        (void) port;
        mConnected = nullptr;
    }

    void disconnect() {
        mConnected = nullptr;
    }

private:
    AudioFloatOutputPort *mConnected = nullptr;
};

AudioFloatBlockPortMantiene principalmente un búfer para la transferencia de datos. AudioProcessorBaseEl trabajo de conectar diferentes módulos lo realiza principalmente AudioFloatOutputPorty AudioFloatInputPort, que agrega operaciones como pullData(), connect()y . disconnect()La implementación de estos AudioPort(ubicada en frameworks/av/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp ) es la siguiente:

/***************************************************************************/
AudioFloatBlockPort::AudioFloatBlockPort(AudioProcessorBase &parent,
                               int32_t samplesPerFrame,
                               int32_t framesPerBlock)
        : AudioPort(parent, samplesPerFrame)
        , mFramesPerBlock(framesPerBlock)
        , mSampleBlock(NULL) {
    int32_t numFloats = framesPerBlock * getSamplesPerFrame();
    mSampleBlock = new float[numFloats]{0.0f};
}

AudioFloatBlockPort::~AudioFloatBlockPort() {
    delete[] mSampleBlock;
}

/***************************************************************************/
int32_t AudioFloatOutputPort::pullData(int64_t framePosition, int32_t numFrames) {
    numFrames = std::min(getFramesPerBlock(), numFrames);
    return mParent.pullData(framePosition, numFrames);
}

// These need to be in the .cpp file because of forward cross references.
void AudioFloatOutputPort::connect(AudioFloatInputPort *port) {
    port->connect(this);
}

void AudioFloatOutputPort::disconnect(AudioFloatInputPort *port) {
    port->disconnect(this);
}

/***************************************************************************/
int32_t AudioFloatInputPort::pullData(int64_t framePosition, int32_t numFrames) {
    return (mConnected == NULL)
            ? std::min(getFramesPerBlock(), numFrames)
            : mConnected->pullData(framePosition, numFrames);
}

float *AudioFloatInputPort::getBlock() {
    if (mConnected == NULL) {
        return AudioFloatBlockPort::getBlock(); // loaded using setValue()
    } else {
        return mConnected->getBlock();
    }
}

AudioPortLa forma en que funciona el diseño es esta:

  1. AudioProcessorBaseEl módulo contiene AudioFloatOutputPortobjetos y la interfaz AudioFloatOutputPortde pullData()se utiliza para impulsar la tubería de procesamiento de datos de audio para procesar datos de audio, es decir, llamar AudioProcessorBasey pullData()luego realizar AudioProcessorBasela operación de datos de onProcess()En AudioProcessorBasela onProcess()implementación de la operación de datos de, AudioProcessorBasese obtienen los datos a procesar desde otro lugar y se coloca después del procesamiento AudioFloatOutputPort... en el búfer;
  2. AudioProcessorBaseLos módulos contienen AudioFloatInputPortobjetos que se utilizan para AudioProcessorBaseobtener datos de otros módulos después de que hayan sido procesados. No es necesario que AudioFloatInputPortel objeto contenga una referencia al módulo que lo contiene ;AudioProcessorBase
  3. AudioFloatOutputPortLas operaciones y AudioFloatInputPortde connect()y se utilizan principalmente para conectar el módulo disconnect()anterior con el siguiente módulo .AudioProcessorBaseAudioFloatInputPortAudioProcessorBaseAudioFloatOutputPort

El proceso de procesamiento de datos de audio compuesto AudioProcessorBasepor y AudioFloatOutputPortserá como se muestra a continuación:AudioFloatOutputPort

Tubería de audio en AAudio

AudioProcessorBaseLa transferencia de datos entre diferentes módulos se completa mediante AudioFloatOutputPorty La transferencia de datos entre y dentro AudioFloatOutputPortdel mismo módulo se completa principalmente mediante la interfaz de operación de datos de .AudioProcessorBaseAudioFloatOutputPortAudioFloatOutputPortAudioProcessorBaseonProcess()

AAudio se define para representar el componente de nodo que AudioSourceprocesa datos de entrada, es decir, convierte un determinado tipo de datos de audio en datos de tipo. La definición de esta clase (ubicada en frameworks/av/media/libaaudio/src/flowgraph/AudioProcessorBase.h ) es como sigue:float

class AudioSource : public AudioProcessorBase {
public:
    explicit AudioSource(int32_t channelCount)
            : output(*this, channelCount) {
    }

    virtual ~AudioSource() = default;

    AudioFloatOutputPort output;

    void setData(const void *data, int32_t numFrames) {
        mData = data;
        mSizeInFrames = numFrames;
        mFrameIndex = 0;
    }

protected:
    const void *mData = nullptr;
    int32_t     mSizeInFrames = 0; // number of frames in mData
    int32_t     mFrameIndex = 0; // index of next frame to be processed
};

AudioSourceProporciona una interfaz con el mundo exterior para proporcionar datos de origen para el canal de procesamiento de datos de audio y pasa los datos convertidos a los módulos posteriores del canal de procesamiento de datos de audio setData()a través de él .AudioFloatOutputPortAudioProcessorBase

AAudio se define para representar el componente de nodo que AudioSinkprocesa datos de salida, es decir, convierte datos de tipo en un determinado tipo de datos de audio. La definición de esta clase (ubicada en frameworks/av/media/libaaudio/src/flowgraph/AudioProcessorBase.h ) es como sigue:float

class AudioSink : public AudioProcessorBase {
public:
    explicit AudioSink(int32_t channelCount)
            : input(*this, channelCount) {
    }

    virtual ~AudioSink() = default;

    AudioFloatInputPort input;

    /**
     * Do nothing. The work happens in the read() method.
     *
     * @param framePosition index of first frame to be processed
     * @param numFrames
     * @return number of frames actually processed
     */
    int32_t onProcess(int64_t framePosition, int32_t numFrames) override {
        (void) framePosition;
        (void) numFrames;
        return 0;
    };

    virtual int32_t read(void *data, int32_t numFrames) = 0;

protected:
    int32_t pull(int32_t numFrames);

private:
    int64_t mFramePosition = 0;
};

AudioSinkLa onProcess()función no es operativa, agrega una nueva read()operación para completar la operación de procesamiento de datos final en la canalización de procesamiento de datos de audio y obtener los datos procesados. AudioSinkEl diseño actual de se onProcess()implementará como una operación no operativa, lo que rompe la semántica de las pullData()interfaces heredadas onProcess(). Para AudioSinkel diseño de , existen otras opciones:

(1). No es necesario agregar read()una operación;
(2). Además de un AudioFloatInputPortobjeto, se incluye AudioSinkotro objeto; (3). Todo el proceso de procesamiento de datos de audio está controlado por el objeto ; (4). El original La interfaz se completa El trabajo se divide en dos partes: la parte de procesamiento de datos se coloca en la interfaz, los datos procesados ​​se guardan en el búfer de y la parte de adquisición de datos se completa directamente a través del objeto.AudioFloatOutputPort
AudioFloatOutputPortpullData()
read()onProcess()AudioFloatOutputPortAudioFloatOutputPort

Sin embargo, el diseño actual puede evitar una copia de datos. Este es probablemente un ejemplo de los esfuerzos realizados por la versatilidad o el compromiso con el rendimiento. A continuación se muestran algunos ejemplos más de diferentes tipos AudioProcessorBasede módulos. SourceI16Se utiliza para int16_tconvertir datos de origen de tipo en floatdatos de tipo AudioSource. La definición de esta clase (ubicada en frameworks/av/media/libaaudio/src/flowgraph/SourceI16.h ) es la siguiente:

namespace flowgraph {

class SourceI16 : public AudioSource {
public:
    explicit SourceI16(int32_t channelCount);

    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;
};

} /* namespace flowgraph */

La implementación de esta clase (ubicada en frameworks/av/media/libaaudio/src/flowgraph/SourceI16.cpp ) es la siguiente:

using namespace flowgraph;

SourceI16::SourceI16(int32_t channelCount)
        : AudioSource(channelCount) {
}

int32_t SourceI16::onProcess(int64_t framePosition, int32_t numFrames) {
    float *floatData = output.getBlock();
    int32_t channelCount = output.getSamplesPerFrame();

    int32_t framesLeft = mSizeInFrames - mFrameIndex;
    int32_t framesToProcess = std::min(numFrames, framesLeft);
    int32_t numSamples = framesToProcess * channelCount;

    const int16_t *shortBase = static_cast<const int16_t *>(mData);
    const int16_t *shortData = &shortBase[mFrameIndex * channelCount];

#ifdef __ANDROID__
    memcpy_to_float_from_i16(floatData, shortData, numSamples);
#else
    for (int i = 0; i < numSamples; i++) {
        *floatData++ = *shortData++ * (1.0f / 32768);
    }
#endif

    mFrameIndex += framesToProcess;
    return framesToProcess;
}

MonoToMultiConverterUbicado en el medio del proceso de procesamiento de datos de audio, se utiliza para convertir datos monocanal en datos multicanal. La definición de esta clase (ubicada en frameworks/av/media/libaaudio/src/flowgraph/MonoToMultiConverter.h ) es como sigue:

namespace flowgraph {

class MonoToMultiConverter : public AudioProcessorBase {
public:
    explicit MonoToMultiConverter(int32_t channelCount);

    virtual ~MonoToMultiConverter();

    int32_t onProcess(int64_t framePosition, int32_t numFrames) override;

    AudioFloatInputPort input;
    AudioFloatOutputPort output;
};

} /* namespace flowgraph */

La implementación de esta clase (ubicada en frameworks/av/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp ) es la siguiente:

using namespace flowgraph;

MonoToMultiConverter::MonoToMultiConverter(int32_t channelCount)
        : input(*this, 1)
        , output(*this, channelCount) {
}

MonoToMultiConverter::~MonoToMultiConverter() { }

int32_t MonoToMultiConverter::onProcess(int64_t framePosition, int32_t numFrames) {
    int32_t framesToProcess = input.pullData(framePosition, numFrames);

    const float *inputBuffer = input.getBlock();
    float *outputBuffer = output.getBlock();
    int32_t channelCount = output.getSamplesPerFrame();
    // TODO maybe move to audio_util as audio_mono_to_multi()
    for (int i = 0; i < framesToProcess; i++) {
        // read one, write many
        float sample = *inputBuffer++;
        for (int channel = 0; channel < channelCount; channel++) {
            *outputBuffer++ = sample;
        }
    }
    return framesToProcess;
}

SinkI16Convierte floatdatos de tipo en int16_tdatos de tipo AudioSink. La definición de esta clase (ubicada en frameworks/av/media/libaaudio/src/flowgraph/SinkI16.h ) es la siguiente:

namespace flowgraph {

class SinkI16 : public AudioSink {
public:
    explicit SinkI16(int32_t channelCount);

    int32_t read(void *data, int32_t numFrames) override;
};

} /* namespace flowgraph */

La implementación de esta clase (ubicada en frameworks/av/media/libaaudio/src/flowgraph/SinkI16.cpp ) es la siguiente:

using namespace flowgraph;

SinkI16::SinkI16(int32_t channelCount)
        : AudioSink(channelCount) {}

int32_t SinkI16::read(void *data, int32_t numFrames) {
    int16_t *shortData = (int16_t *) data;
    const int32_t channelCount = input.getSamplesPerFrame();

    int32_t framesLeft = numFrames;
    while (framesLeft > 0) {
        // Run the graph and pull data through the input port.
        int32_t framesRead = pull(framesLeft);
        if (framesRead <= 0) {
            break;
        }
        const float *signal = input.getBlock();
        int32_t numSamples = framesRead * channelCount;
#ifdef __ANDROID__
        memcpy_to_i16_from_float(shortData, signal, numSamples);
        shortData += numSamples;
        signal += numSamples;
#else
        for (int i = 0; i < numSamples; i++) {
            int32_t n = (int32_t) (*signal++ * 32768.0f);
            *shortData++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip
        }
#endif
        framesLeft -= framesRead;
    }
    return numFrames - framesLeft;
}

La canalización de procesamiento de datos de audio de AAudio es AAudioFlowGraphmantenida por. La definición de esta clase (ubicada en frameworks/av/media/libaaudio/src/client/AAudioFlowGraph.h ) es la siguiente:

class AAudioFlowGraph {
public:
    /** Connect several modules together to convert from source to sink.
     * This should only be called once for each instance.
     *
     * @param sourceFormat
     * @param sourceChannelCount
     * @param sinkFormat
     * @param sinkChannelCount
     * @return
     */
    aaudio_result_t configure(audio_format_t sourceFormat,
                              int32_t sourceChannelCount,
                              audio_format_t sinkFormat,
                              int32_t sinkChannelCount);

    void process(const void *source, void *destination, int32_t numFrames);

    /**
     * @param volume between 0.0 and 1.0
     */
    void setTargetVolume(float volume);

    void setRampLengthInFrames(int32_t numFrames);

private:
    std::unique_ptr<flowgraph::AudioSource>          mSource;
    std::unique_ptr<flowgraph::RampLinear>           mVolumeRamp;
    std::unique_ptr<flowgraph::ClipToRange>          mClipper;
    std::unique_ptr<flowgraph::MonoToMultiConverter> mChannelConverter;
    std::unique_ptr<flowgraph::AudioSink>            mSink;
};

AAudioFlowGraphCree una canalización de procesamiento de datos de audio durante la configuración y proporcione process()operaciones para procesar datos a través de la canalización de procesamiento de datos de audio:

using namespace flowgraph;

aaudio_result_t AAudioFlowGraph::configure(audio_format_t sourceFormat,
                          int32_t sourceChannelCount,
                          audio_format_t sinkFormat,
                          int32_t sinkChannelCount) {
    AudioFloatOutputPort *lastOutput = nullptr;

    // TODO change back to ALOGD
    ALOGI("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d",
          __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount);

    switch (sourceFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            mSource = std::make_unique<SourceFloat>(sourceChannelCount);
            break;
        case AUDIO_FORMAT_PCM_16_BIT:
            mSource = std::make_unique<SourceI16>(sourceChannelCount);
            break;
        case AUDIO_FORMAT_PCM_24_BIT_PACKED:
            mSource = std::make_unique<SourceI24>(sourceChannelCount);
            break;
        case AUDIO_FORMAT_PCM_32_BIT:
            mSource = std::make_unique<SourceI32>(sourceChannelCount);
            break;
        default:
            ALOGE("%s() Unsupported source format = %d", __func__, sourceFormat);
            return AAUDIO_ERROR_UNIMPLEMENTED;
    }
    lastOutput = &mSource->output;

    // Apply volume as a ramp to avoid pops.
    mVolumeRamp = std::make_unique<RampLinear>(sourceChannelCount);
    lastOutput->connect(&mVolumeRamp->input);
    lastOutput = &mVolumeRamp->output;

    // For a pure float graph, there is chance that the data range may be very large.
    // So we should clip to a reasonable value that allows a little headroom.
    if (sourceFormat == AUDIO_FORMAT_PCM_FLOAT && sinkFormat == AUDIO_FORMAT_PCM_FLOAT) {
        mClipper = std::make_unique<ClipToRange>(sourceChannelCount);
        lastOutput->connect(&mClipper->input);
        lastOutput = &mClipper->output;
    }

    // Expand the number of channels if required.
    if (sourceChannelCount == 1 && sinkChannelCount > 1) {
        mChannelConverter = std::make_unique<MonoToMultiConverter>(sinkChannelCount);
        lastOutput->connect(&mChannelConverter->input);
        lastOutput = &mChannelConverter->output;
    } else if (sourceChannelCount != sinkChannelCount) {
        ALOGE("%s() Channel reduction not supported.", __func__);
        return AAUDIO_ERROR_UNIMPLEMENTED;
    }

    switch (sinkFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            mSink = std::make_unique<SinkFloat>(sinkChannelCount);
            break;
        case AUDIO_FORMAT_PCM_16_BIT:
            mSink = std::make_unique<SinkI16>(sinkChannelCount);
            break;
        case AUDIO_FORMAT_PCM_24_BIT_PACKED:
            mSink = std::make_unique<SinkI24>(sinkChannelCount);
            break;
        case AUDIO_FORMAT_PCM_32_BIT:
            mSink = std::make_unique<SinkI32>(sinkChannelCount);
            break;
        default:
            ALOGE("%s() Unsupported sink format = %d", __func__, sinkFormat);
            return AAUDIO_ERROR_UNIMPLEMENTED;
    }
    lastOutput->connect(&mSink->input);

    return AAUDIO_OK;
}

void AAudioFlowGraph::process(const void *source, void *destination, int32_t numFrames) {
    mSource->setData(source, numFrames);
    mSink->read(destination, numFrames);
}

/**
 * @param volume between 0.0 and 1.0
 */
void AAudioFlowGraph::setTargetVolume(float volume) {
    mVolumeRamp->setTarget(volume);
}

void AAudioFlowGraph::setRampLengthInFrames(int32_t numFrames) {
    mVolumeRamp->setLengthInFrames(numFrames);
}

De esta manera, el proceso de procesamiento de datos de audio de AAudio eventualmente se verá así:

AAudioFlowGraph

Implementación de transmisión de audio

La mayoría de los recursos necesarios para que se ejecute la transmisión de audio se asignan cuando se abre la transmisión de audio. La transmisión de audio realmente se ejecutará cuando la aplicación solicite iniciar la transmisión de audio. La implementación de la interfaz de la solicitud AAudio para iniciar la transmisión de audio (ubicada en frameworks/av/media/libaaudio/src/core/AAudioAudio.cpp ) es la siguiente:

AAUDIO_API aaudio_result_t  AAudioStream_requestStart(AAudioStream* stream)
{
    AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
    aaudio_stream_id_t id = audioStream->getId();
    ALOGD("%s(s#%u) called --------------", __func__, id);
    aaudio_result_t result = audioStream->systemStart();
    ALOGD("%s(s#%u) returned %d ---------", __func__, id, result);
    return result;
}

Esta función llama AudioStream::systemStart()a la función, que está definida (ubicada en frameworks/av/media/libaaudio/src/core/AudioStream.cpp ) de la siguiente manera:

aaudio_result_t AudioStream::systemStart() {
    if (collidesWithCallback()) {
        ALOGE("%s cannot be called from a callback!", __func__);
        return AAUDIO_ERROR_INVALID_STATE;
    }

    std::lock_guard<std::mutex> lock(mStreamLock);

    switch (getState()) {
        // Is this a good time to start?
        case AAUDIO_STREAM_STATE_OPEN:
        case AAUDIO_STREAM_STATE_PAUSING:
        case AAUDIO_STREAM_STATE_PAUSED:
        case AAUDIO_STREAM_STATE_STOPPING:
        case AAUDIO_STREAM_STATE_STOPPED:
        case AAUDIO_STREAM_STATE_FLUSHING:
        case AAUDIO_STREAM_STATE_FLUSHED:
            break; // Proceed with starting.

        // Already started?
        case AAUDIO_STREAM_STATE_STARTING:
        case AAUDIO_STREAM_STATE_STARTED:
            ALOGW("%s() stream was already started, state = %s", __func__,
                  AudioGlobal_convertStreamStateToText(getState()));
            return AAUDIO_ERROR_INVALID_STATE;

        // Don't start when the stream is dead!
        case AAUDIO_STREAM_STATE_DISCONNECTED:
        case AAUDIO_STREAM_STATE_CLOSING:
        case AAUDIO_STREAM_STATE_CLOSED:
        default:
            ALOGW("%s() stream is dead, state = %s", __func__,
                  AudioGlobal_convertStreamStateToText(getState()));
            return AAUDIO_ERROR_INVALID_STATE;
    }

    aaudio_result_t result = requestStart_l();
    if (result == AAUDIO_OK) {
        // We only call this for logging in "dumpsys audio". So ignore return code.
        (void) mPlayerBase->startWithStatus(getDeviceId());
    }
    return result;
}

AudioStream::systemStart()La función verifica el estado actual de la transmisión de audio; cuando el estado permite el inicio, solicita la transmisión de audio específica para realizar la acción de inicio, es decir requestStart_l(). AudioStreamInternal::requestStart_l()La definición de la función (ubicada en frameworks/av/media/libaaudio/src/client/AudioStreamInternal.cpp ) es la siguiente:

aaudio_result_t AudioStreamInternal::requestStart_l()
{
    int64_t startTime;
    if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
        ALOGD("requestStart() mServiceStreamHandle invalid");
        return AAUDIO_ERROR_INVALID_STATE;
    }
    if (isActive()) {
        ALOGD("requestStart() already active");
        return AAUDIO_ERROR_INVALID_STATE;
    }

    aaudio_stream_state_t originalState = getState();
    if (originalState == AAUDIO_STREAM_STATE_DISCONNECTED) {
        ALOGD("requestStart() but DISCONNECTED");
        return AAUDIO_ERROR_DISCONNECTED;
    }
    setState(AAUDIO_STREAM_STATE_STARTING);

    // Clear any stale timestamps from the previous run.
    drainTimestampsFromService();

    prepareBuffersForStart(); // tell subclasses to get ready

    aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);
    if (result == AAUDIO_ERROR_INVALID_HANDLE) {
        ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
        // Stealing was added in R. Coerce result to improve backward compatibility.
        result = AAUDIO_ERROR_DISCONNECTED;
        setState(AAUDIO_STREAM_STATE_DISCONNECTED);
    }

    startTime = AudioClock::getNanoseconds();
    mClockModel.start(startTime);
    mNeedCatchUp.request();  // Ask data processing code to catch up when first timestamp received.

    // Start data callback thread.
    if (result == AAUDIO_OK && isDataCallbackSet()) {
        // Launch the callback loop thread.
        int64_t periodNanos = mCallbackFrames
                              * AAUDIO_NANOS_PER_SECOND
                              / getSampleRate();
        mCallbackEnabled.store(true);
        result = createThread_l(periodNanos, aaudio_callback_thread_proc, this);
    }
    if (result != AAUDIO_OK) {
        setState(originalState);
    }
    return result;
}
 . . . . . .
aaudio_result_t AudioStreamInternal::drainTimestampsFromService() {
    aaudio_result_t result = AAUDIO_OK;

    while (result == AAUDIO_OK) {
        AAudioServiceMessage message;
        if (!mAudioEndpoint) {
            break;
        }
        if (mAudioEndpoint->readUpCommand(&message) != 1) {
            break; // no command this time, no problem
        }
        switch (message.what) {
            // ignore most messages
            case AAudioServiceMessage::code::TIMESTAMP_SERVICE:
            case AAudioServiceMessage::code::TIMESTAMP_HARDWARE:
                break;

            case AAudioServiceMessage::code::EVENT:
                result = onEventFromServer(&message);
                break;

            default:
                ALOGE("%s - unrecognized message.what = %d", __func__, (int) message.what);
                result = AAUDIO_ERROR_INTERNAL;
                break;
        }
    }
    return result;
}

El proceso de ejecución de esta función es el siguiente:

  1. Verifique el estado actual de la transmisión de audio y establezca el estado actual en AAUDIO_STREAM_STATE_STARTING;
  2. Borrar los mensajes enviados por el servidor;
  3. Solicita a la subclase de flujo de audio que se prepare para el inicio y prepare el búfer;
  4. Solicite al servicio que inicie la transmisión de audio a través de la interfaz del servicio AAudio media.aaudio;
  5. Establezca la hora actual como la hora de inicio del modelo de reloj sincronizado para facilitar la predicción de la posición del flujo de audio en un momento determinado;
  6. Hay dos tipos de transmisión de datos para transmisiones de audio: modo push y modo pull. Uno es que la aplicación toma sincrónicamente los datos de audio y los envía a la transmisión de audio a través read()de la operación / proporcionada por la transmisión de audio; el otro es configurar una devolución de llamada de datos para write()la secuencia de audio. Cuando AAudio inicia un hilo para la secuencia de audio, primero completa la transferencia de datos con la aplicación a través de devoluciones de llamada y luego escribe o lee los datos en la secuencia de datos. Cuando se establece una devolución de llamada de datos, se calcula el ciclo de ejecución de la devolución de llamada y se solicita que se inicie un hilo.

Las tareas realizadas por el hilo iniciado para la transmisión de audio (ubicada en frameworks/av/media/libaaudio/src/client/AudioStreamInternal.cpp ) son las siguientes:

static void *aaudio_callback_thread_proc(void *context)
{
    AudioStreamInternal *stream = (AudioStreamInternal *)context;
    //LOGD("oboe_callback_thread, stream = %p", stream);
    if (stream != NULL) {
        return stream->callbackLoop();
    } else {
        return NULL;
    }
}

El bucle de devolución de llamada para reproducir la transmisión de audio (ubicado en frameworks/av/media/libaaudio/src/client/AudioStreamInternalPlay.cpp ) es el siguiente:

void *AudioStreamInternalPlay::callbackLoop() {
    ALOGD("%s() entering >>>>>>>>>>>>>>>", __func__);
    aaudio_result_t result = AAUDIO_OK;
    aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
    if (!isDataCallbackSet()) return NULL;
    int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames);

    // result might be a frame count
    while (mCallbackEnabled.load() && isActive() && (result >= 0)) {
        // Call application using the AAudio callback interface.
        callbackResult = maybeCallDataCallback(mCallbackBuffer.get(), mCallbackFrames);

        if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
            // Write audio data to stream. This is a BLOCKING WRITE!
            result = write(mCallbackBuffer.get(), mCallbackFrames, timeoutNanos);
            if ((result != mCallbackFrames)) {
                if (result >= 0) {
                    // Only wrote some of the frames requested. Must have timed out.
                    result = AAUDIO_ERROR_TIMEOUT;
                }
                maybeCallErrorCallback(result);
                break;
            }
        } else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
            ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
            result = systemStopInternal();
            break;
        }
    }

    ALOGD("%s() exiting, result = %d, isActive() = %d <<<<<<<<<<<<<<",
          __func__, result, (int) isActive());
    return NULL;
}

Solicita continuamente datos de audio de la aplicación y los datos de audio obtenidos se guardan en un búfer y luego se escriben en la transmisión de audio.

El bucle de devolución de llamada para recopilar transmisiones de audio (ubicado en frameworks/av/media/libaaudio/src/client/AudioStreamInternalCapture.cpp ) es el siguiente:

void *AudioStreamInternalCapture::callbackLoop() {
    aaudio_result_t result = AAUDIO_OK;
    aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
    if (!isDataCallbackSet()) return NULL;

    // result might be a frame count
    while (mCallbackEnabled.load() && isActive() && (result >= 0)) {

        // Read audio data from stream.
        int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames);

        // This is a BLOCKING READ!
        result = read(mCallbackBuffer.get(), mCallbackFrames, timeoutNanos);
        if ((result != mCallbackFrames)) {
            ALOGE("callbackLoop: read() returned %d", result);
            if (result >= 0) {
                // Only read some of the frames requested. Must have timed out.
                result = AAUDIO_ERROR_TIMEOUT;
            }
            maybeCallErrorCallback(result);
            break;
        }

        // Call application using the AAudio callback interface.
        callbackResult = maybeCallDataCallback(mCallbackBuffer.get(), mCallbackFrames);

        if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
            ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
            result = systemStopInternal();
            break;
        }
    }

    ALOGD("callbackLoop() exiting, result = %d, isActive() = %d",
          result, (int) isActive());
    return NULL;
}

Lee continuamente datos de la transmisión de audio y luego los pasa a la aplicación a través de la función de devolución de llamada.

AudioStreamEl proceso de creación de un hilo (ubicado en frameworks/av/media/libaaudio/src/core/AudioStream.cpp ) es el siguiente:

void* AudioStream::wrapUserThread() {
    void* procResult = nullptr;
    mThreadRegistrationResult = registerThread();
    if (mThreadRegistrationResult == AAUDIO_OK) {
        // Run callback loop. This may take a very long time.
        procResult = mThreadProc(mThreadArg);
        mThreadRegistrationResult = unregisterThread();
    }
    return procResult;
}


// This is the entry point for the new thread created by createThread_l().
// It converts the 'C' function call to a C++ method call.
static void* AudioStream_internalThreadProc(void* threadArg) {
    AudioStream *audioStream = (AudioStream *) threadArg;
    // Prevent the stream from being deleted while being used.
    // This is just for extra safety. It is probably not needed because
    // this callback should be joined before the stream is closed.
    android::sp<AudioStream> protectedStream(audioStream);
    // Balance the incStrong() in createThread_l().
    protectedStream->decStrong(nullptr);
    return protectedStream->wrapUserThread();
}

// This is not exposed in the API.
// But it is still used internally to implement callbacks for MMAP mode.
aaudio_result_t AudioStream::createThread_l(int64_t periodNanoseconds,
                                            aaudio_audio_thread_proc_t threadProc,
                                            void* threadArg)
{
    if (mHasThread) {
        ALOGD("%s() - previous thread was not joined, join now to be safe", __func__);
        joinThread_l(nullptr);
    }
    if (threadProc == nullptr) {
        return AAUDIO_ERROR_NULL;
    }
    // Pass input parameters to the background thread.
    mThreadProc = threadProc;
    mThreadArg = threadArg;
    setPeriodNanoseconds(periodNanoseconds);
    mHasThread = true;
    // Prevent this object from getting deleted before the thread has a chance to create
    // its strong pointer. Assume the thread will call decStrong().
    this->incStrong(nullptr);
    int err = pthread_create(&mThread, nullptr, AudioStream_internalThreadProc, this);
    if (err != 0) {
        android::status_t status = -errno;
        ALOGE("%s() - pthread_create() failed, %d", __func__, status);
        this->decStrong(nullptr); // Because the thread won't do it.
        mHasThread = false;
        return AAudioConvert_androidToAAudioResult(status);
    } else {
        // TODO Use AAudioThread or maybe AndroidThread
        // Name the thread with an increasing index, "AAudio_#", for debugging.
        static std::atomic<uint32_t> nextThreadIndex{1};
        char name[16]; // max length for a pthread_name
        uint32_t index = nextThreadIndex++;
        // Wrap the index so that we do not hit the 16 char limit
        // and to avoid hard-to-read large numbers.
        index = index % 100000;  // arbitrary
        snprintf(name, sizeof(name), "AAudio_%u", index);
        err = pthread_setname_np(mThread, name);
        ALOGW_IF((err != 0), "Could not set name of AAudio thread. err = %d", err);

        return AAUDIO_OK;
    }
}

AudioStreamCreando pthread_create()un hilo, realizando la tarea AudioStream_internalThreadProcy luego configurando el nombre del hilo. Después de que el hilo recién creado comienza a ejecutarse, primero registra el hilo actual y luego ejecuta las tareas realizadas cuando se creó el hilo, que es el bucle de devolución de llamada de la transmisión de audio AAudio. Al registrar el hilo actual se enviará información del hilo al media.aaudioservicio (ubicado en frameworks/av/media/libaaudio/src/client/AudioStreamInternal.cpp ):

aaudio_result_t AudioStreamInternal::registerThread() {
    if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
        ALOGW("%s() mServiceStreamHandle invalid", __func__);
        return AAUDIO_ERROR_INVALID_STATE;
    }
    return mServiceInterface.registerAudioThread(mServiceStreamHandle,
                                              gettid(),
                                              getPeriodNanoseconds());
}

Aunque AudioStreamla duración del período se pasa al solicitar la creación de subprocesos, la ejecución periódica no se AudioStreammaneja en .

En el modo sincrónico de transferencia de datos de flujo de audio, la aplicación escribe datos en el flujo de audio AAudio para reproducir los datos de audio. La implementación de la interfaz para escribir datos de audio en la transmisión de audio (ubicada en frameworks/av/media/libaaudio/src/core/AAudioAudio.cpp ) es la siguiente:

AAUDIO_API aaudio_result_t AAudioStream_write(AAudioStream* stream,
                               const void *buffer,
                               int32_t numFrames,
                               int64_t timeoutNanoseconds)
{
    AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
    if (buffer == nullptr) {
        return AAUDIO_ERROR_NULL;
    }

    // Don't allow writes when playing with a callback.
    if (audioStream->isDataCallbackActive()) {
        // A developer requested this warning because it would have saved lots of debugging.
        ALOGW("%s() - Cannot write to a callback stream when running.", __func__);
        return AAUDIO_ERROR_INVALID_STATE;
    }

    if (numFrames < 0) {
        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
    } else if (numFrames == 0) {
        return 0;
    }

    aaudio_result_t result = audioStream->write(buffer, numFrames, timeoutNanoseconds);

    return result;
}

Esta función verifica los parámetros y si la transmisión de audio está funcionando en modo síncrono, y luego write()escribe los datos de audio mediante la operación del objeto de transmisión de audio, la misma operación utilizada en el bucle de devolución de llamada del modo asíncrono. write()La operación se implementa mediante una secuencia de audio específica. Para la secuencia de reproducción de audio, AudioStreamInternalPlay::write()la función se define (ubicada en frameworks/av/media/libaaudio/src/client/AudioStreamInternalPlay.cpp ) de la siguiente manera:

aaudio_result_t AudioStreamInternalPlay::write(const void *buffer, int32_t numFrames,
                                               int64_t timeoutNanoseconds) {
    return processData((void *)buffer, numFrames, timeoutNanoseconds);
}

AudioStreamInternalPlay::write()Ejecutando AudioStreamInternal::processData()el bucle de procesamiento de datos y realizando la espera necesaria (ubicada en frameworks/av/media/libaaudio/src/client/AudioStreamInternal.cpp ) hasta que se hayan procesado todos los datos o haya expirado el tiempo máximo de espera:

aaudio_result_t AudioStreamInternal::processData(void *buffer, int32_t numFrames,
                                                 int64_t timeoutNanoseconds)
{
    const char * traceName = "aaProc";
    const char * fifoName = "aaRdy";
    ATRACE_BEGIN(traceName);
    if (ATRACE_ENABLED()) {
        int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
        ATRACE_INT(fifoName, fullFrames);
    }

    aaudio_result_t result = AAUDIO_OK;
    int32_t loopCount = 0;
    uint8_t* audioData = (uint8_t*)buffer;
    int64_t currentTimeNanos = AudioClock::getNanoseconds();
    const int64_t entryTimeNanos = currentTimeNanos;
    const int64_t deadlineNanos = currentTimeNanos + timeoutNanoseconds;
    int32_t framesLeft = numFrames;

    // Loop until all the data has been processed or until a timeout occurs.
    while (framesLeft > 0) {
        // The call to processDataNow() will not block. It will just process as much as it can.
        int64_t wakeTimeNanos = 0;
        aaudio_result_t framesProcessed = processDataNow(audioData, framesLeft,
                                                  currentTimeNanos, &wakeTimeNanos);
        if (framesProcessed < 0) {
            result = framesProcessed;
            break;
        }
        framesLeft -= (int32_t) framesProcessed;
        audioData += framesProcessed * getBytesPerFrame();

        // Should we block?
        if (timeoutNanoseconds == 0) {
            break; // don't block
        } else if (wakeTimeNanos != 0) {
            if (!mAudioEndpoint->isFreeRunning()) {
                // If there is software on the other end of the FIFO then it may get delayed.
                // So wake up just a little after we expect it to be ready.
                wakeTimeNanos += mWakeupDelayNanos;
            }

            currentTimeNanos = AudioClock::getNanoseconds();
            int64_t earliestWakeTime = currentTimeNanos + mMinimumSleepNanos;
            // Guarantee a minimum sleep time.
            if (wakeTimeNanos < earliestWakeTime) {
                wakeTimeNanos = earliestWakeTime;
            }

            if (wakeTimeNanos > deadlineNanos) {
                // If we time out, just return the framesWritten so far.
                // TODO remove after we fix the deadline bug
                ALOGW("processData(): entered at %lld nanos, currently %lld",
                      (long long) entryTimeNanos, (long long) currentTimeNanos);
                ALOGW("processData(): TIMEOUT after %lld nanos",
                      (long long) timeoutNanoseconds);
                ALOGW("processData(): wakeTime = %lld, deadline = %lld nanos",
                      (long long) wakeTimeNanos, (long long) deadlineNanos);
                ALOGW("processData(): past deadline by %d micros",
                      (int)((wakeTimeNanos - deadlineNanos) / AAUDIO_NANOS_PER_MICROSECOND));
                mClockModel.dump();
                mAudioEndpoint->dump();
                break;
            }

            if (ATRACE_ENABLED()) {
                int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
                ATRACE_INT(fifoName, fullFrames);
                int64_t sleepForNanos = wakeTimeNanos - currentTimeNanos;
                ATRACE_INT("aaSlpNs", (int32_t)sleepForNanos);
            }

            AudioClock::sleepUntilNanoTime(wakeTimeNanos);
            currentTimeNanos = AudioClock::getNanoseconds();
        }
    }

    if (ATRACE_ENABLED()) {
        int32_t fullFrames = mAudioEndpoint->getFullFramesAvailable();
        ATRACE_INT(fifoName, fullFrames);
    }

    // return error or framesProcessed
    (void) loopCount;
    ATRACE_END();
    return (result < 0) ? result : numFrames - framesLeft;
}

AudioStreamInternal::processData()El bucle de procesamiento de datos procesa datos a través de sus subclases processDataNow(). Para reproducir transmisiones de audio, processDataNow()realiza la acción de escribir datos y para recopilar transmisiones de audio, processDataNow()realiza la acción de leer datos. processDataNow()La ejecución no se bloquea, simplemente procesa la mayor cantidad de datos posible.

La definición de función para reproducir transmisiones de audio AudioStreamInternalPlay::processDataNow()(ubicada en frameworks/av/media/libaaudio/src/client/AudioStreamInternalPlay.cpp ) es la siguiente:

aaudio_result_t AudioStreamInternalPlay::processDataNow(void *buffer, int32_t numFrames,
                                              int64_t currentNanoTime, int64_t *wakeTimePtr) {
    aaudio_result_t result = processCommands();
    if (result != AAUDIO_OK) {
        return result;
    }

    const char *traceName = "aaWrNow";
    ATRACE_BEGIN(traceName);

    if (mClockModel.isStarting()) {
        // Still haven't got any timestamps from server.
        // Keep waiting until we get some valid timestamps then start writing to the
        // current buffer position.
        ALOGV("%s() wait for valid timestamps", __func__);
        // Sleep very briefly and hope we get a timestamp soon.
        *wakeTimePtr = currentNanoTime + (2000 * AAUDIO_NANOS_PER_MICROSECOND);
        ATRACE_END();
        return 0;
    }
    // If we have gotten this far then we have at least one timestamp from server.

    // If a DMA channel or DSP is reading the other end then we have to update the readCounter.
    if (mAudioEndpoint->isFreeRunning()) {
        // Update data queue based on the timing model.
        int64_t estimatedReadCounter = mClockModel.convertTimeToPosition(currentNanoTime);
        // ALOGD("AudioStreamInternal::processDataNow() - estimatedReadCounter = %d", (int)estimatedReadCounter);
        mAudioEndpoint->setDataReadCounter(estimatedReadCounter);
    }

    if (mNeedCatchUp.isRequested()) {
        // Catch an MMAP pointer that is already advancing.
        // This will avoid initial underruns caused by a slow cold start.
        // We add a one burst margin in case the DSP advances before we can write the data.
        // This can help prevent the beginning of the stream from being skipped.
        advanceClientToMatchServerPosition(getFramesPerBurst());
        mNeedCatchUp.acknowledge();
    }

    // If the read index passed the write index then consider it an underrun.
    // For shared streams, the xRunCount is passed up from the service.
    if (mAudioEndpoint->isFreeRunning() && mAudioEndpoint->getFullFramesAvailable() < 0) {
        mXRunCount++;
        if (ATRACE_ENABLED()) {
            ATRACE_INT("aaUnderRuns", mXRunCount);
        }
    }

    // Write some data to the buffer.
    //ALOGD("AudioStreamInternal::processDataNow() - writeNowWithConversion(%d)", numFrames);
    int32_t framesWritten = writeNowWithConversion(buffer, numFrames);
    //ALOGD("AudioStreamInternal::processDataNow() - tried to write %d frames, wrote %d",
    //    numFrames, framesWritten);
    if (ATRACE_ENABLED()) {
        ATRACE_INT("aaWrote", framesWritten);
    }

    // Sleep if there is too much data in the buffer.
    // Calculate an ideal time to wake up.
    if (wakeTimePtr != nullptr
            && (mAudioEndpoint->getFullFramesAvailable() >= getBufferSize())) {
        // By default wake up a few milliseconds from now.  // TODO review
        int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND);
        aaudio_stream_state_t state = getState();
        //ALOGD("AudioStreamInternal::processDataNow() - wakeTime based on %s",
        //      AAudio_convertStreamStateToText(state));
        switch (state) {
            case AAUDIO_STREAM_STATE_OPEN:
            case AAUDIO_STREAM_STATE_STARTING:
                if (framesWritten != 0) {
                    // Don't wait to write more data. Just prime the buffer.
                    wakeTime = currentNanoTime;
                }
                break;
            case AAUDIO_STREAM_STATE_STARTED:
            {
                // Sleep until the readCounter catches up and we only have
                // the getBufferSize() frames of data sitting in the buffer.
                int64_t nextReadPosition = mAudioEndpoint->getDataWriteCounter() - getBufferSize();
                wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
            }
                break;
            default:
                break;
        }
        *wakeTimePtr = wakeTime;

    }

    ATRACE_END();
    return framesWritten;
}

Esta función primero llama para processCommands()procesar media.aaudioel comando enviado por el servicio, luego ejecuta principalmente writeNowWithConversion()la escritura de datos de audio y luego calcula el tiempo de espera y regresa. AudioStreamInternalPlay::writeNowWithConversion()La función toma una parte de la memoria del quince que presentamos anteriormente, procesa los datos de audio, escribe los datos de audio procesados ​​en el área de memoria y mueve el contador de escritura:

aaudio_result_t AudioStreamInternalPlay::writeNowWithConversion(const void *buffer,
                                                            int32_t numFrames) {
    WrappingBuffer wrappingBuffer;
    uint8_t *byteBuffer = (uint8_t *) buffer;
    int32_t framesLeft = numFrames;

    mAudioEndpoint->getEmptyFramesAvailable(&wrappingBuffer);

    // Write data in one or two parts.
    int partIndex = 0;
    while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
        int32_t framesToWrite = framesLeft;
        int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
        if (framesAvailable > 0) {
            if (framesToWrite > framesAvailable) {
                framesToWrite = framesAvailable;
            }

            int32_t numBytes = getBytesPerFrame() * framesToWrite;

            mFlowGraph.process((void *)byteBuffer,
                               wrappingBuffer.data[partIndex],
                               framesToWrite);

            byteBuffer += numBytes;
            framesLeft -= framesToWrite;
        } else {
            break;
        }
        partIndex++;
    }
    int32_t framesWritten = numFrames - framesLeft;
    mAudioEndpoint->advanceWriteIndex(framesWritten);

    return framesWritten;
}

En el modo sincrónico de transferencia de datos de flujo de audio, la aplicación lee datos del flujo de audio de AAudio para obtener los datos de audio recopilados. La implementación de la interfaz para leer datos de la transmisión de audio (ubicada en frameworks/av/media/libaaudio/src/core/AAudioAudio.cpp ) es la siguiente:

AAUDIO_API aaudio_result_t AAudioStream_read(AAudioStream* stream,
                               void *buffer,
                               int32_t numFrames,
                               int64_t timeoutNanoseconds)
{
    AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
    if (buffer == nullptr) {
        return AAUDIO_ERROR_NULL;
    }
    if (numFrames < 0) {
        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
    } else if (numFrames == 0) {
        return 0;
    }

    aaudio_result_t result = audioStream->read(buffer, numFrames, timeoutNanoseconds);

    return result;
}

Esta función verifica los parámetros y luego read()lee los datos de audio mediante la operación del objeto de flujo de audio, la misma operación utilizada en el bucle de devolución de llamada en modo asíncrono. read()La operación se implementa mediante una secuencia de audio específica. Para la secuencia de recopilación de audio, AudioStreamInternalCapture::read()la función se define (ubicada en frameworks/av/media/libaaudio/src/client/AudioStreamInternalCapture.cpp ) de la siguiente manera:

aaudio_result_t AudioStreamInternalCapture::read(void *buffer, int32_t numFrames,
                                               int64_t timeoutNanoseconds)
{
    return processData(buffer, numFrames, timeoutNanoseconds);
}

Al igual que AudioStreamInternalPlay::write()aquí, el ciclo de procesamiento de datos también se ejecuta AudioStreamInternal::processData()y se realiza la espera necesaria hasta que se procesen todos los datos o se alcance el tiempo máximo de espera. AudioStreamInternalCaptureLa processDataNow()definición (ubicada en frameworks/av/media/libaaudio/src/client/AudioStreamInternalCapture.cpp ) es la siguiente:

// Read as much data as we can without blocking.
aaudio_result_t AudioStreamInternalCapture::processDataNow(void *buffer, int32_t numFrames,
                                                  int64_t currentNanoTime, int64_t *wakeTimePtr) {
    aaudio_result_t result = processCommands();
    if (result != AAUDIO_OK) {
        return result;
    }

    const char *traceName = "aaRdNow";
    ATRACE_BEGIN(traceName);

    if (mClockModel.isStarting()) {
        // Still haven't got any timestamps from server.
        // Keep waiting until we get some valid timestamps then start writing to the
        // current buffer position.
        ALOGD("processDataNow() wait for valid timestamps");
        // Sleep very briefly and hope we get a timestamp soon.
        *wakeTimePtr = currentNanoTime + (2000 * AAUDIO_NANOS_PER_MICROSECOND);
        ATRACE_END();
        return 0;
    }
    // If we have gotten this far then we have at least one timestamp from server.

    if (mAudioEndpoint->isFreeRunning()) {
        //ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter");
        // Update data queue based on the timing model.
        // Jitter in the DSP can cause late writes to the FIFO.
        // This might be caused by resampling.
        // We want to read the FIFO after the latest possible time
        // that the DSP could have written the data.
        int64_t estimatedRemoteCounter = mClockModel.convertLatestTimeToPosition(currentNanoTime);
        // TODO refactor, maybe use setRemoteCounter()
        mAudioEndpoint->setDataWriteCounter(estimatedRemoteCounter);
    }

    // This code assumes that we have already received valid timestamps.
    if (mNeedCatchUp.isRequested()) {
        // Catch an MMAP pointer that is already advancing.
        // This will avoid initial underruns caused by a slow cold start.
        advanceClientToMatchServerPosition();
        mNeedCatchUp.acknowledge();
    }

    // If the capture buffer is full beyond capacity then consider it an overrun.
    // For shared streams, the xRunCount is passed up from the service.
    if (mAudioEndpoint->isFreeRunning()
        && mAudioEndpoint->getFullFramesAvailable() > mAudioEndpoint->getBufferCapacityInFrames()) {
        mXRunCount++;
        if (ATRACE_ENABLED()) {
            ATRACE_INT("aaOverRuns", mXRunCount);
        }
    }

    // Read some data from the buffer.
    //ALOGD("AudioStreamInternalCapture::processDataNow() - readNowWithConversion(%d)", numFrames);
    int32_t framesProcessed = readNowWithConversion(buffer, numFrames);
    //ALOGD("AudioStreamInternalCapture::processDataNow() - tried to read %d frames, read %d",
    //    numFrames, framesProcessed);
    if (ATRACE_ENABLED()) {
        ATRACE_INT("aaRead", framesProcessed);
    }

    // Calculate an ideal time to wake up.
    if (wakeTimePtr != nullptr && framesProcessed >= 0) {
        // By default wake up a few milliseconds from now.  // TODO review
        int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND);
        aaudio_stream_state_t state = getState();
        //ALOGD("AudioStreamInternalCapture::processDataNow() - wakeTime based on %s",
        //      AAudio_convertStreamStateToText(state));
        switch (state) {
            case AAUDIO_STREAM_STATE_OPEN:
            case AAUDIO_STREAM_STATE_STARTING:
                break;
            case AAUDIO_STREAM_STATE_STARTED:
            {
                // When do we expect the next write burst to occur?

                // Calculate frame position based off of the readCounter because
                // the writeCounter might have just advanced in the background,
                // causing us to sleep until a later burst.
                int64_t nextPosition = mAudioEndpoint->getDataReadCounter() + getFramesPerBurst();
                wakeTime = mClockModel.convertPositionToLatestTime(nextPosition);
            }
                break;
            default:
                break;
        }
        *wakeTimePtr = wakeTime;

    }

    ATRACE_END();
    return framesProcessed;
}

Esta función también llama primero al comando processCommands()de procesamiento media.aaudioenviado por el servicio, y luego ejecuta principalmente readNowWithConversion()la lectura de datos de audio, luego calcula el tiempo de espera y regresa. AudioStreamInternalCapture::readNowWithConversion()La función toma una parte de la memoria del quince que presentamos anteriormente, copia los datos de audio recopilados y mueve el contador de lectura:

aaudio_result_t AudioStreamInternalCapture::readNowWithConversion(void *buffer,
                                                                int32_t numFrames) {
    // ALOGD("readNowWithConversion(%p, %d)",
    //              buffer, numFrames);
    WrappingBuffer wrappingBuffer;
    uint8_t *destination = (uint8_t *) buffer;
    int32_t framesLeft = numFrames;

    mAudioEndpoint->getFullFramesAvailable(&wrappingBuffer);

    // Read data in one or two parts.
    for (int partIndex = 0; framesLeft > 0 && partIndex < WrappingBuffer::SIZE; partIndex++) {
        int32_t framesToProcess = framesLeft;
        const int32_t framesAvailable = wrappingBuffer.numFrames[partIndex];
        if (framesAvailable <= 0) break;

        if (framesToProcess > framesAvailable) {
            framesToProcess = framesAvailable;
        }

        const int32_t numBytes = getBytesPerFrame() * framesToProcess;
        const int32_t numSamples = framesToProcess * getSamplesPerFrame();

        const audio_format_t sourceFormat = getDeviceFormat();
        const audio_format_t destinationFormat = getFormat();

        memcpy_by_audio_format(destination, destinationFormat,
                wrappingBuffer.data[partIndex], sourceFormat, numSamples);

        destination += numBytes;
        framesLeft -= framesToProcess;
    }

    int32_t framesProcessed = numFrames - framesLeft;
    mAudioEndpoint->advanceReadIndex(framesProcessed);

    //ALOGD("readNowWithConversion() returns %d", framesProcessed);
    return framesProcessed;
}

Las operaciones clave de la transmisión de audio se implementan más o menos así.

Hecho.

Supongo que te gusta

Origin blog.csdn.net/tq08g2z/article/details/129633653
Recomendado
Clasificación