diff -ur --exclude=CVS /home/stevek/src/asteriskCVS/libiax2/src/iax.c libiax2/src/iax.c --- /home/stevek/src/asteriskCVS/libiax2/src/iax.c 2004-04-12 10:24:50.000000000 -0400 +++ libiax2/src/iax.c 2004-05-14 17:47:06.000000000 -0400 @@ -57,6 +57,15 @@ #include "iax-client.h" #include "md5.h" +/* + work around jitter-buffer shrinking in asterisk: + channels/chan_iax2.c:schedule_delivery() shrinks jitter buffer by 2. + this causes frames timestamped 1ms apart to ( sometimes ) be delivered + out of order, and results in garbled audio. our temporary fix is to increase + the minimum number of ( timestamped ) milliseconds between frames to 3 ( 2 + 1 ). +*/ +#define IAX_MIN_TIMESTAMP_INCREMENT 3 + /* Define socket options for IAX2 sockets, based on platform * availability of flags */ #ifdef WIN32 @@ -96,7 +105,7 @@ #define MIN_RETRY_TIME 10 #define MAX_RETRY_TIME 10000 -#define MEMORY_SIZE 100 +#define MEMORY_SIZE 1000 #define TRANSFER_NONE 0 #define TRANSFER_BEGIN 1 @@ -135,6 +144,8 @@ unsigned int lastsent; /* Last transmitted voice timestamp */ unsigned int lastvoicets; + /* Next predicted voice ts */ + unsigned int nextpred; /* Our last measured ping time */ unsigned int pingtime; /* Address of peer */ @@ -286,7 +297,7 @@ #ifdef WIN32 #define DEBU #else -#define DEBU(...) +#define DEBU #endif #define G #endif @@ -425,10 +436,21 @@ return 0; } -static int calc_timestamp(struct iax_session *session, unsigned int ts) +static int calc_timestamp(struct iax_session *session, unsigned int ts, struct ast_frame *f) { int ms; struct timeval tv; + int voice = 0; + int genuine = 0; + + if (f) { + if (f->frametype == AST_FRAME_VOICE) { + voice = 1; + } else if (f->frametype == AST_FRAME_IAX) { + genuine = 1; + } + } + /* If this is the first packet we're sending, get our offset now. */ @@ -448,14 +470,35 @@ ms = (tv.tv_sec - session->offset.tv_sec) * 1000 + (tv.tv_usec - session->offset.tv_usec) / 1000; - /* Never send a packet with the same timestamp since timestamps can be used - to acknowledge certain packets */ - if ((unsigned) ms <= session->lastsent) - ms = session->lastsent + 1; + if (ms < 0) + ms = 0; + + if(voice) { + if(abs(ms - session->nextpred) <= 240) { + if(!session->nextpred) + session->nextpred = f->samples; + ms = session->nextpred; + } else + session->nextpred = ms; + } else { + /* On a dataframe, use last value + 3 (to accomodate jitter buffer shrinking) + if appropriate unless it's a genuine frame */ + if (genuine) { + if (ms <= session->lastsent) + ms = session->lastsent + 3; + } else if (abs(ms - session->lastsent) <= 240) { + ms = session->lastsent + 3; + } + + } /* Record the last sent packet for future reference */ session->lastsent = ms; + /* set next predicted ts based on 8khz samples */ + if(voice) + session->nextpred = session->nextpred + f->samples / 8; + return ms; } @@ -613,10 +656,10 @@ or delayed, with retransmission */ struct ast_iax2_full_hdr *fh; struct ast_iax2_mini_hdr *mh; - struct { - struct iax_frame fr2; - unsigned char buffer[4096]; /* Buffer -- must preceed fr2 */ - } buf; + struct { + struct iax_frame fr2; + unsigned char buffer[4096]; /* Buffer -- must preceed fr2 */ + } buf; struct iax_frame *fr; int res; int sendmini=0; @@ -631,9 +674,12 @@ return -1; } - /* Calculate actual timestamp */ + /* this must come before the next call to calc_timestamp() since + calc_timestamp() will change lastsent to the returned value */ lastsent = pvt->lastsent; - fts = calc_timestamp(pvt, ts); + + /* Calculate actual timestamp */ + fts = calc_timestamp(pvt, ts, f); if (((fts & 0xFFFF0000L) == (lastsent & 0xFFFF0000L)) /* High two bits are the same on timestamp, or sending on a trunk */ && @@ -755,13 +801,13 @@ #endif static int __send_command(struct iax_session *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno, - int now, int transfer, int final) + int now, int transfer, int final, int samples) { struct ast_frame f; f.frametype = type; f.subclass = command; f.datalen = datalen; - f.samples = 0; + f.samples = samples; f.mallocd = 0; f.offset = 0; #ifdef __GNUC__ @@ -775,7 +821,7 @@ static int send_command(struct iax_session *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno) { - return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0); + return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0, 0); } static int send_command_final(struct iax_session *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno) @@ -784,19 +830,25 @@ /* It is assumed that the callno has already been locked */ iax_predestroy(i); #endif - return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 1); + return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 1, 0); } static int send_command_immediate(struct iax_session *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno) { - return __send_command(i, type, command, ts, data, datalen, seqno, 1, 0, 0); + return __send_command(i, type, command, ts, data, datalen, seqno, 1, 0, 0, 0); } static int send_command_transfer(struct iax_session *i, char type, int command, unsigned int ts, char *data, int datalen) { - return __send_command(i, type, command, ts, data, datalen, 0, 0, 1, 0); + return __send_command(i, type, command, ts, data, datalen, 0, 0, 1, 0, 0); +} + +static int send_command_samples(struct iax_session *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno, int samples) +{ + return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 0, samples); } + int iax_transfer(struct iax_session *session, char *number) { static int res; //Return Code @@ -843,403 +895,6 @@ } -#if 0 -int iax_do_event(struct iax_session *session, struct iax_event *event) -{ - struct iax_frame f; - int left; - struct hostent *hp; - unsigned int ts; - char buf[32768]; - struct ast_iax2_full_hdr *fh = (struct ast_iax2_full_hdr *)buf; - struct iax_mini_hdr *mh = (struct iax_mini_hdr *)buf; - struct MD5Context md5; - char reply[32]; - char realreply[80]; - char *requeststr = fh->iedata; - -#define MYSNPRINTF snprintf(requeststr + strlen(requeststr), sizeof(buf) - sizeof(struct ast_iax2_full_hdr) - strlen(requeststr), - - bzero(buf, sizeof(buf)); - - /* Default some things in the frame */ - - f.session = session; - f.data = buf; - f.datalen = sizeof(struct ast_iax2_full_hdr); - left = sizeof(buf) - f.datalen; - f.retries = maxretries; - f.transfer = 0; - - /* Assume a full header and default some things */ - fh->scallno = htons((short)(session->callno | IAX_FLAG_FULL)); - fh->dcallno = htons((short)session->peercallno); - - /* Calculate timestamp */ - fh->ts = htonl(calc_timestamp(session, 0)); - - /* Start by using twice the pingtime */ - f.retrytime = session->pingtime * 2; - if (f.retrytime > MAX_RETRY_TIME) - f.retrytime = MAX_RETRY_TIME; - if (f.retrytime < MIN_RETRY_TIME) - f.retrytime = MIN_RETRY_TIME; - - f.next = NULL; - - /* Some sanity checks */ - - if (!event) { - DEBU(G "Event is null?\n"); - IAXERROR "Null event"); - return -1; - } - if (!session) - session = event->session; - if (!iax_session_valid(session)) { - DEBU(G "Session invalid for sending event\n"); - IAXERROR "Invalid session for transmitting event"); - } - - /* Send (possibly reliably) the correct frame given the kind - of event requested */ - - switch(event->etype) { - case IAX_EVENT_CONNECT: - /* Connect first */ - hp = gethostbyname(event->event.connect.hostname); - if (!hp) { - snprintf(iax_errstr, sizeof(iax_errstr), "Invalid hostname: %s", event->event.connect.hostname); - return -1; - } - memcpy(&session->peeraddr.sin_addr, hp->h_addr, sizeof(session->peeraddr.sin_addr)); - session->peeraddr.sin_port = htons(event->event.connect.portno); - session->peeraddr.sin_family = AF_INET; - fh->type = AST_FRAME_IAX; - fh->csub = IAX_COMMAND_NEW; - fh->seqno = htons(session->oseqno++); - if (event->event.connect.exten) - MYSNPRINTF "exten=%s;", event->event.connect.exten); - if (event->event.connect.callerid) - MYSNPRINTF "callerid=%s;", event->event.connect.callerid); - if (event->event.connect.dnid) - MYSNPRINTF "dnid=%s;", event->event.connect.dnid); - if (event->event.connect.context) - MYSNPRINTF "context=%s;", event->event.connect.context); - if (event->event.connect.username) - MYSNPRINTF "username=%s;", event->event.connect.username); - if (event->event.connect.language) - MYSNPRINTF "language=%s;", event->event.connect.language); - MYSNPRINTF "formats=%d;", sformats); - MYSNPRINTF "version=%d;", IAX_PROTO_VERSION); - f.datalen += strlen(requeststr); - if (event->event.connect.username) - strncpy(session->username, event->event.connect.username, sizeof(session->username) - 1); - else - strcpy(session->username, ""); - if (event->event.connect.secret) - strncpy(session->secret, event->event.connect.secret, sizeof(session->secret) - 1); - else - strcpy(session->secret, ""); - iax_reliable_xmit(&f); - break; - case IAX_EVENT_REREQUEST: - fh->type = AST_FRAME_IAX; - fh->csub = IAX_COMMAND_REGREQ; - fh->seqno = htons(session->oseqno++); - MYSNPRINTF "peer=%s;refresh=%d;", session->peer, session->refresh); - if (strstr(session->methods, "md5")) { - MD5Init(&md5); - MD5Update(&md5, (const unsigned char *) &session->challenge[0], strlen(session->challenge)); - MD5Update(&md5, (const unsigned char *) &session->secret[0], strlen(session->secret)); - MD5Final((unsigned char *) reply, &md5); - memset(realreply, 0, sizeof(realreply)); - convert_reply(realreply, (unsigned char *) &reply[0]); - MYSNPRINTF "md5secret=%s;", realreply); - } else { - MYSNPRINTF "secret=%s;", session->secret); - } - f.datalen += strlen(requeststr); - iax_reliable_xmit(&f); - break; - case IAX_EVENT_REGREQ: - /* Connect first */ - hp = gethostbyname(event->event.regrequest.server); - if (!hp) { - snprintf(iax_errstr, sizeof(iax_errstr), "Invalid hostname: %s", event->event.regrequest.server); - return -1; - } - memcpy(&session->peeraddr.sin_addr, hp->h_addr, sizeof(session->peeraddr.sin_addr)); - session->peeraddr.sin_port = htons(event->event.regrequest.portno); - session->peeraddr.sin_family = AF_INET; - fh->type = AST_FRAME_IAX; - fh->csub = IAX_COMMAND_REGREQ; - fh->seqno = htons(session->oseqno++); - if (event->event.regrequest.secret) - strncpy(session->secret, event->event.regrequest.secret, sizeof(session->secret)-1); - else - strcpy(session->secret, ""); - if (event->event.regrequest.peer) { - MYSNPRINTF "peer=%s;", event->event.regrequest.peer); - strncpy(session->peer, event->event.regrequest.peer, sizeof(session->peer)-1); - } else - strcpy(session->peer, ""); - if (event->event.regrequest.refresh) { - MYSNPRINTF "refresh=%d;", event->event.regrequest.refresh); - session->refresh = event->event.regrequest.refresh; - } else - session->refresh = 0; - f.datalen += strlen(requeststr); - iax_reliable_xmit(&f); - break; - case IAX_EVENT_AUTHRP: - fh->type = AST_FRAME_IAX; - fh->csub = IAX_COMMAND_AUTHREP; - fh->seqno = htons(session->oseqno++); - if (event->event.authreply.authmethod == IAX_AUTHMETHOD_MD5) { - snprintf(requeststr, left, "md5secret=%s;", event->event.authreply.reply); - } else if (event->event.authreply.authmethod == IAX_AUTHMETHOD_PLAINTEXT) { - snprintf(requeststr, left, "secret=%s;", event->event.authreply.reply); - } else { - DEBU(G "Unknown auth method: %d\n", event->event.authreply.authmethod); - IAXERROR "Invalid authentication method %d\n", event->event.authreply.authmethod); - return -1; - } - f.datalen += strlen(requeststr); - iax_reliable_xmit(&f); - break; - case IAX_EVENT_LAGRP: - /* Special case -- return the original timestamp in the message instead of our - own. */ - fh->type = AST_FRAME_IAX; - fh->csub = IAX_COMMAND_LAGRP; - fh->ts = htonl(event->event.lagrq.ts); - fh->seqno = htons(session->oseqno++); - iax_reliable_xmit(&f); - break; - case IAX_EVENT_DTMF: - /* Send a DTMF tone as a reliable transmission -- easy */ - fh->type = AST_FRAME_DTMF; - fh->seqno = htons(session->oseqno++); - fh->csub = event->event.dtmf.digit; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_RINGA: - /* Announce that we are ringing */ - fh->type = AST_FRAME_CONTROL; - fh->seqno = htons(session->oseqno++); - fh->csub = AST_CONTROL_RINGING; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_HANGUP: - fh->type = AST_FRAME_IAX; - fh->seqno = htons(session->oseqno++); - fh->csub = IAX_COMMAND_HANGUP; - if (event->event.hangup.byemsg) { - strncpy(fh->data, event->event.hangup.byemsg, left-1); - f.datalen += strlen(fh->data); - } - /* XXX Not really reliable since we turn right around and kill it XXX */ - iax_reliable_xmit(&f); - destroy_session(session); - break; - case IAX_EVENT_REJECT: - fh->type = AST_FRAME_IAX; - fh->seqno = htons(session->oseqno++); - fh->csub = IAX_COMMAND_REJECT; - strncpy(fh->data, event->event.reject.reason, left-1); - f.datalen += strlen(fh->data); - /* XXX Not really reliable since we turn right around and kill it XXX */ - iax_reliable_xmit(&f); - destroy_session(session); - break; - case IAX_EVENT_BUSY: - fh->type = AST_FRAME_CONTROL; - fh->seqno = htons(session->oseqno++); - fh->csub = AST_CONTROL_BUSY; - /* XXX Not really reliable since we turn right around and kill it XXX */ - iax_reliable_xmit(&f); - destroy_session(session); - break; - case IAX_EVENT_ACCEPT: - fh->type = AST_FRAME_IAX; - fh->seqno = htons(session->oseqno++); - fh->csub = IAX_COMMAND_ACCEPT; - f.datalen += snprintf((char *)(fh->data), left, "formats=%d;", sformats); - f.datalen++; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_LAGRQ: - fh->type = AST_FRAME_IAX; - fh->seqno = htons(session->oseqno++); - fh->csub = IAX_COMMAND_LAGRQ; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_ANSWER: - fh->type = AST_FRAME_CONTROL; - fh->seqno = htons(session->oseqno++); - fh->csub = AST_CONTROL_ANSWER; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_PONG: - fh->type = AST_FRAME_IAX; - fh->csub = IAX_COMMAND_PONG; - fh->ts = htonl(event->event.ping.ts); - iax_reliable_xmit(&f); - break; - case IAX_EVENT_URL: - fh->type = AST_FRAME_HTML; - fh->seqno = htons(session->oseqno++); - if (event->event.url.link) - fh->csub = AST_HTML_LINKURL; - else - fh->csub = AST_HTML_URL; - if (event->event.url.url) { - f.datalen += strlen(event->event.url.url) + 1; - strcpy(fh->data, event->event.url.url); - } - iax_reliable_xmit(&f); - break; - case IAX_EVENT_TEXT: - fh->type = AST_FRAME_TEXT; - fh->seqno = htons(session->oseqno++); - fh->csub = AST_FRAME_TEXT; - f.datalen += strlen(event->event.text.text) + 1; - strcpy(fh->data, event->event.text.text); - - iax_reliable_xmit(&f); - break; - - case IAX_EVENT_UNLINK: - fh->type = AST_FRAME_HTML; - fh->seqno = htons(session->oseqno++); - fh->csub = AST_HTML_UNLINK; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_LINKREJECT: - fh->type = AST_FRAME_HTML; - fh->seqno = htons(session->oseqno++); - fh->csub = AST_HTML_LINKREJECT; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_LDCOMPLETE: - fh->type = AST_FRAME_HTML; - fh->seqno = htons(session->oseqno++); - fh->csub = AST_HTML_LDCOMPLETE; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_VOICE: - ts = ntohl(fh->ts); - if (event->event.voice.datalen > left) { - strcpy(iax_errstr, "Voice frame too large\n"); - } - /* Don't do anything if we're quelching audio */ - if (session->quelch) - break; - /* If the voice format is the same, and the top of our - timestamp is the same, then we stick with a mini-frame, otherwise - we send a large frame */ - if ((event->event.voice.format == session->svoiceformat) && - ((session->lastvoicets & 0xFFFF0000) == (ts & 0xFFFF0000))) { - /* We can send a mini-frame since we're using the same - voice format and don't need a timestamp update. */ - f.datalen += event->event.voice.datalen; - f.datalen -= sizeof(struct ast_iax2_full_hdr) - sizeof(struct iax_mini_hdr); - memcpy(mh->data, event->event.voice.data, event->event.voice.datalen); - mh->ts = htons((short)(ts & 0x0000FFFF)); - mh->callno = htons((short)session->callno); - session->lastvoicets = ts; - iax_xmit_frame(&f); - } else { - /* Send a full frame for our voice frame */ - fh->type = AST_FRAME_VOICE; - fh->csub = compress_subclass(event->event.voice.format); - session->svoiceformat = event->event.voice.format; - fh->seqno = htons((short) session->oseqno++); - memcpy(fh->data, event->event.voice.data, event->event.voice.datalen); - f.datalen += event->event.voice.datalen; - session->lastvoicets = ts; - iax_reliable_xmit(&f); - } - break; - case IAX_EVENT_IMAGE: - fh->type = AST_FRAME_IMAGE; - fh->csub = compress_subclass(event->event.image.format); - fh->seqno = htons((short) session->oseqno++); - memcpy(fh->data, event->event.image.data, event->event.image.datalen); - f.datalen += event->event.image.datalen; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_DIAL: - fh->type = AST_FRAME_IAX; - fh->seqno = htons(session->oseqno++); - fh->csub = IAX_COMMAND_DIAL; - MYSNPRINTF "%s", event->event.dial.number); - f.datalen += strlen(requeststr); - iax_reliable_xmit(&f); - break; - case IAX_EVENT_DPREQ: - fh->type = AST_FRAME_IAX; - fh->seqno = htons(session->oseqno++); - fh->csub = IAX_COMMAND_DPREQ; - MYSNPRINTF "%s", event->event.dpreq.number); - f.datalen += strlen(requeststr); - iax_reliable_xmit(&f); - break; - case IAX_EVENT_TXREPLY: - /* Transmit an IAX Transmit request */ - fh->type = AST_FRAME_IAX; - fh->seqno = htons(0); - fh->csub = IAX_COMMAND_TXCNT; - fh->dcallno = htons((short)session->transfercallno); - f.transfer = 1; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_TXREJECT: - /* Reject the IAX transfer -- the peer couldn't see us or we couldn't see them */ - fh->type = AST_FRAME_IAX; - fh->seqno = htons(session->oseqno++); - fh->csub = IAX_COMMAND_TXREJ; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_TXACCEPT: - /* Accept a connect request */ - fh->type = AST_FRAME_IAX; - fh->seqno = htons(0); - fh->csub = IAX_COMMAND_TXACC; - fh->dcallno = htons((short)session->transfercallno); - f.transfer = 1; - f.retries = -1; - iax_xmit_frame(&f); - break; - case IAX_EVENT_TXREADY: - /* We've been accepted on the transfer. Notify the gateway that we're ready */ - fh->type = AST_FRAME_IAX; - fh->seqno = htons(session->oseqno++); - fh->csub = IAX_COMMAND_TXREADY; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_QUELCH: - fh->type = AST_FRAME_IAX; - fh->seqno = htons(session->oseqno++); - fh->csub = IAX_COMMAND_QUELCH; - iax_reliable_xmit(&f); - break; - case IAX_EVENT_UNQUELCH: - fh->type = AST_FRAME_IAX; - fh->seqno = htons(session->oseqno++); - fh->csub = IAX_COMMAND_UNQUELCH; - iax_reliable_xmit(&f); - break; - default: - DEBU(G "Don't know how to send a %d event\n", event->etype); - IAXERROR "Unknown event type.\n"); - } - return 0; -} - -#endif - static void destroy_session(struct iax_session *session) { struct iax_session *cur, *prev=NULL; @@ -1327,12 +982,11 @@ return send_command(session, AST_FRAME_DTMF, digit, 0, NULL, 0, -1); } -int iax_send_voice(struct iax_session *session, int format, char *data, int datalen) +int iax_send_voice(struct iax_session *session, int format, char *data, int datalen, int samples) { - /* Send a (possibly compressed) voice frame */ if (!session->quelch) - return send_command(session, AST_FRAME_VOICE, format, 0, data, datalen, -1); + return send_command_samples(session, AST_FRAME_VOICE, format, 0, data, datalen, -1, samples); return 0; } @@ -1750,7 +1404,7 @@ #define FUDGE 1 -static struct iax_event *schedule_delivery(struct iax_event *e, unsigned int ts) +static struct iax_event *schedule_delivery(struct iax_event *e, unsigned int ts, int updatehistory) { /* * This is the core of the IAX jitterbuffer delivery mechanism: @@ -1789,6 +1443,15 @@ /* How many ms from now should this packet be delivered? (remember this can be a negative number, too */ ms = calc_rxstamp(e->session) - ts; + + /* Drop voice frame if timestamp is way off */ + if ((e->etype == IAX_EVENT_VOICE) && ((ms > 65536) || (ms < -65536))) { + DEBU(G "Dropping a voice packet with odd ts (ts = %d; ms = %d)\n", ts, ms); + free(e); + return NULL; + } + + /* Adjust if voice frame timestamp is off by a step */ if (ms > 32768) { /* What likely happened here is that our counter has circled but we haven't gotten the update from the main packet. We'll just pretend that we did, and @@ -1805,11 +1468,13 @@ printf("rxstamp is %d, timestamp is %d, ms is %d\n", calc_rxstamp(e->session), ts, ms); #endif /* Rotate history queue. Leading 0's are irrelevant. */ - for (x=0; x < MEMORY_SIZE - 1; x++) - e->session->history[x] = e->session->history[x+1]; - - /* Add new entry for this time */ - e->session->history[x] = ms; + if (updatehistory) { + for (x=0; x < MEMORY_SIZE - 1; x++) + e->session->history[x] = e->session->history[x+1]; + + /* Add new entry for this time */ + e->session->history[x] = ms; + } /* We have to find the maximum and minimum time delay we've had to deliver. */ min = e->session->history[0]; @@ -1850,7 +1515,7 @@ #ifdef EXTREME_DEBUG DEBU(G "Shrinking jitterbuffer (target = %d, current = %d...\n", max, e->session->jitterbuffer); #endif - e->session->jitterbuffer -= 2; + e->session->jitterbuffer -= 1; } /* Keep the jitter buffer from becoming unreasonably large */ @@ -1943,7 +1608,9 @@ int nowts; int updatehistory = 1; ts = ntohl(fh->ts); - session->last_ts = ts; + /* don't run last_ts backwards; i.e. for retransmits and the like */ + if (ts > session->last_ts) + session->last_ts = ts; e = (struct iax_event *)malloc(sizeof(struct iax_event) + datalen + 1); #ifdef DEBUG_SUPPORT @@ -1986,7 +1653,7 @@ } /* Check where we are */ - if (ntohs(fh->dcallno) & IAX_FLAG_RETRANS) + if ((ntohs(fh->dcallno) & IAX_FLAG_RETRANS) || (fh->type != AST_FRAME_VOICE)) updatehistory = 0; if ((session->iseqno != fh->oseqno) && (session->iseqno || @@ -2036,7 +1703,8 @@ case AST_FRAME_DTMF: e->etype = IAX_EVENT_DTMF; e->subclass = subclass; - return schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); + break; case AST_FRAME_VOICE: e->etype = IAX_EVENT_VOICE; e->subclass = subclass; @@ -2045,7 +1713,8 @@ memcpy(e->data, fh->iedata, datalen); e->datalen = datalen; } - return schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); + break; case AST_FRAME_IAX: /* Parse IE's */ if (datalen) { @@ -2062,7 +1731,7 @@ case IAX_COMMAND_NEW: /* This is a new, incoming call */ e->etype = IAX_EVENT_CONNECT; - e = schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_AUTHREQ: /* This is a request for a call */ @@ -2075,46 +1744,44 @@ e = NULL; break; } - e = schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_HANGUP: e->etype = IAX_EVENT_HANGUP; - e = schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_INVAL: e->etype = IAX_EVENT_HANGUP; - e = schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_REJECT: e->etype = IAX_EVENT_REJECT; - e = schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_ACK: - e = NULL; + e = NULL; break; case IAX_COMMAND_LAGRQ: /* Pass this along for later handling */ e->etype = IAX_EVENT_LAGRQ; e->ts = ts; - e = schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_PING: - /* Just immediately reply */ + /* PINGS and PONGS don't get scheduled; */ e->etype = IAX_EVENT_PING; e->ts = ts; - e = schedule_delivery(e, ts); break; case IAX_COMMAND_PONG: e->etype = IAX_EVENT_PONG; - e = schedule_delivery(e, ts); break; case IAX_COMMAND_ACCEPT: e->etype = IAX_EVENT_ACCEPT; - e = schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_REGACK: e->etype = IAX_EVENT_REGACK; - e = schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_REGAUTH: iax_regauth_reply(session, session->secret, e->ies.challenge, e->ies.authmethods); @@ -2123,11 +1790,11 @@ break; case IAX_COMMAND_REGREJ: e->etype = IAX_EVENT_REGREJ; - e = schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_LAGRP: e->etype = IAX_EVENT_LAGRP; - nowts = calc_timestamp(session, 0); + nowts = calc_timestamp(session, 0, NULL); e->ts = nowts - ts; e->subclass = session->jitter; /* Can't call schedule_delivery since timestamp is non-normal */ @@ -2169,7 +1836,8 @@ iax_send_txready(session); } free(e); - return NULL; + e = NULL; + break; case IAX_COMMAND_TXREL: /* Release the transfer */ session->peercallno = e->ies.callno; @@ -2202,35 +1870,40 @@ sch->frame->retries = -1; sch = sch->next; } - return e; + break; case IAX_COMMAND_QUELCH: e->etype = IAX_EVENT_QUELCH; session->quelch = 1; - return e; + break; case IAX_COMMAND_UNQUELCH: e->etype = IAX_EVENT_UNQUELCH; session->quelch = 0; - return e; + break; default: DEBU(G "Don't know what to do with IAX command %d\n", subclass); free(e); e = NULL; } - if (session->aseqno != session->iseqno) + + if (session->aseqno != session->iseqno) { send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_ACK, ts, NULL, 0, fh->iseqno); + } break; case AST_FRAME_CONTROL: switch(subclass) { case AST_CONTROL_ANSWER: e->etype = IAX_EVENT_ANSWER; - return schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); + break; case AST_CONTROL_CONGESTION: case AST_CONTROL_BUSY: e->etype = IAX_EVENT_BUSY; - return schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); + break; case AST_CONTROL_RINGING: e->etype = IAX_EVENT_RINGA; - return schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); + break; default: DEBU(G "Don't know what to do with AST control %d\n", subclass); free(e); @@ -2243,14 +1916,16 @@ if (datalen) { memcpy(e->data, fh->iedata, datalen); } - return schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); + break; case AST_FRAME_TEXT: e->etype = IAX_EVENT_TEXT; if (datalen) { memcpy(e->data, fh->iedata, datalen); } - return schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); + break; case AST_FRAME_HTML: switch(fh->csub) { @@ -2263,16 +1938,20 @@ if (datalen) { memcpy(e->data, fh->iedata, datalen); } - return schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); + break; case AST_HTML_LDCOMPLETE: e->etype = IAX_EVENT_LDCOMPLETE; - return schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); + break; case AST_HTML_UNLINK: e->etype = IAX_EVENT_UNLINK; - return schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); + break; case AST_HTML_LINKREJECT: e->etype = IAX_EVENT_LINKREJECT; - return schedule_delivery(e, ts); + e = schedule_delivery(e, ts, updatehistory); + break; default: DEBU(G "Don't know how to handle HTML type %d frames\n", fh->csub); free(e); @@ -2286,7 +1965,12 @@ } } else DEBU(G "Out of memory\n"); - return NULL; + + /* Already ack'd iax frames */ + if ((fh->type != AST_FRAME_IAX) && (session->aseqno != session->iseqno)) { + send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_ACK, ts, NULL, 0, fh->iseqno); + } + return e; } static struct iax_event *iax_miniheader_to_event(struct iax_session *session, @@ -2295,6 +1979,7 @@ { struct iax_event *e; unsigned int ts; + int updatehistory = 1; e = (struct iax_event *)malloc(sizeof(struct iax_event) + datalen); if (e) { if (session->voiceformat > 0) { @@ -2309,7 +1994,7 @@ e->datalen = datalen; } ts = (session->last_ts & 0xFFFF0000) | ntohs(mh->ts); - return schedule_delivery(e, ts); + return schedule_delivery(e, ts, updatehistory); } else { DEBU(G "No last format received on session %d\n", session->callno); free(e); @@ -2484,7 +2169,7 @@ nextEventTime = iax_time_to_next_event(); if(nextEventTime < 0) - select(netfd + 1, &fds, NULL, NULL,NULL); + select(netfd + 1, &fds, NULL, NULL, NULL); else { struct timeval nextEvent; @@ -2517,3 +2202,19 @@ things, or can add it to a network add sort of gtk_input_add for example */ return netfd; } + +int iax_quelch_moh(struct iax_session *session, int MOH) +{ + + struct iax_ie_data ied; //IE Data Structure (Stuff To Send) + memset(&ied, 0, sizeof(ied)); + + // You can't quelch the quelched + if (session->quelch == 1) + return -1; + + if (MOH) + iax_ie_append(&ied, IAX_IE_MUSICONHOLD); + + return send_command(session, AST_FRAME_IAX, IAX_COMMAND_QUELCH, 0, ied.buf, ied.pos, -1); +} diff -ur --exclude=CVS /home/stevek/src/asteriskCVS/libiax2/src/iax-client.h libiax2/src/iax-client.h --- /home/stevek/src/asteriskCVS/libiax2/src/iax-client.h 2004-04-12 10:24:50.000000000 -0400 +++ libiax2/src/iax-client.h 2004-05-05 10:53:00.000000000 -0400 @@ -126,7 +126,7 @@ /* Front ends for sending events */ extern void iax_set_formats(int fmt); extern int iax_send_dtmf(struct iax_session *session, char digit); -extern int iax_send_voice(struct iax_session *session, int format, char *data, int datalen); +extern int iax_send_voice(struct iax_session *session, int format, char *data, int datalen, int samples); extern int iax_send_image(struct iax_session *session, int format, char *data, int datalen); extern int iax_send_url(struct iax_session *session, char *url, int link); extern int iax_send_text(struct iax_session *session, char *text); @@ -149,6 +149,7 @@ extern int iax_quelch(struct iax_session *session); extern int iax_unquelch(struct iax_session * session); extern int iax_transfer(struct iax_session *session, char *number); +extern int iax_quelch_moh(struct iax_session *session, int MOH); extern void iax_destroy(struct iax_session * session); Only in libiax2/src: iax.c.orig Only in libiax2/src: iax.c.rej Only in libiax2/src: .iax.c.swp Only in libiax2/src: iax.o Only in libiax2/src: md5.o