call: P2P аудиозвонки — сигналинг, релей медиа, аудио-контур, headless/desktop/android
- src/call/: сигналинг (call.c, call_proto.h, ETCP_RT_ID_CALL 0x36) + релей медиа + headless аудио-сокет (call_headless.c, call_audio_bind) + Opus/jitter/тоны - chat: события CHAT_EVT_CALL_* (34–40), CHAT_EVT_DM_MESSAGES (41), DM-трамплины - chatgui: call window, miniaudio I/O, audio_frame_ring, test_audio_jitter - chatgui-android: CallScreen/IncomingCallDialog, аудио-роутинг, JNI, рингтон - tests: test_call, test_call_headless
This commit is contained in:
1 parent
0157f7f96b
commit
b11e77e4c0
78 files changed
+7053
-70
No files matched your search
@@ -54,6 +54,14 @@ extern "C" {
|
||||
#define CHAT_EVT_CHANNEL_DELETED 31 /* [ch_id_len:1][ch_id:var] — канал удалён локально */
|
||||
#define CHAT_EVT_DM_MSG_RECEIVED 32 /* [conv_id_len:1][conv_id:var][author_node_id:8] */
|
||||
#define CHAT_EVT_DM_CONV_UPDATED 33 /* [conv_id_len:1][conv_id:var] — беседа создана/изменена */
|
||||
#define CHAT_EVT_CALL_INCOMING 34 /* [ch_id_len:1][ch_id:var][caller_node_id:8][call_id:8] */
|
||||
#define CHAT_EVT_CALL_RINGING 35 /* [call_id:8] */
|
||||
#define CHAT_EVT_CALL_ACCEPTED 36 /* [call_id:8] */
|
||||
#define CHAT_EVT_CALL_ENDED 37 /* [call_id:8][reason:1] */
|
||||
#define CHAT_EVT_CALL_DECLINED 38 /* [call_id:8][reason:1] */
|
||||
#define CHAT_EVT_CALL_STATS 39 /* [call_id:8][rtt_ms:2][buffer_ms:2][tempo_x100:2] */
|
||||
#define CHAT_EVT_CALL_ERROR 40 /* [call_id:8][err:1][text:var] */
|
||||
#define CHAT_EVT_DM_MESSAGES 41 /* [conv_id_len:1][conv_id:var][count:2][msg:var]* — расшифрованные сообщения беседы */
|
||||
|
||||
typedef void (*chat_event_handler_fn)(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int len);
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include "invite_build.h"
|
||||
#include "../dm/dm_core.h"
|
||||
#include "../dm/dm_crypto.h"
|
||||
#include "../call/call.h"
|
||||
#include "../call/call_proto.h"
|
||||
#include "../transport_layer/secure_channel.h"
|
||||
#include "../transport_layer/etcp.h"
|
||||
#include "../transport_layer/etcp_connections.h"
|
||||
@@ -201,6 +203,27 @@ static void hc_on_chat_event(struct UTUN_INSTANCE* inst, int type, const uint8_t
|
||||
} else if (type == 33 && data && len >= 2) { /* DM_CONV_UPDATED */
|
||||
uint8_t cl = data[0];
|
||||
snprintf(json, sizeof(json), "{\"event\":\"dm_conv\",\"conv\":\"%.*s\"}", (int)cl, data + 1);
|
||||
} else if (type == CHAT_EVT_CALL_INCOMING && data && len >= 1 + 8 + 8) { /* CALL_INCOMING */
|
||||
uint8_t cl = data[0];
|
||||
if (len >= 1 + (int)cl + 8 + 8) {
|
||||
uint64_t caller; memcpy(&caller, data + 1 + cl, 8);
|
||||
uint64_t cid; memcpy(&cid, data + 1 + cl + 8, 8);
|
||||
snprintf(json, sizeof(json),
|
||||
"{\"event\":\"call_incoming\",\"ch\":\"%.*s\",\"caller_id\":\"0x%016llx\",\"call_id\":\"0x%016llx\"}",
|
||||
(int)cl, data + 1, (unsigned long long)caller, (unsigned long long)cid);
|
||||
}
|
||||
} else if (type == CHAT_EVT_CALL_RINGING && data && len >= 8) { /* CALL_RINGING */
|
||||
uint64_t cid; memcpy(&cid, data, 8);
|
||||
snprintf(json, sizeof(json), "{\"event\":\"call_ringing\",\"call_id\":\"0x%016llx\"}", (unsigned long long)cid);
|
||||
} else if (type == CHAT_EVT_CALL_ACCEPTED && data && len >= 8) { /* CALL_ACCEPTED */
|
||||
uint64_t cid; memcpy(&cid, data, 8);
|
||||
snprintf(json, sizeof(json), "{\"event\":\"call_accepted\",\"call_id\":\"0x%016llx\"}", (unsigned long long)cid);
|
||||
} else if (type == CHAT_EVT_CALL_ENDED && data && len >= 9) { /* CALL_ENDED */
|
||||
uint64_t cid; memcpy(&cid, data, 8);
|
||||
snprintf(json, sizeof(json), "{\"event\":\"call_ended\",\"call_id\":\"0x%016llx\",\"reason\":%d}", (unsigned long long)cid, data[8]);
|
||||
} else if (type == CHAT_EVT_CALL_DECLINED && data && len >= 9) { /* CALL_DECLINED */
|
||||
uint64_t cid; memcpy(&cid, data, 8);
|
||||
snprintf(json, sizeof(json), "{\"event\":\"call_declined\",\"call_id\":\"0x%016llx\",\"reason\":%d}", (unsigned long long)cid, data[8]);
|
||||
}
|
||||
if (json[0]) hc_broadcast_event(hc, json);
|
||||
}
|
||||
@@ -650,12 +673,58 @@ static void hc_handle_dm_send(struct headless_client* cli, int id, const char* j
|
||||
send_response(cli, id, "{\"sent\":true}", NULL);
|
||||
}
|
||||
|
||||
/* ── Звонки (P2P аудио) ── */
|
||||
|
||||
static uint64_t hc_gen_call_id(void) {
|
||||
uint64_t id = ((uint64_t)rand() << 32) ^ (uint64_t)rand();
|
||||
return id ? id : 1;
|
||||
}
|
||||
|
||||
static void hc_handle_call_start(struct headless_client* cli, int id, const char* json) {
|
||||
char ch[64], node_id_str[32], call_id_str[32];
|
||||
if (json_get_str(json, "ch", ch, sizeof(ch)) < 0) { send_response(cli, id, NULL, "missing 'ch' param"); return; }
|
||||
if (json_get_str(json, "node_id", node_id_str, sizeof(node_id_str)) < 0) { send_response(cli, id, NULL, "missing 'node_id' param"); return; }
|
||||
uint64_t peer = strtoull(node_id_str, NULL, 16);
|
||||
if (!peer || !cli->hc->inst) { send_response(cli, id, NULL, "invalid node_id"); return; }
|
||||
uint64_t call_id = 0;
|
||||
if (json_get_str(json, "call_id", call_id_str, sizeof(call_id_str)) == 0) call_id = strtoull(call_id_str, NULL, 16);
|
||||
if (!call_id) call_id = hc_gen_call_id();
|
||||
|
||||
call_start(cli->hc->inst, ch, peer, call_id);
|
||||
char resp[256]; snprintf(resp, sizeof(resp), "{\"call_id\":\"0x%016llx\",\"calling\":true}", (unsigned long long)call_id);
|
||||
send_response(cli, id, resp, NULL);
|
||||
}
|
||||
|
||||
static void hc_handle_call_accept(struct headless_client* cli, int id, const char* json) {
|
||||
char call_id_str[32];
|
||||
if (json_get_str(json, "call_id", call_id_str, sizeof(call_id_str)) < 0) { send_response(cli, id, NULL, "missing 'call_id' param"); return; }
|
||||
uint64_t call_id = strtoull(call_id_str, NULL, 16);
|
||||
call_accept(cli->hc->inst, call_id);
|
||||
send_response(cli, id, "{\"accepted\":true}", NULL);
|
||||
}
|
||||
|
||||
static void hc_handle_call_decline(struct headless_client* cli, int id, const char* json) {
|
||||
char call_id_str[32];
|
||||
if (json_get_str(json, "call_id", call_id_str, sizeof(call_id_str)) < 0) { send_response(cli, id, NULL, "missing 'call_id' param"); return; }
|
||||
uint64_t call_id = strtoull(call_id_str, NULL, 16);
|
||||
call_decline(cli->hc->inst, call_id);
|
||||
send_response(cli, id, "{\"declined\":true}", NULL);
|
||||
}
|
||||
|
||||
static void hc_handle_call_hangup(struct headless_client* cli, int id, const char* json) {
|
||||
char call_id_str[32];
|
||||
if (json_get_str(json, "call_id", call_id_str, sizeof(call_id_str)) < 0) { send_response(cli, id, NULL, "missing 'call_id' param"); return; }
|
||||
uint64_t call_id = strtoull(call_id_str, NULL, 16);
|
||||
call_hangup(cli->hc->inst, call_id);
|
||||
send_response(cli, id, "{\"hungup\":true}", NULL);
|
||||
}
|
||||
|
||||
/* ── Command dispatch ── */
|
||||
|
||||
typedef void (*hc_cmd_fn)(struct headless_client* cli, int id, const char* json);
|
||||
|
||||
static const char* cmd_names[] = { "ping", "status", "channels", "members", "messages", "send", "attach", "invite", "invite_nodes", "connect", "create_channel", "invite_to", "subscribe", "dm_start", "dm_list", "dm_messages", "dm_send", "quit", NULL };
|
||||
static hc_cmd_fn cmd_handlers[] = { hc_handle_ping, hc_handle_status, hc_handle_channels, hc_handle_members, hc_handle_messages, hc_handle_send, hc_handle_attach, hc_handle_invite, hc_handle_invite_nodes, hc_handle_connect, hc_handle_create_channel, hc_handle_invite_to, hc_handle_subscribe, hc_handle_dm_start, hc_handle_dm_list, hc_handle_dm_messages, hc_handle_dm_send, hc_handle_quit };
|
||||
static const char* cmd_names[] = { "ping", "status", "channels", "members", "messages", "send", "attach", "invite", "invite_nodes", "connect", "create_channel", "invite_to", "subscribe", "dm_start", "dm_list", "dm_messages", "dm_send", "call_start", "call_accept", "call_decline", "call_hangup", "quit", NULL };
|
||||
static hc_cmd_fn cmd_handlers[] = { hc_handle_ping, hc_handle_status, hc_handle_channels, hc_handle_members, hc_handle_messages, hc_handle_send, hc_handle_attach, hc_handle_invite, hc_handle_invite_nodes, hc_handle_connect, hc_handle_create_channel, hc_handle_invite_to, hc_handle_subscribe, hc_handle_dm_start, hc_handle_dm_list, hc_handle_dm_messages, hc_handle_dm_send, hc_handle_call_start, hc_handle_call_accept, hc_handle_call_decline, hc_handle_call_hangup, hc_handle_quit };
|
||||
|
||||
static void hc_handle_command(struct headless_client* cli, const char* json) {
|
||||
if (!cli || !json) return;
|
||||
|
||||
@@ -43,10 +43,6 @@ static uint8_t parse_adm_tags_flags(const char* adm_tags) {
|
||||
if (json_flat_get(adm_tags, "supernode", buf, sizeof(buf)) == 0
|
||||
&& strcmp(buf, "yes") == 0) flags |= CHAT_MEMBER_FLAG_SUPERNODE;
|
||||
|
||||
/* dm_storage: узел хранит offline-сообщения прямого чата (DM) */
|
||||
if (json_flat_get(adm_tags, "dm_storage", buf, sizeof(buf)) == 0
|
||||
&& strcmp(buf, "yes") == 0) flags |= CHAT_MEMBER_FLAG_DM_STORAGE;
|
||||
|
||||
/* admin: значение — накопительная строка ed...ed..., последний символ e/d */
|
||||
if (json_flat_get(adm_tags, "admin", buf, sizeof(buf)) == 0) {
|
||||
const char* last = buf;
|
||||
|
||||
@@ -30,7 +30,6 @@ extern "C" {
|
||||
#define CHAT_MEMBER_FLAG_ADMIN 0x02
|
||||
#define CHAT_MEMBER_FLAG_MODER 0x04
|
||||
#define CHAT_MEMBER_FLAG_DELETED 0x08
|
||||
#define CHAT_MEMBER_FLAG_DM_STORAGE 0x10
|
||||
|
||||
#define CHAT_MEMBER_DISPLAY_SIZE 79
|
||||
|
||||
|
||||
Reference in new issue
Block a user