[client] audio: harden low-latency streaming

Rework audio provider and backend lifecycles so playback and capture
callbacks quiesce without blocking real-time threads. Move activation,
teardown, controls, retries, and diagnostics onto bounded workers.

Harden USB audio cadence, feedback, and capture recovery.
Preserve source clocks through recording and pace packets from the
device clock. Bound queues, waits, conversion buffers, and packet sizes.

Make PipeWire and PulseAudio stream control thread-safe and recoverable.
Correct latency clock domains, coalesce rate updates, preserve recent
capture under overload, and keep logging outside real-time callbacks.
This commit is contained in:
Geoffrey McRae
2026-08-10 16:36:12 +10:00
parent 4434985aa3
commit 87aa61510c
13 changed files with 4795 additions and 1217 deletions

View File

@@ -1728,7 +1728,7 @@ int spiceThread(void * arg)
DEBUG_WARN("USB audio requires a playback backend, using SPICE audio");
g_params.useSpiceUSBAudio = false;
}
else if (!(usbAudio = lgaUsb_create()))
else if (!(usbAudio = lgaUsb_create(g_params.audioDebug)))
{
DEBUG_WARN("Failed to initialize USB audio, using SPICE audio");
g_params.useSpiceUSBAudio = false;
@@ -1839,7 +1839,17 @@ int spiceThread(void * arg)
if (usbRedir && !lgUsbRedir_process(usbRedir))
DEBUG_WARN("Failed to process USB audio redirection");
if (usbAudio)
processTimeout = lgaUsb_recording(usbAudio) ? 1 : 10;
{
const uint64_t delay = lgaUsb_processDelayNs(usbAudio);
if (delay == UINT64_MAX)
processTimeout = 10;
else
{
const uint64_t timeout = delay / UINT64_C(1000000) +
(delay % UINT64_C(1000000) != 0);
processTimeout = (int)min(timeout, UINT64_C(10));
}
}
#endif
if ((status = purespice_process(processTimeout)) != PS_STATUS_RUN)