Index: files/patch-bug1021761 =================================================================== --- files/patch-bug1021761 (revision 374691) +++ files/patch-bug1021761 (working copy) @@ -120,19 +120,20 @@ }; int i; -diff --git media/libcubeb/src/cubeb_alsa.c media/libcubeb/src/cubeb_alsa.c -index a962553..1f780f4 100644 ---- media/libcubeb/src/cubeb_alsa.c -+++ media/libcubeb/src/cubeb_alsa.c -@@ -11,6 +11,7 @@ +--- media/libcubeb/src/cubeb_alsa.c 2014-11-26 07:30:16.000000000 -0500 ++++ media/libcubeb/src/cubeb_alsa.c 2014-12-16 22:03:04.000000000 -0500 +@@ -11,7 +11,10 @@ #include #include #include +#include #include ++#include ++#include #include #include -@@ -24,6 +25,50 @@ + #include "cubeb/cubeb.h" +@@ -24,6 +27,51 @@ #define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin" @@ -155,6 +156,7 @@ +MAKE_TYPEDEF(snd_pcm_close); +MAKE_TYPEDEF(snd_pcm_delay); +MAKE_TYPEDEF(snd_pcm_drain); ++MAKE_TYPEDEF(snd_pcm_forward); +MAKE_TYPEDEF(snd_pcm_frames_to_bytes); +MAKE_TYPEDEF(snd_pcm_get_params); +/* snd_pcm_hw_params_alloca is actually a macro */ @@ -183,7 +185,7 @@ /* ALSA is not thread-safe. snd_pcm_t instances are individually protected by the owning cubeb_stream's mutex. snd_pcm_t creation and destruction is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1), -@@ -64,6 +109,8 @@ struct cubeb { +@@ -64,6 +112,8 @@ workaround is not required. */ snd_config_t * local_config; int is_pa; @@ -192,30 +194,62 @@ }; enum stream_state { -@@ -262,7 +309,7 @@ alsa_refill_stream(cubeb_stream * stm) +@@ -257,32 +307,35 @@ + long got; + void * p; + int draining; ++ unsigned pipefailures, againfailures; + draining = 0; + pthread_mutex_lock(&stm->mutex); - r = snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents); -+ r = WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents); - if (r < 0 || revents != POLLOUT) { - /* This should be a stream error; it makes no sense for poll(2) to wake - for this stream and then have the stream report that it's not ready. -@@ -271,10 +318,10 @@ alsa_refill_stream(cubeb_stream * stm) - return RUNNING; - } - +- if (r < 0 || revents != POLLOUT) { +- /* This should be a stream error; it makes no sense for poll(2) to wake +- for this stream and then have the stream report that it's not ready. +- Unfortunately, this does happen, so just bail out and try again. */ +- pthread_mutex_unlock(&stm->mutex); +- return RUNNING; +- } +- - avail = snd_pcm_avail_update(stm->pcm); -+ avail = WRAP(snd_pcm_avail_update)(stm->pcm); - if (avail == -EPIPE) { +- if (avail == -EPIPE) { - snd_pcm_recover(stm->pcm, avail, 1); - avail = snd_pcm_avail_update(stm->pcm); +- } ++ for (pipefailures = 0;;) { ++ r = WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents); ++ if (r < 0 || revents != POLLOUT || ++ (avail = WRAP(snd_pcm_avail_update)(stm->pcm)) == 0) { ++ /* This should be a stream error; it makes no sense for poll(2) to wake ++ for this stream and then have the stream report that it's not ready. ++ Unfortunately, this does happen, so just bail out and try again. */ ++ pthread_mutex_unlock(&stm->mutex); ++ return RUNNING; ++ } + +- /* Failed to recover from an xrun, this stream must be broken. */ +- if (avail < 0) { +- pthread_mutex_unlock(&stm->mutex); +- stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); +- return ERROR; ++ if (avail > 0) ++ break; ++ if (pipefailures++ > 11) { ++ fprintf(stderr, "%s: repeated failures from snd_pcm_avail_update, " ++ "giving up\n", __func__); ++ pthread_mutex_unlock(&stm->mutex); ++ stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); ++ return ERROR; ++ } + WRAP(snd_pcm_recover)(stm->pcm, avail, 1); -+ avail = WRAP(snd_pcm_avail_update)(stm->pcm); } ++ pipefailures = againfailures = 0; - /* Failed to recover from an xrun, this stream must be broken. */ -@@ -293,8 +340,8 @@ alsa_refill_stream(cubeb_stream * stm) + /* This should never happen. */ + if ((unsigned int) avail > stm->buffer_size) { +@@ -293,8 +346,8 @@ available to write. If avail is still zero here, the stream must be in a funky state, so recover and try again. */ if (avail == 0) { @@ -226,7 +260,7 @@ if (avail <= 0) { pthread_mutex_unlock(&stm->mutex); stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); -@@ -302,7 +349,7 @@ alsa_refill_stream(cubeb_stream * stm) +@@ -302,7 +355,7 @@ } } @@ -235,21 +269,91 @@ assert(p); pthread_mutex_unlock(&stm->mutex); -@@ -327,10 +374,10 @@ alsa_refill_stream(cubeb_stream * stm) +@@ -311,10 +364,11 @@ + if (got < 0) { + pthread_mutex_unlock(&stm->mutex); + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); ++ free(p); + return ERROR; + } + if (got > 0) { +- snd_pcm_sframes_t wrote; ++ snd_pcm_sframes_t wrote, towrite = got; + + if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) { + float * b = (float *) p; +@@ -327,14 +381,62 @@ b[i] *= stm->volume; } } - wrote = snd_pcm_writei(stm->pcm, p, got); -+ wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got); - if (wrote == -EPIPE) { +- if (wrote == -EPIPE) { - snd_pcm_recover(stm->pcm, wrote, 1); - wrote = snd_pcm_writei(stm->pcm, p, got); -+ WRAP(snd_pcm_recover)(stm->pcm, wrote, 1); -+ wrote = WRAP(snd_pcm_writei)(stm->pcm, p, got); - } - assert(wrote >= 0 && wrote == got); - stm->write_position += wrote; -@@ -342,7 +389,7 @@ alsa_refill_stream(cubeb_stream * stm) +- } +- assert(wrote >= 0 && wrote == got); +- stm->write_position += wrote; +- gettimeofday(&stm->last_activity, NULL); ++ for (;;) { ++ wrote = WRAP(snd_pcm_writei)(stm->pcm, p, ++ towrite > avail ? avail : towrite); ++ switch(wrote) { ++ case -EPIPE: ++ if (pipefailures++ > 3) { ++ fprintf(stderr, "%s: Too many underflows, giving up\n", __func__); ++ stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); ++ pthread_mutex_unlock(&stm->mutex); ++ free(p); ++ return ERROR; ++ } ++ WRAP(snd_pcm_recover)(stm->pcm, wrote, 1); ++ continue; ++ case -EAGAIN: ++ if (againfailures++ > 3) { ++ fprintf(stderr, "%s: Too many -EAGAIN errors from snd_pcm_writei, " ++ "giving up\n", __func__); ++ stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); ++ pthread_mutex_unlock(&stm->mutex); ++ free(p); ++ return ERROR; ++ } ++ continue; ++ case -EBADFD: ++ fprintf(stderr, "%s: snc_pcm_writei returned -%s, giving up\n", ++ __func__, "EBADFD"); ++ free(p); ++ stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); ++ pthread_mutex_unlock(&stm->mutex); ++ return ERROR; ++ } ++ if (wrote < 0) { ++ fprintf(stderr, "%s: snc_pcm_writei returned unexpected error %lld, " ++ "giving up\n", __func__, (long long)wrote); ++ free(p); ++ stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); ++ pthread_mutex_unlock(&stm->mutex); ++ return ERROR; ++ } ++ pipefailures = againfailures = 0; ++ stm->write_position += wrote; ++ gettimeofday(&stm->last_activity, NULL); ++ if (wrote > towrite) { ++ fprintf(stderr, "%s: snc_pcm_writei wrote %lld frames, which was more " ++ "than we requested (%lld). This should not happen, giving up\n", ++ __func__, (long long)wrote, (long long)towrite); ++ free(p); ++ stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); ++ pthread_mutex_unlock(&stm->mutex); ++ return ERROR; ++ } ++ if (towrite == wrote) ++ break; ++ towrite -= wrote; ++ } + } + if (got != avail) { + long buffer_fill = stm->buffer_size - (avail - got); +@@ -342,7 +444,7 @@ /* Fill the remaining buffer with silence to guarantee one full period has been written. */ @@ -258,7 +362,7 @@ set_timeout(&stm->drain_timeout, buffer_time * 1000); -@@ -453,26 +500,26 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm) +@@ -453,26 +555,26 @@ slave_def = NULL; @@ -290,7 +394,7 @@ if (r < 0) { break; } -@@ -481,7 +528,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm) +@@ -481,7 +583,7 @@ if (r < 0 || r > (int) sizeof(node_name)) { break; } @@ -299,7 +403,7 @@ if (r < 0) { break; } -@@ -490,7 +537,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm) +@@ -490,7 +592,7 @@ } while (0); if (slave_def) { @@ -308,7 +412,7 @@ } return NULL; -@@ -513,22 +560,22 @@ init_local_config_with_workaround(char const * pcm_name) +@@ -513,22 +615,22 @@ lconf = NULL; @@ -335,7 +439,7 @@ if (r < 0) { break; } -@@ -537,7 +584,7 @@ init_local_config_with_workaround(char const * pcm_name) +@@ -537,7 +639,7 @@ if (r < 0 || r > (int) sizeof(node_name)) { break; } @@ -344,7 +448,7 @@ if (r < 0) { break; } -@@ -548,12 +595,12 @@ init_local_config_with_workaround(char const * pcm_name) +@@ -548,12 +650,12 @@ } /* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */ @@ -359,7 +463,7 @@ if (r < 0) { break; } -@@ -564,18 +611,18 @@ init_local_config_with_workaround(char const * pcm_name) +@@ -564,18 +666,18 @@ /* Don't clobber an explicit existing handle_underrun value, set it only if it doesn't already exist. */ @@ -381,7 +485,7 @@ if (r < 0) { break; } -@@ -583,7 +630,7 @@ init_local_config_with_workaround(char const * pcm_name) +@@ -583,7 +685,7 @@ return lconf; } while (0); @@ -390,7 +494,7 @@ return NULL; } -@@ -595,9 +642,9 @@ alsa_locked_pcm_open(snd_pcm_t ** pcm, snd_pcm_stream_t stream, snd_config_t * l +@@ -595,9 +697,9 @@ pthread_mutex_lock(&cubeb_alsa_mutex); if (local_config) { @@ -402,7 +506,7 @@ } pthread_mutex_unlock(&cubeb_alsa_mutex); -@@ -610,7 +657,7 @@ alsa_locked_pcm_close(snd_pcm_t * pcm) +@@ -610,7 +712,7 @@ int r; pthread_mutex_lock(&cubeb_alsa_mutex); @@ -411,7 +515,7 @@ pthread_mutex_unlock(&cubeb_alsa_mutex); return r; -@@ -667,12 +714,65 @@ alsa_init(cubeb ** context, char const * context_name) +@@ -667,12 +769,65 @@ pthread_attr_t attr; snd_pcm_t * dummy; @@ -478,7 +582,7 @@ cubeb_alsa_error_handler_set = 1; } pthread_mutex_unlock(&cubeb_alsa_mutex); -@@ -680,6 +780,8 @@ alsa_init(cubeb ** context, char const * context_name) +@@ -680,6 +835,8 @@ ctx = calloc(1, sizeof(*ctx)); assert(ctx); @@ -487,7 +591,7 @@ ctx->ops = &alsa_ops; r = pthread_mutex_init(&ctx->mutex, NULL); -@@ -729,7 +831,7 @@ alsa_init(cubeb ** context, char const * context_name) +@@ -729,7 +886,7 @@ config fails with EINVAL, the PA PCM is too old for this workaround. */ if (r == -EINVAL) { pthread_mutex_lock(&cubeb_alsa_mutex); @@ -496,7 +600,7 @@ pthread_mutex_unlock(&cubeb_alsa_mutex); ctx->local_config = NULL; } else if (r >= 0) { -@@ -768,9 +870,13 @@ alsa_destroy(cubeb * ctx) +@@ -768,9 +925,13 @@ pthread_mutex_destroy(&ctx->mutex); free(ctx->fds); @@ -511,7 +615,7 @@ pthread_mutex_unlock(&cubeb_alsa_mutex); } -@@ -838,7 +944,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, +@@ -838,7 +999,7 @@ return CUBEB_ERROR; } @@ -520,7 +624,7 @@ assert(r == 0); /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't -@@ -848,23 +954,23 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, +@@ -848,23 +1009,23 @@ latency = latency < 500 ? 500 : latency; } @@ -550,7 +654,7 @@ assert((nfds_t) r == stm->nfds); r = pthread_cond_init(&stm->cond, NULL); -@@ -895,7 +1001,7 @@ alsa_stream_destroy(cubeb_stream * stm) +@@ -895,7 +1056,7 @@ pthread_mutex_lock(&stm->mutex); if (stm->pcm) { if (stm->state == DRAINING) { @@ -559,7 +663,7 @@ } alsa_locked_pcm_close(stm->pcm); stm->pcm = NULL; -@@ -937,12 +1043,12 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) +@@ -937,12 +1098,12 @@ return CUBEB_ERROR; } @@ -574,7 +678,7 @@ if (rv < 0) { return CUBEB_ERROR; } -@@ -962,34 +1068,34 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { +@@ -962,34 +1123,34 @@ /* get a pcm, disabling resampling, so we get a rate the * hardware/dmix/pulse/etc. supports. */ @@ -617,7 +721,7 @@ return CUBEB_OK; } -@@ -1013,7 +1119,7 @@ alsa_stream_start(cubeb_stream * stm) +@@ -1013,7 +1174,7 @@ ctx = stm->context; pthread_mutex_lock(&stm->mutex); @@ -626,7 +730,7 @@ gettimeofday(&stm->last_activity, NULL); pthread_mutex_unlock(&stm->mutex); -@@ -1047,7 +1153,7 @@ alsa_stream_stop(cubeb_stream * stm) +@@ -1047,7 +1208,7 @@ pthread_mutex_unlock(&ctx->mutex); pthread_mutex_lock(&stm->mutex); @@ -635,7 +739,7 @@ pthread_mutex_unlock(&stm->mutex); return CUBEB_OK; -@@ -1063,8 +1169,8 @@ alsa_stream_get_position(cubeb_stream * stm, uint64_t * position) +@@ -1063,14 +1224,17 @@ pthread_mutex_lock(&stm->mutex); delay = -1; @@ -646,7 +750,17 @@ *position = stm->last_position; pthread_mutex_unlock(&stm->mutex); return CUBEB_OK; -@@ -1089,7 +1195,7 @@ alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency) + } + +- assert(delay >= 0); ++ if (delay < 0) { ++ WRAP(snd_pcm_forward)(stm->pcm, -delay); ++ delay = 0; ++ } + + *position = 0; + if (stm->write_position >= (snd_pcm_uframes_t) delay) { +@@ -1089,7 +1253,7 @@ snd_pcm_sframes_t delay; /* This function returns the delay in frames until a frame written using snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */