From: Andre Noll Date: Thu, 13 Jun 2013 08:53:55 +0000 (+0200) Subject: alsa: Work around poll fd problems. X-Git-Tag: v0.5.3~22^2~3 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=3ac2a1478a7ad965bb48bec7d21aa49affd95d29 alsa: Work around poll fd problems. In its ->pre_select method the alsa writer adds the alsa poll file descriptor to the set of fds to be monitored during the next select call. When this fd is ready for I/O, select() returns and ->post_select is called. Currently ->post_select checks if the alsa poll fd is set and returns early if it is not. The problem is that on some hardware/kernel/alsa-lib combinations, the poll fd is never marked as ready for I/O. This happens at least on the raspberry pi with linux-3.10.10 and alsa-lib-1.0.27. The result is no audio at all. We address this problem in two ways: * In alsa_init() we remember the buffer duration for later use in ->pre_select where we ask the scheduler to impose half of the duration as an upper bound on the select timeout. * In ->post_select, if there is input data available, we don't check any more whether the poll fd is ready but try to write unconditionally. On EAGAIN, we read from the poll fd to discard any pending events. --- diff --git a/alsa_write.c b/alsa_write.c index e75fa230..358b6346 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -50,6 +50,8 @@ struct private_alsa_write_data { snd_pcm_format_t sample_format; /* The number of channels, again determined like \a sample_rate. */ unsigned channels; + /* time until buffer underrun occurs, in microseconds */ + unsigned buffer_time; struct timeval drain_barrier; /* File descriptor for select(). */ int poll_fd; @@ -77,7 +79,6 @@ static int alsa_init(struct private_alsa_write_data *pad, snd_pcm_uframes_t start_threshold, stop_threshold; snd_pcm_uframes_t buffer_size, period_size; snd_output_t *output_log; - unsigned buffer_time; int ret; const char *msg; unsigned period_time; @@ -114,18 +115,18 @@ static int alsa_init(struct private_alsa_write_data *pad, if (ret < 0) goto fail; msg = "unable to get buffer time"; - ret = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, + ret = snd_pcm_hw_params_get_buffer_time_max(hwparams, &pad->buffer_time, NULL); - if (ret < 0 || buffer_time == 0) + if (ret < 0 || pad->buffer_time == 0) goto fail; /* buffer at most 500 milliseconds */ - buffer_time = PARA_MIN(buffer_time, 500U * 1000U); + pad->buffer_time = PARA_MIN(pad->buffer_time, 500U * 1000U); msg = "could not set buffer time"; ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams, - &buffer_time, NULL); + &pad->buffer_time, NULL); if (ret < 0) goto fail; - period_time = buffer_time / 4; + period_time = pad->buffer_time / 4; msg = "could not set period time"; ret = snd_pcm_hw_params_set_period_time_near(pad->handle, hwparams, &period_time, 0); @@ -221,6 +222,8 @@ static void alsa_write_pre_select(struct sched *s, struct task *t) sched_request_barrier_or_min_delay(&pad->drain_barrier, s); return; } + /* wait at most 50% of the buffer time */ + sched_request_timeout_ms(pad->buffer_time / 2 / 1000, s); ret = snd_pcm_poll_descriptors(pad->handle, &pfd, 1); if (ret < 0) { PARA_ERROR_LOG("could not get alsa poll fd: %s\n", @@ -309,21 +312,13 @@ again: wn->min_iqs = pad->bytes_per_frame; goto again; } - if (pad->poll_fd < 0 || !FD_ISSET(pad->poll_fd, &s->rfds)) - return 0; frames = bytes / pad->bytes_per_frame; frames = snd_pcm_writei(pad->handle, data, frames); if (frames == 0 || frames == -EAGAIN) { - /* - * The alsa poll fd was ready for IO but we failed to write to - * the alsa handle. This means another event is pending. We - * don't care about that but we have to dispatch the event in - * order to avoid a busy loop. So we simply read from the poll - * fd. - */ char buf[100]; - if (read(pad->poll_fd, buf, 100)) - do_nothing; + if (pad->poll_fd >= 0 && FD_ISSET(pad->poll_fd, &s->rfds)) + if (read(pad->poll_fd, buf, 100)) + do_nothing; return 0; } if (frames > 0) {