diff options
author | file <file@f38db490-d61c-443f-a65b-d21fe96a405b> | 2006-08-28 17:37:56 +0000 |
---|---|---|
committer | file <file@f38db490-d61c-443f-a65b-d21fe96a405b> | 2006-08-28 17:37:56 +0000 |
commit | 77be2d9b78060f758181a608029fba1b782fd44a (patch) | |
tree | 8d4c60d4b38470222d45faab0253b21508f6a10a /main/rtp.c | |
parent | 320436acc4a3b4685570a96fc771979ebdc6c9d4 (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
Diffstat (limited to 'main/rtp.c')
-rw-r--r-- | main/rtp.c | 559 |
1 files changed, 362 insertions, 197 deletions
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; |