Colección de audio de Linux y dificultades encontradas en plataformas localizadas (2)

Colección de audio de Linux y dificultades encontradas en plataformas localizadas (2)

La recopilación ALSA es un callejón sin salida, por lo que solo podemos probar otros métodos. Aquí, a través de la interfaz de PulseAudio, hemos realizado con éxito la función de recopilar el sonido del micrófono y del sistema en la plataforma doméstica.

Colección de audio PulseAudio de Linux

En primer lugar, la diferencia entre PulseAudio y ALSA es que ALSA está a nivel de kernel, mientras que PulseAudio es un servicio a nivel de usuario y se utiliza como servidor de sonido para administrar varias entradas y salidas de audio de la aplicación, al igual que ALSA. la mayoría de las distribuciones de Linux tienen PulseAudio instalado de forma predeterminada. Galaxy Kirin, nuestra plataforma de chips de producción nacional, naturalmente no es una excepción. El diagrama de estructura de PulseAudio se ve así:

Insertar descripción de la imagen aquí

Como puede ver, PulseAudio, como servicio, está ubicado en la capa superior de ALSA. Permite que múltiples aplicaciones llamen a PulseAudio al mismo tiempo y lo utiliza como mezclador de audio internamente. Esto puede evitar que el programa aparezca en diferentes entornos de hardware. debido a la exclusividad de ALSA la situación no se puede utilizar con normalidad. La relación de llamada entre la aplicación y PulseAudio es la siguiente:

Insertar descripción de la imagen aquí

Normalmente, el sistema no preinstala el paquete de desarrollo PulseAudio, en este momento necesitamos instalarlo para poder llamar a la interfaz en el código.

sudo apt-get instalar libpulse-dev

La recopilación de audio de PulseAudio es obviamente mucho más complicada que ALSA: cada aplicación se considera un cliente de PulseAudio, se conecta al servicio PulseAudio del sistema y necesita mantener un hilo como una cola circular para la transferencia de datos. Aquí hay una lista de varias funciones utilizadas por carrera:

#include <pulse/pulseaudio.h>

/***
 申请一个包含线程的事件循环
*/
pa_threaded_mainloop* pa_threaded_mainloop_new();

/***
 开启事件循环
 @return: 0表示成功,小于0表示错误码
*/
int pa_threaded_mainloop_start(pa_threaded_mainloop* m);

/***
 终止事件循环,在调用此函数前,必须确保事件循环已经解锁
*/
void pa_threaded_mainloop_stop(pa_threaded_mainloop* m);

/***
 阻塞并等待事件循环中消息被触发,注意,该函数返回并不一定是因为调用了pa_threaded_mainloop_signal()
 需要甄别这一点
*/
void pa_threaded_mainloop_wait(pa_threaded_mainloop* m);

/***
 触发消息
*/
void pa_threaded_mainloop_signal(pa_threaded_mainloop* m, int wait_for_accept);
#include <pulse/pulseaudio.h>

/***
 创建PulseAudio连接上下文
*/
pa_context* pa_context_new(pa_mainloop_api *mainloop, const char *name);

/***
 将context连接到指定的PulseAudio服务,如果server为NULL,则连接到系统默认服务。
 @return: 小于0表示错误
*/
int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api);

/***
 终止事件循环,在调用此函数前,必须确保事件循环已经解锁
*/
void pa_context_disconnect(pa_context* c);

/***
 引用计数减1
*/
void pa_context_unref(pa_context* c);

/***
 返回当前上下文状态
*/
pa_context_state_t pa_context_get_state(const pa_context* c);
#include <pulse/pulseaudio.h>

/***
 在当前PulseAudio连接上,创建一个stream,用于输入或输出音频数据
*/
pa_stream* pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map);

/***
 将context连接到指定的PulseAudio服务,如果server为NULL,则连接到系统默认服务。
 @return: 小于0表示错误
*/
int pa_stream_connect_record(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api);

/***
 从缓冲区中读取下一个采集的音频片段
*/
int pa_stream_peek(pa_stream *p, const void **data, size_t *nbytes);

/***
 放弃当前输入(采集)的音频片段
*/
void pa_stream_drop(pa_stream* s);

/***
 关闭输入输出流
*/
void pa_stream_disconnect(pa_stream* s);

/***
 引用计数减1
*/
void pa_stream_unref(pa_stream* s);

/***
 返回当前stream状态
*/
pa_context_state_t pa_stream_get_state(const pa_stream* s);

Aquí hay un ejemplo simple para demostrar cómo llamar

  1. Cree un bucle de eventos, conéctese al servidor PulseAudio, cree una transmisión y establezca parámetros. Para que parezca más intuitivo, he eliminado aquí algunos códigos juzgados incorrectamente.
bool PulseAudioCapture::Start(Observer* ob)
{
    
    
    observer_ = ob;

    SIMPLE_LOG("try open %s\n", device_name_.c_str());

    int ret = 0;
    const char* name = "HbsPulse";
    const char* stream_name = "HbsPulseStream";
    char* device = NULL;
    if (false == device_name_.empty())
    {
    
    
        device = (char*)device_name_.c_str();
    }

    const struct pa_sample_spec *pss = nullptr;

    pa_sample_format_t sam_fmt = AV_NE(PA_SAMPLE_S16BE, PA_SAMPLE_S16LE);
    const pa_sample_spec ss = {
    
     sam_fmt, sample_rate_, channel_count_ };

    pa_buffer_attr attr = {
    
     (uint32_t)-1 };
    pa_channel_map cmap;
    const pa_buffer_attr *queried_attr = nullptr;
    int stream_flag = 0;

    pa_channel_map_init_extend(&cmap, channel_count_, PA_CHANNEL_MAP_WAVEEX);

    mainloop_ = pa_threaded_mainloop_new();

    context_ = pa_context_new(pa_threaded_mainloop_get_api(mainloop_), name);

    pa_context_set_state_callback(context_, context_state_cb, this);

    pa_context_connect(context_, pulse_server_, /*0*/PA_CONTEXT_NOFLAGS, NULL);

    pa_threaded_mainloop_lock(mainloop_);

    pa_threaded_mainloop_start(mainloop_);

    for (;;)
    {
    
    
        pa_context_state_t state = pa_context_get_state(context_);

        if (state == PA_CONTEXT_READY)
            break;

        if (!PA_CONTEXT_IS_GOOD(state))
        {
    
    
            int ec = pa_context_errno(context_);
            SIMPLE_LOG("pulse context state bad: %d, err: %d\n", state, ec);

            goto unlock_and_fail;
        }

        /* Wait until the context is ready */
        pa_threaded_mainloop_wait(mainloop_);
    }

    SIMPLE_LOG("pulse context ready!\n");

    stream_ = pa_stream_new(context_, stream_name, &ss, &cmap);

    pa_stream_set_state_callback(stream_, stream_state_cb, this);
    pa_stream_set_read_callback(stream_, stream_read_cb, this);
    pa_stream_set_write_callback(stream_, stream_write_cb, this);
    pa_stream_set_latency_update_callback(stream_, stream_latency_update_cb, this);

    ret = pa_stream_connect_record(stream_, device, &attr,
        PA_STREAM_ADJUST_LATENCY|PA_STREAM_AUTO_TIMING_UPDATE);

    for (;;)
    {
    
    
        pa_stream_state_t state = pa_stream_get_state(stream_);

        if (state == PA_STREAM_READY)
            break;

        if (!PA_STREAM_IS_GOOD(state))
        {
    
    
            int ec = pa_context_errno(context_);
            SIMPLE_LOG("pulse stream state bad: %d, err: %d\n", state, ec);

            goto unlock_and_fail;
        }

        /* Wait until the stream is ready */
        pa_threaded_mainloop_wait(mainloop_);
    }

    pa_threaded_mainloop_unlock(mainloop_);

    SIMPLE_LOG("pulse audio start ok, fragsize: %d, framesize: %d\n", fragment_size_, pa_frame_size_);

    ThreadStart();

    return true;

unlock_and_fail:
    pa_threaded_mainloop_unlock(mainloop_);

    ClosePulse();
    return false;
}
  1. Leer datos de audio
bool PulseAudioCapture::ReadData()
{
    
    
    int ret;
    size_t read_length;
    const void *read_data = NULL;

    pa_usec_t latency;
    int negative;
    ptrdiff_t pos = 0;

    pa_threaded_mainloop_lock(mainloop_);

    if (IsPulseDead())
    {
    
    
        SIMPLE_LOG("pulse is dead\n");
        goto unlock_and_fail;
    }

    while (pos < fragment_size_)
    {
    
    
        int r = pa_stream_peek(stream_, &read_data, &read_length);
        if (r != 0)
        {
    
    
            SIMPLE_LOG("pa_stream_peek: %d\n", r);
            goto unlock_and_fail;
        }

        if (read_length <= 0)
        {
    
    
            pa_threaded_mainloop_wait(mainloop_);
            if (IsPulseDead())
            {
    
    
                SIMPLE_LOG("pulse is dead\n");
                goto unlock_and_fail;
            }
        }
        else if (!read_data)
        {
    
    
            /* There's a hole in the stream, skip it. We could generate
            * silence, but that wouldn't work for compressed streams. */
            r = pa_stream_drop(stream_);
            if (r != 0)
            {
    
    
                SIMPLE_LOG("null data, pa_stream_drop: %d\n", r);
                goto unlock_and_fail;
            }
        }
        else 
        {
    
    
            if (!pos)
            {
    
    
                if (pcm_buf_.empty())
                {
    
    
                    pcm_buf_.resize(fragment_size_);
                }

                //pcm_dts_ = av_gettime();
                pa_operation_unref(pa_stream_update_timing_info(stream_, NULL, NULL));

                if (pa_stream_get_latency(stream_, &latency, &negative) >= 0)
                {
    
    
                    if (negative)
                    {
    
    
                        pcm_dts_ += latency;
                    }
                    else
                        pcm_dts_ -= latency;
                }
                else
                {
    
    
                    SIMPLE_LOG("pa_stream_get_latency() failed\n");
                }
            }

            if (pcm_buf_.size() - pos < read_length)
            {
    
    
                if (pos)
                    break;
                pa_stream_drop(stream_);
                /* Oversized fragment??? */
                SIMPLE_LOG("Oversized fragment\n");
                goto unlock_and_fail;
            }

            memcpy(pcm_buf_.data() + pos, read_data, read_length);
            pos += read_length;
            pa_stream_drop(stream_);
        }
    }

SIMPLE_LOG("read pos: %d\n", pos);

    pa_threaded_mainloop_unlock(mainloop_);

    return true;

unlock_and_fail:
    pa_threaded_mainloop_unlock(mainloop_);
    return false;
}

Al seleccionar un dispositivo de audio, el nombre del dispositivo de audio debe consultarse a través de la interfaz relacionada con PulseAudio. Para dispositivos de recopilación de audio, puede llamar a la función pa_context_get_source_info_list(). Después de los experimentos, se utilizó PulseAudio para recopilar audio y se realizó con éxito la función de recopilar el sonido del micrófono y del sistema en el sistema Kirin de la plataforma doméstica, evitando varios problemas que ocurrieron en el uso anterior del código ALSA en un entorno de tarjetas de sonido múltiples. .

Además, cabe señalar que es posible que el tamaño de los datos recopilados a través de PulseAudio no sea necesario para la codificación y que también se requiera el almacenamiento en búfer de datos.

Agregue el autor hbstream para cooperar. (Por favor indique el autor y la fuente al reimprimir)


Supongo que te gusta

Origin blog.csdn.net/haibindev/article/details/128876970
Recomendado
Clasificación