diff options
author | Alfred E. Heggestad <aeh@db.org> | 2016-07-19 18:37:38 +0200 |
---|---|---|
committer | Alfred E. Heggestad <aeh@db.org> | 2016-07-19 18:37:38 +0200 |
commit | 9da1970131662ad480dae589618411734dcf4905 (patch) | |
tree | 44577fcffff35352d432c3aa0c9b0dd69e33c1a0 /src | |
parent | e27e37b547b709d8ecf8be306c2b5137b7affad3 (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.
Diffstat (limited to 'src')
-rw-r--r-- | src/call.c | 47 | ||||
-rw-r--r-- | src/config.c | 12 | ||||
-rw-r--r-- | src/core.h | 13 | ||||
-rw-r--r-- | src/stream.c | 93 |
4 files changed, 162 insertions, 3 deletions
@@ -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); @@ -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); } - - |