Сохрани накопленные исправления аудио, async и диагностики
This commit is contained in:
1 parent
b1e3193899
commit
bdcf8b5634
59 files changed
+4611
-1441
No files matched your search
+55
-21
@@ -30,6 +30,7 @@ struct call_jitter {
|
||||
int lens[CALL_JITTER_MAX_FRAMES];
|
||||
int head = 0, count = 0;
|
||||
uint32_t dropped = 0, underruns = 0;
|
||||
call_jitter_diagnostics diagnostics{};
|
||||
int processing_ms = 0, tempo_x100 = 100; /* снимок аудиопотока под mtx */
|
||||
call_jitter_window depth_window, arrival_window;
|
||||
call_jitter_range range = {0, 0, 60}; /* опубликованный снимок под mtx */
|
||||
@@ -88,16 +89,10 @@ void call_jitter_push(struct call_jitter* j, const uint8_t* enc, int len, uint32
|
||||
(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->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)
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CALL, "call_jitter: overflow capacity=15000ms dropped=%u", j->dropped);
|
||||
}
|
||||
int idx = (j->head + j->count) % CALL_JITTER_MAX_FRAMES;
|
||||
std::memcpy(j->ring[idx], enc, (size_t)len); j->lens[idx] = len; j->count++;
|
||||
@@ -112,18 +107,21 @@ int call_jitter_pull(struct call_jitter* j, int16_t* out, int max_samples) {
|
||||
+ (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->depth_window.observe(now_ms, (int)std::lround(depth));
|
||||
j->depth_window.observe(now_ms, (int)std::lround(depth));
|
||||
int resumed = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(j->mtx);
|
||||
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=%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 < reserve) return 0;
|
||||
if (depth < reserve) {
|
||||
std::lock_guard<std::mutex> lock(j->mtx);
|
||||
j->diagnostics.depth_ms = (int)depth; j->diagnostics.reserve_ms = reserve;
|
||||
j->diagnostics.tempo_x100 = j->tempo_x100; j->diagnostics.waiting = 1;
|
||||
return 0;
|
||||
}
|
||||
j->primed = true;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call_jitter: playback resumed depth=%.0fms reserve=%dms", depth, reserve);
|
||||
resumed = 1;
|
||||
}
|
||||
j->ema_ms += 0.2 * (depth - j->ema_ms);
|
||||
double target = 1.0 + (kMaxTempo - 1.0) * clamp_value((std::min(depth, j->ema_ms) - reserve - 15.0) / 925.0, 0.0, 1.0);
|
||||
@@ -132,13 +130,14 @@ int call_jitter_pull(struct call_jitter* j, int16_t* out, int max_samples) {
|
||||
j->tempo += clamp_value(target - j->tempo, -step, step);
|
||||
j->st.setTempo(j->tempo);
|
||||
bool catching = j->tempo > 1.05;
|
||||
if (catching != j->catching_up) {
|
||||
j->catching_up = catching;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call_jitter: catch-up %s depth=%.0fms tempo=%.2f",
|
||||
catching ? "started" : "finished", depth, j->tempo);
|
||||
}
|
||||
j->catching_up = catching;
|
||||
uint32_t decode_errors = 0;
|
||||
int error_result = 0, error_len = 0;
|
||||
uint8_t enc[kMaxEncoded];
|
||||
while (j->st.numSamples() < (uint)max_samples) {
|
||||
// Бюджет учитывает ускорение 1.6 и начальное накопление SoundTouch, включая битые пакеты.
|
||||
int budget = std::min(CALL_JITTER_MAX_FRAMES, 8 + max_samples / 480);
|
||||
int attempted = 0;
|
||||
while (j->st.numSamples() < (uint)max_samples && attempted++ < budget) {
|
||||
int len;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(j->mtx);
|
||||
@@ -148,7 +147,8 @@ int call_jitter_pull(struct call_jitter* j, int16_t* out, int max_samples) {
|
||||
}
|
||||
int n = j->decode(j->arg, enc, len, j->pcm, kFrameSamples);
|
||||
if (n <= 0 || n > kFrameSamples) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CALL, "call_jitter: decode failed len=%d result=%d", len, n);
|
||||
++decode_errors;
|
||||
error_result = n; error_len = len;
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) j->input[i] = j->pcm[i] / 32768.0f;
|
||||
@@ -162,14 +162,48 @@ int call_jitter_pull(struct call_jitter* j, int16_t* out, int max_samples) {
|
||||
std::lock_guard<std::mutex> lock(j->mtx);
|
||||
j->processing_ms = processing_ms; j->tempo_x100 = (int)std::lround(j->tempo * 100);
|
||||
if (n < max_samples) j->underruns++;
|
||||
j->diagnostics.depth_ms = (int)depth;
|
||||
j->diagnostics.reserve_ms = reserve;
|
||||
j->diagnostics.tempo_x100 = j->tempo_x100;
|
||||
j->diagnostics.decode_errors += decode_errors;
|
||||
if (decode_errors) { j->diagnostics.error_result = error_result; j->diagnostics.error_len = error_len; }
|
||||
if (attempted > budget) ++j->diagnostics.budget_hits;
|
||||
j->diagnostics.resumed |= resumed;
|
||||
j->diagnostics.catching_up = catching;
|
||||
j->diagnostics.waiting = n < max_samples;
|
||||
}
|
||||
if (n < max_samples) {
|
||||
j->primed = false;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "call_jitter: waiting for refill pcm=%d/%d processing=%dms", n, max_samples, processing_ms);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void call_jitter_take_diagnostics(struct call_jitter* j, struct call_jitter_diagnostics* out) {
|
||||
std::lock_guard<std::mutex> lock(j->mtx);
|
||||
*out = j->diagnostics;
|
||||
j->diagnostics.decode_errors = j->diagnostics.budget_hits = 0;
|
||||
j->diagnostics.resumed = 0;
|
||||
}
|
||||
|
||||
void call_jitter_reset(struct call_jitter* j) {
|
||||
if (!j) return;
|
||||
std::lock_guard<std::mutex> lock(j->mtx);
|
||||
int keep = std::min(j->count, (j->arrival_window.reserve + CALL_JITTER_FRAME_MS - 1) / CALL_JITTER_FRAME_MS);
|
||||
int discard = j->count - keep;
|
||||
j->head = (j->head + discard) % CALL_JITTER_MAX_FRAMES;
|
||||
j->count = keep;
|
||||
j->dropped += discard;
|
||||
j->st.clear(); j->st.setTempo(1.0);
|
||||
j->primed = false; j->catching_up = false;
|
||||
j->tempo = 1.0; j->ema_ms = j->arrival_window.reserve;
|
||||
j->processing_ms = 0; j->tempo_x100 = 100;
|
||||
j->depth_window = {};
|
||||
j->diagnostics = {};
|
||||
j->range.min_ms = j->range.max_ms = keep * CALL_JITTER_FRAME_MS;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call_jitter: device discontinuity discarded=%dms kept=%dms reserve=%dms",
|
||||
discard * CALL_JITTER_FRAME_MS, keep * CALL_JITTER_FRAME_MS, j->arrival_window.reserve);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!j) return;
|
||||
|
||||
Reference in new issue
Block a user