diff --git a/apps/app_dial.c b/apps/app_dial.c index 6dddb855b4bc1ea86b227adbe4bbb920ae349b1a..7d97187e3ab8c72c00fc7484db60b6bea869bf61 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -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) diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 4d4a3e1efa85cc0b93df004dac931546be5152cd..5881d2d96390723e10c1d4b9b858efd84e89838c 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -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)); diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h index 3b19da0cc7709f4d73f64a4e9cef88f7d0509227..1dc133cd4567b0e9bd3e617327a65e7a74e28095 100644 --- a/include/asterisk/rtp.h +++ b/include/asterisk/rtp.h @@ -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 88aa1eefd53e92ffeeccb32c7c0a5d1ae28a0bcb..b80fb306968a7e7a73e1115930eee33cfbdede02 100644 --- 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)