diff options
author | Olle Johansson <oej@edvina.net> | 2006-12-02 12:05:40 +0000 |
---|---|---|
committer | Olle Johansson <oej@edvina.net> | 2006-12-02 12:05:40 +0000 |
commit | c23bc46089970c9c7275b662bbd48b0ed7310fc6 (patch) | |
tree | 09ffb0a36d6c9a88e86937e5c2cff44de6ff208f | |
parent | eef9f7958bea41d443fb4e0698636459ab28446a (diff) |
- Disable RTP timeouts during T.38 transmission
- Encapsulate RTP timers to the RTP structure, so we have one set for video and one for audio
- Document RTP keepalive configuration option
- Cleanup and document the monitor support function to hangup on RTP timeouts
- Add RTP keepalive to SIP show settings
Imported from 1.4 with modifications for trunk.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@48200 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r-- | channels/chan_sip.c | 161 | ||||
-rw-r--r-- | configs/sip.conf.sample | 26 | ||||
-rw-r--r-- | include/asterisk/rtp.h | 15 | ||||
-rw-r--r-- | main/rtp.c | 52 |
4 files changed, 178 insertions, 76 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index e3f82edce..26fd403aa 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -961,8 +961,6 @@ struct sip_pvt { time_t lastrtprx; /*!< Last RTP received */ time_t lastrtptx; /*!< Last RTP sent */ int rtptimeout; /*!< RTP timeout time */ - int rtpholdtimeout; /*!< RTP timeout when on hold */ - int rtpkeepalive; /*!< Send RTP packets for keepalive */ struct sockaddr_in recv; /*!< Received as */ struct in_addr ourip; /*!< Our IP */ struct ast_channel *owner; /*!< Who owns us (if we have an owner) */ @@ -2726,17 +2724,21 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) if (dialog->rtp) { ast_rtp_setdtmf(dialog->rtp, ast_test_flag(&dialog->flags[0], SIP_DTMF) != SIP_DTMF_INFO); ast_rtp_setdtmfcompensate(dialog->rtp, ast_test_flag(&dialog->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); + ast_rtp_set_rtptimeout(dialog->rtp, peer->rtptimeout); + ast_rtp_set_rtpholdtimeout(dialog->rtp, peer->rtpholdtimeout); + ast_rtp_set_rtpkeepalive(dialog->rtp, peer->rtpkeepalive); + /* Set Frame packetization */ + ast_rtp_codec_setpref(dialog->rtp, &dialog->prefs); + dialog->autoframing = peer->autoframing; } if (dialog->vrtp) { ast_rtp_setdtmf(dialog->vrtp, 0); ast_rtp_setdtmfcompensate(dialog->vrtp, 0); + ast_rtp_set_rtptimeout(dialog->vrtp, peer->rtptimeout); + ast_rtp_set_rtpholdtimeout(dialog->vrtp, peer->rtpholdtimeout); + ast_rtp_set_rtpkeepalive(dialog->vrtp, peer->rtpkeepalive); } - /* Set Frame packetization */ - if (dialog->rtp) { - ast_rtp_codec_setpref(dialog->rtp, &dialog->prefs); - dialog->autoframing = peer->autoframing; - } ast_string_field_set(dialog, peername, peer->username); ast_string_field_set(dialog, authname, peer->username); ast_string_field_set(dialog, username, peer->username); @@ -2774,8 +2776,6 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) dialog->noncodeccapability &= ~AST_RTP_DTMF; ast_string_field_set(dialog, context, peer->context); dialog->rtptimeout = peer->rtptimeout; - dialog->rtpholdtimeout = peer->rtpholdtimeout; - dialog->rtpkeepalive = peer->rtpkeepalive; if (peer->call_limit) ast_set_flag(&dialog->flags[0], SIP_CALL_LIMIT); dialog->maxcallbitrate = peer->maxcallbitrate; @@ -4299,16 +4299,19 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) != SIP_DTMF_INFO); ast_rtp_setdtmfcompensate(p->rtp, ast_test_flag(&p->flags[1], SIP_PAGE2_RFC2833_COMPENSATE)); ast_rtp_settos(p->rtp, global_tos_audio); + ast_rtp_set_rtptimeout(p->rtp, global_rtptimeout); + ast_rtp_set_rtpholdtimeout(p->rtp, global_rtpholdtimeout); + ast_rtp_set_rtpkeepalive(p->rtp, global_rtpkeepalive); if (p->vrtp) { ast_rtp_settos(p->vrtp, global_tos_video); ast_rtp_setdtmf(p->vrtp, 0); ast_rtp_setdtmfcompensate(p->vrtp, 0); + ast_rtp_set_rtptimeout(p->vrtp, global_rtptimeout); + ast_rtp_set_rtpholdtimeout(p->vrtp, global_rtpholdtimeout); + ast_rtp_set_rtpkeepalive(p->vrtp, global_rtpkeepalive); } if (p->udptl) ast_udptl_settos(p->udptl, global_tos_audio); - p->rtptimeout = global_rtptimeout; - p->rtpholdtimeout = global_rtpholdtimeout; - p->rtpkeepalive = global_rtpkeepalive; p->maxcallbitrate = default_maxcallbitrate; } @@ -10427,6 +10430,7 @@ static int sip_show_settings(int fd, int argc, char *argv[]) ast_cli(fd, " T1 minimum: %d\n", global_t1min); ast_cli(fd, " Relax DTMF: %s\n", global_relaxdtmf ? "Yes" : "No"); ast_cli(fd, " Compact SIP headers: %s\n", compactheaders ? "Yes" : "No"); + ast_cli(fd, " RTP Keepalive: %d %s\n", global_rtpkeepalive, global_rtpkeepalive ? "" : "(Disabled)" ); ast_cli(fd, " RTP Timeout: %d %s\n", global_rtptimeout, global_rtptimeout ? "" : "(Disabled)" ); ast_cli(fd, " RTP Hold Timeout: %d %s\n", global_rtpholdtimeout, global_rtpholdtimeout ? "" : "(Disabled)"); ast_cli(fd, " MWI NOTIFY mime type: %s\n", default_notifymime); @@ -11823,6 +11827,9 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru if (bridgepvt->udptl) { if (p->t38.state == T38_PEER_REINVITE) { sip_handle_t38_reinvite(bridgepeer, p, 0); + ast_rtp_set_rtptimers_onhold(p->rtp); + if (p->vrtp) + ast_rtp_set_rtptimers_onhold(p->vrtp); /* Turn off RTP timers while we send fax */ } else if (p->t38.state == T38_DISABLED && bridgepeer && (bridgepvt->t38.state == T38_ENABLED)) { ast_log(LOG_WARNING, "RTP re-inivte after T38 session not handled yet !\n"); /* Insted of this we should somehow re-invite the other side of the bridge to RTP */ @@ -14912,52 +14919,66 @@ static int does_peer_need_mwi(struct sip_peer *peer) /*! \brief helper function for the monitoring thread */ -static void check_rtp_timeout(struct sip_pvt *sip, time_t t) -{ - if (sip->rtp && sip->owner && - (sip->owner->_state == AST_STATE_UP) && - !sip->redirip.sin_addr.s_addr) { - if (sip->lastrtptx && - sip->rtpkeepalive && - (t > sip->lastrtptx + sip->rtpkeepalive)) { - /* Need to send an empty RTP packet */ - sip->lastrtptx = time(NULL); - ast_rtp_sendcng(sip->rtp, 0); - } - if (sip->lastrtprx && - (sip->rtptimeout || sip->rtpholdtimeout) && - (t > sip->lastrtprx + sip->rtptimeout)) { - /* Might be a timeout now -- see if we're on hold */ - struct sockaddr_in sin; - ast_rtp_get_peer(sip->rtp, &sin); - if (sin.sin_addr.s_addr || - (sip->rtpholdtimeout && - (t > sip->lastrtprx + sip->rtpholdtimeout))) { - /* Needs a hangup */ - if (sip->rtptimeout) { - while (sip->owner && ast_channel_trylock(sip->owner)) { - sip_pvt_unlock(sip); - usleep(1); - sip_pvt_lock(sip); - } - if (sip->owner) { - if (!(ast_rtp_get_bridged(sip->rtp))) { - ast_log(LOG_NOTICE, - "Disconnecting call '%s' for lack of RTP activity in %ld seconds\n", - sip->owner->name, - (long) (t - sip->lastrtprx)); - /* Issue a softhangup */ - ast_softhangup_nolock(sip->owner, AST_SOFTHANGUP_DEV); - } else - ast_log(LOG_NOTICE, "'%s' will not be disconnected in %ld seconds because it is directly bridged to another RTP stream\n", sip->owner->name, (long) (t - sip->lastrtprx)); - ast_channel_unlock(sip->owner); - /* forget the timeouts for this call, since a hangup - has already been requested and we don't want to - repeatedly request hangups - */ - sip->rtptimeout = 0; - sip->rtpholdtimeout = 0; - } +static void check_rtp_timeout(struct sip_pvt *dialog, time_t t) +{ + /* If we have no RTP or no active owner, no need to check timers */ + if (!dialog->rtp || !dialog->owner) + return; + /* If the call is not in UP state or redirected outside Asterisk, no need to check timers */ + if (dialog->owner->_state != AST_STATE_UP || dialog->redirip.sin_addr.s_addr) + return; + + /* If we have no timers set, return now */ + if (ast_rtp_get_rtpkeepalive(dialog->rtp) == 0 || (ast_rtp_get_rtptimeout(dialog->rtp) == 0 && ast_rtp_get_rtpholdtimeout(dialog->rtp) == 0)) + return; + + /* Check AUDIO RTP keepalives */ + if (dialog->lastrtptx && ast_rtp_get_rtpkeepalive(dialog->rtp) && + (t > dialog->lastrtptx + ast_rtp_get_rtpkeepalive(dialog->rtp))) { + /* Need to send an empty RTP packet */ + dialog->lastrtptx = time(NULL); + ast_rtp_sendcng(dialog->rtp, 0); + } + + /*! \todo Check video RTP keepalives + + Do we need to move the lastrtptx to the RTP structure to have one for audio and one + for video? It really does belong to the RTP structure. + */ + + /* Check AUDIO RTP timers */ + if (dialog->lastrtprx && (ast_rtp_get_rtptimeout(dialog->rtp) || ast_rtp_get_rtpholdtimeout(dialog->rtp)) && + (t > dialog->lastrtprx + ast_rtp_get_rtptimeout(dialog->rtp))) { + + /* Might be a timeout now -- see if we're on hold */ + struct sockaddr_in sin; + ast_rtp_get_peer(dialog->rtp, &sin); + if (sin.sin_addr.s_addr || (ast_rtp_get_rtpholdtimeout(dialog->rtp) && + (t > dialog->lastrtprx + ast_rtp_get_rtpholdtimeout(dialog->rtp)))) { + /* Needs a hangup */ + if (dialog->rtptimeout) { + while (dialog->owner && ast_channel_trylock(dialog->owner)) { + sip_pvt_unlock(dialog); + usleep(1); + sip_pvt_lock(dialog); + } + if (!(ast_rtp_get_bridged(dialog->rtp))) { + ast_log(LOG_NOTICE, "Disconnecting call '%s' for lack of RTP activity in %ld seconds\n", + dialog->owner->name, (long) (t - dialog->lastrtprx)); + /* Issue a softhangup */ + ast_softhangup_nolock(dialog->owner, AST_SOFTHANGUP_DEV); + } else + ast_log(LOG_NOTICE, "'%s' will not be disconnected in %ld seconds because it is directly bridged to another RTP stream\n", dialog->owner->name, (long) (t - dialog->lastrtprx)); + ast_channel_unlock(dialog->owner); + /* forget the timeouts for this call, since a hangup + has already been requested and we don't want to + repeatedly request hangups + */ + ast_rtp_set_rtptimeout(dialog->rtp, 0); + ast_rtp_set_rtpholdtimeout(dialog->rtp, 0); + if (dialog->vrtp) { + ast_rtp_set_rtptimeout(dialog->vrtp, 0); + ast_rtp_set_rtpholdtimeout(dialog->vrtp, 0); } } } @@ -14971,7 +14992,7 @@ static void check_rtp_timeout(struct sip_pvt *sip, time_t t) static void *do_monitor(void *data) { int res; - struct sip_pvt *sip; + struct sip_pvt *dialog; struct sip_peer *peer = NULL; time_t t; int fastrestart = FALSE; @@ -14999,6 +15020,7 @@ static void *do_monitor(void *data) if (sipsock > -1) sipsock_read_id = ast_io_change(io, sipsock_read_id, sipsock, NULL, 0, NULL); } + /* Check for dialogs needing to be killed */ dialoglist_lock(); restartsearch: @@ -15007,18 +15029,20 @@ restartsearch: of time since the last time we did it (when MWI is being sent, we can get back to this point every millisecond or less) */ - for (sip = dialoglist; !fastrestart && sip; sip = sip->next) { - sip_pvt_lock(sip); + for (dialog = dialoglist; !fastrestart && dialog; dialog = dialog->next) { + sip_pvt_lock(dialog); /* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */ - check_rtp_timeout(sip, t); + check_rtp_timeout(dialog, t); /* If we have sessions that needs to be destroyed, do it now */ - if (ast_test_flag(&sip->flags[0], SIP_NEEDDESTROY) && !sip->packets && - !sip->owner) { - sip_pvt_unlock(sip); - __sip_destroy(sip, TRUE, FALSE); + /* Check if we have outstanding requests not responsed to or an active call + - if that's the case, wait with destruction */ + if (ast_test_flag(&dialog->flags[0], SIP_NEEDDESTROY) && !dialog->packets && + !dialog->owner) { + sip_pvt_unlock(dialog); + __sip_destroy(dialog, TRUE, FALSE); goto restartsearch; } - sip_pvt_unlock(sip); + sip_pvt_unlock(dialog); } dialoglist_unlock(); @@ -15027,7 +15051,8 @@ restartsearch: res = ast_sched_wait(sched); if ((res < 0) || (res > 1000)) res = 1000; - /* If we might need to send more mailboxes, don't wait long at all.*/ + + /* If we might need to send more mailbox notifications, don't wait long at all.*/ if (fastrestart) res = 1; res = ast_io_wait(io, res); diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index f706f2585..f4ba7e496 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -95,12 +95,6 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ;language=en ; Default language setting for all users/peers ; This may also be set for individual users/peers ;relaxdtmf=yes ; Relax dtmf handling -;rtptimeout=60 ; Terminate call if 60 seconds of no RTP or RTCP activity - ; when we're not on hold. This is to be able to hangup - ; a call in the case of a phone disappearing from the net, - ; like a powerloss or grandma tripping over a cable. -;rtpholdtimeout=300 ; Terminate call if 300 seconds of no RTP or RTCP activity - ; when we're on hold (must be > rtptimeout) ;trustrpid = no ; If Remote-Party-ID should be trusted ;sendrpid = yes ; If Remote-Party-ID should be sent ;progressinband=never ; If we should generate in-band ringing always @@ -162,6 +156,21 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; ;regcontext=sipregistrations ; +;--------------------------- RTP timers ---------------------------------------------------- +; These timers are currently used for both audio and video streams. The RTP timeouts +; are only applied to the audio channel. +; The settings are settable in the global section as well as per device +; +;rtptimeout=60 ; Terminate call if 60 seconds of no RTP or RTCP activity + ; on the audio channel + ; when we're not on hold. This is to be able to hangup + ; a call in the case of a phone disappearing from the net, + ; like a powerloss or grandma tripping over a cable. +;rtpholdtimeout=300 ; Terminate call if 300 seconds of no RTP or RTCP activity + ; on the audio channel + ; when we're on hold (must be > rtptimeout) +;rtpkeepalive=<secs> ; Send keepalives in the RTP stream to keep NAT open + ; (default is off - zero) ;--------------------------- SIP DEBUGGING --------------------------------------------------- ;sipdebug = yes ; Turn on SIP debugging by default, from ; the moment the channel loads this configuration @@ -206,8 +215,9 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ; ; This setting is available in the [general] section as well as in device configurations. ; Setting this to yes, enables T.38 fax (UDPTL) passthrough on SIP to SIP calls, provided -; both parties have T38 support enabled in their Asterisk configuration (either general or -; peer/user/friend sections) +; both parties have T38 support enabled in their Asterisk configuration +; This has to be enabled in the general section for all devices to work. You can then +; disable it on a per device basis. ; ; t38pt_udptl = yes ; Default false ; diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h index aab598a31..f7c1718f9 100644 --- a/include/asterisk/rtp.h +++ b/include/asterisk/rtp.h @@ -219,6 +219,21 @@ struct ast_codec_pref *ast_rtp_codec_getpref(struct ast_rtp *rtp); int ast_rtp_codec_getformat(int pt); +/*! \brief Set rtp timeout */ +void ast_rtp_set_rtptimeout(struct ast_rtp *rtp, int timeout); +/*! \brief Set rtp hold timeout */ +void ast_rtp_set_rtpholdtimeout(struct ast_rtp *rtp, int timeout); +/*! \brief set RTP keepalive interval */ +void ast_rtp_set_rtpkeepalive(struct ast_rtp *rtp, int period); +/*! \brief Get RTP keepalive interval */ +int ast_rtp_get_rtpkeepalive(struct ast_rtp *rtp); +/*! \brief Get rtp hold timeout */ +int ast_rtp_get_rtpholdtimeout(struct ast_rtp *rtp); +/*! \brief Get rtp timeout */ +int ast_rtp_get_rtptimeout(struct ast_rtp *rtp); +/* \brief Put RTP timeout timers on hold during another transaction, like T.38 */ +void ast_rtp_set_rtptimers_onhold(struct ast_rtp *rtp); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/main/rtp.c b/main/rtp.c index 3d394a043..93c92a845 100644 --- a/main/rtp.c +++ b/main/rtp.c @@ -128,6 +128,11 @@ struct ast_rtp { double rxtransit; /*!< Relative transit time for previous packet */ int lasttxformat; int lastrxformat; + + int rtptimeout; /*!< RTP timeout time (negative or zero means disabled, negative value means temporarily disabled) */ + int rtpholdtimeout; /*!< RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */ + int rtpkeepalive; /*!< Send RTP comfort noice packets for keepalive */ + /* DTMF Reception Variables */ char resp; unsigned int lasteventendseqn; @@ -521,6 +526,53 @@ unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp) return interval; } +/* \brief Put RTP timeout timers on hold during another transaction, like T.38 */ +void ast_rtp_set_rtptimers_onhold(struct ast_rtp *rtp) +{ + rtp->rtptimeout = (-1) * rtp->rtptimeout; + rtp->rtpholdtimeout = (-1) * rtp->rtpholdtimeout; +} + +/*! \brief Set rtp timeout */ +void ast_rtp_set_rtptimeout(struct ast_rtp *rtp, int timeout) +{ + rtp->rtptimeout = timeout; +} + +/*! \brief Set rtp hold timeout */ +void ast_rtp_set_rtpholdtimeout(struct ast_rtp *rtp, int timeout) +{ + rtp->rtpholdtimeout = timeout; +} + +/*! \brief set RTP keepalive interval */ +void ast_rtp_set_rtpkeepalive(struct ast_rtp *rtp, int period) +{ + rtp->rtpkeepalive = period; +} + +/*! \brief Get rtp timeout */ +int ast_rtp_get_rtptimeout(struct ast_rtp *rtp) +{ + if (rtp->rtptimeout < 0) /* We're not checking, but remembering the setting (during T.38 transmission) */ + return 0; + return rtp->rtptimeout; +} + +/*! \brief Get rtp hold timeout */ +int ast_rtp_get_rtpholdtimeout(struct ast_rtp *rtp) +{ + if (rtp->rtptimeout < 0) /* We're not checking, but remembering the setting (during T.38 transmission) */ + return 0; + return rtp->rtpholdtimeout; +} + +/*! \brief Get RTP keepalive interval */ +int ast_rtp_get_rtpkeepalive(struct ast_rtp *rtp) +{ + return rtp->rtpkeepalive; +} + void ast_rtp_set_data(struct ast_rtp *rtp, void *data) { rtp->data = data; |