mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
alsa: handle target better
Rework how we handle the current device fill level and our desired fill level so that we handle it more consistently.
This commit is contained in:
parent
88299ab225
commit
4a4fa57efc
3 changed files with 66 additions and 64 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit dde5a0dee8c4806d4660e4b73be84e4aef55c824
|
Subproject commit 2abcc287627568a88424782c05ff527b5817ba56
|
||||||
|
|
@ -584,7 +584,7 @@ static int alsa_recover(struct state *state, int err)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_status(struct state *state, snd_pcm_uframes_t *delay)
|
static int get_status(struct state *state, snd_pcm_uframes_t *delay, snd_pcm_uframes_t *target)
|
||||||
{
|
{
|
||||||
snd_pcm_sframes_t avail;
|
snd_pcm_sframes_t avail;
|
||||||
int res;
|
int res;
|
||||||
|
|
@ -596,34 +596,41 @@ static int get_status(struct state *state, snd_pcm_uframes_t *delay)
|
||||||
return avail;
|
return avail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delay) {
|
|
||||||
if (state->stream == SND_PCM_STREAM_PLAYBACK)
|
|
||||||
*delay = state->buffer_frames - avail;
|
|
||||||
else
|
|
||||||
*delay = avail;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t delay, bool slave)
|
*target = state->last_threshold;
|
||||||
{
|
|
||||||
double err, corr;
|
|
||||||
|
|
||||||
if (state->rate_match) {
|
if (state->rate_match) {
|
||||||
state->delay = state->rate_match->delay;
|
state->delay = state->rate_match->delay;
|
||||||
state->read_size = state->rate_match->size;
|
state->read_size = state->rate_match->size;
|
||||||
/* We try to compensate for the latency introduced by rate matching
|
/* We try to compensate for the latency introduced by rate matching
|
||||||
* by moving a little closer to the device read/write pointers.
|
* by moving a little closer to the device read/write pointers.
|
||||||
* Don't try to get closer than 32 samples but instead increase the
|
* Don't try to get closer than 48 samples but instead increase the
|
||||||
* reported latency on the port (TODO). */
|
* reported latency on the port (TODO). */
|
||||||
if ((int)state->last_threshold <= state->delay + 32)
|
if (*target <= state->delay + 48)
|
||||||
state->delay = SPA_MAX(0, (int)state->last_threshold + 32 - state->delay);
|
state->delay = SPA_MAX(0, (int)(*target - 48 - state->delay));
|
||||||
|
*target -= state->delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state->stream == SND_PCM_STREAM_PLAYBACK) {
|
||||||
|
*delay = state->buffer_frames - avail;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*delay = avail;
|
||||||
|
*target = SPA_MAX(*target, state->read_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t delay,
|
||||||
|
snd_pcm_sframes_t target, bool slave)
|
||||||
|
{
|
||||||
|
double err, corr;
|
||||||
|
|
||||||
if (state->stream == SND_PCM_STREAM_PLAYBACK)
|
if (state->stream == SND_PCM_STREAM_PLAYBACK)
|
||||||
err = delay - state->last_threshold + state->delay;
|
err = delay - target;
|
||||||
else
|
else
|
||||||
err = state->last_threshold - delay + state->delay * 2;
|
err = (target + 128) - delay;
|
||||||
|
|
||||||
if (state->bw == 0.0) {
|
if (state->bw == 0.0) {
|
||||||
set_loop(state, BW_MAX);
|
set_loop(state, BW_MAX);
|
||||||
|
|
@ -650,8 +657,8 @@ static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t del
|
||||||
else if (state->bw == BW_MED)
|
else if (state->bw == BW_MED)
|
||||||
set_loop(state, BW_MIN);
|
set_loop(state, BW_MIN);
|
||||||
|
|
||||||
spa_log_debug(state->log, "slave:%d rate:%f bw:%f del:%d thr:%d err:%f (%f %f %f)",
|
spa_log_debug(state->log, "slave:%d rate:%f bw:%f del:%d target:%ld err:%f (%f %f %f)",
|
||||||
slave, corr, state->bw, state->delay, state->threshold,
|
slave, corr, state->bw, state->delay, target,
|
||||||
err, state->z1, state->z2, state->z3);
|
err, state->z1, state->z2, state->z3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -696,29 +703,29 @@ int spa_alsa_write(struct state *state, snd_pcm_uframes_t silence)
|
||||||
|
|
||||||
if (state->slaved && state->alsa_started) {
|
if (state->slaved && state->alsa_started) {
|
||||||
uint64_t nsec;
|
uint64_t nsec;
|
||||||
snd_pcm_uframes_t delay;
|
snd_pcm_uframes_t delay, target;
|
||||||
|
|
||||||
if ((res = get_status(state, &delay)) < 0)
|
if ((res = get_status(state, &delay, &target)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
if (delay > state->threshold * 2) {
|
if (delay > target + state->threshold) {
|
||||||
spa_log_warn(state->log, "slave delay:%ld resync %f %f %f", delay,
|
spa_log_warn(state->log, "slave delay:%ld resync %f %f %f", delay,
|
||||||
state->z1, state->z2, state->z3);
|
state->z1, state->z2, state->z3);
|
||||||
init_loop(state);
|
init_loop(state);
|
||||||
state->alsa_sync = true;
|
state->alsa_sync = true;
|
||||||
}
|
}
|
||||||
if (state->alsa_sync) {
|
if (state->alsa_sync) {
|
||||||
if (delay > state->threshold)
|
if (delay > target)
|
||||||
snd_pcm_rewind(state->hndl, delay - state->threshold);
|
snd_pcm_rewind(state->hndl, delay - target);
|
||||||
else
|
else
|
||||||
snd_pcm_forward(state->hndl, state->threshold - delay);
|
snd_pcm_forward(state->hndl, target - delay);
|
||||||
|
|
||||||
delay = state->threshold;
|
delay = target;
|
||||||
state->alsa_sync = false;
|
state->alsa_sync = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsec = state->position->clock.nsec;
|
nsec = state->position->clock.nsec;
|
||||||
if ((res = update_time(state, nsec, delay, true)) < 0)
|
if ((res = update_time(state, nsec, delay, target, true)) < 0)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -906,33 +913,31 @@ int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence)
|
||||||
|
|
||||||
if (state->slaved && state->alsa_started) {
|
if (state->slaved && state->alsa_started) {
|
||||||
uint64_t nsec;
|
uint64_t nsec;
|
||||||
snd_pcm_uframes_t delay;
|
snd_pcm_uframes_t delay, target;
|
||||||
uint32_t threshold = state->threshold;
|
uint32_t threshold = state->threshold;
|
||||||
|
|
||||||
if ((res = get_status(state, &delay)) < 0)
|
if ((res = get_status(state, &delay, &target)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
silence = delay;
|
if (delay < target) {
|
||||||
|
|
||||||
if (delay < threshold) {
|
|
||||||
spa_log_warn(state->log, "slave delay:%lu resync %f %f %f", delay,
|
spa_log_warn(state->log, "slave delay:%lu resync %f %f %f", delay,
|
||||||
state->z1, state->z2, state->z3);
|
state->z1, state->z2, state->z3);
|
||||||
init_loop(state);
|
init_loop(state);
|
||||||
state->alsa_sync = true;
|
state->alsa_sync = true;
|
||||||
}
|
}
|
||||||
if (state->alsa_sync) {
|
if (state->alsa_sync) {
|
||||||
spa_log_warn(state->log, "slave resync %ld %d", delay, threshold);
|
spa_log_warn(state->log, "slave resync %ld %d %ld", delay, threshold, target);
|
||||||
if (delay < threshold)
|
if (delay < target)
|
||||||
snd_pcm_rewind(state->hndl, threshold - delay);
|
snd_pcm_rewind(state->hndl, target - delay);
|
||||||
else if (delay > threshold)
|
else if (delay > target)
|
||||||
snd_pcm_forward(state->hndl, delay - threshold);
|
snd_pcm_forward(state->hndl, delay - target);
|
||||||
|
|
||||||
delay = threshold;
|
delay = target;
|
||||||
state->alsa_sync = false;
|
state->alsa_sync = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsec = state->position->clock.nsec;
|
nsec = state->position->clock.nsec;
|
||||||
if ((res = update_time(state, nsec, delay, true)) < 0)
|
if ((res = update_time(state, nsec, delay, target, true)) < 0)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -940,8 +945,6 @@ int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence)
|
||||||
if (frames == 0)
|
if (frames == 0)
|
||||||
frames = state->threshold + state->delay;
|
frames = state->threshold + state->delay;
|
||||||
|
|
||||||
frames = SPA_MIN(frames, silence);
|
|
||||||
|
|
||||||
to_read = state->buffer_frames;
|
to_read = state->buffer_frames;
|
||||||
if ((res = snd_pcm_mmap_begin(hndl, &my_areas, &offset, &to_read)) < 0) {
|
if ((res = snd_pcm_mmap_begin(hndl, &my_areas, &offset, &to_read)) < 0) {
|
||||||
spa_log_error(state->log, "snd_pcm_mmap_begin error: %s", snd_strerror(res));
|
spa_log_error(state->log, "snd_pcm_mmap_begin error: %s", snd_strerror(res));
|
||||||
|
|
@ -952,7 +955,6 @@ int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence)
|
||||||
|
|
||||||
read = push_frames(state, my_areas, offset, frames, state->delay);
|
read = push_frames(state, my_areas, offset, frames, state->delay);
|
||||||
|
|
||||||
|
|
||||||
spa_log_trace_fp(state->log, "commit %ld %ld %"PRIi64, offset, read, state->sample_count);
|
spa_log_trace_fp(state->log, "commit %ld %ld %"PRIi64, offset, read, state->sample_count);
|
||||||
total_read += read;
|
total_read += read;
|
||||||
|
|
||||||
|
|
@ -967,18 +969,18 @@ int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_play(struct state *state, uint64_t nsec, snd_pcm_uframes_t delay)
|
static int handle_play(struct state *state, uint64_t nsec,
|
||||||
|
snd_pcm_uframes_t delay, snd_pcm_uframes_t target)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
uint32_t threshold = state->last_threshold + state->delay;
|
|
||||||
|
|
||||||
if (delay >= threshold + state->last_threshold) {
|
if (delay > target + state->last_threshold) {
|
||||||
spa_log_trace(state->log, "early wakeup %ld %d", delay, threshold);
|
spa_log_trace(state->log, "early wakeup %ld %ld", delay, target);
|
||||||
state->next_time = nsec + (delay - threshold) * SPA_NSEC_PER_SEC / state->rate;
|
state->next_time = nsec + (delay - target) * SPA_NSEC_PER_SEC / state->rate;
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((res = update_time(state, nsec, delay, false)) < 0)
|
if ((res = update_time(state, nsec, delay, target, false)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
if (spa_list_is_empty(&state->ready)) {
|
if (spa_list_is_empty(&state->ready)) {
|
||||||
|
|
@ -996,23 +998,23 @@ static int handle_play(struct state *state, uint64_t nsec, snd_pcm_uframes_t del
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_capture(struct state *state, uint64_t nsec, snd_pcm_uframes_t delay)
|
static int handle_capture(struct state *state, uint64_t nsec,
|
||||||
|
snd_pcm_uframes_t delay, snd_pcm_uframes_t target)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
struct spa_io_buffers *io;
|
struct spa_io_buffers *io;
|
||||||
uint32_t threshold = state->last_threshold + state->delay * 2;
|
|
||||||
|
|
||||||
if (delay < threshold) {
|
if (delay < target) {
|
||||||
spa_log_trace(state->log, "early wakeup %ld %d", delay, threshold);
|
spa_log_trace(state->log, "early wakeup %ld %ld", delay, target);
|
||||||
state->next_time = nsec + (threshold - delay) * SPA_NSEC_PER_SEC /
|
state->next_time = nsec + (target - delay) * SPA_NSEC_PER_SEC /
|
||||||
state->rate;
|
state->rate;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((res = update_time(state, nsec, delay, false)) < 0)
|
if ((res = update_time(state, nsec, delay, target, false)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
if ((res = spa_alsa_read(state, delay)) < 0)
|
if ((res = spa_alsa_read(state, target)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
if (!spa_list_is_empty(&state->ready)) {
|
if (!spa_list_is_empty(&state->ready)) {
|
||||||
|
|
@ -1032,7 +1034,7 @@ static int handle_capture(struct state *state, uint64_t nsec, snd_pcm_uframes_t
|
||||||
static void alsa_on_timeout_event(struct spa_source *source)
|
static void alsa_on_timeout_event(struct spa_source *source)
|
||||||
{
|
{
|
||||||
struct state *state = source->data;
|
struct state *state = source->data;
|
||||||
snd_pcm_uframes_t delay;
|
snd_pcm_uframes_t delay, target;
|
||||||
uint64_t nsec;
|
uint64_t nsec;
|
||||||
uint64_t expire;
|
uint64_t expire;
|
||||||
int res;
|
int res;
|
||||||
|
|
@ -1046,18 +1048,18 @@ static void alsa_on_timeout_event(struct spa_source *source)
|
||||||
}
|
}
|
||||||
|
|
||||||
spa_system_clock_gettime(state->data_system, CLOCK_MONOTONIC, &state->now);
|
spa_system_clock_gettime(state->data_system, CLOCK_MONOTONIC, &state->now);
|
||||||
if ((res = get_status(state, &delay)) < 0)
|
if ((res = get_status(state, &delay, &target)) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nsec = SPA_TIMESPEC_TO_NSEC(&state->now);
|
nsec = SPA_TIMESPEC_TO_NSEC(&state->now);
|
||||||
spa_log_trace_fp(state->log, "timeout %lu %"PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64,
|
spa_log_trace_fp(state->log, "timeout %lu %lu %"PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64,
|
||||||
delay, nsec, state->next_time, nsec - state->next_time,
|
delay, target, nsec, state->next_time, nsec - state->next_time,
|
||||||
state->threshold, state->sample_count);
|
state->threshold, state->sample_count);
|
||||||
|
|
||||||
if (state->stream == SND_PCM_STREAM_PLAYBACK)
|
if (state->stream == SND_PCM_STREAM_PLAYBACK)
|
||||||
handle_play(state, nsec, delay);
|
handle_play(state, nsec, delay, target);
|
||||||
else
|
else
|
||||||
handle_capture(state, nsec, delay);
|
handle_capture(state, nsec, delay, target);
|
||||||
|
|
||||||
set_timeout(state, state->next_time);
|
set_timeout(state, state->next_time);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,8 @@ struct state {
|
||||||
int channels;
|
int channels;
|
||||||
size_t frame_size;
|
size_t frame_size;
|
||||||
int rate_denom;
|
int rate_denom;
|
||||||
int delay;
|
uint32_t delay;
|
||||||
int read_size;
|
uint32_t read_size;
|
||||||
|
|
||||||
uint64_t port_info_all;
|
uint64_t port_info_all;
|
||||||
struct spa_port_info port_info;
|
struct spa_port_info port_info;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue