2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2004-2005, Horizon Wimba, Inc.
7 * Steve Kann <stevek@stevek.com>
9 * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
11 * See http://www.asterisk.org for more information about
12 * the Asterisk project. Please do not directly contact
13 * any of the maintainers of this project for assistance;
14 * the project provides a web site, mailing lists and IRC
15 * channels for your use.
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2. See the LICENSE file
19 * at the top of the source tree.
24 * \brief jitterbuf: an application-independent jitterbuffer
25 * \author Steve Kann <stevek@stevek.com>
33 #include <sys/types.h>
36 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
38 #include "jitterbuf.h"
39 #include "asterisk/utils.h"
41 /*! define these here, just for ancient compiler systems */
42 #define JB_LONGMAX 2147483647L
43 #define JB_LONGMIN (-JB_LONGMAX - 1L)
45 #define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0)
46 #define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0)
47 #define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
50 #define jb_dbg2(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
52 #define jb_dbg2(...) ((void)0)
55 static jb_output_function_t warnf
, errf
, dbgf
;
57 void jb_setoutput(jb_output_function_t err
, jb_output_function_t warn
, jb_output_function_t dbg
)
64 static void increment_losspct(jitterbuf
*jb
)
66 jb
->info
.losspct
= (100000 + 499 * jb
->info
.losspct
)/500;
69 static void decrement_losspct(jitterbuf
*jb
)
71 jb
->info
.losspct
= (499 * jb
->info
.losspct
)/500;
74 void jb_reset(jitterbuf
*jb
)
76 /* only save settings */
77 jb_conf s
= jb
->info
.conf
;
78 memset(jb
, 0, sizeof(*jb
));
81 /* initialize length */
82 jb
->info
.current
= jb
->info
.target
= JB_TARGET_EXTRA
;
83 jb
->info
.silence_begin_ts
= -1;
90 if (!(jb
= ast_malloc(sizeof(*jb
))))
95 jb_dbg2("jb_new() = %x\n", jb
);
99 void jb_destroy(jitterbuf
*jb
)
102 jb_dbg2("jb_destroy(%x)\n", jb
);
104 /* free all the frames on the "free list" */
106 while (frame
!= NULL
) {
107 jb_frame
*next
= frame
->next
;
112 /* free ourselves! */
119 static int longcmp(const void *a
, const void *b
)
121 return *(long *)a
- *(long *)b
;
125 /*! \brief simple history manipulation
126 \note maybe later we can make the history buckets variable size, or something? */
127 /* drop parameter determines whether we will drop outliers to minimize
129 static int history_put(jitterbuf
*jb
, long ts
, long now
, long ms
)
131 long delay
= now
- (ts
- jb
->info
.resync_offset
);
132 long threshold
= 2 * jb
->info
.jitter
+ jb
->info
.conf
.resync_threshold
;
135 /* don't add special/negative times to history */
139 /* check for drastic change in delay */
140 if (jb
->info
.conf
.resync_threshold
!= -1) {
141 if (abs(delay
- jb
->info
.last_delay
) > threshold
) {
142 jb
->info
.cnt_delay_discont
++;
143 if (jb
->info
.cnt_delay_discont
> 3) {
144 /* resync the jitterbuffer */
145 jb
->info
.cnt_delay_discont
= 0;
147 jb
->hist_maxbuf_valid
= 0;
149 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
);
150 jb
->info
.resync_offset
= ts
- now
;
151 jb
->info
.last_delay
= delay
= 0; /* after resync, frame is right on time */
156 jb
->info
.last_delay
= delay
;
157 jb
->info
.cnt_delay_discont
= 0;
161 kicked
= jb
->history
[jb
->hist_ptr
% JB_HISTORY_SZ
];
163 jb
->history
[(jb
->hist_ptr
++) % JB_HISTORY_SZ
] = delay
;
165 /* optimization; the max/min buffers don't need to be recalculated, if this packet's
166 * entry doesn't change them. This happens if this packet is not involved, _and_ any packet
167 * that got kicked out of the history is also not involved
168 * We do a number of comparisons, but it's probably still worthwhile, because it will usually
169 * succeed, and should be a lot faster than going through all 500 packets in history */
170 if (!jb
->hist_maxbuf_valid
)
173 /* don't do this until we've filled history
174 * (reduces some edge cases below) */
175 if (jb
->hist_ptr
< JB_HISTORY_SZ
)
178 /* if the new delay would go into min */
179 if (delay
< jb
->hist_minbuf
[JB_HISTORY_MAXBUF_SZ
-1])
183 if (delay
> jb
->hist_maxbuf
[JB_HISTORY_MAXBUF_SZ
-1])
186 /* or the kicked delay would be in min */
187 if (kicked
<= jb
->hist_minbuf
[JB_HISTORY_MAXBUF_SZ
-1])
190 if (kicked
>= jb
->hist_maxbuf
[JB_HISTORY_MAXBUF_SZ
-1])
193 /* if we got here, we don't need to invalidate, 'cause this delay didn't
196 /* end optimization */
200 jb
->hist_maxbuf_valid
= 0;
204 static void history_calc_maxbuf(jitterbuf
*jb
)
208 if (jb
->hist_ptr
== 0)
212 /* initialize maxbuf/minbuf to the latest value */
213 for (i
=0;i
<JB_HISTORY_MAXBUF_SZ
;i
++) {
215 * jb->hist_maxbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
216 * jb->hist_minbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
218 jb
->hist_maxbuf
[i
] = JB_LONGMIN
;
219 jb
->hist_minbuf
[i
] = JB_LONGMAX
;
222 /* use insertion sort to populate maxbuf */
223 /* we want it to be the top "n" values, in order */
225 /* start at the beginning, or JB_HISTORY_SZ frames ago */
226 i
= (jb
->hist_ptr
> JB_HISTORY_SZ
) ? (jb
->hist_ptr
- JB_HISTORY_SZ
) : 0;
228 for (;i
<jb
->hist_ptr
;i
++) {
229 long toins
= jb
->history
[i
% JB_HISTORY_SZ
];
231 /* if the maxbuf should get this */
232 if (toins
> jb
->hist_maxbuf
[JB_HISTORY_MAXBUF_SZ
-1]) {
234 /* insertion-sort it into the maxbuf */
235 for (j
=0;j
<JB_HISTORY_MAXBUF_SZ
;j
++) {
236 /* found where it fits */
237 if (toins
> jb
->hist_maxbuf
[j
]) {
239 memmove(jb
->hist_maxbuf
+ j
+ 1, jb
->hist_maxbuf
+ j
, (JB_HISTORY_MAXBUF_SZ
- (j
+ 1)) * sizeof(jb
->hist_maxbuf
[0]));
241 jb
->hist_maxbuf
[j
] = toins
;
248 /* if the minbuf should get this */
249 if (toins
< jb
->hist_minbuf
[JB_HISTORY_MAXBUF_SZ
-1]) {
251 /* insertion-sort it into the maxbuf */
252 for (j
=0;j
<JB_HISTORY_MAXBUF_SZ
;j
++) {
253 /* found where it fits */
254 if (toins
< jb
->hist_minbuf
[j
]) {
256 memmove(jb
->hist_minbuf
+ j
+ 1, jb
->hist_minbuf
+ j
, (JB_HISTORY_MAXBUF_SZ
- (j
+ 1)) * sizeof(jb
->hist_minbuf
[0]));
258 jb
->hist_minbuf
[j
] = toins
;
267 fprintf(stderr
, "toins = %ld\n", toins
);
268 fprintf(stderr
, "maxbuf =");
269 for (k
=0;k
<JB_HISTORY_MAXBUF_SZ
;k
++)
270 fprintf(stderr
, "%ld ", jb
->hist_maxbuf
[k
]);
271 fprintf(stderr
, "\nminbuf =");
272 for (k
=0;k
<JB_HISTORY_MAXBUF_SZ
;k
++)
273 fprintf(stderr
, "%ld ", jb
->hist_minbuf
[k
]);
274 fprintf(stderr
, "\n");
278 jb
->hist_maxbuf_valid
= 1;
281 static void history_get(jitterbuf
*jb
)
283 long max
, min
, jitter
;
287 if (!jb
->hist_maxbuf_valid
)
288 history_calc_maxbuf(jb
);
290 /* count is how many items in history we're examining */
291 count
= (jb
->hist_ptr
< JB_HISTORY_SZ
) ? jb
->hist_ptr
: JB_HISTORY_SZ
;
293 /* index is the "n"ths highest/lowest that we'll look for */
294 index
= count
* JB_HISTORY_DROPPCT
/ 100;
296 /* sanity checks for index */
297 if (index
> (JB_HISTORY_MAXBUF_SZ
- 1))
298 index
= JB_HISTORY_MAXBUF_SZ
- 1;
307 max
= jb
->hist_maxbuf
[index
];
308 min
= jb
->hist_minbuf
[index
];
312 /* these debug stmts compare the difference between looking at the absolute jitter, and the
313 * values we get by throwing away the outliers */
315 fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", index, min, max, jitter);
316 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]);
320 jb
->info
.jitter
= jitter
;
323 /* returns 1 if frame was inserted into head of queue, 0 otherwise */
324 static int queue_put(jitterbuf
*jb
, void *data
, const enum jb_frame_type type
, long ms
, long ts
)
329 long resync_ts
= ts
- jb
->info
.resync_offset
;
331 if ((frame
= jb
->free
)) {
332 jb
->free
= frame
->next
;
333 } else if (!(frame
= ast_malloc(sizeof(*frame
)))) {
334 jb_err("cannot allocate frame\n");
338 jb
->info
.frames_cur
++;
341 frame
->ts
= resync_ts
;
346 * frames are a circular list, jb-frames points to to the lowest ts,
347 * jb->frames->prev points to the highest ts
350 if (!jb
->frames
) { /* queue is empty */
355 } else if (resync_ts
< jb
->frames
->ts
) {
356 frame
->next
= jb
->frames
;
357 frame
->prev
= jb
->frames
->prev
;
359 frame
->next
->prev
= frame
;
360 frame
->prev
->next
= frame
;
362 /* frame is out of order */
363 jb
->info
.frames_ooo
++;
370 /* frame is out of order */
371 if (resync_ts
< p
->prev
->ts
) jb
->info
.frames_ooo
++;
373 while (resync_ts
< p
->prev
->ts
&& p
->prev
!= jb
->frames
)
377 frame
->prev
= p
->prev
;
379 frame
->next
->prev
= frame
;
380 frame
->prev
->next
= frame
;
385 static long queue_next(jitterbuf
*jb
)
388 return jb
->frames
->ts
;
393 static long queue_last(jitterbuf
*jb
)
396 return jb
->frames
->prev
->ts
;
401 static jb_frame
*_queue_get(jitterbuf
*jb
, long ts
, int all
)
409 /*jb_warn("queue_get: ASK %ld FIRST %ld\n", ts, frame->ts); */
411 if (all
|| ts
>= frame
->ts
) {
412 /* remove this frame */
413 frame
->prev
->next
= frame
->next
;
414 frame
->next
->prev
= frame
->prev
;
416 if (frame
->next
== frame
)
419 jb
->frames
= frame
->next
;
422 /* insert onto "free" single-linked list */
423 frame
->next
= jb
->free
;
426 jb
->info
.frames_cur
--;
428 /* we return the frame pointer, even though it's on free list,
429 * but caller must copy data */
436 static jb_frame
*queue_get(jitterbuf
*jb
, long ts
)
438 return _queue_get(jb
,ts
,0);
441 static jb_frame
*queue_getall(jitterbuf
*jb
)
443 return _queue_get(jb
,0,1);
447 /* some diagnostics */
448 static void jb_dbginfo(jitterbuf
*jb
)
453 jb_dbg("\njb info: fin=%ld fout=%ld flate=%ld flost=%ld fdrop=%ld fcur=%ld\n",
454 jb
->info
.frames_in
, jb
->info
.frames_out
, jb
->info
.frames_late
, jb
->info
.frames_lost
, jb
->info
.frames_dropped
, jb
->info
.frames_cur
);
456 jb_dbg("jitter=%ld current=%ld target=%ld min=%ld sil=%d len=%d len/fcur=%ld\n",
457 jb
->info
.jitter
, jb
->info
.current
, jb
->info
.target
, jb
->info
.min
, jb
->info
.silence_begin_ts
, jb
->info
.current
- jb
->info
.min
,
458 jb
->info
.frames_cur
? (jb
->info
.current
- jb
->info
.min
)/jb
->info
.frames_cur
: -8);
459 if (jb
->info
.frames_in
> 0)
460 jb_dbg("jb info: Loss PCT = %ld%%, Late PCT = %ld%%\n",
461 jb
->info
.frames_lost
* 100/(jb
->info
.frames_in
+ jb
->info
.frames_lost
),
462 jb
->info
.frames_late
* 100/jb
->info
.frames_in
);
463 jb_dbg("jb info: queue %d -> %d. last_ts %d (queue len: %d) last_ms %d\n",
466 jb
->info
.next_voice_ts
,
467 queue_last(jb
) - queue_next(jb
),
468 jb
->info
.last_voice_ms
);
473 static void jb_chkqueue(jitterbuf
*jb
)
476 jb_frame
*p
= jb
->frames
;
483 if (p
->next
== NULL
) {
484 jb_err("Queue is BROKEN at item [%d]", i
);
488 } while (p
->next
!= jb
->frames
);
491 static void jb_dbgqueue(jitterbuf
*jb
)
494 jb_frame
*p
= jb
->frames
;
504 jb_dbg("[%d]=%ld ", i
++, p
->ts
);
506 } while (p
->next
!= jb
->frames
);
512 enum jb_return_code
jb_put(jitterbuf
*jb
, void *data
, const enum jb_frame_type type
, long ms
, long ts
, long now
)
514 jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb
, data
, ms
, ts
, now
);
516 jb
->info
.frames_in
++;
518 if (type
== JB_TYPE_VOICE
) {
519 /* presently, I'm only adding VOICE frames to history and drift calculations; mostly because with the
520 * IAX integrations, I'm sending retransmitted control frames with their awkward timestamps through */
521 if (history_put(jb
,ts
,now
,ms
))
525 /* if put into head of queue, caller needs to reschedule */
526 if (queue_put(jb
,data
,type
,ms
,ts
)) {
533 static enum jb_return_code
_jb_get(jitterbuf
*jb
, jb_frame
*frameout
, long now
, long interpl
)
537 static int dbg_cnt
= 0;
539 /*if ((now - jb_next(jb)) > 2 * jb->info.last_voice_ms) jb_warn("SCHED: %ld", (now - jb_next(jb))); */
540 /* get jitter info */
543 if (dbg_cnt
&& dbg_cnt
% 50 == 0) {
549 jb
->info
.target
= jb
->info
.jitter
+ jb
->info
.min
+ JB_TARGET_EXTRA
;
551 /* if a hard clamp was requested, use it */
552 if ((jb
->info
.conf
.max_jitterbuf
) && ((jb
->info
.target
- jb
->info
.min
) > jb
->info
.conf
.max_jitterbuf
)) {
553 jb_dbg("clamping target from %d to %d\n", (jb
->info
.target
- jb
->info
.min
), jb
->info
.conf
.max_jitterbuf
);
554 jb
->info
.target
= jb
->info
.min
+ jb
->info
.conf
.max_jitterbuf
;
557 diff
= jb
->info
.target
- jb
->info
.current
;
559 /* jb_warn("diff = %d lms=%d last = %d now = %d\n", diff, */
560 /* jb->info.last_voice_ms, jb->info.last_adjustment, now); */
562 /* let's work on non-silent case first */
563 if (!jb
->info
.silence_begin_ts
) {
564 /* we want to grow */
566 /* we haven't grown in the delay length */
567 (((jb
->info
.last_adjustment
+ JB_ADJUST_DELAY
) < now
) ||
568 /* we need to grow more than the "length" we have left */
569 (diff
> queue_last(jb
) - queue_next(jb
)) ) ) {
570 /* grow by interp frame length */
571 jb
->info
.current
+= interpl
;
572 jb
->info
.next_voice_ts
+= interpl
;
573 jb
->info
.last_voice_ms
= interpl
;
574 jb
->info
.last_adjustment
= now
;
575 jb
->info
.cnt_contig_interp
++;
576 if (jb
->info
.conf
.max_contig_interp
&& jb
->info
.cnt_contig_interp
>= jb
->info
.conf
.max_contig_interp
) {
577 jb
->info
.silence_begin_ts
= jb
->info
.next_voice_ts
- jb
->info
.current
;
583 frame
= queue_get(jb
, jb
->info
.next_voice_ts
- jb
->info
.current
);
585 /* not a voice frame; just return it. */
586 if (frame
&& frame
->type
!= JB_TYPE_VOICE
) {
587 if (frame
->type
== JB_TYPE_SILENCE
) {
588 jb
->info
.silence_begin_ts
= frame
->ts
;
589 jb
->info
.cnt_contig_interp
= 0;
593 jb
->info
.frames_out
++;
599 /* voice frame is later than expected */
600 if (frame
&& frame
->ts
+ jb
->info
.current
< jb
->info
.next_voice_ts
) {
601 if (frame
->ts
+ jb
->info
.current
> jb
->info
.next_voice_ts
- jb
->info
.last_voice_ms
) {
602 /* either we interpolated past this frame in the last jb_get */
603 /* or the frame is still in order, but came a little too quick */
605 /* reset expectation for next frame */
606 jb
->info
.next_voice_ts
= frame
->ts
+ jb
->info
.current
+ frame
->ms
;
607 jb
->info
.frames_out
++;
608 decrement_losspct(jb
);
609 jb
->info
.cnt_contig_interp
= 0;
613 /* voice frame is late */
615 jb
->info
.frames_out
++;
616 decrement_losspct(jb
);
617 jb
->info
.frames_late
++;
618 jb
->info
.frames_lost
--;
620 /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
626 /* keep track of frame sizes, to allow for variable sized-frames */
627 if (frame
&& frame
->ms
> 0) {
628 jb
->info
.last_voice_ms
= frame
->ms
;
631 /* we want to shrink; shrink at 1 frame / 500ms */
632 /* unless we don't have a frame, then shrink 1 frame */
633 /* every 80ms (though perhaps we can shrink even faster */
635 if (diff
< -JB_TARGET_EXTRA
&&
636 ((!frame
&& jb
->info
.last_adjustment
+ 80 < now
) ||
637 (jb
->info
.last_adjustment
+ 500 < now
))) {
639 jb
->info
.last_adjustment
= now
;
640 jb
->info
.cnt_contig_interp
= 0;
644 /* shrink by frame size we're throwing out */
645 jb
->info
.current
-= frame
->ms
;
646 jb
->info
.frames_out
++;
647 decrement_losspct(jb
);
648 jb
->info
.frames_dropped
++;
652 /* shrink by last_voice_ms */
653 jb
->info
.current
-= jb
->info
.last_voice_ms
;
654 jb
->info
.frames_lost
++;
655 increment_losspct(jb
);
663 /* this is a bit of a hack for now, but if we're close to
664 * target, and we find a missing frame, it makes sense to
665 * grow, because the frame might just be a bit late;
666 * otherwise, we presently get into a pattern where we return
667 * INTERP for the lost frame, then it shows up next, and we
668 * throw it away because it's late */
669 /* I've recently only been able to replicate this using
670 * iaxclient talking to app_echo on asterisk. In this case,
671 * my outgoing packets go through asterisk's (old)
672 * jitterbuffer, and then might get an unusual increasing delay
673 * there if it decides to grow?? */
674 /* Update: that might have been a different bug, that has been fixed..
675 * But, this still seemed like a good idea, except that it ended up making a single actual
676 * lost frame get interpolated two or more times, when there was "room" to grow, so it might
677 * be a bit of a bad idea overall */
678 /*if (diff > -1 * jb->info.last_voice_ms) {
679 jb->info.current += jb->info.last_voice_ms;
680 jb->info.last_adjustment = now;
684 jb
->info
.frames_lost
++;
685 increment_losspct(jb
);
686 jb
->info
.next_voice_ts
+= interpl
;
687 jb
->info
.last_voice_ms
= interpl
;
688 jb
->info
.cnt_contig_interp
++;
689 if (jb
->info
.conf
.max_contig_interp
&& jb
->info
.cnt_contig_interp
>= jb
->info
.conf
.max_contig_interp
) {
690 jb
->info
.silence_begin_ts
= jb
->info
.next_voice_ts
- jb
->info
.current
;
696 /* normal case; return the frame, increment stuff */
698 jb
->info
.next_voice_ts
+= frame
->ms
;
699 jb
->info
.frames_out
++;
700 jb
->info
.cnt_contig_interp
= 0;
701 decrement_losspct(jb
);
705 /* TODO: after we get the non-silent case down, we'll make the
706 * silent case -- basically, we'll just grow and shrink faster
707 * here, plus handle next_voice_ts a bit differently */
709 /* to disable silent special case altogether, just uncomment this: */
710 /* jb->info.silence_begin_ts = 0; */
712 /* shrink interpl len every 10ms during silence */
713 if (diff
< -JB_TARGET_EXTRA
&&
714 jb
->info
.last_adjustment
+ 10 <= now
) {
715 jb
->info
.current
-= interpl
;
716 jb
->info
.last_adjustment
= now
;
719 frame
= queue_get(jb
, now
- jb
->info
.current
);
722 } else if (frame
->type
!= JB_TYPE_VOICE
) {
723 /* normal case; in silent mode, got a non-voice frame */
725 jb
->info
.frames_out
++;
728 if (frame
->ts
< jb
->info
.silence_begin_ts
) {
729 /* voice frame is late */
731 jb
->info
.frames_out
++;
732 decrement_losspct(jb
);
733 jb
->info
.frames_late
++;
734 jb
->info
.frames_lost
--;
736 /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
741 /* try setting current to target right away here */
742 jb
->info
.current
= jb
->info
.target
;
743 jb
->info
.silence_begin_ts
= 0;
744 jb
->info
.next_voice_ts
= frame
->ts
+ jb
->info
.current
+ frame
->ms
;
745 jb
->info
.last_voice_ms
= frame
->ms
;
746 jb
->info
.frames_out
++;
747 decrement_losspct(jb
);
755 long jb_next(jitterbuf
*jb
)
757 if (jb
->info
.silence_begin_ts
) {
758 long next
= queue_next(jb
);
761 /* shrink during silence */
762 if (jb
->info
.target
- jb
->info
.current
< -JB_TARGET_EXTRA
)
763 return jb
->info
.last_adjustment
+ 10;
764 return next
+ jb
->info
.target
;
769 return jb
->info
.next_voice_ts
;
773 enum jb_return_code
jb_get(jitterbuf
*jb
, jb_frame
*frameout
, long now
, long interpl
)
775 enum jb_return_code ret
= _jb_get(jb
, frameout
, now
, interpl
);
778 int thists
= ((ret
== JB_OK
) || (ret
== JB_DROP
)) ? frameout
->ts
: 0;
779 jb_warn("jb_get(%x,%x,%ld) = %d (%d)\n", jb
, frameout
, now
, ret
, thists
);
780 if (thists
&& thists
< lastts
) jb_warn("XXXX timestamp roll-back!!!\n");
784 frameout
->ms
= jb
->info
.last_voice_ms
;
789 enum jb_return_code
jb_getall(jitterbuf
*jb
, jb_frame
*frameout
)
792 frame
= queue_getall(jb
);
803 enum jb_return_code
jb_getinfo(jitterbuf
*jb
, jb_info
*stats
)
813 enum jb_return_code
jb_setconf(jitterbuf
*jb
, jb_conf
*conf
)
815 /* take selected settings from the struct */
817 jb
->info
.conf
.max_jitterbuf
= conf
->max_jitterbuf
;
818 jb
->info
.conf
.resync_threshold
= conf
->resync_threshold
;
819 jb
->info
.conf
.max_contig_interp
= conf
->max_contig_interp
;