diff --exclude='*.o*' --exclude='.*' -urN asterisk-cleanhead/channels/chan_iax2.c asterisk/channels/chan_iax2.c --- asterisk-cleanhead/channels/chan_iax2.c 2005-01-21 16:19:00.000000000 -0500 +++ asterisk/channels/chan_iax2.c 2005-01-21 16:18:02.000000000 -0500 @@ -120,6 +120,7 @@ static int maxjitterbuffer=1000; static int jittershrinkrate=2; static int trunkfreq = 20; +static int send_trunktimestamps = 1; static int authdebug = 1; static int autokill = 0; static int iaxcompat = 0; @@ -203,9 +204,9 @@ #define IAX_ALREADYGONE (1 << 9) /* Already disconnected */ #define IAX_PROVISION (1 << 10) /* This is a provisioning request */ #define IAX_QUELCH (1 << 11) /* Whether or not we quelch audio */ -#define IAX_ENCRYPTED (1 << 12) /* Whether we should assume encrypted tx/rx */ -#define IAX_KEYPOPULATED (1 << 13) /* Whether we have a key populated */ -#define IAX_CODEC_USER_FIRST (1 << 14) /* are we willing to let the other guy choose the codec? */ +#define IAX_ENCRYPTED (1 << 12) /* Whether we should assume encrypted tx/rx */ +#define IAX_KEYPOPULATED (1 << 13) /* Whether we have a key populated */ +#define IAX_CODEC_USER_FIRST (1 << 14) /* are we willing to let the other guy choose the codec? */ static struct iax2_peer *realtime_peer(const char *peername); @@ -287,6 +288,9 @@ unsigned char *trunkdata; unsigned int trunkdatalen; unsigned int trunkdataalloc; + /* trunk timestamps */ + unsigned short *timestamps; /* a list of trunk frame timestamps */ + unsigned int timestampalloc; /* how large the array is above */ struct iax2_trunk_peer *next; int trunkerror; int calls; @@ -3149,32 +3153,46 @@ return tpeer; } -static int iax2_trunk_queue(struct chan_iax2_pvt *pvt, struct ast_frame *f) +/* make room for at least needed extra bytes in trunkdata; realloc if necessary */ +/* return 0 on success */ +static int trunk_data_makeroom(struct iax2_trunk_peer *tpeer, size_t needed) { + char iabuf[INET_ADDRSTRLEN]; + void *tmp; + + needed += tpeer->trunkdatalen; + if (needed >= tpeer->trunkdataalloc) { + /* Need to reallocate space */ + if (tpeer->trunkdataalloc < MAX_TRUNKDATA) { + tmp = realloc(tpeer->trunkdata, tpeer->trunkdataalloc + DEFAULT_TRUNKDATA + IAX2_TRUNK_PREFACE); + if (tmp) { + tpeer->trunkdataalloc += DEFAULT_TRUNKDATA; + tpeer->trunkdata = tmp; + ast_log(LOG_DEBUG, "Expanded trunk '%s:%d' to %d bytes\n", ast_inet_ntoa(iabuf, sizeof(iabuf), tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), tpeer->trunkdataalloc); + } else { + ast_log(LOG_WARNING, "Insufficient memory to expand trunk data to %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port)); + return -1; + } + } else { + ast_log(LOG_WARNING, "Maximum trunk data space exceeded to %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port)); + return -1; + } + } + return 0; +} + +static int iax2_trunk_queue(struct chan_iax2_pvt *pvt, struct iax_frame *fr) { struct iax2_trunk_peer *tpeer; void *tmp, *ptr; struct ast_iax2_meta_trunk_entry *met; + struct ast_frame *f = &fr->af; char iabuf[INET_ADDRSTRLEN]; + tpeer = find_tpeer(&pvt->addr, pvt->sockfd); if (tpeer) { - if (tpeer->trunkdatalen + f->datalen + 4 >= tpeer->trunkdataalloc) { - /* Need to reallocate space */ - if (tpeer->trunkdataalloc < MAX_TRUNKDATA) { - tmp = realloc(tpeer->trunkdata, tpeer->trunkdataalloc + DEFAULT_TRUNKDATA + IAX2_TRUNK_PREFACE); - if (tmp) { - tpeer->trunkdataalloc += DEFAULT_TRUNKDATA; - tpeer->trunkdata = tmp; - ast_log(LOG_DEBUG, "Expanded trunk '%s:%d' to %d bytes\n", ast_inet_ntoa(iabuf, sizeof(iabuf), tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), tpeer->trunkdataalloc); - } else { - ast_log(LOG_WARNING, "Insufficient memory to expand trunk data to %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port)); - ast_mutex_unlock(&tpeer->lock); - return -1; - } - } else { - ast_log(LOG_WARNING, "Maximum trunk data space exceeded to %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port)); - ast_mutex_unlock(&tpeer->lock); - return -1; - } + if(trunk_data_makeroom(tpeer, f->datalen + 4)) { + ast_mutex_unlock(&tpeer->lock); + return -1; } /* Append to meta frame */ @@ -3189,6 +3207,22 @@ /* Copy actual trunk data */ memcpy(ptr, f->data, f->datalen); tpeer->trunkdatalen += f->datalen; + + /* remember trunktimestamp, if so configured */ + if(send_trunktimestamps) { + /* alloc/realloc if necessary */ + if(tpeer->timestampalloc < tpeer->calls + 1) { + tmp = realloc(tpeer->timestamps, sizeof(short) * (tpeer->timestampalloc + 10)); + if(!tmp) { + ast_log(LOG_WARNING, "Insufficient memory to expand trunk timestamps to %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port)); + goto tsbail; + } + tpeer->timestamps = tmp; + tpeer->timestampalloc += 10; + } + tpeer->timestamps[tpeer->calls] = 0xFFFF & fr->ts; + } +tsbail: tpeer->calls++; ast_mutex_unlock(&tpeer->lock); } @@ -3461,7 +3495,7 @@ res = iax2_transmit(fr); } else { if (ast_test_flag(pvt, IAX_TRUNK)) { - iax2_trunk_queue(pvt, &fr->af); + iax2_trunk_queue(pvt, fr); res = 0; } else if (fr->af.frametype == AST_FRAME_VIDEO) { /* Video frame have no sequence number */ @@ -5118,34 +5152,52 @@ struct ast_iax2_meta_hdr *meta; struct ast_iax2_meta_trunk_hdr *mth; int calls = 0; + size_t tssize; /* Point to frame */ fr = (struct iax_frame *)tpeer->trunkdata; /* Point to meta data */ meta = (struct ast_iax2_meta_hdr *)fr->afdata; mth = (struct ast_iax2_meta_trunk_hdr *)meta->data; - if (tpeer->trunkdatalen) { - /* We're actually sending a frame, so fill the meta trunk header and meta header */ - meta->zeros = 0; - meta->metacmd = IAX_META_TRUNK; - meta->cmddata = 0; - mth->ts = htonl(calc_txpeerstamp(tpeer, trunkfreq, now)); - /* And the rest of the ast_iax2 header */ - fr->direction = DIRECTION_OUTGRESS; - fr->retrans = -1; - fr->transfer = 0; - /* Any appropriate call will do */ - fr->data = fr->afdata; - fr->datalen = tpeer->trunkdatalen + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr); + + if(tpeer->calls == 0 || tpeer->trunkdatalen == 0) /* might as well just bail now if we have no data */ + return 0; + + calls = tpeer->calls; + + tssize = sizeof(struct ast_iax2_meta_trunk_timestamps) + sizeof(short) * calls; + /* append trunktimestamps to trunk data, if requested */ + if(send_trunktimestamps && !trunk_data_makeroom(tpeer, tssize)) { + struct ast_iax2_meta_trunk_timestamps *mts; + + mts = (struct ast_iax2_meta_trunk_timestamps *)(tpeer->trunkdata + IAX2_TRUNK_PREFACE + tpeer->trunkdatalen); + mts->zeros=0; + mts->len = sizeof(mts->type) + sizeof(short) * calls; + mts->type = IAX_META_TRUNK_EXTRA_TIMESTAMPS; + memcpy(mts->timestamps, tpeer->timestamps, sizeof(short) * calls); + tpeer->trunkdatalen += tssize; + } + + /* We're actually sending a frame, so fill the meta trunk header and meta header */ + meta->zeros = 0; + meta->metacmd = IAX_META_TRUNK; + meta->cmddata = 0; + mth->ts = htonl(calc_txpeerstamp(tpeer, trunkfreq, now)); + /* And the rest of the ast_iax2 header */ + fr->direction = DIRECTION_OUTGRESS; + fr->retrans = -1; + fr->transfer = 0; + /* Any appropriate call will do */ + fr->data = fr->afdata; + fr->datalen = tpeer->trunkdatalen + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr); #if 0 - ast_log(LOG_DEBUG, "Trunking %d calls in %d bytes, ts=%d\n", calls, fr->datalen, ntohl(mth->ts)); + ast_log(LOG_DEBUG, "Trunking %d calls in %d bytes, ts=%d\n", calls, fr->datalen, ntohl(mth->ts)); #endif - res = transmit_trunk(fr, &tpeer->addr, tpeer->sockfd); - calls = tpeer->calls; - /* Reset transmit trunk side data */ - tpeer->trunkdatalen = 0; - tpeer->calls = 0; - } + res = transmit_trunk(fr, &tpeer->addr, tpeer->sockfd); + calls = tpeer->calls; + /* Reset transmit trunk side data */ + tpeer->trunkdatalen = 0; + if (res < 0) return res; return calls; @@ -5404,17 +5456,30 @@ struct ast_iax2_meta_hdr *meta = (struct ast_iax2_meta_hdr *)buf; struct ast_iax2_meta_trunk_hdr *mth; struct ast_iax2_meta_trunk_entry *mte; + struct ast_iax2_meta_trunk_extra *mtex; + struct ast_iax2_meta_trunk_timestamps *mts = NULL; + int i, ntimestamps; struct iax2_trunk_peer *tpeer; char *ptr; unsigned int ts; int res=len, updatehistory=1; char dblbuf[4096]; /* Declaration of dblbuf must immediately *preceed* fr on the stack */ struct iax_frame fr; + struct iax_frame *first,*last,*cur; char iabuf[INET_ADDRSTRLEN]; struct ast_frame f; struct timeval rxtrunktime; - dblbuf[0]=0; + /* a bunch of gcc-shutter-uppers */ + dblbuf[0]=i=ntimestamps=0; + mtex = NULL; + + + /* as we receive trunk frames, we put them in a linked list, from first to last. + * we always add to the end, but we need to keep track of the beginning, of course, + * so the last ptr keeps us from having to re-walk + */ + first = last = NULL; /* This is a meta header */ switch(meta->metacmd) { @@ -5448,6 +5513,24 @@ /* Stop if we don't have enough data */ if (len > res) break; + if(fr.callno == 0) { /* this is a meta_trunk_extra subframe */ + mtex = (struct ast_iax2_meta_trunk_extra *)ptr; + switch(mtex->type) + case IAX_META_TRUNK_EXTRA_TIMESTAMPS: + if(mts) { + ast_log(LOG_WARNING, "Duplicate trunk timestamp extension from '%s:%d'\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port)); + } else { + mts = (struct ast_iax2_meta_trunk_timestamps *)mtex; + } + break; + default: + /* unknown meta trunk extension -- ignore it! */ + ast_log(LOG_NOTICE, "Unknown trunk extension from '%s:%d': type %x\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port), mtex->type); + break; + + } fr.callno = find_callno(ntohs(mte->callno) & ~IAX_FLAG_FULL, 0, &sin, NEW_PREVENT, 1, fd); if (fr.callno) { ast_mutex_lock(&iaxsl[fr.callno]); @@ -5477,15 +5560,16 @@ f.samples = 0; fr.outoforder = 0; iax_frame_wrap(&fr, &f); -#ifdef BRIDGE_OPTIMIZATION - if (iaxs[fr.callno]->bridgecallno) { - forward_delivery(&fr); + + /* copy and queue the frame; we might add a timestamp later */ + cur = iaxfrdup2(&fr); + cur->next = NULL; + if(last) { + last->next = cur; + last = cur; } else { - schedule_delivery(iaxfrdup2(&fr), 1, updatehistory, 1); + first = last = cur; } -#else - schedule_delivery(iaxfrdup2(&fr), 1, updatehistory, 1); -#endif } } else { ast_log(LOG_WARNING, "Datalen < 0?\n"); @@ -5500,7 +5584,32 @@ ptr += len; res -= len; } - + + /* finally, send along all the frames, applying timestamps if requested */ + for(cur = first; cur; cur = cur->next) { + if(mts) { + if(i >= ntimestamps) { + ast_log(LOG_WARNING, "Short trunk timestamp extension from '%s:%d'\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port)); + mts = NULL; /* suppress many warnings */ + } + cur->ts = mts->timestamps[i++]; + } +#ifdef BRIDGE_OPTIMIZATION + if (iaxs[fr.callno]->bridgecallno) { + forward_delivery(cur); + iax_frame_free(cur); /* forward_delivery does not free the frame */ + } else +#endif + { + schedule_delivery(cur, 1, updatehistory, 1); + } + } + + if(mts && i < ntimestamps) + ast_log(LOG_WARNING, "Too many trunk timestamps from '%s:%d'\n", + ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port)); + } return 1; } @@ -7471,6 +7580,8 @@ delayreject = ast_true(v->value); else if (!strcasecmp(v->name, "mailboxdetail")) ast_set2_flag((&globalflags), ast_true(v->value), IAX_MESSAGEDETAIL); + else if (!strcasecmp(v->name, "trunktimestamps")) + send_trunktimestamps = ast_true(v->value); else if (!strcasecmp(v->name, "trunkfreq")) { trunkfreq = atoi(v->value); if (trunkfreq < 10) diff --exclude='*.o*' --exclude='.*' -urN asterisk-cleanhead/channels/iax2.h asterisk/channels/iax2.h --- asterisk-cleanhead/channels/iax2.h 2005-01-21 15:18:39.000000000 -0500 +++ asterisk/channels/iax2.h 2005-01-21 15:20:47.000000000 -0500 @@ -201,6 +201,26 @@ unsigned short len; /* Length of data for this callno */ } __attribute__ ((__packed__)); +#define IAX_META_TRUNK_EXTRA_TIMESTAMPS 0x0001 + +/* meta_trunk_extra: A meta_trunk_entry for non-voice frames; We could have called this + meta_trunk_meta, but that would be redundant, wouldn't it */ +struct ast_iax2_meta_trunk_extra { + unsigned short zeros; + unsigned short len; + unsigned short type; + unsigned char data[0]; +}; + +/* timestamps for frames in the trunk: These are to be applied, in order, to each + of the meta_trunk_entry voice miniframes */ +struct ast_iax2_meta_trunk_timestamps { + unsigned short zeros; /* must be zero */ + unsigned short len; /* must be sizeof(type) + sizeof(timestamps) , or 2 + 2*(ntimestamps) */ + unsigned short type; /* must be IAX_META_TRUNK_EXTRA_TIMESTAMPS */ + unsigned short timestamps[0]; +}; + #define IAX_FIRMWARE_MAGIC 0x69617879 struct ast_iax2_firmware_header { diff --exclude='*.o*' --exclude='.*' -urN asterisk-cleanhead/configs/iax.conf.sample asterisk/configs/iax.conf.sample --- asterisk-cleanhead/configs/iax.conf.sample 2005-01-21 15:18:40.000000000 -0500 +++ asterisk/configs/iax.conf.sample 2005-01-21 15:20:47.000000000 -0500 @@ -97,6 +97,15 @@ ;jittershrinkrate=1 ;trunkfreq=20 ; How frequently to send trunk msgs (in ms) + +; Should we send timestamps for the individual sub-frames within trunk frames? +; There is a small bandwidth use for these (less than 1kbps/call), but they ensure +; that frame timestamps get sent end-to-end properly. If both ends of all your trunks +; go directly to TDM, _and_ your trunkfreq equals the frame length for your codecs, you +; can probably suppress these. +; +; trunktimestamps=yes + ; ; ; We can register with another IAX server to let him know where we are