【问题标题】:Set snd_pcm_sw_params_set_stop_threshold to boundary, still getting underrun on snd_pcm_writei将 snd_pcm_sw_params_set_stop_threshold 设置为边界,仍然在 snd_pcm_writei 上运行不足
【发布时间】:2021-11-02 03:19:11
【问题描述】:

问题说明了一切。我在这里转圈。我将 snd_pcm_sw_params_set_stop_threshold 设置为边界(为了好玩也设置为零),但我仍然在 snd_pcm_writei 上遇到缓冲区欠载错误。我不明白为什么。文档对此非常清楚:

If the stop threshold is equal to boundary (also software parameter - sw_param) then automatic stop will be disabled

这是一个可重复性极低的示例:

#include <alsa/asoundlib.h>
#include <iostream>

#define AUDIO_DEV "default"

#define AC_FRAME_SIZE 960
#define AC_SAMPLE_RATE 48000
#define AC_CHANNELS 2

//BUILD g++ -o main main.cpp -lasound

using namespace std;

int main() {
    int err;
    unsigned int i;
    snd_pcm_t *handle;
    snd_pcm_sframes_t frames;
    snd_pcm_uframes_t boundary;
    snd_pcm_sw_params_t *sw;
    snd_pcm_hw_params_t *params;
    unsigned int s_rate;
    unsigned int buffer_time;
    snd_pcm_uframes_t f_size;
    unsigned char buffer[AC_FRAME_SIZE * 2];
    int rc;

    for (i = 0; i < sizeof(buffer); i++)
        buffer[i] = random() & 0xff;

    if ((err = snd_pcm_open(&handle, AUDIO_DEV, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
        cout << "open error " << snd_strerror(err) << endl;
        return 0;
    }

    s_rate = AC_SAMPLE_RATE;
    f_size = AC_FRAME_SIZE;
    buffer_time = 2500;

    cout << s_rate << " " << f_size << endl;

    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(handle, params);
    snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_channels(handle, params, AC_CHANNELS);
    snd_pcm_hw_params_set_rate_near(handle, params, &s_rate, 0);
    snd_pcm_hw_params_set_period_size_near(handle, params, &f_size, 0);

    cout << s_rate << " " << f_size << endl;

    rc = snd_pcm_hw_params(handle, params);
    if (rc < 0) {
        cout << "open error " << snd_strerror(err) << endl;
        return 0;
    }

    snd_pcm_sw_params_alloca(&sw);
    snd_pcm_sw_params_current(handle, sw);
    snd_pcm_sw_params_get_boundary(sw, &boundary);
    snd_pcm_sw_params_set_stop_threshold(handle, sw, boundary);

    rc = snd_pcm_sw_params(handle, sw);
    if (rc < 0) {
        cout << "open error " << snd_strerror(err) << endl;
        return 0;
    }

    snd_pcm_sw_params_current(handle, sw);

    snd_pcm_sw_params_get_stop_threshold(sw, &boundary);
    cout << "VALUE " << boundary << endl;

    for (i = 0; i < 1600; i++) {
        usleep(100 * 1000);
        frames = snd_pcm_writei(handle, buffer, f_size);
        if (frames < 0)
            frames = snd_pcm_recover(handle, frames, 0);
        if (frames < 0) {
            cout << "open error " << snd_strerror(frames) << endl;
            break;
        }
    }

    return 0;
}

【问题讨论】:

    标签: c++ alsa


    【解决方案1】:

    好的,我想通了。对于遇到此问题并同时启用 pipewire 或 pulse(或任何其他第三方非 alsa 声卡)作为“默认”卡的任何人,解决方案是不直接使用 pipewire 或 pulse。似乎 snd_pcm_sw_params_set_stop_threshold 在 pipewire/pulseaudio 中没有正确实现。您会注意到,如果您禁用 pipewire 或 pulse,此代码将完全按照您希望的方式运行。

    以下是如何禁用pulseaudio(这是我系统上的问题):

    systemctl --user stop pulseaudio.socket
    systemctl --user stop pulseaudio.service
    

    虽然更好的解决方案是将 AUDIO_DEV 设置为直接写入 alsa 卡。您可以通过运行 aplay -L 找到这些卡的名称。但在 95% 的情况下,将我的示例代码中的 AUDIO_DEV 更新为以下内容:

    #define AUDIO_DEV "hw:0,0"
    

    通常会解决问题。

    【讨论】:

      猜你喜欢
      • 2018-09-02
      • 1970-01-01
      • 1970-01-01
      • 2019-06-10
      • 2019-03-24
      • 2016-08-18
      • 2021-06-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多