You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用poll()结合ALSA采集音频样本失败问题求助

单个ALSA线程中poll()无法触发采集事件的问题解决思路

我需要开发一个仅包含单个ALSA线程的应用程序,通过poll()来处理混音器、播放和采集的所有事件。播放功能已实现,但使用poll()进行音频样本采集时,无法收到采集事件。

代码实现

#include <stdio.h>
#include <alsa/asoundlib.h>
#include <sys/eventfd.h>
#include <pthread.h>

#define COUNT_SAMPLES 1024
#define COUNT_CHANNELS 2
#define DEVICE "plughw:CARD=DSC8,DEV=0"
#define SAMPLE_RATE 48000

#define FD_IGNORE -1
enum ALSA_FD {
    FD_CONFIG,
    FD_PLAYBACK,
    FD_CAPTURE,
    FD_MIXER,
    FD_COUNT
};

static snd_pcm_t *capture_handle = NULL;
static struct pollfd fds[FD_COUNT];
static int fds_initialized = 0;

#define exit_on_fail(result, msg) { \
    int error = result; \
    if (error < 0) { \
        printf("ERROR: %s (%s)\n", msg, snd_strerror(error)); \
        exit(0); \
    } \
}

void set_parameters(snd_pcm_t *handle, unsigned sample_rate, snd_pcm_format_t format, unsigned int count_channels) {
    unsigned long period_size = COUNT_SAMPLES;
    snd_pcm_sw_params_t *sw_params = NULL;
    snd_pcm_hw_params_t *hw_params = NULL;

    snd_pcm_sw_params_malloc(&sw_params);
    snd_pcm_sw_params_current(handle, sw_params);
    snd_pcm_hw_params_alloca(&hw_params);
    exit_on_fail(snd_pcm_hw_params_any(handle, hw_params), "Failed to retrieve HW params");
    exit_on_fail(snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "Can't set interleaved mode.");
    exit_on_fail(snd_pcm_hw_params_set_format(handle, hw_params, format), "Can't set format.");
    exit_on_fail(snd_pcm_hw_params_set_channels(handle, hw_params, count_channels), "Can't set channels number.");
    exit_on_fail(snd_pcm_hw_params_set_rate_near(handle, hw_params, &sample_rate, 0), "Can't set rate.");
    exit_on_fail(snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, 0), "Can't set period size");
    exit_on_fail(snd_pcm_hw_params(handle, hw_params), "Failed to set HW params");
}

void * alsa_thread(void * arg) {
    int status;
    int terminated = 0;

    printf("Starting alsa thread\n");

    fds[FD_CONFIG].fd = FD_IGNORE; // EFD_NONBLOCK
    fds[FD_CONFIG].fd = eventfd(0, EFD_NONBLOCK); // EFD_NONBLOCK
    fds[FD_CONFIG].events = POLLIN;

    fds[FD_CAPTURE].fd = FD_IGNORE;
    fds[FD_CAPTURE].events = POLLIN;

    fds[FD_PLAYBACK].fd = FD_IGNORE;
    fds[FD_PLAYBACK].events = POLLOUT;

    fds[FD_MIXER].fd = FD_IGNORE;
    fds[FD_MIXER].events = POLLIN;

    fds_initialized = 1;

    while (!terminated) {
        status = poll(fds, FD_COUNT, -1);
        if (status > 0) {
            printf("Status: %d\n", status);
            if (fds[FD_PLAYBACK].revents & POLLOUT) {
                printf("PLAYBACK\n");
            }
            if (fds[FD_CAPTURE].revents & POLLIN) {
                printf("CAPTURE\n");
            }
            if (fds[FD_MIXER].revents & POLLOUT) {
                printf("MIXER\n");
            }
            if (fds[FD_CONFIG].revents & POLLIN) {
                eventfd_t value;
                eventfd_read(fds[FD_CONFIG].fd, &value);
                printf("CONFIG\n");
            }
        } else if (status < 0) {
            printf("Error: %d", status);
        }
    }

    close(fds[FD_CONFIG].fd);

    return NULL;
}

void notify_config_changed() {
    while (!fds_initialized);
    eventfd_write(fds[FD_CONFIG].fd, 1);
}

void start_capture(const char *device, unsigned sample_rate, unsigned count_channels) {
    exit_on_fail(snd_pcm_open(&capture_handle, device, SND_PCM_STREAM_CAPTURE, 0), "Failed to open PCM device");
    set_parameters(capture_handle, sample_rate, SND_PCM_FORMAT_S32_LE, count_channels);
    exit_on_fail(snd_pcm_poll_descriptors(capture_handle, &fds[FD_CAPTURE], 1), "Failed to set file descriptor");

    notify_config_changed();

    printf("Capturing ...\n");
}

int main(int argc, char **argv) {
    pthread_t tid_alsa;
    pthread_create(&tid_alsa, NULL, alsa_thread, NULL);
    start_capture(DEVICE, SAMPLE_RATE, COUNT_CHANNELS);
    for(;;);
    return 0;
}

控制台输出

Starting alsa thread
Capturing ...
Status: 1
CONFIG

解决思路

  • 启动采集流:当前代码仅完成了采集设备的参数配置,但未启动采集流。在start_capture函数末尾添加snd_pcm_start(capture_handle),让设备开始采集数据,这样当缓冲区中有可用数据时才会触发POLLIN事件。
  • 验证poll事件状态:在poll返回后,使用snd_pcm_poll_descriptors_revents函数验证采集设备的实际事件,确保revents的判断逻辑正确,示例代码:
    unsigned short revents;
    snd_pcm_poll_descriptors_revents(capture_handle, &fds[FD_CAPTURE], 1, &revents);
    if (revents & POLLIN) {
        printf("CAPTURE\n");
    }
    
  • 检查PCM设备状态:启动采集流后,调用snd_pcm_state(capture_handle)确认设备是否进入SND_PCM_STATE_RUNNING状态,避免设备处于暂停或未启动状态。
  • 线程同步保护:修改全局fds数组时,添加内存屏障或互斥锁,确保线程间的变量可见性,避免因CPU缓存导致的线程无法读取到最新的fd值。

内容的提问来源于stack exchange,提问作者suddenfall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 09:44:58