ALSA во встроенной системе — готовность — ошибка ввода/вывода и зависание

Я все еще сталкиваюсь с проблемами при использовании захвата ALSA во встроенной системе.

Теперь я могу открыть устройство из библиотеки, после использования скрипта snddevices. Но приложение возвращает сообщения об ошибках Input/output error (EIO) при readi звонках через время около 10 секунд для каждого звонка. После попытки остановить устройство вся система зависает и требует полной перезагрузки.

Я не могу сказать, правильно ли ALSA установлена ​​в системе. При запуске упоминается драйвер ALSA. Это version 1.0.18.rc3. Как проверить правильность установки ALSA и/или устройств?

Вот мой тестовый код, скажите, пожалуйста, есть ли внутри ошибка. Я не уверен, правильно ли установлены размеры frame и period (я не очень разбираюсь в обработке звука) и придется ли мне использовать захват interleaved или non-interleaved или это не имеет значения.

#include <alsa/asoundlib.h>



const char* print_pcm_state(snd_pcm_state_t state)
{
    switch(state)
    {
    case SND_PCM_STATE_OPEN: return("SND_PCM_STATE_OPEN");
    case SND_PCM_STATE_SETUP: return("SND_PCM_STATE_SETUP");
    case SND_PCM_STATE_PREPARED: return("SND_PCM_STATE_PREPARED");
    case SND_PCM_STATE_RUNNING: return("SND_PCM_STATE_RUNNING");
    case SND_PCM_STATE_XRUN: return("SND_PCM_STATE_XRUN");
    case SND_PCM_STATE_DRAINING: return("SND_PCM_STATE_DRAINING");
    case SND_PCM_STATE_PAUSED: return("SND_PCM_STATE_PAUSED");
    case SND_PCM_STATE_SUSPENDED: return("SND_PCM_STATE_SUSPENDED");
    case SND_PCM_STATE_DISCONNECTED: return("SND_PCM_STATE_DISCONNECTED");
    }
return "UNKNOWN STATE";
}



int main(int argc, char* argv[])
{
    int rc; int err;

    // pcm state
    snd_pcm_state_t pcm_state;
    // handle
    snd_pcm_t *handle;

    // hardware to open
    char *pcm_name;
    pcm_name =  strdup("hw:0,0");

    // parameters
    snd_pcm_hw_params_t *params;

    /* Open PCM device for playback. */
    rc = snd_pcm_open(&handle, pcm_name, SND_PCM_STREAM_CAPTURE, 0);

    if (rc < 0) 
    {

        printf("unable to open pcm device: %s\n", snd_strerror(rc));
        exit(1);
    }

    // Allocate a hardware parameters object.
    //  rc = snd_pcm_hw_params_alloca(&params); // original from a sample code, didn't compile?!?
    rc = snd_pcm_hw_params_malloc(&params);
    if(rc<0)
    {
        printf("cannot allocate hardware parameter structure (%s) ...\n", snd_strerror(rc));
    }

    /* Fill it in with default values. */
    rc = snd_pcm_hw_params_any(handle, params);
    if(rc<0)
    {
        printf("Error: (%s) ...\n", snd_strerror(rc));
    }

    bool interleaved = true;
    // Set the desired hardware parameters. 
    if (interleaved)
    {   
        // Interleaved mode 
        if ((rc = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
        {
            printf("cannot set access type (%s) ...\n", snd_strerror(rc));
            return rc;
        }
        printf("access type = SND_PCM_ACCESS_RW_INTERLEAVED\n");
    }
    else
    {
        if ((rc = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED)) < 0)
        {
            printf("cannot set access type (%s) ...\n", snd_strerror(rc));
            return rc;
        }
        printf("access type = SND_PCM_ACCESS_RW_NONINTERLEAVED\n");
    }

    // Signed 16-bit little-endian format 
    if ((rc = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE)) < 0) {
        printf("cannot set sample format (%s) ...\n", snd_strerror(rc));
        return 1;
    }

    // TODO: channel number important - will a bad channel number crash the whole capturing?
    int nChannels = 2;
    if ((rc = snd_pcm_hw_params_set_channels(handle, params, nChannels)) < 0) {
        printf("cannot set channel count (%s) ...\n", snd_strerror(rc));
        return 1;   
    }

    // TODO: right?
    unsigned int wanted_rate = 22000;
    unsigned int exact_rate = wanted_rate;
    if ((rc = snd_pcm_hw_params_set_rate_near(handle, params, &exact_rate, 0)) < 0) {
        printf("cannot set sample rate (%s) ...\n", snd_strerror(rc));
        return 1;
    }

    if (wanted_rate != exact_rate) 
    {
          printf("The rate %i Hz is not supported by your hardware.\n" 
                  "==> Using %i Hz instead.\n", wanted_rate, exact_rate);
    }

    // TODO: what about these parts? What are those "frames" and what are the "periods"?
    // must this be set according to the hardware?!?
    snd_pcm_uframes_t frames = 640;
    int periods = 8;

    // Set number of periods. Periods used to be called fragments. 
    if (snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0) {    
          printf("Error setting periods.\n");
          return 1;
        }

    if ((err = snd_pcm_hw_params_set_period_size_near(handle, params, &frames, 0)) < 0) {
        fprintf(stderr, "Init: cannot set period_size (%s) ...\n", snd_strerror(err));
        return err; 
    }

    // TODO: is this the size needed for a single read-call?
    snd_pcm_uframes_t buff_size = 0;
    err = snd_pcm_hw_params_get_buffer_size(params, &buff_size);
    if (err < 0) {
        printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
        return err;
    }
    printf("needed buffersize: %i \n", (int)buff_size);

    pcm_state = snd_pcm_state(handle);
    printf("0.m State: %s\n", print_pcm_state(pcm_state));

    // what's this?
    snd_pcm_sw_params_t*  swparams_c;

    // snd_pcm_hw_params will call PREPARE internally!
    if ((err = snd_pcm_hw_params(handle, params)) < 0) {
        fprintf(stderr, "Init: cannot set parameters (%s) ...\n", snd_strerror(err));
        return err;     
    }

    pcm_state = snd_pcm_state(handle);
    printf("0.n State: %s\n", print_pcm_state(pcm_state));

    printf("capture\n");

    // test: start device:
    /*
    err = snd_pcm_start(handle);
    if(err < 0)
    {
        fprintf (stderr, "cannot start device (%s)\n",
                     snd_strerror (err));
                exit (1);
    }
    */


    //state
    pcm_state = snd_pcm_state(handle);  
    printf("1. State: %s\n", print_pcm_state(pcm_state));

    // Is this ok?!?
    //short buf[100*2048];
    //short buf[5120*1024]; // seg-fault?!?
    short buf[5120];

    // only try to read a single time
    int i=0;
    for (i = 0; i < 1; ++i) {
        if(interleaved)
        {
            if ((err = snd_pcm_readi (handle, buf, frames)) < 0)
    //      if ((err = snd_pcm_writei (handle, buf, 1)) < 0)  // ioctl error
            {
                printf("EBADFD %i -> EPIPE %i -> ESTRPIPE %i\n",EBADFD,EPIPE,ESTRPIPE);
                if(err == -EBADFD)
                    printf("-EBADFD: PCM is not in the right state (SND_PCM_STATE_PREPARED or SND_PCM_STATE_RUNNING) \n");
                if(err == -EPIPE) printf("-EPIPE:   an overrun occurred \n");
                if(err == -ESTRPIPE) printf("-ESTRPIPE: a suspend event occurred (stream is suspended and waiting for an application recovery)\n");

                fprintf (stderr, "error %i : interleaved read from audio interface failed (%s)\n",
                     err, snd_strerror (err));

                pcm_state = snd_pcm_state(handle);
                printf("1.1 State: %s\n", print_pcm_state(pcm_state));
                exit (1);
            }
        }
        else
        {
            // TODO: is it hardware dependent whether I can exlusively use readi or readn?
            // how does a readn call have to look like? needs multiple buffers?!?
            printf("non-interleaved capture not implemented\n");



            //if ((err = snd_pcm_readn (handle, buf, frames)) != frames) {
            //  fprintf (stderr, "non-interleaved read from audio interface failed (%s)\n",
            //       snd_strerror (err));
            //  exit (1);
            //}
        }
    }

    pcm_state = snd_pcm_state(handle);
    printf("2. State: %s\n", print_pcm_state(pcm_state));

    printf("close\n");

    snd_pcm_close (handle);

    pcm_state = snd_pcm_state(handle);
    printf("3. State: %s\n", print_pcm_state(pcm_state));

    printf("finish\n");
    return 0;
}

производя этот вывод:

~ # ./audioTest 
access type = SND_PCM_ACCESS_RW_INTERLEAVED
The rate 22000 Hz is not supported by your hardware.
==> Using 16000 Hz instead.
needed buffersize: 5120 
0.m State: SND_PCM_STATE_OPEN
hello, alsa~.
0.n State: SND_PCM_STATE_PREPARED
capture
1. State: SND_PCM_STATE_PREPARED
EBADFD 77 -> EPIPE 32 -> ESTRPIPE 86
error -5 : interleaved read from audio interface failed (Input/output error)
1.1 State: SND_PCM_STATE_RUNNING

потом замерзает...

если я добавлю еще один блок параметризации перед печатью capture (взято из эталонной реализации):

{
        snd_pcm_uframes_t boundary = 0;
        snd_pcm_sw_params_alloca(&swparams_c);
        /* get the current swparams */
        err = snd_pcm_sw_params_current(handle, swparams_c);
        if (err < 0) {
            printf("Unable to determine current swparams_c: %s\n", snd_strerror(err));
            return err;
        }

        // what's this? necessary?
        /*
        //err = snd_pcm_sw_params_set_tstamp_mode(handle, swparams_c, SND_PCM_TSTAMP_ENABLE);
        err = snd_pcm_sw_params_set_tstamp_mode(handle, swparams_c, SND_PCM_TSTAMP_MMAP);
        if (err < 0) {
            printf("Unable to set tstamp mode : %s\n", snd_strerror(err));
            return err;
        }
        */

        err = snd_pcm_sw_params_set_avail_min(handle, swparams_c, frames);
        if (err < 0) {
            printf("Unable to call snd_pcm_sw_params_set_avail_min(): %s\n", snd_strerror(err));
            return err;
        }

        err = snd_pcm_sw_params_set_start_threshold(handle, swparams_c, (buff_size / frames) * frames);
        if (err < 0) {
            printf("Unable to call snd_pcm_sw_params_set_start_threshold(): %s\n", snd_strerror(err));
            return err;
        }

        err =  snd_pcm_sw_params_get_boundary(swparams_c, &boundary);
        if (err < 0) {
            printf("Unable to call snd_pcm_sw_params_get_boundary(): %s\n", snd_strerror(err));
            return err;
        }


        err = snd_pcm_sw_params_set_stop_threshold(handle, swparams_c, boundary);
        if (err < 0) {
            printf("Unable to call snd_pcm_sw_params_set_stop_threshold(): %s\n", snd_strerror(err));
            return err;
        }

        /* align all transfers to 1 sample */
        err = snd_pcm_sw_params_set_xfer_align(handle, swparams_c, 1);
        if (err < 0) {
            printf("Unable to set transfer align for playback: %s\n", snd_strerror(err));
            return err;
        }

        /* write the sw parameters */
        err = snd_pcm_sw_params(handle, swparams_c);
        if (err < 0) {
            printf("Unable to set swparams_c : %s\n", snd_strerror(err));
            return err;
        }
    }

Я заканчиваю сообщениями:

error -5 : interleaved read from audio interface failed (Input/output error)
2.1 State: SND_PCM_STATE_PREPARED

но без заморозки. Так почему же он может зависнуть, если устройство «работает»?

Любые предложения, что сделать, чтобы это устройство/код работало?

Извините за весь этот код, я не уверен, нужны ли все параметры. Если это слишком сложно для чтения, скажите мне, может быть, было бы лучше инкапсулировать всю часть инициализации для удобства чтения.


person Micka    schedule 19.10.2015    source источник
comment
Зависание системы похоже на ошибку драйвера.   -  person CL.    schedule 19.10.2015
comment
Есть ли шанс узнать, что это, скорее всего, ошибка alsa-driver или ошибка device-driver?   -  person Micka    schedule 21.10.2015
comment
Сайт вопросов и ответов, такой как SO, не является подходящим форумом, чтобы научить вас всему об отладке.   -  person CL.    schedule 21.10.2015


Ответы (1)


Второй случай (настройка параметров программного обеспечения) не является неопределённым поведением и не является ОШИБКОЙ. На самом деле это ожидаемое поведение. Когда состояние PREPARED, чтение менее start_threshold кадров приводит к блокировке потока. Другой поток может начать захват. Исправление было отменено (см. " rel="nofollow noreferrer">этот коммит):

ALSA: pcm: Комментарий, почему читаются блоки, когда PCM не запущен

Это позволяет избежать повторения проблемы, возникшей в 62ba568f7aef ("ALSA: pcm: вернуть 0, когда размер ‹ start_threshold при захвате") и исправленной в 00a399cad1a0 ("ALSA: pcm: отменить изменение поведения потока захвата в режиме блокировки"), которая запретил пользователю начинать захват из другого потока.


В первом случае проблем быть не должно. Когда параметры оборудования установлены, ALSA устанавливает значения по умолчанию для параметров программного обеспечения. start_threshold устанавливается на 1. Устройство запускается в первом чтении. Однако, кажется, есть еще один тайм-аут, приводящий к EIO. Я опубликую здесь обновление, если обнаружу проблему в ядре ALSA. В любом случае, это, вероятно, драйвер устройства, который не выдавал прерываний и вызывал зависание системы.

person Ricardo Biehl Pasquali    schedule 26.08.2018