From 5b106015de66665a2284db4a3923e2afb04d63c2 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sat, 22 Aug 2026 17:04:25 +1000 Subject: [PATCH] [client] audio: ignore jitter after underruns Do not learn late delivery from packets that already caused an underrun and media-clock rebase. Preserve learned jitter only across continuous playback. --- client/src/audio.c | 7 ++++--- client/tests/CMakeLists.txt | 1 + client/tests/audio_test.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/client/src/audio.c b/client/src/audio.c index aae5add1..17c7e36e 100644 --- a/client/src/audio.c +++ b/client/src/audio.c @@ -924,7 +924,7 @@ static int64_t playbackMapMediaTime(PlaybackSourceData * sourceData, } const int64_t delta = time - sourceData->mediaTime; - if ((!clock || !clock->discontinuity) && + if (!*discontinuity && (!clock || !clock->discontinuity) && sourceData->mediaClockFromSource == fromSource && sourceData->mediaPositionValid && sourceData->mediaPosition == position && delta >= 0 && @@ -937,8 +937,9 @@ static int64_t playbackMapMediaTime(PlaybackSourceData * sourceData, (now - sourceData->lastArrivalTime) * 1.0e-9; const double lateness = max(arrivalDelta - mediaDelta, 0.0); - /* Learn harmful late delivery before a buffer underrun rebases the media - * clock. Early catch-up packets do not require additional reserve. */ + /* Learn harmful late delivery only while playback remains continuous. + * An underrun rebases the media clock and must not become future + * latency. Early catch-up packets do not require additional reserve. */ sourceData->arrivalJitterSec = min(PLAYBACK_MAX_JITTER_SEC, max(lateness, sourceData->arrivalJitterSec * diff --git a/client/tests/CMakeLists.txt b/client/tests/CMakeLists.txt index a9198e83..521c667b 100644 --- a/client/tests/CMakeLists.txt +++ b/client/tests/CMakeLists.txt @@ -819,6 +819,7 @@ if(ENABLE_AUDIO) convert provider playback-retry + jitter consent quiesce ) diff --git a/client/tests/audio_test.c b/client/tests/audio_test.c index 5bb1a8bb..481743b4 100644 --- a/client/tests/audio_test.c +++ b/client/tests/audio_test.c @@ -624,6 +624,36 @@ static void testPlaybackRetry(void) stopAudio(); } +static void testPlaybackJitter(void) +{ + PlaybackSourceData source = { 0 }; + playbackPrepareMediaClock(&source, NULL); + + bool discontinuity = false; + playbackMapMediaTime(&source, NULL, 480, 48000, 0, + &discontinuity); + source.inputPosition = 480; + source.lastArrivalTime = 0; + + discontinuity = true; + playbackMapMediaTime(&source, NULL, 480, 48000, + INT64_C(110000000), &discontinuity); + CHECK(discontinuity); + CHECK(source.arrivalJitterSec == 0.0); + + memset(&source, 0, sizeof(source)); + playbackPrepareMediaClock(&source, NULL); + discontinuity = false; + playbackMapMediaTime(&source, NULL, 480, 48000, 0, + &discontinuity); + source.inputPosition = 480; + source.lastArrivalTime = 0; + playbackMapMediaTime(&source, NULL, 480, 48000, + INT64_C(20000000), &discontinuity); + CHECK(source.arrivalJitterSec > 0.009); + CHECK(source.arrivalJitterSec < 0.011); +} + static void testConsent(void) { reset(); @@ -750,6 +780,7 @@ static const struct Test tests[] = { "convert" , testConvert }, { "provider" , testProvider }, { "playback-retry", testPlaybackRetry }, + { "jitter" , testPlaybackJitter }, { "consent" , testConsent }, { "quiesce" , testQuiesce }, };