Use snd_pcm_recover only _after_ an XRUN or suspend has happened. Don't use it to remove one -- it doesn't work.

This commit is contained in:
Mike Brady
2019-01-04 13:50:14 +00:00
parent 4610d0870d
commit 459dd51a31
+89 -102
View File
@@ -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 ((*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);
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;
// 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 (((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) {
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);
}
}
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
*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
// 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)) {
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);
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
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 (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
// 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;
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;
}
}
// 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…
frame_index++;
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;
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;
}
const uint64_t start_measurement_from_this_frame =
(2 * 44100) / 352; // two seconds of frames…
frame_index++;
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;
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 {