📄 iax.c
字号:
return x;}int iax_call(struct iax_session *session, const char *cidnum, const char *cidname, const char *ich, const char *lang, int wait, int formats, int capabilities){ char tmp[256]=""; char *part1, *part2; int res; int portno; char *username, *hostname, *secret, *context, *exten, *dnid; struct iax_ie_data ied; struct hostent *hp; /* We start by parsing up the temporary variable which is of the form of: [user@]peer[:portno][/exten[@context]] */ if (!ich) { IAXERROR "Invalid IAX Call Handle\n"); DEBU(G "Invalid IAX Call Handle\n"); return -1; } memset(&ied, 0, sizeof(ied)); strncpy(tmp, ich, sizeof(tmp) - 1); iax_ie_append_short(&ied, IAX_IE_VERSION, IAX_PROTO_VERSION); if (cidnum) iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, cidnum); if (cidname) iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, cidname); if (session->codec_order_len) { iax_ie_append_str(&ied, IAX_IE_CODEC_PREFS, session->codec_order); } session->capability = capabilities; session->pingid = iax_sched_add(NULL,NULL, send_ping, (void *)session, 2 * 1000); /* XXX We should have a preferred format XXX */ iax_ie_append_int(&ied, IAX_IE_FORMAT, formats); iax_ie_append_int(&ied, IAX_IE_CAPABILITY, capabilities); if (lang) iax_ie_append_str(&ied, IAX_IE_LANGUAGE, lang); /* Part 1 is [user[:password]@]peer[:port] */ part1 = strtok(tmp, "/"); /* Part 2 is exten[@context] if it is anything all */ part2 = strtok(NULL, "/"); if (strchr(part1, '@')) { username = strtok(part1, "@"); hostname = strtok(NULL, "@"); } else { username = NULL; hostname = part1; } if (username && strchr(username, ':')) { username = strtok(username, ":"); secret = strtok(NULL, ":"); } else secret = NULL; if(username) strncpy(session->username, username, sizeof(session->username) - 1); if(secret) strncpy(session->secret, secret, sizeof(session->secret) - 1); if (strchr(hostname, ':')) { strtok(hostname, ":"); portno = atoi(strtok(NULL, ":")); } else { portno = IAX_DEFAULT_PORTNO; } if (part2) { exten = strtok(part2, "@"); dnid = exten; context = strtok(NULL, "@"); } else { exten = NULL; dnid = NULL; context = NULL; } if (username) iax_ie_append_str(&ied, IAX_IE_USERNAME, username); if (exten && strlen(exten)) iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, exten); if (dnid && strlen(dnid)) iax_ie_append_str(&ied, IAX_IE_DNID, dnid); if (context && strlen(context)) iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, context); /* Setup host connection */ hp = gethostbyname(hostname); if (!hp) { snprintf(iax_errstr, sizeof(iax_errstr), "Invalid hostname: %s", hostname); return -1; } memcpy(&session->peeraddr.sin_addr, hp->h_addr, sizeof(session->peeraddr.sin_addr)); session->peeraddr.sin_port = htons(portno); session->peeraddr.sin_family = AF_INET; res = send_command(session, AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1); if (res < 0) return res; if (wait) { DEBU(G "Waiting not yet implemented\n"); return -1; } return res;}static int calc_rxstamp(struct iax_session *session){ struct timeval tv; int ms; if (!session->rxcore.tv_sec && !session->rxcore.tv_usec) { gettimeofday(&session->rxcore, NULL); } gettimeofday(&tv, NULL); ms = (tv.tv_sec - session->rxcore.tv_sec) * 1000 + (tv.tv_usec - session->rxcore.tv_usec) / 1000; return ms;}#ifdef notdef_cruftstatic int match(struct sockaddr_in *sin, short callno, short dcallno, struct iax_session *cur){ if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) && (cur->peeraddr.sin_port == sin->sin_port)) { /* This is the main host */ if ((cur->peercallno == callno) || ((dcallno == cur->callno) && !cur->peercallno)) { /* 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;}#endif/* splitted match into 2 passes otherwise causing problem of matching up the wrong session using the dcallno and the peercallno because during a transfer (2 IAX channels on the same client/system) the same peercallno (from two different asterisks) exist in more than one session. */static int forward_match(struct sockaddr_in *sin, short callno, short dcallno, struct iax_session *cur){ 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; } } if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) && (cur->peeraddr.sin_port == sin->sin_port)) { if (dcallno == cur->callno && dcallno != 0) { /* That's us. Be sure we keep track of the peer call number */ if (cur->peercallno == 0) { cur->peercallno = callno; } else if ( cur->peercallno != callno ) { // print a warning when the callno's don't match fprintf( stderr, "WARNING: peercallno does not match callno" ", peercallno => %d, callno => %d, dcallno => %d", cur->peercallno, callno, dcallno ) ; return 0 ; } return 1; } } return 0;}static int reverse_match(struct sockaddr_in *sin, short callno, struct iax_session *cur){ 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 (callno == cur->peercallno) { return 1; } } if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) && (cur->peeraddr.sin_port == sin->sin_port)) { if (callno == cur->peercallno) { 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 (forward_match(sin, callno, dcallno, cur)) { return cur; } cur = cur->next; } cur = sessions; while(cur) { if (reverse_match(sin, callno, cur)) { return cur; } cur = cur->next; } if (makenew && !dcallno) { 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; cur->pingid = iax_sched_add(NULL,NULL, send_ping, (void *)cur, 2 * 1000); 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/* From chan_iax2/steve davies: need to get permission from steve or digium, I guess */static long unwrap_timestamp(long ts, long last){ int x; if ( (ts & 0xFFFF0000) == (last & 0xFFFF0000) ) { x = ts - last; if (x < -50000) { /* Sudden big jump backwards in timestamp: What likely happened here is that miniframe timestamp 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. */ ts = ( (last & 0xFFFF0000) + 0x10000) | (ts & 0xFFFF); DEBU(G "schedule_delivery: pushed forward timestamp\n"); } if (x > 50000) { /* Sudden apparent big jump forwards in timestamp: What's likely happened is this is an old miniframe belonging to the previous top-16-bit timestamp that has turned up out of order. Adjust the timestamp appropriately. */ ts = ( (last & 0xFFFF0000) - 0x10000) | (ts & 0xFFFF); DEBU(G "schedule_delivery: pushed back timestamp\n"); } } else if ( (ts & 0xFFFF8000L) == (last & 0xFFFF8000L) ) { x = ts - last; if (x < -50000) { /* Sudden big jump backwards in timestamp: What likely happened here is that miniframe timestamp 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. */ ts = ( (last & 0xFFFF8000L) + 0x10000) | (ts & 0xFFFF); DEBU(G "schedule_delivery: pushed forward timestamp\n"); } if (x > 50000) { /* Sudden apparent big jump forwards in timestamp: * What's likely happened is this is an old miniframe * belonging to the previous top-16-bit timestamp that * has turned up out of order. Adjust the timestamp * appropriately. */ ts = ( (last & 0xFFFF8000L) - 0x10000) | (ts & 0xFFFF); DEBU(G "schedule_delivery: pushed back timestamp\n"); } } return 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: * Dynamically adjust the jitterbuffer and decide how long to wait * before delivering the packet. */#ifdef EXTREME_DEBUG DEBU(G "[%p] We are at %d, packet is for %d\n", e->session, calc_rxstamp(e->session), ts);#endif /* insert into jitterbuffer */ /* TODO: Perhaps we could act immediately if it's not droppable and late */ if ( e->etype == IAX_EVENT_VIDEO && video_bypass_jitterbuffer ) { iax_sched_add(e, NULL, NULL, NULL, 0); return NULL; } else { int type = JB_TYPE_CONTROL; int len = 0; if(e->etype == IAX_EVENT_VOICE) { type = JB_TYPE_VOICE; /* The frame time only has an effect for voice */ len = get_sample_cnt(e) / 8; } else if(e->etype == IAX_EVENT_VIDEO) { type = JB_TYPE_VIDEO; } else if(e->etype == IAX_EVENT_CNG) { type = JB_TYPE_SILENCE; } /* unwrap timestamp */ ts = unwrap_timestamp(ts,e->session->last_ts); /* move forward last_ts if it's greater. We do this _after_ * unwrapping, because asterisk _still_ has cases where it * doesn't send full frames when it ought to */ if(ts > e->session->last_ts) { e->session->last_ts = ts; } if(jb_put(e->session->jb, e, type, len, ts, calc_rxstamp(e->session)) == JB_DROP) { iax_event_free(e); } } 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 void iax_handle_vnak(struct iax_session *session, struct ast_iax2_full_hdr *fh){ struct iax_sched *sch, *list, *l, *tmp; /* * According to the IAX2 02 draft, we MUST immediately retransmit all frames * with higher sequence number than the VNAK's iseqno * However, it seems that the right thing to do would be to retransmit * frames with sequence numbers higher OR EQUAL to VNAK's iseqno. */ sch = schedq; list = NULL; while ( sch != NULL ) { if ( sch->frame != NULL && sch->frame->session == session ) { /* * We want to check if our frame's oseqno is greater or equal than * the VNAK's iseqno, but we need to take into account sequence * number wrap-arounds * session->rseqno is our last acknowledged sequence number, so * we use that as a base */ if ( (unsigned char)(fh->iseqno - session->rseqno) <= (unsigned char)(sch->frame->oseqno - session->rseqno) ) { /* * We cannot retransmit immediately, since the frames are ordered by retransmit time * We need to collect them and orrange them in ascending order of their oseqno */ tmp = (struct iax_sched *)calloc(1, sizeof(struct iax_sched)); tmp->frame = sch->frame; if ( list == NULL || (list->frame->oseqno - session->rseqno) > (tmp->frame->oseqno - session->rseqno) ) { tmp->next = list; list = tmp; } else { l = list; while ( l != NULL ) { if ( l->next == NULL || (l->next->frame->oseqno - session->rseqno) > (tmp->frame->oseqno - session->rseqno) ) { tmp->next = l->next; l->next = tmp; break; } l = l->next; } } } } sch = sch->next; } /* Transmit collected frames and free the space */ while ( list != NULL ) { tmp = list; iax_xmit_frame(tmp->frame); list = list->next; free(tmp); }}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; int nowts; int updatehistory = 1; ts = ntohl(fh->ts); if (fh->type==AST_FRAME_VIDEO) subclass = uncompress_subclass(fh->csub & ~0x40) | ((fh->csub >> 6) & 0x1); else subclass = uncompress_subclass(fh->csub); /* don't run last_ts backwards; i.e. for retransmits and the like */ if (ts > session->last_ts && (fh->type == AST_FRAME_IAX && subclass != IAX_COMMAND_ACK && subclass != IAX_COMMAND_PONG && subclass != IAX_COMMAND_LAGRP)) { 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-
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -