2

我仍然面临在嵌入式系统上使用 ALSA 捕获的问题。

snddevices使用脚本后,我现在可以从库中打开设备。但是应用程序在每次调用大约 10 秒后返回错误消息Input/output error( EIO) 。readi尝试停止设备后,整个系统冻结并需要硬重置。

我无法判断系统上是否正确安装了 ALSA。在启动时,会提到 ALSA 驱动程序。是version 1.0.18.rc3。如何测试 ALSA 和/或设备是否安装正确?

这是我的测试代码,请告诉我里面是否有错误。我不确定sizeframeperiodist 设置是否正确(我对音频处理了解不多)以及我是否必须使用interleavednon-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

但没有冻结。那么,如果设备正在“运行”,为什么它会冻结呢?

有什么建议可以使该设备/代码正常工作吗?

抱歉所有代码,我不确定是否需要所有参数。如果太难阅读,请告诉我是否最好封装整个 init 部分以提高可读性。

4

1 回答 1

3

第二种情况(设置软件参数)既不未定义的行为也不是BUG。这实际上是预期的行为。当状态为 PREPARED 时,读取少于start_threshold帧会导致线程阻塞。另一个线程可能开始捕获。该补丁已恢复(请参阅此提交):

ALSA:pcm:评论为什么在 PCM 未运行时读取块

这避免了由 62ba568f7aef 引入的问题(“ALSA: pcm: Return 0 when size < start_threshold in capture”)并在 00a399cad1a0 中修复(“ALSA: pcm: Revert capture stream behavior change in blocking mode”),这阻止了用户从另一个线程开始捕获


第一个案例应该没有任何问题。设置硬件参数时,ALSA 会为软件参数设置默认值。start_threshold设置为1。设备在第一次读取时启动。但是,似乎还有另一个超时导致 EIO。如果我在 ALSA 核心中发现问题,我将在此处发布更新。无论如何,可能是设备驱动程序没有发出中断并导致系统冻结。

于 2018-08-26T00:08:50.147 回答