summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2016-07-19 18:37:38 +0200
committerAlfred E. Heggestad <aeh@db.org>2016-07-19 18:37:38 +0200
commit9da1970131662ad480dae589618411734dcf4905 (patch)
tree44577fcffff35352d432c3aa0c9b0dd69e33c1a0
parente27e37b547b709d8ecf8be306c2b5137b7affad3 (diff)
add support for rtp_timeout and redial
1. Added support for RTP timeout. The feature is disabled by default and can be enabled with config "rtp_timeout N" where N is the number of seconds of RTP inactivity. If this is detected, the call is closed with a "special" SIP reason code of 701. 2. Added support for automatic re-connect in the menu module. This can be enabled by setting the 2 config items: redial_attempts 3 redial_delay 5 This work was contributed by Sveriges Radio. Thanks goes to Ola Palm and Jim Eld.
-rw-r--r--include/baresip.h2
-rw-r--r--modules/menu/menu.c107
-rw-r--r--src/call.c47
-rw-r--r--src/config.c12
-rw-r--r--src/core.h13
-rw-r--r--src/stream.c93
-rw-r--r--test/call.c48
-rw-r--r--test/main.c1
-rw-r--r--test/test.h1
9 files changed, 320 insertions, 4 deletions
diff --git a/include/baresip.h b/include/baresip.h
index ae73cae..22c75ea 100644
--- a/include/baresip.h
+++ b/include/baresip.h
@@ -100,6 +100,7 @@ struct list *call_streaml(const struct call *call);
struct ua *call_get_ua(const struct call *call);
bool call_is_onhold(const struct call *call);
bool call_is_outgoing(const struct call *call);
+void call_enable_rtp_timeout(struct call *call, uint32_t timeout_ms);
/*
@@ -201,6 +202,7 @@ struct config_avt {
bool rtcp_mux; /**< RTP/RTCP multiplexing */
struct range jbuf_del; /**< Delay, number of frames */
bool rtp_stats; /**< Enable RTP statistics */
+ uint32_t rtp_timeout; /**< RTP Timeout in seconds (0=off) */
};
/* Network */
diff --git a/modules/menu/menu.c b/modules/menu/menu.c
index 3c98c8f..b24038a 100644
--- a/modules/menu/menu.c
+++ b/modules/menu/menu.c
@@ -36,6 +36,11 @@ static struct le *le_cur; /**< Current User-Agent (struct ua) */
static struct {
struct play *play;
bool bell;
+
+ struct tmr tmr_redial;
+ uint32_t redial_delay; /* Redial delay in [seconds] */
+ uint32_t redial_attempts; /* Number of re-dial attempts */
+ uint32_t current_attempts;
} menu;
@@ -44,6 +49,13 @@ static void update_callstatus(void);
static void alert_stop(void);
+static void redial_reset(void)
+{
+ tmr_cancel(&menu.tmr_redial);
+ menu.current_attempts = 0;
+}
+
+
static const char *translate_errorcode(uint16_t scode)
{
switch (scode) {
@@ -790,6 +802,42 @@ static void alert_stop(void)
}
+static void redial_handler(void *arg)
+{
+ char *uri = NULL;
+ int err;
+ (void)arg;
+
+ info("now: redialing now. current_attempts=%u, max_attempts=%u\n",
+ menu.current_attempts,
+ menu.redial_attempts);
+
+ if (menu.current_attempts > menu.redial_attempts) {
+
+ info("menu: redial: too many attemptes -- giving up\n");
+ return;
+ }
+
+ if (dialbuf->end == 0) {
+ warning("menu: redial: dialbuf is empty\n");
+ return;
+ }
+
+ dialbuf->pos = 0;
+ err = mbuf_strdup(dialbuf, &uri, dialbuf->end);
+ if (err)
+ return;
+
+ err = ua_connect(uag_cur(), NULL, NULL, uri, NULL, VIDMODE_ON);
+ if (err) {
+ warning("menu: redial: ua_connect failed (%m)\n", err);
+ }
+
+ mem_deref(uri);
+
+}
+
+
static void ua_event_handler(struct ua *ua, enum ua_event ev,
struct call *call, const char *prm, void *arg)
{
@@ -842,6 +890,10 @@ static void ua_event_handler(struct ua *ua, enum ua_event ev,
menu.play = mem_deref(menu.play);
alert_stop();
+
+ /* We must stop the re-dialing if the call was
+ established */
+ redial_reset();
break;
case UA_EVENT_CALL_CLOSED:
@@ -856,6 +908,35 @@ static void ua_event_handler(struct ua *ua, enum ua_event ev,
}
alert_stop();
+
+ /* Activate the re-dialing if:
+ *
+ * - redial_attempts must be enabled in config
+ * - the closed call must be of outgoing direction
+ * - the closed call must fail with special code 701
+ */
+ if (menu.redial_attempts) {
+
+ if (menu.current_attempts
+ ||
+ (call_is_outgoing(call) &&
+ call_scode(call) == 701)) {
+
+ info("menu: call closed"
+ " -- redialing in %u seconds\n",
+ menu.redial_delay);
+
+ ++menu.current_attempts;
+
+ tmr_start(&menu.tmr_redial,
+ menu.redial_delay*1000,
+ redial_handler, NULL);
+ }
+ else {
+ info("menu: call closed -- not redialing\n");
+ }
+ }
+
break;
case UA_EVENT_REGISTER_OK:
@@ -889,10 +970,31 @@ static void message_handler(const struct pl *peer, const struct pl *ctype,
static int module_init(void)
{
+ struct pl val;
int err;
+ /*
+ * Read the config values
+ */
conf_get_bool(conf_cur(), "menu_bell", &menu.bell);
+ if (0 == conf_get(conf_cur(), "redial_attempts", &val) &&
+ 0 == pl_strcasecmp(&val, "inf")) {
+ menu.redial_attempts = (uint32_t)-1;
+ }
+ else {
+ conf_get_u32(conf_cur(), "redial_attempts",
+ &menu.redial_attempts);
+ }
+ conf_get_u32(conf_cur(), "redial_delay", &menu.redial_delay);
+
+ if (menu.redial_attempts) {
+ info("menu: redial enabled with %u attempts and"
+ " %u seconds delay\n",
+ menu.redial_attempts,
+ menu.redial_delay);
+ }
+
dialbuf = mbuf_alloc(64);
if (!dialbuf)
return ENOMEM;
@@ -913,6 +1015,9 @@ static int module_init(void)
static int module_close(void)
{
+ debug("menu: close (redial current_attempts=%d)\n",
+ menu.current_attempts);
+
message_close();
uag_event_unregister(ua_event_handler);
cmd_unregister(cmdv);
@@ -926,6 +1031,8 @@ static int module_close(void)
menu.play = mem_deref(menu.play);
+ tmr_cancel(&menu.tmr_redial);
+
return 0;
}
diff --git a/src/call.c b/src/call.c
index 6d07639..9e949b3 100644
--- a/src/call.c
+++ b/src/call.c
@@ -78,6 +78,8 @@ struct call {
struct config_avt config_avt;
struct config_call config_call;
+
+ uint32_t rtp_timeout_ms; /**< RTP Timeout in [ms] */
};
@@ -420,6 +422,22 @@ static void menc_error_handler(int err, void *arg)
}
+static void stream_error_handler(struct stream *strm, int err, void *arg)
+{
+ struct call *call = arg;
+ MAGIC_CHECK(call);
+
+ info("call: error in \"%s\" rtp stream (%m)\n",
+ sdp_media_name(stream_sdpmedia(strm)), err);
+
+ call->scode = 701;
+ set_state(call, STATE_TERMINATED);
+
+ call_stream_stop(call);
+ call_event_handler(call, CALL_EVENT_CLOSED, "rtp stream error");
+}
+
+
/**
* Allocate a new Call state object
*
@@ -445,6 +463,7 @@ int call_alloc(struct call **callp, const struct config *cfg, struct list *lst,
call_event_h *eh, void *arg)
{
struct call *call;
+ struct le *le;
enum vidmode vidmode = prm ? prm->vidmode : VIDMODE_OFF;
bool use_video = true, got_offer = false;
int label = 0;
@@ -567,6 +586,15 @@ int call_alloc(struct call **callp, const struct config *cfg, struct list *lst,
call->not = mem_ref(xcall->not);
}
+ FOREACH_STREAM {
+ struct stream *strm = le->data;
+ stream_set_error_handler(strm, stream_error_handler, call);
+ }
+
+ if (cfg->avt.rtp_timeout) {
+ call_enable_rtp_timeout(call, cfg->avt.rtp_timeout*1000);
+ }
+
list_append(lst, &call->le, call);
out:
@@ -1033,6 +1061,16 @@ static void sipsess_estab_handler(const struct sip_msg *msg, void *arg)
call_stream_start(call, true);
+ if (call->rtp_timeout_ms) {
+
+ struct le *le;
+
+ FOREACH_STREAM {
+ struct stream *strm = le->data;
+ stream_enable_rtp_timeout(strm, call->rtp_timeout_ms);
+ }
+ }
+
/* the transferor will hangup this call */
if (call->not) {
(void)call_notify_sipfrag(call, 200, "OK");
@@ -1716,3 +1754,12 @@ bool call_is_outgoing(const struct call *call)
{
return call ? call->outgoing : false;
}
+
+
+void call_enable_rtp_timeout(struct call *call, uint32_t timeout_ms)
+{
+ if (!call)
+ return;
+
+ call->rtp_timeout_ms = timeout_ms;
+}
diff --git a/src/config.c b/src/config.c
index 77a4411..899575d 100644
--- a/src/config.c
+++ b/src/config.c
@@ -70,7 +70,8 @@ static struct config core_config = {
true,
false,
{5, 10},
- false
+ false,
+ 0
},
/* Network */
@@ -223,6 +224,7 @@ int config_parse_conf(struct config *cfg, const struct conf *conf)
(void)conf_get_range(conf, "jitter_buffer_delay",
&cfg->avt.jbuf_del);
(void)conf_get_bool(conf, "rtp_stats", &cfg->avt.rtp_stats);
+ (void)conf_get_u32(conf, "rtp_timeout", &cfg->avt.rtp_timeout);
if (err) {
warning("config: configure parse error (%m)\n", err);
@@ -289,6 +291,7 @@ int config_print(struct re_printf *pf, const struct config *cfg)
"rtcp_mux\t\t%s\n"
"jitter_buffer_delay\t%H\n"
"rtp_stats\t\t%s\n"
+ "rtp_timeout\t\t%u # in seconds\n"
"\n"
"# Network\n"
"net_interface\t\t%s\n"
@@ -327,6 +330,7 @@ int config_print(struct re_printf *pf, const struct config *cfg)
cfg->avt.rtcp_mux ? "yes" : "no",
range_print, &cfg->avt.jbuf_del,
cfg->avt.rtp_stats ? "yes" : "no",
+ cfg->avt.rtp_timeout,
cfg->net.ifname
@@ -465,6 +469,7 @@ static int core_config_template(struct re_printf *pf, const struct config *cfg)
"rtcp_mux\t\tno\n"
"jitter_buffer_delay\t%u-%u\t\t# frames\n"
"rtp_stats\t\tno\n"
+ "#rtp_timeout\t\t60\n"
"\n# Network\n"
"#dns_server\t\t10.0.0.1:53\n"
"#net_interface\t\t%H\n",
@@ -749,6 +754,11 @@ int config_write_template(const char *file, const struct config *cfg)
"ice_nomination\t\tregular\t# {regular,aggressive}\n"
"ice_mode\t\tfull\t# {full,lite}\n");
+ (void)re_fprintf(f,
+ "\n# Menu\n"
+ "#redial_attempts\t\t3 # Num or <inf>\n"
+ "#redial_delay\t\t5 # Delay in seconds\n");
+
if (f)
(void)fclose(f);
diff --git a/src/core.h b/src/core.h
index dca28b5..d6958b4 100644
--- a/src/core.h
+++ b/src/core.h
@@ -281,6 +281,9 @@ typedef void (stream_rtp_h)(const struct rtp_header *hdr, struct mbuf *mb,
void *arg);
typedef void (stream_rtcp_h)(struct rtcp_msg *msg, void *arg);
+typedef void (stream_error_h)(struct stream *strm, int err, void *arg);
+
+
/** Defines a generic media stream */
struct stream {
struct le le; /**< Linked list element */
@@ -307,6 +310,13 @@ struct stream {
stream_rtp_h *rtph; /**< Stream RTP handler */
stream_rtcp_h *rtcph; /**< Stream RTCP handler */
void *arg; /**< Handler argument */
+
+ stream_error_h *errorh;
+ void *errorh_arg;
+ struct tmr tmr_rtp;
+ uint64_t ts_last;
+ bool terminated;
+ uint32_t rtp_timeout_ms; /* [ms] */
};
int stream_alloc(struct stream **sp, const struct config_avt *cfg,
@@ -327,8 +337,11 @@ void stream_set_srate(struct stream *s, uint32_t srate_tx, uint32_t srate_rx);
void stream_send_fir(struct stream *s, bool pli);
void stream_reset(struct stream *s);
void stream_set_bw(struct stream *s, uint32_t bps);
+void stream_set_error_handler(struct stream *strm,
+ stream_error_h *errorh, void *arg);
int stream_debug(struct re_printf *pf, const struct stream *s);
int stream_print(struct re_printf *pf, const struct stream *s);
+void stream_enable_rtp_timeout(struct stream *strm, uint32_t timeout_ms);
/*
diff --git a/src/stream.c b/src/stream.c
index 9dc572f..9250fe2 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -12,9 +12,63 @@
enum {
RTP_RECV_SIZE = 8192,
+ RTP_CHECK_INTERVAL = 1000 /* how often to check for RTP [ms] */
};
+static void stream_close(struct stream *strm, int err)
+{
+ strm->terminated = true;
+
+ if (strm->errorh) {
+ strm->errorh(strm, err, strm->errorh_arg);
+ strm->errorh = NULL;
+ }
+}
+
+
+/* TODO: should we check RTP per stream, or should we have the
+ * check in struct call instead?
+ */
+static void check_rtp_handler(void *arg)
+{
+ struct stream *strm = arg;
+ const uint64_t now = tmr_jiffies();
+ int diff_ms;
+
+ tmr_start(&strm->tmr_rtp, RTP_CHECK_INTERVAL,
+ check_rtp_handler, strm);
+
+ /* If no RTP was received at all, check later */
+ if (!strm->ts_last)
+ return;
+
+ /* We are in sendrecv mode, check when the last RTP packet
+ * was received.
+ */
+ if (sdp_media_dir(strm->sdp) == SDP_SENDRECV) {
+
+ diff_ms = (int)(now - strm->ts_last);
+
+ debug("stream: last \"%s\" RTP packet: %d milliseconds\n",
+ sdp_media_name(strm->sdp), diff_ms);
+
+ if (diff_ms > (int)strm->rtp_timeout_ms) {
+
+ info("stream: no %s RTP packets received for"
+ " %d milliseconds\n",
+ sdp_media_name(strm->sdp), diff_ms);
+
+ stream_close(strm, ETIMEDOUT);
+ }
+ }
+ else {
+ re_printf("check_rtp: not checking (dir=%s)\n",
+ sdp_dir_name(sdp_media_dir(strm->sdp)));
+ }
+}
+
+
static inline int lostcalc(struct stream *s, uint16_t seq)
{
const uint16_t delta = seq - s->pseq;
@@ -79,6 +133,7 @@ static void stream_destructor(void *arg)
metric_reset(&s->metric_tx);
metric_reset(&s->metric_rx);
+ tmr_cancel(&s->tmr_rtp);
list_unlink(&s->le);
mem_deref(s->rtpkeep);
mem_deref(s->sdp);
@@ -98,6 +153,8 @@ static void rtp_recv(const struct sa *src, const struct rtp_header *hdr,
bool flush = false;
int err;
+ s->ts_last = tmr_jiffies();
+
if (!mbuf_get_left(mb))
return;
@@ -446,6 +503,9 @@ void stream_update(struct stream *s)
void stream_update_encoder(struct stream *s, int pt_enc)
{
+ if (!s)
+ return;
+
if (pt_enc >= 0)
s->pt_enc = pt_enc;
}
@@ -534,6 +594,37 @@ void stream_set_bw(struct stream *s, uint32_t bps)
}
+void stream_enable_rtp_timeout(struct stream *strm, uint32_t timeout_ms)
+{
+ if (!strm)
+ return;
+
+ strm->rtp_timeout_ms = timeout_ms;
+
+ tmr_cancel(&strm->tmr_rtp);
+
+ if (timeout_ms) {
+
+ info("stream: Enable RTP timeout (%u milliseconds)\n",
+ timeout_ms);
+
+ strm->ts_last = tmr_jiffies();
+ tmr_start(&strm->tmr_rtp, 10, check_rtp_handler, strm);
+ }
+}
+
+
+void stream_set_error_handler(struct stream *strm,
+ stream_error_h *errorh, void *arg)
+{
+ if (!strm)
+ return;
+
+ strm->errorh = errorh;
+ strm->errorh_arg = arg;
+}
+
+
int stream_debug(struct re_printf *pf, const struct stream *s)
{
struct sa rrtcp;
@@ -567,5 +658,3 @@ int stream_print(struct re_printf *pf, const struct stream *s)
s->metric_tx.cur_bitrate,
s->metric_rx.cur_bitrate);
}
-
-
diff --git a/test/call.c b/test/call.c
index 0c168ad..06c7bf3 100644
--- a/test/call.c
+++ b/test/call.c
@@ -20,7 +20,8 @@ enum behaviour {
enum action {
ACTION_RECANCEL = 0,
ACTION_HANGUP_A,
- ACTION_HANGUP_B
+ ACTION_HANGUP_B,
+ ACTION_NOTHING
};
struct agent {
@@ -164,6 +165,10 @@ static void event_handler(struct ua *ua, enum ua_event ev,
f->b.failed = true;
ua_hangup(f->b.ua, NULL, 0, 0);
break;
+
+ case ACTION_NOTHING:
+ /* Do nothing, wait */
+ break;
}
}
break;
@@ -364,3 +369,44 @@ int test_call_answer_hangup_b(void)
return err;
}
+
+
+int test_call_rtp_timeout(void)
+{
+#define RTP_TIMEOUT_MS 1
+ struct fixture fix, *f = &fix;
+ struct call *call;
+ int err = 0;
+
+ fixture_init(f);
+
+ f->behaviour = BEHAVIOUR_ANSWER;
+ f->estab_action = ACTION_NOTHING;
+
+ /* Make a call from A to B */
+ err = ua_connect(f->a.ua, 0, NULL, f->buri, NULL, VIDMODE_OFF);
+ TEST_ERR(err);
+
+ call = ua_call(f->a.ua);
+ ASSERT_TRUE(call != NULL);
+
+ call_enable_rtp_timeout(call, RTP_TIMEOUT_MS);
+
+ /* run main-loop with timeout, wait for events */
+ err = re_main_timeout(5000);
+ TEST_ERR(err);
+ TEST_ERR(fix.err);
+
+ ASSERT_EQ(1, fix.a.n_established);
+ ASSERT_EQ(1, fix.a.n_closed);
+ ASSERT_EQ(701, fix.a.close_scode); /* verify timeout */
+
+ ASSERT_EQ(1, fix.b.n_established);
+ ASSERT_EQ(1, fix.b.n_closed);
+ ASSERT_EQ(0, fix.b.close_scode);
+
+ out:
+ fixture_close(f);
+
+ return err;
+}
diff --git a/test/main.c b/test/main.c
index 061f8ff..d403084 100644
--- a/test/main.c
+++ b/test/main.c
@@ -24,6 +24,7 @@ static const struct test tests[] = {
TEST(test_call_answer_hangup_a),
TEST(test_call_answer_hangup_b),
TEST(test_call_reject),
+ TEST(test_call_rtp_timeout),
TEST(test_cmd),
TEST(test_cplusplus),
TEST(test_mos),
diff --git a/test/test.h b/test/test.h
index fe9cd4c..15629ba 100644
--- a/test/test.h
+++ b/test/test.h
@@ -102,6 +102,7 @@ int test_call_reject(void);
int test_call_af_mismatch(void);
int test_call_answer_hangup_a(void);
int test_call_answer_hangup_b(void);
+int test_call_rtp_timeout(void);
#ifdef __cplusplus