--- channels/chan_sip.c.orig 2011-10-17 19:46:26.000000000 +0200 +++ channels/chan_sip.c 2011-12-09 11:48:43.876869404 +0100 @@ -709,6 +709,7 @@ static int global_relaxdtmf; /*!< Relax DTMF */ static int global_prematuremediafilter; /*!< Enable/disable premature frames in a call (causing 183 early media) */ +static int global_rtpprodhack; /*!< Prod an RTP stream immediately to get things going */ static int global_rtptimeout; /*!< Time out call if no RTP */ static int global_rtpholdtimeout; /*!< Time out call if no RTP during hold */ static int global_rtpkeepalive; /*!< Send RTP keepalives */ @@ -1321,6 +1322,8 @@ static struct sip_auth *find_realm_authentication(struct sip_auth_container *credentials, const char *realm); /*--- Misc functions */ +static void _sip_rtp_prod(const char *function, struct sip_pvt *p); +#define sip_rtp_prod(p) _sip_rtp_prod(__PRETTY_FUNCTION__, p) static void check_rtp_timeout(struct sip_pvt *dialog, time_t t); static int reload_config(enum channelreloadreason reason); static int expire_register(const void *data); @@ -6325,6 +6328,7 @@ ast_rtp_instance_update_source(p->rtp); res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, FALSE, TRUE); ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); + sip_rtp_prod(p); /* explicitly sip_pvt_lock'ed */ } sip_pvt_unlock(p); return res; @@ -6694,6 +6698,7 @@ p->invitestate = INV_EARLY_MEDIA; transmit_provisional_response(p, "183 Session Progress", &p->initreq, TRUE); ast_set_flag(&p->flags[0], SIP_PROGRESS_SENT); + sip_rtp_prod(p); /* explicitly sip_pvt_lock'ed */ break; } res = -1; @@ -7292,6 +7297,19 @@ return fr; } +/*! \brief Poke media session. This is sometimes required to break through a + * stand off where both parties are waiting for the other one to send. + * This is especially an issue when both: + * (a) we're behind NAT and our peer is not, and + * (b) we're an asterisk and we like to wait for incoming RTP before replying + */ +static void _sip_rtp_prod(char const *function, struct sip_pvt *dialog) +{ + if (global_rtpprodhack) { + ast_verb(3, "Prodding channel '%s' RTP (triggered by %s)\n", dialog->owner->name, function); + ast_rtp_instance_sendcng(dialog->rtp, 0); + } +} /*! \brief Generate 32 byte random string for callid's etc */ static char *generate_random_string(char *buf, size_t size) @@ -19520,6 +19538,7 @@ ast_queue_control(p->owner, AST_CONTROL_RINGING); } } + sip_rtp_prod(p); /* sip_pvt_locked in handle_request_do */ check_pendings(p); break; @@ -19642,6 +19661,7 @@ p->invitestate = INV_TERMINATED; ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED); xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE); + sip_rtp_prod(p); /* sip_pvt_locked in handle_request_do */ check_pendings(p); break; @@ -25037,8 +25057,8 @@ /* 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 the call is not in UP state or redirected outside Asterisk, no need to check timers */ if (dialog->owner->_state != AST_STATE_UP || !ast_sockaddr_isnull(&dialog->redirip)) return; @@ -27371,6 +27391,7 @@ sip_cfg.allowguest = DEFAULT_ALLOWGUEST; global_callcounter = DEFAULT_CALLCOUNTER; global_match_auth_username = FALSE; /*!< Match auth username if available instead of From: Default off. */ + global_rtpprodhack = 0; global_rtptimeout = 0; global_rtpholdtimeout = 0; global_rtpkeepalive = DEFAULT_RTPKEEPALIVE; @@ -27561,6 +27582,8 @@ global_relaxdtmf = ast_true(v->value); } else if (!strcasecmp(v->name, "vmexten")) { ast_copy_string(default_vmexten, v->value, sizeof(default_vmexten)); + } else if (!strcasecmp(v->name, "rtpprodhack")) { + global_rtpprodhack = ast_true(v->value); } else if (!strcasecmp(v->name, "rtptimeout")) { if ((sscanf(v->value, "%30d", &global_rtptimeout) != 1) || (global_rtptimeout < 0)) { ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d. Using default.\n", v->value, v->lineno);