|
|
|
|
@ -86,8 +86,8 @@ int main() {
|
|
|
|
|
w.observe(0, 100); w.observe(500, 400); |
|
|
|
|
CHECK(w.low == 100 && w.high == 400 && w.reserve == 300); |
|
|
|
|
w.observe(1000, 2000); CHECK(w.reserve == 1000); |
|
|
|
|
w.observe(10000, 2000); CHECK(w.low == 2000 && w.reserve == 60); |
|
|
|
|
w.observe(30000, 80); CHECK(w.low == 80 && w.high == 80 && w.reserve == 60); |
|
|
|
|
w.observe(10000, 2000); CHECK(w.low == 2000 && w.reserve == 20); |
|
|
|
|
w.observe(30000, 80); CHECK(w.low == 80 && w.high == 80 && w.reserve == 20); |
|
|
|
|
for (int cap : {500, 2000}) { |
|
|
|
|
call_jitter_window limited; |
|
|
|
|
limited.max_reserve = cap; |
|
|
|
|
@ -100,13 +100,18 @@ int main() {
|
|
|
|
|
// Разные часы и переполнение uint32 не создают джиттер при ровной доставке.
|
|
|
|
|
uint32_t sent = UINT32_MAX - 39; |
|
|
|
|
for (int i = 0; i < 100; i++) arrival.observe_arrival(900000 + i * 20, sent + (uint32_t)(i * 20)); |
|
|
|
|
CHECK(arrival.high == 0 && arrival.low == 0 && arrival.reserve == 60); |
|
|
|
|
CHECK(arrival.high == 0 && arrival.low == 0 && arrival.reserve == 20); |
|
|
|
|
arrival.observe_arrival(902200, sent + 2000); // задержка доставки +200мс
|
|
|
|
|
CHECK(arrival.reserve == 200); |
|
|
|
|
arrival.observe_arrival(902220, sent + 2220); // очередь пришла пачкой
|
|
|
|
|
CHECK(arrival.reserve == 200); |
|
|
|
|
arrival.observe_arrival(920000, sent + 20000); // старый burst ушёл из окна
|
|
|
|
|
CHECK(arrival.reserve == 60); |
|
|
|
|
CHECK(arrival.reserve == 20); |
|
|
|
|
// Небольшой джиттер больше минимума тоже учитывается, без прежнего порога 60мс.
|
|
|
|
|
arrival.observe_arrival(920055, sent + 20020); |
|
|
|
|
CHECK(arrival.reserve == 35); |
|
|
|
|
arrival.observe_arrival(931055, sent + 31020); |
|
|
|
|
CHECK(arrival.reserve == 20); |
|
|
|
|
} |
|
|
|
|
debug_set_level(DEBUG_LEVEL_WARN); |
|
|
|
|
debug_enable_console(1); |
|
|
|
|
@ -118,13 +123,13 @@ int main() {
|
|
|
|
|
decoder d; |
|
|
|
|
auto* j = call_jitter_create(decode, &d, 1000); |
|
|
|
|
CHECK(j); |
|
|
|
|
push(j, 0); push(j, 1); |
|
|
|
|
push(j, 0); // 20мс достаточно для допуска в decoder, но SoundTouch ещё накапливает PCM.
|
|
|
|
|
CHECK(call_jitter_pull(j, pcm, 480) == 0); |
|
|
|
|
push(j, 2); |
|
|
|
|
push(j, 1); |
|
|
|
|
CHECK(call_jitter_pull(j, pcm, 480) > 0); |
|
|
|
|
call_jitter_diagnostics diagnostic{}; |
|
|
|
|
call_jitter_get_diagnostics(j, &diagnostic, 0); |
|
|
|
|
CHECK(diagnostic.resumed == 1 && diagnostic.pcm_samples == 480 && diagnostic.reserve_ms == 60); |
|
|
|
|
CHECK(diagnostic.resumed == 1 && diagnostic.pcm_samples == 480 && diagnostic.reserve_ms == 20); |
|
|
|
|
CHECK(diagnostic.encoded_ms >= 0 && diagnostic.processing_ms >= 0 && diagnostic.arrival_spread_ms == 0); |
|
|
|
|
call_jitter_get_diagnostics(j, &diagnostic, 0); |
|
|
|
|
CHECK(diagnostic.resumed == 1); // Снимок playback не потребляет события для uasync.
|
|
|
|
|
@ -133,6 +138,7 @@ int main() {
|
|
|
|
|
call_jitter_get_diagnostics(j, &diagnostic, 0); |
|
|
|
|
CHECK(diagnostic.resumed == 0); |
|
|
|
|
call_jitter_range range; |
|
|
|
|
push(j, 2); |
|
|
|
|
for (int i = 3; i < 503; i++) push(j, i, 10060); // 10с сетевой задержки, затем пачка
|
|
|
|
|
call_jitter_get_stats(j, nullptr, nullptr, nullptr, nullptr, &range); |
|
|
|
|
CHECK(range.reserve_ms == 1000); |
|
|
|
|
@ -144,7 +150,7 @@ int main() {
|
|
|
|
|
if (tempo > highest) highest = tempo; |
|
|
|
|
} |
|
|
|
|
call_jitter_get_stats(j, nullptr, nullptr, nullptr, nullptr, &range); |
|
|
|
|
CHECK(range.reserve_ms == 60 && range.max_ms >= 10000); |
|
|
|
|
CHECK(range.reserve_ms == 20 && range.max_ms >= 10000); |
|
|
|
|
CHECK(range.min_ms <= range.max_ms); |
|
|
|
|
CHECK(highest >= 150); |
|
|
|
|
CHECK(depth < 200); |
|
|
|
|
@ -153,16 +159,16 @@ int main() {
|
|
|
|
|
for (size_t i = 0; i < d.seen.size(); i++) CHECK(d.seen[i] == (int)i); |
|
|
|
|
for (int i = 0; i < 100; i++) call_jitter_pull(j, pcm, 480); |
|
|
|
|
size_t before = d.seen.size(); |
|
|
|
|
// Один кадр плюс остаток SoundTouch ниже 60мс; два кадра уже могут заполнить запас.
|
|
|
|
|
// После опустошения один кадр дополняет остаток SoundTouch и возобновляет PCM при запасе 20мс.
|
|
|
|
|
push(j, next++); |
|
|
|
|
int refill = call_jitter_pull(j, pcm, 480); |
|
|
|
|
if (refill) { |
|
|
|
|
if (refill != 480) { |
|
|
|
|
call_jitter_get_stats(j, &depth, nullptr, nullptr, nullptr, &range); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "refill check: pcm=%d remaining=%dms reserve=%dms decoded=%zu", |
|
|
|
|
refill, depth, range.reserve_ms, d.seen.size() - before); |
|
|
|
|
} |
|
|
|
|
CHECK(refill == 0); |
|
|
|
|
CHECK(d.seen.size() == before); |
|
|
|
|
CHECK(refill == 480); |
|
|
|
|
CHECK(d.seen.size() == before + 1); |
|
|
|
|
for (int i = 0; i < 50; i++) push(j, next++); |
|
|
|
|
CHECK(call_jitter_pull(j, pcm, 480) > 0); |
|
|
|
|
call_jitter_destroy(j); |
|
|
|
|
@ -174,15 +180,24 @@ int main() {
|
|
|
|
|
for (int i = 0; i < 60; i++) push(j, i); |
|
|
|
|
call_jitter_range range; |
|
|
|
|
call_jitter_get_stats(j, &depth, nullptr, nullptr, nullptr, &range); |
|
|
|
|
CHECK(depth == 1200 && range.reserve_ms == 60); |
|
|
|
|
CHECK(depth == 1200 && range.reserve_ms == 20); |
|
|
|
|
CHECK(call_jitter_pull(j, pcm, 480) == 480); // первый pull сразу воспроизводит речь
|
|
|
|
|
int next = 60; |
|
|
|
|
int next = 60, short_pulls = 0; |
|
|
|
|
for (int tick = 0; tick < 6000; tick++) { |
|
|
|
|
if (tick % 2 == 0) push(j, next++); |
|
|
|
|
CHECK(call_jitter_pull(j, pcm, 480) == 480); |
|
|
|
|
int produced = call_jitter_pull(j, pcm, 480); |
|
|
|
|
if (produced != 480 && ++short_pulls <= 3) { |
|
|
|
|
call_jitter_diagnostics state{}; |
|
|
|
|
call_jitter_get_diagnostics(j, &state, 0); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, |
|
|
|
|
"steady delivery short pull tick=%d pcm=%d depth_before=%d encoded=%d processing=%d reserve=%d tempo=%d spread=%lld", |
|
|
|
|
tick, produced, state.depth_ms, state.encoded_ms, state.processing_ms, state.reserve_ms, |
|
|
|
|
state.tempo_x100, (long long)state.arrival_spread_ms); |
|
|
|
|
} |
|
|
|
|
call_jitter_get_stats(j, &depth, &tempo, &dropped, &under, &range); |
|
|
|
|
CHECK(range.reserve_ms == 60); // догон не увеличивает сетевой резерв
|
|
|
|
|
CHECK(range.reserve_ms == 20); // догон не увеличивает сетевой резерв
|
|
|
|
|
} |
|
|
|
|
CHECK(short_pulls == 0); |
|
|
|
|
CHECK(depth < 150 && tempo <= 110 && dropped == 0 && under == 0); |
|
|
|
|
for (size_t i = 0; i < d.seen.size(); i++) CHECK(d.seen[i] == (int)i); |
|
|
|
|
call_jitter_destroy(j); |
|
|
|
|
|