diff options
author | Wim Taymans <wim.taymans@collabora.co.uk> | 2010-09-08 13:26:39 +0200 |
---|---|---|
committer | Colin Guthrie <cguthrie@mandriva.org> | 2010-09-14 08:46:41 +0100 |
commit | 1c29f5582d3638a32271d8882de7c22579a42c6f (patch) | |
tree | 914bb74913383c43c15982cc43b74f8e70cfb1bf | |
parent | 74c117f49f9a896448ba9c4140d0fa0ed4be71d2 (diff) |
alsa: work around slightly broken _delay implementations
Use snd_pcm_avail_delay() in pa_alsa_safe_delay() so that we can check the delay
value against the avail value and patch it up when it looks invalid. Only do
this for capture.
-rw-r--r-- | src/modules/alsa/alsa-sink.c | 2 | ||||
-rw-r--r-- | src/modules/alsa/alsa-source.c | 2 | ||||
-rw-r--r-- | src/modules/alsa/alsa-util.c | 46 | ||||
-rw-r--r-- | src/modules/alsa/alsa-util.h | 2 |
4 files changed, 46 insertions, 6 deletions
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 4a5fe3fb..ae7fecd2 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -803,7 +803,7 @@ static void update_smoother(struct userdata *u) { /* Let's update the time smoother */ - if (PA_UNLIKELY((err = pa_alsa_safe_delay(u->pcm_handle, &delay, u->hwbuf_size, &u->sink->sample_spec)) < 0)) { + if (PA_UNLIKELY((err = pa_alsa_safe_delay(u->pcm_handle, &delay, u->hwbuf_size, &u->sink->sample_spec, FALSE)) < 0)) { pa_log_warn("Failed to query DSP status data: %s", pa_alsa_strerror(err)); return; } diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 8b2cee50..10f0d74e 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -760,7 +760,7 @@ static void update_smoother(struct userdata *u) { /* Let's update the time smoother */ - if (PA_UNLIKELY((err = pa_alsa_safe_delay(u->pcm_handle, &delay, u->hwbuf_size, &u->source->sample_spec)) < 0)) { + if (PA_UNLIKELY((err = pa_alsa_safe_delay(u->pcm_handle, &delay, u->hwbuf_size, &u->source->sample_spec, TRUE)) < 0)) { pa_log_warn("Failed to get delay: %s", pa_alsa_strerror(err)); return; } diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 1cbb3f31..2d0d3563 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -1125,10 +1125,11 @@ snd_pcm_sframes_t pa_alsa_safe_avail(snd_pcm_t *pcm, size_t hwbuf_size, const pa return n; } -int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_size, const pa_sample_spec *ss) { +int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_size, const pa_sample_spec *ss, pa_bool_t capture) { ssize_t k; size_t abs_k; int r; + snd_pcm_sframes_t avail = 0; pa_assert(pcm); pa_assert(delay); @@ -1136,9 +1137,10 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_si pa_assert(ss); /* Some ALSA driver expose weird bugs, let's inform the user about - * what is going on */ + * what is going on. We're going to get both the avail and delay values so + * that we can compare and check them for capture */ - if ((r = snd_pcm_delay(pcm, delay)) < 0) + if ((r = snd_pcm_avail_delay(pcm, &avail, delay)) < 0) return r; k = (ssize_t) *delay * (ssize_t) pa_frame_size(ss); @@ -1167,6 +1169,44 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_si *delay = (snd_pcm_sframes_t) (hwbuf_size / pa_frame_size(ss)); } + if (capture) { + abs_k = (size_t) avail * pa_frame_size(ss); + + if (abs_k >= hwbuf_size * 5 || + abs_k >= pa_bytes_per_second(ss)*10) { + + PA_ONCE_BEGIN { + char *dn = pa_alsa_get_driver_name_by_pcm(pcm); + pa_log(_("snd_pcm_avail() returned a value that is exceptionally large: %lu bytes (%lu ms).\n" + "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."), + (unsigned long) k, + (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC), + pa_strnull(dn)); + pa_xfree(dn); + pa_alsa_dump(PA_LOG_ERROR, pcm); + } PA_ONCE_END; + + /* Mhmm, let's try not to fail completely */ + avail = (snd_pcm_sframes_t) (hwbuf_size / pa_frame_size(ss)); + } + + if (*delay < avail) { + PA_ONCE_BEGIN { + char *dn = pa_alsa_get_driver_name_by_pcm(pcm); + pa_log(_("snd_pcm_avail_delay() returned strange values: delay %lu is less than avail %lu.\n" + "Most likely this is a bug in the ALSA driver '%s'. Please report this issue to the ALSA developers."), + (unsigned long) *delay, + (unsigned long) avail, + pa_strnull(dn)); + pa_xfree(dn); + pa_alsa_dump(PA_LOG_ERROR, pcm); + } PA_ONCE_END; + + /* try to fixup */ + *delay = avail; + } + } + return 0; } diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index 1d1256bd..9e29fd46 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -129,7 +129,7 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents); pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll); snd_pcm_sframes_t pa_alsa_safe_avail(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss); -int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_size, const pa_sample_spec *ss); +int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_size, const pa_sample_spec *ss, pa_bool_t capture); int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss); char *pa_alsa_get_driver_name(int card); |