summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWim Taymans <wim.taymans@collabora.co.uk>2010-09-08 13:26:39 +0200
committerColin Guthrie <cguthrie@mandriva.org>2010-09-09 17:03:34 +0100
commitf29acfd0e0413a9bd126782763ee2dcf10357546 (patch)
tree29bb37d84c49b4f27684f37d39f3d9df0c48f17e
parentb0042cec71ffb09d3720fdcc4223de8153fed67a (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.c2
-rw-r--r--src/modules/alsa/alsa-source.c2
-rw-r--r--src/modules/alsa/alsa-util.c46
-rw-r--r--src/modules/alsa/alsa-util.h2
4 files changed, 46 insertions, 6 deletions
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index f82494407..14f08a567 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -802,7 +802,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 dbaa305e0..fd6c189a6 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 1cbb3f31f..2d0d35638 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 1d1256bd3..9e29fd46d 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);