Android Framework subsistema de audio (12) análisis de capa HAL

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


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

Este capítulo se centra en ➕ La parte de análisis de capa HAL en la esquina superior izquierda del mapa mental anterior es suficiente. Explica principalmente el análisis del marco de la capa HAL, y luego analiza el proceso de lectura de datos de audio, escritura de datos de audio, configuración del parámetro y obtención del parámetro a través del código fuente para comprender profundamente el proceso de llamada del HAL.


1 análisis de marco de capa HAL

El diagrama de cuadro completo del sistema de audio es el siguiente:

@ 1 Dos partes de la capa HAL

Aquí prestamos atención a la capa HAL. Hay HAL para audio y HAL para Audio_policy (no nos importa HAL para Audio_policy y básicamente lo descartamos). La siguiente capa de la capa HAL usa TiniAlsa (versión recortada de la biblioteca AlSA). La capa HAL se divide en dos partes:

  1. Una parte es una variedad de dispositivos de audio, y cada dispositivo de audio está implementado por un archivo de biblioteca independiente: como audio.a2dp.default.so (administra el audio Bluetooth a2dp), audio.usb.default.so (administra el audio externo usb), audio .primary.default.so (gestiona la mayor parte del audio en el dispositivo).
  2. Parte de la estrategia de audio implementada por el propio fabricante es audio.primary.tiny4412.so.

@ 2 Clases y estructuras clave

En términos generales, por conveniencia, HAL debe proporcionar una interfaz unificada a la capa superior, y el hardware operativo también tendrá un conjunto de interfaces / clase. Ellos son:

  • Proporcione la interfaz struct audio_hw_device hacia arriba: la interfaz struct audio_hw_device está en el archivo audio_hw_hal.cpp, que encapsula la estructura hw_device_t. audio_hw_hal.cpp se encuentra en hardware / libhardware_legacy / audio (la nueva arquitectura tiene un archivo audio_hw.c en hardware / libhardware / modules / audio, es el nuevo archivo Audio HAL, pero no se usa en la placa 5.0, está dentro Todas las funciones están vacías. Este archivo tampoco está implementado, por lo que libhardware_legacy todavía juega el papel principal).
  • Vaya a la clase de hardware AudioHardware: generalmente implementado en el dispositivo / "fabricante de la plataforma" /common/libaudio/audioHardware.cpp, proporcionado por el fabricante, que utiliza la interfaz de la biblioteca tinyalsa. El fabricante implementa la interfaz de acceso de hardware, y Android especifica un conjunto de interfaces para ello. La relación de herencia de varias clases clave es:
AudioHardwareInterface   //hardware/AudioHardwareInterface.cpp 最基础 接口类
    ↑ (继承)
AudioHardwareBase        //hardware/AudioHardwareBase.h 这个应该是Audio HAL给厂商定义的接口
    ↑ (继承)
AudioHardware            //device/"平台厂商"/common/libaudio/audioHardware.cpp 厂商的实现

En el HAL del fabricante, AudioHardware (en audioHardware.cpp) representa una tarjeta de sonido, que utiliza la estructura audio_stream_out para representar la salida y audio_stream_in para representar la entrada. (Hay write () en audio_stream_out y read () en audio_stream_in).

Resumen de la estructura de datos relacionados con @ 3 HAL:

/* 上下衔接
 * Audio HAL的调用流程总结上层应用程序调用
 * audio_hw_hal.cpp中的legacy_adev_open()
 * 会得到一个struct audio_hw_device结构体,
 * 这个结构体代表上层使用硬件的接口,这个
 * 结构体中的函数都依赖于厂家提供的
 * struct AudioHardwareInterface结构。
 */
struct legacy_audio_device {
    struct audio_hw_device device;      //规范了向上提供的接口
    struct AudioHardwareInterface *hwif;//向下访问硬件,指向厂家的AudioHardware
};

/*由于HAL对上层直接提供的接口中没有read/write函数
 *因此,应用程序想要录音或播放声音的时候需要先打开
 *output或input(audio_hw_device中的open_output_stream/open_input_stream),
 *进而使用其里面的write/read函数通过声卡硬件读/写音频数据。
 *这也就是audio_hw_device与audio_stream_out/audio_stream_in之间的关系
 */
struct legacy_stream_out {
    struct audio_stream_out stream; //规范了向上提供的接口
    AudioStreamOut *legacy_out;     //向下访问硬件,指向厂家的AudioStreamOutALSA
};

struct legacy_stream_in {
    struct audio_stream_in stream;//规范了向上提供的接口
    AudioStreamIn *legacy_in;     //向下访问硬件,指向厂家的AudioStreamInALSA
};

2 Análisis del código fuente de la capa HAL

A continuación, analizamos principalmente varios procesos clave (operación de escritura de datos, operación de lectura de datos, obtención de parámetros, configuración de parámetros) para interpretar el proceso de llamada de HAL.

El proceso de carga de la biblioteca AudioFlinger es el siguiente:

AudioFlinger::loadHwModule
->AudioFlinger::loadHwModule_l
-->load_audio_interface
--->audio_hw_device_open(mod, dev);//获取audio_hw_device_t结构体以及它的操作
---->module->methods->open //这里对应的是legacy_adev_open

Aquí, se llama al método legacy_adev_open de la capa HAL, y el código se implementa de la siguiente manera:

static int legacy_adev_open(const hw_module_t* module, const char* name,
                            hw_device_t** device)
{
    struct legacy_audio_device *ladev;
    int ret;

    if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
        return -EINVAL;

    ladev = (struct legacy_audio_device *)calloc(1, sizeof(*ladev));
    if (!ladev)
        return -ENOMEM;

    //结构体赋值
    ladev->device.common.tag = HARDWARE_DEVICE_TAG;
    ladev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
    ladev->device.common.module = const_cast<hw_module_t*>(module);
    ladev->device.common.close = legacy_adev_close;
    ladev->device.init_check = adev_init_check;
    ladev->device.set_voice_volume = adev_set_voice_volume;
    ladev->device.set_master_volume = adev_set_master_volume;
    ladev->device.get_master_volume = adev_get_master_volume;
    ladev->device.set_mode = adev_set_mode;
    ladev->device.set_mic_mute = adev_set_mic_mute;
    ladev->device.get_mic_mute = adev_get_mic_mute;
    ladev->device.set_parameters = adev_set_parameters;
    ladev->device.get_parameters = adev_get_parameters;
    ladev->device.get_input_buffer_size = adev_get_input_buffer_size;
    ladev->device.open_output_stream = adev_open_output_stream;
    ladev->device.close_output_stream = adev_close_output_stream;
    ladev->device.open_input_stream = adev_open_input_stream;
    ladev->device.close_input_stream = adev_close_input_stream;
    ladev->device.dump = adev_dump;
    /* 关键点:
     * audio_hw_device_t结构体 和 hwif(hardwareInterface)接口之间建立联系
     * 这里通过createAudioHardware 获取 实现hardwareInterface接口的厂商指针
     * 后面调用hwif的相关操作 <=等价=> 使用厂商的库函数中的方法
     */
    ladev->hwif = createAudioHardware();
    if (!ladev->hwif) {
        ret = -EIO;
        goto err_create_audio_hw;
    }
    *device = &ladev->device.common;
    return 0;

err_create_audio_hw:
    free(ladev);
    return ret;
}

A través del análisis anterior, desde la capa Framework Native a la capa de capa HAL, y luego a la llamada de la biblioteca del proveedor de la plataforma de terceros, la relación entre ellos se rompe.

2.1 Operación de escritura de datos

A partir de la capa nativa de Framework, la estructura audio_hw_device_t primero obtendrá audio_stream_out a través de adev_open_output_stream, así que comience a analizar a partir de ella. El código es el siguiente:

static int adev_open_output_stream(struct audio_hw_device *dev,
                                   audio_io_handle_t handle,
                                   audio_devices_t devices,
                                   audio_output_flags_t flags,
                                   struct audio_config *config,
                                   struct audio_stream_out **stream_out,
                                   const char *address __unused)
{
    struct legacy_audio_device *ladev = to_ladev(dev);
    status_t status;
    struct legacy_stream_out *out;
    int ret;

    //这里的legacy_stream_out <=结构体类型 等价=>audio_stream_out
    out = (struct legacy_stream_out *)calloc(1, sizeof(*out));
    if (!out)
        return -ENOMEM;

    devices = convert_audio_device(devices, HAL_API_REV_2_0, HAL_API_REV_1_0);
    //这里将audio_stream_out与ladev->hwif之间建立联系
    out->legacy_out = ladev->hwif->openOutputStreamWithFlags(devices, flags,
                                                    (int *) &config->format,
                                                    &config->channel_mask,
                                                    &config->sample_rate, &status);
    if (!out->legacy_out) {
        ret = status;
        goto err_open;
    }

    out->stream.common.get_sample_rate = out_get_sample_rate;
    out->stream.common.set_sample_rate = out_set_sample_rate;
    out->stream.common.get_buffer_size = out_get_buffer_size;
    out->stream.common.get_channels = out_get_channels;
    out->stream.common.get_format = out_get_format;
    out->stream.common.set_format = out_set_format;
    out->stream.common.standby = out_standby;
    out->stream.common.dump = out_dump;
    out->stream.common.set_parameters = out_set_parameters;
    out->stream.common.get_parameters = out_get_parameters;
    out->stream.common.add_audio_effect = out_add_audio_effect;
    out->stream.common.remove_audio_effect = out_remove_audio_effect;
    out->stream.get_latency = out_get_latency;
    out->stream.set_volume = out_set_volume;
    out->stream.write = out_write;
    out->stream.get_render_position = out_get_render_position;
    out->stream.get_next_write_timestamp = out_get_next_write_timestamp;

    //将out->stream写回到参数 stream_out中
    *stream_out = &out->stream;
    return 0;

err_open:
    free(out);
    *stream_out = NULL;
    return ret;
}

Aquí obtenemos la secuencia de salida de audio (tipo audio_stream_out) a través de hwif, y luego inicializamos out-> stream, registramos la función de escritura out_write y la devolvemos a la variable de puntero stream_out (tipo audio_stream_out) pasada, cuando la capa superior escribe , Se ejecutará la función out_write, el código es el siguiente:

static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
                         size_t bytes)
{
    struct legacy_stream_out *out = reinterpret_cast<struct legacy_stream_out *>(stream);
    return out->legacy_out->write(buffer, bytes);
}

Aquí llame directamente al método write (out-> legacy_out-> write) de la biblioteca del proveedor de la plataforma de terceros (si se trata de una plataforma Qualcomm, la llamada hwif es AudioHardwareALSA, la llamada write es el método de escritura de AudioStreamOutALSA, y eventualmente traerá pcm_write Operación de escritura de datos).

2.2 Lectura de datos

A partir de la capa nativa del Framework, la estructura audio_hw_device_t primero obtendrá audio_stream_in a través de adev_open_Input_stream, así que comience a analizar a partir de ella. El código es el siguiente:


/** This method creates and opens the audio hardware input stream */
static int adev_open_input_stream(struct audio_hw_device *dev,
                                  audio_io_handle_t handle,
                                  audio_devices_t devices,
                                  struct audio_config *config,
                                  struct audio_stream_in **stream_in,
                                  audio_input_flags_t flags __unused,
                                  const char *address __unused,
                                  audio_source_t source __unused)
{
    struct legacy_audio_device *ladev = to_ladev(dev);
    status_t status;
    struct legacy_stream_in *in;
    int ret;
	//这里的legacy_stream_in <=结构体类型 等价=>audio_stream_in
    in = (struct legacy_stream_in *)calloc(1, sizeof(*in));
    if (!in)
        return -ENOMEM;

    devices = convert_audio_device(devices, HAL_API_REV_2_0, HAL_API_REV_1_0);
	//这里将audio_stream_in与ladev->hwif之间建立联系
    in->legacy_in = ladev->hwif->openInputStream(devices, (int *) &config->format,
                                                 &config->channel_mask, &config->sample_rate,
                                                 &status, (AudioSystem::audio_in_acoustics)0);
    if (!in->legacy_in) {
        ret = status;
        goto err_open;
    }

    in->stream.common.get_sample_rate = in_get_sample_rate;
    in->stream.common.set_sample_rate = in_set_sample_rate;
    in->stream.common.get_buffer_size = in_get_buffer_size;
    in->stream.common.get_channels = in_get_channels;
    in->stream.common.get_format = in_get_format;
    in->stream.common.set_format = in_set_format;
    in->stream.common.standby = in_standby;
    in->stream.common.dump = in_dump;
    in->stream.common.set_parameters = in_set_parameters;
    in->stream.common.get_parameters = in_get_parameters;
    in->stream.common.add_audio_effect = in_add_audio_effect;
    in->stream.common.remove_audio_effect = in_remove_audio_effect;
    in->stream.set_gain = in_set_gain;
    in->stream.read = in_read;
    in->stream.get_input_frames_lost = in_get_input_frames_lost;
	//将in->stream写回到参数 stream_in中
    *stream_in = &in->stream;
    return 0;

err_open:
    free(in);
    *stream_in = NULL;
    return ret;
}

Aquí, la secuencia de entrada de audio (tipo audio_stream_in) se obtiene a través de hwif, y luego se inicializa in> stream. Después de que se registra la función de escritura in_read, se devuelve a la variable de puntero stream_in (tipo audio_stream_in) pasada. , Ejecutará esta función in_read, el código es el siguiente:

static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
                       size_t bytes)
{
    struct legacy_stream_in *in =
        reinterpret_cast<struct legacy_stream_in *>(stream);
    return in->legacy_in->read(buffer, bytes);
}

Aquí llame directamente al método read (out-> legacy_in-> read) de la biblioteca del proveedor de la plataforma de terceros (si se trata de una plataforma Qualcomm, la llamada hwif es AudioHardwareALSA, la llamada read es el método de lectura de AudioStreamInALSA, y eventualmente traerá pcm_read Operación de lectura de datos).

2.3 Obtener parámetros

A partir de la capa nativa de Framework, la operación de obtener los parámetros de la estructura audio_hw_device_t eventualmente se llamará a adev_get_parameters, por lo que comienza el análisis. El código es el siguiente:

static char * adev_get_parameters(const struct audio_hw_device *dev,
                                  const char *keys)
{
    const struct legacy_audio_device *ladev = to_cladev(dev);
    String8 s8;

    s8 = ladev->hwif->getParameters(String8(keys));
    return strdup(s8.string());
}

Aquí llame directamente al método getParameters de la biblioteca del proveedor de la plataforma de terceros.

2.4 Configuración de parámetros

A partir de la capa nativa del Framework, la operación de establecer los parámetros de la estructura audio_hw_device_t eventualmente se llamará a adev_set_parameters, así que comience a analizar desde ella. El código es el siguiente:

static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
{
    struct legacy_audio_device *ladev = to_ladev(dev);
    return ladev->hwif->setParameters(String8(kvpairs));
}

Aquí llame directamente al método setParameters de la biblioteca del proveedor de la plataforma de terceros.

2.5 Resumen del proceso

  1. Determine el nombre del archivo de biblioteca a partir del archivo de configuración. El archivo HAL generalmente se encuentra en / system / lib / hardware, y el nombre del archivo de biblioteca que opera el hardware en el audio se especifica en (parte HAL del fabricante del sistema) en / system / etc / policy_config.
  2. Cargue el archivo de la biblioteca (* .so). Abra la función abierta en el archivo HAL, y la estructura audio_hw_device se construirá en la HAL. Hay varias funciones en esta estructura, especialmente open_output_stream / open_input_stream. AudioFlinger construye un objeto AudioHwDev de acuerdo con la estructura audio_hw_device y lo coloca en mAudioHwDevs.
  3. Llame a open_input_stream / open_output_stream de la estructura HAL audio_hw_device, construirá la estructura audio_stream_in / audio_stream_out.
  4. Escriba / lea datos, llame a algunas operaciones de tinyALSA, use directamente la llamada del sistema para controlar la tarjeta de sonido es la biblioteca tinyalsa, ubicada en el directorio / externo / tinyalsa, compile y genere el archivo de biblioteca libtinyalsa.so (solo involucra dos archivos mixer.c, pcm .c), compilando y generando herramientas tinycap, tinymix, tinypcminfo, tinyplay, que pueden usarse para controlar directamente el canal de audio para la prueba de grabación y transmisión. Utilice la operación pcm_XXX para operar la tarjeta de sonido, que es la encapsulación de la capa del controlador.
  5. La biblioteca tinyALSA opera el controlador de audio, y el controlador de audio opera el dispositivo de tarjeta de sonido de hardware.
Publicado 290 artículos originales · elogiados 47 · 30,000+ visitas

Supongo que te gusta

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