Preserve adaptive audio batch target without partial-packet deadlines
This commit is contained in:
1 parent
e598568652
commit
ee5c258b3c
2 files changed
+63
-2
No files matched your search
@@ -1,4 +1,5 @@
|
||||
#include "call_audio_batch.h"
|
||||
#include "../routing_layer/etcp_router.h"
|
||||
#include "../../lib/debug_config.h"
|
||||
#include <string.h>
|
||||
|
||||
@@ -49,3 +50,51 @@ int call_audio_batch_parse(const uint8_t *wire, size_t length, struct call_audio
|
||||
invalid:
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CALL, "audio batch RX malformed bytes=%zu", length); return -1;
|
||||
}
|
||||
|
||||
void call_audio_batch_init(struct call_audio_batch_control *c, uint64_t now) {
|
||||
if (!c) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "audio batch reset without state"); return; }
|
||||
*c = (struct call_audio_batch_control){.target = 4, .last_us = now};
|
||||
}
|
||||
int call_audio_batch_step(struct call_audio_batch_control *c, uint64_t now, const struct ETCP_ROUTER_TX_STATS *net) {
|
||||
if (!c || !c->target || now < c->last_us || (net && net->last_ack_us > now)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "audio batch invalid control sample time=%llu", (unsigned long long)now); return -1;
|
||||
}
|
||||
unsigned previous = c->target;
|
||||
if ((!net && (c->local_epoch || c->peer_epoch || c->feedback_received))
|
||||
|| (net && (net->local_epoch != c->local_epoch || net->peer_epoch != c->peer_epoch))) {
|
||||
call_audio_batch_init(c, now);
|
||||
if (net) { c->local_epoch = net->local_epoch; c->peer_epoch = net->peer_epoch; }
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "audio batch reset router epoch=%llx/%llx previous=%u",
|
||||
(unsigned long long)c->local_epoch, (unsigned long long)c->peer_epoch, previous);
|
||||
}
|
||||
c->last_us = now;
|
||||
c->delay_us = net ? net->delivery_rtt_us : 0;
|
||||
if (net && net->queued_age_us > c->delay_us) c->delay_us = net->queued_age_us;
|
||||
if (net && net->inflight_age_us > c->delay_us) c->delay_us = net->inflight_age_us;
|
||||
uint64_t interval = net && net->delivery_rtt_us > 250000 ? net->delivery_rtt_us * 2 : 500000;
|
||||
uint64_t fresh_us = interval > 2000000 ? interval : 2000000;
|
||||
int measured = net && net->ready && !net->no_route && !net->receive_busy && !net->probing
|
||||
&& net->acked_packets && now - net->last_ack_us <= fresh_us;
|
||||
if (measured) c->feedback_received = 1;
|
||||
int usable = measured && net->window.valid;
|
||||
const char *reason = "hold";
|
||||
if (!usable) { c->high = c->low = 0; reason = "unavailable"; }
|
||||
else if (c->delay_us > 100000) {
|
||||
c->low = 0;
|
||||
if (!c->high) { c->high = 1; c->high_since_us = now; }
|
||||
if (c->target < CALL_AUDIO_BATCH_MAX && now >= c->next_change_us && now - c->high_since_us >= 200000) {
|
||||
c->target++; c->next_change_us = now + interval; reason = "enqueue_ack_delay";
|
||||
}
|
||||
} else if (c->delay_us < 40000 && !net->queued_packets) {
|
||||
c->high = 0;
|
||||
if (!c->low) { c->low = 1; c->low_since_us = now; }
|
||||
if (c->target > 1 && now >= c->next_change_us && now - c->low_since_us >= 2000000) {
|
||||
c->target--; c->next_change_us = now + interval; c->low_since_us = now; reason = "recovered";
|
||||
}
|
||||
} else c->high = c->low = 0;
|
||||
if (c->target != previous) DEBUG_INFO(DEBUG_CATEGORY_CALL,
|
||||
"audio batch target=%u->%u reason=%s delay_us=%llu router_rtt_us=%llu queued_us=%llu flight_us=%llu",
|
||||
previous, c->target, reason, (unsigned long long)c->delay_us, (unsigned long long)(net ? net->delivery_rtt_us : 0),
|
||||
(unsigned long long)(net ? net->queued_age_us : 0), (unsigned long long)(net ? net->inflight_age_us : 0));
|
||||
return 0;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Упаковка Opus звонка: 1..10 кадров, не более 1024 байт. Wire views заимствуют входной буфер.
|
||||
* call.c собирает доступные кадры по готовности Router, без отдельного ожидания наполнения. */
|
||||
/* Упаковка Opus звонка и выбор размера пачки по Router enqueue->ACK. Views заимствуют входной буфер.
|
||||
* Владелец снимает статистику каждые 50мс. Отправка ждёт target кадров И готовности Router; deadline нет. */
|
||||
#ifndef CALL_AUDIO_BATCH_H
|
||||
#define CALL_AUDIO_BATCH_H
|
||||
#include "call_proto.h"
|
||||
@@ -15,10 +15,22 @@ struct call_audio_batch_view {
|
||||
struct call_pkt_media header; /* Общие поля; count определяет число frames. */
|
||||
struct call_audio_frame_view frames[CALL_AUDIO_BATCH_MAX];
|
||||
};
|
||||
struct ETCP_ROUTER_TX_STATS;
|
||||
struct call_audio_batch_control {
|
||||
uint64_t local_epoch, peer_epoch, last_us, high_since_us, low_since_us, next_change_us;
|
||||
uint64_t delay_us; /* max полного RTT и возраста Router-очередей; локальное накопление не входит. */
|
||||
unsigned target; /* Необходимое число кадров, 1..10; старт с 4. */
|
||||
int feedback_received; /* Первый пригодный ACK в текущей Router epoch. */
|
||||
int high, low; /* Гистерезис роста/снижения target. */
|
||||
};
|
||||
/* Encode/parse: 0/-1. Проверяют весь пакет до изменения out/length/view; ошибки логируются.
|
||||
* Encode не допускает перекрытие output с Opus; capacity >= вычисленного размера, максимум 1024. */
|
||||
int call_audio_batch_encode(const struct call_audio_batch_view *batch, uint8_t *out, size_t capacity, size_t *length);
|
||||
int call_audio_batch_parse(const uint8_t *wire, size_t length, struct call_audio_batch_view *out);
|
||||
/* Начальное target=4; новый звонок или смена Router epoch сбрасывает историю. */
|
||||
void call_audio_batch_init(struct call_audio_batch_control *control, uint64_t now_us);
|
||||
/* NULL — канал отсутствует; без свежих ACK/при блокировке target не растёт. 0/-1 с логом. */
|
||||
int call_audio_batch_step(struct call_audio_batch_control *control, uint64_t now_us, const struct ETCP_ROUTER_TX_STATS *net);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in new issue
Block a user