📄 iax.c
字号:
(cur->peeraddr.sin_port == sin->sin_port)) { /* This is the main host */ if ((cur->peercallno == callno) || ((dcallno == cur->callno) && (cur->peercallno) == -1)) { /* That's us. Be sure we keep track of the peer call number */ cur->peercallno = callno; return 1; } } if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) && (cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) { /* We're transferring */ if (dcallno == cur->callno) return 1; } return 0;}static struct iax_session *iax_find_session(struct sockaddr_in *sin, short callno, short dcallno, int makenew){ struct iax_session *cur = sessions; while(cur) { if (match(sin, callno, dcallno, cur)) return cur; cur = cur->next; } if (makenew && (dcallno == -1)) { cur = iax_session_new(); cur->peercallno = callno; cur->peeraddr.sin_addr.s_addr = sin->sin_addr.s_addr; cur->peeraddr.sin_port = sin->sin_port; cur->peeraddr.sin_family = AF_INET; DEBU(G "Making new session, peer callno %d, our callno %d\n", callno, cur->callno); } else { DEBU(G "No session, peer = %d, us = %d\n", callno, dcallno); } return cur; }#ifdef EXTREME_DEBUGstatic int display_time(int ms){ static int oldms = -1; if (oldms < 0) { DEBU(G "First measure\n"); oldms = ms; return 0; } DEBU(G "Time from last frame is %d ms\n", ms - oldms); oldms = ms; return 0;}#endif#define FUDGE 1static struct iax_event *schedule_delivery(struct iax_event *e, unsigned int ts){ /* * This is the core of the IAX jitterbuffer delivery mechanism: * Dynamically adjust the jitterbuffer and decide how long to wait * before delivering the packet. */ int ms, x; int drops[MEMORY_SIZE]; int min, max=0, maxone=0, y, z, match;#ifdef EXTREME_DEBUG DEBU(G "[%p] We are at %d, packet is for %d\n", e->session, calc_rxstamp(e->session), ts);#endif #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 /* How many ms from now should this packet be delivered? (remember this can be a negative number, too */ ms = calc_rxstamp(e->session) - ts; 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. */ 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 -= 2; } /* 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 ;-) */ 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->event.voice.data); free(e); return NULL; } /* We need this to be delivered in the future, so we use our scheduler */ iax_sched_event(e, NULL, ms);#ifdef EXTREME_DEBUG DEBU(G "Delivering packet in %d ms\n", ms);#endif 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 void send_ack(struct iax_session *session, struct iax_full_hdr *fhi){ short tco; struct iax_frame f; struct iax_full_hdr h; struct iax_full_hdr *fh = &h; /* To ack, we use the same sequence number and timestamp, just swapping the source and destination */ memcpy(fh, fhi, sizeof(h)); tco = ntohs(fh->callno) & ~IAX_FLAG_FULL;#ifdef EXTREME_DEBUG DEBU(G "Acking peer's callno %d (our callno %d) seqno %d\n", tco, (int)session->callno, ntohs(fh->seqno));#endif fh->dcallno = htons(tco); fh->callno = htons((short) (session->callno | IAX_FLAG_FULL)); fh->type = AST_FRAME_IAX; fh->csub = IAX_COMMAND_ACK; f.retries = -1; f.session = session; f.data = fh; f.datalen = sizeof(struct iax_full_hdr); f.transferpacket = 0; iax_xmit_frame(&f);}static struct iax_event *iax_header_to_event(struct iax_session *session, struct iax_full_hdr *fh, int datalen){ struct iax_event *e; struct iax_sched *sch; unsigned int ts; int subclass = uncompress_subclass(fh->csub); char *text = fh->data; char *s; char *methods; int nowts; ts = ntohl(fh->ts); session->last_ts = ts; e = (struct iax_event *)malloc(sizeof(struct iax_event));#ifdef DEBUG_SUPPORT showframe(NULL, fh, 1);#endif /* Get things going with it, timestamp wise, if we haven't already. */ if ((fh->type != AST_FRAME_IAX) || ((subclass != IAX_COMMAND_ACK) && (subclass != IAX_COMMAND_INVAL) && (subclass != IAX_COMMAND_REJECT) && (subclass != IAX_COMMAND_TXCNT) && (subclass != IAX_COMMAND_TXACC))) send_ack(session, fh); /* Null terminate text */ if (text) text[datalen] = '\0'; if (e) { memset(e, 0, sizeof(struct iax_event)); e->session = session; switch(fh->type) { case AST_FRAME_DTMF: e->etype = IAX_EVENT_DTMF; e->event.dtmf.digit = subclass; return schedule_delivery(e, ts); case AST_FRAME_VOICE: e->etype = IAX_EVENT_VOICE; e->event.voice.format = subclass; session->voiceformat = subclass; if (datalen) { e->event.voice.data = (char *)malloc(datalen); e->event.voice.datalen = datalen; if (e->event.voice.data) { memcpy(e->event.voice.data, fh->data, datalen); } else { free(e); e = NULL; DEBU(G "Out of memory\n"); return e; } } else { /* Empty voice frame? Maybe it could happen... */ e->event.voice.data = NULL; e->event.voice.datalen = 0; } return schedule_delivery(e, ts); case AST_FRAME_IAX: switch(subclass) { case IAX_COMMAND_NEW: /* This is a new, incoming call */ e->etype = IAX_EVENT_CONNECT; /* Now we search for each each component of the call, if present. */ e->event.connect.callerid = extract(text, "callerid="); e->event.connect.dnid = extract(text, "dnid="); e->event.connect.exten = extract(text, "exten="); e->event.connect.context = extract(text, "context="); e->event.connect.username = extract(text, "username="); e->event.connect.language = extract(text, "language="); s = extract(text, "formats="); if (s) e->event.connect.formats = atoi(s); else e->event.connect.formats = 0; s = extract(text, "version="); if (s) e->event.connect.version = atoi(s); else e->event.connect.version = 0; e->event.connect.hostname = strdup(inet_ntoa(e->session->peeraddr.sin_addr)); return schedule_delivery(e, ts); case IAX_COMMAND_AUTHREQ: /* This is a new, incoming call */ e->etype = IAX_EVENT_AUTHRQ; /* Now we search for each each component of the call, if present. */ e->event.authrequest.username = extract(text, "username="); methods = extract(text, "methods="); e->event.authrequest.authmethods = 0; if (methods) { if (strstr(methods, "md5")) e->event.authrequest.authmethods |= IAX_AUTHMETHOD_MD5; if (strstr(methods, "plaintext")) e->event.authrequest.authmethods |= IAX_AUTHMETHOD_PLAINTEXT; free(methods); } e->event.authrequest.challenge = extract(text, "challenge="); return schedule_delivery(e, ts); case IAX_COMMAND_HANGUP: e->etype = IAX_EVENT_HANGUP; if (datalen) e->event.hangup.byemsg = strdup(text); else e->event.hangup.byemsg = NULL; return schedule_delivery(e, ts); case IAX_COMMAND_INVAL: e->etype = IAX_EVENT_HANGUP; e->event.hangup.byemsg = NULL; return schedule_delivery(e, ts); case IAX_COMMAND_REJECT: e->etype = IAX_EVENT_REJECT; e->event.reject.reason = (text ? strdup(text) : NULL); return schedule_delivery(e, ts); case IAX_COMMAND_ACK: if (ntohs(fh->seqno) <= session->iseqno) { /* We just need to go through and acknowledge the matching packet(s) that are planned to be retransmitted */ sch = schedq; while(sch) { if (sch->frame && (sch->frame->session == session) && (((struct iax_full_hdr *)(sch->frame->data))->seqno == fh->seqno)) sch->frame->retries = -1; sch = sch->next; } if (ntohs(fh->seqno) == session->iseqno) session->iseqno++; } else DEBU(G "Received ACK for %d, expecting %d\n", ntohs(fh->seqno), session->iseqno); free(e); return NULL; break; case IAX_COMMAND_LAGRQ: /* Pass this along for later handling */ e->etype = IAX_EVENT_LAGRQ; e->event.lagrq.ts = ts; return schedule_delivery(e, ts); case IAX_COMMAND_PING: /* Just immediately reply */ e->etype = IAX_EVENT_PING; e->event.ping.ts = ts; e->event.ping.seqno = ntohs(fh->seqno); return schedule_delivery(e, ts); case IAX_COMMAND_ACCEPT: e->etype = IAX_EVENT_ACCEPT; return schedule_delivery(e, ts); case IAX_COMMAND_REGACK: e->etype = IAX_EVENT_REGREP; e->event.regreply.status = IAX_REG_SUCCESS; e->event.regreply.ourip = extract(text, "yourip="); s = extract(text, "yourport="); e->event.regreply.ourport = s ? atoi(s) : 0; if (s) free(s); s = extract(text, "refresh="); e->event.regreply.refresh = s ? atoi(s) : 0; if (s) free(s); s = extract(text, "callerid="); e->event.regreply.callerid = s; return schedule_delivery(e, ts); case IAX_COMMAND_REGAUTH: /* Ooh, don't bother telling the user, just do it */ e->etype = IAX_EVENT_REREQUEST; s = extract(text, "methods="); if (!s) { DEBU(G "No methods specified?\n"); free(e); return NULL; } strncpy(session->methods, s, sizeof(session->methods)-1); s = extract(text, "challenge="); if (s) strncpy(session->challenge, s, sizeof(session->challenge)-1); else strcpy(session->challenge, ""); iax_do_event(session, e); free(e); return NULL; case IAX_COMMAND_REGREJ: e->etype = IAX_EVENT_REGREP; e->event.regreply.status = IAX_REG_REJECT; e->event.regreply.ourip = NULL; e->event.regreply.ourport = 0; s = extract(text, "refresh="); e->event.regreply.refresh = s ? atoi(s) : 0; return schedule_delivery(e, ts); case IAX_COMMAND_LAGRP: e->etype = IAX_EVENT_LAGRP; nowts = calc_timestamp(session, 0); e->event.lag.lag = nowts - ts; e->event.lag.jitter = session->jitter; /* Can't call schedule_delivery since timestamp is non-normal */ return e; case IAX_COMMAND_TXREQ: /* Received transfer request, start the process */ s = extract(text, "remip="); if (s) { if (inet_aton(s, &session->transfer.sin_addr)) { s = extract(text, "remport="); if (s) { session->transfer.sin_port = htons(atoi(s)); free(s); s = extract(text, "remcall="); if (s) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -