diff -uNr asterisk-1.2.4.org/include/asterisk/rtp.h asterisk-1.2.4/include/asterisk/rtp.h --- asterisk-1.2.4.org/include/asterisk/rtp.h 2005-11-29 19:24:39.000000000 +0100 +++ asterisk-1.2.4/include/asterisk/rtp.h 2006-02-17 15:51:04.000000000 +0100 @@ -102,6 +102,8 @@ void ast_rtp_destroy(struct ast_rtp *rtp); +char *ast_rtp_get_quality(struct ast_rtp *rtp); + void ast_rtp_reset(struct ast_rtp *rtp); void ast_rtp_set_callback(struct ast_rtp *rtp, ast_rtp_callback callback); @@ -114,6 +116,12 @@ struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp); +int ast_rtcp_send_h261fur(void *data); + +int ast_rtcp_write_sr(void *data); + +int ast_rtcp_write_rr(void *data); + int ast_rtp_fd(struct ast_rtp *rtp); int ast_rtcp_fd(struct ast_rtp *rtp); @@ -160,6 +168,10 @@ void ast_rtp_reload(void); +void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw); + +unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff -uNr asterisk-1.2.4.org/rtp.c asterisk-1.2.4/rtp.c --- asterisk-1.2.4.org/rtp.c 2005-11-30 15:27:59.000000000 +0100 +++ asterisk-1.2.4/rtp.c 2006-02-17 15:46:20.000000000 +0100 @@ -55,6 +55,16 @@ #include "asterisk/utils.h" #define MAX_TIMESTAMP_SKEW 640 +#define RTP_SEQ_MOD (1<<16) /*A sequence number can't be more than 16 bits */ +#define RTCP_DEFAULT_INTERVALMS 5000 /* Default milli-seconds between RTCP reports we send */ +#define RTCP_MIN_INTERVALMS 500 /* Min milli-seconds between RTCP reports we send */ +#define RTCP_MAX_INTERVALMS 60000 /* Max milli-seconds between RTCP reports we send */ +#define RTCP_PT_FUR 192 +#define RTCP_PT_SR 200 +#define RTCP_PT_RR 201 +#define RTCP_PT_SDES 202 +#define RTCP_PT_BYE 203 +#define RTCP_PT_APP 204 #define RTP_MTU 1200 @@ -65,11 +75,18 @@ static int rtpstart = 0; static int rtpend = 0; static int rtpdebug = 0; /* Are we debugging? */ +static int rtcpdebug = 0; /*!< Are we debugging RTCP? */ +static int rtcpstats = 0; /*!< Are we debugging RTCP? */ +static int rtcpinterval = RTCP_DEFAULT_INTERVALMS; /*!< Time between rtcp reports in millisecs */ static struct sockaddr_in rtpdebugaddr; /* Debug packets to/from this host */ +static struct sockaddr_in rtcpdebugaddr; /*!< Debug RTCP packets to/from this host */ #ifdef SO_NO_CHECK static int nochecksums = 0; #endif +/* Forward declarations */ +int ast_rtcp_write(void *data); + /* The value of each payload format mapping: */ struct rtpPayloadType { int isAstFormat; /* whether the following code is an AST_FORMAT */ @@ -90,12 +107,23 @@ unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET]; /*! Synchronization source, RFC 3550, page 10. */ unsigned int ssrc; + unsigned int themssrc; /* Their SSRC */ unsigned int lastts; unsigned int lastdigitts; unsigned int lastrxts; unsigned int lastividtimestamp; unsigned int lastovidtimestamp; unsigned int lasteventseqn; + int lastrxseqno; /* Last received sequence number */ + unsigned short seedrxseqno; /* What sequence number did they start with?*/ + unsigned int seedrxts; /* What RTP timestamp did they start with? */ + unsigned int rxcount; /* How many packets have we received? */ + unsigned int rxoctetcount; /* How many octets have we received? should be rxcount *160*/ + unsigned int txcount; /* How many packets have we sent? */ + unsigned int txoctetcount; /* How many octets have we sent? (txcount*160)*/ + unsigned int cycles; /* Shifted count of sequence number cycles */ + double rxjitter; /* Interarrival jitter at the moment */ + double rxtransit; /* Relative transit time for previous packet */ unsigned int lasteventendseqn; int lasttxformat; int lastrxformat; @@ -109,6 +137,8 @@ struct sockaddr_in them; struct timeval rxcore; struct timeval txcore; + double drxcore; /* The double representation of the first received packet */ + struct timeval lastrx; /* timeval when we last received a packet */ struct timeval dtmfmute; struct ast_smoother *smoother; int *ioid; @@ -145,10 +175,41 @@ struct sockaddr_in us; /*! Socket representation of the remote endpoint. */ struct sockaddr_in them; + unsigned int soc; /* What they told us */ + unsigned int spc; /* What they told us */ + unsigned int themrxlsr; /* The middle 32 bits of the NTP timestamp in the last received SR*/ + struct timeval rxlsr; /* Time when we got their last SR */ + struct timeval txlsr; /* Time when we sent or last SR*/ + unsigned int expected_prior; /* no. packets in previous interval */ + unsigned int received_prior; /* no. packets received in previous interval */ + int schedid; /*Schedid returned from ast_sched_add() to schedule RTCP-transmissions*/ + unsigned int rr_count; /* number of RRs we've sent, not including report blocks in SR's */ + unsigned int sr_count; /* number of SRs we've sent */ + unsigned int lastsrtxcount; /* Transmit packet count when last SR sent */ + double accumulated_transit; /* accumulated a-dlsr-lsr */ + double rtt; /* Last reported rtt */ + unsigned int reported_jitter; /* The contents of their last jitter entry in the RR */ + unsigned int reported_lost; /* Reported lost packets in their RR */ + char quality[AST_MAX_USER_FIELD]; + double maxrxjitter; + double minrxjitter; + double maxrtt; + double minrtt; + int sendfur; }; static struct ast_rtp_protocol *protos = NULL; +void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw) +{ + unsigned int sec, usec, frac; + sec = tv.tv_sec + 2208988800u; /* Sec between 1900 and 1970 */ + usec = tv.tv_usec; + frac = (usec << 12) + (usec << 8) - ((usec * 3650) >> 6); + *msw = sec; + *lsw = frac; +} + int ast_rtp_fd(struct ast_rtp *rtp) { return rtp->s; @@ -161,6 +222,15 @@ return -1; } +unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp) +{ + unsigned int interval; + /* TODO Do a more reasonable calculation on this one + * Look in RFC 3550 Section A.7 for an example*/ + interval = rtcpinterval; + return interval; +} + void ast_rtp_set_data(struct ast_rtp *rtp, void *data) { rtp->data = data; @@ -220,6 +290,20 @@ return 1; } +static inline int rtcp_debug_test_addr(struct sockaddr_in *addr) +{ + if (rtcpdebug == 0) + return 0; + if (rtcpdebugaddr.sin_addr.s_addr) { + if (((ntohs(rtcpdebugaddr.sin_port) != 0) + && (rtcpdebugaddr.sin_port != addr->sin_port)) + || (rtcpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr)) + return 0; + } + return 1; +} + + static struct ast_frame *process_cisco_dtmf(struct ast_rtp *rtp, unsigned char *data, int len) { unsigned int event; @@ -370,32 +454,42 @@ { static struct ast_frame null_frame = { AST_FRAME_NULL, }; socklen_t len; - int hdrlen = 8; + int position, i, packetwords; int res; struct sockaddr_in sin; - unsigned int rtcpdata[1024]; + unsigned int rtcpdata[8192 + AST_FRIENDLY_OFFSET]; char iabuf[INET_ADDRSTRLEN]; - + unsigned int *rtcpheader; + int pt; + struct timeval now; + unsigned int length; + int rc; + double rtt = 0; + double a; + double dlsr; + double lsr; + unsigned int msw; + unsigned int lsw; + unsigned int comp; + if (!rtp || !rtp->rtcp) return &null_frame; len = sizeof(sin); - res = recvfrom(rtp->rtcp->s, rtcpdata, sizeof(rtcpdata), + res = recvfrom(rtp->rtcp->s, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len); + rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET); if (res < 0) { if (errno != EAGAIN) - ast_log(LOG_WARNING, "RTP Read error: %s\n", strerror(errno)); + ast_log(LOG_WARNING, "RTCP Read error: %s\n", strerror(errno)); if (errno == EBADF) CRASH; return &null_frame; } - if (res < hdrlen) { - ast_log(LOG_WARNING, "RTP Read too short\n"); - return &null_frame; - } + packetwords = res / 4; if (rtp->nat) { /* Send to whoever sent to us */ @@ -408,18 +502,147 @@ } if (option_debug) ast_log(LOG_DEBUG, "Got RTCP report of %d bytes\n", res); + + /* Process a compound packet */ + position = 0; + while (position < packetwords) { + i = position; + length = ntohl(rtcpheader[i]); + pt = (length & 0xff0000) >> 16; + rc = (length & 0x1f000000) >> 24; + length &= 0xffff; + + if ((i + length) > packetwords) { + ast_log(LOG_WARNING, "RTCP Read too short\n"); + return &null_frame; + } + + if(rtcp_debug_test_addr(&sin)){ + ast_verbose("\n\nGot RTCP from %s:%d\n",ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr),ntohs(sin.sin_port)); + ast_verbose("PT: %d(%s)\n",pt,(pt==200)?"Sender Report":(pt==201)?"Receiver Report":(pt==192)?"H.261 FUR":"Unknown"); + ast_verbose("Reception reports: %d\n",rc); + ast_verbose("SSRC of sender: %u\n",rtcpheader[i+1]); + } + + i+=2; /* Advance past header and ssrc */ + + switch(pt){ + case RTCP_PT_SR: + gettimeofday(&rtp->rtcp->rxlsr,NULL); /* To be able to populate the dlsr */ + rtp->rtcp->spc = ntohl(rtcpheader[i+3]); + rtp->rtcp->soc = ntohl(rtcpheader[i+4]); + rtp->rtcp->themrxlsr = ((ntohl(rtcpheader[i]) & 0x0000ffff) << 16) | ((ntohl(rtcpheader[i+1]) & 0xffff) >> 16); /* Going to LSR in RR*/ + + if(rtcp_debug_test_addr(&sin)){ + ast_verbose("NTP timestamp: %u.%010u\n",ntohl(rtcpheader[i]), ntohl(rtcpheader[i+1])*4096); + ast_verbose("RTP timestamp: %u\n",ntohl(rtcpheader[i+2])); + ast_verbose("SPC: %u\tSOC: %u\n",ntohl(rtcpheader[i+3]),ntohl(rtcpheader[i+4])); + } + i += 5; + if (rc < 1) + break; + /* Intentional fall through */ + case RTCP_PT_RR: + /* This is the place to calculate RTT */ + /* Don't handle multiple reception reports (rc > 1) yet */ + gettimeofday(&now, NULL); + timeval2ntp(now, &msw, &lsw); + /* Use the one we sent them in our SR instead, rtcp->txlsr could have been rewritten if the dlsr is large */ + if(ntohl(rtcpheader[i+4])){ /* We must have the LSR */ + comp = ((msw & 0xffff) << 16) | ((lsw & 0xffff0000) >> 16); + a = (double)((comp & 0xffff0000) >> 16) + (double)((double)(comp & 0xffff)/1000000.); + lsr = (double)((ntohl(rtcpheader[i+4]) & 0xffff0000) >> 16) + (double)((double)(ntohl(rtcpheader[i+4]) & 0xffff)/1000000.); + dlsr = (double)(ntohl(rtcpheader[i+5])/65536.); + rtt = a - dlsr - lsr; + rtp->rtcp->accumulated_transit += rtt; + rtp->rtcp->rtt = rtt; + if(rtp->rtcp->maxrttrtcp->maxrtt = rtt; + if(rtp->rtcp->minrtt>rtt) + rtp->rtcp->minrtt = rtt; + } + rtp->rtcp->reported_jitter = ntohl(rtcpheader[i+3]); + rtp->rtcp->reported_lost = ntohl(rtcpheader[i+1]) & 0xffffff; + if(rtcp_debug_test_addr(&sin)){ + ast_verbose("Fraction lost: %d\n", ((ntohl(rtcpheader[i+1]) & 0xff000000) >> 24)); + ast_verbose("Packets lost so far: %d\n", rtp->rtcp->reported_lost); + ast_verbose("Highest sequence number: %d\n", (ntohl(rtcpheader[i+2]) & 0xffff)); + ast_verbose("Sequence number cycles: %d\n", (ntohl(rtcpheader[i+2]) & 0xffff) >> 16); + ast_verbose("Interarrival jitter: %u\n", rtp->rtcp->reported_jitter); + ast_verbose("Last SR(our NTP): %u.%010u\n",ntohl(rtcpheader[i+4])>>16,(ntohl(rtcpheader[i+4])<<16)*4096); + ast_verbose("DLSR: %4.4f (sec)\n",ntohl(rtcpheader[i+5])/65536.0); + if(rtt) + ast_verbose("RTT: %f(sec)\n", rtt); + } + break; + case RTCP_PT_FUR: + if(rtcp_debug_test_addr(&sin)) + ast_verbose("Received a Fast Update Request\n"); + break; + case RTCP_PT_SDES: + if(rtcp_debug_test_addr(&sin)) + ast_verbose("Received an SDES from %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + break; + case RTCP_PT_BYE: + if(rtcp_debug_test_addr(&sin)) + ast_verbose("Received a BYE from %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + break; + default: + ast_log(LOG_NOTICE, "Unknown RTCP packet (pt=%d) received from %s:%d\n", pt, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + break; + } + position += (length + 1); + } + return &null_frame; } static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int timestamp, int mark) { - struct timeval ts = ast_samp2tv( timestamp, 8000); - if (ast_tvzero(rtp->rxcore) || mark) { - rtp->rxcore = ast_tvsub(ast_tvnow(), ts); - /* Round to 20ms for nice, pretty timestamps */ - rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 20000; + struct timeval now; + double transit; + double current_time; + double d; + double dtv; + double prog; + + if ((!rtp->rxcore.tv_sec && !rtp->rxcore.tv_usec) || mark) { + gettimeofday(&rtp->rxcore, NULL); + rtp->drxcore = (double)rtp->rxcore.tv_sec + (double)rtp->rxcore.tv_usec/1000000; + /* map timestamp to a real time */ + rtp->seedrxts = timestamp; /* Their RTP timestamp started with this */ + rtp->rxcore.tv_sec -= timestamp / 8000; + rtp->rxcore.tv_usec -= (timestamp % 8000) * 125; + /* Round to 0.1ms for nice, pretty timestamps */ + rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 100; + if (rtp->rxcore.tv_usec < 0) { + /* Adjust appropriately if necessary */ + rtp->rxcore.tv_usec += 1000000; + rtp->rxcore.tv_sec -= 1; + } } - *tv = ast_tvadd(rtp->rxcore, ts); + + gettimeofday(&now,NULL); + /* rxcore is the mapping between the RTP timestamp and _our_ real time from gettimeofday() */ + tv->tv_sec = rtp->rxcore.tv_sec + timestamp / 8000; + tv->tv_usec = rtp->rxcore.tv_usec + (timestamp % 8000) * 125; + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec += 1; + } + prog = (double)((timestamp-rtp->seedrxts)/8000.); + dtv = (double)rtp->drxcore + (double)(prog); + current_time = (double)now.tv_sec + (double)now.tv_usec/1000000; + transit = current_time - dtv; + d = transit - rtp->rxtransit; + rtp->rxtransit = transit; + if(d<0) + d=-d; + rtp->rxjitter += (1./16.) * (d - rtp->rxjitter); + if(rtp->rxjitter > rtp->rtcp->maxrxjitter) + rtp->rtcp->maxrxjitter = rtp->rxjitter; + if(rtp->rxjitter < rtp->rtcp->minrxjitter) + rtp->rtcp->minrxjitter = rtp->rxjitter; } struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) @@ -430,6 +653,7 @@ unsigned int seqno; int version; int payloadtype; + int tseqno; int hdrlen = 12; int padding; int mark; @@ -456,6 +680,7 @@ CRASH; return &null_frame; } + if (res < hdrlen) { ast_log(LOG_WARNING, "RTP Read too short\n"); return &null_frame; @@ -471,6 +696,10 @@ if ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) || (rtp->them.sin_port != sin.sin_port)) { memcpy(&rtp->them, &sin, sizeof(rtp->them)); + if(rtp->rtcp){ + memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them)); + rtp->rtcp->them.sin_port = htons(ntohs(rtp->them.sin_port)+1); + } rtp->rxseqno = 0; ast_set_flag(rtp, FLAG_NAT_ACTIVE); if (option_debug || rtpdebug) @@ -509,11 +738,35 @@ return &null_frame; } + rtp->rxcount++; /* Only count reasonably valid packets, this'll make the rtcp stats more accurate */ + + tseqno = rtp->lastrxseqno +1; + + if(rtp->rxcount==1){ + /* This is the first RTP packet successfully received from source */ + rtp->seedrxseqno = seqno; + } + + if(rtp->rtcp->schedid<1){ + /* Schedule transmission of Receiver Report */ + rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp); + } + + if(tseqno > RTP_SEQ_MOD){ /* if tseqno is greater than RTP_SEQ_MOD it would indicate that the sender cycled */ + rtp->cycles += RTP_SEQ_MOD; + ast_verbose("SEQNO cycled: %u\t%d\n", rtp->cycles, seqno); + } + + rtp->lastrxseqno = seqno; + + if(rtp->themssrc==0) + rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */ + if(rtp_debug_test_addr(&sin)) ast_verbose("Got RTP packet from %s:%d (type %d, seq %d, ts %d, len %d)\n" , ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp,res - hdrlen); - rtpPT = ast_rtp_lookup_pt(rtp, payloadtype); + rtpPT = ast_rtp_lookup_pt(rtp, payloadtype); if (!rtpPT.isAstFormat) { /* This is special in-band data that's not one of our codecs */ if (rtpPT.code == AST_RTP_DTMF) { @@ -906,9 +1159,11 @@ memset(rtcp, 0, sizeof(struct ast_rtcp)); rtcp->s = rtp_socket(); rtcp->us.sin_family = AF_INET; + rtcp->them.sin_family = AF_INET; + if (rtcp->s < 0) { free(rtcp); - ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno)); + ast_log(LOG_WARNING, "Unable to allocate RTCP socket: %s\n", strerror(errno)); return NULL; } return rtcp; @@ -1043,11 +1298,16 @@ void ast_rtp_stop(struct ast_rtp *rtp) { + if(rtp->rtcp->schedid>0){ + ast_sched_del(rtp->sched, rtp->rtcp->schedid); + rtp->rtcp->schedid = -1; + } + memset(&rtp->them.sin_addr, 0, sizeof(rtp->them.sin_addr)); memset(&rtp->them.sin_port, 0, sizeof(rtp->them.sin_port)); if (rtp->rtcp) { - memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->them.sin_addr)); - memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->them.sin_port)); + 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)); } } @@ -1071,8 +1331,50 @@ rtp->rxseqno = 0; } +char *ast_rtp_get_quality(struct ast_rtp *rtp) +{ + /* + *ssrc our ssrc + *themssrc their ssrc + *lp lost packets + *rxjitter our calculated jitter(rx) + *rxcount no. received packets + *txjitter reported jitter of the other end + *txcount transmitted packets + *rlp remote lost packets + */ + + snprintf(rtp->rtcp->quality, sizeof(rtp->rtcp->quality), "ssrc=%u;themssrc=%u;lp=%u;rxjitter=%f;rxcount=%u;txjitter=%f;txcount=%u;rlp=%u;rtt=%f", rtp->ssrc, rtp->themssrc, rtp->rtcp->expected_prior - rtp->rtcp->received_prior, rtp->rxjitter, rtp->rxcount, (double)rtp->rtcp->reported_jitter/65536., rtp->txcount, rtp->rtcp->reported_lost, rtp->rtcp->rtt); + + return rtp->rtcp->quality; +} + void ast_rtp_destroy(struct ast_rtp *rtp) { + if(rtcp_debug_test_addr(&rtp->them) || rtcpstats){ + /*Print some info on the call here */ + ast_verbose(" RTP-stats\n"); + ast_verbose("* Our Receiver:\n"); + ast_verbose(" SSRC: %u\n", rtp->themssrc); + ast_verbose(" Received packets: %u\n", rtp->rxcount); + ast_verbose(" Lost packets: %u\n", rtp->rtcp->expected_prior - rtp->rtcp->received_prior); + ast_verbose(" Jitter: %.4f\n", rtp->rxjitter); + ast_verbose(" Transit: %.4f\n", rtp->rxtransit); + ast_verbose(" RR-count: %u\n", rtp->rtcp->rr_count); + ast_verbose("* Our Sender:\n"); + ast_verbose(" SSRC: %u\n", rtp->ssrc); + ast_verbose(" Sent packets: %u\n", rtp->txcount); + ast_verbose(" Lost packets: %u\n", rtp->rtcp->reported_lost); + ast_verbose(" Jitter: %u\n", rtp->rtcp->reported_jitter); + ast_verbose(" SR-count: %u\n", rtp->rtcp->sr_count); + ast_verbose(" RTT: %f\n", rtp->rtcp->rtt); + } + + if(rtp->rtcp->schedid>0){ + ast_sched_del(rtp->sched, rtp->rtcp->schedid); + rtp->rtcp->schedid = -1; + } + if (rtp->smoother) ast_smoother_free(rtp->smoother); if (rtp->ioid) @@ -1082,6 +1384,7 @@ if (rtp->rtcp) { close(rtp->rtcp->s); free(rtp->rtcp); + rtp->rtcp=NULL; } free(rtp); } @@ -1187,6 +1490,238 @@ return 0; } +/* Send an H.261 fast update request, some devices need this rather than SIP XML */ +int ast_rtcp_send_h261fur(void *data) +{ + struct ast_rtp *rtp = data; + int res; + + rtp->rtcp->sendfur = 1; + res = ast_rtcp_write(data); + + return res; +} + +int ast_rtcp_write_sr(void *data) +{ + struct ast_rtp *rtp = data; + int res; + int len = 0; + struct timeval now; + unsigned int now_lsw; + unsigned int now_msw; + unsigned int *rtcpheader; + unsigned int lost; + unsigned int extended; + unsigned int expected; + unsigned int expected_interval; + unsigned int received_interval; + unsigned int lost_interval; + int fraction; + struct timeval dlsr; + char bdata[512]; + char iabuf[INET_ADDRSTRLEN]; + + if(!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0)) + return 0; + + if(!rtp->rtcp->them.sin_addr.s_addr){ /* This'll stop rtcp for this rtp session */ + ast_verbose("RTCP SR transmission error, rtcp halted %s\n",strerror(errno)); + ast_sched_del(rtp->sched, rtp->rtcp->schedid); + rtp->rtcp->schedid = -1; + return 0; + } + + gettimeofday(&now, NULL); + timeval2ntp(now, &now_msw, &now_lsw); /* fill thses ones in from utils.c*/ + rtcpheader = (unsigned int *)bdata; + rtcpheader[1] = htonl(rtp->ssrc); /* Our SSRC */ + rtcpheader[2] = htonl(now_msw); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/ + rtcpheader[3] = htonl(now_lsw); /* now, LSW */ + rtcpheader[4] = htonl(rtp->lastts); /* FIXME shouldn't be that, it should be now */ + rtcpheader[5] = htonl(rtp->txcount); /* No. packets sent */ + rtcpheader[6] = htonl(rtp->txoctetcount); /* No. bytes sent */ + len += 28; + + extended = rtp->cycles + rtp->lastrxseqno; + expected = extended - rtp->seedrxseqno + 1; + if(rtp->rxcount > expected) { + expected += rtp->rxcount - expected; + } + lost = expected - rtp->rxcount; + expected_interval = expected - rtp->rtcp->expected_prior; + rtp->rtcp->expected_prior = expected; + received_interval = rtp->rxcount - rtp->rtcp->received_prior; + rtp->rtcp->received_prior = rtp->rxcount; + lost_interval = expected_interval - received_interval; + if(expected_interval == 0 || lost_interval <= 0) + fraction = 0; + else + fraction = (lost_interval << 8) / expected_interval; + timersub(&now, &rtp->rtcp->rxlsr, &dlsr); + rtcpheader[7] = htonl(rtp->themssrc); + rtcpheader[8] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff)); + rtcpheader[9] = htonl((rtp->cycles << 16) | ((rtp->lastrxseqno & 0xffff))); + rtcpheader[10] = htonl((unsigned int)rtp->rxjitter); + rtcpheader[11] = htonl(rtp->rtcp->themrxlsr); + rtcpheader[12] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000); + len += 24; + + rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SR << 16) | ((len/4)-1)); + + if (rtp->rtcp->sendfur) { + rtcpheader[13] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1); + rtcpheader[14] = htonl(rtp->ssrc); /* Our SSRC */ + len += 8; + rtp->rtcp->sendfur = 0; + } + + /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */ + /* it can change mid call, and SDES can't) */ + rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); + rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */ + rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */ + len += 12; + + res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them)); + if(res<0){ + ast_verbose("RTCP SR transmission error to %s:%d, rtcp halted %s\n",ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), strerror(errno)); + ast_sched_del(rtp->sched, rtp->rtcp->schedid); + rtp->rtcp->schedid = -1; + return 0; + } + + /* FIXME Don't need to get a new one */ + gettimeofday(&rtp->rtcp->txlsr, NULL); + rtp->rtcp->sr_count++; + + rtp->rtcp->lastsrtxcount = rtp->txcount; + + if(rtcp_debug_test_addr(&rtp->rtcp->them)){ + ast_verbose("Sent RTCP SR to %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + ast_verbose("Our SSRC: %u\n", rtp->ssrc); + ast_verbose("Sent(NTP): %u.%010u\n", (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096); + ast_verbose("Sent(RTP): %u\n", rtp->lastts); + ast_verbose("Sent packets: %u\n", rtp->txcount); + ast_verbose("Sent octets: %u\n", rtp->txoctetcount); + ast_verbose("Report block:\n"); + ast_verbose("Fraction lost: %u\n", fraction); + ast_verbose("Cumulative loss: %u\n", lost); + ast_verbose("IA jitter: %.4f\n", rtp->rxjitter); + ast_verbose("Their last SR: %u\n", rtp->rtcp->themrxlsr); + ast_verbose("DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[12])/65536.0)); + } + return res; +} + +int ast_rtcp_write_rr(void *data) +{ + struct ast_rtp *rtp = data; + int res; + int len = 32; + unsigned int lost; + unsigned int extended; + unsigned int expected; + unsigned int expected_interval; + unsigned int received_interval; + unsigned int lost_interval; + struct timeval now; + unsigned int *rtcpheader; + char bdata[1024]; + char iabuf[INET_ADDRSTRLEN]; + struct timeval dlsr; + int fraction; + + if(!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0)) + return 0; + + if(!rtp->rtcp->them.sin_addr.s_addr){ + ast_verbose("RTCP RR transmission error to, rtcp halted %s\n",strerror(errno)); + ast_sched_del(rtp->sched, rtp->rtcp->schedid); + rtp->rtcp->schedid = -1; + return 0; + } + + extended = rtp->cycles + rtp->lastrxseqno; + expected = extended - rtp->seedrxseqno + 1; + lost = expected - rtp->rxcount; + expected_interval = expected - rtp->rtcp->expected_prior; + rtp->rtcp->expected_prior = expected; + received_interval = rtp->rxcount - rtp->rtcp->received_prior; + rtp->rtcp->received_prior = rtp->rxcount; + lost_interval = expected_interval - received_interval; + if(expected_interval == 0 || lost_interval <= 0) + fraction = 0; + else + fraction = (lost_interval << 8) / expected_interval; + gettimeofday(&now, NULL); + timersub(&now, &rtp->rtcp->rxlsr, &dlsr); + rtcpheader = (unsigned int *)bdata; + rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_RR << 16) | ((len/4)-1)); + rtcpheader[1] = htonl(rtp->ssrc); + rtcpheader[2] = htonl(rtp->themssrc); + rtcpheader[3] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff)); + rtcpheader[4] = htonl((rtp->cycles << 16) | ((rtp->lastrxseqno & 0xffff))); + rtcpheader[5] = htonl((unsigned int)rtp->rxjitter); + rtcpheader[6] = htonl(rtp->rtcp->themrxlsr); + rtcpheader[7] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000); + + if (rtp->rtcp->sendfur) { + rtcpheader[8] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1); /* Header from page 36 in RFC 3550 */ + rtcpheader[9] = htonl(rtp->ssrc); /* Our SSRC */ + len += 8; + rtp->rtcp->sendfur = 0; + } + + /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */ + /* it can change mid call, and SDES can't) */ + rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); + rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */ + rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */ + len += 12; + + res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them)); + + if(res<0){ + ast_verbose("RTCP RR transmission error, rtcp halted: %s\n",strerror(errno)); + /* Remove the scheduler */ + ast_sched_del(rtp->sched, rtp->rtcp->schedid); + rtp->rtcp->schedid = -1; + return 0; + } + + rtp->rtcp->rr_count++; + + if(rtcp_debug_test_addr(&rtp->rtcp->them)){ + ast_verbose("\nSending RTCP RR to %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port)); + ast_verbose("Our SSRC: %u\n", rtp->ssrc); + ast_verbose("Their SSRC: %u\n", rtp->themssrc); + ast_verbose("Fraction lost: %d\n", fraction); + ast_verbose("Cumulative loss: %u\n", lost); + ast_verbose("IA jitter: %.4f\n", rtp->rxjitter); + ast_verbose("Their last SR: %u\n", rtp->rtcp->themrxlsr); + ast_verbose("DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[7])/65536.0)); + } + + return res; +} + +/* Write and RTCP packet to the far end */ +/* Decide if we are going to send an SR (with Reception Block) or RR */ +/* RR is sent if we have not sent any rtp packets in the previous interval */ +int ast_rtcp_write(void *data) +{ + struct ast_rtp *rtp = data; + int res; + + if (rtp->txcount > rtp->rtcp->lastsrtxcount) + res = ast_rtcp_write_sr(data); + else + res = ast_rtcp_write_rr(data); + + return res; +} + int ast_rtp_sendcng(struct ast_rtp *rtp, int level) { unsigned int *rtpheader; @@ -1215,7 +1750,7 @@ if (res <0) ast_log(LOG_ERROR, "RTP Comfort Noise Transmission error to %s:%d: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno)); if(rtp_debug_test_addr(&rtp->them)) - ast_verbose("Sent Comfort Noise RTP packet to %s:%d (type %d, seq %d, ts %d, len %d)\n" + ast_verbose("Sent Comfort Noise RTP packet to %s:%d (type %d, seq %d, ts %u, len %d)\n" , ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastts,res - hdrlen); } @@ -1291,6 +1826,13 @@ 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(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port)); ast_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN); } + }else{ + rtp->txcount++; + rtp->txoctetcount +=(res - hdrlen); + + if(rtp->rtcp->schedid<1){ + rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp); + } } if(rtp_debug_test_addr(&rtp->them)) @@ -1771,6 +2313,37 @@ return RESULT_SUCCESS; } +static int rtcp_do_debug_ip(int fd, int argc, char *argv[]) +{ + struct hostent *hp; + struct ast_hostent ahp; + char iabuf[INET_ADDRSTRLEN]; + int port = 0; + char *p, *arg; + if (argc != 5) + return RESULT_SHOWUSAGE; + + arg = argv[4]; + p = strstr(arg, ":"); + if (p){ + *p = '\0'; + p++; + port = atoi(p); + } + hp = ast_gethostbyname(arg, &ahp); + if (hp == NULL) + return RESULT_SHOWUSAGE; + rtcpdebugaddr.sin_family = AF_INET; + memcpy(&rtcpdebugaddr.sin_addr, hp->h_addr, sizeof(rtcpdebugaddr.sin_addr)); + rtcpdebugaddr.sin_port = htons(port); + if (port == 0) + ast_cli(fd, "RTCP Debugging Enabled for IP: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtcpdebugaddr.sin_addr)); + else + ast_cli(fd, "RTCP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtcpdebugaddr.sin_addr), port); + rtcpdebug = 1; + return RESULT_SUCCESS; +} + static int rtp_do_debug(int fd, int argc, char *argv[]) { if(argc != 2) { @@ -1784,6 +2357,27 @@ return RESULT_SUCCESS; } +static int rtcp_do_debug(int fd, int argc, char *argv[]){ + if(argc != 3){ + if(argc != 5) + return RESULT_SHOWUSAGE; + return rtcp_do_debug_ip(fd, argc, argv); + } + rtcpdebug = 1; + memset(&rtcpdebugaddr,0,sizeof(rtcpdebugaddr)); + ast_cli(fd, "RTCP Debugging Enabled\n"); + return RESULT_SUCCESS; +} + +static int rtcp_do_stats(int fd, int argc, char *argv[]){ + if(argc != 3){ + return RESULT_SHOWUSAGE; + } + rtcpstats = 1; + ast_cli(fd, "RTCP Stats Enabled\n"); + return RESULT_SUCCESS; +} + static int rtp_no_debug(int fd, int argc, char *argv[]) { if(argc !=3) @@ -1793,6 +2387,25 @@ return RESULT_SUCCESS; } +static int rtcp_no_debug(int fd, int argc, char *argv[]) +{ + if(argc !=4) + return RESULT_SHOWUSAGE; + rtcpdebug = 0; + ast_cli(fd,"RTCP Debugging Disabled\n"); + return RESULT_SUCCESS; +} + +static int rtcp_no_stats(int fd, int argc, char *argv[]) +{ + if(argc !=4) + return RESULT_SHOWUSAGE; + rtcpstats = 0; + ast_cli(fd,"RTCP Stats Disabled\n"); + return RESULT_SUCCESS; +} + + static char debug_usage[] = "Usage: rtp debug [ip host[:port]]\n" " Enable dumping of all RTP packets to and from host.\n"; @@ -1810,6 +2423,37 @@ static struct ast_cli_entry cli_no_debug = {{ "rtp", "no", "debug", NULL } , rtp_no_debug, "Disable RTP debugging", no_debug_usage }; +static char rtcp_debug_usage[] = + "Usage: rtp rtcp debug [ip host[:port]]\n" + " Enable dumping of all RTCP packets to and from host.\n"; + +static char rtcp_no_debug_usage[] = + "Usage: rtp rtcp no debug\n" + " Disable all RTCP debugging\n"; + +static char rtcp_stats_usage[] = + "Usage: rtp rtcp stats\n" + " Enable dumping of RTCP stats.\n"; + +static char rtcp_no_stats_usage[] = + "Usage: rtp rtcp no stats\n" + " Disable all RTCP stats\n"; + +static struct ast_cli_entry cli_debug_ip_rtcp = +{{ "rtp", "rtcp", "debug", "ip", NULL } , rtcp_do_debug, "Enable RTCP debugging on IP", rtcp_debug_usage }; + +static struct ast_cli_entry cli_debug_rtcp = +{{ "rtp", "rtcp", "debug", NULL } , rtcp_do_debug, "Enable RTCP debugging", rtcp_debug_usage }; + +static struct ast_cli_entry cli_no_debug_rtcp = +{{ "rtp", "rtcp", "no", "debug", NULL } , rtcp_no_debug, "Disable RTCP debugging", rtcp_no_debug_usage }; + +static struct ast_cli_entry cli_stats_rtcp = +{{ "rtp", "rtcp", "stats", NULL } , rtcp_do_stats, "Enable RTCP stats", rtcp_stats_usage }; + +static struct ast_cli_entry cli_no_stats_rtcp = +{{ "rtp", "rtcp", "no", "stats", NULL } , rtcp_no_stats, "Disable RTCP stats", rtcp_no_stats_usage }; + void ast_rtp_reload(void) { struct ast_config *cfg; @@ -1834,6 +2478,15 @@ if (rtpend > 65535) rtpend = 65535; } + if ((s = ast_variable_retrieve(cfg, "general", "rtcpinterval"))) { + rtcpinterval = atoi(s); + if (rtcpinterval == 0) + rtcpinterval = 0; /* Just so we're clear... it's zero */ + if (rtcpinterval < RTCP_MIN_INTERVALMS) + rtcpinterval = RTCP_MIN_INTERVALMS; /* This catches negative numbers too */ + if (rtcpinterval > RTCP_MAX_INTERVALMS) + rtcpinterval = RTCP_MAX_INTERVALMS; + } if ((s = ast_variable_retrieve(cfg, "general", "rtpchecksums"))) { #ifdef SO_NO_CHECK if (ast_false(s)) @@ -1871,5 +2524,14 @@ ast_cli_register(&cli_debug); ast_cli_register(&cli_debug_ip); ast_cli_register(&cli_no_debug); + + ast_cli_register(&cli_debug_rtcp); + ast_cli_register(&cli_debug_ip_rtcp); + ast_cli_register(&cli_no_debug_rtcp); + + ast_cli_register(&cli_stats_rtcp); + ast_cli_register(&cli_no_stats_rtcp); + ast_rtp_reload(); } +