Browse Source

Separate playback backlog from adaptive call reserve

master
evgeny 2 days ago
parent
commit
50e3db81ea
  1. 4
      src/call/call_audio.c
  2. 39
      src/call/call_jitter.cpp

4
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);
}

39
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<std::mutex> 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<std::mutex> lock(j->mtx); count = j->count; }
int count, reserve;
{ std::lock_guard<std::mutex> 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::milliseconds>(
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<std::mutex> 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);

Loading…
Cancel
Save