From 2e1908a52be468ca5a87cac020400f515bec5ae2 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 14 Oct 2026 19:24:51 +0200 Subject: [PATCH] Preserve capture clock across call delivery and validate media peer --- src/call/call.c | 29 +++++++++++++++++++++++++---- src/call/call_headless.c | 2 +- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/call/call.c b/src/call/call.c index da1d21dc..b617be5c 100644 --- a/src/call/call.c +++ b/src/call/call.c @@ -668,13 +668,24 @@ static void call_handle_hangup(struct call_ctx* ctx, uint64_t from, const uint8_ } static void call_handle_media(struct call_ctx* ctx, uint64_t from, const uint8_t* b, size_t n) { - if (n < CALL_MEDIA_HDR_SIZE) return; + if (n <= CALL_MEDIA_HDR_SIZE) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "call media truncated bytes=%zu", n); return; } uint64_t call_id; memcpy(&call_id, b + 1, 8); struct call_session* s = call_session_find(ctx, call_id); if (!s || s->state != CALL_ST_ACTIVE) { /* медиа вне ACTIVE — игнорируем (гонка с завершением) */ return; } + struct call_pkt_media packet; + memcpy(&packet, b, sizeof(packet)); + if (from != s->peer_node_id || packet.track_id != CALL_TRACK_AUDIO || packet.codec_delay_samples > CALL_OPUS_SAMPLE_RATE + || (packet.capture_pts_us != INT64_MIN && (packet.capture_pts_us < 0 || packet.capture_pts_us > INT64_MAX / 2 + || !packet.capture_epoch))) { + DEBUG_WARN(DEBUG_CATEGORY_CALL, "call media invalid peer=%016llx expected=%016llx track=%u pts=%lld epoch=%llu delay=%u", + (unsigned long long)from, (unsigned long long)s->peer_node_id, packet.track_id, + (long long)packet.capture_pts_us, (unsigned long long)packet.capture_epoch, packet.codec_delay_samples); + return; + } + struct call_capture_time timing = {packet.capture_pts_us, packet.capture_epoch, packet.codec_delay_samples}; s->c_rx_frames++; s->last_media_tb = get_time_tb(); @@ -686,7 +697,7 @@ static void call_handle_media(struct call_ctx* ctx, uint64_t from, const uint8_t /* аудио-движок (декод + джиттер-буфер) — если зарегистрирован */ if (ctx->audio_ops && ctx->audio_ops->on_media) ctx->audio_ops->on_media(ctx->inst, call_id, seq, ts_ms, - b + CALL_MEDIA_HDR_SIZE, (int)(n - CALL_MEDIA_HDR_SIZE)); + b + CALL_MEDIA_HDR_SIZE, (int)(n - CALL_MEDIA_HDR_SIZE), &timing); /* релей Opus-кадра потребителю (headless-аудио-сокет) */ if (ctx->media_recv_fn) @@ -843,12 +854,19 @@ static void call_tx_flush(struct call_session* s) { } } -int call_send_media(struct UTUN_INSTANCE* inst, uint64_t call_id, const uint8_t* opus, int len) { +int call_send_media(struct UTUN_INSTANCE* inst, uint64_t call_id, const uint8_t* opus, int len, + const struct call_capture_time* timing) { struct call_ctx* ctx = call_of(inst); if (!ctx || !ctx->initialized || !opus || len <= 0) return -1; struct call_session* s = call_session_find(ctx, call_id); if (!s || s->state != CALL_ST_ACTIVE) return -1; if (CALL_MEDIA_HDR_SIZE + (size_t)len > 1024) return -1; + if (timing && (timing->delay_samples > CALL_OPUS_SAMPLE_RATE || (timing->pts_us != INT64_MIN + && (timing->pts_us < 0 || timing->pts_us > INT64_MAX / 2 || !timing->epoch)))) { + DEBUG_ERROR(DEBUG_CATEGORY_CALL, "call media invalid capture metadata pts=%lld epoch=%llu delay=%u", + (long long)timing->pts_us, (unsigned long long)timing->epoch, timing->delay_samples); + return -1; + } uint8_t buf[1024]; struct call_pkt_media* m = (struct call_pkt_media*)buf; @@ -857,6 +875,9 @@ int call_send_media(struct UTUN_INSTANCE* inst, uint64_t call_id, const uint8_t* m->track_id = CALL_TRACK_AUDIO; m->seq = s->tx_media_seq++; m->ts_ms = (uint32_t)(get_time_tb() / 10); + m->capture_pts_us = timing ? timing->pts_us : INT64_MIN; + m->capture_epoch = timing ? timing->epoch : 0; + m->codec_delay_samples = timing ? timing->delay_samples : 0; memcpy(buf + CALL_MEDIA_HDR_SIZE, opus, (size_t)len); size_t mlen = CALL_MEDIA_HDR_SIZE + (size_t)len; @@ -1025,6 +1046,6 @@ void call_hangup_trampoline(void* arg) { void call_send_media_trampoline(void* arg) { struct call_media_arg* a = (struct call_media_arg*)arg; - call_send_media(a->inst, a->call_id, a->opus, a->len); + call_send_media(a->inst, a->call_id, a->opus, a->len, &a->timing); u_free(a); } diff --git a/src/call/call_headless.c b/src/call/call_headless.c index 259bdf1c..410b15d5 100644 --- a/src/call/call_headless.c +++ b/src/call/call_headless.c @@ -206,7 +206,7 @@ static void ca_parse_frames(struct call_audio_client* cli) { CH_ID, olen, (unsigned long long)call_id); } else { ca->c_enc_frames++; - if (call_send_media(ca->inst, call_id, opus, olen) != 0) + if (call_send_media(ca->inst, call_id, opus, olen, NULL) != 0) DEBUG_WARN(DEBUG_CATEGORY_CALL, "%s: send_media failed call_id=%016llx len=%d", CH_ID, (unsigned long long)call_id, olen); }