diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 606002a05..e63d4249a 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -1856,7 +1856,8 @@ recover: return do_start(state); } -static int get_avail(struct state *state, uint64_t current_time) +#if 1 +static int get_avail(struct state *state, uint64_t current_time, snd_pcm_uframes_t *delay) { int res, missed; snd_pcm_sframes_t avail; @@ -1874,11 +1875,12 @@ static int get_avail(struct state *state, uint64_t current_time) } else { state->alsa_recovering = false; } + *delay = avail; return avail; } -#if 0 -static int get_avail_htimestamp(struct state *state, uint64_t current_time) +#else +static int get_avail(struct state *state, uint64_t current_time, snd_pcm_uframes_t *delay) { int res, missed; snd_pcm_uframes_t avail; @@ -1886,7 +1888,7 @@ static int get_avail_htimestamp(struct state *state, uint64_t current_time) uint64_t then; if ((res = snd_pcm_htimestamp(state->hndl, &avail, &tstamp)) < 0) { - if ((res = alsa_recover(state, avail)) < 0) + if ((res = alsa_recover(state, res)) < 0) return res; if ((res = snd_pcm_htimestamp(state->hndl, &avail, &tstamp)) < 0) { if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) { @@ -1898,28 +1900,34 @@ static int get_avail_htimestamp(struct state *state, uint64_t current_time) } else { state->alsa_recovering = false; } + *delay = avail; if ((then = SPA_TIMESPEC_TO_NSEC(&tstamp)) != 0) { + int64_t diff; + if (then < current_time) - avail += (current_time - then) * state->rate / SPA_NSEC_PER_SEC; + diff = ((int64_t)(current_time - then)) * state->rate / SPA_NSEC_PER_SEC; else - avail -= (then - current_time) * state->rate / SPA_NSEC_PER_SEC; + diff = -((int64_t)(then - current_time)) * state->rate / SPA_NSEC_PER_SEC; + + spa_log_trace_fp(state->log, "%"PRIu64" %"PRIu64" %"PRIi64, current_time, then, diff); + + *delay += diff; } return SPA_MIN(avail, state->buffer_frames); } #endif -static int get_status(struct state *state, uint64_t current_time, +static int get_status(struct state *state, uint64_t current_time, snd_pcm_uframes_t *avail, snd_pcm_uframes_t *delay, snd_pcm_uframes_t *target) { - int avail; + int res; + snd_pcm_uframes_t a, d; - if ((avail = get_avail(state, current_time)) < 0) - return avail; + if ((res = get_avail(state, current_time, &d)) < 0) + return res; - avail = SPA_MIN(avail, (int)state->buffer_frames); - - *target = state->threshold + state->headroom; + a = SPA_MIN(res, (int)state->buffer_frames); if (state->resample && state->rate_match) { state->delay = state->rate_match->delay; @@ -1928,12 +1936,14 @@ static int get_status(struct state *state, uint64_t current_time, state->delay = 0; state->read_size = state->threshold; } - if (state->stream == SND_PCM_STREAM_PLAYBACK) { - *delay = state->buffer_frames - avail; + *avail = state->buffer_frames - a; + *delay = state->buffer_frames - SPA_MIN(d, state->buffer_frames); + *target = state->threshold + state->headroom; } else { - *delay = avail; - *target = SPA_MAX(*target, state->read_size + state->headroom); + *avail = a; + *delay = d; + *target = SPA_MAX(state->threshold, state->read_size) + state->headroom; } *target = SPA_CLAMP(*target, state->min_delay, state->max_delay); return 0; @@ -2105,11 +2115,11 @@ int spa_alsa_write(struct state *state) if (state->following && state->alsa_started) { uint64_t current_time; - snd_pcm_uframes_t delay, target; + snd_pcm_uframes_t avail, delay, target; current_time = state->position->clock.nsec; - if (SPA_UNLIKELY((res = get_status(state, current_time, &delay, &target)) < 0)) + if (SPA_UNLIKELY((res = get_status(state, current_time, &avail, &delay, &target)) < 0)) return res; if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, true)) < 0)) @@ -2124,16 +2134,17 @@ int spa_alsa_write(struct state *state) lev = SPA_LOG_LEVEL_INFO; if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) { - spa_log_lev(state->log, lev, "%s: follower delay:%ld target:%ld thr:%u, " - "resync (%d missed)", state->props.device, delay, + spa_log_lev(state->log, lev, "%s: follower avail:%lu delay:%ld " + "target:%ld thr:%u, resync (%d missed)", + state->props.device, avail, delay, target, state->threshold, missed); } - if (delay > target) - snd_pcm_rewind(state->hndl, delay - target); - else if (delay < target) - spa_alsa_silence(state, target - delay); - delay = target; + if (avail > target) + snd_pcm_rewind(state->hndl, avail - target); + else if (avail < target) + spa_alsa_silence(state, target - avail); + avail = target; state->alsa_sync = false; } else state->alsa_sync_warning = true; @@ -2344,11 +2355,9 @@ int spa_alsa_read(struct state *state) current_time = state->position->clock.nsec; - if ((res = get_status(state, current_time, &delay, &target)) < 0) + if ((res = get_status(state, current_time, &avail, &delay, &target)) < 0) return res; - avail = delay; - if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, true)) < 0)) return res; @@ -2366,11 +2375,11 @@ int spa_alsa_read(struct state *state) target, state->threshold, missed); } - if (delay < target) - max_read = target - delay; - else if (delay > target) - snd_pcm_forward(state->hndl, delay - target); - delay = target; + if (avail < target) + max_read = target - avail; + else if (avail > target) + snd_pcm_forward(state->hndl, avail - target); + avail = target; state->alsa_sync = false; } else state->alsa_sync_warning = true; @@ -2458,13 +2467,14 @@ int spa_alsa_skip(struct state *state) } -static int handle_play(struct state *state, uint64_t current_time, +static int handle_play(struct state *state, uint64_t current_time, snd_pcm_uframes_t avail, snd_pcm_uframes_t delay, snd_pcm_uframes_t target) { int res; if (state->alsa_started && SPA_UNLIKELY(delay > target + state->max_error)) { - spa_log_trace(state->log, "%p: early wakeup %lu %lu", state, delay, target); + spa_log_trace(state->log, "%p: early wakeup %ld %lu %lu", state, + avail, delay, target); if (delay > target * 3) delay = target * 3; state->next_time = current_time + (delay - target) * SPA_NSEC_PER_SEC / state->rate; @@ -2490,15 +2500,15 @@ static int handle_play(struct state *state, uint64_t current_time, return res; } -static int handle_capture(struct state *state, uint64_t current_time, +static int handle_capture(struct state *state, uint64_t current_time, snd_pcm_uframes_t avail, snd_pcm_uframes_t delay, snd_pcm_uframes_t target) { int res; struct spa_io_buffers *io; - if (SPA_UNLIKELY(delay < state->read_size)) { - spa_log_trace(state->log, "%p: early wakeup %ld %ld %d", state, delay, target, - state->read_size); + if (SPA_UNLIKELY(avail < state->read_size)) { + spa_log_trace(state->log, "%p: early wakeup %ld %ld %ld %d", state, + delay, avail, target, state->read_size); state->next_time = current_time + (target - delay) * SPA_NSEC_PER_SEC / state->rate; return -EAGAIN; @@ -2544,7 +2554,7 @@ static uint64_t get_time_ns(struct state *state) static void alsa_wakeup_event(struct spa_source *source) { struct state *state = source->data; - snd_pcm_uframes_t delay, target; + snd_pcm_uframes_t avail, delay, target; uint64_t expire, current_time; int res; @@ -2594,7 +2604,7 @@ static void alsa_wakeup_event(struct spa_source *source) return; } - if (SPA_UNLIKELY(get_status(state, current_time, &delay, &target) < 0)) { + if (SPA_UNLIKELY(get_status(state, current_time, &avail, &delay, &target) < 0)) { spa_log_error(state->log, "get_status error"); state->next_time += state->threshold * 1e9 / state->rate; goto done; @@ -2603,24 +2613,25 @@ static void alsa_wakeup_event(struct spa_source *source) #ifndef FASTPATH if (SPA_UNLIKELY(spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_TRACE))) { uint64_t nsec = get_time_ns(state); - spa_log_trace_fp(state->log, "%p: wakeup %lu %lu %"PRIu64" %"PRIu64" %"PRIi64 - " %d %"PRIi64, state, delay, target, nsec, nsec, + spa_log_trace_fp(state->log, "%p: wakeup %lu %lu %lu %"PRIu64" %"PRIu64" %"PRIi64 + " %d %"PRIi64, state, avail, delay, target, nsec, nsec, nsec - current_time, state->threshold, state->sample_count); } #endif if (state->stream == SND_PCM_STREAM_PLAYBACK) - handle_play(state, current_time, delay, target); + handle_play(state, current_time, avail, delay, target); else - handle_capture(state, current_time, delay, target); + handle_capture(state, current_time, avail, delay, target); done: if (!state->disable_tsched && (state->next_time > current_time + SPA_NSEC_PER_SEC || current_time > state->next_time + SPA_NSEC_PER_SEC)) { - spa_log_error(state->log, "%s: impossible timeout %lu %lu %"PRIu64" %"PRIu64" %"PRIi64 - " %d %"PRIi64, state->props.device, delay, target, current_time, state->next_time, - state->next_time - current_time, state->threshold, state->sample_count); + spa_log_error(state->log, "%s: impossible timeout %lu %lu %lu %"PRIu64" %"PRIu64" %"PRIi64 + " %d %"PRIi64, state->props.device, avail, delay, target, + current_time, state->next_time, state->next_time - current_time, + state->threshold, state->sample_count); state->next_time = current_time + state->threshold * 1e9 / state->rate; } set_timeout(state, state->next_time);