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:
+89
-102
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user