00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "asterisk.h"
00035
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 401786 $")
00037
00038 #include "jitterbuf.h"
00039 #include "asterisk/utils.h"
00040
00041
00042 #define JB_LONGMAX 2147483647L
00043 #define JB_LONGMIN (-JB_LONGMAX - 1L)
00044
00045 #define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0)
00046 #define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0)
00047 #define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
00048
00049 #ifdef DEEP_DEBUG
00050 #define jb_dbg2(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
00051 #else
00052 #define jb_dbg2(...) ((void)0)
00053 #endif
00054
00055 static jb_output_function_t warnf, errf, dbgf;
00056
00057 void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg)
00058 {
00059 errf = err;
00060 warnf = warn;
00061 dbgf = dbg;
00062 }
00063
00064 static void increment_losspct(jitterbuf *jb)
00065 {
00066 jb->info.losspct = (100000 + 499 * jb->info.losspct)/500;
00067 }
00068
00069 static void decrement_losspct(jitterbuf *jb)
00070 {
00071 jb->info.losspct = (499 * jb->info.losspct)/500;
00072 }
00073
00074 void jb_reset(jitterbuf *jb)
00075 {
00076
00077 jb_conf s = jb->info.conf;
00078 jb_frame *fr = jb->free;
00079 memset(jb, 0, sizeof(*jb));
00080 jb->info.conf = s;
00081 jb->free = fr;
00082
00083
00084 jb->info.current = jb->info.target = jb->info.conf.target_extra = JB_TARGET_EXTRA;
00085 jb->info.silence_begin_ts = -1;
00086 }
00087
00088 jitterbuf * jb_new()
00089 {
00090 jitterbuf *jb;
00091
00092 if (!(jb = ast_calloc(1, sizeof(*jb))))
00093 return NULL;
00094
00095 jb_reset(jb);
00096
00097 jb_dbg2("jb_new() = %x\n", jb);
00098 return jb;
00099 }
00100
00101 void jb_destroy(jitterbuf *jb)
00102 {
00103 jb_frame *frame;
00104 jb_dbg2("jb_destroy(%x)\n", jb);
00105
00106
00107 frame = jb->free;
00108 while (frame != NULL) {
00109 jb_frame *next = frame->next;
00110 ast_free(frame);
00111 frame = next;
00112 }
00113
00114
00115 ast_free(jb);
00116 }
00117
00118 static int check_resync(jitterbuf *jb, long ts, long now, long ms, const enum jb_frame_type type, long *delay)
00119 {
00120 long numts = 0;
00121 long threshold = 2 * jb->info.jitter + jb->info.conf.resync_threshold;
00122
00123
00124 if (jb->frames) {
00125 numts = jb->frames->prev->ts - jb->frames->ts;
00126 }
00127
00128 if (numts >= (jb->info.conf.max_jitterbuf)) {
00129 if (!jb->dropem) {
00130 ast_debug(1, "Attempting to exceed Jitterbuf max %ld timeslots\n",
00131 jb->info.conf.max_jitterbuf);
00132 jb->dropem = 1;
00133 }
00134 jb->info.frames_dropped++;
00135 return -1;
00136 } else {
00137 jb->dropem = 0;
00138 }
00139
00140
00141 if (jb->info.conf.resync_threshold != -1) {
00142 if (abs(*delay - jb->info.last_delay) > threshold) {
00143 jb->info.cnt_delay_discont++;
00144
00145
00146 if ((jb->info.cnt_delay_discont > 3) || (type == JB_TYPE_CONTROL)) {
00147 jb->info.cnt_delay_discont = 0;
00148 jb->hist_ptr = 0;
00149 jb->hist_maxbuf_valid = 0;
00150 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);
00151 jb->info.resync_offset = ts - now;
00152 jb->info.last_delay = *delay = 0;
00153 } else {
00154 jb->info.frames_dropped++;
00155 return -1;
00156 }
00157 } else {
00158 jb->info.last_delay = *delay;
00159 jb->info.cnt_delay_discont = 0;
00160 }
00161 }
00162 return 0;
00163 }
00164
00165 static int history_put(jitterbuf *jb, long ts, long now, long ms, long delay)
00166 {
00167 long kicked;
00168
00169
00170 if (ts <= 0)
00171 return 0;
00172
00173 kicked = jb->history[jb->hist_ptr % JB_HISTORY_SZ];
00174
00175 jb->history[(jb->hist_ptr++) % JB_HISTORY_SZ] = delay;
00176
00177
00178
00179
00180
00181
00182 if (!jb->hist_maxbuf_valid)
00183 return 0;
00184
00185
00186
00187 if (jb->hist_ptr < JB_HISTORY_SZ)
00188 goto invalidate;
00189
00190
00191 if (delay < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])
00192 goto invalidate;
00193
00194
00195 if (delay > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])
00196 goto invalidate;
00197
00198
00199 if (kicked <= jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])
00200 goto invalidate;
00201
00202 if (kicked >= jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])
00203 goto invalidate;
00204
00205
00206
00207 return 0;
00208
00209
00210
00211 invalidate:
00212 jb->hist_maxbuf_valid = 0;
00213 return 0;
00214 }
00215
00216 static void history_calc_maxbuf(jitterbuf *jb)
00217 {
00218 int i,j;
00219
00220 if (jb->hist_ptr == 0)
00221 return;
00222
00223
00224
00225 for (i=0;i<JB_HISTORY_MAXBUF_SZ;i++) {
00226
00227
00228
00229
00230 jb->hist_maxbuf[i] = JB_LONGMIN;
00231 jb->hist_minbuf[i] = JB_LONGMAX;
00232 }
00233
00234
00235
00236
00237
00238 i = (jb->hist_ptr > JB_HISTORY_SZ) ? (jb->hist_ptr - JB_HISTORY_SZ) : 0;
00239
00240 for (;i<jb->hist_ptr;i++) {
00241 long toins = jb->history[i % JB_HISTORY_SZ];
00242
00243
00244 if (toins > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1]) {
00245
00246
00247 for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
00248
00249 if (toins > jb->hist_maxbuf[j]) {
00250
00251 if (j != JB_HISTORY_MAXBUF_SZ - 1) {
00252 memmove(jb->hist_maxbuf + j + 1, jb->hist_maxbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_maxbuf[0]));
00253 }
00254
00255 jb->hist_maxbuf[j] = toins;
00256
00257 break;
00258 }
00259 }
00260 }
00261
00262
00263 if (toins < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1]) {
00264
00265
00266 for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
00267
00268 if (toins < jb->hist_minbuf[j]) {
00269
00270 if (j != JB_HISTORY_MAXBUF_SZ - 1) {
00271 memmove(jb->hist_minbuf + j + 1, jb->hist_minbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_minbuf[0]));
00272 }
00273
00274 jb->hist_minbuf[j] = toins;
00275
00276 break;
00277 }
00278 }
00279 }
00280
00281 if (0) {
00282 int k;
00283 fprintf(stderr, "toins = %ld\n", toins);
00284 fprintf(stderr, "maxbuf =");
00285 for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++)
00286 fprintf(stderr, "%ld ", jb->hist_maxbuf[k]);
00287 fprintf(stderr, "\nminbuf =");
00288 for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++)
00289 fprintf(stderr, "%ld ", jb->hist_minbuf[k]);
00290 fprintf(stderr, "\n");
00291 }
00292 }
00293
00294 jb->hist_maxbuf_valid = 1;
00295 }
00296
00297 static void history_get(jitterbuf *jb)
00298 {
00299 long max, min, jitter;
00300 int idx;
00301 int count;
00302
00303 if (!jb->hist_maxbuf_valid)
00304 history_calc_maxbuf(jb);
00305
00306
00307 count = (jb->hist_ptr < JB_HISTORY_SZ) ? jb->hist_ptr : JB_HISTORY_SZ;
00308
00309
00310 idx = count * JB_HISTORY_DROPPCT / 100;
00311
00312
00313 if (idx > (JB_HISTORY_MAXBUF_SZ - 1))
00314 idx = JB_HISTORY_MAXBUF_SZ - 1;
00315
00316 if (idx < 0) {
00317 jb->info.min = 0;
00318 jb->info.jitter = 0;
00319 return;
00320 }
00321
00322 max = jb->hist_maxbuf[idx];
00323 min = jb->hist_minbuf[idx];
00324
00325 jitter = max - min;
00326
00327
00328
00329
00330
00331
00332
00333
00334 jb->info.min = min;
00335 jb->info.jitter = jitter;
00336 }
00337
00338
00339 static int queue_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts)
00340 {
00341 jb_frame *frame;
00342 jb_frame *p;
00343 int head = 0;
00344 long resync_ts = ts - jb->info.resync_offset;
00345
00346 if ((frame = jb->free)) {
00347 jb->free = frame->next;
00348 } else if (!(frame = ast_malloc(sizeof(*frame)))) {
00349 jb_err("cannot allocate frame\n");
00350 return 0;
00351 }
00352
00353 jb->info.frames_cur++;
00354
00355 frame->data = data;
00356 frame->ts = resync_ts;
00357 frame->ms = ms;
00358 frame->type = type;
00359
00360
00361
00362
00363
00364
00365 if (!jb->frames) {
00366 jb->frames = frame;
00367 frame->next = frame;
00368 frame->prev = frame;
00369 head = 1;
00370 } else if (resync_ts < jb->frames->ts) {
00371 frame->next = jb->frames;
00372 frame->prev = jb->frames->prev;
00373
00374 frame->next->prev = frame;
00375 frame->prev->next = frame;
00376
00377
00378 jb->info.frames_ooo++;
00379
00380 jb->frames = frame;
00381 head = 1;
00382 } else {
00383 p = jb->frames;
00384
00385
00386 if (resync_ts < p->prev->ts) jb->info.frames_ooo++;
00387
00388 while (resync_ts < p->prev->ts && p->prev != jb->frames)
00389 p = p->prev;
00390
00391 frame->next = p;
00392 frame->prev = p->prev;
00393
00394 frame->next->prev = frame;
00395 frame->prev->next = frame;
00396 }
00397 return head;
00398 }
00399
00400 static long queue_next(jitterbuf *jb)
00401 {
00402 if (jb->frames)
00403 return jb->frames->ts;
00404 else
00405 return -1;
00406 }
00407
00408 static long queue_last(jitterbuf *jb)
00409 {
00410 if (jb->frames)
00411 return jb->frames->prev->ts;
00412 else
00413 return -1;
00414 }
00415
00416 static jb_frame *_queue_get(jitterbuf *jb, long ts, int all)
00417 {
00418 jb_frame *frame;
00419 frame = jb->frames;
00420
00421 if (!frame)
00422 return NULL;
00423
00424
00425
00426 if (all || ts >= frame->ts) {
00427
00428 frame->prev->next = frame->next;
00429 frame->next->prev = frame->prev;
00430
00431 if (frame->next == frame)
00432 jb->frames = NULL;
00433 else
00434 jb->frames = frame->next;
00435
00436
00437
00438 frame->next = jb->free;
00439 jb->free = frame;
00440
00441 jb->info.frames_cur--;
00442
00443
00444
00445 return frame;
00446 }
00447
00448 return NULL;
00449 }
00450
00451 static jb_frame *queue_get(jitterbuf *jb, long ts)
00452 {
00453 return _queue_get(jb,ts,0);
00454 }
00455
00456 static jb_frame *queue_getall(jitterbuf *jb)
00457 {
00458 return _queue_get(jb,0,1);
00459 }
00460
00461 #if 0
00462
00463 static void jb_dbginfo(jitterbuf *jb)
00464 {
00465 if (dbgf == NULL)
00466 return;
00467
00468 jb_dbg("\njb info: fin=%ld fout=%ld flate=%ld flost=%ld fdrop=%ld fcur=%ld\n",
00469 jb->info.frames_in, jb->info.frames_out, jb->info.frames_late, jb->info.frames_lost, jb->info.frames_dropped, jb->info.frames_cur);
00470
00471 jb_dbg("jitter=%ld current=%ld target=%ld min=%ld sil=%d len=%d len/fcur=%ld\n",
00472 jb->info.jitter, jb->info.current, jb->info.target, jb->info.min, jb->info.silence_begin_ts, jb->info.current - jb->info.min,
00473 jb->info.frames_cur ? (jb->info.current - jb->info.min)/jb->info.frames_cur : -8);
00474 if (jb->info.frames_in > 0)
00475 jb_dbg("jb info: Loss PCT = %ld%%, Late PCT = %ld%%\n",
00476 jb->info.frames_lost * 100/(jb->info.frames_in + jb->info.frames_lost),
00477 jb->info.frames_late * 100/jb->info.frames_in);
00478 jb_dbg("jb info: queue %d -> %d. last_ts %d (queue len: %d) last_ms %d\n",
00479 queue_next(jb),
00480 queue_last(jb),
00481 jb->info.next_voice_ts,
00482 queue_last(jb) - queue_next(jb),
00483 jb->info.last_voice_ms);
00484 }
00485 #endif
00486
00487 #ifdef DEEP_DEBUG
00488 static void jb_chkqueue(jitterbuf *jb)
00489 {
00490 int i=0;
00491 jb_frame *p = jb->frames;
00492
00493 if (!p) {
00494 return;
00495 }
00496
00497 do {
00498 if (p->next == NULL) {
00499 jb_err("Queue is BROKEN at item [%d]", i);
00500 }
00501 i++;
00502 p=p->next;
00503 } while (p->next != jb->frames);
00504 }
00505
00506 static void jb_dbgqueue(jitterbuf *jb)
00507 {
00508 int i=0;
00509 jb_frame *p = jb->frames;
00510
00511 jb_dbg("queue: ");
00512
00513 if (!p) {
00514 jb_dbg("EMPTY\n");
00515 return;
00516 }
00517
00518 do {
00519 jb_dbg("[%d]=%ld ", i++, p->ts);
00520 p=p->next;
00521 } while (p->next != jb->frames);
00522
00523 jb_dbg("\n");
00524 }
00525 #endif
00526
00527 enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts, long now)
00528 {
00529 long delay = now - (ts - jb->info.resync_offset);
00530 jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb, data, ms, ts, now);
00531
00532 if (check_resync(jb, ts, now, ms, type, &delay)) {
00533 return JB_DROP;
00534 }
00535
00536 if (type == JB_TYPE_VOICE) {
00537
00538
00539 history_put(jb, ts, now, ms, delay);
00540 }
00541
00542 jb->info.frames_in++;
00543
00544
00545 if (queue_put(jb,data,type,ms,ts)) {
00546 return JB_SCHED;
00547 }
00548 return JB_OK;
00549 }
00550
00551
00552 static enum jb_return_code _jb_get(jitterbuf *jb, jb_frame *frameout, long now, long interpl)
00553 {
00554 jb_frame *frame;
00555 long diff;
00556 static int dbg_cnt = 0;
00557
00558
00559 history_get(jb);
00560
00561 if (dbg_cnt && dbg_cnt % 50 == 0) {
00562 jb_dbg("\n");
00563 }
00564 dbg_cnt++;
00565
00566
00567 jb->info.target = jb->info.jitter + jb->info.min + jb->info.conf.target_extra;
00568
00569
00570 if ((jb->info.conf.max_jitterbuf) && ((jb->info.target - jb->info.min) > jb->info.conf.max_jitterbuf)) {
00571 jb_dbg("clamping target from %ld to %ld\n", (jb->info.target - jb->info.min), jb->info.conf.max_jitterbuf);
00572 jb->info.target = jb->info.min + jb->info.conf.max_jitterbuf;
00573 }
00574
00575 diff = jb->info.target - jb->info.current;
00576
00577
00578
00579
00580
00581 if (!jb->info.silence_begin_ts) {
00582
00583 if ((diff > 0) &&
00584
00585 (((jb->info.last_adjustment + JB_ADJUST_DELAY) < now) ||
00586
00587 (diff > queue_last(jb) - queue_next(jb)) ) ) {
00588
00589 jb->info.current += interpl;
00590 jb->info.next_voice_ts += interpl;
00591 jb->info.last_voice_ms = interpl;
00592 jb->info.last_adjustment = now;
00593 jb->info.cnt_contig_interp++;
00594 if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) {
00595 jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;
00596 }
00597 jb_dbg("G");
00598 return JB_INTERP;
00599 }
00600
00601 frame = queue_get(jb, jb->info.next_voice_ts - jb->info.current);
00602
00603
00604 if (frame && frame->type != JB_TYPE_VOICE) {
00605 if (frame->type == JB_TYPE_SILENCE) {
00606 jb->info.silence_begin_ts = frame->ts;
00607 jb->info.cnt_contig_interp = 0;
00608 }
00609
00610 *frameout = *frame;
00611 jb->info.frames_out++;
00612 jb_dbg("o");
00613 return JB_OK;
00614 }
00615
00616
00617
00618 if (frame && frame->ts + jb->info.current < jb->info.next_voice_ts) {
00619 if (frame->ts + jb->info.current > jb->info.next_voice_ts - jb->info.last_voice_ms) {
00620
00621
00622 *frameout = *frame;
00623
00624 jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;
00625 jb->info.frames_out++;
00626 decrement_losspct(jb);
00627 jb->info.cnt_contig_interp = 0;
00628 jb_dbg("v");
00629 return JB_OK;
00630 } else {
00631
00632 *frameout = *frame;
00633 jb->info.frames_out++;
00634 decrement_losspct(jb);
00635 jb->info.frames_late++;
00636 jb->info.frames_lost--;
00637 jb_dbg("l");
00638 return JB_DROP;
00639 }
00640 }
00641
00642
00643 if (frame && frame->ms > 0) {
00644 jb->info.last_voice_ms = frame->ms;
00645 }
00646
00647
00648
00649
00650
00651 if (diff < -jb->info.conf.target_extra &&
00652 ((!frame && jb->info.last_adjustment + 80 < now) ||
00653 (jb->info.last_adjustment + 500 < now))) {
00654
00655 jb->info.last_adjustment = now;
00656 jb->info.cnt_contig_interp = 0;
00657
00658 if (frame) {
00659 *frameout = *frame;
00660
00661 jb->info.current -= frame->ms;
00662 jb->info.frames_out++;
00663 decrement_losspct(jb);
00664 jb->info.frames_dropped++;
00665 jb_dbg("s");
00666 return JB_DROP;
00667 } else {
00668
00669 jb->info.current -= jb->info.last_voice_ms;
00670 jb->info.frames_lost++;
00671 increment_losspct(jb);
00672 jb_dbg("S");
00673 return JB_NOFRAME;
00674 }
00675 }
00676
00677
00678 if (!frame) {
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700 jb->info.frames_lost++;
00701 increment_losspct(jb);
00702 jb->info.next_voice_ts += interpl;
00703 jb->info.last_voice_ms = interpl;
00704 jb->info.cnt_contig_interp++;
00705 if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) {
00706 jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;
00707 }
00708 jb_dbg("L");
00709 return JB_INTERP;
00710 }
00711
00712
00713 *frameout = *frame;
00714 jb->info.next_voice_ts += frame->ms;
00715 jb->info.frames_out++;
00716 jb->info.cnt_contig_interp = 0;
00717 decrement_losspct(jb);
00718 jb_dbg("v");
00719 return JB_OK;
00720 } else {
00721
00722
00723
00724
00725
00726
00727
00728
00729 if (diff < -jb->info.conf.target_extra &&
00730 jb->info.last_adjustment + 10 <= now) {
00731 jb->info.current -= interpl;
00732 jb->info.last_adjustment = now;
00733 }
00734
00735 frame = queue_get(jb, now - jb->info.current);
00736 if (!frame) {
00737 return JB_NOFRAME;
00738 } else if (frame->type != JB_TYPE_VOICE) {
00739
00740 *frameout = *frame;
00741 jb->info.frames_out++;
00742 return JB_OK;
00743 }
00744 if (frame->ts < jb->info.silence_begin_ts) {
00745
00746 *frameout = *frame;
00747 jb->info.frames_out++;
00748 decrement_losspct(jb);
00749 jb->info.frames_late++;
00750 jb->info.frames_lost--;
00751 jb_dbg("l");
00752 return JB_DROP;
00753 } else {
00754
00755
00756 jb->info.current = jb->info.target;
00757 jb->info.silence_begin_ts = 0;
00758 jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;
00759 jb->info.last_voice_ms = frame->ms;
00760 jb->info.frames_out++;
00761 decrement_losspct(jb);
00762 *frameout = *frame;
00763 jb_dbg("V");
00764 return JB_OK;
00765 }
00766 }
00767 }
00768
00769 long jb_next(jitterbuf *jb)
00770 {
00771 if (jb->info.silence_begin_ts) {
00772 if (jb->frames) {
00773 long next = queue_next(jb);
00774 history_get(jb);
00775
00776 if (jb->info.target - jb->info.current < -jb->info.conf.target_extra)
00777 return jb->info.last_adjustment + 10;
00778 return next + jb->info.target;
00779 }
00780 else
00781 return JB_LONGMAX;
00782 } else {
00783 return jb->info.next_voice_ts;
00784 }
00785 }
00786
00787 enum jb_return_code jb_get(jitterbuf *jb, jb_frame *frameout, long now, long interpl)
00788 {
00789 enum jb_return_code ret = _jb_get(jb, frameout, now, interpl);
00790 #if 0
00791 static int lastts=0;
00792 int thists = ((ret == JB_OK) || (ret == JB_DROP)) ? frameout->ts : 0;
00793 jb_warn("jb_get(%x,%x,%ld) = %d (%d)\n", jb, frameout, now, ret, thists);
00794 if (thists && thists < lastts) jb_warn("XXXX timestamp roll-back!!!\n");
00795 lastts = thists;
00796 #endif
00797 if (ret == JB_INTERP)
00798 frameout->ms = jb->info.last_voice_ms;
00799
00800 return ret;
00801 }
00802
00803 enum jb_return_code jb_getall(jitterbuf *jb, jb_frame *frameout)
00804 {
00805 jb_frame *frame;
00806 frame = queue_getall(jb);
00807
00808 if (!frame) {
00809 return JB_NOFRAME;
00810 }
00811
00812 *frameout = *frame;
00813 return JB_OK;
00814 }
00815
00816
00817 enum jb_return_code jb_getinfo(jitterbuf *jb, jb_info *stats)
00818 {
00819
00820 history_get(jb);
00821
00822 *stats = jb->info;
00823
00824 return JB_OK;
00825 }
00826
00827 enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf)
00828 {
00829
00830
00831 jb->info.conf.max_jitterbuf = conf->max_jitterbuf;
00832 jb->info.conf.resync_threshold = conf->resync_threshold;
00833 jb->info.conf.max_contig_interp = conf->max_contig_interp;
00834
00835
00836 jb->info.conf.target_extra = ( conf->target_extra == -1 )
00837 ? JB_TARGET_EXTRA
00838 : conf->target_extra
00839 ;
00840
00841
00842 jb->info.current = jb->info.conf.target_extra;
00843 jb->info.target = jb->info.conf.target_extra;
00844
00845 return JB_OK;
00846 }
00847
00848