mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
alsa: improve audio output to multiple devices
There are currently several issues when multiple alsa devices are involved. For alsa devices that are followers, all data is written via impl_node_process(). Currently spa_alsa_write() is only called if a new buffer was queued. It can happen that all buffers are in the ready list (queued by previous calls but not yet written because there was no free space in the kernel ring buffer). In this case writing stalls indefinitely. To fix this, also call spa_alsa_write() if no new buffer is queued but there are still buffers in the ready list. If the ready list of the primary device is not empty then only this device is handled because spa_alsa_write() is called directly. The other devices make no progress during this interval. The clock drift calculation works by comparing the alsa delay with the expected delay since the last wakeup. This only work if the alsa ringbuffer was filled completly. If the ready list contains a partial buffer then the ringbuffer is not filled and the timing calculation during the next wakeup is incorrect. To fix all this, remove the special case for the non-empty ready list and just call spa_node_call_ready() every time.
This commit is contained in:
parent
ad5ac964af
commit
e808875d60
2 changed files with 10 additions and 12 deletions
|
|
@ -820,6 +820,11 @@ static int impl_node_process(void *object)
|
||||||
|
|
||||||
io->status = SPA_STATUS_OK;
|
io->status = SPA_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
else if (!spa_list_is_empty(&this->ready)) {
|
||||||
|
spa_alsa_write(this);
|
||||||
|
|
||||||
|
io->status = SPA_STATUS_OK;
|
||||||
|
}
|
||||||
return SPA_STATUS_HAVE_DATA;
|
return SPA_STATUS_HAVE_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2574,6 +2574,7 @@ int spa_alsa_skip(struct state *state)
|
||||||
static int handle_play(struct state *state, uint64_t current_time, snd_pcm_uframes_t avail,
|
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)
|
snd_pcm_uframes_t delay, snd_pcm_uframes_t target)
|
||||||
{
|
{
|
||||||
|
struct spa_io_buffers *io = state->io;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
if (state->alsa_started && SPA_UNLIKELY(delay > target + state->max_error)) {
|
if (state->alsa_started && SPA_UNLIKELY(delay > target + state->max_error)) {
|
||||||
|
|
@ -2588,20 +2589,12 @@ static int handle_play(struct state *state, uint64_t current_time, snd_pcm_ufram
|
||||||
if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, false)) < 0))
|
if (SPA_UNLIKELY((res = update_time(state, current_time, delay, target, false)) < 0))
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
if (spa_list_is_empty(&state->ready)) {
|
spa_log_trace_fp(state->log, "%p: %d", state, io->status);
|
||||||
struct spa_io_buffers *io = state->io;
|
|
||||||
|
|
||||||
spa_log_trace_fp(state->log, "%p: %d", state, io->status);
|
update_sources(state, false);
|
||||||
|
|
||||||
update_sources(state, false);
|
io->status = SPA_STATUS_NEED_DATA;
|
||||||
|
return spa_node_call_ready(&state->callbacks, SPA_STATUS_NEED_DATA);
|
||||||
io->status = SPA_STATUS_NEED_DATA;
|
|
||||||
res = spa_node_call_ready(&state->callbacks, SPA_STATUS_NEED_DATA);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res = spa_alsa_write(state);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_capture(struct state *state, uint64_t current_time, snd_pcm_uframes_t avail,
|
static int handle_capture(struct state *state, uint64_t current_time, snd_pcm_uframes_t avail,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue