1 /* $Id$ */
2 /*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20 /*
21 * Based on implementation kindly contributed by Switchlab, Ltd.
22 */
23 #include <pjmedia/jbuf.h>
24 #include <pjmedia/errno.h>
25 #include <pj/pool.h>
26 #include <pj/assert.h>
27 #include <pj/log.h>
28 #include <pj/math.h>
29 #include <pj/string.h>
30
31
32 #define THIS_FILE "jbuf.c"
33
34
35 /* Invalid sequence number, used as the initial value. */
36 #define INVALID_OFFSET -9999
37
38 /* Maximum burst length, whenever an operation is bursting longer than
39 * this value, JB will assume that the opposite operation was idle.
40 */
41 #define MAX_BURST_MSEC 1000
42
43 /* Number of OP switches to be performed in JB_STATUS_INITIALIZING, before
44 * JB can switch its states to JB_STATUS_PROCESSING.
45 */
46 #define INIT_CYCLE 10
47
48
49 /* Maximum frame index in JB framelist (estimated).
50 * As index is calculated as (RTP-timestamp/timestamp-span), the maximum index
51 * usually ranging from MAXUINT32/9000 (10 fps video at 90kHz) to MAXUINT32/80
52 * (10 ms audio at 8000Hz), lets take the 'lowest'.
53 */
54 #define MAX_FRAME_INDEX (0xFFFFFFFF/9000)
55
56
57 /* Minimal difference between JB size and 2*burst-level to perform
58 * JB shrinking in static discard algorithm.
59 */
60 #define STA_DISC_SAFE_SHRINKING_DIFF 1
61
62
63 /* Struct of JB internal buffer, represented in a circular buffer containing
64 * frame content, frame type, frame length, and frame bit info.
65 */
66 typedef struct jb_framelist_t
67 {
68 /* Settings */
69 unsigned frame_size; /**< maximum size of frame */
70 unsigned max_count; /**< maximum number of frames */
71
72 /* Buffers */
73 char *content; /**< frame content array */
74 int *frame_type; /**< frame type array */
75 pj_size_t *content_len; /**< frame length array */
76 pj_uint32_t *bit_info; /**< frame bit info array */
77 pj_uint32_t *ts; /**< timestamp array */
78
79 /* States */
80 unsigned head; /**< index of head, pointed frame
81 will be returned by next GET */
82 unsigned size; /**< current size of framelist,
83 including discarded frames. */
84 unsigned discarded_num; /**< current number of discarded
85 frames. */
86 int origin; /**< original index of flist_head */
87
88 } jb_framelist_t;
89
90
91 typedef void (*discard_algo)(pjmedia_jbuf *jb);
92 static void jbuf_discard_static(pjmedia_jbuf *jb);
93 static void jbuf_discard_progressive(pjmedia_jbuf *jb);
94
95
96 struct pjmedia_jbuf
97 {
98 /* Settings (consts) */
99 pj_str_t jb_name; /**< jitter buffer name */
100 pj_size_t jb_frame_size; /**< frame size */
101 unsigned jb_frame_ptime; /**< frame duration. */
102 pj_size_t jb_max_count; /**< capacity of jitter buffer,
103 in frames */
104 int jb_init_prefetch; /**< Initial prefetch */
105 int jb_min_prefetch; /**< Minimum allowable prefetch */
106 int jb_max_prefetch; /**< Maximum allowable prefetch */
107 int jb_max_burst; /**< maximum possible burst, whenever
108 burst exceeds this value, it
109 won't be included in level
110 calculation */
111 int jb_min_shrink_gap; /**< How often can we shrink */
112 discard_algo jb_discard_algo; /**< Discard algorithm */
113
114 /* Buffer */
115 jb_framelist_t jb_framelist; /**< the buffer */
116
117 /* States */
118 int jb_level; /**< delay between source &
119 destination (calculated according
120 of the number of burst get/put
121 operations) */
122 int jb_max_hist_level; /**< max level during the last level
123 calculations */
124 int jb_stable_hist; /**< num of times the delay has been
125 lower then the prefetch num */
126 int jb_last_op; /**< last operation executed
127 (put/get) */
128 int jb_eff_level; /**< effective burst level */
129 int jb_prefetch; /**< no. of frame to insert before
130 removing some (at the beginning
131 of the framelist->content
132 operation), the value may be
133 continuously updated based on
134 current frame burst level. */
135 pj_bool_t jb_prefetching; /**< flag if jbuf is prefetching. */
136 int jb_status; /**< status is 'init' until the first
137 'put' operation */
138 int jb_init_cycle_cnt; /**< status is 'init' until the first
139 'put' operation */
140
141 int jb_discard_ref; /**< Seq # of last frame deleted or
142 discarded */
143 unsigned jb_discard_dist; /**< Distance from jb_discard_ref
144 to perform discard (in frm) */
145
146 /* Statistics */
147 pj_math_stat jb_delay; /**< Delay statistics of jitter buffer
148 (in ms) */
149 pj_math_stat jb_burst; /**< Burst statistics (in frames) */
150 unsigned jb_lost; /**< Number of lost frames. */
151 unsigned jb_discard; /**< Number of discarded frames. */
152 unsigned jb_empty; /**< Number of empty/prefetching frame
153 returned by GET. */
154 };
155
156
157 #define JB_STATUS_INITIALIZING 0
158 #define JB_STATUS_PROCESSING 1
159
160
161
162 /* Progressive discard algorithm introduced to reduce JB latency
163 * by discarding incoming frames with adaptive aggressiveness based on
164 * actual burst level.
165 */
166 #define PROGRESSIVE_DISCARD 1
167
168 /* Internal JB frame flag, discarded frame will not be returned by JB to
169 * application, it's just simply discarded.
170 */
171 #define PJMEDIA_JB_DISCARDED_FRAME 1024
172
173
174
175 /* Enabling this would log the jitter buffer state about once per
176 * second.
177 */
178 #if 0
179 # define TRACE__(args) PJ_LOG(5,args)
180 #else
181 # define TRACE__(args)
182 #endif
183
184 static pj_status_t jb_framelist_reset(jb_framelist_t *framelist);
185 static unsigned jb_framelist_remove_head(jb_framelist_t *framelist,
186 unsigned count);
187
jb_framelist_init(pj_pool_t * pool,jb_framelist_t * framelist,unsigned frame_size,unsigned max_count)188 static pj_status_t jb_framelist_init( pj_pool_t *pool,
189 jb_framelist_t *framelist,
190 unsigned frame_size,
191 unsigned max_count)
192 {
193 PJ_ASSERT_RETURN(pool && framelist, PJ_EINVAL);
194
195 pj_bzero(framelist, sizeof(jb_framelist_t));
196
197 framelist->frame_size = frame_size;
198 framelist->max_count = max_count;
199 framelist->content = (char*)
200 pj_pool_alloc(pool,
201 (pj_size_t)framelist->frame_size*
202 framelist->max_count);
203 framelist->frame_type = (int*)
204 pj_pool_alloc(pool,
205 sizeof(framelist->frame_type[0])*
206 framelist->max_count);
207 framelist->content_len = (pj_size_t*)
208 pj_pool_alloc(pool,
209 sizeof(framelist->content_len[0])*
210 framelist->max_count);
211 framelist->bit_info = (pj_uint32_t*)
212 pj_pool_alloc(pool,
213 sizeof(framelist->bit_info[0])*
214 framelist->max_count);
215 framelist->ts = (pj_uint32_t*)
216 pj_pool_alloc(pool,
217 sizeof(framelist->ts[0])*
218 framelist->max_count);
219
220 return jb_framelist_reset(framelist);
221
222 }
223
jb_framelist_destroy(jb_framelist_t * framelist)224 static pj_status_t jb_framelist_destroy(jb_framelist_t *framelist)
225 {
226 PJ_UNUSED_ARG(framelist);
227 return PJ_SUCCESS;
228 }
229
jb_framelist_reset(jb_framelist_t * framelist)230 static pj_status_t jb_framelist_reset(jb_framelist_t *framelist)
231 {
232 framelist->head = 0;
233 framelist->origin = INVALID_OFFSET;
234 framelist->size = 0;
235 framelist->discarded_num = 0;
236
237
238 //pj_bzero(framelist->content,
239 // framelist->frame_size *
240 // framelist->max_count);
241
242 pj_memset(framelist->frame_type,
243 PJMEDIA_JB_MISSING_FRAME,
244 sizeof(framelist->frame_type[0]) *
245 framelist->max_count);
246
247 pj_bzero(framelist->content_len,
248 sizeof(framelist->content_len[0]) *
249 framelist->max_count);
250
251 //pj_bzero(framelist->bit_info,
252 // sizeof(framelist->bit_info[0]) *
253 // framelist->max_count);
254
255 return PJ_SUCCESS;
256 }
257
258
jb_framelist_size(const jb_framelist_t * framelist)259 static unsigned jb_framelist_size(const jb_framelist_t *framelist)
260 {
261 return framelist->size;
262 }
263
264
jb_framelist_eff_size(const jb_framelist_t * framelist)265 static unsigned jb_framelist_eff_size(const jb_framelist_t *framelist)
266 {
267 return (framelist->size - framelist->discarded_num);
268 }
269
jb_framelist_origin(const jb_framelist_t * framelist)270 static int jb_framelist_origin(const jb_framelist_t *framelist)
271 {
272 return framelist->origin;
273 }
274
275
jb_framelist_get(jb_framelist_t * framelist,void * frame,pj_size_t * size,pjmedia_jb_frame_type * p_type,pj_uint32_t * bit_info,pj_uint32_t * ts,int * seq)276 static pj_bool_t jb_framelist_get(jb_framelist_t *framelist,
277 void *frame, pj_size_t *size,
278 pjmedia_jb_frame_type *p_type,
279 pj_uint32_t *bit_info,
280 pj_uint32_t *ts,
281 int *seq)
282 {
283 if (framelist->size) {
284 pj_bool_t prev_discarded = PJ_FALSE;
285
286 /* Skip discarded frames */
287 while (framelist->frame_type[framelist->head] ==
288 PJMEDIA_JB_DISCARDED_FRAME)
289 {
290 jb_framelist_remove_head(framelist, 1);
291 prev_discarded = PJ_TRUE;
292 }
293
294 /* Return the head frame if any */
295 if (framelist->size) {
296 if (prev_discarded) {
297 /* Ticket #1188: when previous frame(s) was discarded, return
298 * 'missing' frame to trigger PLC to get smoother signal.
299 */
300 *p_type = PJMEDIA_JB_MISSING_FRAME;
301 if (size)
302 *size = 0;
303 if (bit_info)
304 *bit_info = 0;
305 } else {
306 pj_size_t frm_size = framelist->content_len[framelist->head];
307 pj_size_t max_size = size? *size : frm_size;
308 pj_size_t copy_size = PJ_MIN(max_size, frm_size);
309
310 /* Buffer size should not be smaller than frame size. */
311 if (max_size < frm_size) {
312 pj_assert(!"Buffer too small");
313 PJ_LOG(4, (THIS_FILE, "Warning: buffer too small for the "
314 "retrieved frame!"));
315 }
316
317 pj_memcpy(frame,
318 framelist->content +
319 framelist->head * framelist->frame_size,
320 copy_size);
321 *p_type = (pjmedia_jb_frame_type)
322 framelist->frame_type[framelist->head];
323 if (size)
324 *size = copy_size;
325 if (bit_info)
326 *bit_info = framelist->bit_info[framelist->head];
327 }
328 if (ts)
329 *ts = framelist->ts[framelist->head];
330 if (seq)
331 *seq = framelist->origin;
332
333 //pj_bzero(framelist->content +
334 // framelist->head * framelist->frame_size,
335 // framelist->frame_size);
336 framelist->frame_type[framelist->head] = PJMEDIA_JB_MISSING_FRAME;
337 framelist->content_len[framelist->head] = 0;
338 framelist->bit_info[framelist->head] = 0;
339 framelist->ts[framelist->head] = 0;
340
341 framelist->origin++;
342 framelist->head = (framelist->head + 1) % framelist->max_count;
343 framelist->size--;
344
345 return PJ_TRUE;
346 }
347 }
348
349 /* No frame available */
350 pj_bzero(frame, framelist->frame_size);
351
352 return PJ_FALSE;
353 }
354
355
jb_framelist_peek(jb_framelist_t * framelist,unsigned offset,const void ** frame,pj_size_t * size,pjmedia_jb_frame_type * type,pj_uint32_t * bit_info,pj_uint32_t * ts,int * seq)356 static pj_bool_t jb_framelist_peek(jb_framelist_t *framelist,
357 unsigned offset,
358 const void **frame,
359 pj_size_t *size,
360 pjmedia_jb_frame_type *type,
361 pj_uint32_t *bit_info,
362 pj_uint32_t *ts,
363 int *seq)
364 {
365 unsigned pos, idx;
366
367 if (offset >= jb_framelist_eff_size(framelist))
368 return PJ_FALSE;
369
370 pos = framelist->head;
371 idx = offset;
372
373 /* Find actual peek position, note there may be discarded frames */
374 while (1) {
375 if (framelist->frame_type[pos] != PJMEDIA_JB_DISCARDED_FRAME) {
376 if (idx == 0)
377 break;
378 else
379 --idx;
380 }
381 pos = (pos + 1) % framelist->max_count;
382 }
383
384 /* Return the frame pointer */
385 if (frame)
386 *frame = framelist->content + pos*framelist->frame_size;
387 if (type)
388 *type = (pjmedia_jb_frame_type)
389 framelist->frame_type[pos];
390 if (size)
391 *size = framelist->content_len[pos];
392 if (bit_info)
393 *bit_info = framelist->bit_info[pos];
394 if (ts)
395 *ts = framelist->ts[pos];
396 if (seq)
397 *seq = framelist->origin + offset;
398
399 return PJ_TRUE;
400 }
401
402
403 /* Remove oldest frames as many as param 'count' */
jb_framelist_remove_head(jb_framelist_t * framelist,unsigned count)404 static unsigned jb_framelist_remove_head(jb_framelist_t *framelist,
405 unsigned count)
406 {
407 if (count > framelist->size)
408 count = framelist->size;
409
410 if (count) {
411 /* may be done in two steps if overlapping */
412 unsigned step1,step2;
413 unsigned tmp = framelist->head+count;
414 unsigned i;
415
416 if (tmp > framelist->max_count) {
417 step1 = framelist->max_count - framelist->head;
418 step2 = count-step1;
419 } else {
420 step1 = count;
421 step2 = 0;
422 }
423
424 for (i = framelist->head; i < (framelist->head + step1); ++i) {
425 if (framelist->frame_type[i] == PJMEDIA_JB_DISCARDED_FRAME) {
426 pj_assert(framelist->discarded_num > 0);
427 framelist->discarded_num--;
428 }
429 }
430
431 //pj_bzero(framelist->content +
432 // framelist->head * framelist->frame_size,
433 // step1*framelist->frame_size);
434 pj_memset(framelist->frame_type+framelist->head,
435 PJMEDIA_JB_MISSING_FRAME,
436 step1*sizeof(framelist->frame_type[0]));
437 pj_bzero(framelist->content_len+framelist->head,
438 step1*sizeof(framelist->content_len[0]));
439
440 if (step2) {
441 for (i = 0; i < step2; ++i) {
442 if (framelist->frame_type[i] == PJMEDIA_JB_DISCARDED_FRAME) {
443 pj_assert(framelist->discarded_num > 0);
444 framelist->discarded_num--;
445 }
446 }
447 //pj_bzero( framelist->content,
448 // step2*framelist->frame_size);
449 pj_memset(framelist->frame_type,
450 PJMEDIA_JB_MISSING_FRAME,
451 step2*sizeof(framelist->frame_type[0]));
452 pj_bzero (framelist->content_len,
453 step2*sizeof(framelist->content_len[0]));
454 }
455
456 /* update states */
457 framelist->origin += count;
458 framelist->head = (framelist->head + count) % framelist->max_count;
459 framelist->size -= count;
460 }
461
462 return count;
463 }
464
465
jb_framelist_put_at(jb_framelist_t * framelist,int index,const void * frame,unsigned frame_size,pj_uint32_t bit_info,pj_uint32_t ts,unsigned frame_type)466 static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist,
467 int index,
468 const void *frame,
469 unsigned frame_size,
470 pj_uint32_t bit_info,
471 pj_uint32_t ts,
472 unsigned frame_type)
473 {
474 int distance;
475 unsigned pos;
476 enum { MAX_MISORDER = 100 };
477 enum { MAX_DROPOUT = 3000 };
478
479 PJ_ASSERT_RETURN(frame_size <= framelist->frame_size, PJ_EINVAL);
480
481 /* get distance of this frame to the first frame in the buffer */
482 distance = index - framelist->origin;
483
484 /* too late or sequence restart or far jump */
485 if (distance < 0) {
486 if (framelist->origin - index < MAX_MISORDER) {
487 /* too late */
488 TRACE__((THIS_FILE,"Put frame #%d: too late (distance=%d)",
489 index, distance));
490 return PJ_ETOOSMALL;
491 } else if (framelist->origin + framelist->size >= MAX_FRAME_INDEX) {
492 /* sequence restart */
493 TRACE__((THIS_FILE,"Put frame #%d: sequence restart (distance=%d, "
494 "orig=%d, size=%d)",
495 index, distance, framelist->origin,
496 framelist->size));
497 framelist->origin = index - framelist->size;
498 distance = framelist->size;
499 } else {
500 /* jump too far, reset the buffer */
501 TRACE__((THIS_FILE,"Put frame #%d: far jump (distance=%d)",
502 index, distance));
503 jb_framelist_reset(framelist);
504 framelist->origin = index;
505 distance = 0;
506 }
507 }
508
509 /* if jbuf is empty, just reset the origin */
510 if (framelist->size == 0) {
511 pj_assert(framelist->discarded_num == 0);
512 TRACE__((THIS_FILE,"Put frame #%d: origin reset (from %d) as JB empty",
513 index, framelist->origin));
514 framelist->origin = index;
515 distance = 0;
516 }
517
518 /* far jump, the distance is greater than buffer capacity */
519 if (distance >= (int)framelist->max_count) {
520 if (distance > MAX_DROPOUT) {
521 /* jump too far, reset the buffer */
522 TRACE__((THIS_FILE,"Put frame #%d: far jump (distance=%d)",
523 index, distance));
524 jb_framelist_reset(framelist);
525 framelist->origin = index;
526 distance = 0;
527 } else {
528 /* otherwise, reject the frame */
529 TRACE__((THIS_FILE,"Put frame #%d: rejected due to JB full",
530 index));
531 return PJ_ETOOMANY;
532 }
533 }
534
535 /* get the slot position */
536 pos = (framelist->head + distance) % framelist->max_count;
537
538 /* if the slot is occupied, it must be duplicated frame, ignore it. */
539 if (framelist->frame_type[pos] != PJMEDIA_JB_MISSING_FRAME) {
540 TRACE__((THIS_FILE,"Put frame #%d maybe a duplicate, ignored", index));
541 return PJ_EEXISTS;
542 }
543
544 /* put the frame into the slot */
545 framelist->frame_type[pos] = frame_type;
546 framelist->content_len[pos] = frame_size;
547 framelist->bit_info[pos] = bit_info;
548 framelist->ts[pos] = ts;
549
550 /* update framelist size */
551 if (framelist->origin + (int)framelist->size <= index)
552 framelist->size = distance + 1;
553
554 if(PJMEDIA_JB_NORMAL_FRAME == frame_type) {
555 /* copy frame content */
556 pj_memcpy(framelist->content + pos * framelist->frame_size,
557 frame, frame_size);
558 }
559
560 return PJ_SUCCESS;
561 }
562
563
jb_framelist_discard(jb_framelist_t * framelist,int index)564 static pj_status_t jb_framelist_discard(jb_framelist_t *framelist,
565 int index)
566 {
567 unsigned pos;
568
569 PJ_ASSERT_RETURN(index >= framelist->origin &&
570 index < framelist->origin + (int)framelist->size,
571 PJ_EINVAL);
572
573 /* Get the slot position */
574 pos = (framelist->head + (index - framelist->origin)) %
575 framelist->max_count;
576
577 /* Discard the frame */
578 framelist->frame_type[pos] = PJMEDIA_JB_DISCARDED_FRAME;
579 framelist->discarded_num++;
580
581 return PJ_SUCCESS;
582 }
583
584
585 enum pjmedia_jb_op
586 {
587 JB_OP_INIT = -1,
588 JB_OP_PUT = 1,
589 JB_OP_GET = 2
590 };
591
592
pjmedia_jbuf_create(pj_pool_t * pool,const pj_str_t * name,unsigned frame_size,unsigned ptime,unsigned max_count,pjmedia_jbuf ** p_jb)593 PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool,
594 const pj_str_t *name,
595 unsigned frame_size,
596 unsigned ptime,
597 unsigned max_count,
598 pjmedia_jbuf **p_jb)
599 {
600 pjmedia_jbuf *jb;
601 pj_status_t status;
602
603 jb = PJ_POOL_ZALLOC_T(pool, pjmedia_jbuf);
604
605 status = jb_framelist_init(pool, &jb->jb_framelist, frame_size, max_count);
606 if (status != PJ_SUCCESS)
607 return status;
608
609 pj_strdup_with_null(pool, &jb->jb_name, name);
610 jb->jb_frame_size = frame_size;
611 jb->jb_frame_ptime = ptime;
612 jb->jb_prefetch = PJ_MIN(PJMEDIA_JB_DEFAULT_INIT_DELAY,max_count*4/5);
613 jb->jb_min_prefetch = 0;
614 jb->jb_max_prefetch = max_count*4/5;
615 jb->jb_max_count = max_count;
616 jb->jb_min_shrink_gap= PJMEDIA_JBUF_DISC_MIN_GAP / ptime;
617 jb->jb_max_burst = PJ_MAX(MAX_BURST_MSEC / ptime, max_count*3/4);
618
619 pj_math_stat_init(&jb->jb_delay);
620 pj_math_stat_init(&jb->jb_burst);
621
622 pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_PROGRESSIVE);
623 pjmedia_jbuf_reset(jb);
624
625 *p_jb = jb;
626 return PJ_SUCCESS;
627 }
628
629
pjmedia_jbuf_set_ptime(pjmedia_jbuf * jb,unsigned ptime)630 PJ_DEF(pj_status_t) pjmedia_jbuf_set_ptime( pjmedia_jbuf *jb,
631 unsigned ptime)
632 {
633 PJ_ASSERT_RETURN(jb, PJ_EINVAL);
634
635 jb->jb_frame_ptime = ptime;
636 jb->jb_min_shrink_gap = PJMEDIA_JBUF_DISC_MIN_GAP / ptime;
637 jb->jb_max_burst = PJ_MAX(MAX_BURST_MSEC / ptime,
638 jb->jb_max_count*3/4);
639
640 return PJ_SUCCESS;
641 }
642
643
644 /*
645 * Set the jitter buffer to fixed delay mode. The default behavior
646 * is to adapt the delay with actual packet delay.
647 *
648 */
pjmedia_jbuf_set_fixed(pjmedia_jbuf * jb,unsigned prefetch)649 PJ_DEF(pj_status_t) pjmedia_jbuf_set_fixed( pjmedia_jbuf *jb,
650 unsigned prefetch)
651 {
652 PJ_ASSERT_RETURN(jb, PJ_EINVAL);
653 PJ_ASSERT_RETURN(prefetch <= jb->jb_max_count, PJ_EINVAL);
654
655 jb->jb_min_prefetch = jb->jb_max_prefetch =
656 jb->jb_prefetch = jb->jb_init_prefetch = prefetch;
657
658 pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_NONE);
659 return PJ_SUCCESS;
660 }
661
662
663 /*
664 * Set the jitter buffer to adaptive mode.
665 */
pjmedia_jbuf_set_adaptive(pjmedia_jbuf * jb,unsigned prefetch,unsigned min_prefetch,unsigned max_prefetch)666 PJ_DEF(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb,
667 unsigned prefetch,
668 unsigned min_prefetch,
669 unsigned max_prefetch)
670 {
671 PJ_ASSERT_RETURN(jb, PJ_EINVAL);
672 PJ_ASSERT_RETURN(min_prefetch <= max_prefetch &&
673 prefetch <= max_prefetch &&
674 max_prefetch <= jb->jb_max_count,
675 PJ_EINVAL);
676
677 jb->jb_prefetch = jb->jb_init_prefetch = prefetch;
678 jb->jb_min_prefetch = min_prefetch;
679 jb->jb_max_prefetch = max_prefetch;
680
681 return PJ_SUCCESS;
682 }
683
684
pjmedia_jbuf_set_discard(pjmedia_jbuf * jb,pjmedia_jb_discard_algo algo)685 PJ_DEF(pj_status_t) pjmedia_jbuf_set_discard( pjmedia_jbuf *jb,
686 pjmedia_jb_discard_algo algo)
687 {
688 PJ_ASSERT_RETURN(jb, PJ_EINVAL);
689 PJ_ASSERT_RETURN(algo >= PJMEDIA_JB_DISCARD_NONE &&
690 algo <= PJMEDIA_JB_DISCARD_PROGRESSIVE,
691 PJ_EINVAL);
692
693 switch(algo) {
694 case PJMEDIA_JB_DISCARD_PROGRESSIVE:
695 jb->jb_discard_algo = &jbuf_discard_progressive;
696 break;
697 case PJMEDIA_JB_DISCARD_STATIC:
698 jb->jb_discard_algo = &jbuf_discard_static;
699 break;
700 default:
701 jb->jb_discard_algo = NULL;
702 break;
703 }
704
705 return PJ_SUCCESS;
706 }
707
708
pjmedia_jbuf_reset(pjmedia_jbuf * jb)709 PJ_DEF(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb)
710 {
711 jb->jb_level = 0;
712 jb->jb_last_op = JB_OP_INIT;
713 jb->jb_stable_hist = 0;
714 jb->jb_status = JB_STATUS_INITIALIZING;
715 jb->jb_init_cycle_cnt= 0;
716 jb->jb_max_hist_level= 0;
717 jb->jb_prefetching = (jb->jb_prefetch != 0);
718 jb->jb_discard_dist = 0;
719
720 jb_framelist_reset(&jb->jb_framelist);
721
722 return PJ_SUCCESS;
723 }
724
725
pjmedia_jbuf_destroy(pjmedia_jbuf * jb)726 PJ_DEF(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb)
727 {
728 PJ_LOG(5, (jb->jb_name.ptr, ""
729 "JB summary:\n"
730 " size=%d/eff=%d prefetch=%d level=%d\n"
731 " delay (min/max/avg/dev)=%d/%d/%d/%d ms\n"
732 " burst (min/max/avg/dev)=%d/%d/%d/%d frames\n"
733 " lost=%d discard=%d empty=%d",
734 jb_framelist_size(&jb->jb_framelist),
735 jb_framelist_eff_size(&jb->jb_framelist),
736 jb->jb_prefetch, jb->jb_eff_level,
737 jb->jb_delay.min, jb->jb_delay.max, jb->jb_delay.mean,
738 pj_math_stat_get_stddev(&jb->jb_delay),
739 jb->jb_burst.min, jb->jb_burst.max, jb->jb_burst.mean,
740 pj_math_stat_get_stddev(&jb->jb_burst),
741 jb->jb_lost, jb->jb_discard, jb->jb_empty));
742
743 return jb_framelist_destroy(&jb->jb_framelist);
744 }
745
pjmedia_jbuf_is_full(const pjmedia_jbuf * jb)746 PJ_DEF(pj_bool_t) pjmedia_jbuf_is_full(const pjmedia_jbuf *jb)
747 {
748 return jb->jb_framelist.size == jb->jb_framelist.max_count;
749 }
750
jbuf_calculate_jitter(pjmedia_jbuf * jb)751 static void jbuf_calculate_jitter(pjmedia_jbuf *jb)
752 {
753 int diff, cur_size;
754
755 cur_size = jb_framelist_eff_size(&jb->jb_framelist);
756 pj_math_stat_update(&jb->jb_burst, jb->jb_level);
757 jb->jb_max_hist_level = PJ_MAX(jb->jb_max_hist_level, jb->jb_level);
758
759 /* Burst level is decreasing */
760 if (jb->jb_level < jb->jb_eff_level) {
761
762 enum { STABLE_HISTORY_LIMIT = 20 };
763
764 jb->jb_stable_hist++;
765
766 /* Only update the effective level (and prefetch) if 'stable'
767 * condition is reached (not just short time impulse)
768 */
769 if (jb->jb_stable_hist > STABLE_HISTORY_LIMIT) {
770
771 diff = (jb->jb_eff_level - jb->jb_max_hist_level) / 3;
772
773 if (diff < 1)
774 diff = 1;
775
776 /* Update effective burst level */
777 jb->jb_eff_level -= diff;
778
779 /* Update prefetch based on level */
780 if (jb->jb_init_prefetch) {
781 jb->jb_prefetch = jb->jb_eff_level;
782 if (jb->jb_prefetch < jb->jb_min_prefetch)
783 jb->jb_prefetch = jb->jb_min_prefetch;
784 if (jb->jb_prefetch > jb->jb_max_prefetch)
785 jb->jb_prefetch = jb->jb_max_prefetch;
786 }
787
788 /* Reset history */
789 jb->jb_max_hist_level = 0;
790 jb->jb_stable_hist = 0;
791
792 TRACE__((jb->jb_name.ptr,"jb updated(1), lvl=%d pre=%d, size=%d",
793 jb->jb_eff_level, jb->jb_prefetch, cur_size));
794 PJ_UNUSED_ARG(cur_size); /* Warning about unused var */
795 }
796 }
797
798 /* Burst level is increasing */
799 else if (jb->jb_level > jb->jb_eff_level) {
800
801 /* Instaneous set effective burst level to recent maximum level */
802 jb->jb_eff_level = PJ_MIN(jb->jb_max_hist_level,
803 (int)(jb->jb_max_count*4/5));
804
805 /* Update prefetch based on level */
806 if (jb->jb_init_prefetch) {
807 jb->jb_prefetch = jb->jb_eff_level;
808 if (jb->jb_prefetch > jb->jb_max_prefetch)
809 jb->jb_prefetch = jb->jb_max_prefetch;
810 if (jb->jb_prefetch < jb->jb_min_prefetch)
811 jb->jb_prefetch = jb->jb_min_prefetch;
812 }
813
814 jb->jb_stable_hist = 0;
815 /* Do not reset max_hist_level. */
816 //jb->jb_max_hist_level = 0;
817
818 TRACE__((jb->jb_name.ptr,"jb updated(2), lvl=%d pre=%d, size=%d",
819 jb->jb_eff_level, jb->jb_prefetch, cur_size));
820 }
821
822 /* Level is unchanged */
823 else {
824 jb->jb_stable_hist = 0;
825 }
826 }
827
828
jbuf_discard_static(pjmedia_jbuf * jb)829 static void jbuf_discard_static(pjmedia_jbuf *jb)
830 {
831 /* These code is used for shortening the delay in the jitter buffer.
832 * It needs shrink only when there is possibility of drift. Drift
833 * detection is performed by inspecting the jitter buffer size, if
834 * its size is twice of current burst level, there can be drift.
835 *
836 * Moreover, normally drift level is quite low, so JB shouldn't need
837 * to shrink aggresively, it will shrink maximum one frame per
838 * PJMEDIA_JBUF_DISC_MIN_GAP ms. Theoritically, JB may handle drift level
839 * as much as = FRAME_PTIME/PJMEDIA_JBUF_DISC_MIN_GAP * 100%
840 *
841 * Whenever there is drift, where PUT > GET, this method will keep
842 * the latency (JB size) as much as twice of burst level.
843 */
844
845 /* Shrinking due of drift will be implicitly done by progressive discard,
846 * so just disable it when progressive discard is active.
847 */
848 int diff, burst_level;
849
850 burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level);
851 diff = jb_framelist_eff_size(&jb->jb_framelist) - burst_level*2;
852
853 if (diff >= STA_DISC_SAFE_SHRINKING_DIFF) {
854 int seq_origin;
855
856 /* Check and adjust jb_discard_ref, in case there was
857 * seq restart
858 */
859 seq_origin = jb_framelist_origin(&jb->jb_framelist);
860 if (seq_origin < jb->jb_discard_ref)
861 jb->jb_discard_ref = seq_origin;
862
863 if (seq_origin - jb->jb_discard_ref >= jb->jb_min_shrink_gap)
864 {
865 /* Shrink slowly, one frame per cycle */
866 diff = 1;
867
868 /* Drop frame(s)! */
869 diff = jb_framelist_remove_head(&jb->jb_framelist, diff);
870 jb->jb_discard_ref = jb_framelist_origin(&jb->jb_framelist);
871 jb->jb_discard += diff;
872
873 TRACE__((jb->jb_name.ptr,
874 "JB shrinking %d frame(s), cur size=%d", diff,
875 jb_framelist_eff_size(&jb->jb_framelist)));
876 }
877 }
878 }
879
880
jbuf_discard_progressive(pjmedia_jbuf * jb)881 static void jbuf_discard_progressive(pjmedia_jbuf *jb)
882 {
883 unsigned cur_size, burst_level, overflow, T, discard_dist;
884 int last_seq;
885
886 /* Should be done in PUT operation */
887 if (jb->jb_last_op != JB_OP_PUT)
888 return;
889
890 /* Check if latency is longer than burst */
891 cur_size = jb_framelist_eff_size(&jb->jb_framelist);
892 burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level);
893 if (cur_size <= burst_level) {
894 /* Reset any scheduled discard */
895 jb->jb_discard_dist = 0;
896 return;
897 }
898
899 /* Estimate discard duration needed for adjusting latency */
900 if (burst_level <= PJMEDIA_JBUF_PRO_DISC_MIN_BURST)
901 T = PJMEDIA_JBUF_PRO_DISC_T1;
902 else if (burst_level >= PJMEDIA_JBUF_PRO_DISC_MAX_BURST)
903 T = PJMEDIA_JBUF_PRO_DISC_T2;
904 else
905 T = PJMEDIA_JBUF_PRO_DISC_T1 +
906 (PJMEDIA_JBUF_PRO_DISC_T2 - PJMEDIA_JBUF_PRO_DISC_T1) *
907 (burst_level - PJMEDIA_JBUF_PRO_DISC_MIN_BURST) /
908 (PJMEDIA_JBUF_PRO_DISC_MAX_BURST-PJMEDIA_JBUF_PRO_DISC_MIN_BURST);
909
910 /* Calculate current discard distance */
911 overflow = cur_size - burst_level;
912 discard_dist = T / overflow / jb->jb_frame_ptime;
913
914 /* Get last seq number in the JB */
915 last_seq = jb_framelist_origin(&jb->jb_framelist) +
916 jb_framelist_size(&jb->jb_framelist) - 1;
917
918 /* Setup new discard schedule if none, otherwise, update the existing
919 * discard schedule (can be delayed or accelerated).
920 */
921 if (jb->jb_discard_dist == 0) {
922 /* Setup new discard schedule */
923 jb->jb_discard_ref = last_seq;
924 } else if (last_seq < jb->jb_discard_ref) {
925 /* Seq restarted, update discard reference */
926 jb->jb_discard_ref = last_seq;
927 }
928 jb->jb_discard_dist = PJ_MAX(jb->jb_min_shrink_gap, (int)discard_dist);
929
930 /* Check if we need to discard now */
931 if (last_seq >= (jb->jb_discard_ref + (int)jb->jb_discard_dist)) {
932 int discard_seq;
933
934 discard_seq = jb->jb_discard_ref + jb->jb_discard_dist;
935 if (discard_seq < jb_framelist_origin(&jb->jb_framelist))
936 discard_seq = jb_framelist_origin(&jb->jb_framelist);
937
938 jb_framelist_discard(&jb->jb_framelist, discard_seq);
939
940 TRACE__((jb->jb_name.ptr,
941 "Discard #%d: ref=#%d dist=%d orig=%d size=%d/%d "
942 "burst=%d/%d",
943 discard_seq,
944 jb->jb_discard_ref,
945 jb->jb_discard_dist,
946 jb_framelist_origin(&jb->jb_framelist),
947 cur_size,
948 jb_framelist_size(&jb->jb_framelist),
949 jb->jb_eff_level,
950 burst_level));
951
952 /* Update discard reference */
953 jb->jb_discard_ref = discard_seq;
954 }
955 }
956
957
jbuf_update(pjmedia_jbuf * jb,int oper)958 PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper)
959 {
960 if(jb->jb_last_op != oper) {
961 jb->jb_last_op = oper;
962
963 if (jb->jb_status == JB_STATUS_INITIALIZING) {
964 /* Switch status 'initializing' -> 'processing' after some OP
965 * switch cycles and current OP is GET (burst level is calculated
966 * based on PUT burst), so burst calculation is guaranted to be
967 * performed right after the status switching.
968 */
969 if (++jb->jb_init_cycle_cnt >= INIT_CYCLE && oper == JB_OP_GET) {
970 jb->jb_status = JB_STATUS_PROCESSING;
971 /* To make sure the burst calculation will be done right after
972 * this, adjust burst level if it exceeds max burst level.
973 */
974 jb->jb_level = PJ_MIN(jb->jb_level, jb->jb_max_burst);
975 } else {
976 jb->jb_level = 0;
977 return;
978 }
979 }
980
981 /* Perform jitter calculation based on PUT burst-level only, since
982 * GET burst-level may not be accurate, e.g: when VAD is active.
983 * Note that when burst-level is too big, i.e: exceeds jb_max_burst,
984 * the GET op may be idle, in this case, we better skip the jitter
985 * calculation.
986 */
987 if (oper == JB_OP_GET && jb->jb_level <= jb->jb_max_burst)
988 jbuf_calculate_jitter(jb);
989
990 jb->jb_level = 0;
991 }
992
993 /* Call discard algorithm */
994 if (jb->jb_status == JB_STATUS_PROCESSING && jb->jb_discard_algo) {
995 (*jb->jb_discard_algo)(jb);
996 }
997 }
998
pjmedia_jbuf_put_frame(pjmedia_jbuf * jb,const void * frame,pj_size_t frame_size,int frame_seq)999 PJ_DEF(void) pjmedia_jbuf_put_frame( pjmedia_jbuf *jb,
1000 const void *frame,
1001 pj_size_t frame_size,
1002 int frame_seq)
1003 {
1004 pjmedia_jbuf_put_frame3(jb, frame, frame_size, 0, frame_seq, 0, NULL);
1005 }
1006
pjmedia_jbuf_put_frame2(pjmedia_jbuf * jb,const void * frame,pj_size_t frame_size,pj_uint32_t bit_info,int frame_seq,pj_bool_t * discarded)1007 PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb,
1008 const void *frame,
1009 pj_size_t frame_size,
1010 pj_uint32_t bit_info,
1011 int frame_seq,
1012 pj_bool_t *discarded)
1013 {
1014 pjmedia_jbuf_put_frame3(jb, frame, frame_size, bit_info, frame_seq, 0,
1015 discarded);
1016 }
1017
pjmedia_jbuf_put_frame3(pjmedia_jbuf * jb,const void * frame,pj_size_t frame_size,pj_uint32_t bit_info,int frame_seq,pj_uint32_t ts,pj_bool_t * discarded)1018 PJ_DEF(void) pjmedia_jbuf_put_frame3(pjmedia_jbuf *jb,
1019 const void *frame,
1020 pj_size_t frame_size,
1021 pj_uint32_t bit_info,
1022 int frame_seq,
1023 pj_uint32_t ts,
1024 pj_bool_t *discarded)
1025 {
1026 pj_size_t min_frame_size;
1027 int new_size, cur_size;
1028 pj_status_t status;
1029
1030 cur_size = jb_framelist_eff_size(&jb->jb_framelist);
1031
1032 /* Check if frame size is larger than JB frame size */
1033 if (frame_size > jb->jb_frame_size) {
1034 PJ_LOG(4, (THIS_FILE, "Warning: frame too large for jitter buffer, "
1035 "it will be truncated!"));
1036 }
1037
1038 /* Attempt to store the frame */
1039 min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size);
1040 status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
1041 (unsigned)min_frame_size, bit_info, ts,
1042 PJMEDIA_JB_NORMAL_FRAME);
1043
1044 /* Jitter buffer is full, remove some older frames */
1045 while (status == PJ_ETOOMANY) {
1046 int distance;
1047 unsigned removed;
1048
1049 /* Remove as few as possible just to make this frame in. Note that
1050 * the cases of seq-jump, out-of-order, and seq restart should have
1051 * been handled/normalized by previous call of jb_framelist_put_at().
1052 * So we're confident about 'distance' value here.
1053 */
1054 distance = (frame_seq - jb_framelist_origin(&jb->jb_framelist)) -
1055 (int)jb->jb_max_count + 1;
1056 pj_assert(distance > 0);
1057
1058 removed = jb_framelist_remove_head(&jb->jb_framelist, distance);
1059 status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
1060 (unsigned)min_frame_size, bit_info, ts,
1061 PJMEDIA_JB_NORMAL_FRAME);
1062
1063 jb->jb_discard += removed;
1064 }
1065
1066 /* Get new JB size after PUT */
1067 new_size = jb_framelist_eff_size(&jb->jb_framelist);
1068
1069 /* Return the flag if this frame is discarded */
1070 if (discarded)
1071 *discarded = (status != PJ_SUCCESS);
1072
1073 if (status == PJ_SUCCESS) {
1074 if (jb->jb_prefetching) {
1075 TRACE__((jb->jb_name.ptr, "PUT prefetch_cnt=%d/%d",
1076 new_size, jb->jb_prefetch));
1077 if (new_size >= jb->jb_prefetch)
1078 jb->jb_prefetching = PJ_FALSE;
1079 }
1080 jb->jb_level += (new_size > cur_size ? new_size-cur_size : 1);
1081 jbuf_update(jb, JB_OP_PUT);
1082 } else
1083 jb->jb_discard++;
1084 }
1085
1086 /*
1087 * Get frame from jitter buffer.
1088 */
pjmedia_jbuf_get_frame(pjmedia_jbuf * jb,void * frame,char * p_frame_type)1089 PJ_DEF(void) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb,
1090 void *frame,
1091 char *p_frame_type)
1092 {
1093 pjmedia_jbuf_get_frame3(jb, frame, NULL, p_frame_type, NULL,
1094 NULL, NULL);
1095 }
1096
1097 /*
1098 * Get frame from jitter buffer.
1099 */
pjmedia_jbuf_get_frame2(pjmedia_jbuf * jb,void * frame,pj_size_t * size,char * p_frame_type,pj_uint32_t * bit_info)1100 PJ_DEF(void) pjmedia_jbuf_get_frame2(pjmedia_jbuf *jb,
1101 void *frame,
1102 pj_size_t *size,
1103 char *p_frame_type,
1104 pj_uint32_t *bit_info)
1105 {
1106 pjmedia_jbuf_get_frame3(jb, frame, size, p_frame_type, bit_info,
1107 NULL, NULL);
1108 }
1109
1110 /*
1111 * Get frame from jitter buffer.
1112 */
pjmedia_jbuf_get_frame3(pjmedia_jbuf * jb,void * frame,pj_size_t * size,char * p_frame_type,pj_uint32_t * bit_info,pj_uint32_t * ts,int * seq)1113 PJ_DEF(void) pjmedia_jbuf_get_frame3(pjmedia_jbuf *jb,
1114 void *frame,
1115 pj_size_t *size,
1116 char *p_frame_type,
1117 pj_uint32_t *bit_info,
1118 pj_uint32_t *ts,
1119 int *seq)
1120 {
1121 if (jb->jb_prefetching) {
1122
1123 /* Can't return frame because jitter buffer is filling up
1124 * minimum prefetch.
1125 */
1126
1127 //pj_bzero(frame, jb->jb_frame_size);
1128 *p_frame_type = PJMEDIA_JB_ZERO_PREFETCH_FRAME;
1129 if (size)
1130 *size = 0;
1131
1132 TRACE__((jb->jb_name.ptr, "GET prefetch_cnt=%d/%d",
1133 jb_framelist_eff_size(&jb->jb_framelist), jb->jb_prefetch));
1134
1135 jb->jb_empty++;
1136
1137 } else {
1138
1139 pjmedia_jb_frame_type ftype = PJMEDIA_JB_NORMAL_FRAME;
1140 pj_bool_t res;
1141
1142 /* Try to retrieve a frame from frame list */
1143 res = jb_framelist_get(&jb->jb_framelist, frame, size, &ftype,
1144 bit_info, ts, seq);
1145 if (res) {
1146 /* We've successfully retrieved a frame from the frame list, but
1147 * the frame could be a blank frame!
1148 */
1149 if (ftype == PJMEDIA_JB_NORMAL_FRAME) {
1150 *p_frame_type = PJMEDIA_JB_NORMAL_FRAME;
1151 } else {
1152 *p_frame_type = PJMEDIA_JB_MISSING_FRAME;
1153 jb->jb_lost++;
1154 }
1155
1156 /* Store delay history at the first GET */
1157 if (jb->jb_last_op == JB_OP_PUT) {
1158 unsigned cur_size;
1159
1160 /* We've just retrieved one frame, so add one to cur_size */
1161 cur_size = jb_framelist_eff_size(&jb->jb_framelist) + 1;
1162 pj_math_stat_update(&jb->jb_delay,
1163 cur_size*jb->jb_frame_ptime);
1164 }
1165 } else {
1166 /* Jitter buffer is empty */
1167 if (jb->jb_prefetch)
1168 jb->jb_prefetching = PJ_TRUE;
1169
1170 //pj_bzero(frame, jb->jb_frame_size);
1171 *p_frame_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
1172 if (size)
1173 *size = 0;
1174
1175 jb->jb_empty++;
1176 }
1177 }
1178
1179 jb->jb_level++;
1180 jbuf_update(jb, JB_OP_GET);
1181 }
1182
1183 /*
1184 * Get jitter buffer state.
1185 */
pjmedia_jbuf_get_state(const pjmedia_jbuf * jb,pjmedia_jb_state * state)1186 PJ_DEF(pj_status_t) pjmedia_jbuf_get_state( const pjmedia_jbuf *jb,
1187 pjmedia_jb_state *state )
1188 {
1189 PJ_ASSERT_RETURN(jb && state, PJ_EINVAL);
1190
1191 state->frame_size = (unsigned)jb->jb_frame_size;
1192 state->min_prefetch = jb->jb_min_prefetch;
1193 state->max_prefetch = jb->jb_max_prefetch;
1194 state->max_count = jb->jb_max_count;
1195
1196 state->burst = jb->jb_eff_level;
1197 state->prefetch = jb->jb_prefetch;
1198 state->size = jb_framelist_eff_size(&jb->jb_framelist);
1199
1200 state->avg_delay = jb->jb_delay.mean;
1201 state->min_delay = jb->jb_delay.min;
1202 state->max_delay = jb->jb_delay.max;
1203 state->dev_delay = pj_math_stat_get_stddev(&jb->jb_delay);
1204
1205 state->avg_burst = jb->jb_burst.mean;
1206 state->empty = jb->jb_empty;
1207 state->discard = jb->jb_discard;
1208 state->lost = jb->jb_lost;
1209
1210 return PJ_SUCCESS;
1211 }
1212
1213
pjmedia_jbuf_peek_frame(pjmedia_jbuf * jb,unsigned offset,const void ** frame,pj_size_t * size,char * p_frm_type,pj_uint32_t * bit_info,pj_uint32_t * ts,int * seq)1214 PJ_DEF(void) pjmedia_jbuf_peek_frame( pjmedia_jbuf *jb,
1215 unsigned offset,
1216 const void **frame,
1217 pj_size_t *size,
1218 char *p_frm_type,
1219 pj_uint32_t *bit_info,
1220 pj_uint32_t *ts,
1221 int *seq)
1222 {
1223 pjmedia_jb_frame_type ftype;
1224 pj_bool_t res;
1225
1226 res = jb_framelist_peek(&jb->jb_framelist, offset, frame, size, &ftype,
1227 bit_info, ts, seq);
1228 if (!res)
1229 *p_frm_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
1230 else if (ftype == PJMEDIA_JB_NORMAL_FRAME)
1231 *p_frm_type = PJMEDIA_JB_NORMAL_FRAME;
1232 else
1233 *p_frm_type = PJMEDIA_JB_MISSING_FRAME;
1234 }
1235
1236
pjmedia_jbuf_remove_frame(pjmedia_jbuf * jb,unsigned frame_cnt)1237 PJ_DEF(unsigned) pjmedia_jbuf_remove_frame(pjmedia_jbuf *jb,
1238 unsigned frame_cnt)
1239 {
1240 unsigned count, last_discard_num;
1241
1242 last_discard_num = jb->jb_framelist.discarded_num;
1243 count = jb_framelist_remove_head(&jb->jb_framelist, frame_cnt);
1244
1245 /* Remove some more when there were discarded frames included */
1246 while (jb->jb_framelist.discarded_num < last_discard_num) {
1247 /* Calculate frames count to be removed next */
1248 frame_cnt = last_discard_num - jb->jb_framelist.discarded_num;
1249
1250 /* Normalize non-discarded frames count just been removed */
1251 count -= frame_cnt;
1252
1253 /* Remove more frames */
1254 last_discard_num = jb->jb_framelist.discarded_num;
1255 count += jb_framelist_remove_head(&jb->jb_framelist, frame_cnt);
1256 }
1257
1258 return count;
1259 }
1260