📄 iax.c
字号:
#ifdef VOICE_SMOOTHING if (e->etype == IAX_EVENT_VOICE) { /* Smooth voices if we know enough about the format */ switch(e->event.voice.format) { case AST_FORMAT_GSM: /* GSM frames are 20 ms long, although there could be periods of silence. If the time is < 50 ms, assume it ought to be 20 ms */ if (ts - e->session->lastts < 50) ts = e->session->lastts + 20;#ifdef EXTREME_DEBUG display_time(ts);#endif break; default: /* Can't do anything */ } e->session->lastts = ts; }#endif#ifdef NEWJB { int type = JB_TYPE_CONTROL; int len = 0; if(e->etype == IAX_EVENT_VOICE) { type = JB_TYPE_VOICE; /* XXX: Should probably parse this in full frames, then * store that, and use this for miniframes, instead of * hardcoding this guess here */ len = (e->subclass == AST_FORMAT_ILBC) ? 30 : 20; } else if(e->etype == IAX_EVENT_CNG) { type = JB_TYPE_SILENCE; } /* insert into jitterbuffer */ /* TODO: Perhaps we could act immediately if it's not droppable and late */ if(jb_put(e->session->jb, e, type, len, unwrap_timestamp(ts,e->session->last_ts), calc_rxstamp(e->session)) == JB_DROP) { iax_event_free(e); } }#else /* 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 update the timestamp appropriately. */ ms -= 65536; } if (ms < -32768) { /* We got this packet out of order. Lets add 65536 to it to bring it into our new time frame */ ms += 65536; }#if 0 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. */ 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]; for (z=0;z < iax_dropcount + 1; z++) { /* We drop the top iax_dropcount entries. iax_dropcount represents a tradeoff between quality of voice and latency. 3% drop seems to be unnoticable to the client and can significantly improve latency. We add one more to our droplist, but that's the one we actually use, and don't drop. */ max = -99999999; for (x=0;x<MEMORY_SIZE;x++) { if (max < e->session->history[x]) { /* New candidate value. Make sure we haven't dropped it. */ match=0; for(y=0;!match && (y<z); y++) match |= (drops[y] == x); /* If there is no match, this is our new maximum */ if (!match) { max = e->session->history[x]; maxone = x; } } if (!z) { /* First pass, calcualte our minimum, too */ if (min > e->session->history[x]) min = e->session->history[x]; } } drops[z] = maxone; } /* Again, just for reference. The "jitter buffer" is the max. The difference is the perceived jitter correction. */ e->session->jitter = max - min; /* If the jitter buffer is substantially too large, shrink it, slowly enough that the client won't notice ;-) . */ if (max < e->session->jitterbuffer - max_extra_jitterbuffer) {#ifdef EXTREME_DEBUG DEBU(G "Shrinking jitterbuffer (target = %d, current = %d...\n", max, e->session->jitterbuffer);#endif e->session->jitterbuffer -= 1; } /* Keep the jitter buffer from becoming unreasonably large */ if (max > min + max_jitterbuffer) { DEBU(G "Constraining jitter buffer (min = %d, max = %d)...\n", min, max); max = min + max_jitterbuffer; } /* If the jitter buffer is too small, we immediately grow our buffer to accomodate */ if (max > e->session->jitterbuffer) e->session->jitterbuffer = max; /* Start with our jitter buffer delay, and subtract the lateness (or earliness). Remember these times are all relative to the first packet, so their absolute values are really irrelevant. */ ms = e->session->jitterbuffer - ms - IAX_SCHEDULE_FUZZ; /* If the jitterbuffer is disabled, always deliver immediately */ if (!iax_use_jitterbuffer) ms = 0; if (ms < 1) {#ifdef EXTREME_DEBUG DEBU(G "Calculated delay is only %d\n", ms);#endif if ((ms > -4) || (e->etype != IAX_EVENT_VOICE)) { /* Return the event immediately if it's it's less than 3 milliseconds too late, or if it's not voice (believe me, you don't want to just drop a hangup frame because it's late, or a ping, or some such. That kinda ruins retransmissions too ;-) */ /* Queue for immediate delivery */ iax_sched_add(e, NULL, NULL, NULL, 0); return NULL; //return e; } DEBU(G "(not so) Silently dropping a packet (ms = %d)\n", ms); /* Silently discard this as if it were to be delivered */ free(e); return NULL; } /* We need this to be delivered in the future, so we use our scheduler */ iax_sched_add(e, NULL, NULL, NULL, ms);#ifdef EXTREME_DEBUG DEBU(G "Delivering packet in %d ms\n", ms);#endif#endif /* NEWJB */ return NULL; }static int uncompress_subclass(unsigned char csub){ /* If the SC_LOG flag is set, return 2^csub otherwise csub */ if (csub & IAX_FLAG_SC_LOG) return 1 << (csub & ~IAX_FLAG_SC_LOG & IAX_MAX_SHIFT); else return csub;}static#ifndef WIN32inline#endifchar *extract(char *src, char *string){ /* Extract and duplicate what we need from a string */ char *s, *t; s = strstr(src, string); if (s) { s += strlen(string); s = strdup(s); /* End at ; */ t = strchr(s, ';'); if (t) { *t = '\0'; } } return s; }static struct iax_event *iax_header_to_event(struct iax_session *session, struct ast_iax2_full_hdr *fh, int datalen, struct sockaddr_in *sin){ struct iax_event *e; struct iax_sched *sch; unsigned int ts; int subclass = uncompress_subclass(fh->csub); int nowts; int updatehistory = 1; ts = ntohl(fh->ts); /* don't run last_ts backwards; i.e. for retransmits and the like */ if (ts > session->last_ts) session->last_ts = ts;#ifdef DEBUG_SUPPORT iax_showframe(NULL, fh, 1, sin, datalen);#endif /* Get things going with it, timestamp wise, if we haven't already. */ /* Handle implicit ACKing unless this is an INVAL, and only if this is from the real peer, not the transfer peer */ if (!inaddrcmp(sin, &session->peeraddr) && (((subclass != IAX_COMMAND_INVAL)) || (fh->type != AST_FRAME_IAX))) { unsigned char x; /* XXX This code is not very efficient. Surely there is a better way which still properly handles boundary conditions? XXX */ /* First we have to qualify that the ACKed value is within our window */ for (x=session->rseqno; x != session->oseqno; x++) if (fh->iseqno == x) break; if ((x != session->oseqno) || (session->oseqno == fh->iseqno)) { /* The acknowledgement is within our window. Time to acknowledge everything that it says to */ for (x=session->rseqno; x != fh->iseqno; x++) { /* Ack the packet with the given timestamp */ DEBU(G "Cancelling transmission of packet %d\n", x); sch = schedq; while(sch) { if (sch->frame && (sch->frame->session == session) && (sch->frame->oseqno == x)) sch->frame->retries = -1; sch = sch->next; } } /* Note how much we've received acknowledgement for */ session->rseqno = fh->iseqno; } else DEBU(G "Received iseqno %d not within window %d->%d\n", fh->iseqno, session->rseqno, session->oseqno); } /* Check where we are */ if ((ntohs(fh->dcallno) & IAX_FLAG_RETRANS) || (fh->type != AST_FRAME_VOICE)) updatehistory = 0; if ((session->iseqno != fh->oseqno) && (session->iseqno || ((subclass != IAX_COMMAND_TXREADY) && (subclass != IAX_COMMAND_TXREL) && (subclass != IAX_COMMAND_TXCNT) && (subclass != IAX_COMMAND_TXACC)) || (fh->type != AST_FRAME_IAX))) { if ( ((subclass != IAX_COMMAND_ACK) && (subclass != IAX_COMMAND_INVAL) && (subclass != IAX_COMMAND_TXREADY) && (subclass != IAX_COMMAND_TXREL) && (subclass != IAX_COMMAND_TXCNT) && (subclass != IAX_COMMAND_TXACC) && (subclass != IAX_COMMAND_VNAK)) || (fh->type != AST_FRAME_IAX)) { /* If it's not an ACK packet, it's out of order. */ DEBU(G "Packet arrived out of order (expecting %d, got %d) (frametype = %d, subclass = %d)\n", session->iseqno, fh->oseqno, fh->type, subclass); if (session->iseqno > fh->oseqno) { /* If we've already seen it, ack it XXX There's a border condition here XXX */ if ((fh->type != AST_FRAME_IAX) || ((subclass != IAX_COMMAND_ACK) && (subclass != IAX_COMMAND_INVAL))) { DEBU(G "Acking anyway\n"); /* XXX Maybe we should handle its ack to us, but then again, it's probably outdated anyway, and if we have anything to send, we'll retransmit and get an ACK back anyway XXX */ send_command_immediate(session, AST_FRAME_IAX, IAX_COMMAND_ACK, ts, NULL, 0,fh->iseqno); } } else { /* Send a VNAK requesting retransmission */ iax2_vnak(session); } return NULL; } } else { /* Increment unless it's an ACK or VNAK */ if (((subclass != IAX_COMMAND_ACK) && (subclass != IAX_COMMAND_INVAL) && (subclass != IAX_COMMAND_TXCNT) && (subclass != IAX_COMMAND_TXACC) && (subclass != IAX_COMMAND_VNAK)) || (fh->type != AST_FRAME_IAX)) session->iseqno++; } e = (struct iax_event *)malloc(sizeof(struct iax_event) + datalen + 1); if (e) { memset(e, 0, sizeof(struct iax_event) + datalen); /* Set etype to some unknown value so do not inavertently sending IAX_EVENT_CONNECT event, which is 0 to application. */ e->etype = -1; e->session = session; switch(fh->type) { case AST_FRAME_DTMF: e->etype = IAX_EVENT_DTMF; e->subclass = subclass; /* We want the DTMF event deliver immediately so all I/O can be terminate quickly in an IVR system. e = schedule_delivery(e, ts, updatehistory); */ break; case AST_FRAME_VOICE: e->etype = IAX_EVENT_VOICE; e->subclass = subclass; session->voiceformat = subclass; if (datalen) { memcpy(e->data, fh->iedata, datalen); e->datalen = datalen; } e = schedule_delivery(e, ts, updatehistory); break; case AST_FRAME_CNG: e->etype = IAX_EVENT_CNG; e->subclass = subclass; if (datalen) { memcpy(e->data, fh->iedata, datalen); e->datalen = datalen; } e = schedule_delivery(e, ts, updatehistory); break; case AST_FRAME_IAX: /* Parse IE's */ if (datalen) { memcpy(e->data, fh->iedata, datalen); e->datalen = datalen; } if (iax_parse_ies(&e->ies, e->data, e->datalen)) { IAXERROR "Unable to parse IE's"); free(e); e = NULL; break; } switch(subclass) { case IAX_COMMAND_NEW: /* This is a new, incoming call */ /* save the capability for validation */ session->capability = e->ies.capability; e->etype = IAX_EVENT_CONNECT; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_AUTHREQ: /* This is a request for a call */ e->etype = IAX_EVENT_AUTHRQ; if (strlen(session->username) && !strcmp(e->ies.username, session->username) && strlen(session->secret)) { /* Hey, we already know this one */ iax_auth_reply(session, session->secret, e->ies.challenge, e->ies.authmethods); free(e); e = NULL; break; } e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_HANGUP: e->etype = IAX_EVENT_HANGUP; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_INVAL: e->etype = IAX_EVENT_HANGUP; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_REJECT: e->etype = IAX_EVENT_REJECT; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_ACK: free(e); 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, updatehistory); break; case IAX_COMMAND_PING: /* PINGS and PONGS don't get scheduled; */ e->etype = IAX_EVENT_PING; e->ts = ts; break; case IAX_COMMAND_PONG: e->etype = IAX_EVENT_PONG; session->pingtime = calc_timestamp(session,0,NULL) - ts; session->remote_netstats.jitter = e->ies.rr_jitter; session->remote_netstats.losspct = e->ies.rr_loss & 0xff; session->remote_netstats.losscnt = e->ies.rr_loss << 24; session->remote_netstats.packets = e->ies.rr_pkts; session->remote_netstats.delay = e->ies.rr_delay; session->remote_netstats.dropped = e->ies.rr_dropped; session->remote_netstats.ooo = e->ies.rr_dropped; break; case IAX_COMMAND_ACCEPT: if (e->ies.format & session->capability) { e->etype = IAX_EVENT_ACCEPT; } else { struct iax_ie_data ied; /* Although this should not happen, we added this to make sure the negotiation protocol is enforced. For lack of event to notify the application we use the defined REJECT event. */ memset(&ied, 0, sizeof(ied)); iax_ie_append_str(&ied, IAX_IE_CAUSE, "Unable to negotiate codec"); send_command_final(session, AST_FRAME_IAX, IAX_COMMAND_REJECT, 0, ied.buf, ied.pos, -1); e->etype = IAX_EVENT_REJECT; } e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_REGACK: e->etype = IAX_EVENT_REGACK; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_REGAUTH: iax_regauth_reply(session, session->secret, e->ies.challenge, e->ies.authmethods); free(e); e = NULL; break; case IAX_COMMAND_REGREJ: e->etype = IAX_EVENT_REGREJ; e = schedule_delivery(e, ts, updatehistory); break; case IAX_COMMAND_LAGRP: e->etype = IAX_EVENT_LAGRP; nowts = calc_timestamp(session, 0, NULL); e->ts = nowts - ts; e->subclass = session->jitter; /* Can't call schedule_delivery since timestamp is non-normal */ break;; case IAX_COMMAND_TXREQ: /* added check for defensive programming * - in case the asterisk server * or another client does not send the * apparent transfer address */ if (e->ies.apparent_addr != NULL) { /* so a full voice frame is sent on the next voice output */ session->svoiceformat = -1; session->transfer = *e->ies.apparent_addr; session->transfer.sin_family = A
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -