ALSA на вградена система - readi - Грешка при вход/изход и замръзване

Все още имам проблеми с използването на 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 print (взет от референтната реализация):

{
        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

но без замръзване. Така че защо може да замръзне, ако устройството "работи"?

Някакви предложения какво да направите, за да накарате това устройство/код да работи?

Съжалявам за целия този код, не съм сигурен дали всички параметри са необходими. Ако е твърде трудно за четене, кажете ми дали може да е по-добре да капсулирам цялата init част за четимост.


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)


Вторият случай (задаване на софтуерни параметри) не е недефинирано поведение, нито ГРЕШКА. Това всъщност е очакваното поведение. Когато състоянието е ПОДГОТОВЕН, четенето на по-малко от start_threshold кадъра води до блокиране на нишката. Друга нишка може да започне заснемане. Корекцията беше върната (вижте този ангажимент):

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