diff --git a/daemon/call.c b/daemon/call.c index a1aba82b5d..33ddf68690 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -145,7 +145,8 @@ void call_make_own_foreign(struct call *c, bool foreign) { static void call_timer_iterator(struct call *c, struct iterator_helper *hlp) { GList *it; unsigned int check; - bool good = false; + bool good = false; // gets true if any stream has active media + bool failed = false; // gets true if any stream has a timeout event struct packet_stream *ps; struct stream_fd *sfd; int tmp_t_reason = UNKNOWN; @@ -176,6 +177,12 @@ static void call_timer_iterator(struct call *c, struct iterator_helper *hlp) { goto out; } + if (c->timeout_mode == TIMEOUT_OFF) + goto out; + + if (c->timeout_activated > rtpe_now.tv_sec) + goto out; + if (c->deleted && rtpe_now.tv_sec >= c->deleted && c->last_signal <= c->deleted) goto delete; @@ -206,6 +213,16 @@ static void call_timer_iterator(struct call *c, struct iterator_helper *hlp) { /* valid stream */ + // ignore RTCP Streams + if (PS_ISSET(ps, RTCP)) + goto next; + + // ignore Streams which are already marked fot deletion + if (ps->media->monologue->mark_deleted) { + ilog(LOG_DEBUG, "Ignoring deleted monologue"); + goto next; + } + css = call_stream_state_machine(ps); if (css == CSS_ICE) @@ -216,12 +233,12 @@ static void call_timer_iterator(struct call *c, struct iterator_helper *hlp) { g_hash_table_insert(hlp->addr_sfd, &sfd->socket.local, obj_get(sfd)); no_sfd: - if (good) + if (failed && c->timeout_mode == TIMEOUT_ANY) goto next; check = rtpe_config.timeout; tmp_t_reason = TIMEOUT; - if (!MEDIA_ISSET(ps->media, RECV) || !sfd) { + if (!MEDIA_ISSET(ps->media, RECV) || !sfd || !MEDIA_ISSET(ps->media, SEND)) { check = rtpe_config.silent_timeout; tmp_t_reason = SILENT_TIMEOUT; } @@ -230,8 +247,14 @@ static void call_timer_iterator(struct call *c, struct iterator_helper *hlp) { tmp_t_reason = OFFER_TIMEOUT; } - if (rtpe_now.tv_sec - atomic64_get(timestamp) < check) + if (rtpe_now.tv_sec - atomic64_get(timestamp) < check) { + ps->missed_packet_counter = 0; good = true; + } else { + ps->missed_packet_counter++; + if (ps->missed_packet_counter > 2) + failed = true; + } next: ; @@ -243,7 +266,7 @@ static void call_timer_iterator(struct call *c, struct iterator_helper *hlp) { hlp->transcoded_media++; } - if (good || IS_FOREIGN_CALL(c)) { + if (!failed || (good && (c->timeout_mode == TIMEOUT_ALL)) || IS_FOREIGN_CALL(c)) { goto out; } @@ -3418,6 +3441,8 @@ static struct call *call_create(const str *callid) { c->created = rtpe_now; c->dtls_cert = dtls_cert(); c->tos = rtpe_config.default_tos; + c->timeout_mode = rtpe_config.timeout_mode; + c->timeout_activated = rtpe_now.tv_sec; if (rtpe_config.cpu_affinity) c->cpu_affinity = call_socket_cpu_affinity++ % rtpe_config.cpu_affinity; else @@ -4022,6 +4047,7 @@ int call_delete_branch(const str *callid, const str *branch, "(via-branch '" STR_FORMAT_M "') in %d seconds", STR_FMT_M(&ml->tag), STR_FMT0_M(branch), delete_delay); ml->deleted = rtpe_now.tv_sec + delete_delay; + ml->mark_deleted = 1; if (!c->ml_deleted || c->ml_deleted > ml->deleted) c->ml_deleted = ml->deleted; } diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index c48e90f388..2f274421f9 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1120,6 +1120,26 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu } } + out->timeout_mode = TIMEOUT_DEFAULT; + if (bencode_dictionary_get_str(input, "timeout", &s)) { + switch (__csh_lookup(&s)) { + case CSH_LOOKUP("off"): + out->timeout_mode=TIMEOUT_OFF; + break; + case CSH_LOOKUP("multi"): + case CSH_LOOKUP("all"): + out->timeout_mode=TIMEOUT_ALL; + break; + case CSH_LOOKUP("any"): + case CSH_LOOKUP("single"): + out->timeout_mode=TIMEOUT_ANY; + break; + default: + ilog(LOG_WARN, "Unknown 'timeout' flag encountered: '"STR_FORMAT"'", + STR_FMT(&s)); + } + } + call_ng_flags_list(out, input, "rtcp-mux", call_ng_flags_rtcp_mux, NULL); call_ng_flags_list(out, input, "RTCP-mux", call_ng_flags_rtcp_mux, NULL); call_ng_flags_list(out, input, "SDES", ng_sdes_option, NULL); @@ -1503,6 +1523,27 @@ static const char *call_offer_answer_ng(struct ng_buffer *ngbuf, bencode_item_t call->drop_traffic = 0; } + switch (flags.timeout_mode) { + case TIMEOUT_DEFAULT: + // NO CHANGE + break; + case TIMEOUT_OFF: + ilog(LOG_INFO, "Disable RTP timeout monitoring"); + call->timeout_mode = TIMEOUT_OFF; + call->timeout_activated = 0; + break; + case TIMEOUT_ANY: + ilog(LOG_INFO, "Activating RTP any timeout monitoring"); + call->timeout_mode = TIMEOUT_ANY; + call->timeout_activated = rtpe_now.tv_sec + rtpe_config.timeout; + break; + case TIMEOUT_ALL: + ilog(LOG_INFO, "Activating RTP all timeout monitoring"); + call->timeout_mode = TIMEOUT_ALL; + call->timeout_activated = rtpe_now.tv_sec + rtpe_config.timeout; + break; + } + int do_dequeue = 1; ret = monologue_offer_answer(dialogue, &streams, &flags); diff --git a/daemon/main.c b/daemon/main.c index e9f6b6a0a4..c807470e64 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -428,6 +428,7 @@ static void options(int *argc, char ***argv) { AUTO_CLEANUP_GBUF(dtmf_udp_ep); AUTO_CLEANUP_GBUF(endpoint_learning); AUTO_CLEANUP_GBUF(dtls_sig); + AUTO_CLEANUP_GBUF(timeout_mode); double silence_detect = 0; AUTO_CLEANUP_GVBUF(cn_payload); AUTO_CLEANUP_GVBUF(dtx_cn_params); @@ -460,6 +461,7 @@ static void options(int *argc, char ***argv) { { "silent-timeout",'s',0,G_OPTION_ARG_INT, &rtpe_config.silent_timeout,"RTP timeout for muted", "SECS" }, { "final-timeout",'a',0,G_OPTION_ARG_INT, &rtpe_config.final_timeout, "Call timeout", "SECS" }, { "offer-timeout",0,0, G_OPTION_ARG_INT, &rtpe_config.offer_timeout, "Timeout for incomplete one-sided calls", "SECS" }, + { "timeout-mode", 0, 0, G_OPTION_ARG_STRING, &timeout_mode, "Timeout Mode", "off|any|all" }, { "port-min", 'm', 0, G_OPTION_ARG_INT, &rtpe_config.port_min, "Lowest port to use for RTP", "INT" }, { "port-max", 'M', 0, G_OPTION_ARG_INT, &rtpe_config.port_max, "Highest port to use for RTP", "INT" }, { "redis", 'r', 0, G_OPTION_ARG_STRING, &redisps, "Connect to Redis database", "[PW@]IP:PORT/INT" }, @@ -684,6 +686,19 @@ static void options(int *argc, char ***argv) { if (rtpe_config.final_timeout <= 0) rtpe_config.final_timeout = 0; + int to_config = TIMEOUT_ALL; + if (timeout_mode) { + if (!strcasecmp(timeout_mode, "all")) + to_config = TIMEOUT_ALL; + else if (!strcasecmp(timeout_mode, "any")) + to_config = TIMEOUT_ANY; + else if (!strcasecmp(timeout_mode, "off")) + to_config = TIMEOUT_OFF; + else + die("Invalid --timeout-mode option ('%s')", timeout_mode); + } + rtpe_config.timeout_mode = to_config; + if (redisps) if (redis_ep_parse(&rtpe_config.redis_ep, &rtpe_config.redis_db, &rtpe_config.redis_auth, "RTPENGINE_REDIS_AUTH_PW", redisps)) die("Invalid Redis endpoint [IP:PORT/INT] '%s' (--redis)", redisps); diff --git a/include/call.h b/include/call.h index 27cad8f2eb..5475826d65 100644 --- a/include/call.h +++ b/include/call.h @@ -342,6 +342,8 @@ struct packet_stream { /* in_lock must be held for SETTING these: */ volatile unsigned int ps_flags; + + unsigned int missed_packet_counter; }; /* protected by call->master_lock, except the RO elements */ @@ -441,6 +443,7 @@ struct call_monologue { unsigned int silence_media:1; unsigned int rec_forwarding:1; unsigned int inject_dtmf:1; + unsigned int mark_deleted:1; }; struct call_iterator_list { @@ -546,6 +549,9 @@ struct call { unsigned int foreign_media:1; // for calls taken over, tracks whether we have media unsigned int disable_jb:1; unsigned int debug:1; + + int timeout_mode; + time_t timeout_activated; }; diff --git a/include/call_interfaces.h b/include/call_interfaces.h index 2d17cd889f..c6229ee278 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -74,6 +74,12 @@ struct sdp_ng_flags { MEO_BKW, MEO_BOTH, } media_echo:3; + enum { + TIMEOUT_DEFAULT = 0, + TIMEOUT_OFF, + TIMEOUT_ALL, + TIMEOUT_ANY + } timeout_mode:2; unsigned int asymmetric:1, protocol_accept:1, no_redis_update:1, diff --git a/include/main.h b/include/main.h index 62bc10c3f0..b8619d0ff8 100644 --- a/include/main.h +++ b/include/main.h @@ -39,6 +39,7 @@ struct rtpengine_config { int silent_timeout; int final_timeout; int offer_timeout; + int timeout_mode; int delete_delay; GQueue redis_subscribed_keyspaces; int redis_expires_secs;