From: Mike Brady Date: Fri, 4 Jan 2019 13:50:14 +0000 (+0000) Subject: Use snd_pcm_recover only _after_ an XRUN or suspend has happened. Don't use it to... X-Git-Tag: 3.3RC0~66^2~95 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=459dd51a31fa088774cbbdb3466147d72c37d32e;p=thirdparty%2Fshairport-sync.git Use snd_pcm_recover only _after_ an XRUN or suspend has happened. Don't use it to remove one -- it doesn't work. --- diff --git a/audio_alsa.c b/audio_alsa.c index 0528c270..20c30bd1 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -1020,82 +1020,51 @@ int delay_prep_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay) { if (ret == 0) { *state = snd_pcm_status_get_state(alsa_snd_pcm_status); - if ((*state == SND_PCM_STATE_SUSPENDED) || (*state == SND_PCM_STATE_XRUN)) { - debug(1, "alsa: recovering from SND_PCM_STATE_* %d in delay_prep_and_status.", *state); - ret = snd_pcm_recover(alsa_handle, ret, 1); - if (ret == 0) { - ret = snd_pcm_status(alsa_handle, alsa_snd_pcm_status); - if (ret == 0) - *state = snd_pcm_status_get_state(alsa_snd_pcm_status); - if (*state != SND_PCM_STATE_PREPARED) { - debug(1, "alsa: snd_pcm_recover failed -- have to use snd_pcm_prepare."); - ret = snd_pcm_prepare(alsa_handle); - if (ret == 0) - ret = snd_pcm_recover(alsa_handle, ret, 1); - if (ret == 0) { - ret = snd_pcm_status(alsa_handle, alsa_snd_pcm_status); - if (ret == 0) - *state = snd_pcm_status_get_state(alsa_snd_pcm_status); - } - } - } - } - - // here, the device must be either SND_PCM_STATE_RUNNING - // or SND_PCM_STATE_PREPARED or SND_PCM_STATE_DRAINING - - if (ret == 0) { - - if ((*state == SND_PCM_STATE_RUNNING) || (*state == SND_PCM_STATE_DRAINING)) { + if ((*state == SND_PCM_STATE_RUNNING) || (*state == SND_PCM_STATE_DRAINING)) { - snd_pcm_status_get_htstamp(alsa_snd_pcm_status, &update_timestamp); - *delay = snd_pcm_status_get_delay(alsa_snd_pcm_status); + snd_pcm_status_get_htstamp(alsa_snd_pcm_status, &update_timestamp); + *delay = snd_pcm_status_get_delay(alsa_snd_pcm_status); - if (*state == SND_PCM_STATE_DRAINING) - debug(1, "alsa: draining with a delay of %d.", delay); + if (*state == SND_PCM_STATE_DRAINING) + debug(1, "alsa: draining with a delay of %d.", delay); - clock_gettime(CLOCK_MONOTONIC, &tn); - uint64_t time_now_ns = tn.tv_sec * (uint64_t)1000000000 + tn.tv_nsec; - uint64_t update_timestamp_ns = - update_timestamp.tv_sec * (uint64_t)1000000000 + update_timestamp.tv_nsec; + clock_gettime(CLOCK_MONOTONIC, &tn); + uint64_t time_now_ns = tn.tv_sec * (uint64_t)1000000000 + tn.tv_nsec; + uint64_t update_timestamp_ns = + update_timestamp.tv_sec * (uint64_t)1000000000 + update_timestamp.tv_nsec; - // see if it's stalled + // see if it's stalled - if ((stall_monitor_start_time != 0) && (stall_monitor_frame_count == *delay)) { - // hasn't outputted anything since the last call to delay() + if ((stall_monitor_start_time != 0) && (stall_monitor_frame_count == *delay)) { + // hasn't outputted anything since the last call to delay() - if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) || - ((time_now_ns - stall_monitor_start_time) > stall_monitor_error_threshold)) { - ret = sps_extra_code_output_stalled; - } - } else { - stall_monitor_start_time = update_timestamp_ns; - stall_monitor_frame_count = *delay; + if (((update_timestamp_ns - stall_monitor_start_time) > stall_monitor_error_threshold) || + ((time_now_ns - stall_monitor_start_time) > stall_monitor_error_threshold)) { + ret = sps_extra_code_output_stalled; } + } else { + stall_monitor_start_time = update_timestamp_ns; + stall_monitor_frame_count = *delay; + } - if (ret == 0) { - uint64_t delta = time_now_ns - update_timestamp_ns; - - uint64_t frames_played_since_last_interrupt = - ((uint64_t)desired_sample_rate * delta) / 1000000000; - snd_pcm_sframes_t frames_played_since_last_interrupt_sized = - frames_played_since_last_interrupt; - - *delay = *delay - frames_played_since_last_interrupt_sized; - } - } else { // not running, thus no delay information, thus can't check for stall - stall_monitor_start_time = 0; // zero if not initialised / not started / zeroed by flush - stall_monitor_frame_count = 0; // set to delay at start of time, incremented by any writes + if (ret == 0) { + uint64_t delta = time_now_ns - update_timestamp_ns; - // not running, thus no delay information, thus can't check for frame rates - frame_index = 0; // we'll be starting over... - measurement_data_is_valid = 0; + uint64_t frames_played_since_last_interrupt = + ((uint64_t)desired_sample_rate * delta) / 1000000000; + snd_pcm_sframes_t frames_played_since_last_interrupt_sized = + frames_played_since_last_interrupt; - if ((*state != SND_PCM_STATE_PREPARED)) { - debug(1, "alsa: can't get device into valid state in delay. State is %d.", *state); - ret = sps_extra_code_output_state_cannot_make_ready; - } + *delay = *delay - frames_played_since_last_interrupt_sized; } + } else { // not running, thus no delay information, thus can't check for stall + *delay = 0; + stall_monitor_start_time = 0; // zero if not initialised / not started / zeroed by flush + stall_monitor_frame_count = 0; // set to delay at start of time, incremented by any writes + + // not running, thus no delay information, thus can't check for frame rates + frame_index = 0; // we'll be starting over... + measurement_data_is_valid = 0; } } return ret; @@ -1180,41 +1149,59 @@ int untimed_play(void *buf, int samples) { ret = delay_prep_and_status(&state, &my_delay); if (ret == 0) { // will be non-zero if an error or a stall - if ((state == SND_PCM_STATE_PREPARED) || (state == SND_PCM_STATE_RUNNING)) { - if ((samples != 0) && (buf != NULL)) { + if ((samples != 0) && (buf != NULL)) { - // debug(3, "write %d frames.", samples); - ret = alsa_pcm_write(alsa_handle, buf, samples); - if (ret == samples) { - stall_monitor_frame_count += samples; + // debug(3, "write %d frames.", samples); + ret = alsa_pcm_write(alsa_handle, buf, samples); + if (ret == samples) { + stall_monitor_frame_count += samples; - if (frame_index == 0) { - frames_sent_for_playing = samples; - } else { - frames_sent_for_playing += samples; - } + if (frame_index == 0) { + frames_sent_for_playing = samples; + } else { + frames_sent_for_playing += samples; + } - const uint64_t start_measurement_from_this_frame = - (2 * 44100) / 352; // two seconds of frames… + const uint64_t start_measurement_from_this_frame = + (2 * 44100) / 352; // two seconds of frames… - frame_index++; + frame_index++; - if ((frame_index == start_measurement_from_this_frame) || - ((frame_index > start_measurement_from_this_frame) && (frame_index % 32 == 0))) { + if ((frame_index == start_measurement_from_this_frame) || + ((frame_index > start_measurement_from_this_frame) && (frame_index % 32 == 0))) { - measurement_time = get_absolute_time_in_fp(); - frames_played_at_measurement_time = frames_sent_for_playing - my_delay - samples; + measurement_time = get_absolute_time_in_fp(); + frames_played_at_measurement_time = frames_sent_for_playing - my_delay - samples; - if (frame_index == start_measurement_from_this_frame) { - // debug(1, "Start frame counting"); - frames_played_at_measurement_start_time = frames_played_at_measurement_time; - measurement_start_time = measurement_time; - measurement_data_is_valid = 1; + if (frame_index == start_measurement_from_this_frame) { + // debug(1, "Start frame counting"); + frames_played_at_measurement_start_time = frames_played_at_measurement_time; + measurement_start_time = measurement_time; + measurement_data_is_valid = 1; + } + } + } else { + debug(1, "alsa: error %d writing %d samples to alsa device.", ret, samples); + if (ret == -EPIPE) { /* underrun */ + ret = snd_pcm_recover(alsa_handle, ret, debuglev > 0 ? 1 : 0); + if (ret < 0) { + ret = snd_pcm_prepare(alsa_handle); + if (ret < 0) + warn("alsa: can't recover from SND_PCM_STATE_XRUN, snd_pcm_recover() and " + "snd_pcm_prepare() failed: %s.", + snd_strerror(ret)); + } else if (ret == -ESTRPIPE) { /* suspended */ + while ((ret = snd_pcm_resume(alsa_handle)) == -EAGAIN) + sleep(1); /* wait until the suspend flag is released */ + if (ret < 0) { + ret = snd_pcm_prepare(alsa_handle); + if (ret < 0) + warn("alsa: can't recover from SND_PCM_STATE_SUSPENDED state, snd_pcm_prepare() " + "failed: %s.", + snd_strerror(ret)); } } - } else { - debug(1, "alsa: error writing to alsa device."); } } } else {