使用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
相关产品推荐
相关产品推荐

