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
)
517 jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb
, data
, ms
, ts
, now
);
519 jb
->info
.frames_in
++;
521 if (jb
->frames
&& jb
->dropem
)
525 if (type
== JB_TYPE_VOICE
) {
526 /* presently, I'm only adding VOICE frames to history and drift calculations; mostly because with the
527 * IAX integrations, I'm sending retransmitted control frames with their awkward timestamps through */
528 if (history_put(jb
,ts
,now
,ms
))
533 numts
= jb
->frames
->prev
->ts
- jb
->frames
->ts
;
534 if (numts
>= jb
->info
.conf
.max_jitterbuf
) {
535 ast_log(LOG_DEBUG
, "Attempting to exceed Jitterbuf max %ld timeslots\n",
536 jb
->info
.conf
.max_jitterbuf
);
540 /* if put into head of queue, caller needs to reschedule */
541 if (queue_put(jb
,data
,type
,ms
,ts
)) {
548 static enum jb_return_code
_jb_get(jitterbuf
*jb
, jb_frame
*frameout
, long now
, long interpl
)
552 static int dbg_cnt
= 0;
554 /*if ((now - jb_next(jb)) > 2 * jb->info.last_voice_ms) jb_warn("SCHED: %ld", (now - jb_next(jb))); */
555 /* get jitter info */
558 if (dbg_cnt
&& dbg_cnt
% 50 == 0) {
564 jb
->info
.target
= jb
->info
.jitter
+ jb
->info
.min
+ JB_TARGET_EXTRA
;
566 /* if a hard clamp was requested, use it */
567 if ((jb
->info
.conf
.max_jitterbuf
) && ((jb
->info
.target
- jb
->info
.min
) > jb
->info
.conf
.max_jitterbuf
)) {
568 jb_dbg("clamping target from %d to %d\n", (jb
->info
.target
- jb
->info
.min
), jb
->info
.conf
.max_jitterbuf
);
569 jb
->info
.target
= jb
->info
.min
+ jb
->info
.conf
.max_jitterbuf
;
572 diff
= jb
->info
.target
- jb
->info
.current
;
574 /* jb_warn("diff = %d lms=%d last = %d now = %d\n", diff, */
575 /* jb->info.last_voice_ms, jb->info.last_adjustment, now); */
577 /* let's work on non-silent case first */
578 if (!jb
->info
.silence_begin_ts
) {
579 /* we want to grow */
581 /* we haven't grown in the delay length */
582 (((jb
->info
.last_adjustment
+ JB_ADJUST_DELAY
) < now
) ||
583 /* we need to grow more than the "length" we have left */
584 (diff
> queue_last(jb
) - queue_next(jb
)) ) ) {
585 /* grow by interp frame length */
586 jb
->info
.current
+= interpl
;
587 jb
->info
.next_voice_ts
+= interpl
;
588 jb
->info
.last_voice_ms
= interpl
;
589 jb
->info
.last_adjustment
= now
;
590 jb
->info
.cnt_contig_interp
++;
591 if (jb
->info
.conf
.max_contig_interp
&& jb
->info
.cnt_contig_interp
>= jb
->info
.conf
.max_contig_interp
) {
592 jb
->info
.silence_begin_ts
= jb
->info
.next_voice_ts
- jb
->info
.current
;
598 frame
= queue_get(jb
, jb
->info
.next_voice_ts
- jb
->info
.current
);
600 /* not a voice frame; just return it. */
601 if (frame
&& frame
->type
!= JB_TYPE_VOICE
) {
602 if (frame
->type
== JB_TYPE_SILENCE
) {
603 jb
->info
.silence_begin_ts
= frame
->ts
;
604 jb
->info
.cnt_contig_interp
= 0;
608 jb
->info
.frames_out
++;
614 /* voice frame is later than expected */
615 if (frame
&& frame
->ts
+ jb
->info
.current
< jb
->info
.next_voice_ts
) {
616 if (frame
->ts
+ jb
->info
.current
> jb
->info
.next_voice_ts
- jb
->info
.last_voice_ms
) {
617 /* either we interpolated past this frame in the last jb_get */
618 /* or the frame is still in order, but came a little too quick */
620 /* reset expectation for next frame */
621 jb
->info
.next_voice_ts
= frame
->ts
+ jb
->info
.current
+ frame
->ms
;
622 jb
->info
.frames_out
++;
623 decrement_losspct(jb
);
624 jb
->info
.cnt_contig_interp
= 0;
628 /* voice frame is late */
630 jb
->info
.frames_out
++;
631 decrement_losspct(jb
);
632 jb
->info
.frames_late
++;
633 jb
->info
.frames_lost
--;
635 /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
641 /* keep track of frame sizes, to allow for variable sized-frames */
642 if (frame
&& frame
->ms
> 0) {
643 jb
->info
.last_voice_ms
= frame
->ms
;
646 /* we want to shrink; shrink at 1 frame / 500ms */
647 /* unless we don't have a frame, then shrink 1 frame */
648 /* every 80ms (though perhaps we can shrink even faster */
650 if (diff
< -JB_TARGET_EXTRA
&&
651 ((!frame
&& jb
->info
.last_adjustment
+ 80 < now
) ||
652 (jb
->info
.last_adjustment
+ 500 < now
))) {
654 jb
->info
.last_adjustment
= now
;
655 jb
->info
.cnt_contig_interp
= 0;
659 /* shrink by frame size we're throwing out */
660 jb
->info
.current
-= frame
->ms
;
661 jb
->info
.frames_out
++;
662 decrement_losspct(jb
);
663 jb
->info
.frames_dropped
++;
667 /* shrink by last_voice_ms */
668 jb
->info
.current
-= jb
->info
.last_voice_ms
;
669 jb
->info
.frames_lost
++;
670 increment_losspct(jb
);
678 /* this is a bit of a hack for now, but if we're close to
679 * target, and we find a missing frame, it makes sense to
680 * grow, because the frame might just be a bit late;
681 * otherwise, we presently get into a pattern where we return
682 * INTERP for the lost frame, then it shows up next, and we
683 * throw it away because it's late */
684 /* I've recently only been able to replicate this using
685 * iaxclient talking to app_echo on asterisk. In this case,
686 * my outgoing packets go through asterisk's (old)
687 * jitterbuffer, and then might get an unusual increasing delay
688 * there if it decides to grow?? */
689 /* Update: that might have been a different bug, that has been fixed..
690 * But, this still seemed like a good idea, except that it ended up making a single actual
691 * lost frame get interpolated two or more times, when there was "room" to grow, so it might
692 * be a bit of a bad idea overall */
693 /*if (diff > -1 * jb->info.last_voice_ms) {
694 jb->info.current += jb->info.last_voice_ms;
695 jb->info.last_adjustment = now;
699 jb
->info
.frames_lost
++;
700 increment_losspct(jb
);
701 jb
->info
.next_voice_ts
+= interpl
;
702 jb
->info
.last_voice_ms
= interpl
;
703 jb
->info
.cnt_contig_interp
++;
704 if (jb
->info
.conf
.max_contig_interp
&& jb
->info
.cnt_contig_interp
>= jb
->info
.conf
.max_contig_interp
) {
705 jb
->info
.silence_begin_ts
= jb
->info
.next_voice_ts
- jb
->info
.current
;
711 /* normal case; return the frame, increment stuff */
713 jb
->info
.next_voice_ts
+= frame
->ms
;
714 jb
->info
.frames_out
++;
715 jb
->info
.cnt_contig_interp
= 0;
716 decrement_losspct(jb
);
720 /* TODO: after we get the non-silent case down, we'll make the
721 * silent case -- basically, we'll just grow and shrink faster
722 * here, plus handle next_voice_ts a bit differently */
724 /* to disable silent special case altogether, just uncomment this: */
725 /* jb->info.silence_begin_ts = 0; */
727 /* shrink interpl len every 10ms during silence */
728 if (diff
< -JB_TARGET_EXTRA
&&
729 jb
->info
.last_adjustment
+ 10 <= now
) {
730 jb
->info
.current
-= interpl
;
731 jb
->info
.last_adjustment
= now
;
734 frame
= queue_get(jb
, now
- jb
->info
.current
);
737 } else if (frame
->type
!= JB_TYPE_VOICE
) {
738 /* normal case; in silent mode, got a non-voice frame */
740 jb
->info
.frames_out
++;
743 if (frame
->ts
< jb
->info
.silence_begin_ts
) {
744 /* voice frame is late */
746 jb
->info
.frames_out
++;
747 decrement_losspct(jb
);
748 jb
->info
.frames_late
++;
749 jb
->info
.frames_lost
--;
751 /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
756 /* try setting current to target right away here */
757 jb
->info
.current
= jb
->info
.target
;
758 jb
->info
.silence_begin_ts
= 0;
759 jb
->info
.next_voice_ts
= frame
->ts
+ jb
->info
.current
+ frame
->ms
;
760 jb
->info
.last_voice_ms
= frame
->ms
;
761 jb
->info
.frames_out
++;
762 decrement_losspct(jb
);
770 long jb_next(jitterbuf
*jb
)
772 if (jb
->info
.silence_begin_ts
) {
774 long next
= queue_next(jb
);
776 /* shrink during silence */
777 if (jb
->info
.target
- jb
->info
.current
< -JB_TARGET_EXTRA
)
778 return jb
->info
.last_adjustment
+ 10;
779 return next
+ jb
->info
.target
;
784 return jb
->info
.next_voice_ts
;
788 enum jb_return_code
jb_get(jitterbuf
*jb
, jb_frame
*frameout
, long now
, long interpl
)
790 enum jb_return_code ret
= _jb_get(jb
, frameout
, now
, interpl
);
793 int thists
= ((ret
== JB_OK
) || (ret
== JB_DROP
)) ? frameout
->ts
: 0;
794 jb_warn("jb_get(%x,%x,%ld) = %d (%d)\n", jb
, frameout
, now
, ret
, thists
);
795 if (thists
&& thists
< lastts
) jb_warn("XXXX timestamp roll-back!!!\n");
799 frameout
->ms
= jb
->info
.last_voice_ms
;
804 enum jb_return_code
jb_getall(jitterbuf
*jb
, jb_frame
*frameout
)
807 frame
= queue_getall(jb
);
818 enum jb_return_code
jb_getinfo(jitterbuf
*jb
, jb_info
*stats
)
828 enum jb_return_code
jb_setconf(jitterbuf
*jb
, jb_conf
*conf
)
830 /* take selected settings from the struct */
832 jb
->info
.conf
.max_jitterbuf
= conf
->max_jitterbuf
;
833 jb
->info
.conf
.resync_threshold
= conf
->resync_threshold
;
834 jb
->info
.conf
.max_contig_interp
= conf
->max_contig_interp
;