// call.h — P2P аудио-звонок (сигналинг + медиа), per-instance // // Модуль оперирует (group_id, peer_node_id): звонок можно инициировать из // CHAT-группы или из DM (group_id — группа маршрутизации, как в dm_core). // Сигналинг идёт через etcp_router (ROUTE_CRYPTO_SIGN|ENCRYPT), соединение // устанавливается через conn_mgr (3-фазное DIRECT→REVERSE→INDIRECT). // // Все функции — в uasync-потоке. GUI/headless вызывают через trampoline // (gui_bridge_post_uasync_fn / uasync_post). #ifndef CALL_H #define CALL_H #include struct UTUN_INSTANCE; #ifdef __cplusplus extern "C" { #endif /* ── жизненный цикл ── */ int call_init(struct UTUN_INSTANCE* inst); /* из utun_instance.c после dm_core_init */ void call_destroy(struct UTUN_INSTANCE* inst); /* перед dm_core_destroy */ /* ── сигналинг (uasync-поток) ── */ /* Caller: начать звонок. out_call_id — случайный uint64, генерит вызывающий. */ void call_start (struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t peer_node_id, uint64_t out_call_id); /* Callee: ответить/отклонить. hangup — любая сторона в любой момент. */ void call_accept (struct UTUN_INSTANCE* inst, uint64_t call_id); void call_decline(struct UTUN_INSTANCE* inst, uint64_t call_id); void call_hangup (struct UTUN_INSTANCE* inst, uint64_t call_id); /* Read-only онлайн-проверка (GUI может звать из любого потока). */ int call_is_online(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t peer_node_id); /* ── медиа (релей Opus-кадров, uasync-поток) ── */ /* Отправить закодированный Opus-кадр пиру (оборачивает в CALL_MEDIA). * Только когда звонок ACTIVE. seq/ts выставляются автоматически. */ int call_send_media(struct UTUN_INSTANCE* inst, uint64_t call_id, const uint8_t* opus, int len); /* Коллбэк приёма медиа: вызывается в uasync-потоке на каждый Opus-кадр пира. * Используется headless-аудио-сокетом (и в фазе 3 — декодером GUI). */ typedef void (*call_media_recv_fn)(struct UTUN_INSTANCE* inst, uint64_t call_id, const uint8_t* opus, int len, void* arg); void call_set_media_recv_cb(struct UTUN_INSTANCE* inst, call_media_recv_fn fn, void* arg); /* ── аудио-движок (опционально, GUI-сборки) ── * Сервисный аудио-контур (codec + джиттер-буфер + тоны) регистрируется через * call_set_audio_ops. call.c зовёт on_media на каждый принятый медиа-кадр (с seq/ts) * и get_stats для события CALL_STATS. Демон без аудио (headless) — ops не ставит. */ 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); /* uasync-поток: текущие метрики джиттер-буфера (buffer_ms, tempo_x100, * dropped/underruns — опционально, NULL допустим). */ int (*get_stats)(uint64_t call_id, int* buffer_ms, int* tempo_x100, uint32_t* dropped, uint32_t* underruns); }; void call_set_audio_ops(struct UTUN_INSTANCE* inst, const struct call_audio_ops* ops); /* Отправить Opus-кадр из ДРУГОГО потока (GUI-аудио): post в uasync через trampoline. */ struct call_media_arg { struct UTUN_INSTANCE* inst; uint64_t call_id; uint16_t len; uint8_t opus[256]; }; void call_send_media_trampoline(void* arg); /* ── trampoline-структуры для gui_bridge_post_uasync_fn / uasync_post ── */ struct call_start_arg { struct UTUN_INSTANCE* inst; char ch_id[64]; uint64_t peer_node_id; uint64_t call_id; }; struct call_id_arg { struct UTUN_INSTANCE* inst; uint64_t call_id; }; void call_start_trampoline (void* arg); void call_accept_trampoline (void* arg); void call_decline_trampoline(void* arg); void call_hangup_trampoline (void* arg); #ifdef __cplusplus } #endif #endif /* CALL_H */