|
|
|
|
@ -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); |
|
|
|
|
|