diff --git a/src/call/call.h b/src/call/call.h index c18c84ac..ce28b5fe 100644 --- a/src/call/call.h +++ b/src/call/call.h @@ -12,6 +12,7 @@ #include #include "call_jitter.h" +#include "call_clock.h" struct UTUN_INSTANCE; @@ -42,7 +43,8 @@ int call_is_online(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t peer /* Отправить закодированный Opus-кадр пиру (оборачивает в CALL_MEDIA). * Только когда звонок ACTIVE. seq/ts выставляются автоматически. */ -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); /* Коллбэк приёма медиа: вызывается в uasync-потоке на каждый Opus-кадр пира. * Используется headless-аудио-сокетом (и в фазе 3 — декодером GUI). */ @@ -58,7 +60,7 @@ void call_set_media_recv_cb(struct UTUN_INSTANCE* inst, call_media_recv_fn fn, v struct call_audio_ops { /* uasync-поток: принятый Opus-кадр пира (декод + jitter-буфер). */ void (*on_media)(struct UTUN_INSTANCE* inst, uint64_t call_id, - uint16_t seq, uint32_t ts_ms, const uint8_t* opus, int len); + uint16_t seq, uint32_t ts_ms, const uint8_t* opus, int len, const struct call_capture_time* timing); /* uasync-поток: текущие метрики джиттер-буфера (buffer_ms, tempo_x100, * dropped/underruns — опционально, NULL допустим). */ int (*get_stats)(uint64_t call_id, int* buffer_ms, int* tempo_x100, @@ -71,6 +73,7 @@ struct call_media_arg { struct UTUN_INSTANCE* inst; uint64_t call_id; uint16_t len; + struct call_capture_time timing; uint8_t opus[256]; }; void call_send_media_trampoline(void* arg); diff --git a/src/call/call_clock.h b/src/call/call_clock.h index 98ab5d7c..3cccb0f2 100644 --- a/src/call/call_clock.h +++ b/src/call/call_clock.h @@ -5,6 +5,13 @@ #ifndef CALL_CLOCK_H #define CALL_CLOCK_H #include +/* pts_us — первый захваченный сэмпл; INT64_MIN означает отсутствие метки. + * delay_samples — задержка Opus от encoder. epoch меняется при разрыве захвата. */ +struct call_capture_time { + int64_t pts_us; + uint64_t epoch; + uint32_t delay_samples; +}; struct call_clock_snapshot { uint64_t epoch, source_epoch, played_samples, written_samples; int64_t pts_us; diff --git a/src/call/call_proto.h b/src/call/call_proto.h index d04a56f9..29ef6898 100644 --- a/src/call/call_proto.h +++ b/src/call/call_proto.h @@ -21,7 +21,7 @@ enum { CALL_SUBCMD_BUSY = 0x05, /* {call_id:8} */ CALL_SUBCMD_CANCEL = 0x06, /* {call_id:8} */ CALL_SUBCMD_HANGUP = 0x07, /* {call_id:8, reason:1} */ - CALL_SUBCMD_MEDIA = 0x10, /* {call_id:8, track_id:1, seq:2, ts_ms:4, payload[]} */ + CALL_SUBCMD_MEDIA = 0x10, /* call_pkt_media + payload[] */ }; /* ── треки и кодеки (модель расширяема под видео) ── */ @@ -71,12 +71,15 @@ struct call_pkt_media { uint8_t track_id; uint16_t seq; uint32_t ts_ms; + int64_t capture_pts_us; /* INT64_MIN — источник не предоставляет capture clock. */ + uint64_t capture_epoch; + uint32_t codec_delay_samples; /* payload[] */ }; #pragma pack(pop) -#define CALL_MEDIA_HDR_SIZE sizeof(struct call_pkt_media) /* 16 */ +#define CALL_MEDIA_HDR_SIZE sizeof(struct call_pkt_media) /* 36 */ /* ── параметры аудио (Opus 48kHz mono, 20ms) ── */ #define CALL_OPUS_SAMPLE_RATE 48000