Make SIP early media work more efficiently without so many reinvites
authorMark Spencer <markster@digium.com>
Tue, 9 May 2006 11:44:50 +0000 (11:44 +0000)
committerMark Spencer <markster@digium.com>
Tue, 9 May 2006 11:44:50 +0000 (11:44 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@26019 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_dial.c
channels/chan_sip.c
include/asterisk/rtp.h
rtp.c

index 6dddb85..7d97187 100644 (file)
@@ -482,7 +482,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_l
                                        ast_clear_flag(o, DIAL_STILLGOING);     
                                        HANDLE_CAUSE(cause, in);
                                } else {
-                                       ast_rtp_make_compatible(c, in);
+                                       ast_rtp_make_compatible(c, in, single);
                                        if (c->cid.cid_num)
                                                free(c->cid.cid_num);
                                        c->cid.cid_num = NULL;
@@ -550,6 +550,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_l
                                                               OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
                                                               OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
                                                               DIAL_NOFORWARDHTML);
+                                               /* Setup early media if appropriate */
+                                               ast_rtp_early_media(in, peer);
                                        }
                                        /* If call has been answered, then the eventual hangup is likely to be normal hangup */
                                        in->hangupcause = AST_CAUSE_NORMAL_CLEARING;
@@ -576,6 +578,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_l
                                case AST_CONTROL_RINGING:
                                        if (option_verbose > 2)
                                                ast_verbose(VERBOSE_PREFIX_3 "%s is ringing\n", c->name);
+                                       /* Setup early media if appropriate */
+                                       if (single)
+                                               ast_rtp_early_media(in, c);
                                        if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) {
                                                ast_indicate(in, AST_CONTROL_RINGING);
                                                (*sentringing)++;
@@ -584,6 +589,9 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_l
                                case AST_CONTROL_PROGRESS:
                                        if (option_verbose > 2)
                                                ast_verbose (VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", c->name, in->name);
+                                       /* Setup early media if appropriate */
+                                       if (single)
+                                               ast_rtp_early_media(in, c);
                                        if (!ast_test_flag(outgoing, OPT_RINGBACK))
                                                ast_indicate(in, AST_CONTROL_PROGRESS);
                                        break;
@@ -595,6 +603,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_l
                                case AST_CONTROL_PROCEEDING:
                                        if (option_verbose > 2)
                                                ast_verbose (VERBOSE_PREFIX_3 "%s is proceeding passing it to %s\n", c->name, in->name);
+                                       if (single)
+                                               ast_rtp_early_media(in, c);
                                        if (!ast_test_flag(outgoing, OPT_RINGBACK))
                                                ast_indicate(in, AST_CONTROL_PROCEEDING);
                                        break;
@@ -1056,7 +1066,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags
                }
 
                /* Setup outgoing SDP to match incoming one */
-               ast_rtp_make_compatible(tmp->chan, chan);
+               ast_rtp_make_compatible(tmp->chan, chan, !outgoing && !rest);
                
                /* Inherit specially named variables from parent channel */
                ast_channel_inherit_variables(chan, tmp->chan);
@@ -1550,6 +1560,7 @@ out:
                sentringing = 0;
                ast_indicate(chan, -1);
        }
+       ast_rtp_early_media(chan, NULL);
        hanguptree(outgoing, NULL);
        pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
        if (option_debug)
index 4d4a3e1..5881d2d 100644 (file)
@@ -13588,6 +13588,7 @@ static struct ast_rtp *sip_get_vrtp_peer(struct ast_channel *chan)
 static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active)
 {
        struct sip_pvt *p;
+       int changed = 0;
 
        p = chan->tech_pvt;
        if (!p) 
@@ -13598,17 +13599,23 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struc
                ast_mutex_unlock(&p->lock);
                return 0;
        }
-       if (rtp)
-               ast_rtp_get_peer(rtp, &p->redirip);
+       if (rtp) 
+               changed |= ast_rtp_get_peer(rtp, &p->redirip);
        else
                memset(&p->redirip, 0, sizeof(p->redirip));
        if (vrtp)
-               ast_rtp_get_peer(vrtp, &p->vredirip);
+               changed |= ast_rtp_get_peer(vrtp, &p->vredirip);
        else
                memset(&p->vredirip, 0, sizeof(p->vredirip));
-       p->redircodecs = codecs;
-       if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
-               if (!p->pendinginvite) {
+       if (p->redircodecs != codecs) {
+               p->redircodecs = codecs;
+               changed = 1;
+       }
+       if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
+               if (chan->_state != AST_STATE_UP) {
+                               char iabuf[INET_ADDRSTRLEN];
+                               ast_log(LOG_DEBUG, "Early media setting SIP '%s' - Sending early media to %s\n", p->callid, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp ? p->redirip.sin_addr : p->ourip));
+               } else if (!p->pendinginvite) {
                        if (option_debug > 2) {
                                char iabuf[INET_ADDRSTRLEN];
                                ast_log(LOG_DEBUG, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp ? p->redirip.sin_addr : p->ourip));
index 3b19da0..1dc133c 100644 (file)
@@ -97,7 +97,8 @@ struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io
 
 void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
 
-void ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
+/* Copies from rtp to them and returns 1 if there was a change or 0 if it was already the same */
+int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
 
 void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us);
 
@@ -154,7 +155,9 @@ int ast_rtp_proto_register(struct ast_rtp_protocol *proto);
 
 void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto);
 
-int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src);
+int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, int media);
+
+int ast_rtp_early_media(struct ast_channel *dest, struct ast_channel *src);
 
 void ast_rtp_stop(struct ast_rtp *rtp);
 
diff --git a/rtp.c b/rtp.c
index 88aa1ee..b80fb30 100644 (file)
--- a/rtp.c
+++ b/rtp.c
@@ -733,11 +733,83 @@ static struct ast_rtp_protocol *get_proto(struct ast_channel *chan)
        return cur;
 }
 
-int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src)
+int ast_rtp_early_media(struct ast_channel *dest, struct ast_channel *src)
+{
+       struct ast_rtp *destp, *srcp=NULL;              /* Audio RTP Channels */
+       struct ast_rtp *vdestp, *vsrcp=NULL;            /* Video RTP channels */
+       struct ast_rtp_protocol *destpr, *srcpr=NULL;
+       int srccodec;
+       /* Lock channels */
+       ast_channel_lock(dest);
+       if (src) {
+               while(ast_channel_trylock(src)) {
+                       ast_channel_unlock(dest);
+                       usleep(1);
+                       ast_channel_lock(dest);
+               }
+       }
+
+       /* Find channel driver interfaces */
+       destpr = get_proto(dest);
+       if (src)
+               srcpr = get_proto(src);
+       if (!destpr) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Channel '%s' has no RTP, not doing anything\n", dest->name);
+               ast_channel_unlock(dest);
+               if (src)
+                       ast_channel_unlock(src);
+               return 0;
+       }
+       if (!srcpr) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Channel '%s' has no RTP, not doing anything\n", src->name);
+               ast_channel_unlock(dest);
+               if (src)
+                       ast_channel_unlock(src);
+               return 0;
+       }
+
+       /* Get audio and video interface (if native bridge is possible) */
+       destp = destpr->get_rtp_info(dest);
+       vdestp = (destpr->get_vrtp_info) ? destpr->get_vrtp_info(dest) : NULL;
+       if (srcpr) {
+               srcp = srcpr->get_rtp_info(src);
+               vsrcp = (srcpr->get_vrtp_info) ? srcpr->get_vrtp_info(src) : NULL;
+       }
+
+       /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
+       if (!destp) {
+               /* Somebody doesn't want to play... */
+               ast_channel_unlock(dest);
+               if (src)
+                       ast_channel_unlock(src);
+               return 0;
+       }
+       if (srcpr && srcpr->get_codec)
+               srccodec = srcpr->get_codec(src);
+       else
+               srccodec = 0;
+       /* Consider empty media as non-existant */
+       if (srcp && !srcp->them.sin_addr.s_addr)
+               srcp = NULL;
+       /* Bridge early media */
+       if (destpr->set_rtp_peer(dest, srcp, vsrcp, srccodec, srcp ? ast_test_flag(srcp, FLAG_NAT_ACTIVE) : 0))
+               ast_log(LOG_WARNING, "Channel '%s' failed to send early media to '%s'\n", dest->name, src ? src->name : "<unspecified>");
+       ast_channel_unlock(dest);
+       if (src)
+               ast_channel_unlock(src);
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Setting early  media SDP of '%s' with that of '%s'\n", dest->name, src ? src->name : "<unspecified>");
+       return 1;
+}
+
+int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, int media)
 {
        struct ast_rtp *destp, *srcp;           /* Audio RTP Channels */
        struct ast_rtp *vdestp, *vsrcp;         /* Video RTP channels */
        struct ast_rtp_protocol *destpr, *srcpr;
+       int srccodec;
        /* Lock channels */
        ast_channel_lock(dest);
        while(ast_channel_trylock(src)) {
@@ -780,6 +852,15 @@ int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src)
        ast_rtp_pt_copy(destp, srcp);
        if (vdestp && vsrcp)
                ast_rtp_pt_copy(vdestp, vsrcp);
+       if (srcpr->get_codec)
+               srccodec = srcpr->get_codec(src);
+       else
+               srccodec = 0;
+       if (media) {
+               /* Bridge early media */
+               if (destpr->set_rtp_peer(dest, srcp, vsrcp, srccodec, ast_test_flag(srcp, FLAG_NAT_ACTIVE)))
+                       ast_log(LOG_WARNING, "Channel '%s' failed to send early media to '%s'\n", dest->name, src->name);
+       }
        ast_channel_unlock(dest);
        ast_channel_unlock(src);
        if (option_debug)
@@ -1086,11 +1167,17 @@ void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
        rtp->rxseqno = 0;
 }
 
-void ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
+int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
 {
-       them->sin_family = AF_INET;
-       them->sin_port = rtp->them.sin_port;
-       them->sin_addr = rtp->them.sin_addr;
+       if ((them->sin_family != AF_INET) ||
+               (them->sin_port != rtp->them.sin_port) ||
+               (them->sin_addr.s_addr != rtp->them.sin_addr.s_addr)) {
+               them->sin_family = AF_INET;
+               them->sin_port = rtp->them.sin_port;
+               them->sin_addr = rtp->them.sin_addr;
+               return 1;
+       }
+       return 0;
 }
 
 void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us)