diff --git a/src/call/call_audio.c b/src/call/call_audio.c index 01c19e43..2efdf9b7 100644 --- a/src/call/call_audio.c +++ b/src/call/call_audio.c @@ -82,11 +82,11 @@ void call_audio_destroy(struct UTUN_INSTANCE* inst) { void call_audio_on_media(struct UTUN_INSTANCE* inst, uint64_t call_id, uint16_t seq, uint32_t ts_ms, const uint8_t* opus, int len) { - (void)inst; (void)seq; (void)ts_ms; + (void)inst; (void)seq; pthread_mutex_lock(&g_mtx); if (g_active && call_id == g_call_id && g_vj && opus && len > 0) { - call_jitter_push(g_vj, opus, len); + call_jitter_push(g_vj, opus, len, ts_ms, (int64_t)(get_time_tb() / 10)); } pthread_mutex_unlock(&g_mtx); } diff --git a/src/call/call_jitter.cpp b/src/call/call_jitter.cpp index 3b363bd6..e33e36ff 100644 --- a/src/call/call_jitter.cpp +++ b/src/call/call_jitter.cpp @@ -31,7 +31,7 @@ struct call_jitter { int head = 0, count = 0; uint32_t dropped = 0, underruns = 0; int processing_ms = 0, tempo_x100 = 100; /* снимок аудиопотока под mtx */ - call_jitter_window window; + call_jitter_window depth_window, arrival_window; call_jitter_range range = {0, 0, 60}; /* опубликованный снимок под mtx */ call_jitter_decode_fn decode; void* arg; @@ -59,7 +59,7 @@ struct call_jitter* call_jitter_create(call_jitter_decode_fn decode, void* arg, return nullptr; } auto* j = new (memory) call_jitter; - j->window.max_reserve = max_reserve_ms; + j->arrival_window.max_reserve = max_reserve_ms; j->decode = decode; j->arg = arg; j->st.setSampleRate(kSampleRate); j->st.setChannels(1); j->st.setTempo(1.0); j->st.setSetting(SETTING_USE_QUICKSEEK, 1); @@ -77,12 +77,23 @@ void call_jitter_destroy(struct call_jitter* j) { j->~call_jitter(); u_free(j); } -void call_jitter_push(struct call_jitter* j, const uint8_t* enc, int len) { - if (!j || !enc || len <= 0 || len > kMaxEncoded) { - DEBUG_WARN(DEBUG_CATEGORY_CALL, "call_jitter: invalid frame len=%d", len); +void call_jitter_push(struct call_jitter* j, const uint8_t* enc, int len, uint32_t sent_ms, int64_t received_ms) { + if (!j || !enc || len <= 0 || len > kMaxEncoded || received_ms < 0) { + DEBUG_WARN(DEBUG_CATEGORY_CALL, "call_jitter: invalid frame len=%d received=%lldms", len, (long long)received_ms); return; } std::lock_guard lock(j->mtx); + if (received_ms < j->arrival_window.last_received_ms) { + DEBUG_WARN(DEBUG_CATEGORY_CALL, "call_jitter: receive clock moved backwards previous=%lldms now=%lldms", + (long long)j->arrival_window.last_received_ms, (long long)received_ms); + return; + } + bool log_arrival = j->arrival_window.observe_arrival(received_ms, sent_ms); + j->range.reserve_ms = j->arrival_window.reserve; + if (log_arrival) + DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "call_jitter: delivery variation=%lldms reserve=%dms queued=%dms", + (long long)(j->arrival_window.high - j->arrival_window.low), j->arrival_window.reserve, + j->count * CALL_JITTER_FRAME_MS); if (j->count == CALL_JITTER_MAX_FRAMES) { j->head = (j->head + 1) % CALL_JITTER_MAX_FRAMES; j->count--; j->dropped++; if (j->dropped == 1 || j->dropped % 50 == 0) @@ -94,28 +105,28 @@ void call_jitter_push(struct call_jitter* j, const uint8_t* enc, int len) { int call_jitter_pull(struct call_jitter* j, int16_t* out, int max_samples) { if (!j || !out || max_samples <= 0) return 0; - int count; - { std::lock_guard lock(j->mtx); count = j->count; } + int count, reserve; + { std::lock_guard lock(j->mtx); count = j->count; reserve = j->arrival_window.reserve; } // Готовый PCM учитываем в длительности исходной речи. double depth = count * CALL_JITTER_FRAME_MS + (j->st.numUnprocessedSamples() + j->st.numSamples() * j->tempo) * 1000.0 / kSampleRate; int64_t now_ms = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()).count(); - bool log_stats = j->window.observe(now_ms, (int)std::lround(depth)); + bool log_stats = j->depth_window.observe(now_ms, (int)std::lround(depth)); { std::lock_guard lock(j->mtx); - j->range = {j->window.low, j->window.high, j->window.reserve}; + j->range.min_ms = (int)j->depth_window.low; j->range.max_ms = (int)j->depth_window.high; } if (log_stats) - DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "call_jitter: depth=%.0fms min=%dms max=%dms reserve=%dms tempo=%.2f", - depth, j->window.low, j->window.high, j->window.reserve, j->tempo); + DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "call_jitter: depth=%.0fms min=%lldms max=%lldms reserve=%dms tempo=%.2f", + depth, (long long)j->depth_window.low, (long long)j->depth_window.high, reserve, j->tempo); if (!j->primed) { - if (depth < j->window.reserve) return 0; + if (depth < reserve) return 0; j->primed = true; - DEBUG_INFO(DEBUG_CATEGORY_CALL, "call_jitter: playback resumed depth=%.0fms reserve=%dms", depth, j->window.reserve); + DEBUG_INFO(DEBUG_CATEGORY_CALL, "call_jitter: playback resumed depth=%.0fms reserve=%dms", depth, reserve); } j->ema_ms += 0.2 * (depth - j->ema_ms); - double target = 1.0 + (kMaxTempo - 1.0) * clamp_value((std::min(depth, j->ema_ms) - j->window.reserve - 15.0) / 925.0, 0.0, 1.0); + double target = 1.0 + (kMaxTempo - 1.0) * clamp_value((std::min(depth, j->ema_ms) - reserve - 15.0) / 925.0, 0.0, 1.0); // Линейный предел 1.0→1.6 за 500мс PCM. Снижение следует за глубиной буфера с тем же пределом. double step = (kMaxTempo - 1.0) * max_samples * 1000.0 / (kSampleRate * kTempoRampMs); j->tempo += clamp_value(target - j->tempo, -step, step);