|
|
|
@ -13,6 +13,7 @@ |
|
|
|
|
|
|
|
|
|
|
|
#include "call.h" |
|
|
|
#include "call.h" |
|
|
|
#include "call_priv.h" |
|
|
|
#include "call_priv.h" |
|
|
|
|
|
|
|
#include "call_video_transport.h" |
|
|
|
#include "call_proto.h" |
|
|
|
#include "call_proto.h" |
|
|
|
#include "../dm/dm_core.h" |
|
|
|
#include "../dm/dm_core.h" |
|
|
|
#include "../utun_instance.h" |
|
|
|
#include "../utun_instance.h" |
|
|
|
@ -147,7 +148,8 @@ static void call_session_free_cb(void* arg) { |
|
|
|
/* Освободить медиа-объекты сессии: отменить waiter на send_q роутера и
|
|
|
|
/* Освободить медиа-объекты сессии: отменить waiter на send_q роутера и
|
|
|
|
* дренировать tx_q (без отправки). Безопасно вызывать из uasync-потока. */ |
|
|
|
* дренировать tx_q (без отправки). Безопасно вызывать из uasync-потока. */ |
|
|
|
static void call_session_cleanup(struct call_ctx* ctx, struct call_session* s) { |
|
|
|
static void call_session_cleanup(struct call_ctx* ctx, struct call_session* s) { |
|
|
|
etcp_router_cancel_send_ready(ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL, &s->send_waiter); |
|
|
|
call_video_transport_stop(s); |
|
|
|
|
|
|
|
etcp_router_cancel_send_ready(ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL_AUDIO, &s->send_waiter); |
|
|
|
if (s->tx_q) { |
|
|
|
if (s->tx_q) { |
|
|
|
struct ll_entry* e; |
|
|
|
struct ll_entry* e; |
|
|
|
while ((e = queue_data_get(s->tx_q)) != NULL) { |
|
|
|
while ((e = queue_data_get(s->tx_q)) != NULL) { |
|
|
|
@ -282,6 +284,7 @@ static void call_end(struct call_ctx* ctx, struct call_session* s, uint8_t reaso |
|
|
|
if (!ctx || !s || s->state == CALL_ST_ENDED) return; |
|
|
|
if (!ctx || !s || s->state == CALL_ST_ENDED) return; |
|
|
|
|
|
|
|
|
|
|
|
s->state = CALL_ST_ENDED; |
|
|
|
s->state = CALL_ST_ENDED; |
|
|
|
|
|
|
|
call_video_transport_stop(s); |
|
|
|
s->reason = reason; |
|
|
|
s->reason = reason; |
|
|
|
ctx->c_calls_ended++; |
|
|
|
ctx->c_calls_ended++; |
|
|
|
|
|
|
|
|
|
|
|
@ -679,6 +682,19 @@ static void call_etcp_recv(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
|
|
const uint8_t* body = d + ROUTER_SVC_PAYLOAD_OFF; |
|
|
|
const uint8_t* body = d + ROUTER_SVC_PAYLOAD_OFF; |
|
|
|
size_t blen = entry->len - ROUTER_SVC_PAYLOAD_OFF; |
|
|
|
size_t blen = entry->len - ROUTER_SVC_PAYLOAD_OFF; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((subcmd == CALL_SUBCMD_MEDIA) != (d[0] == ETCP_RT_ID_CALL_AUDIO)) { |
|
|
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "call packet in wrong channel svc=%u subcmd=%u", d[0], subcmd); |
|
|
|
|
|
|
|
queue_dgram_free(entry); queue_entry_free(entry); return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (subcmd != CALL_SUBCMD_INVITE && blen >= 9) { |
|
|
|
|
|
|
|
uint64_t id; memcpy(&id, body + 1, 8); |
|
|
|
|
|
|
|
struct call_session *s = call_session_find(ctx, id); |
|
|
|
|
|
|
|
if (s && (s->peer_node_id != from || s->group_id != group)) { |
|
|
|
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "call packet peer/group mismatch id=%llu", (unsigned long long)id); |
|
|
|
|
|
|
|
queue_dgram_free(entry); queue_entry_free(entry); return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
switch (subcmd) { |
|
|
|
switch (subcmd) { |
|
|
|
case CALL_SUBCMD_INVITE: call_handle_invite(ctx, from, group, body, blen); break; |
|
|
|
case CALL_SUBCMD_INVITE: call_handle_invite(ctx, from, group, body, blen); break; |
|
|
|
case CALL_SUBCMD_RINGING: call_handle_ringing(ctx, from, body, blen); break; |
|
|
|
case CALL_SUBCMD_RINGING: call_handle_ringing(ctx, from, body, blen); break; |
|
|
|
@ -722,6 +738,10 @@ int call_init(struct UTUN_INSTANCE* inst) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ctx->initialized = 1; |
|
|
|
ctx->initialized = 1; |
|
|
|
|
|
|
|
if (etcp_router_bind(inst, ETCP_RT_ID_CALL_AUDIO, call_etcp_recv) || call_video_transport_bind(inst)) { |
|
|
|
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "call media channel bind failed"); |
|
|
|
|
|
|
|
call_destroy(inst); return -1; |
|
|
|
|
|
|
|
} |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: initialized (svc=0x%02x)", CALL_ID, ETCP_RT_ID_CALL); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: initialized (svc=0x%02x)", CALL_ID, ETCP_RT_ID_CALL); |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -746,6 +766,8 @@ void call_destroy(struct UTUN_INSTANCE* inst) { |
|
|
|
queue_entry_free(e); |
|
|
|
queue_entry_free(e); |
|
|
|
} |
|
|
|
} |
|
|
|
queue_free(ctx->sessions); |
|
|
|
queue_free(ctx->sessions); |
|
|
|
|
|
|
|
call_video_transport_unbind(inst); |
|
|
|
|
|
|
|
etcp_router_unbind(inst, ETCP_RT_ID_CALL_AUDIO); |
|
|
|
etcp_router_unbind(inst, ETCP_RT_ID_CALL); |
|
|
|
etcp_router_unbind(inst, ETCP_RT_ID_CALL); |
|
|
|
inst->call = NULL; |
|
|
|
inst->call = NULL; |
|
|
|
uint32_t started = ctx->c_calls_started, ended = ctx->c_calls_ended; |
|
|
|
uint32_t started = ctx->c_calls_started, ended = ctx->c_calls_ended; |
|
|
|
@ -792,17 +814,19 @@ static void call_tx_flush(struct call_session* s) { |
|
|
|
struct call_ctx* ctx = s->ctx; |
|
|
|
struct call_ctx* ctx = s->ctx; |
|
|
|
if (s->state == CALL_ST_ENDED) return; |
|
|
|
if (s->state == CALL_ST_ENDED) return; |
|
|
|
/* гарантируем router conn — иначе has_room/on_send_ready не видят его */ |
|
|
|
/* гарантируем router conn — иначе has_room/on_send_ready не видят его */ |
|
|
|
etcp_router_conn_get(ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL); |
|
|
|
etcp_router_conn_get(ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL_AUDIO); |
|
|
|
while (s->tx_q && s->tx_q->head) { |
|
|
|
while (s->tx_q && s->tx_q->head) { |
|
|
|
if (!etcp_router_send_q_has_room(ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL)) { |
|
|
|
if (!etcp_router_send_q_has_room(ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL_AUDIO)) { |
|
|
|
etcp_router_on_send_ready(ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL, |
|
|
|
etcp_router_on_send_ready(ctx->inst, s->group_id, s->peer_node_id, ETCP_RT_ID_CALL_AUDIO, |
|
|
|
&s->send_waiter, call_tx_drain_cb, s); |
|
|
|
&s->send_waiter, call_tx_drain_cb, s); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
struct ll_entry* e = queue_data_get(s->tx_q); |
|
|
|
struct ll_entry* e = queue_data_get(s->tx_q); |
|
|
|
if (etcp_route_send(ctx->inst, s->group_id, s->peer_node_id, e, 1, 0) == 0) |
|
|
|
struct call_pkt_media packet; memcpy(&packet, e->dgram + 1, sizeof(packet)); |
|
|
|
|
|
|
|
if (etcp_route_send(ctx->inst, s->group_id, s->peer_node_id, e, 1, 0) == 0) { |
|
|
|
s->c_tx_frames++; |
|
|
|
s->c_tx_frames++; |
|
|
|
else DEBUG_WARN(DEBUG_CATEGORY_CALL, "%s: media enqueue failed id=%016llx", CALL_ID, (unsigned long long)s->call_id); |
|
|
|
if (packet.capture_pts_us != INT64_MIN) call_video_audio_sent(s, packet.capture_pts_us + 20000); |
|
|
|
|
|
|
|
} else DEBUG_WARN(DEBUG_CATEGORY_CALL, "%s: media enqueue failed id=%016llx", CALL_ID, (unsigned long long)s->call_id); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -838,7 +862,7 @@ int call_send_media(struct UTUN_INSTANCE* inst, uint64_t call_id, const uint8_t* |
|
|
|
if (!e) return -1; |
|
|
|
if (!e) return -1; |
|
|
|
e->dgram = u_malloc(1 + mlen); |
|
|
|
e->dgram = u_malloc(1 + mlen); |
|
|
|
if (!e->dgram) { queue_entry_free(e); return -1; } |
|
|
|
if (!e->dgram) { queue_entry_free(e); return -1; } |
|
|
|
e->dgram[0] = ETCP_RT_ID_CALL; |
|
|
|
e->dgram[0] = ETCP_RT_ID_CALL_AUDIO; |
|
|
|
memcpy(e->dgram + 1, buf, mlen); |
|
|
|
memcpy(e->dgram + 1, buf, mlen); |
|
|
|
e->len = (uint16_t)(1 + mlen); |
|
|
|
e->len = (uint16_t)(1 + mlen); |
|
|
|
|
|
|
|
|
|
|
|
|