aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfile <file@f38db490-d61c-443f-a65b-d21fe96a405b>2006-08-28 17:37:56 +0000
committerfile <file@f38db490-d61c-443f-a65b-d21fe96a405b>2006-08-28 17:37:56 +0000
commit77be2d9b78060f758181a608029fba1b782fd44a (patch)
tree8d4c60d4b38470222d45faab0253b21508f6a10a
parent320436acc4a3b4685570a96fc771979ebdc6c9d4 (diff)
Merge in RTP-level packet bridging. Packet comes in, packet goes out - that's what RTP-level packet bridging is all about!
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@41235 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--channels/chan_mgcp.c18
-rw-r--r--channels/chan_sip.c59
-rw-r--r--channels/chan_skinny.c32
-rw-r--r--include/asterisk/rtp.h15
-rw-r--r--main/rtp.c559
5 files changed, 444 insertions, 239 deletions
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 94cb20f02..c77b8a61e 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -3876,13 +3876,19 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
return (gw_reload ? NULL : gw);
}
-static struct ast_rtp *mgcp_get_rtp_peer(struct ast_channel *chan)
+static enum ast_rtp_get_result mgcp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
{
- struct mgcp_subchannel *sub;
- sub = chan->tech_pvt;
- if (sub && sub->rtp && sub->parent->canreinvite)
- return sub->rtp;
- return NULL;
+ struct mgcp_subchannel *sub = NULL;
+
+ if (!(sub = chan->tech_pvt) || !(sub->rtp))
+ return AST_RTP_GET_FAILED;
+
+ *rtp = sub->rtp;
+
+ if (sub->parent->canreinvite)
+ return AST_RTP_TRY_NATIVE;
+ else
+ return AST_RTP_TRY_PARTIAL;
}
static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active)
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 7703530b8..5508405ad 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1479,8 +1479,8 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
/*----- RTP interface functions */
static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active);
-static struct ast_rtp *sip_get_rtp_peer(struct ast_channel *chan);
-static struct ast_rtp *sip_get_vrtp_peer(struct ast_channel *chan);
+static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp);
+static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp);
static int sip_get_codec(struct ast_channel *chan);
static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p, int *faxdetect);
@@ -16051,34 +16051,53 @@ static int sip_handle_t38_reinvite(struct ast_channel *chan, struct sip_pvt *pvt
/*! \brief Returns null if we can't reinvite audio (part of RTP interface) */
-static struct ast_rtp *sip_get_rtp_peer(struct ast_channel *chan)
+static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
{
- struct sip_pvt *p;
- struct ast_rtp *rtp = NULL;
- p = chan->tech_pvt;
- if (!p)
- return NULL;
+ struct sip_pvt *p = NULL;
+ enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL;
+
+ if (!(p = chan->tech_pvt))
+ return AST_RTP_GET_FAILED;
+
ast_mutex_lock(&p->lock);
- if (p->rtp && ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
- rtp = p->rtp;
+ if (!(p->rtp)) {
+ ast_mutex_unlock(&p->lock);
+ return AST_RTP_GET_FAILED;
+ }
+
+ *rtp = p->rtp;
+
+ if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
+ res = AST_RTP_TRY_NATIVE;
+
ast_mutex_unlock(&p->lock);
- return rtp;
+
+ return res;
}
/*! \brief Returns null if we can't reinvite video (part of RTP interface) */
-static struct ast_rtp *sip_get_vrtp_peer(struct ast_channel *chan)
+static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
{
- struct sip_pvt *p;
- struct ast_rtp *rtp = NULL;
- p = chan->tech_pvt;
- if (!p)
- return NULL;
+ struct sip_pvt *p = NULL;
+ enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL;
+
+ if (!(p = chan->tech_pvt))
+ return AST_RTP_GET_FAILED;
ast_mutex_lock(&p->lock);
- if (p->vrtp && ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
- rtp = p->vrtp;
+ if (!(p->rtp)) {
+ ast_mutex_unlock(&p->lock);
+ return AST_RTP_GET_FAILED;
+ }
+
+ *rtp = p->vrtp;
+
+ if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
+ res = AST_RTP_TRY_NATIVE;
+
ast_mutex_unlock(&p->lock);
- return rtp;
+
+ return res;
}
/*! \brief Set the RTP peer for this call */
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index acd6ad951..b05fabb9b 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -1591,24 +1591,28 @@ static void do_housekeeping(struct skinnysession *s)
/* I do not believe skinny can deal with video.
Anyone know differently? */
/* Yes, it can. Currently 7985 and Cisco VT Advantage do video. */
-static struct ast_rtp *skinny_get_vrtp_peer(struct ast_channel *c)
+static enum ast_rtp_get_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
{
- struct skinny_subchannel *sub;
- sub = c->tech_pvt;
- if (sub && sub->vrtp) {
- return sub->vrtp;
- }
- return NULL;
+ struct skinny_subchannel *sub = NULL;
+
+ if (!(sub = c->tech_pvt) || !(sub->vrtp))
+ return AST_RTP_GET_FAILED;
+
+ *rtp = sub->vrtp;
+
+ return AST_RTP_TRY_NATIVE;
}
-static struct ast_rtp *skinny_get_rtp_peer(struct ast_channel *c)
+static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
{
- struct skinny_subchannel *sub;
- sub = c->tech_pvt;
- if (sub && sub->rtp) {
- return sub->rtp;
- }
- return NULL;
+ struct skinny_subchannel *sub = NULL;
+
+ if (!(sub = c->tech_pvt) || !(sub->rtp))
+ return AST_RTP_GET_FAILED;
+
+ *rtp = sub->rtp;
+
+ return AST_RTP_TRY_NATIVE;
}
static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active)
diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h
index bc539cb29..cdc81fd77 100644
--- a/include/asterisk/rtp.h
+++ b/include/asterisk/rtp.h
@@ -54,11 +54,22 @@ enum ast_rtp_options {
AST_RTP_OPT_G726_NONSTANDARD = (1 << 0),
};
+enum ast_rtp_get_result {
+ /*! Failed to find the RTP structure */
+ AST_RTP_GET_FAILED = 0,
+ /*! RTP structure exists but true native bridge can not occur so try partial */
+ AST_RTP_TRY_PARTIAL,
+ /*! RTP structure exists and native bridge can occur */
+ AST_RTP_TRY_NATIVE,
+};
+
+struct ast_rtp;
+
struct ast_rtp_protocol {
/*! Get RTP struct, or NULL if unwilling to transfer */
- struct ast_rtp *(* const get_rtp_info)(struct ast_channel *chan);
+ enum ast_rtp_get_result (* const get_rtp_info)(struct ast_channel *chan, struct ast_rtp **rtp);
/*! Get RTP struct, or NULL if unwilling to transfer */
- struct ast_rtp *(* const get_vrtp_info)(struct ast_channel *chan);
+ enum ast_rtp_get_result (* const get_vrtp_info)(struct ast_channel *chan, struct ast_rtp **rtp);
/*! Set RTP peer */
int (* const set_rtp_peer)(struct ast_channel *chan, struct ast_rtp *peer, struct ast_rtp *vpeer, int codecs, int nat_active);
int (* const get_codec)(struct ast_channel *chan);
diff --git a/main/rtp.c b/main/rtp.c
index 373eb102d..4fdde194c 100644
--- a/main/rtp.c
+++ b/main/rtp.c
@@ -155,6 +155,7 @@ struct ast_rtp {
int rtp_lookup_code_cache_code;
int rtp_lookup_code_cache_result;
struct ast_rtcp *rtcp;
+ struct ast_rtp *bridged; /*!< Who we are Packet briged to */
};
/* Forward declarations */
@@ -169,6 +170,8 @@ static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp);
#define FLAG_NAT_INACTIVE (0 << 1)
#define FLAG_NAT_INACTIVE_NOWARN (1 << 1)
#define FLAG_HAS_DTMF (1 << 3)
+#define FLAG_P2P_SENT_MARK (1 << 4)
+#define FLAG_P2P_NEED_DTMF (1 << 5)
/*!
* \brief Structure defining an RTCP session.
@@ -926,6 +929,63 @@ static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int t
rtp->rtcp->minrxjitter = rtp->rxjitter;
}
+/*! \brief Perform a Packet2Packet write */
+static int bridge_p2p_write(struct ast_rtp *rtp, unsigned int *rtpheader, int len, int hdrlen)
+{
+ struct ast_rtp *bridged = rtp->bridged;
+ int res = 0, payload = 0, bridged_payload = 0, version, padding, mark, ext;
+ struct rtpPayloadType rtpPT;
+ unsigned int seqno;
+
+ /* Get fields from packet */
+ seqno = ntohl(rtpheader[0]);
+ version = (seqno & 0xC0000000) >> 30;
+ payload = (seqno & 0x7f0000) >> 16;
+ padding = seqno & (1 << 29);
+ mark = seqno & (1 << 23);
+ ext = seqno & (1 << 28);
+ seqno &= 0xffff;
+
+ /* Check what the payload value should be */
+ rtpPT = ast_rtp_lookup_pt(rtp, payload);
+
+ /* If the payload is DTMF, and we are listening for DTMF - then feed it into the core */
+ if (!rtpPT.isAstFormat && rtpPT.code == AST_RTP_DTMF)
+ return -1;
+
+ /* Otherwise adjust bridged payload to match */
+ bridged_payload = ast_rtp_lookup_code(bridged, rtpPT.isAstFormat, rtpPT.code);
+
+ /* If the mark bit has not been sent yet... do it now */
+ if (!ast_test_flag(rtp, FLAG_P2P_SENT_MARK)) {
+ mark = 1;
+ ast_set_flag(rtp, FLAG_P2P_SENT_MARK);
+ }
+
+ /* Reconstruct part of the packet */
+ rtpheader[0] = htonl((version << 30) | (mark << 23) | (bridged_payload << 16) | (seqno));
+
+ if (bridged->them.sin_port && bridged->them.sin_addr.s_addr) {
+ res = sendto(bridged->s, (void *)rtpheader, len, 0, (struct sockaddr *)&bridged->them, sizeof(bridged->them));
+ if (res < 0) {
+ if (!bridged->nat || (bridged->nat && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
+ ast_log(LOG_DEBUG, "RTP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), strerror(errno));
+ } else if ((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) {
+ if (option_debug || rtpdebug)
+ ast_log(LOG_DEBUG, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port));
+ ast_set_flag(bridged, FLAG_NAT_INACTIVE_NOWARN);
+ }
+ return -1;
+ } else {
+ if (rtp_debug_test_addr(&bridged->them))
+ ast_verbose("Sent RTP P2P packet to %s:%d (type %-2.2d, len %-6.6u)\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), bridged_payload, len - hdrlen);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
{
int res;
@@ -964,6 +1024,11 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
return &ast_null_frame;
}
+ /* If we are P2P bridged to another channel, and the write is a success - then return a null frame and not the actual data */
+ if (rtp->bridged && !bridge_p2p_write(rtp, rtpheader, res, hdrlen)) {
+ return &ast_null_frame;
+ }
+
/* Get fields */
seqno = ntohl(rtpheader[0]);
@@ -1285,9 +1350,11 @@ static struct ast_rtp_protocol *get_proto(struct ast_channel *chan)
int ast_rtp_early_bridge(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;
+ struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */
+ struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */
+ struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL;
+ enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED;
+ enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED;
int srccodec;
/* Lock channels */
@@ -1322,27 +1389,27 @@ int ast_rtp_early_bridge(struct ast_channel *dest, struct ast_channel *src)
}
/* 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;
+ audio_dest_res = destpr->get_rtp_info(dest, &destp);
+ video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(dest, &vdestp) : AST_RTP_GET_FAILED;
if (srcpr) {
- srcp = srcpr->get_rtp_info(src);
- vsrcp = (srcpr->get_vrtp_info) ? srcpr->get_vrtp_info(src) : NULL;
+ audio_src_res = srcpr->get_rtp_info(src, &srcp);
+ video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(src, &vsrcp) : AST_RTP_GET_FAILED;
}
/* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
- if (!destp) {
+ if (audio_dest_res != AST_RTP_TRY_NATIVE) {
/* Somebody doesn't want to play... */
ast_channel_unlock(dest);
if (src)
ast_channel_unlock(src);
return 0;
}
- if (srcpr && srcpr->get_codec)
+ if (audio_src_res == AST_RTP_TRY_NATIVE && 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)
+ if (audio_src_res == AST_RTP_TRY_NATIVE && !srcp->them.sin_addr.s_addr)
srcp = NULL;
/* Bridge media early */
if (destpr->set_rtp_peer(dest, srcp, vsrcp, srccodec, srcp ? ast_test_flag(srcp, FLAG_NAT_ACTIVE) : 0))
@@ -1357,10 +1424,13 @@ int ast_rtp_early_bridge(struct ast_channel *dest, struct ast_channel *src)
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;
+ struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */
+ struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */
+ struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL;
+ enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED;
+ enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED;
int srccodec;
+
/* Lock channels */
ast_channel_lock(dest);
while(ast_channel_trylock(src)) {
@@ -1370,16 +1440,14 @@ int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, i
}
/* Find channel driver interfaces */
- destpr = get_proto(dest);
- srcpr = get_proto(src);
- if (!destpr) {
+ if (!(destpr = get_proto(dest))) {
if (option_debug)
ast_log(LOG_DEBUG, "Channel '%s' has no RTP, not doing anything\n", dest->name);
ast_channel_unlock(dest);
ast_channel_unlock(src);
return 0;
}
- if (!srcpr) {
+ if (!(srcpr = get_proto(src))) {
if (option_debug)
ast_log(LOG_DEBUG, "Channel '%s' has no RTP, not doing anything\n", src->name);
ast_channel_unlock(dest);
@@ -1388,13 +1456,13 @@ int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, i
}
/* 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;
- srcp = srcpr->get_rtp_info(src);
- vsrcp = (srcpr->get_vrtp_info) ? srcpr->get_vrtp_info(src) : NULL;
+ audio_dest_res = destpr->get_rtp_info(dest, &destp);
+ video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(dest, &vdestp) : AST_RTP_GET_FAILED;
+ audio_src_res = srcpr->get_rtp_info(src, &srcp);
+ video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(src, &vsrcp) : AST_RTP_GET_FAILED;
/* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
- if (!destp || !srcp) {
+ if (audio_dest_res != AST_RTP_TRY_NATIVE || audio_src_res != AST_RTP_TRY_NATIVE) {
/* Somebody doesn't want to play... */
ast_channel_unlock(dest);
ast_channel_unlock(src);
@@ -1766,6 +1834,8 @@ void ast_rtp_stop(struct ast_rtp *rtp)
memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->rtcp->them.sin_addr));
memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->rtcp->them.sin_port));
}
+
+ ast_clear_flag(rtp, FLAG_P2P_SENT_MARK);
}
void ast_rtp_reset(struct ast_rtp *rtp)
@@ -2479,182 +2549,92 @@ int ast_rtp_proto_register(struct ast_rtp_protocol *proto)
return 0;
}
-/*! \brief Bridge calls. If possible and allowed, initiate
- re-invite so the peers exchange media directly outside
- of Asterisk. */
-enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
+/*! \brief Bridge loop for true native bridge (reinvite) */
+static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, struct ast_rtp *vp0, struct ast_rtp *vp1, struct ast_rtp_protocol *pr0, struct ast_rtp_protocol *pr1, int codec0, int codec1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1)
{
- struct ast_frame *f;
- struct ast_channel *who, *other, *cs[3];
- struct ast_rtp *p0, *p1; /* Audio RTP Channels */
- struct ast_rtp *vp0, *vp1; /* Video RTP channels */
- struct ast_rtp_protocol *pr0, *pr1;
- struct sockaddr_in ac0, ac1;
- struct sockaddr_in vac0, vac1;
- struct sockaddr_in t0, t1;
- struct sockaddr_in vt0, vt1;
-
- void *pvt0, *pvt1;
- int codec0,codec1, oldcodec0, oldcodec1;
-
- memset(&vt0, 0, sizeof(vt0));
- memset(&vt1, 0, sizeof(vt1));
- memset(&vac0, 0, sizeof(vac0));
- memset(&vac1, 0, sizeof(vac1));
-
- /* Lock channels */
- ast_channel_lock(c0);
- while(ast_channel_trylock(c1)) {
- ast_channel_unlock(c0);
- usleep(1);
- ast_channel_lock(c0);
- }
-
- /* Find channel driver interfaces */
- pr0 = get_proto(c0);
- pr1 = get_proto(c1);
- if (!pr0) {
- ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
- ast_channel_unlock(c0);
- ast_channel_unlock(c1);
- return AST_BRIDGE_FAILED;
- }
- if (!pr1) {
- ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
- ast_channel_unlock(c0);
- ast_channel_unlock(c1);
- return AST_BRIDGE_FAILED;
- }
-
- /* Get channel specific interface structures */
- pvt0 = c0->tech_pvt;
- pvt1 = c1->tech_pvt;
-
- /* Get audio and video interface (if native bridge is possible) */
- p0 = pr0->get_rtp_info(c0);
- vp0 = pr0->get_vrtp_info ? pr0->get_vrtp_info(c0) : NULL;
- p1 = pr1->get_rtp_info(c1);
- vp1 = pr1->get_vrtp_info ? pr1->get_vrtp_info(c1) : NULL;
-
- /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
- if (!p0 || !p1) {
- /* Somebody doesn't want to play... */
- ast_channel_unlock(c0);
- ast_channel_unlock(c1);
- return AST_BRIDGE_FAILED_NOWARN;
- }
-
- if (ast_test_flag(p0, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
- /* can't bridge, we are carrying DTMF for this channel and the bridge
- needs it
- */
- ast_channel_unlock(c0);
- ast_channel_unlock(c1);
- return AST_BRIDGE_FAILED_NOWARN;
- }
-
- if (ast_test_flag(p1, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)) {
- /* can't bridge, we are carrying DTMF for this channel and the bridge
- needs it
- */
- ast_channel_unlock(c0);
- ast_channel_unlock(c1);
- return AST_BRIDGE_FAILED_NOWARN;
- }
-
- /* Get codecs from both sides */
- codec0 = pr0->get_codec ? pr0->get_codec(c0) : 0;
- codec1 = pr1->get_codec ? pr1->get_codec(c1) : 0;
- if (pr0->get_codec && pr1->get_codec) {
- /* Hey, we can't do reinvite if both parties speak different codecs */
- if (!(codec0 & codec1)) {
- if (option_debug)
- ast_log(LOG_DEBUG, "Channel codec0 = %d is not codec1 = %d, cannot native bridge in RTP.\n", codec0, codec1);
- ast_channel_unlock(c0);
- ast_channel_unlock(c1);
- return AST_BRIDGE_FAILED_NOWARN;
- }
- }
+ struct ast_frame *fr = NULL;
+ struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, };
+ int oldcodec0 = codec0, oldcodec1 = codec1;
+ struct sockaddr_in ac1 = {0,}, vac1 = {0,}, ac0 = {0,}, vac0 = {0,};
+ struct sockaddr_in t1 = {0,}, vt1 = {0,}, t0 = {0,}, vt0 = {0,};
- if (option_verbose > 2)
- ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name);
+ /* Set it up so audio goes directly between the two endpoints */
- /* Ok, we should be able to redirect the media. Start with one channel */
- if (pr0->set_rtp_peer(c0, p1, vp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))
- ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
- else {
- /* Store RTP peer */
+ /* Test the first channel */
+ if (!(pr0->set_rtp_peer(c0, p1, vp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))) {
ast_rtp_get_peer(p1, &ac1);
if (vp1)
ast_rtp_get_peer(vp1, &vac1);
- }
- /* Then test the other channel */
- if (pr1->set_rtp_peer(c1, p0, vp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))
- ast_log(LOG_WARNING, "Channel '%s' failed to talk back to '%s'\n", c1->name, c0->name);
- else {
- /* Store RTP peer */
+ } else
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
+
+ /* Test the second channel */
+ if (!(pr1->set_rtp_peer(c1, p0, vp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))) {
ast_rtp_get_peer(p0, &ac0);
if (vp0)
ast_rtp_get_peer(vp0, &vac0);
- }
+ } else
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c1->name, c0->name);
+
+ /* Now we can unlock and move into our loop */
ast_channel_unlock(c0);
ast_channel_unlock(c1);
- /* External RTP Bridge up, now loop and see if something happes that force us to take the
- media back to Asterisk */
+
+ /* Throw our channels into the structure and enter the loop */
cs[0] = c0;
cs[1] = c1;
cs[2] = NULL;
- oldcodec0 = codec0;
- oldcodec1 = codec1;
for (;;) {
- /* Check if something changed... */
- if ((c0->tech_pvt != pvt0) ||
- (c1->tech_pvt != pvt1) ||
- (c0->masq || c0->masqr || c1->masq || c1->masqr)) {
- ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
- if (c0->tech_pvt == pvt0) {
- if (pr0->set_rtp_peer(c0, NULL, NULL, 0, 0))
- ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
- }
- if (c1->tech_pvt == pvt1) {
- if (pr1->set_rtp_peer(c1, NULL, NULL, 0, 0))
- ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
- }
- return AST_BRIDGE_RETRY;
+ /* Check if anything changed */
+ if ((c0->tech_pvt != pvt0) ||
+ (c1->tech_pvt != pvt1) ||
+ (c0->masq || c0->masqr || c1->masq || c1->masqr)) {
+ ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
+ if (c0->tech_pvt == pvt0)
+ if (pr0->set_rtp_peer(c0, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
+ if (c1->tech_pvt == pvt1)
+ if (pr1->set_rtp_peer(c1, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+ return AST_BRIDGE_RETRY;
}
- /* Now check if they have changed address */
+
+ /* Check if they have changed their address */
ast_rtp_get_peer(p1, &t1);
- ast_rtp_get_peer(p0, &t0);
- if (pr0->get_codec)
- codec0 = pr0->get_codec(c0);
- if (pr1->get_codec)
- codec1 = pr1->get_codec(c1);
if (vp1)
ast_rtp_get_peer(vp1, &vt1);
+ if (pr1->get_codec)
+ codec1 = pr1->get_codec(c1);
+ ast_rtp_get_peer(p0, &t0);
if (vp0)
ast_rtp_get_peer(vp0, &vt0);
- if (inaddrcmp(&t1, &ac1) || (vp1 && inaddrcmp(&vt1, &vac1)) || (codec1 != oldcodec1)) {
+ if (pr0->get_codec)
+ codec0 = pr0->get_codec(c0);
+ if ((inaddrcmp(&t1, &ac1)) ||
+ (vp1 && inaddrcmp(&vt1, &vac1)) ||
+ (codec1 != oldcodec1)) {
if (option_debug > 1) {
- ast_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d (format %d)\n",
+ ast_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d (format %d)\n",
c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port), codec1);
- ast_log(LOG_DEBUG, "Oooh, '%s' changed end vaddress to %s:%d (format %d)\n",
+ ast_log(LOG_DEBUG, "Oooh, '%s' changed end vaddress to %s:%d (format %d)\n",
c1->name, ast_inet_ntoa(vt1.sin_addr), ntohs(vt1.sin_port), codec1);
- ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n",
+ ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n",
c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port), oldcodec1);
- ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n",
+ ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n",
c1->name, ast_inet_ntoa(vac1.sin_addr), ntohs(vac1.sin_port), oldcodec1);
}
- if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))
+ if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))
ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name);
memcpy(&ac1, &t1, sizeof(ac1));
memcpy(&vac1, &vt1, sizeof(vac1));
oldcodec1 = codec1;
}
- if (inaddrcmp(&t0, &ac0) || (vp0 && inaddrcmp(&vt0, &vac0))) {
- if (option_debug) {
- ast_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d (format %d)\n",
+ if ((inaddrcmp(&t0, &ac0)) ||
+ (vp0 && inaddrcmp(&vt0, &vac0))) {
+ if (option_debug > 1) {
+ ast_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d (format %d)\n",
c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port), codec0);
- ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n",
+ ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n",
c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port), oldcodec0);
}
if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : NULL, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))
@@ -2663,65 +2643,250 @@ enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel
memcpy(&vac0, &vt0, sizeof(vac0));
oldcodec0 = codec0;
}
- who = ast_waitfor_n(cs, 2, &timeoutms);
- if (!who) {
- if (!timeoutms)
+
+ /* Wait for frame to come in on the channels */
+ if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
+ if (!timeoutms)
return AST_BRIDGE_RETRY;
if (option_debug)
ast_log(LOG_DEBUG, "Ooh, empty read...\n");
- /* check for hangup / whentohangup */
if (ast_check_hangup(c0) || ast_check_hangup(c1))
break;
continue;
}
- f = ast_read(who);
- other = (who == c0) ? c1 : c0; /* the other channel */
- if (!f || ((f->frametype == AST_FRAME_DTMF) &&
- (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) ||
- ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
- /* breaking out of the bridge. */
- *fo = f;
+ fr = ast_read(who);
+ other = (who == c0) ? c1 : c0;
+ if (!fr || ((fr->frametype == AST_FRAME_DTMF) &&
+ (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) ||
+ ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
+ /* Break out of bridge */
+ *fo = fr;
*rc = who;
if (option_debug)
- ast_log(LOG_DEBUG, "Oooh, got a %s\n", f ? "digit" : "hangup");
- if ((c0->tech_pvt == pvt0)) {
- if (pr0->set_rtp_peer(c0, NULL, NULL, 0, 0))
+ ast_log(LOG_DEBUG, "Oooh, got a %s\n", fr ? "digit" : "hangup");
+ if (c0->tech_pvt == pvt0)
+ if (pr0->set_rtp_peer(c0, NULL, NULL, 0, 0))
ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
- }
- if ((c1->tech_pvt == pvt1)) {
- if (pr1->set_rtp_peer(c1, NULL, NULL, 0, 0))
+ if (c1->tech_pvt == pvt1)
+ if (pr1->set_rtp_peer(c1, NULL, NULL, 0, 0))
ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+ return AST_BRIDGE_COMPLETE;
+ } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
+ if ((fr->subclass == AST_CONTROL_HOLD) ||
+ (fr->subclass == AST_CONTROL_UNHOLD) ||
+ (fr->subclass == AST_CONTROL_VIDUPDATE)) {
+ ast_indicate(other, fr->subclass);
+ ast_frfree(fr);
+ } else {
+ *fo = fr;
+ *rc = who;
+ ast_log(LOG_DEBUG, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name);
+ return AST_BRIDGE_COMPLETE;
+ }
+ } else {
+ if ((fr->frametype == AST_FRAME_DTMF) ||
+ (fr->frametype == AST_FRAME_VOICE) ||
+ (fr->frametype == AST_FRAME_VIDEO)) {
+ ast_write(other, fr);
+ }
+ ast_frfree(fr);
+ }
+ /* Swap priority */
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+ }
+
+ return AST_BRIDGE_FAILED;
+}
+
+/*! \brief Bridge loop for partial native bridge (packet2packet) */
+static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, struct ast_rtp *vp0, struct ast_rtp *vp1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1)
+{
+ struct ast_frame *fr = NULL;
+ struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, };
+
+ /* Okay, setup each RTP structure to do P2P forwarding */
+ ast_clear_flag(p0, FLAG_P2P_SENT_MARK);
+ p0->bridged = p1;
+ ast_clear_flag(p1, FLAG_P2P_SENT_MARK);
+ p1->bridged = p0;
+ if (vp0) {
+ ast_clear_flag(vp0, FLAG_P2P_SENT_MARK);
+ vp0->bridged = vp1;
+ ast_clear_flag(vp1, FLAG_P2P_SENT_MARK);
+ vp1->bridged = vp0;
+ }
+
+ /* Now let go of the channel locks and be on our way */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+
+ /* Go into a loop forwarding frames until we don't need to anymore */
+ cs[0] = c0;
+ cs[1] = c1;
+ cs[2] = NULL;
+ for (;;) {
+ /* Check if anything changed */
+ if ((c0->tech_pvt != pvt0) ||
+ (c1->tech_pvt != pvt1) ||
+ (c0->masq || c0->masqr || c1->masq || c1->masqr)) {
+ ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
+ return AST_BRIDGE_RETRY;
+ }
+ /* Wait on a channel to feed us a frame */
+ if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
+ if (!timeoutms)
+ return AST_BRIDGE_RETRY;
+ if (option_debug)
+ ast_log(LOG_NOTICE, "Ooh, empty read...\n");
+ if (ast_check_hangup(c0) || ast_check_hangup(c1))
+ break;
+ continue;
+ }
+ /* Read in frame from channel */
+ fr = ast_read(who);
+ other = (who == c0) ? c1 : c0;
+ /* Dependong on the frame we may need to break out of our bridge */
+ if (!fr || ((fr->frametype == AST_FRAME_DTMF) &&
+ ((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) |
+ ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)))) {
+ /* Record received frame and who */
+ *fo = fr;
+ *rc = who;
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Oooh, got a %s\n", fr ? "digit" : "hangup");
+ /* Break out of the bridge */
+ p0->bridged = NULL;
+ p1->bridged = NULL;
+ if (vp0) {
+ vp0->bridged = NULL;
+ vp1->bridged = NULL;
}
return AST_BRIDGE_COMPLETE;
- } else if ((f->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
- if ((f->subclass == AST_CONTROL_HOLD) || (f->subclass == AST_CONTROL_UNHOLD) ||
- (f->subclass == AST_CONTROL_VIDUPDATE)) {
- ast_indicate(other, f->subclass);
- ast_frfree(f);
+ } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
+ if ((fr->subclass == AST_CONTROL_HOLD) ||
+ (fr->subclass == AST_CONTROL_UNHOLD) ||
+ (fr->subclass == AST_CONTROL_VIDUPDATE)) {
+ ast_indicate(other, fr->subclass);
+ ast_frfree(fr);
} else {
- *fo = f;
+ *fo = fr;
*rc = who;
- ast_log(LOG_DEBUG, "Got a FRAME_CONTROL (%d) frame on channel %s\n", f->subclass, who->name);
+ ast_log(LOG_DEBUG, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name);
return AST_BRIDGE_COMPLETE;
}
} else {
- if ((f->frametype == AST_FRAME_DTMF) ||
- (f->frametype == AST_FRAME_VOICE) ||
- (f->frametype == AST_FRAME_VIDEO)) {
- /* Forward voice or DTMF frames if they happen upon us */
- ast_write(other, f);
+ /* If this is a DTMF, voice, or video frame write it to the other channel */
+ if ((fr->frametype == AST_FRAME_DTMF) ||
+ (fr->frametype == AST_FRAME_VOICE) ||
+ (fr->frametype == AST_FRAME_VIDEO)) {
+ ast_write(other, fr);
}
- ast_frfree(f);
+ ast_frfree(fr);
}
- /* Swap priority not that it's a big deal at this point */
+ /* Swap priority */
cs[2] = cs[0];
cs[0] = cs[1];
cs[1] = cs[2];
-
}
+
return AST_BRIDGE_FAILED;
}
+/*! \brief Bridge calls. If possible and allowed, initiate
+ re-invite so the peers exchange media directly outside
+ of Asterisk. */
+enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
+{
+ struct ast_rtp *p0 = NULL, *p1 = NULL; /* Audio RTP Channels */
+ struct ast_rtp *vp0 = NULL, *vp1 = NULL; /* Video RTP channels */
+ struct ast_rtp_protocol *pr0 = NULL, *pr1 = NULL;
+ enum ast_rtp_get_result audio_p0_res = AST_RTP_GET_FAILED, video_p0_res = AST_RTP_GET_FAILED;
+ enum ast_rtp_get_result audio_p1_res = AST_RTP_GET_FAILED, video_p1_res = AST_RTP_GET_FAILED;
+ enum ast_bridge_result res = AST_BRIDGE_FAILED;
+ int codec0 = 0, codec1 = 0;
+ void *pvt0 = NULL, *pvt1 = NULL;
+
+ /* Lock channels */
+ ast_channel_lock(c0);
+ while(ast_channel_trylock(c1)) {
+ ast_channel_unlock(c0);
+ usleep(1);
+ ast_channel_lock(c0);
+ }
+
+ /* Find channel driver interfaces */
+ if (!(pr0 = get_proto(c0))) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED;
+ }
+ if (!(pr1 = get_proto(c1))) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED;
+ }
+
+ /* Get channel specific interface structures */
+ pvt0 = c0->tech_pvt;
+ pvt1 = c1->tech_pvt;
+
+ /* Get audio and video interface (if native bridge is possible) */
+ audio_p0_res = pr0->get_rtp_info(c0, &p0);
+ video_p0_res = pr0->get_vrtp_info ? pr0->get_vrtp_info(c0, &vp0) : AST_RTP_GET_FAILED;
+ audio_p1_res = pr1->get_rtp_info(c1, &p1);
+ video_p1_res = pr1->get_vrtp_info ? pr1->get_vrtp_info(c1, &vp1) : AST_RTP_GET_FAILED;
+
+ /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
+ if (audio_p0_res == AST_RTP_GET_FAILED || audio_p1_res == AST_RTP_GET_FAILED) {
+ /* Somebody doesn't want to play... */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+ /* If we need to feed DTMF frames into the core then only do a partial native bridge */
+ if (ast_test_flag(p0, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
+ ast_set_flag(p0, FLAG_P2P_NEED_DTMF);
+ audio_p0_res = AST_RTP_TRY_PARTIAL;
+ }
+
+ if (ast_test_flag(p1, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)) {
+ ast_set_flag(p1, FLAG_P2P_NEED_DTMF);
+ audio_p1_res = AST_RTP_TRY_PARTIAL;
+ }
+
+ /* Get codecs from both sides */
+ codec0 = pr0->get_codec ? pr0->get_codec(c0) : 0;
+ codec1 = pr1->get_codec ? pr1->get_codec(c1) : 0;
+ if (pr0->get_codec && pr1->get_codec) {
+ /* Hey, we can't do native bridging if both parties speak different codecs */
+ if (!(codec0 & codec1)) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Channel codec0 = %d is not codec1 = %d, cannot native bridge in RTP.\n", codec0, codec1);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+ }
+
+ /* If either side can only do a partial bridge, then don't try for a true native bridge */
+ if (audio_p0_res == AST_RTP_TRY_PARTIAL || audio_p1_res == AST_RTP_TRY_PARTIAL) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Packet2Packet bridging %s and %s\n", c0->name, c1->name);
+ res = bridge_p2p_loop(c0, c1, p0, p1, vp0, vp1, timeoutms, flags, fo, rc, pvt0, pvt1);
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name);
+ res = bridge_native_loop(c0, c1, p0, p1, vp0, vp1, pr0, pr1, codec0, codec1, timeoutms, flags, fo, rc, pvt0, pvt1);
+ }
+
+ return res;
+}
+
static int rtp_do_debug_ip(int fd, int argc, char *argv[])
{
struct hostent *hp;