Require the adaptive frame count before requesting or using a Router slot
This commit is contained in:
1 parent
ee5c258b3c
commit
6ae080d417
2 files changed
+33
-9
No files matched your search
+31
-9
@@ -47,6 +47,7 @@ struct call_audio_pending {
|
||||
uint16_t seq;
|
||||
};
|
||||
static void call_tx_flush(struct call_session* s);
|
||||
static void call_audio_batch_tick(void* arg);
|
||||
static void call_tx_flush(struct call_session* s);
|
||||
|
||||
|
||||
@@ -150,6 +151,7 @@ static struct call_session* call_session_new(struct call_ctx* ctx, uint64_t call
|
||||
s->state = CALL_ST_IDLE;
|
||||
s->started_tb = get_time_tb();
|
||||
s->ctx = ctx;
|
||||
call_audio_batch_init(&s->audio_batch, get_time_us());
|
||||
s->tx_q = queue_new(ctx->inst->ua, 0, 0, 0, "call_txq");
|
||||
if (!s->tx_q) { queue_entry_free(qe); return NULL; }
|
||||
// При провале queue_data_put_with_index сам освобождает qe (вместе с s), поэтому
|
||||
@@ -171,6 +173,8 @@ static void call_session_free_cb(void* arg) {
|
||||
* дренировать tx_q (без отправки). Безопасно вызывать из uasync-потока. */
|
||||
static void call_session_cleanup(struct call_ctx* ctx, struct call_session* s) {
|
||||
call_video_transport_stop(s);
|
||||
if (s->audio_batch_timer) uasync_cancel_timeout(ctx->inst->ua, s->audio_batch_timer);
|
||||
s->audio_batch_timer = NULL;
|
||||
etcp_router_cancel_request(&s->audio_send);
|
||||
if (s->tx_q) {
|
||||
struct ll_entry* e;
|
||||
@@ -427,8 +431,9 @@ static void call_activate(struct call_session* s) {
|
||||
if (s->state == CALL_ST_ACTIVE || s->state == CALL_ST_ENDED) return;
|
||||
struct call_ctx* ctx = s->ctx;
|
||||
s->state = CALL_ST_ACTIVE;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "audio batch startup call=%llx mode=router-ready max_frames=%u max_bytes=%u",
|
||||
(unsigned long long)s->call_id, CALL_AUDIO_BATCH_MAX, CALL_MEDIA_MAX_BYTES);
|
||||
call_audio_batch_tick(s);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "audio batch startup call=%llx mode=full-and-router-ready target=%u max_bytes=%u",
|
||||
(unsigned long long)s->call_id, s->audio_batch.target, CALL_MEDIA_MAX_BYTES);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: accepted id=%016llx -> ACTIVE (router delivery)", CALL_ID,
|
||||
(unsigned long long)s->call_id);
|
||||
call_event_id(ctx, CHAT_EVT_CALL_ACCEPTED, s->call_id, 0);
|
||||
@@ -534,11 +539,12 @@ static void call_post_stats(struct call_session* s) {
|
||||
s->tx_wait_frames = s->tx_wait_max_ms = 0; s->tx_wait_total_ms = 0;
|
||||
struct ETCP_ROUTER_TX_STATS net = {0};
|
||||
etcp_router_tx_stats(ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL_AUDIO, get_time_us(), &net);
|
||||
const char *wait = !s->audio_send.conn ? "idle" : !net.ready ? "handshake" : net.no_route ? "no-route"
|
||||
const char *wait = s->tx_q && s->tx_q->count < (int)s->audio_batch.target ? "frames"
|
||||
: !s->audio_send.conn ? "unregistered" : net.no_route ? "no-route" : !net.ready ? "handshake"
|
||||
: net.receive_busy ? "peer-busy" : rconn && net.inflight_packets >= rconn->inflight_limit ? "window" : "transport";
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "audio batch flow call=%llx wait=%s queued=%u/%llu inflight=%u/%llu "
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "audio batch flow call=%llx target=%u wait=%s queued=%u/%llu inflight=%u/%llu "
|
||||
"enqueue_ack_us=%llu submit_ack_us=%u ack_bytes_500ms=%llu tx_frames=%u tx_batches=%u rx_frames=%u rx_batches=%u",
|
||||
(unsigned long long)s->call_id, wait, net.queued_packets, (unsigned long long)net.queued_age_us,
|
||||
(unsigned long long)s->call_id, s->audio_batch.target, wait, net.queued_packets, (unsigned long long)net.queued_age_us,
|
||||
net.inflight_packets, (unsigned long long)net.inflight_age_us, (unsigned long long)net.delivery_rtt_us,
|
||||
net.rtt_us, (unsigned long long)net.window.bytes, s->c_tx_frames, s->tx_batches, s->c_rx_frames, s->rx_batches);
|
||||
call_tx_flush(s); /* Повтор регистрации после reset/ошибки выделения; обычный drain работает по событиям. */
|
||||
@@ -1000,10 +1006,25 @@ void call_set_audio_ops(struct UTUN_INSTANCE* inst, const struct call_audio_ops*
|
||||
|
||||
/* ─────────────────── TX медиа: tx_q + backpressure ─────────────────── */
|
||||
|
||||
static void call_audio_batch_tick(void* arg) {
|
||||
struct call_session *s = arg; s->audio_batch_timer = NULL;
|
||||
if (s->state != CALL_ST_ACTIVE) return;
|
||||
struct ETCP_ROUTER_TX_STATS net = {0};
|
||||
uint64_t now = get_time_us();
|
||||
int found = etcp_router_tx_stats(s->ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL_AUDIO, now, &net) == 0;
|
||||
if (call_audio_batch_step(&s->audio_batch, now, found ? &net : NULL))
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "audio batch control failed call=%llx; retaining target=%u",
|
||||
(unsigned long long)s->call_id, s->audio_batch.target);
|
||||
call_tx_flush(s);
|
||||
s->audio_batch_timer = uasync_set_timeout(s->ctx->inst->ua, 500, s, call_audio_batch_tick, "audio_batch");
|
||||
if (!s->audio_batch_timer) DEBUG_ERROR(DEBUG_CATEGORY_CALL, "audio batch control timer allocation failed");
|
||||
}
|
||||
|
||||
/* Пачка формируется по готовности Router; до принятия все кадры остаются в FIFO сервиса. */
|
||||
static void call_tx_ready(struct ETCP_ROUTER_CONN* rconn, void* arg) {
|
||||
struct call_session *s = arg;
|
||||
if (s->state != CALL_ST_ACTIVE || !s->tx_q || !s->tx_q->head) return;
|
||||
if (s->tx_q->count < (int)s->audio_batch.target) return; /* target мог вырасти после регистрации. */
|
||||
struct call_audio_pending *first = (struct call_audio_pending*)s->tx_q->head->data;
|
||||
struct call_audio_batch_view batch = {.header = {.subcmd = CALL_SUBCMD_MEDIA, .call_id = s->call_id,
|
||||
.track_id = CALL_TRACK_AUDIO, .capture_epoch = first->timing.epoch,
|
||||
@@ -1011,7 +1032,7 @@ static void call_tx_ready(struct ETCP_ROUTER_CONN* rconn, void* arg) {
|
||||
size_t size = CALL_MEDIA_HDR_SIZE;
|
||||
struct ll_entry *next = s->tx_q->head;
|
||||
const char *reason = "available";
|
||||
while (next && batch.header.count < CALL_AUDIO_BATCH_MAX) {
|
||||
while (next && batch.header.count < s->audio_batch.target) {
|
||||
struct call_audio_pending *p = (struct call_audio_pending*)next->data;
|
||||
if (p->timing.epoch != first->timing.epoch || p->timing.delay_samples != first->timing.delay_samples) {
|
||||
reason = "capture-change"; break;
|
||||
@@ -1021,7 +1042,7 @@ static void call_tx_ready(struct ETCP_ROUTER_CONN* rconn, void* arg) {
|
||||
.header = {p->seq, p->timing.pts_us, next->len}, .opus = next->dgram};
|
||||
size += CALL_AUDIO_FRAME_HDR_SIZE + next->len; next = next->next;
|
||||
}
|
||||
if (batch.header.count == CALL_AUDIO_BATCH_MAX) reason = "frames";
|
||||
if (batch.header.count == s->audio_batch.target) reason = "target";
|
||||
uint8_t wire[CALL_MEDIA_MAX_BYTES];
|
||||
size_t encoded = 0;
|
||||
if (call_audio_batch_encode(&batch, wire, sizeof(wire), &encoded)) return;
|
||||
@@ -1042,8 +1063,8 @@ static void call_tx_ready(struct ETCP_ROUTER_CONN* rconn, void* arg) {
|
||||
queue_dgram_free(e); queue_entry_free(e);
|
||||
}
|
||||
s->c_tx_frames += count; s->tx_batches++;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "audio batch TX call=%llx frames=%u bytes=%zu oldest_us=%llu first_seq=%u reason=%s remaining=%d",
|
||||
(unsigned long long)s->call_id, count, encoded, (unsigned long long)age,
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "audio batch TX call=%llx frames=%u target=%u bytes=%zu oldest_us=%llu first_seq=%u reason=%s remaining=%d",
|
||||
(unsigned long long)s->call_id, count, s->audio_batch.target, encoded, (unsigned long long)age,
|
||||
batch.frames[0].header.seq, reason, queue_entry_count(s->tx_q));
|
||||
if (last_pts != INT64_MIN) call_video_audio_sent(s, last_pts + 20000);
|
||||
call_tx_flush(s);
|
||||
@@ -1051,6 +1072,7 @@ static void call_tx_ready(struct ETCP_ROUTER_CONN* rconn, void* arg) {
|
||||
|
||||
static void call_tx_flush(struct call_session* s) {
|
||||
if (s->state != CALL_ST_ACTIVE || !s->tx_q || !s->tx_q->head || s->audio_send.conn) return;
|
||||
if (s->tx_q->count < (int)s->audio_batch.target) return;
|
||||
struct ETCP_ROUTER_CONN *rconn = etcp_router_conn_get(s->ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL_AUDIO);
|
||||
if (etcp_router_request_send(rconn, &s->audio_send, call_tx_ready, s))
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "audio router request failed call=%llx; retaining Opus", (unsigned long long)s->call_id);
|
||||
|
||||
@@ -35,6 +35,8 @@ struct call_session {
|
||||
uint8_t connection_reported, connection_type; /* Было событие CONNECTION / последний router path.type (0..3). */
|
||||
uint64_t connection_next_hop, last_media_tb; /* Кэш next hop и время последнего RX; watchdog использует traffic_timer. */
|
||||
uint16_t tx_media_seq; /* Следующий sequence аудиопакета с wrap uint16; расходуется до выделения TX entry. */
|
||||
struct call_audio_batch_control audio_batch; /* Прежний адаптивный выбор обязательного числа кадров. */
|
||||
void *audio_batch_timer; /* Снимок Router и пересчёт target каждые 50мс; не отправляет неполную пачку. */
|
||||
uint32_t tx_batches, rx_batches; /* Число Router-кодограмм, отдельно от аудиокадров. */
|
||||
struct ll_queue *tx_q; /* FIFO Opus с локальными seq/PTS/created_us в entry->data; до 750, overflow удаляет голову. */
|
||||
struct router_send_request audio_send; /* Слот отправки: до callback кадры остаются в tx_q. */
|
||||
|
||||
Reference in new issue
Block a user