]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
backport change from the iaxclient sf repository rev 648, 649, 650, 656.
authorMichael Jerris <mike@jerris.com>
Fri, 11 May 2007 21:20:13 +0000 (21:20 +0000)
committerMichael Jerris <mike@jerris.com>
Fri, 11 May 2007 21:20:13 +0000 (21:20 +0000)
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@5158 d0543943-73ff-0310-b7d9-9358b9ac24b2

libs/iax/src/iax-client.h
libs/iax/src/iax.c
libs/iax/src/jitterbuf.c
libs/iax/src/jitterbuf.h

index f2a0281d86dfb5b8e657957cf80db75e228495be..2f0faf71cd27025f2d2deff1aee664671c84f130 100644 (file)
@@ -226,6 +226,10 @@ extern unsigned int iax_session_get_capability(struct iax_session *s);
 extern char iax_pref_codec_add(struct iax_session *session, unsigned int format);
 extern void iax_pref_codec_del(struct iax_session *session, unsigned int format);
 extern int iax_pref_codec_get(struct iax_session *session, unsigned int *array, int len);
+
+/* Fine tune jitterbuffer */\r
+extern void iax_set_jb_target_extra( long value );\r
+
 extern char *iax_get_peer_ip(struct iax_session *session);
 extern char *iax_event_get_apparent_ip(struct iax_event *event);
 extern void iax_set_samplerate(struct iax_session *session, unsigned short samplemask);
index ae1fecaa66d72559ea1ae81eae8b20dbe17e04a9..12e8289c50355842d4a4b98885ca352a8f8ed765 100644 (file)
@@ -140,6 +140,9 @@ static int netfd = -1;
 /* Max timeouts */
 static int maxretries = 10;
 
+/* configurable jitterbuffer options */\r
+static long jb_target_extra = -1; 
+
 static int do_shutdown = 0;
 
 /* external global networking replacements */
@@ -521,6 +524,7 @@ struct iax_session *iax_session_new(void)
                        jbconf.max_jitterbuf = 0;
                        jbconf.resync_threshold = 1000;
                        jbconf.max_contig_interp = 0;
+                       jbconf.target_extra = jb_target_extra;
                        jb_setconf(s->jb, &jbconf);
                }
 #endif
@@ -944,6 +948,12 @@ int iax_shutdown(void)
        return do_shutdown++;
 }
 
+void iax_set_jb_target_extra( long value )\r
+{\r
+       /* store in jb_target_extra, a static global */\r
+       jb_target_extra = value ;\r
+}\r
+
 int iax_init(char *ip, int preferredportno)
 {
        int portno = preferredportno;
index 025c8fe8a61b9803140d354862dd9c3e2bbe8996..16138b9c4e47a644429e329447e169655cc50e5c 100644 (file)
-/*
- * jitterbuf: an application-independent jitterbuffer
- *
- * Copyrights:
- * Copyright (C) 2004-2005, Horizon Wimba, Inc.
- *
- * Contributors:
- * Steve Kann <stevek@stevek.com>
- *
- * This program is free software, distributed under the terms of
- * the GNU Lesser (Library) General Public License
- *
- * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
- */
-
-#include "iax2.h"
-#include "jitterbuf.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* define these here, just for ancient compiler systems */
-#define JB_LONGMAX 2147483647L
-#define JB_LONGMIN (-JB_LONGMAX - 1L)
-
-/* MS VC can't do __VA_ARGS__ */
-#if defined(WIN32) && defined(_MSC_VER)
-#define jb_warn if (warnf) warnf
-#define jb_err if (errf) errf
-#define jb_dbg if (dbgf) dbgf
-
-#ifdef DEEP_DEBUG
-  #define jb_dbg2 if (dbgf) dbgf
-#else
-  #define jb_dbg2 if (0) dbgf
-#endif
-
-#else
-
-#define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0)
-#define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0)
-#define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
-
-#ifdef DEEP_DEBUG
-#define jb_dbg2(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
-#else
-#define jb_dbg2(...) ((void)0)
-#endif
-
-#endif
-
-static jb_output_function_t warnf, errf, dbgf;
-
-void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg) 
-{
-       errf = err;
-       warnf = warn;
-       dbgf = dbg;
-}
-
-static void increment_losspct(jitterbuf *jb) 
-{
-    jb->info.losspct = (100000 + 499 * jb->info.losspct)/500;    
-}
-
-static void decrement_losspct(jitterbuf *jb) 
-{
-    jb->info.losspct = (499 * jb->info.losspct)/500;    
-}
-
-void jb_reset(jitterbuf *jb) 
-{
-       /* only save settings */
-       jb_conf s = jb->info.conf;
-       memset(jb,0,sizeof(jitterbuf));
-       jb->info.conf = s;
-
-       /* initialize length */
-       jb->info.current = jb->info.target = JB_TARGET_EXTRA; 
-       jb->info.silence_begin_ts = -1; 
-}
-
-jitterbuf * jb_new() 
-{
-    jitterbuf *jb;
-
-
-    jb = malloc(sizeof(jitterbuf));
-    if(!jb) return NULL;
-
-    jb_reset(jb);
-
-    jb_dbg2("jb_new() = %x\n", jb);
-    return jb;
-}
-
-void jb_destroy(jitterbuf *jb) 
-{
-    jb_frame *frame; 
-    jb_dbg2("jb_destroy(%x)\n", jb);
-
-    /* free all the frames on the "free list" */
-    frame = jb->free;
-    while(frame != NULL) {
-      jb_frame *next = frame->next;
-      free(frame);
-      frame = next;
-    }
-
-    /* free ourselves! */ 
-    free(jb);
-}
-
-
-
-/* simple history manipulation */
-/* maybe later we can make the history buckets variable size, or something? */
-/* drop parameter determines whether we will drop outliers to minimize
- * delay */
-#if 0
-static int longcmp(const void *a, const void *b) 
-{
-    return *(long *)a - *(long *)b;
-}
-#endif
-
-static int history_put(jitterbuf *jb, time_in_ms_t ts, time_in_ms_t now, long ms) 
-{
-       time_in_ms_t delay = now - (ts - jb->info.resync_offset);
-       time_in_ms_t threshold = 2 * jb->info.jitter + jb->info.conf.resync_threshold;
-       time_in_ms_t kicked;
-
-       /* don't add special/negative times to history */
-       if (ts <= 0) 
-               return 0;
-
-       /* check for drastic change in delay */
-       if (jb->info.conf.resync_threshold != -1) {
-               if (iax_abs(delay - jb->info.last_delay) > threshold) {
-                       jb->info.cnt_delay_discont++;
-                       if (jb->info.cnt_delay_discont > 3) {
-                               /* resync the jitterbuffer */
-                               jb->info.cnt_delay_discont = 0;
-                               jb->hist_ptr = 0;
-                               jb->hist_maxbuf_valid = 0;
-
-                               jb_warn("Resyncing the jb. last_delay %ld, this delay %ld, threshold %ld, new offset %ld\n", jb->info.last_delay, delay, threshold, ts - now);
-                               jb->info.resync_offset = ts - now;
-                               jb->info.last_delay = delay = 0; /* after resync, frame is right on time */
-                       } else {
-                               return -1;
-                       }
-               } else {
-                       jb->info.last_delay = delay;
-                       jb->info.cnt_delay_discont = 0;
-               }
-       }
-
-    kicked = jb->history[jb->hist_ptr & JB_HISTORY_SZ];
-
-    jb->history[(jb->hist_ptr++) % JB_HISTORY_SZ] = delay;
-
-    /* optimization; the max/min buffers don't need to be recalculated, if this packet's
-     * entry doesn't change them.  This happens if this packet is not involved, _and_ any packet
-     * that got kicked out of the history is also not involved 
-     * We do a number of comparisons, but it's probably still worthwhile, because it will usually
-     * succeed, and should be a lot faster than going through all 500 packets in history */
-    if(!jb->hist_maxbuf_valid)
-      return 0;
-
-    /* don't do this until we've filled history 
-     * (reduces some edge cases below) */
-    if(jb->hist_ptr < JB_HISTORY_SZ)
-      goto invalidate;
-
-    /* if the new delay would go into min */
-    if(delay < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])
-      goto invalidate;
-    
-    /* or max.. */
-    if(delay > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])
-      goto invalidate;
-
-    /* or the kicked delay would be in min */
-    if(kicked <= jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1]) 
-      goto invalidate;
-
-    if(kicked >= jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1]) 
-      goto invalidate;
-
-    /* if we got here, we don't need to invalidate, 'cause this delay didn't 
-     * affect things */
-    return 0;
-    /* end optimization */
-
-
-invalidate:
-    jb->hist_maxbuf_valid = 0;
-    return 0;
-}
-
-static void history_calc_maxbuf(jitterbuf *jb) 
-{
-    int i,j;
-
-    if(jb->hist_ptr == 0) return;
-
-
-    /* initialize maxbuf/minbuf to the latest value */
-    for(i=0;i<JB_HISTORY_MAXBUF_SZ;i++) {
-/*
- * jb->hist_maxbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
- * jb->hist_minbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
- */
-      jb->hist_maxbuf[i] = JB_LONGMIN;
-      jb->hist_minbuf[i] = JB_LONGMAX;
-    }
-
-    /* use insertion sort to populate maxbuf */
-    /* we want it to be the top "n" values, in order */
-
-    /* start at the beginning, or JB_HISTORY_SZ frames ago */
-    i = (jb->hist_ptr > JB_HISTORY_SZ) ? (jb->hist_ptr - JB_HISTORY_SZ) : 0; 
-
-    for(;i<jb->hist_ptr;i++) {
-       time_in_ms_t toins = jb->history[i % JB_HISTORY_SZ];
-
-       /* if the maxbuf should get this */
-       if(toins > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])  {
-
-           /* insertion-sort it into the maxbuf */
-           for(j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
-               /* found where it fits */
-               if(toins > jb->hist_maxbuf[j]) {
-                   /* move over */
-                   memmove(jb->hist_maxbuf+j+1,jb->hist_maxbuf+j, (JB_HISTORY_MAXBUF_SZ-(j+1)) * sizeof(long));
-                   /* insert */
-                   jb->hist_maxbuf[j] = toins;
-
-                   break;
-               }
-           }
-       }
-
-       /* if the minbuf should get this */
-       if(toins < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])  {
-
-           /* insertion-sort it into the maxbuf */
-           for(j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
-               /* found where it fits */
-               if(toins < jb->hist_minbuf[j]) {
-                   /* move over */
-                   memmove(jb->hist_minbuf+j+1,jb->hist_minbuf+j, (JB_HISTORY_MAXBUF_SZ-(j+1)) * sizeof(long));
-                   /* insert */
-                   jb->hist_minbuf[j] = toins;
-
-                   break;
-               }
-           }
-       }
-
-       if(0) { 
-         int k;
-         fprintf(stderr, "toins = %ld\n", toins);
-         fprintf(stderr, "maxbuf =");
-         for(k=0;k<JB_HISTORY_MAXBUF_SZ;k++) 
-             fprintf(stderr, "%ld ", jb->hist_maxbuf[k]);
-         fprintf(stderr, "\nminbuf =");
-         for(k=0;k<JB_HISTORY_MAXBUF_SZ;k++) 
-             fprintf(stderr, "%ld ", jb->hist_minbuf[k]);
-         fprintf(stderr, "\n");
-       }
-    }
-
-    jb->hist_maxbuf_valid = 1;
-}
-
-static void history_get(jitterbuf *jb) 
-{
-    time_in_ms_t max, min, jitter;
-    int index;
-    int count;
-
-    if(!jb->hist_maxbuf_valid) 
-      history_calc_maxbuf(jb);
-
-    /* count is how many items in history we're examining */
-    count = (jb->hist_ptr < JB_HISTORY_SZ) ? jb->hist_ptr : JB_HISTORY_SZ;
-
-    /* index is the "n"ths highest/lowest that we'll look for */
-    index = count * JB_HISTORY_DROPPCT / 100;
-
-    /* sanity checks for index */
-    if(index > (JB_HISTORY_MAXBUF_SZ - 1)) index = JB_HISTORY_MAXBUF_SZ - 1;
-
-
-    if(index < 0) {
-      jb->info.min = 0;
-      jb->info.jitter = 0;
-      return;
-    }
-
-    max = jb->hist_maxbuf[index];
-    min = jb->hist_minbuf[index];
-
-    jitter = max - min;
-
-    /* these debug stmts compare the difference between looking at the absolute jitter, and the
-     * values we get by throwing away the outliers */
-    /*
-    fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", index, min, max, jitter);
-    fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", 0, jb->hist_minbuf[0], jb->hist_maxbuf[0], jb->hist_maxbuf[0]-jb->hist_minbuf[0]);
-    */
-
-    jb->info.min = min;
-    jb->info.jitter = jitter;
-}
-
-/* returns 1 if frame was inserted into head of queue, 0 otherwise */
-static int queue_put(jitterbuf *jb, void *data, int type, long ms, time_in_ms_t ts) 
-{
-    jb_frame *frame;
-    jb_frame *p;
-       int head = 0;
-       time_in_ms_t resync_ts = ts - jb->info.resync_offset;
-
-    frame = jb->free;
-    if(frame) {
-       jb->free = frame->next;
-    } else {
-       frame = malloc(sizeof(jb_frame));
-    }
-
-    if(!frame) {
-       jb_err("cannot allocate frame\n");
-       return 0;
-    }
-
-    jb->info.frames_cur++;
-
-    frame->data = data;
-       frame->ts = resync_ts;
-    frame->ms = ms;
-    frame->type = type;
-
-    /* 
-     * frames are a circular list, jb-frames points to to the lowest ts, 
-     * jb->frames->prev points to the highest ts
-     */
-
-    if(!jb->frames) {  /* queue is empty */
-       jb->frames = frame;
-       frame->next = frame;
-       frame->prev = frame;
-               head = 1;
-       } else if (resync_ts < jb->frames->ts) {
-       frame->next = jb->frames;
-       frame->prev = jb->frames->prev;
-
-       frame->next->prev = frame;
-       frame->prev->next = frame;
-
-       /* frame is out of order */
-       jb->info.frames_ooo++;
-
-       jb->frames = frame;
-               head = 1;
-    } else { 
-       p = jb->frames;
-
-       /* frame is out of order */
-       if(ts < p->prev->ts) jb->info.frames_ooo++;
-
-               while (resync_ts < p->prev->ts && p->prev != jb->frames) 
-           p = p->prev;
-
-       frame->next = p;
-       frame->prev = p->prev;
-
-       frame->next->prev = frame;
-       frame->prev->next = frame;
-    }
-       return head;
-}
-
-static time_in_ms_t queue_next(jitterbuf *jb) 
-{
-    if(jb->frames) return jb->frames->ts;
-    else return -1;
-}
-
-static time_in_ms_t queue_last(jitterbuf *jb) 
-{
-    if(jb->frames) return jb->frames->prev->ts;
-    else return -1;
-}
-
-static jb_frame *_queue_get(jitterbuf *jb, time_in_ms_t ts, int all) 
-{
-    jb_frame *frame;
-    frame = jb->frames;
-
-    if(!frame)
-       return NULL;
-
-    /*jb_warn("queue_get: ASK %ld FIRST %ld\n", ts, frame->ts); */
-
-    if(all || ts >= frame->ts) {
-       /* remove this frame */
-       frame->prev->next = frame->next;
-       frame->next->prev = frame->prev;
-
-       if(frame->next == frame)
-         jb->frames = NULL;
-       else
-         jb->frames = frame->next;
-
-
-       /* insert onto "free" single-linked list */
-       frame->next = jb->free;
-       jb->free = frame;
-
-       jb->info.frames_cur--;
-
-       /* we return the frame pointer, even though it's on free list, 
-        * but caller must copy data */
-       return frame;
-    } 
-
-    return NULL;
-}
-
-static jb_frame *queue_get(jitterbuf *jb, time_in_ms_t ts) 
-{
-    return _queue_get(jb,ts,0);
-}
-
-static jb_frame *queue_getall(jitterbuf *jb) 
-{
-    return _queue_get(jb,0,1);
-}
-
-#if 0
-/* some diagnostics */
-static void jb_dbginfo(jitterbuf *jb) 
-{
-    if(dbgf == NULL) return;
-
-    jb_dbg("\njb info: fin=%ld fout=%ld flate=%ld flost=%ld fdrop=%ld fcur=%ld\n",
-           jb->info.frames_in, jb->info.frames_out, jb->info.frames_late, jb->info.frames_lost, jb->info.frames_dropped, jb->info.frames_cur);
-       
-    jb_dbg("   jitter=%ld current=%ld target=%ld min=%ld sil=%d len=%d len/fcur=%ld\n",
-           jb->info.jitter, jb->info.current, jb->info.target, jb->info.min, jb->info.silence_begin_ts, jb->info.current - jb->info.min, 
-           jb->info.frames_cur ? (jb->info.current - jb->info.min)/jb->info.frames_cur : -8);
-    if(jb->info.frames_in > 0) 
-       jb_dbg("jb info: Loss PCT = %ld%%, Late PCT = %ld%%\n",
-           jb->info.frames_lost * 100/(jb->info.frames_in + jb->info.frames_lost), 
-           jb->info.frames_late * 100/jb->info.frames_in);
-       jb_dbg("jb info: queue %d -> %d.  last_ts %d (queue len: %d) last_ms %d\n",
-           queue_next(jb), 
-           queue_last(jb),
-           jb->info.next_voice_ts, 
-           queue_last(jb) - queue_next(jb),
-           jb->info.last_voice_ms);
-}
-#endif
-
-#ifdef DEEP_DEBUG
-static void jb_chkqueue(jitterbuf *jb) 
-{
-    int i=0;
-    jb_frame *p = jb->frames;
-
-    if(!p) {
-      return;
-    }
-
-    do {
-       if(p->next == NULL)  {
-         jb_err("Queue is BROKEN at item [%d]", i);    
-       }
-       i++;
-       p=p->next;
-    } while (p->next != jb->frames);
-}
-
-static void jb_dbgqueue(jitterbuf *jb) 
-{
-    int i=0;
-    jb_frame *p = jb->frames;
-
-    jb_dbg("queue: ");
-
-    if(!p) {
-      jb_dbg("EMPTY\n");
-      return;
-    }
-
-    do {
-       jb_dbg("[%d]=%ld ", i++, p->ts);
-       p=p->next;
-    } while (p->next != jb->frames);
-
-    jb_dbg("\n");
-}
-#endif
-
-int jb_put(jitterbuf *jb, void *data, int type, long ms, time_in_ms_t ts, time_in_ms_t now) 
-{
-    jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb, data, ms, ts, now);
-
-    jb->info.frames_in++;
-
-    if(type == JB_TYPE_VOICE) {
-      /* presently, I'm only adding VOICE frames to history and drift calculations; mostly because with the
-       * IAX integrations, I'm sending retransmitted control frames with their awkward timestamps through */
-               if (history_put(jb,ts,now,ms))
-                       return JB_DROP;
-    }
-
-       /* if put into head of queue, caller needs to reschedule */
-       if (queue_put(jb,data,type,ms,ts)) {
-               return JB_SCHED;
-       }
-
-    return JB_OK;
-}
-
-
-static int _jb_get(jitterbuf *jb, jb_frame *frameout, time_in_ms_t now, long interpl) 
-{
-    jb_frame *frame;
-    time_in_ms_t diff;
-
-    /*if((now - jb_next(jb)) > 2 * jb->info.last_voice_ms) jb_warn("SCHED: %ld", (now - jb_next(jb))); */
-    /* get jitter info */
-    history_get(jb);
-
-
-    /* target */
-    jb->info.target = jb->info.jitter + jb->info.min + JB_TARGET_EXTRA; 
-
-    /* if a hard clamp was requested, use it */
-    if((jb->info.conf.max_jitterbuf) && ((jb->info.target - jb->info.min) > jb->info.conf.max_jitterbuf)) {
-       jb_dbg("clamping target from %d to %d\n", (jb->info.target - jb->info.min), jb->info.conf.max_jitterbuf);
-       jb->info.target = jb->info.min + jb->info.conf.max_jitterbuf;
-    }
-
-    diff = jb->info.target - jb->info.current;
-
-    /*    jb_warn("diff = %d lms=%d last = %d now = %d\n", diff,  */
-    /* jb->info.last_voice_ms, jb->info.last_adjustment, now); */
-
-    /* let's work on non-silent case first */
-    if(!jb->info.silence_begin_ts) { 
-      /* we want to grow */
-      if( (diff > 0) && 
-         /* we haven't grown in the delay length */
-         (((jb->info.last_adjustment + JB_ADJUST_DELAY) < now) || 
-          /* we need to grow more than the "length" we have left */
-          (diff > queue_last(jb)  - queue_next(jb)) ) ) {
-              /* grow by interp frame len */
-             jb->info.current += interpl;
-              jb->info.next_voice_ts += interpl;
-              jb->info.last_voice_ms = interpl;
-             jb->info.last_adjustment = now;
-              jb->info.cnt_contig_interp++;
-             jb_dbg("G");
-              /* assume silence instead of continuing to interpolate */
-              if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp)
-                  jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;
-             return JB_INTERP;
-      }
-
-      frame = queue_get(jb, jb->info.next_voice_ts - jb->info.current);
-
-      /* not a voice frame; just return it. */
-      if(frame && frame->type != JB_TYPE_VOICE) {
-        /* track start of silence */
-       if(frame->type == JB_TYPE_SILENCE) {
-         jb->info.silence_begin_ts = frame->ts;
-          jb->info.cnt_contig_interp = 0;
-        }
-
-       *frameout = *frame;
-       jb->info.frames_out++;
-       jb_dbg("o");
-       return JB_OK;
-      }
-
-      /* voice frame is later than expected */
-      if(frame && frame->ts + jb->info.current < jb->info.next_voice_ts) {
-        if (frame->ts + jb->info.current > jb->info.next_voice_ts - jb->info.last_voice_ms) {
-            /* either we interpolated past this frame in the last jb_get */
-            /* or the frame is still in order, but came a little too quick */ 
-            *frameout = *frame;
-            /* reset expectation for next frame */
-            jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;
-            jb->info.frames_out++;
-            decrement_losspct(jb);
-            jb->info.cnt_contig_interp = 0;
-            jb_dbg("v");
-            return JB_OK;
-        } else {
-           /* voice frame is late */
-            *frameout = *frame;
-            jb->info.frames_out++;
-            decrement_losspct(jb);
-            jb->info.frames_late++;
-            jb->info.frames_lost--;
-            jb_dbg("l");
-            /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
-            jb_warninfo(jb); */
-            return JB_DROP;
-        }
-      }
-
-      /* keep track of frame sizes, to allow for variable sized-frames */
-      if(frame && frame->ms > 0) {
-       jb->info.last_voice_ms = frame->ms;
-      }
-
-      /* we want to shrink; shrink at 1 frame / 500ms */
-      /* unless we don't have a frame, then shrink 1 frame */
-      /* every 80ms (though perhaps we can shrink even faster */
-      /* in this case) */
-      if(diff < -JB_TARGET_EXTRA && 
-               ((!frame && jb->info.last_adjustment + 80 < now) || 
-                (jb->info.last_adjustment + 500 < now))) {
-       jb->info.last_adjustment = now;
-        jb->info.cnt_contig_interp = 0;
-
-       if(frame)  {
-         *frameout = *frame;
-          /* shrink by frame size we're throwing out */
-          jb->info.current -= frame->ms;
-         jb->info.frames_out++;
-         decrement_losspct(jb);
-         jb->info.frames_dropped++;
-         jb_dbg("s");
-         return JB_DROP;
-       } else {
-          /* shrink by last_voice_ms */
-          jb->info.current -= jb->info.last_voice_ms;
-         jb->info.frames_lost++;
-         increment_losspct(jb);
-         jb_dbg("S");
-         return JB_NOFRAME;
-       }
-      }
-
-      /* lost frame */
-      if(!frame) {
-         /* this is a bit of a hack for now, but if we're close to
-          * target, and we find a missing frame, it makes sense to
-          * grow, because the frame might just be a bit late;
-          * otherwise, we presently get into a pattern where we return
-          * INTERP for the lost frame, then it shows up next, and we
-          * throw it away because it's late */
-         /* I've recently only been able to replicate this using
-          * iaxclient talking to app_echo on asterisk.  In this case,
-          * my outgoing packets go through asterisk's (old)
-          * jitterbuffer, and then might get an unusual increasing delay 
-          * there if it decides to grow?? */
-         /* Update: that might have been a different bug, that has been fixed..
-          * But, this still seemed like a good idea, except that it ended up making a single actual
-          * lost frame get interpolated two or more times, when there was "room" to grow, so it might
-          * be a bit of a bad idea overall */
-         /*if(diff > -1 * jb->info.last_voice_ms) { 
-             jb->info.current += jb->info.last_voice_ms;
-             jb->info.last_adjustment = now;
-             jb_warn("g");
-             return JB_INTERP;
-         } */
-         jb->info.frames_lost++;
-         increment_losspct(jb);
-          jb->info.next_voice_ts += interpl;
-          jb->info.last_voice_ms = interpl;
-          jb->info.cnt_contig_interp++;
-          /* assume silence instead of continuing to interpolate */
-          if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp)
-            jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;
-         jb_dbg("L");
-         return JB_INTERP;
-      }
-
-      /* normal case; return the frame, increment stuff */
-      *frameout = *frame;
-      jb->info.next_voice_ts += frame->ms;
-      jb->info.frames_out++;
-      decrement_losspct(jb);
-      jb->info.cnt_contig_interp = 0;
-      jb_dbg("v");
-      return JB_OK;
-  } else {     
-      /* TODO: after we get the non-silent case down, we'll make the
-       * silent case -- basically, we'll just grow and shrink faster
-       * here, plus handle next_voice_ts a bit differently */
-      
-      /* to disable silent special case altogether, just uncomment this: */
-       /* jb->info.silence_begin_ts = 0; */
-
-       /* shrink interpl len every 10ms during silence */
-       if (diff < -JB_TARGET_EXTRA &&
-           jb->info.last_adjustment + 10 <= now) {
-         jb->info.current -= interpl;
-         jb->info.last_adjustment = now;
-       }
-
-       frame = queue_get(jb, now - jb->info.current);
-       if(!frame) {
-         return JB_NOFRAME;
-       } else if (frame->type != JB_TYPE_VOICE) {
-          /* normal case; in silent mode, got a non-voice frame */
-          *frameout = *frame;
-         jb->info.frames_out++;
-          return JB_OK;
-       }
-       if (frame->ts < jb->info.silence_begin_ts) {
-          /* voice frame is late */
-         *frameout = *frame;
-         jb->info.frames_out++;
-         decrement_losspct(jb);
-         jb->info.frames_late++;
-         jb->info.frames_lost--;
-         jb_dbg("l");
-         /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
-       jb_warninfo(jb); */
-         return JB_DROP;
-       } else { 
-          /* voice frame */
-         /* try setting current to target right away here */
-         jb->info.current = jb->info.target;
-         jb->info.silence_begin_ts = 0;
-         jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;
-         jb->info.last_voice_ms = frame->ms;
-         jb->info.frames_out++;
-         decrement_losspct(jb);
-         *frameout = *frame;
-         jb_dbg("V");
-         return JB_OK;
-       }
-  }
-}
-
-time_in_ms_t jb_next(jitterbuf *jb) 
-{
-    if(jb->info.silence_begin_ts) {
-      time_in_ms_t next = queue_next(jb);
-      if(next > 0) { 
-        /* shrink during silence */
-        if (jb->info.target - jb->info.current < -JB_TARGET_EXTRA)
-          return jb->info.last_adjustment + 10;
-        return next + jb->info.target;
-      }
-      else return JB_LONGMAX;
-    } else {
-      return jb->info.next_voice_ts;
-    }
-}
-
-int jb_get(jitterbuf *jb, jb_frame *frameout, time_in_ms_t now, long interpl) 
-{
-    int ret = _jb_get(jb,frameout,now,interpl);
-#if 0
-    static int lastts=0;
-    int thists = ((ret == JB_OK) || (ret == JB_DROP)) ? frameout->ts : 0;
-    jb_warn("jb_get(%x,%x,%ld) = %d (%d)\n", jb, frameout, now, ret, thists);
-    if(thists && thists < lastts) jb_warn("XXXX timestamp roll-back!!!\n");
-    lastts = thists;
-#endif
-    return ret;
-}
-
-int jb_getall(jitterbuf *jb, jb_frame *frameout) 
-{
-    jb_frame *frame;
-    frame = queue_getall(jb);
-
-    if(!frame) {
-      return JB_NOFRAME;
-    }
-
-    *frameout = *frame;
-    return JB_OK;
-}
-
-
-int jb_getinfo(jitterbuf *jb, jb_info *stats) 
-{
-
-    history_get(jb);
-
-    *stats = jb->info;
-
-  return JB_OK;
-}
-
-int jb_setconf(jitterbuf *jb, jb_conf *conf) 
-{
-  /* take selected settings from the struct */
-
-       jb->info.conf.max_jitterbuf = conf->max_jitterbuf;
-       jb->info.conf.resync_threshold = conf->resync_threshold;
-       jb->info.conf.max_contig_interp = conf->max_contig_interp;
-
-  return JB_OK;
-}
-
-
+/*\r
+ * jitterbuf: an application-independent jitterbuffer\r
+ *\r
+ * Copyrights:\r
+ * Copyright (C) 2004-2005, Horizon Wimba, Inc.\r
+ *\r
+ * Contributors:\r
+ * Steve Kann <stevek@stevek.com>\r
+ *\r
+ * This program is free software, distributed under the terms of\r
+ * the GNU Lesser (Library) General Public License\r
+ *\r
+ * Copyright on this file is disclaimed to Digium for inclusion in Asterisk\r
+ */\r
+\r
+#include "iax2.h"\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <sys/types.h>\r
+\r
+#include "jitterbuf.h"\r
+\r
+/* define these here, just for ancient compiler systems */\r
+#define JB_LONGMAX 2147483647L\r
+#define JB_LONGMIN (-JB_LONGMAX - 1L)\r
+\r
+/* MS VC can't do __VA_ARGS__ */\r
+#if (defined(WIN32)  ||  defined(_WIN32_WCE))  &&  defined(_MSC_VER)\r
+#define jb_warn if (warnf) warnf\r
+#define jb_err if (errf) errf\r
+#define jb_dbg if (dbgf) dbgf\r
+\r
+#ifdef DEEP_DEBUG\r
+  #define jb_dbg2 if (dbgf) dbgf\r
+#else\r
+  #define jb_dbg2 if (0) dbgf\r
+#endif\r
+\r
+#else\r
+\r
+#define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0)\r
+#define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0)\r
+#define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)\r
+\r
+#ifdef DEEP_DEBUG\r
+#define jb_dbg2(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)\r
+#else\r
+#define jb_dbg2(...) ((void)0)\r
+#endif\r
+\r
+#endif\r
+\r
+static jb_output_function_t warnf, errf, dbgf;\r
+\r
+void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg)\r
+{\r
+       errf = err;\r
+       warnf = warn;\r
+       dbgf = dbg;\r
+}\r
+\r
+static void increment_losspct(jitterbuf *jb)\r
+{\r
+       jb->info.losspct = (100000 + 499 * jb->info.losspct)/500;\r
+}\r
+\r
+static void decrement_losspct(jitterbuf *jb)\r
+{\r
+       jb->info.losspct = (499 * jb->info.losspct)/500;\r
+}\r
+\r
+void jb_reset(jitterbuf *jb)\r
+{\r
+       /* only save settings */\r
+       jb_conf s = jb->info.conf;\r
+       memset(jb, 0, sizeof(*jb));\r
+       jb->info.conf = s;\r
+\r
+       /* initialize length, using the configured value */\r
+       jb->info.current = jb->info.target = jb->info.conf.target_extra;\r
+       jb->info.silence_begin_ts = -1;\r
+}\r
+\r
+jitterbuf * jb_new()\r
+{\r
+       jitterbuf *jb;\r
+\r
+       if (!(jb = (jitterbuf *)malloc(sizeof(*jb))))\r
+               return NULL;\r
+\r
+       jb->info.conf.target_extra = JB_TARGET_EXTRA;\r
+\r
+       jb_reset(jb);\r
+\r
+       jb_dbg2("jb_new() = %x\n", jb);\r
+       return jb;\r
+}\r
+\r
+void jb_destroy(jitterbuf *jb)\r
+{\r
+       jb_frame *frame;\r
+       jb_dbg2("jb_destroy(%x)\n", jb);\r
+\r
+       /* free all the frames on the "free list" */\r
+       frame = jb->free;\r
+       while (frame != NULL) {\r
+               jb_frame *next = frame->next;\r
+               free(frame);\r
+               frame = next;\r
+       }\r
+\r
+       /* free ourselves! */\r
+       free(jb);\r
+}\r
+\r
+\r
+\r
+#if 0\r
+static int longcmp(const void *a, const void *b)\r
+{\r
+       return *(long *)a - *(long *)b;\r
+}\r
+#endif\r
+\r
+/* simple history manipulation */\r
+/* maybe later we can make the history buckets variable size, or something? */\r
+/* drop parameter determines whether we will drop outliers to minimize\r
+ * delay */\r
+static int history_put(jitterbuf *jb, time_in_ms_t ts, time_in_ms_t now, long ms)\r
+{\r
+       time_in_ms_t delay = now - (ts - jb->info.resync_offset);\r
+       time_in_ms_t threshold = 2 * jb->info.jitter + jb->info.conf.resync_threshold;\r
+       time_in_ms_t kicked;\r
+\r
+       /* don't add special/negative times to history */\r
+       if (ts <= 0)\r
+               return 0;\r
+\r
+       /* check for drastic change in delay */\r
+       if (jb->info.conf.resync_threshold != -1) {\r
+               if (iax_abs(delay - jb->info.last_delay) > threshold) {\r
+                       jb->info.cnt_delay_discont++;\r
+                       if (jb->info.cnt_delay_discont > 3) {\r
+                               /* resync the jitterbuffer */\r
+                               jb->info.cnt_delay_discont = 0;\r
+                               jb->hist_ptr = 0;\r
+                               jb->hist_maxbuf_valid = 0;\r
+\r
+                               jb_warn("Resyncing the jb. last_delay %ld, this delay %ld, threshold %ld, new offset %ld\n", jb->info.last_delay, delay, threshold, ts - now);\r
+                               jb->info.resync_offset = ts - now;\r
+                               jb->info.last_delay = delay = 0; /* after resync, frame is right on time */\r
+                       } else {\r
+                               return -1;\r
+                       }\r
+               } else {\r
+                       jb->info.last_delay = delay;\r
+                       jb->info.cnt_delay_discont = 0;\r
+               }\r
+       }\r
+\r
+       kicked = jb->history[jb->hist_ptr % JB_HISTORY_SZ];\r
+\r
+       jb->history[(jb->hist_ptr++) % JB_HISTORY_SZ] = delay;\r
+\r
+       /* optimization; the max/min buffers don't need to be recalculated,\r
+        * if this packet's entry doesn't change them. This happens if this\r
+        * packet is not involved, _and_ any packet that got kicked out of\r
+        * the history is also not involved. We do a number of comparisons,\r
+        * but it's probably still worthwhile, because it will usually\r
+        * succeed, and should be a lot faster than going through all 500\r
+        * packets in history */\r
+       if (!jb->hist_maxbuf_valid)\r
+               return 0;\r
+\r
+       /* don't do this until we've filled history\r
+        * (reduces some edge cases below) */\r
+       if (jb->hist_ptr < JB_HISTORY_SZ)\r
+               goto invalidate;\r
+\r
+       /* if the new delay would go into min */\r
+       if (delay < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])\r
+               goto invalidate;\r
+\r
+       /* or max.. */\r
+       if (delay > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])\r
+               goto invalidate;\r
+\r
+       /* or the kicked delay would be in min */\r
+       if (kicked <= jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])\r
+               goto invalidate;\r
+\r
+       if (kicked >= jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])\r
+               goto invalidate;\r
+\r
+       /* if we got here, we don't need to invalidate, 'cause this delay didn't\r
+        * affect things */\r
+       return 0;\r
+       /* end optimization */\r
+\r
+\r
+invalidate:\r
+       jb->hist_maxbuf_valid = 0;\r
+       return 0;\r
+}\r
+\r
+static void history_calc_maxbuf(jitterbuf *jb)\r
+{\r
+       int i,j;\r
+\r
+       if (jb->hist_ptr == 0)\r
+               return;\r
+\r
+\r
+       /* initialize maxbuf/minbuf to the latest value */\r
+       for (i=0;i<JB_HISTORY_MAXBUF_SZ;i++) {\r
+               /*\r
+                * jb->hist_maxbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];\r
+                * jb->hist_minbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];\r
+                */\r
+               jb->hist_maxbuf[i] = JB_LONGMIN;\r
+               jb->hist_minbuf[i] = JB_LONGMAX;\r
+       }\r
+\r
+       /* use insertion sort to populate maxbuf */\r
+       /* we want it to be the top "n" values, in order */\r
+\r
+       /* start at the beginning, or JB_HISTORY_SZ frames ago */\r
+       i = (jb->hist_ptr > JB_HISTORY_SZ) ? (jb->hist_ptr - JB_HISTORY_SZ) : 0;\r
+\r
+       for (;i<jb->hist_ptr;i++) {\r
+       time_in_ms_t toins = jb->history[i % JB_HISTORY_SZ];\r
+\r
+               /* if the maxbuf should get this */\r
+               if (toins > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])  {\r
+\r
+                       /* insertion-sort it into the maxbuf */\r
+                       for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {\r
+                               /* found where it fits */\r
+                               if (toins > jb->hist_maxbuf[j]) {\r
+                                       /* move over */\r
+                                       memmove(jb->hist_maxbuf + j + 1, jb->hist_maxbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_maxbuf[0]));\r
+                                       /* insert */\r
+                                       jb->hist_maxbuf[j] = toins;\r
+\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               /* if the minbuf should get this */\r
+               if (toins < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])  {\r
+\r
+                       /* insertion-sort it into the maxbuf */\r
+                       for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {\r
+                               /* found where it fits */\r
+                               if (toins < jb->hist_minbuf[j]) {\r
+                                       /* move over */\r
+                                       memmove(jb->hist_minbuf + j + 1, jb->hist_minbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_minbuf[0]));\r
+                                       /* insert */\r
+                                       jb->hist_minbuf[j] = toins;\r
+\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               if (0) {\r
+                       int k;\r
+                       fprintf(stderr, "toins = %ld\n", toins);\r
+                       fprintf(stderr, "maxbuf =");\r
+                       for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++)\r
+                               fprintf(stderr, "%ld ", jb->hist_maxbuf[k]);\r
+                       fprintf(stderr, "\nminbuf =");\r
+                       for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++)\r
+                               fprintf(stderr, "%ld ", jb->hist_minbuf[k]);\r
+                       fprintf(stderr, "\n");\r
+               }\r
+       }\r
+\r
+       jb->hist_maxbuf_valid = 1;\r
+}\r
+\r
+static void history_get(jitterbuf *jb)\r
+{\r
+    time_in_ms_t max, min, jitter;\r
+       int index;\r
+       int count;\r
+\r
+       if (!jb->hist_maxbuf_valid)\r
+               history_calc_maxbuf(jb);\r
+\r
+       /* count is how many items in history we're examining */\r
+       count = (jb->hist_ptr < JB_HISTORY_SZ) ? jb->hist_ptr : JB_HISTORY_SZ;\r
+\r
+       /* index is the "n"ths highest/lowest that we'll look for */\r
+       index = count * JB_HISTORY_DROPPCT / 100;\r
+\r
+       /* sanity checks for index */\r
+       if (index > (JB_HISTORY_MAXBUF_SZ - 1))\r
+               index = JB_HISTORY_MAXBUF_SZ - 1;\r
+\r
+\r
+       if (index < 0) {\r
+               jb->info.min = 0;\r
+               jb->info.jitter = 0;\r
+               return;\r
+       }\r
+\r
+       max = jb->hist_maxbuf[index];\r
+       min = jb->hist_minbuf[index];\r
+\r
+       jitter = max - min;\r
+\r
+       /* these debug stmts compare the difference between looking at the absolute jitter, and the\r
+        * values we get by throwing away the outliers */\r
+       /*\r
+       fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", index, min, max, jitter);\r
+       fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", 0, jb->hist_minbuf[0], jb->hist_maxbuf[0], jb->hist_maxbuf[0]-jb->hist_minbuf[0]);\r
+       */\r
+\r
+       jb->info.min = min;\r
+       jb->info.jitter = jitter;\r
+}\r
+\r
+/* returns 1 if frame was inserted into head of queue, 0 otherwise */\r
+static int queue_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, time_in_ms_t ts)\r
+{\r
+       jb_frame *frame;\r
+       jb_frame *p;\r
+       int head = 0;\r
+       time_in_ms_t resync_ts = ts - jb->info.resync_offset;\r
+\r
+       if ((frame = jb->free)) {\r
+               jb->free = frame->next;\r
+       } else if (!(frame = (jb_frame *)malloc(sizeof(*frame)))) {\r
+               jb_err("cannot allocate frame\n");\r
+               return 0;\r
+       }\r
+\r
+       jb->info.frames_cur++;\r
+\r
+       frame->data = data;\r
+       frame->ts = resync_ts;\r
+       frame->ms = ms;\r
+       frame->type = type;\r
+\r
+       /*\r
+        * frames are a circular list, jb-frames points to to the lowest ts,\r
+        * jb->frames->prev points to the highest ts\r
+        */\r
+\r
+       if (!jb->frames) {  /* queue is empty */\r
+               jb->frames = frame;\r
+               frame->next = frame;\r
+               frame->prev = frame;\r
+               head = 1;\r
+       } else if (resync_ts < jb->frames->ts) {\r
+               frame->next = jb->frames;\r
+               frame->prev = jb->frames->prev;\r
+\r
+               frame->next->prev = frame;\r
+               frame->prev->next = frame;\r
+\r
+               /* frame is out of order */\r
+               jb->info.frames_ooo++;\r
+\r
+               jb->frames = frame;\r
+               head = 1;\r
+       } else {\r
+               p = jb->frames;\r
+\r
+               /* frame is out of order */\r
+               if (resync_ts < p->prev->ts) jb->info.frames_ooo++;\r
+\r
+               while (resync_ts < p->prev->ts && p->prev != jb->frames)\r
+                       p = p->prev;\r
+\r
+               frame->next = p;\r
+               frame->prev = p->prev;\r
+\r
+               frame->next->prev = frame;\r
+               frame->prev->next = frame;\r
+       }\r
+       return head;\r
+}\r
+\r
+static time_in_ms_t queue_next(jitterbuf *jb) \r
+{\r
+       if (jb->frames)\r
+               return jb->frames->ts;\r
+       else\r
+               return -1;\r
+}\r
+\r
+static time_in_ms_t queue_last(jitterbuf *jb) \r
+{\r
+       if (jb->frames)\r
+               return jb->frames->prev->ts;\r
+       else\r
+               return -1;\r
+}\r
+\r
+static jb_frame *_queue_get(jitterbuf *jb, time_in_ms_t ts, int all) \r
+{\r
+       jb_frame *frame;\r
+       frame = jb->frames;\r
+\r
+       if (!frame)\r
+               return NULL;\r
+\r
+       /*jb_warn("queue_get: ASK %ld FIRST %ld\n", ts, frame->ts); */\r
+\r
+       if (all || ts >= frame->ts) {\r
+               /* remove this frame */\r
+               frame->prev->next = frame->next;\r
+               frame->next->prev = frame->prev;\r
+\r
+               if (frame->next == frame)\r
+                       jb->frames = NULL;\r
+               else\r
+                       jb->frames = frame->next;\r
+\r
+\r
+               /* insert onto "free" single-linked list */\r
+               frame->next = jb->free;\r
+               jb->free = frame;\r
+\r
+               jb->info.frames_cur--;\r
+\r
+               /* we return the frame pointer, even though it's on free list,\r
+                * but caller must copy data */\r
+               return frame;\r
+       }\r
+\r
+       return NULL;\r
+}\r
+\r
+static jb_frame *queue_get(jitterbuf *jb, time_in_ms_t ts) \r
+{\r
+       return _queue_get(jb,ts,0);\r
+}\r
+\r
+static jb_frame *queue_getall(jitterbuf *jb)\r
+{\r
+       return _queue_get(jb,0,1);\r
+}\r
+\r
+#if 0\r
+/* some diagnostics */\r
+static void jb_dbginfo(jitterbuf *jb)\r
+{\r
+       if (dbgf == NULL)\r
+               return;\r
+\r
+       jb_dbg("\njb info: fin=%ld fout=%ld flate=%ld flost=%ld fdrop=%ld fcur=%ld\n",\r
+               jb->info.frames_in, jb->info.frames_out, jb->info.frames_late, jb->info.frames_lost, jb->info.frames_dropped, jb->info.frames_cur);\r
+\r
+       jb_dbg("jitter=%ld current=%ld target=%ld min=%ld sil=%d len=%d len/fcur=%ld\n",\r
+               jb->info.jitter, jb->info.current, jb->info.target, jb->info.min, jb->info.silence_begin_ts, jb->info.current - jb->info.min,\r
+               jb->info.frames_cur ? (jb->info.current - jb->info.min)/jb->info.frames_cur : -8);\r
+       if (jb->info.frames_in > 0)\r
+               jb_dbg("jb info: Loss PCT = %ld%%, Late PCT = %ld%%\n",\r
+               jb->info.frames_lost * 100/(jb->info.frames_in + jb->info.frames_lost),\r
+               jb->info.frames_late * 100/jb->info.frames_in);\r
+       jb_dbg("jb info: queue %d -> %d.  last_ts %d (queue len: %d) last_ms %d\n",\r
+               queue_next(jb),\r
+               queue_last(jb),\r
+               jb->info.next_voice_ts,\r
+               queue_last(jb) - queue_next(jb),\r
+               jb->info.last_voice_ms);\r
+}\r
+#endif\r
+\r
+#ifdef DEEP_DEBUG\r
+static void jb_chkqueue(jitterbuf *jb)\r
+{\r
+       int i=0;\r
+       jb_frame *p = jb->frames;\r
+\r
+       if (!p) {\r
+               return;\r
+       }\r
+\r
+       do {\r
+               if (p->next == NULL)  {\r
+                       jb_err("Queue is BROKEN at item [%d]", i);\r
+               }\r
+               i++;\r
+               p=p->next;\r
+       } while (p->next != jb->frames);\r
+}\r
+\r
+static void jb_dbgqueue(jitterbuf *jb)\r
+{\r
+       int i=0;\r
+       jb_frame *p = jb->frames;\r
+\r
+       jb_dbg("queue: ");\r
+\r
+       if (!p) {\r
+               jb_dbg("EMPTY\n");\r
+               return;\r
+       }\r
+\r
+       do {\r
+               jb_dbg("[%d]=%ld ", i++, p->ts);\r
+               p=p->next;\r
+       } while (p->next != jb->frames);\r
+\r
+       jb_dbg("\n");\r
+}\r
+#endif\r
+\r
+enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, time_in_ms_t ts, time_in_ms_t now)\r
+{\r
+       jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb, data, ms, ts, now);\r
+\r
+       jb->info.frames_in++;\r
+\r
+       if (type == JB_TYPE_VOICE) {\r
+               /* presently, I'm only adding VOICE frames to history and drift\r
+                * calculations; mostly because with the IAX integrations, I'm\r
+                * sending retransmitted control frames with their awkward\r
+                * timestamps through\r
+                */\r
+               if (history_put(jb,ts,now,ms))\r
+                       return JB_DROP;\r
+       }\r
+\r
+       /* if put into head of queue, caller needs to reschedule */\r
+       if (queue_put(jb,data,type,ms,ts)) {\r
+               return JB_SCHED;\r
+       }\r
+\r
+       return JB_OK;\r
+}\r
+\r
+\r
+static enum jb_return_code _jb_get(jitterbuf *jb, jb_frame *frameout, time_in_ms_t now, long interpl)\r
+{\r
+       jb_frame *frame;\r
+       time_in_ms_t diff;\r
+\r
+       /*if ((now - jb_next(jb)) > 2 * jb->info.last_voice_ms) jb_warn("SCHED: %ld", (now - jb_next(jb))); */\r
+       /* get jitter info */\r
+       history_get(jb);\r
+\r
+\r
+       /* target */\r
+       jb->info.target = jb->info.jitter + jb->info.min + jb->info.conf.target_extra;\r
+\r
+       /* if a hard clamp was requested, use it */\r
+       if ((jb->info.conf.max_jitterbuf) && ((jb->info.target - jb->info.min) > jb->info.conf.max_jitterbuf)) {\r
+               jb_dbg("clamping target from %d to %d\n", (jb->info.target - jb->info.min), jb->info.conf.max_jitterbuf);\r
+               jb->info.target = jb->info.min + jb->info.conf.max_jitterbuf;\r
+       }\r
+\r
+       diff = jb->info.target - jb->info.current;\r
+\r
+       /* jb_warn("diff = %d lms=%d last = %d now = %d\n", diff,  */\r
+       /*      jb->info.last_voice_ms, jb->info.last_adjustment, now); */\r
+\r
+       /* let's work on non-silent case first */\r
+       if (!jb->info.silence_begin_ts) {\r
+               /* we want to grow */\r
+               if ((diff > 0) &&\r
+                       /* we haven't grown in the delay length */\r
+                       (((jb->info.last_adjustment + JB_ADJUST_DELAY) < now) ||\r
+                       /* we need to grow more than the "length" we have left */\r
+                       (diff > queue_last(jb) - queue_next(jb)) ) ) {\r
+                       /* grow by interp frame length */\r
+                       jb->info.current += interpl;\r
+                       jb->info.next_voice_ts += interpl;\r
+                       jb->info.last_voice_ms = interpl;\r
+                       jb->info.last_adjustment = now;\r
+                       jb->info.cnt_contig_interp++;\r
+                       /* assume silence instead of continuing to interpolate */\r
+                       if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) {\r
+                               jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;\r
+                       }\r
+                       jb_dbg("G");\r
+                       return JB_INTERP;\r
+               }\r
+\r
+               frame = queue_get(jb, jb->info.next_voice_ts - jb->info.current);\r
+\r
+               /* not a voice frame; just return it. */\r
+               if (frame && frame->type != JB_TYPE_VOICE) {\r
+                       /* track start of silence */\r
+                       if (frame->type == JB_TYPE_SILENCE) {\r
+                               jb->info.silence_begin_ts = frame->ts;\r
+                               jb->info.cnt_contig_interp = 0;\r
+                       }\r
+\r
+                       *frameout = *frame;\r
+                       jb->info.frames_out++;\r
+                       jb_dbg("o");\r
+                       return JB_OK;\r
+               }\r
+\r
+               /* voice frame is later than expected */\r
+               if (frame && frame->ts + jb->info.current < jb->info.next_voice_ts) {\r
+                       if (frame->ts + jb->info.current > jb->info.next_voice_ts - jb->info.last_voice_ms) {\r
+                               /* either we interpolated past this frame in the last jb_get */\r
+                               /* or the frame is still in order, but came a little too quick */\r
+                               *frameout = *frame;\r
+                               /* reset expectation for next frame */\r
+                               jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;\r
+                               jb->info.frames_out++;\r
+                               decrement_losspct(jb);\r
+                               jb->info.cnt_contig_interp = 0;\r
+                               jb_dbg("v");\r
+                               return JB_OK;\r
+                       } else {\r
+                               /* voice frame is late */\r
+                               *frameout = *frame;\r
+                               jb->info.frames_out++;\r
+                               decrement_losspct(jb);\r
+                               jb->info.frames_late++;\r
+                               jb->info.frames_lost--;\r
+                               jb_dbg("l");\r
+                               /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));\r
+                                 jb_warninfo(jb); */\r
+                               return JB_DROP;\r
+                       }\r
+               }\r
+\r
+               /* keep track of frame sizes, to allow for variable sized-frames */\r
+               if (frame && frame->ms > 0) {\r
+                       jb->info.last_voice_ms = frame->ms;\r
+               }\r
+\r
+               /* we want to shrink; shrink at 1 frame / 500ms */\r
+               /* unless we don't have a frame, then shrink 1 frame */\r
+               /* every 80ms (though perhaps we can shrink even faster */\r
+               /* in this case) */\r
+               if (diff < -jb->info.conf.target_extra &&\r
+                               ((!frame && jb->info.last_adjustment + 80 < now) ||\r
+                                (jb->info.last_adjustment + 500 < now))) {\r
+\r
+                       jb->info.last_adjustment = now;\r
+                       jb->info.cnt_contig_interp = 0;\r
+\r
+                       if (frame) {\r
+                               *frameout = *frame;\r
+                               /* shrink by frame size we're throwing out */\r
+                               jb->info.current -= frame->ms;\r
+                               jb->info.frames_out++;\r
+                               decrement_losspct(jb);\r
+                               jb->info.frames_dropped++;\r
+                               jb_dbg("s");\r
+                               return JB_DROP;\r
+                       } else {\r
+                               /* shrink by last_voice_ms */\r
+                               jb->info.current -= jb->info.last_voice_ms;\r
+                               jb->info.frames_lost++;\r
+                               increment_losspct(jb);\r
+                               jb_dbg("S");\r
+                               return JB_NOFRAME;\r
+                       }\r
+               }\r
+\r
+               /* lost frame */\r
+               if (!frame) {\r
+                       /* this is a bit of a hack for now, but if we're close to\r
+                        * target, and we find a missing frame, it makes sense to\r
+                        * grow, because the frame might just be a bit late;\r
+                        * otherwise, we presently get into a pattern where we return\r
+                        * INTERP for the lost frame, then it shows up next, and we\r
+                        * throw it away because it's late */\r
+                       /* I've recently only been able to replicate this using\r
+                        * iaxclient talking to app_echo on asterisk.  In this case,\r
+                        * my outgoing packets go through asterisk's (old)\r
+                        * jitterbuffer, and then might get an unusual increasing delay\r
+                        * there if it decides to grow?? */\r
+                       /* Update: that might have been a different bug, that has been fixed..\r
+                        * But, this still seemed like a good idea, except that it ended up making a single actual\r
+                        * lost frame get interpolated two or more times, when there was "room" to grow, so it might\r
+                        * be a bit of a bad idea overall */\r
+                       /*if (diff > -1 * jb->info.last_voice_ms) {\r
+                               jb->info.current += jb->info.last_voice_ms;\r
+                               jb->info.last_adjustment = now;\r
+                               jb_warn("g");\r
+                               return JB_INTERP;\r
+                       } */\r
+                       jb->info.frames_lost++;\r
+                       increment_losspct(jb);\r
+                       jb->info.next_voice_ts += interpl;\r
+                       jb->info.last_voice_ms = interpl;\r
+                       jb->info.cnt_contig_interp++;\r
+                       /* assume silence instead of continuing to interpolate */\r
+                       if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) {\r
+                               jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;\r
+                       }\r
+                       jb_dbg("L");\r
+                       return JB_INTERP;\r
+               }\r
+\r
+               /* normal case; return the frame, increment stuff */\r
+               *frameout = *frame;\r
+               jb->info.next_voice_ts += frame->ms;\r
+               jb->info.frames_out++;\r
+               jb->info.cnt_contig_interp = 0;\r
+               decrement_losspct(jb);\r
+               jb_dbg("v");\r
+               return JB_OK;\r
+       } else {\r
+               /* TODO: after we get the non-silent case down, we'll make the\r
+                * silent case -- basically, we'll just grow and shrink faster\r
+                * here, plus handle next_voice_ts a bit differently */\r
+\r
+               /* to disable silent special case altogether, just uncomment this: */\r
+               /* jb->info.silence_begin_ts = 0; */\r
+\r
+               /* shrink interpl len every 10ms during silence */\r
+               if (diff < -jb->info.conf.target_extra &&\r
+                       jb->info.last_adjustment + 10 <= now) {\r
+                       jb->info.current -= interpl;\r
+                       jb->info.last_adjustment = now;\r
+               }\r
+\r
+               frame = queue_get(jb, now - jb->info.current);\r
+               if (!frame) {\r
+                       return JB_NOFRAME;\r
+               } else if (frame->type != JB_TYPE_VOICE) {\r
+                       /* normal case; in silent mode, got a non-voice frame */\r
+                       *frameout = *frame;\r
+                       jb->info.frames_out++;\r
+                       return JB_OK;\r
+               }\r
+               if (frame->ts < jb->info.silence_begin_ts) {\r
+                       /* voice frame is late */\r
+                       *frameout = *frame;\r
+                       jb->info.frames_out++;\r
+                       decrement_losspct(jb);\r
+                       jb->info.frames_late++;\r
+                       jb->info.frames_lost--;\r
+                       jb_dbg("l");\r
+                       /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));\r
+                         jb_warninfo(jb); */\r
+                       return JB_DROP;\r
+               } else {\r
+                       /* voice frame */\r
+                       /* try setting current to target right away here */\r
+                       jb->info.current = jb->info.target;\r
+                       jb->info.silence_begin_ts = 0;\r
+                       jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;\r
+                       jb->info.last_voice_ms = frame->ms;\r
+                       jb->info.frames_out++;\r
+                       decrement_losspct(jb);\r
+                       *frameout = *frame;\r
+                       jb_dbg("V");\r
+                       return JB_OK;\r
+               }\r
+       }\r
+}\r
+\r
+time_in_ms_t jb_next(jitterbuf *jb)\r
+{\r
+       if (jb->info.silence_begin_ts) {\r
+               if (jb->frames) {\r
+                       time_in_ms_t next = queue_next(jb);\r
+                       history_get(jb);\r
+                       /* shrink during silence */\r
+                       if (jb->info.target - jb->info.current < -jb->info.conf.target_extra)\r
+                               return jb->info.last_adjustment + 10;\r
+                       return next + jb->info.target;\r
+               }\r
+               else\r
+                       return JB_LONGMAX;\r
+       } else {\r
+               return jb->info.next_voice_ts;\r
+       }\r
+}\r
+\r
+enum jb_return_code jb_get(jitterbuf *jb, jb_frame *frameout, time_in_ms_t now, long interpl)\r
+{\r
+       enum jb_return_code ret = _jb_get(jb, frameout, now, interpl);\r
+#if 0\r
+       static int lastts=0;\r
+       int thists = ((ret == JB_OK) || (ret == JB_DROP)) ? frameout->ts : 0;\r
+       jb_warn("jb_get(%x,%x,%ld) = %d (%d)\n", jb, frameout, now, ret, thists);\r
+       if (thists && thists < lastts) jb_warn("XXXX timestamp roll-back!!!\n");\r
+       lastts = thists;\r
+#endif\r
+       return ret;\r
+}\r
+\r
+enum jb_return_code jb_getall(jitterbuf *jb, jb_frame *frameout)\r
+{\r
+       jb_frame *frame;\r
+       frame = queue_getall(jb);\r
+\r
+       if (!frame) {\r
+               return JB_NOFRAME;\r
+       }\r
+\r
+       *frameout = *frame;\r
+       return JB_OK;\r
+}\r
+\r
+\r
+enum jb_return_code jb_getinfo(jitterbuf *jb, jb_info *stats)\r
+{\r
+       history_get(jb);\r
+\r
+       *stats = jb->info;\r
+\r
+       return JB_OK;\r
+}\r
+\r
+enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf)\r
+{\r
+       /* take selected settings from the struct */\r
+\r
+       jb->info.conf.max_jitterbuf = conf->max_jitterbuf;\r
+       jb->info.conf.resync_threshold = conf->resync_threshold;\r
+       jb->info.conf.max_contig_interp = conf->max_contig_interp;\r
+\r
+       /* -1 indicates use of the default JB_TARGET_EXTRA value */\r
+       jb->info.conf.target_extra = ( conf->target_extra == -1 )\r
+               ? JB_TARGET_EXTRA\r
+               : conf->target_extra\r
+               ;\r
+\r
+       /* update these to match new target_extra setting */\r
+       jb->info.current = jb->info.conf.target_extra;\r
+       jb->info.target = jb->info.conf.target_extra;\r
+\r
+       return JB_OK;\r
+}\r
+\r
+\r
index 300cb5a4c659553778684e917d5a209f9e914d43..eb6721bfd7a65b35d3adb30ad0c0f29cb954510b 100644 (file)
-/*
- * jitterbuf: an application-independent jitterbuffer
- *
- * Copyrights:
- * Copyright (C) 2004-2005, Horizon Wimba, Inc.
- *
- * Contributors:
- * Steve Kann <stevek@stevek.com>
- *
- * This program is free software, distributed under the terms of
- * the GNU Lesser (Library) General Public License
- *
- * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
- */
-
-#ifndef _JITTERBUF_H_
-#define _JITTERBUF_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* configuration constants */
-       /* Number of historical timestamps to use in calculating jitter and drift */
-#define JB_HISTORY_SZ          500     
-       /* what percentage of timestamps should we drop from the history when we examine it;
-        * this might eventually be something made configurable */
-#define JB_HISTORY_DROPPCT     3
-       /* the maximum droppct we can handle (say it was configurable). */
-#define JB_HISTORY_DROPPCT_MAX 4
-       /* the size of the buffer we use to keep the top and botton timestamps for dropping */
-#define JB_HISTORY_MAXBUF_SZ   JB_HISTORY_SZ * JB_HISTORY_DROPPCT_MAX / 100 
-       /* amount of additional jitterbuffer adjustment  */
-#define JB_TARGET_EXTRA 40
-       /* ms between growing and shrinking; may not be honored if jitterbuffer runs out of space */
-#define JB_ADJUST_DELAY 40
-
-
-/* return codes */
-#define JB_OK          0
-#define JB_EMPTY       1
-#define JB_NOFRAME     2
-#define JB_INTERP      3
-#define JB_DROP                4
-#define JB_SCHED       5
-
-/* frame types */
-#define JB_TYPE_CONTROL        0
-#define JB_TYPE_VOICE  1
-#define JB_TYPE_VIDEO  2  /* reserved */
-#define JB_TYPE_SILENCE        3
-
-
-typedef struct jb_conf {
-       /* settings */
-       long max_jitterbuf;     /* defines a hard clamp to use in setting the jitter buffer delay */
-       long resync_threshold;  /* the jb will resync when delay increases to (2 * jitter) + this param */
-       long max_contig_interp; /* the max interp frames to return in a row */
-} jb_conf;
-
-typedef struct jb_info {
-       jb_conf conf;
-
-       /* statistics */
-       long frames_in;         /* number of frames input to the jitterbuffer.*/
-       long frames_out;        /* number of frames output from the jitterbuffer.*/
-       long frames_late;       /* number of frames which were too late, and dropped.*/
-       long frames_lost;       /* number of missing frames.*/
-       long frames_dropped;    /* number of frames dropped (shrinkage) */
-       long frames_ooo;        /* number of frames received out-of-order */
-       long frames_cur;        /* number of frames presently in jb, awaiting delivery.*/
-       time_in_ms_t jitter;            /* jitter measured within current history interval*/
-       time_in_ms_t min;               /* minimum lateness within current history interval */
-       time_in_ms_t current;           /* the present jitterbuffer adjustment */
-       time_in_ms_t target;            /* the target jitterbuffer adjustment */
-       long losspct;           /* recent lost frame percentage (* 1000) */
-       time_in_ms_t next_voice_ts;     /* the ts of the next frame to be read from the jb - in receiver's time */
-       long last_voice_ms;     /* the duration of the last voice frame */
-       time_in_ms_t silence_begin_ts;  /* the time of the last CNG frame, when in silence */
-       time_in_ms_t last_adjustment;   /* the time of the last adjustment */
-       time_in_ms_t last_delay;        /* the last now added to history */
-       long cnt_delay_discont; /* the count of discontinuous delays */
-       time_in_ms_t resync_offset;     /* the amount to offset ts to support resyncs */
-       long cnt_contig_interp; /* the number of contiguous interp frames returned */
-} jb_info;
-
-typedef struct jb_frame {
-       void *data;             /* the frame data */
-       time_in_ms_t ts;        /* the relative delivery time expected */
-       long ms;        /* the time covered by this frame, in sec/8000 */
-       int  type;      /* the type of frame */
-       struct jb_frame *next, *prev;
-} jb_frame;
-
-typedef struct jitterbuf {
-       jb_info info;
-
-       /* history */
-       time_in_ms_t history[JB_HISTORY_SZ];            /* history */
-       int  hist_ptr;                          /* points to index in history for next entry */
-       time_in_ms_t hist_maxbuf[JB_HISTORY_MAXBUF_SZ]; /* a sorted buffer of the max delays (highest first) */
-       time_in_ms_t hist_minbuf[JB_HISTORY_MAXBUF_SZ]; /* a sorted buffer of the min delays (lowest first) */
-       int  hist_maxbuf_valid;                 /* are the "maxbuf"/minbuf valid? */
-
-
-       jb_frame *frames;               /* queued frames */
-       jb_frame *free;                 /* free frames (avoid malloc?) */
-} jitterbuf;
-
-
-/* new jitterbuf */
-extern jitterbuf *             jb_new(void);
-
-/* destroy jitterbuf */
-extern void                    jb_destroy(jitterbuf *jb);
-
-/* reset jitterbuf */
-/* NOTE:  The jitterbuffer should be empty before you call this, otherwise
- * you will leak queued frames, and some internal structures */
-extern void                    jb_reset(jitterbuf *jb);
-
-/* queue a frame data=frame data, timings (in ms): ms=length of frame (for voice), ts=ts (sender's time) 
- * now=now (in receiver's time) return value is one of 
- * JB_OK: Frame added. Last call to jb_next() still valid
- * JB_DROP: Drop this frame immediately
- * JB_SCHED: Frame added. Call jb_next() to get a new time for the next frame
- */
-extern int                     jb_put(jitterbuf *jb, void *data, int type, long ms, time_in_ms_t ts, time_in_ms_t now);
-
-/* get a frame for time now (receiver's time)  return value is one of
- * JB_OK:  You've got frame!
- * JB_DROP: Here's an audio frame you should just drop.  Ask me again for this time..
- * JB_NOFRAME: There's no frame scheduled for this time.
- * JB_INTERP: Please interpolate an interpl-length frame for this time (either we need to grow, or there was a lost frame) 
- * JB_EMPTY: The jb is empty.
- */
-extern int                     jb_get(jitterbuf *jb, jb_frame *frame, time_in_ms_t now, long interpl);
-
-/* unconditionally get frames from jitterbuf until empty */
-extern int jb_getall(jitterbuf *jb, jb_frame *frameout);
-
-/* when is the next frame due out, in receiver's time (0=EMPTY) 
- * This value may change as frames are added (esp non-audio frames) */
-extern time_in_ms_t            jb_next(jitterbuf *jb);
-
-/* get jitterbuf info: only "statistics" may be valid */
-extern int                     jb_getinfo(jitterbuf *jb, jb_info *stats);
-
-/* set jitterbuf conf */
-extern int                     jb_setconf(jitterbuf *jb, jb_conf *conf);
-
-typedef                void (*jb_output_function_t)(const char *fmt, ...);
-extern void                    jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg);
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#endif
+/*\r
+ * jitterbuf: an application-independent jitterbuffer\r
+ *\r
+ * Copyrights:\r
+ * Copyright (C) 2004-2005, Horizon Wimba, Inc.\r
+ *\r
+ * Contributors:\r
+ * Steve Kann <stevek@stevek.com>\r
+ *\r
+ * This program is free software, distributed under the terms of\r
+ * the GNU Lesser (Library) General Public License\r
+ *\r
+ * Copyright on this file is disclaimed to Digium for inclusion in Asterisk\r
+ */\r
+\r
+#ifndef _JITTERBUF_H_\r
+#define _JITTERBUF_H_\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+/* configuration constants */\r
+       /* Number of historical timestamps to use in calculating jitter and drift */\r
+#define JB_HISTORY_SZ          500\r
+       /* what percentage of timestamps should we drop from the history when we examine it;\r
+        * this might eventually be something made configurable */\r
+#define JB_HISTORY_DROPPCT     3\r
+       /* the maximum droppct we can handle (say it was configurable). */\r
+#define JB_HISTORY_DROPPCT_MAX 4\r
+       /* the size of the buffer we use to keep the top and botton timestamps for dropping */\r
+#define JB_HISTORY_MAXBUF_SZ   JB_HISTORY_SZ * JB_HISTORY_DROPPCT_MAX / 100\r
+       /* amount of additional jitterbuffer adjustment  */\r
+#define JB_TARGET_EXTRA 40\r
+       /* ms between growing and shrinking; may not be honored if jitterbuffer runs out of space */\r
+#define JB_ADJUST_DELAY 40\r
+\r
+enum jb_return_code {\r
+       /* return codes */\r
+       JB_OK,            /* 0 */\r
+       JB_EMPTY,         /* 1 */\r
+       JB_NOFRAME,       /* 2 */\r
+       JB_INTERP,        /* 3 */\r
+       JB_DROP,          /* 4 */\r
+       JB_SCHED          /* 5 */\r
+};\r
+\r
+enum jb_frame_type {\r
+/* frame types */\r
+       JB_TYPE_CONTROL,  /* 0            */\r
+       JB_TYPE_VOICE,    /* 1            */\r
+       JB_TYPE_VIDEO,    /* 2 - reserved */\r
+       JB_TYPE_SILENCE   /* 3            */\r
+};\r
+\r
+typedef struct jb_conf {\r
+       /* settings */\r
+       long max_jitterbuf;     /* defines a hard clamp to use in setting the jitter buffer delay */\r
+       long resync_threshold;  /* the jb will resync when delay increases to (2 * jitter) + this param */\r
+       long max_contig_interp; /* the max interp frames to return in a row */\r
+       long target_extra;      /* amount of additional jitterbuffer adjustment, overrides JB_TARGET_EXTRA */\r
+} jb_conf;\r
+\r
+typedef struct jb_info {\r
+       jb_conf conf;\r
+\r
+       /* statistics */\r
+       long frames_in;         /* number of frames input to the jitterbuffer.*/\r
+       long frames_out;        /* number of frames output from the jitterbuffer.*/\r
+       long frames_late;       /* number of frames which were too late, and dropped.*/\r
+       long frames_lost;       /* number of missing frames.*/\r
+       long frames_dropped;    /* number of frames dropped (shrinkage) */\r
+       long frames_ooo;        /* number of frames received out-of-order */\r
+       long frames_cur;        /* number of frames presently in jb, awaiting delivery.*/\r
+       time_in_ms_t jitter;            /* jitter measured within current history interval*/\r
+       time_in_ms_t min;               /* minimum lateness within current history interval */\r
+       time_in_ms_t current;           /* the present jitterbuffer adjustment */\r
+       time_in_ms_t target;            /* the target jitterbuffer adjustment */\r
+       long losspct;           /* recent lost frame percentage (* 1000) */\r
+       time_in_ms_t next_voice_ts;     /* the ts of the next frame to be read from the jb - in receiver's time */\r
+       long last_voice_ms;     /* the duration of the last voice frame */\r
+       time_in_ms_t silence_begin_ts;  /* the time of the last CNG frame, when in silence */\r
+       time_in_ms_t last_adjustment;   /* the time of the last adjustment */\r
+       time_in_ms_t last_delay;        /* the last now added to history */\r
+       long cnt_delay_discont; /* the count of discontinuous delays */\r
+       time_in_ms_t resync_offset;     /* the amount to offset ts to support resyncs */\r
+       long cnt_contig_interp; /* the number of contiguous interp frames returned */\r
+} jb_info;\r
+\r
+typedef struct jb_frame {\r
+       void *data;               /* the frame data */\r
+       time_in_ms_t ts;        /* the relative delivery time expected */\r
+       long ms;                  /* the time covered by this frame, in sec/8000 */\r
+       enum jb_frame_type type;  /* the type of frame */\r
+       struct jb_frame *next, *prev;\r
+} jb_frame;\r
+\r
+typedef struct jitterbuf {\r
+       jb_info info;\r
+\r
+       /* history */\r
+       time_in_ms_t history[JB_HISTORY_SZ];            /* history */\r
+       int  hist_ptr;                          /* points to index in history for next entry */\r
+       time_in_ms_t hist_maxbuf[JB_HISTORY_MAXBUF_SZ]; /* a sorted buffer of the max delays (highest first) */\r
+       time_in_ms_t hist_minbuf[JB_HISTORY_MAXBUF_SZ]; /* a sorted buffer of the min delays (lowest first) */\r
+       int  hist_maxbuf_valid;                 /* are the "maxbuf"/minbuf valid? */\r
+\r
+       jb_frame *frames;               /* queued frames */\r
+       jb_frame *free;                 /* free frames (avoid malloc?) */\r
+} jitterbuf;\r
+\r
+\r
+/* new jitterbuf */\r
+jitterbuf *            jb_new(void);\r
+\r
+/* destroy jitterbuf */\r
+void                   jb_destroy(jitterbuf *jb);\r
+\r
+/* reset jitterbuf */\r
+/* NOTE:  The jitterbuffer should be empty before you call this, otherwise\r
+ * you will leak queued frames, and some internal structures */\r
+void                   jb_reset(jitterbuf *jb);\r
+\r
+/* queue a frame data=frame data, timings (in ms): ms=length of frame (for voice), ts=ts (sender's time)\r
+ * now=now (in receiver's time) return value is one of\r
+ * JB_OK: Frame added. Last call to jb_next() still valid\r
+ * JB_DROP: Drop this frame immediately\r
+ * JB_SCHED: Frame added. Call jb_next() to get a new time for the next frame\r
+ */\r
+enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, time_in_ms_t ts, time_in_ms_t now);\r
+\r
+/* get a frame for time now (receiver's time)  return value is one of\r
+ * JB_OK:  You've got frame!\r
+ * JB_DROP: Here's an audio frame you should just drop.  Ask me again for this time..\r
+ * JB_NOFRAME: There's no frame scheduled for this time.\r
+ * JB_INTERP: Please interpolate an interpl-length frame for this time (either we need to grow, or there was a lost frame)\r
+ * JB_EMPTY: The jb is empty.\r
+ */\r
+enum jb_return_code jb_get(jitterbuf *jb, jb_frame *frame, time_in_ms_t now, long interpl);\r
+\r
+/* unconditionally get frames from jitterbuf until empty */\r
+enum jb_return_code jb_getall(jitterbuf *jb, jb_frame *frameout);\r
+\r
+/* when is the next frame due out, in receiver's time (0=EMPTY)\r
+ * This value may change as frames are added (esp non-audio frames) */\r
+time_in_ms_t                   jb_next(jitterbuf *jb);\r
+\r
+/* get jitterbuf info: only "statistics" may be valid */\r
+enum jb_return_code jb_getinfo(jitterbuf *jb, jb_info *stats);\r
+\r
+/* set jitterbuf conf */\r
+enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf);\r
+\r
+typedef                        void (*jb_output_function_t)(const char *fmt, ...);\r
+extern void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg);\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+\r
+#endif\r