2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2004-2005, Horizon Wimba, Inc.
7 * Steve Kann <stevek@stevek.com>
9 * A license has been granted to Digium (via disclaimer) for the use of
12 * See http://www.asterisk.org for more information about
13 * the Asterisk project. Please do not directly contact
14 * any of the maintainers of this project for assistance;
15 * the project provides a web site, mailing lists and IRC
16 * channels for your use.
18 * This program is free software, distributed under the terms of
19 * the GNU General Public License Version 2. See the LICENSE file
20 * at the top of the source tree.
25 * \brief jitterbuf: an application-independent jitterbuffer
26 * \author Steve Kann <stevek@stevek.com>
32 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
37 #include <sys/types.h>
39 #include "jitterbuf.h"
40 #include "asterisk/utils.h"
42 /*! define these here, just for ancient compiler systems */
43 #define JB_LONGMAX 2147483647L
44 #define JB_LONGMIN (-JB_LONGMAX - 1L)
46 #define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0)
47 #define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0)
48 #define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
51 #define jb_dbg2(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
53 #define jb_dbg2(...) ((void)0)
56 static jb_output_function_t warnf
, errf
, dbgf
;
58 void jb_setoutput(jb_output_function_t err
, jb_output_function_t warn
, jb_output_function_t dbg
)
65 static void increment_losspct(jitterbuf
*jb
)
67 jb
->info
.losspct
= (100000 + 499 * jb
->info
.losspct
)/500;
70 static void decrement_losspct(jitterbuf
*jb
)
72 jb
->info
.losspct
= (499 * jb
->info
.losspct
)/500;
75 void jb_reset(jitterbuf
*jb
)
77 /* only save settings */
78 jb_conf s
= jb
->info
.conf
;
79 memset(jb
, 0, sizeof(*jb
));
82 /* initialize length */
83 jb
->info
.current
= jb
->info
.target
= JB_TARGET_EXTRA
;
84 jb
->info
.silence_begin_ts
= -1;
91 if (!(jb
= ast_malloc(sizeof(*jb
))))
96 jb_dbg2("jb_new() = %x\n", jb
);
100 void jb_destroy(jitterbuf
*jb
)
103 jb_dbg2("jb_destroy(%x)\n", jb
);
105 /* free all the frames on the "free list" */
107 while (frame
!= NULL
) {
108 jb_frame
*next
= frame
->next
;
113 /* free ourselves! */
120 static int longcmp(const void *a
, const void *b
)
122 return *(long *)a
- *(long *)b
;
126 /*! \brief simple history manipulation
127 \note maybe later we can make the history buckets variable size, or something? */
128 /* drop parameter determines whether we will drop outliers to minimize
130 static int history_put(jitterbuf
*jb
, long ts
, long now
, long ms
)
132 long delay
= now
- (ts
- jb
->info
.resync_offset
);
133 long threshold
= 2 * jb
->info
.jitter
+ jb
->info
.conf
.resync_threshold
;
136 /* don't add special/negative times to history */
140 /* check for drastic change in delay */
141 if (jb
->info
.conf
.resync_threshold
!= -1) {
142 if (abs(delay
- jb
->info
.last_delay
) > threshold
) {
143 jb
->info
.cnt_delay_discont
++;
144 if (jb
->info
.cnt_delay_discont
> 3) {
145 /* resync the jitterbuffer */
146 jb
->info
.cnt_delay_discont
= 0;
148 jb
->hist_maxbuf_valid
= 0;
150 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
);
151 jb
->info
.resync_offset
= ts
- now
;
152 jb
->info
.last_delay
= delay
= 0; /* after resync, frame is right on time */
157 jb
->info
.last_delay
= delay
;
158 jb
->info
.cnt_delay_discont
= 0;
162 kicked
= jb
->history
[jb
->hist_ptr
% JB_HISTORY_SZ
];
164 jb
->history
[(jb
->hist_ptr
++) % JB_HISTORY_SZ
] = delay
;
166 /* optimization; the max/min buffers don't need to be recalculated, if this packet's
167 * entry doesn't change them. This happens if this packet is not involved, _and_ any packet
168 * that got kicked out of the history is also not involved
169 * We do a number of comparisons, but it's probably still worthwhile, because it will usually
170 * succeed, and should be a lot faster than going through all 500 packets in history */
171 if (!jb
->hist_maxbuf_valid
)
174 /* don't do this until we've filled history
175 * (reduces some edge cases below) */
176 if (jb
->hist_ptr
< JB_HISTORY_SZ
)
179 /* if the new delay would go into min */
180 if (delay
< jb
->hist_minbuf
[JB_HISTORY_MAXBUF_SZ
-1])
184 if (delay
> jb
->hist_maxbuf
[JB_HISTORY_MAXBUF_SZ
-1])
187 /* or the kicked delay would be in min */
188 if (kicked
<= jb
->hist_minbuf
[JB_HISTORY_MAXBUF_SZ
-1])
191 if (kicked
>= jb
->hist_maxbuf
[JB_HISTORY_MAXBUF_SZ
-1])
194 /* if we got here, we don't need to invalidate, 'cause this delay didn't
197 /* end optimization */
201 jb
->hist_maxbuf_valid
= 0;
205 static void history_calc_maxbuf(jitterbuf
*jb
)
209 if (jb
->hist_ptr
== 0)
213 /* initialize maxbuf/minbuf to the latest value */
214 for (i
=0;i
<JB_HISTORY_MAXBUF_SZ
;i
++) {
216 * jb->hist_maxbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
217 * jb->hist_minbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
219 jb
->hist_maxbuf
[i
] = JB_LONGMIN
;
220 jb
->hist_minbuf
[i
] = JB_LONGMAX
;
223 /* use insertion sort to populate maxbuf */
224 /* we want it to be the top "n" values, in order */
226 /* start at the beginning, or JB_HISTORY_SZ frames ago */
227 i
= (jb
->hist_ptr
> JB_HISTORY_SZ
) ? (jb
->hist_ptr
- JB_HISTORY_SZ
) : 0;
229 for (;i
<jb
->hist_ptr
;i
++) {
230 long toins
= jb
->history
[i
% JB_HISTORY_SZ
];
232 /* if the maxbuf should get this */
233 if (toins
> jb
->hist_maxbuf
[JB_HISTORY_MAXBUF_SZ
-1]) {
235 /* insertion-sort it into the maxbuf */
236 for (j
=0;j
<JB_HISTORY_MAXBUF_SZ
;j
++) {
237 /* found where it fits */
238 if (toins
> jb
->hist_maxbuf
[j
]) {
240 memmove(jb
->hist_maxbuf
+ j
+ 1, jb
->hist_maxbuf
+ j
, (JB_HISTORY_MAXBUF_SZ
- (j
+ 1)) * sizeof(jb
->hist_maxbuf
[0]));
242 jb
->hist_maxbuf
[j
] = toins
;
249 /* if the minbuf should get this */
250 if (toins
< jb
->hist_minbuf
[JB_HISTORY_MAXBUF_SZ
-1]) {
252 /* insertion-sort it into the maxbuf */
253 for (j
=0;j
<JB_HISTORY_MAXBUF_SZ
;j
++) {
254 /* found where it fits */
255 if (toins
< jb
->hist_minbuf
[j
]) {
257 memmove(jb
->hist_minbuf
+ j
+ 1, jb
->hist_minbuf
+ j
, (JB_HISTORY_MAXBUF_SZ
- (j
+ 1)) * sizeof(jb
->hist_minbuf
[0]));
259 jb
->hist_minbuf
[j
] = toins
;
268 fprintf(stderr
, "toins = %ld\n", toins
);
269 fprintf(stderr
, "maxbuf =");
270 for (k
=0;k
<JB_HISTORY_MAXBUF_SZ
;k
++)
271 fprintf(stderr
, "%ld ", jb
->hist_maxbuf
[k
]);
272 fprintf(stderr
, "\nminbuf =");
273 for (k
=0;k
<JB_HISTORY_MAXBUF_SZ
;k
++)
274 fprintf(stderr
, "%ld ", jb
->hist_minbuf
[k
]);
275 fprintf(stderr
, "\n");
279 jb
->hist_maxbuf_valid
= 1;
282 static void history_get(jitterbuf
*jb
)
284 long max
, min
, jitter
;
288 if (!jb
->hist_maxbuf_valid
)
289 history_calc_maxbuf(jb
);
291 /* count is how many items in history we're examining */
292 count
= (jb
->hist_ptr
< JB_HISTORY_SZ
) ? jb
->hist_ptr
: JB_HISTORY_SZ
;
294 /* index is the "n"ths highest/lowest that we'll look for */
295 index
= count
* JB_HISTORY_DROPPCT
/ 100;
297 /* sanity checks for index */
298 if (index
> (JB_HISTORY_MAXBUF_SZ
- 1))
299 index
= JB_HISTORY_MAXBUF_SZ
- 1;
308 max
= jb
->hist_maxbuf
[index
];
309 min
= jb
->hist_minbuf
[index
];
313 /* these debug stmts compare the difference between looking at the absolute jitter, and the
314 * values we get by throwing away the outliers */
316 fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", index, min, max, jitter);
317 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]);
321 jb
->info
.jitter
= jitter
;
324 /* returns 1 if frame was inserted into head of queue, 0 otherwise */
325 static int queue_put(jitterbuf
*jb
, void *data
, const enum jb_frame_type type
, long ms
, long ts
)
330 long resync_ts
= ts
- jb
->info
.resync_offset
;
332 if ((frame
= jb
->free
)) {
333 jb
->free
= frame
->next
;
334 } else if (!(frame
= ast_malloc(sizeof(*frame
)))) {
335 jb_err("cannot allocate frame\n");
339 jb
->info
.frames_cur
++;
342 frame
->ts
= resync_ts
;
347 * frames are a circular list, jb-frames points to to the lowest ts,
348 * jb->frames->prev points to the highest ts
351 if (!jb
->frames
) { /* queue is empty */
356 } else if (resync_ts
< jb
->frames
->ts
) {
357 frame
->next
= jb
->frames
;
358 frame
->prev
= jb
->frames
->prev
;
360 frame
->next
->prev
= frame
;
361 frame
->prev
->next
= frame
;
363 /* frame is out of order */
364 jb
->info
.frames_ooo
++;
371 /* frame is out of order */
372 if (resync_ts
< p
->prev
->ts
) jb
->info
.frames_ooo
++;
374 while (resync_ts
< p
->prev
->ts
&& p
->prev
!= jb
->frames
)
378 frame
->prev
= p
->prev
;
380 frame
->next
->prev
= frame
;
381 frame
->prev
->next
= frame
;
386 static long queue_next(jitterbuf
*jb
)
389 return jb
->frames
->ts
;
394 static long queue_last(jitterbuf
*jb
)
397 return jb
->frames
->prev
->ts
;
402 static jb_frame
*_queue_get(jitterbuf
*jb
, long ts
, int all
)
410 /*jb_warn("queue_get: ASK %ld FIRST %ld\n", ts, frame->ts); */
412 if (all
|| ts
>= frame
->ts
) {
413 /* remove this frame */
414 frame
->prev
->next
= frame
->next
;
415 frame
->next
->prev
= frame
->prev
;
417 if (frame
->next
== frame
)
420 jb
->frames
= frame
->next
;
423 /* insert onto "free" single-linked list */
424 frame
->next
= jb
->free
;
427 jb
->info
.frames_cur
--;
429 /* we return the frame pointer, even though it's on free list,
430 * but caller must copy data */
437 static jb_frame
*queue_get(jitterbuf
*jb
, long ts
)
439 return _queue_get(jb
,ts
,0);
442 static jb_frame
*queue_getall(jitterbuf
*jb
)
444 return _queue_get(jb
,0,1);
448 /* some diagnostics */
449 static void jb_dbginfo(jitterbuf
*jb
)
454 jb_dbg("\njb info: fin=%ld fout=%ld flate=%ld flost=%ld fdrop=%ld fcur=%ld\n",
455 jb
->info
.frames_in
, jb
->info
.frames_out
, jb
->info
.frames_late
, jb
->info
.frames_lost
, jb
->info
.frames_dropped
, jb
->info
.frames_cur
);
457 jb_dbg("jitter=%ld current=%ld target=%ld min=%ld sil=%d len=%d len/fcur=%ld\n",
458 jb
->info
.jitter
, jb
->info
.current
, jb
->info
.target
, jb
->info
.min
, jb
->info
.silence_begin_ts
, jb
->info
.current
- jb
->info
.min
,
459 jb
->info
.frames_cur
? (jb
->info
.current
- jb
->info
.min
)/jb
->info
.frames_cur
: -8);
460 if (jb
->info
.frames_in
> 0)
461 jb_dbg("jb info: Loss PCT = %ld%%, Late PCT = %ld%%\n",
462 jb
->info
.frames_lost
* 100/(jb
->info
.frames_in
+ jb
->info
.frames_lost
),
463 jb
->info
.frames_late
* 100/jb
->info
.frames_in
);
464 jb_dbg("jb info: queue %d -> %d. last_ts %d (queue len: %d) last_ms %d\n",
467 jb
->info
.next_voice_ts
,
468 queue_last(jb
) - queue_next(jb
),
469 jb
->info
.last_voice_ms
);
474 static void jb_chkqueue(jitterbuf
*jb
)
477 jb_frame
*p
= jb
->frames
;
484 if (p
->next
== NULL
) {
485 jb_err("Queue is BROKEN at item [%d]", i
);
489 } while (p
->next
!= jb
->frames
);
492 static void jb_dbgqueue(jitterbuf
*jb
)
495 jb_frame
*p
= jb
->frames
;
505 jb_dbg("[%d]=%ld ", i
++, p
->ts
);
507 } while (p
->next
!= jb
->frames
);
513 enum jb_return_code
jb_put(jitterbuf
*jb
, void *data
, const enum jb_frame_type type
, long ms
, long ts
, long now
)
515 jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb
, data
, ms
, ts
, now
);
517 jb
->info
.frames_in
++;
519 if (type
== JB_TYPE_VOICE
) {
520 /* presently, I'm only adding VOICE frames to history and drift calculations; mostly because with the
521 * IAX integrations, I'm sending retransmitted control frames with their awkward timestamps through */
522 if (history_put(jb
,ts
,now
,ms
))
526 /* if put into head of queue, caller needs to reschedule */
527 if (queue_put(jb
,data
,type
,ms
,ts
)) {
534 static enum jb_return_code
_jb_get(jitterbuf
*jb
, jb_frame
*frameout
, long now
, long interpl
)
538 static int dbg_cnt
= 0;
540 /*if ((now - jb_next(jb)) > 2 * jb->info.last_voice_ms) jb_warn("SCHED: %ld", (now - jb_next(jb))); */
541 /* get jitter info */
544 if (dbg_cnt
&& dbg_cnt
% 50 == 0) {
550 jb
->info
.target
= jb
->info
.jitter
+ jb
->info
.min
+ JB_TARGET_EXTRA
;
552 /* if a hard clamp was requested, use it */
553 if ((jb
->info
.conf
.max_jitterbuf
) && ((jb
->info
.target
- jb
->info
.min
) > jb
->info
.conf
.max_jitterbuf
)) {
554 jb_dbg("clamping target from %d to %d\n", (jb
->info
.target
- jb
->info
.min
), jb
->info
.conf
.max_jitterbuf
);
555 jb
->info
.target
= jb
->info
.min
+ jb
->info
.conf
.max_jitterbuf
;
558 diff
= jb
->info
.target
- jb
->info
.current
;
560 /* jb_warn("diff = %d lms=%d last = %d now = %d\n", diff, */
561 /* jb->info.last_voice_ms, jb->info.last_adjustment, now); */
563 /* let's work on non-silent case first */
564 if (!jb
->info
.silence_begin_ts
) {
565 /* we want to grow */
567 /* we haven't grown in the delay length */
568 (((jb
->info
.last_adjustment
+ JB_ADJUST_DELAY
) < now
) ||
569 /* we need to grow more than the "length" we have left */
570 (diff
> queue_last(jb
) - queue_next(jb
)) ) ) {
571 /* grow by interp frame length */
572 jb
->info
.current
+= interpl
;
573 jb
->info
.next_voice_ts
+= interpl
;
574 jb
->info
.last_voice_ms
= interpl
;
575 jb
->info
.last_adjustment
= now
;
576 jb
->info
.cnt_contig_interp
++;
577 if (jb
->info
.conf
.max_contig_interp
&& jb
->info
.cnt_contig_interp
>= jb
->info
.conf
.max_contig_interp
) {
578 jb
->info
.silence_begin_ts
= jb
->info
.next_voice_ts
- jb
->info
.current
;
584 frame
= queue_get(jb
, jb
->info
.next_voice_ts
- jb
->info
.current
);
586 /* not a voice frame; just return it. */
587 if (frame
&& frame
->type
!= JB_TYPE_VOICE
) {
588 if (frame
->type
== JB_TYPE_SILENCE
) {
589 jb
->info
.silence_begin_ts
= frame
->ts
;
590 jb
->info
.cnt_contig_interp
= 0;
594 jb
->info
.frames_out
++;
600 /* voice frame is later than expected */
601 if (frame
&& frame
->ts
+ jb
->info
.current
< jb
->info
.next_voice_ts
) {
602 if (frame
->ts
+ jb
->info
.current
> jb
->info
.next_voice_ts
- jb
->info
.last_voice_ms
) {
603 /* either we interpolated past this frame in the last jb_get */
604 /* or the frame is still in order, but came a little too quick */
606 /* reset expectation for next frame */
607 jb
->info
.next_voice_ts
= frame
->ts
+ jb
->info
.current
+ frame
->ms
;
608 jb
->info
.frames_out
++;
609 decrement_losspct(jb
);
610 jb
->info
.cnt_contig_interp
= 0;
614 /* voice frame is late */
616 jb
->info
.frames_out
++;
617 decrement_losspct(jb
);
618 jb
->info
.frames_late
++;
619 jb
->info
.frames_lost
--;
621 /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
627 /* keep track of frame sizes, to allow for variable sized-frames */
628 if (frame
&& frame
->ms
> 0) {
629 jb
->info
.last_voice_ms
= frame
->ms
;
632 /* we want to shrink; shrink at 1 frame / 500ms */
633 /* unless we don't have a frame, then shrink 1 frame */
634 /* every 80ms (though perhaps we can shrink even faster */
636 if (diff
< -JB_TARGET_EXTRA
&&
637 ((!frame
&& jb
->info
.last_adjustment
+ 80 < now
) ||
638 (jb
->info
.last_adjustment
+ 500 < now
))) {
640 jb
->info
.last_adjustment
= now
;
641 jb
->info
.cnt_contig_interp
= 0;
645 /* shrink by frame size we're throwing out */
646 jb
->info
.current
-= frame
->ms
;
647 jb
->info
.frames_out
++;
648 decrement_losspct(jb
);
649 jb
->info
.frames_dropped
++;
653 /* shrink by last_voice_ms */
654 jb
->info
.current
-= jb
->info
.last_voice_ms
;
655 jb
->info
.frames_lost
++;
656 increment_losspct(jb
);
664 /* this is a bit of a hack for now, but if we're close to
665 * target, and we find a missing frame, it makes sense to
666 * grow, because the frame might just be a bit late;
667 * otherwise, we presently get into a pattern where we return
668 * INTERP for the lost frame, then it shows up next, and we
669 * throw it away because it's late */
670 /* I've recently only been able to replicate this using
671 * iaxclient talking to app_echo on asterisk. In this case,
672 * my outgoing packets go through asterisk's (old)
673 * jitterbuffer, and then might get an unusual increasing delay
674 * there if it decides to grow?? */
675 /* Update: that might have been a different bug, that has been fixed..
676 * But, this still seemed like a good idea, except that it ended up making a single actual
677 * lost frame get interpolated two or more times, when there was "room" to grow, so it might
678 * be a bit of a bad idea overall */
679 /*if (diff > -1 * jb->info.last_voice_ms) {
680 jb->info.current += jb->info.last_voice_ms;
681 jb->info.last_adjustment = now;
685 jb
->info
.frames_lost
++;
686 increment_losspct(jb
);
687 jb
->info
.next_voice_ts
+= interpl
;
688 jb
->info
.last_voice_ms
= interpl
;
689 jb
->info
.cnt_contig_interp
++;
690 if (jb
->info
.conf
.max_contig_interp
&& jb
->info
.cnt_contig_interp
>= jb
->info
.conf
.max_contig_interp
) {
691 jb
->info
.silence_begin_ts
= jb
->info
.next_voice_ts
- jb
->info
.current
;
697 /* normal case; return the frame, increment stuff */
699 jb
->info
.next_voice_ts
+= frame
->ms
;
700 jb
->info
.frames_out
++;
701 jb
->info
.cnt_contig_interp
= 0;
702 decrement_losspct(jb
);
706 /* TODO: after we get the non-silent case down, we'll make the
707 * silent case -- basically, we'll just grow and shrink faster
708 * here, plus handle next_voice_ts a bit differently */
710 /* to disable silent special case altogether, just uncomment this: */
711 /* jb->info.silence_begin_ts = 0; */
713 /* shrink interpl len every 10ms during silence */
714 if (diff
< -JB_TARGET_EXTRA
&&
715 jb
->info
.last_adjustment
+ 10 <= now
) {
716 jb
->info
.current
-= interpl
;
717 jb
->info
.last_adjustment
= now
;
720 frame
= queue_get(jb
, now
- jb
->info
.current
);
723 } else if (frame
->type
!= JB_TYPE_VOICE
) {
724 /* normal case; in silent mode, got a non-voice frame */
726 jb
->info
.frames_out
++;
729 if (frame
->ts
< jb
->info
.silence_begin_ts
) {
730 /* voice frame is late */
732 jb
->info
.frames_out
++;
733 decrement_losspct(jb
);
734 jb
->info
.frames_late
++;
735 jb
->info
.frames_lost
--;
737 /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
742 /* try setting current to target right away here */
743 jb
->info
.current
= jb
->info
.target
;
744 jb
->info
.silence_begin_ts
= 0;
745 jb
->info
.next_voice_ts
= frame
->ts
+ jb
->info
.current
+ frame
->ms
;
746 jb
->info
.last_voice_ms
= frame
->ms
;
747 jb
->info
.frames_out
++;
748 decrement_losspct(jb
);
756 long jb_next(jitterbuf
*jb
)
758 if (jb
->info
.silence_begin_ts
) {
759 long next
= queue_next(jb
);
762 /* shrink during silence */
763 if (jb
->info
.target
- jb
->info
.current
< -JB_TARGET_EXTRA
)
764 return jb
->info
.last_adjustment
+ 10;
765 return next
+ jb
->info
.target
;
770 return jb
->info
.next_voice_ts
;
774 enum jb_return_code
jb_get(jitterbuf
*jb
, jb_frame
*frameout
, long now
, long interpl
)
776 enum jb_return_code ret
= _jb_get(jb
, frameout
, now
, interpl
);
779 int thists
= ((ret
== JB_OK
) || (ret
== JB_DROP
)) ? frameout
->ts
: 0;
780 jb_warn("jb_get(%x,%x,%ld) = %d (%d)\n", jb
, frameout
, now
, ret
, thists
);
781 if (thists
&& thists
< lastts
) jb_warn("XXXX timestamp roll-back!!!\n");
785 frameout
->ms
= jb
->info
.last_voice_ms
;
790 enum jb_return_code
jb_getall(jitterbuf
*jb
, jb_frame
*frameout
)
793 frame
= queue_getall(jb
);
804 enum jb_return_code
jb_getinfo(jitterbuf
*jb
, jb_info
*stats
)
814 enum jb_return_code
jb_setconf(jitterbuf
*jb
, jb_conf
*conf
)
816 /* take selected settings from the struct */
818 jb
->info
.conf
.max_jitterbuf
= conf
->max_jitterbuf
;
819 jb
->info
.conf
.resync_threshold
= conf
->resync_threshold
;
820 jb
->info
.conf
.max_contig_interp
= conf
->max_contig_interp
;