From d8745fd303836daa68806e0fd4edb8b3d1b3764d Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 1 Oct 2026 02:48:53 +0300 Subject: [PATCH] Measure call jitter from media delivery timestamps --- src/call/call_jitter.h | 6 ++++-- src/call/call_jitter_window.h | 20 +++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/call/call_jitter.h b/src/call/call_jitter.h index 5cf9c541..e8e23078 100644 --- a/src/call/call_jitter.h +++ b/src/call/call_jitter.h @@ -7,7 +7,7 @@ extern "C" { #endif -/* Звонок: FIFO Opus, адаптивный запас 60мс..max_reserve_ms (max-min за 10 секунд) и догон без сброса после паузы устройства. +/* Звонок: FIFO Opus, запас 60мс..max_reserve_ms по изменению задержки доставки за 10 секунд и догон после паузы устройства. * push — сетевой поток, pull/decode/SoundTouch — единственный аудиопоток. * destroy — после остановки обоих потребителей. get_stats — любой поток. */ #define CALL_JITTER_FRAME_MS 20 @@ -17,7 +17,9 @@ struct call_jitter_range { int min_ms, max_ms, reserve_ms; }; struct call_jitter; struct call_jitter* call_jitter_create(call_jitter_decode_fn decode, void* arg, int max_reserve_ms); void call_jitter_destroy(struct call_jitter* j); -void call_jitter_push(struct call_jitter* j, const uint8_t* enc, int len); +/* sent_ms — timestamp кадра на проводе (uint32 с переполнением), received_ms — локальные монотонные часы. + * Абсолютное смещение часов пиров не влияет на оценку джиттера. */ +void call_jitter_push(struct call_jitter* j, const uint8_t* enc, int len, uint32_t sent_ms, int64_t received_ms); int call_jitter_pull(struct call_jitter* j, int16_t* out, int max_samples); void call_jitter_get_stats(struct call_jitter* j, int* depth_ms, int* tempo_x100, uint32_t* dropped, uint32_t* underruns, struct call_jitter_range* range); diff --git a/src/call/call_jitter_window.h b/src/call/call_jitter_window.h index 9eaa2b9b..48ad745e 100644 --- a/src/call/call_jitter_window.h +++ b/src/call/call_jitter_window.h @@ -2,12 +2,15 @@ #include #include -// Десять секундных интервалов. Время передаёт аудиопоток из монотонных часов. +// Десять секундных интервалов. Отдельные экземпляры для глубины очереди и задержки доставки. struct call_jitter_window { - struct bucket { int64_t second = -1; int low = 0, high = 0; } buckets[10]; - int low = 0, high = 0, reserve = 60; + struct bucket { int64_t second = -1, low = 0, high = 0; } buckets[10]; + int64_t low = 0, high = 0; + int reserve = 60; int max_reserve = 1000; - bool observe(int64_t now_ms, int depth) { + int64_t last_received_ms = -1, transit_ms = 0; + uint32_t last_sent_ms = 0; + bool observe(int64_t now_ms, int64_t depth) { int64_t second = now_ms / 1000; auto& b = buckets[second % 10]; bool changed_second = b.second != second; @@ -18,7 +21,14 @@ struct call_jitter_window { if (item.second < 0 || item.second > second || second - item.second >= 10) continue; low = std::min(low, item.low); high = std::max(high, item.high); } - reserve = std::max(60, std::min(max_reserve, high - low)); + reserve = (int)std::max(60, std::min(max_reserve, high - low)); return changed_second; } + + // Разность интервалов прихода и отправки: смещение часов исключается, uint32 timestamp разворачивается. + bool observe_arrival(int64_t received_ms, uint32_t sent_ms) { + if (last_received_ms >= 0) transit_ms += received_ms - last_received_ms - (int32_t)(sent_ms - last_sent_ms); + last_received_ms = received_ms; last_sent_ms = sent_ms; + return observe(received_ms, transit_ms); + } };