diff --git a/main/rtp.c b/main/rtp.c index 9e32ebd6570117d5af4d94fe3b0e901ff397ed16..55533157ff10b0a3dc5b718b3896c67a368e7a63 100644 --- a/main/rtp.c +++ b/main/rtp.c @@ -164,6 +164,7 @@ static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw) static int ast_rtcp_write_sr(void *data); static int ast_rtcp_write_rr(void *data); static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp); +static int bridge_p2p_rtcp_write(struct ast_rtp *rtp, unsigned int *rtcpheader, int len); #define FLAG_3389_WARNING (1 << 0) #define FLAG_NAT_ACTIVE (3 << 1) @@ -777,6 +778,11 @@ struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp) ast_log(LOG_DEBUG, "RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); } } + + /* If we are P2P bridged to another RTP stream, send it directly over */ + if (rtp->bridged && !bridge_p2p_rtcp_write(rtp, rtcpheader, res)) + return &ast_null_frame; + if (option_debug) ast_log(LOG_DEBUG, "Got RTCP report of %d bytes\n", res); @@ -929,8 +935,31 @@ 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) +/*! \brief Perform a Packet2Packet RTCP write */ +static int bridge_p2p_rtcp_write(struct ast_rtp *rtp, unsigned int *rtcpheader, int len) +{ + struct ast_rtp *bridged = rtp->bridged; + int res = 0; + + /* If RTCP is not present on the bridged RTP session, then ignore this */ + if (!bridged->rtcp) + return 0; + + /* Send the data out */ + res = sendto(bridged->rtcp->s, (void *)rtcpheader, len, 0, (struct sockaddr *)&bridged->rtcp->them, sizeof(bridged->rtcp->them)); + if (res < 0) { + if (!bridged->nat || (bridged->nat && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) + ast_log(LOG_DEBUG, "RTCP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(bridged->rtcp->them.sin_addr), ntohs(bridged->rtcp->them.sin_port), strerror(errno)); + else if ((((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug)) && (option_debug || rtpdebug)) + ast_log(LOG_DEBUG, "RTCP NAT: Can't write RTCP to private address %s:%d, waiting for other end to send first...\n", ast_inet_ntoa(bridged->rtcp->them.sin_addr), ntohs(bridged->rtcp->them.sin_port)); + } else if (rtp_debug_test_addr(&bridged->rtcp->them)) + ast_verbose("Sent RTCP P2P packet to %s:%d (len %-6.6u)\n", ast_inet_ntoa(bridged->rtcp->them.sin_addr), ntohs(bridged->rtcp->them.sin_port), len); + + return 0; +} + +/*! \brief Perform a Packet2Packet RTP write */ +static int bridge_p2p_rtp_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; @@ -965,23 +994,19 @@ static int bridge_p2p_write(struct ast_rtp *rtp, unsigned int *rtpheader, int le /* 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; + /* Send the packet back out */ + 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) && !ast_test_flag(bridged, FLAG_NAT_INACTIVE_NOWARN)) { + 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 -1; } @@ -1024,11 +1049,6 @@ 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]); @@ -1063,7 +1083,7 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) } /* If we are bridged to another RTP stream, send direct */ - if (rtp->bridged && !bridge_p2p_write(rtp, rtpheader, res, hdrlen)) + if (rtp->bridged && !bridge_p2p_rtp_write(rtp, rtpheader, res, hdrlen)) return &ast_null_frame; if (version != 2) @@ -1769,12 +1789,10 @@ struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io return NULL; } } - if (io && sched && callbackmode) { - /* Operate this one in a callback mode */ - rtp->sched = sched; - rtp->io = io; + rtp->sched = sched; + rtp->io = io; + if (callbackmode) rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp); - } ast_rtp_pt_default(rtp); return rtp; } @@ -2355,7 +2373,7 @@ static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec if (res <0) { if (!rtp->nat || (rtp->nat && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) { ast_log(LOG_DEBUG, "RTP Transmission error of packet %d to %s:%d: %s\n", rtp->seqno, ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); - } else if ((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) { + } else if (((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(rtp, FLAG_NAT_INACTIVE_NOWARN)) { /* Only give this error message once if we are not RTP debugging */ 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(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); @@ -2704,11 +2722,105 @@ static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct return AST_BRIDGE_FAILED; } +/*! \brief P2P RTP/RTCP Callback */ +static int p2p_rtp_callback(int *id, int fd, short events, void *cbdata) +{ + int res = 0, hdrlen = 12; + struct sockaddr_in sin; + socklen_t len; + unsigned int *header; + struct ast_rtp *rtp = cbdata; + int is_rtp = 0, is_rtcp = 0; + + if (!rtp) + return 1; + + len = sizeof(sin); + if ((res = recvfrom(fd, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len)) < 0) + return 1; + + header = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET); + + /* Determine what this file descriptor is for */ + if (rtp->s == fd) + is_rtp = 1; + else if (rtp->rtcp && rtp->rtcp->s == fd) + is_rtcp = 1; + + /* If NAT support is turned on, then see if we need to change their address */ + if (rtp->nat) { + /* If this is for RTP, check that - if it's for RTCP, check that */ + if (is_rtp) { + if ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) || + (rtp->them.sin_port != sin.sin_port)) { + rtp->them = sin; + rtp->rxseqno = 0; + ast_set_flag(rtp, FLAG_NAT_ACTIVE); + if (option_debug || rtpdebug) + ast_log(LOG_DEBUG, "P2P RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port)); + } + } else if (is_rtcp) { + if ((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) || + (rtp->rtcp->them.sin_port != sin.sin_port)) { + rtp->rtcp->them = sin; + if (option_debug || rtpdebug) + ast_log(LOG_DEBUG, "P2P RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + } + } + } + + /* If this came from the RTP stream, write out via RTP - if it's RTCP, write out via RTCP */ + if (is_rtp) + bridge_p2p_rtp_write(rtp, header, res, hdrlen); + else if (is_rtcp) + bridge_p2p_rtcp_write(rtp, header, res); + + return 1; +} + +/*! \brief Helper function to switch a channel and RTP stream into callback mode */ +static int p2p_callback_enable(struct ast_channel *chan, struct ast_rtp *rtp, int *fds, int **iod) +{ + /* If we need DTMF or we have no IO structure, then we can't do direct callback */ + if (ast_test_flag(rtp, FLAG_P2P_NEED_DTMF) || !rtp->io) + return 0; + + /* Steal the file descriptors from the channel and stash them away */ + fds[0] = chan->fds[0]; + fds[1] = chan->fds[1]; + chan->fds[0] = -1; + chan->fds[1] = -1; + + /* Now, fire up callback mode */ + iod[0] = ast_io_add(rtp->io, fds[0], p2p_rtp_callback, AST_IO_IN, rtp); + iod[1] = ast_io_add(rtp->io, fds[1], p2p_rtp_callback, AST_IO_IN, rtp); + + return 1; +} + +/*! \brief Helper function to switch a channel and RTP stream out of callback mode */ +static int p2p_callback_disable(struct ast_channel *chan, struct ast_rtp *rtp, int *fds, int **iod) +{ + ast_channel_lock(chan); + /* Remove the callback from the IO context */ + ast_io_remove(rtp->io, iod[0]); + ast_io_remove(rtp->io, iod[1]); + /* Restore file descriptors */ + chan->fds[0] = fds[0]; + chan->fds[1] = fds[1]; + ast_channel_unlock(chan); + return 0; +} + /*! \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, }; + int p0_fds[2] = {-1, -1}, p1_fds[2] = {-1, -1}; + int *p0_iod[2] = {NULL, }, *p1_iod[2] = {NULL, }; + int p0_callback = 0, p1_callback = 0; + enum ast_bridge_result res = AST_BRIDGE_FAILED; /* Okay, setup each RTP structure to do P2P forwarding */ ast_clear_flag(p0, FLAG_P2P_SENT_MARK); @@ -2722,6 +2834,10 @@ static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast vp1->bridged = vp0; } + /* Activate callback modes if possible */ + p0_callback = p2p_callback_enable(c0, p0, &p0_fds[0], &p0_iod[0]); + p1_callback = p2p_callback_enable(c1, p1, &p1_fds[0], &p1_iod[0]); + /* Now let go of the channel locks and be on our way */ ast_channel_unlock(c0); ast_channel_unlock(c1); @@ -2736,12 +2852,15 @@ static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast (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; + res = AST_BRIDGE_RETRY; + break; } /* Wait on a channel to feed us a frame */ if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) { - if (!timeoutms) - return AST_BRIDGE_RETRY; + if (!timeoutms) { + res = AST_BRIDGE_RETRY; + break; + } if (option_debug) ast_log(LOG_NOTICE, "Ooh, empty read...\n"); if (ast_check_hangup(c0) || ast_check_hangup(c1)) @@ -2767,18 +2886,31 @@ static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast vp0->bridged = NULL; vp1->bridged = NULL; } - return AST_BRIDGE_COMPLETE; + res = AST_BRIDGE_COMPLETE; + break; } 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)) { + /* If we are going on hold, then break callback mode */ + if (fr->subclass == AST_CONTROL_HOLD) { + if (p0_callback) + p0_callback = p2p_callback_disable(c0, p0, &p0_fds[0], &p0_iod[0]); + if (p1_callback) + p1_callback = p2p_callback_disable(c1, p1, &p1_fds[0], &p1_iod[0]); + } else if (fr->subclass == AST_CONTROL_UNHOLD) { + /* If we are off hold, then go back to callback mode */ + p0_callback = p2p_callback_enable(c0, p0, &p0_fds[0], &p0_iod[0]); + p1_callback = p2p_callback_enable(c1, p1, &p1_fds[0], &p1_iod[0]); + } 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; + res = AST_BRIDGE_COMPLETE; + break; } } else { /* If this is a DTMF, voice, or video frame write it to the other channel */ @@ -2795,7 +2927,13 @@ static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast cs[1] = cs[2]; } - return AST_BRIDGE_FAILED; + /* If we are totally avoiding the core, then restore our link to it */ + if (p0_callback) + p0_callback = p2p_callback_disable(c0, p0, &p0_fds[0], &p0_iod[0]); + if (p1_callback) + p1_callback = p2p_callback_disable(c1, p1, &p1_fds[0], &p1_iod[0]); + + return res; } /*! \brief Bridge calls. If possible and allowed, initiate