jitterbuf: an application-independent jitterbuffer More...

Go to the source code of this file.
Defines | |
| #define | jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0) |
| #define | jb_dbg2(...) ((void)0) |
| #define | jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0) |
| #define | JB_LONGMAX 2147483647L |
| #define | JB_LONGMIN (-JB_LONGMAX - 1L) |
| #define | jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0) |
Functions | |
| static enum jb_return_code | _jb_get (jitterbuf *jb, jb_frame *frameout, long now, long interpl) |
| static jb_frame * | _queue_get (jitterbuf *jb, long ts, int all) |
| static void | decrement_losspct (jitterbuf *jb) |
| static void | history_calc_maxbuf (jitterbuf *jb) |
| static void | history_get (jitterbuf *jb) |
| static int | history_put (jitterbuf *jb, long ts, long now, long ms) |
| simple history manipulation | |
| static void | increment_losspct (jitterbuf *jb) |
| void | jb_destroy (jitterbuf *jb) |
| destroy jitterbuf | |
| enum jb_return_code | jb_get (jitterbuf *jb, jb_frame *frameout, long now, long interpl) |
| 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. | |
| enum jb_return_code | jb_getall (jitterbuf *jb, jb_frame *frameout) |
| unconditionally get frames from jitterbuf until empty | |
| enum jb_return_code | jb_getinfo (jitterbuf *jb, jb_info *stats) |
| get jitterbuf info: only "statistics" may be valid | |
| jitterbuf * | jb_new () |
| new jitterbuf | |
| long | jb_next (jitterbuf *jb) |
| 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) | |
| enum jb_return_code | jb_put (jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts, long now) |
| queue a frame | |
| void | jb_reset (jitterbuf *jb) |
| reset jitterbuf | |
| enum jb_return_code | jb_setconf (jitterbuf *jb, jb_conf *conf) |
| set jitterbuf conf | |
| void | jb_setoutput (jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg) |
| static jb_frame * | queue_get (jitterbuf *jb, long ts) |
| static jb_frame * | queue_getall (jitterbuf *jb) |
| static long | queue_last (jitterbuf *jb) |
| static long | queue_next (jitterbuf *jb) |
| static int | queue_put (jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts) |
Variables | |
| static jb_output_function_t | dbgf |
| static jb_output_function_t | errf |
| static jb_output_function_t | warnf |
jitterbuf: an application-independent jitterbuffer
Definition in file jitterbuf.c.
Definition at line 43 of file jitterbuf.c.
Referenced by _jb_get().
| #define jb_dbg2 | ( | ... | ) | ((void)0) |
Definition at line 48 of file jitterbuf.c.
Referenced by jb_destroy(), jb_new(), and jb_put().
Definition at line 42 of file jitterbuf.c.
Referenced by queue_put().
| #define JB_LONGMAX 2147483647L |
define these here, just for ancient compiler systems
Definition at line 38 of file jitterbuf.c.
Referenced by history_calc_maxbuf(), and jb_next().
| #define JB_LONGMIN (-JB_LONGMAX - 1L) |
Definition at line 39 of file jitterbuf.c.
Referenced by history_calc_maxbuf().
Definition at line 41 of file jitterbuf.c.
Referenced by history_put(), and jb_get().
| static enum jb_return_code _jb_get | ( | jitterbuf * | jb, |
| jb_frame * | frameout, | ||
| long | now, | ||
| long | interpl | ||
| ) | [static] |
Definition at line 548 of file jitterbuf.c.
References jb_info::cnt_contig_interp, jb_info::conf, jb_info::current, decrement_losspct(), jb_info::frames_dropped, jb_info::frames_late, jb_info::frames_lost, jb_info::frames_out, history_get(), increment_losspct(), jitterbuf::info, JB_ADJUST_DELAY, jb_dbg, JB_DROP, JB_INTERP, JB_NOFRAME, JB_OK, JB_TYPE_SILENCE, JB_TYPE_VOICE, jb_info::jitter, jb_info::last_adjustment, jb_info::last_voice_ms, jb_conf::max_contig_interp, jb_conf::max_jitterbuf, jb_info::min, jb_frame::ms, jb_info::next_voice_ts, queue_get(), queue_last(), queue_next(), jb_info::silence_begin_ts, jb_info::target, jb_conf::target_extra, jb_frame::ts, and jb_frame::type.
Referenced by jb_get().
{
jb_frame *frame;
long diff;
static int dbg_cnt = 0;
/*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);
if (dbg_cnt && dbg_cnt % 50 == 0) {
jb_dbg("\n");
}
dbg_cnt++;
/* target */
jb->info.target = jb->info.jitter + jb->info.min + jb->info.conf.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 %ld to %ld\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 length */
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++;
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("G");
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) {
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->info.conf.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++;
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++;
jb->info.cnt_contig_interp = 0;
decrement_losspct(jb);
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->info.conf.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;
}
}
}
Definition at line 396 of file jitterbuf.c.
References jitterbuf::frames, jb_info::frames_cur, jitterbuf::free, jitterbuf::info, jb_frame::next, jb_frame::prev, and jb_frame::ts.
Referenced by queue_get(), and queue_getall().
{
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 void decrement_losspct | ( | jitterbuf * | jb | ) | [static] |
Definition at line 65 of file jitterbuf.c.
References jitterbuf::info, and jb_info::losspct.
Referenced by _jb_get().
| static void history_calc_maxbuf | ( | jitterbuf * | jb | ) | [static] |
Definition at line 200 of file jitterbuf.c.
References jitterbuf::hist_maxbuf, jitterbuf::hist_maxbuf_valid, jitterbuf::hist_minbuf, jitterbuf::hist_ptr, jitterbuf::history, JB_HISTORY_MAXBUF_SZ, JB_HISTORY_SZ, JB_LONGMAX, and JB_LONGMIN.
Referenced by history_get().
{
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++) {
long 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(jb->hist_maxbuf[0]));
/* 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(jb->hist_minbuf[0]));
/* 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 | ) | [static] |
Definition at line 277 of file jitterbuf.c.
References jitterbuf::hist_maxbuf, jitterbuf::hist_maxbuf_valid, jitterbuf::hist_minbuf, jitterbuf::hist_ptr, history_calc_maxbuf(), jitterbuf::info, JB_HISTORY_DROPPCT, JB_HISTORY_MAXBUF_SZ, JB_HISTORY_SZ, jb_info::jitter, and jb_info::min.
Referenced by _jb_get(), jb_getinfo(), and jb_next().
{
long max, min, jitter;
int idx;
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;
/* idx is the "n"ths highest/lowest that we'll look for */
idx = count * JB_HISTORY_DROPPCT / 100;
/* sanity checks for idx */
if (idx > (JB_HISTORY_MAXBUF_SZ - 1))
idx = JB_HISTORY_MAXBUF_SZ - 1;
if (idx < 0) {
jb->info.min = 0;
jb->info.jitter = 0;
return;
}
max = jb->hist_maxbuf[idx];
min = jb->hist_minbuf[idx];
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;
}
| static int history_put | ( | jitterbuf * | jb, |
| long | ts, | ||
| long | now, | ||
| long | ms | ||
| ) | [static] |
simple history manipulation
Definition at line 125 of file jitterbuf.c.
References jb_info::cnt_delay_discont, jb_info::conf, jitterbuf::hist_maxbuf, jitterbuf::hist_maxbuf_valid, jitterbuf::hist_ptr, jitterbuf::history, jitterbuf::info, JB_HISTORY_MAXBUF_SZ, JB_HISTORY_SZ, jb_warn, jb_info::jitter, jb_info::last_delay, jb_info::resync_offset, and jb_conf::resync_threshold.
Referenced by jb_put().
{
long delay = now - (ts - jb->info.resync_offset);
long threshold = 2 * jb->info.jitter + jb->info.conf.resync_threshold;
long 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 (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 increment_losspct | ( | jitterbuf * | jb | ) | [static] |
Definition at line 60 of file jitterbuf.c.
References jitterbuf::info, and jb_info::losspct.
Referenced by _jb_get().
| void jb_destroy | ( | jitterbuf * | jb | ) |
destroy jitterbuf
Definition at line 95 of file jitterbuf.c.
References ast_free, jitterbuf::free, jb_dbg2, and jb_frame::next.
Referenced by jb_destroy_adaptive(), and pvt_destructor().
| enum jb_return_code jb_get | ( | jitterbuf * | jb, |
| jb_frame * | frameout, | ||
| long | now, | ||
| long | interpl | ||
| ) |
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.
Definition at line 788 of file jitterbuf.c.
References _jb_get(), jitterbuf::info, JB_DROP, JB_INTERP, JB_OK, jb_warn, jb_info::last_voice_ms, jb_frame::ms, and jb_frame::ts.
Referenced by __get_from_jb(), and jb_get_adaptive().
{
enum jb_return_code 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
if (ret == JB_INTERP)
frameout->ms = jb->info.last_voice_ms;
return ret;
}
| enum jb_return_code jb_getall | ( | jitterbuf * | jb, |
| jb_frame * | frameout | ||
| ) |
unconditionally get frames from jitterbuf until empty
Definition at line 804 of file jitterbuf.c.
References JB_NOFRAME, JB_OK, and queue_getall().
Referenced by complete_transfer(), jb_empty_and_reset_adaptive(), jb_remove_adaptive(), pvt_destructor(), and schedule_delivery().
{
jb_frame *frame;
frame = queue_getall(jb);
if (!frame) {
return JB_NOFRAME;
}
*frameout = *frame;
return JB_OK;
}
| enum jb_return_code jb_getinfo | ( | jitterbuf * | jb, |
| jb_info * | stats | ||
| ) |
get jitterbuf info: only "statistics" may be valid
Definition at line 818 of file jitterbuf.c.
References history_get(), jitterbuf::info, and JB_OK.
Referenced by ast_cli_netstats(), construct_rr(), handle_cli_iax2_show_channels(), and log_jitterstats().
{
history_get(jb);
*stats = jb->info;
return JB_OK;
}
| jitterbuf* jb_new | ( | void | ) |
new jitterbuf
Definition at line 82 of file jitterbuf.c.
References ast_malloc, jb_dbg2, and jb_reset().
Referenced by jb_create_adaptive(), and new_iax().
{
jitterbuf *jb;
if (!(jb = ast_malloc(sizeof(*jb))))
return NULL;
jb_reset(jb);
jb_dbg2("jb_new() = %x\n", jb);
return jb;
}
| long jb_next | ( | jitterbuf * | jb | ) |
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)
Definition at line 770 of file jitterbuf.c.
References jb_info::conf, jb_info::current, jitterbuf::frames, history_get(), jitterbuf::info, JB_LONGMAX, jb_info::last_adjustment, jb_info::next_voice_ts, queue_next(), jb_info::silence_begin_ts, jb_info::target, and jb_conf::target_extra.
Referenced by __get_from_jb(), jb_next_adaptive(), and update_jbsched().
{
if (jb->info.silence_begin_ts) {
if (jb->frames) {
long next = queue_next(jb);
history_get(jb);
/* shrink during silence */
if (jb->info.target - jb->info.current < -jb->info.conf.target_extra)
return jb->info.last_adjustment + 10;
return next + jb->info.target;
}
else
return JB_LONGMAX;
} else {
return jb->info.next_voice_ts;
}
}
| enum jb_return_code jb_put | ( | jitterbuf * | jb, |
| void * | data, | ||
| const enum jb_frame_type | type, | ||
| long | ms, | ||
| long | ts, | ||
| long | now | ||
| ) |
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
Definition at line 507 of file jitterbuf.c.
References ast_debug, jb_info::conf, jitterbuf::dropem, jitterbuf::frames, jb_info::frames_dropped, jb_info::frames_in, history_put(), jitterbuf::info, jb_dbg2, JB_DROP, JB_OK, JB_SCHED, JB_TYPE_VOICE, jb_conf::max_jitterbuf, jb_frame::prev, queue_put(), and jb_frame::ts.
Referenced by jb_put_adaptive(), and schedule_delivery().
{
long numts;
jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb, data, ms, ts, now);
numts = 0;
if (jb->frames)
numts = jb->frames->prev->ts - jb->frames->ts;
if (numts >= jb->info.conf.max_jitterbuf) {
if (!jb->dropem) {
ast_debug(1, "Attempting to exceed Jitterbuf max %ld timeslots\n",
jb->info.conf.max_jitterbuf);
jb->dropem = 1;
}
jb->info.frames_dropped++;
return JB_DROP;
} else {
jb->dropem = 0;
}
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)) {
jb->info.frames_dropped++;
return JB_DROP;
}
}
jb->info.frames_in++;
/* if put into head of queue, caller needs to reschedule */
if (queue_put(jb,data,type,ms,ts)) {
return JB_SCHED;
}
return JB_OK;
}
| void jb_reset | ( | jitterbuf * | jb | ) |
reset jitterbuf
Definition at line 70 of file jitterbuf.c.
References jb_info::conf, jb_info::current, jitterbuf::info, JB_TARGET_EXTRA, jb_info::silence_begin_ts, jb_info::target, and jb_conf::target_extra.
Referenced by complete_transfer(), jb_empty_and_reset_adaptive(), jb_new(), and schedule_delivery().
{
/* only save settings */
jb_conf s = jb->info.conf;
memset(jb, 0, sizeof(*jb));
jb->info.conf = s;
/* initialize length, using the default value */
jb->info.current = jb->info.target = jb->info.conf.target_extra = JB_TARGET_EXTRA;
jb->info.silence_begin_ts = -1;
}
| enum jb_return_code jb_setconf | ( | jitterbuf * | jb, |
| jb_conf * | conf | ||
| ) |
set jitterbuf conf
Definition at line 828 of file jitterbuf.c.
References jb_info::conf, jb_info::current, jitterbuf::info, JB_OK, JB_TARGET_EXTRA, jb_conf::max_contig_interp, jb_conf::max_jitterbuf, jb_conf::resync_threshold, jb_info::target, and jb_conf::target_extra.
Referenced by jb_create_adaptive(), and new_iax().
{
/* 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;
/* -1 indicates use of the default JB_TARGET_EXTRA value */
jb->info.conf.target_extra = ( conf->target_extra == -1 )
? JB_TARGET_EXTRA
: conf->target_extra
;
/* update these to match new target_extra setting */
jb->info.current = jb->info.conf.target_extra;
jb->info.target = jb->info.conf.target_extra;
return JB_OK;
}
| void jb_setoutput | ( | jb_output_function_t | err, |
| jb_output_function_t | warn, | ||
| jb_output_function_t | dbg | ||
| ) |
Definition at line 53 of file jitterbuf.c.
Referenced by handle_cli_iax2_set_debug_jb(), and load_module().
Definition at line 431 of file jitterbuf.c.
References _queue_get().
Referenced by _jb_get().
{
return _queue_get(jb,ts,0);
}
Definition at line 436 of file jitterbuf.c.
References _queue_get().
Referenced by jb_getall().
{
return _queue_get(jb,0,1);
}
| static long queue_last | ( | jitterbuf * | jb | ) | [static] |
Definition at line 388 of file jitterbuf.c.
References jitterbuf::frames, jb_frame::prev, and jb_frame::ts.
Referenced by _jb_get().
| static long queue_next | ( | jitterbuf * | jb | ) | [static] |
Definition at line 380 of file jitterbuf.c.
References jitterbuf::frames, and jb_frame::ts.
| static int queue_put | ( | jitterbuf * | jb, |
| void * | data, | ||
| const enum jb_frame_type | type, | ||
| long | ms, | ||
| long | ts | ||
| ) | [static] |
Definition at line 319 of file jitterbuf.c.
References ast_malloc, jb_frame::data, frames, jitterbuf::frames, jb_info::frames_cur, jb_info::frames_ooo, jitterbuf::free, jitterbuf::info, jb_err, jb_frame::ms, jb_frame::next, jb_frame::prev, jb_info::resync_offset, jb_frame::ts, type, and jb_frame::type.
Referenced by jb_put().
{
jb_frame *frame;
jb_frame *p;
int head = 0;
long resync_ts = ts - jb->info.resync_offset;
if ((frame = jb->free)) {
jb->free = frame->next;
} else if (!(frame = ast_malloc(sizeof(*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 (resync_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;
}
Definition at line 51 of file jitterbuf.c.
Definition at line 51 of file jitterbuf.c.
jb_output_function_t warnf [static] |
Definition at line 51 of file jitterbuf.c.