summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2009-04-06 17:05:34 +0000
committerNanang Izzuddin <nanang@teluu.com>2009-04-06 17:05:34 +0000
commitbbdeaae143a7707d8dcf8a5ff77f06d342ea1393 (patch)
tree7f101218de16cc9c192997458e9a93259b6788d3
parent0ed6c3bf457e0dbd887f8e1e4cb7154bbdb87ab1 (diff)
Ticket #762: Major updates on jitter buffer:
- Updated loop condition in put_frame() to avoid possibility of infinite loop. - Added JB capabilities to handle sequence restart & jump. - Updated jitter calculation, e.g: reset max_hist_level after updating prefetch, avoid updating prefetch when burst level is exceeding max_burst. - Updated shrinking method to be less agressive (only shrink JB when JB size is twice larger than burst level). - Updated the way JB switching status from 'initializing' to 'processing' by waiting for some OP switch cycles. - Few simplifications in framelist process, e.g: replacing fields 'empty' & 'tail' with 'size'. - Minor updates: comments, shortened framelist field names, added some JB states for reporting/monitoring purpose. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2578 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/include/pjmedia/jbuf.h15
-rw-r--r--pjmedia/src/pjmedia/jbuf.c768
2 files changed, 448 insertions, 335 deletions
diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h
index 0c9299cc..4088e359 100644
--- a/pjmedia/include/pjmedia/jbuf.h
+++ b/pjmedia/include/pjmedia/jbuf.h
@@ -66,19 +66,28 @@ typedef enum pjmedia_jb_frame_type pjmedia_jb_frame_type;
/**
- * This structure describes jitter buffer current status.
+ * This structure describes jitter buffer state.
*/
struct pjmedia_jb_state
{
+ /* Setting */
unsigned frame_size; /**< Individual frame size, in bytes. */
- unsigned prefetch; /**< Current prefetch value, in frames */
unsigned min_prefetch; /**< Minimum allowed prefetch, in frms. */
unsigned max_prefetch; /**< Maximum allowed prefetch, in frms. */
+
+ /* Status */
+ unsigned prefetch; /**< Current prefetch value, in frames */
unsigned size; /**< Current buffer size, in frames. */
+
+ /* Statistic */
unsigned avg_delay; /**< Average delay, in ms. */
unsigned min_delay; /**< Minimum delay, in ms. */
unsigned max_delay; /**< Maximum delay, in ms. */
- unsigned dev_delay; /**< Standard deviation of delay, in ms. */
+ unsigned dev_delay; /**< Standard deviation of delay, in ms.*/
+ unsigned avg_burst; /**< Average burst, in frames. */
+ unsigned lost; /**< Number of lost frames. */
+ unsigned discard; /**< Number of discarded frames. */
+ unsigned empty; /**< Number of empty on GET events. */
};
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index 2cbb1643..dd45d781 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -31,49 +31,103 @@
#define THIS_FILE "jbuf.c"
+
+/* Minimal difference between JB size and 2*burst-level to perform
+ * JB shrinking.
+ */
#define SAFE_SHRINKING_DIFF 1
+
+/* Minimal gap (in ms) between JB shrinking */
#define MIN_SHRINK_GAP_MSEC 200
+/* Invalid sequence number, used as the initial value. */
+#define INVALID_OFFSET -9999
+
+/* Maximum burst length, whenever an operation is bursting longer than
+ * this value, JB will assume that the opposite operation was idle.
+ */
+#define MAX_BURST_MSEC 1000
+
+/* Number of OP switches to be performed in JB_STATUS_INITIALIZING, before
+ * JB can switch its states to JB_STATUS_PROCESSING.
+ */
+#define INIT_CYCLE 10
+
+
+/* Struct of JB internal buffer, represented in a circular buffer containing
+ * frame content, frame type, frame length, and frame bit info.
+ */
typedef struct jb_framelist_t
{
- char *flist_buffer;
- int *flist_frame_type;
- pj_size_t *flist_content_len;
- pj_uint32_t *flist_bit_info;
- unsigned flist_frame_size;
- unsigned flist_max_count;
- unsigned flist_empty;
- unsigned flist_head;
- unsigned flist_tail;
- unsigned flist_origin;
+ /* Settings */
+ unsigned frame_size; /**< maximum size of frame */
+ unsigned max_count; /**< maximum number of frames */
+
+ /* Buffers */
+ char *content; /**< frame content array */
+ int *frame_type; /**< frame type array */
+ pj_size_t *content_len; /**< frame length array */
+ pj_uint32_t *bit_info; /**< frame bit info array */
+
+ /* States */
+ unsigned head; /**< index of head, pointed frame
+ will be returned by next GET */
+ unsigned size; /**< current size of framelist. */
+ int origin; /**< original index of flist_head */
} jb_framelist_t;
struct pjmedia_jbuf
{
- pj_str_t name; // jitter buffer name
- jb_framelist_t jb_framelist;
- pj_size_t jb_frame_size; // frame size
- unsigned jb_frame_ptime; // frame duration.
- pj_size_t jb_max_count; // max frames in the jitter framelist->flist_buffer
-
- int jb_level; // delay between source & destination
- // (calculated according of the number of get/put operations)
- int jb_max_hist_level; // max level during the last level calculations
- int jb_stable_hist; // num of times the delay has been lower then the prefetch num
- int jb_last_op; // last operation executed on the framelist->flist_buffer (put/get)
- int jb_last_seq_no; // seq no. of the last frame inserted to the framelist->flist_buffer
- int jb_prefetch; // no. of frame to insert before removing some
- // (at the beginning of the framelist->flist_buffer operation)
- int jb_prefetch_cnt; // prefetch counter
- int jb_def_prefetch; // Default prefetch
- int jb_min_prefetch; // Minimum allowable prefetch
- int jb_max_prefetch; // Maximum allowable prefetch
- int jb_status; // status is 'init' until the first 'put' operation
- pj_math_stat jb_delay; // Delay statistics of jitter buffer (in frame unit)
-
- unsigned jb_last_del_seq; // Seq # of last frame deleted
- unsigned jb_min_shrink_gap; // How often can we shrink
+ /* Settings (consts) */
+ pj_str_t jb_name; /**< jitter buffer name */
+ pj_size_t jb_frame_size; /**< frame size */
+ unsigned jb_frame_ptime; /**< frame duration. */
+ pj_size_t jb_max_count; /**< capacity of jitter buffer,
+ in frames */
+ int jb_def_prefetch; /**< Default prefetch */
+ int jb_min_prefetch; /**< Minimum allowable prefetch */
+ int jb_max_prefetch; /**< Maximum allowable prefetch */
+ int jb_max_burst; /**< maximum possible burst, whenever
+ burst exceeds this value, it
+ won't be included in level
+ calculation */
+ int jb_min_shrink_gap; /**< How often can we shrink */
+
+ /* Buffer */
+ jb_framelist_t jb_framelist; /**< the buffer */
+
+ /* States */
+ int jb_level; /**< delay between source &
+ destination (calculated according
+ of the number of burst get/put
+ operations) */
+ int jb_max_hist_level; /**< max level during the last level
+ calculations */
+ int jb_stable_hist; /**< num of times the delay has been
+ lower then the prefetch num */
+ int jb_last_op; /**< last operation executed
+ (put/get) */
+ int jb_prefetch; /**< no. of frame to insert before
+ removing some (at the beginning
+ of the framelist->content
+ operation), the value may be
+ continuously updated based on
+ current frame burst level. */
+ int jb_status; /**< status is 'init' until the first
+ 'put' operation */
+ int jb_init_cycle_cnt; /**< status is 'init' until the first
+ 'put' operation */
+ int jb_last_del_seq; /**< Seq # of last frame deleted */
+
+ /* Statistics */
+ pj_math_stat jb_delay; /**< Delay statistics of jitter buffer
+ (in ms) */
+ pj_math_stat jb_burst; /**< Burst statistics (in frames) */
+ unsigned jb_lost; /**< Number of lost frames. */
+ unsigned jb_discard; /**< Number of discarded frames. */
+ unsigned jb_empty; /**< Number of empty/prefetching frame
+ returned by GET. */
};
@@ -90,6 +144,7 @@ struct pjmedia_jbuf
# define TRACE__(args)
#endif
+static pj_status_t jb_framelist_reset(jb_framelist_t *framelist);
static pj_status_t jb_framelist_init( pj_pool_t *pool,
jb_framelist_t *framelist,
@@ -100,46 +155,70 @@ static pj_status_t jb_framelist_init( pj_pool_t *pool,
pj_bzero(framelist, sizeof(jb_framelist_t));
- framelist->flist_frame_size = frame_size;
- framelist->flist_max_count = max_count;
- framelist->flist_buffer = (char*)
- pj_pool_zalloc(pool,
- framelist->flist_frame_size *
- framelist->flist_max_count);
+ framelist->frame_size = frame_size;
+ framelist->max_count = max_count;
+ framelist->content = (char*)
+ pj_pool_alloc(pool,
+ framelist->frame_size*
+ framelist->max_count);
+ framelist->frame_type = (int*)
+ pj_pool_alloc(pool,
+ sizeof(framelist->frame_type[0])*
+ framelist->max_count);
+ framelist->content_len = (pj_size_t*)
+ pj_pool_alloc(pool,
+ sizeof(framelist->content_len[0])*
+ framelist->max_count);
+ framelist->bit_info = (pj_uint32_t*)
+ pj_pool_alloc(pool,
+ sizeof(framelist->bit_info[0])*
+ framelist->max_count);
+
+ return jb_framelist_reset(framelist);
- framelist->flist_frame_type = (int*)
- pj_pool_zalloc(pool, sizeof(framelist->flist_frame_type[0]) *
- framelist->flist_max_count);
+}
- framelist->flist_content_len = (pj_size_t*)
- pj_pool_zalloc(pool, sizeof(framelist->flist_content_len[0]) *
- framelist->flist_max_count);
+static pj_status_t jb_framelist_destroy(jb_framelist_t *framelist)
+{
+ PJ_UNUSED_ARG(framelist);
+ return PJ_SUCCESS;
+}
- framelist->flist_bit_info = (pj_uint32_t*)
- pj_pool_zalloc(pool, sizeof(framelist->flist_bit_info[0]) *
- framelist->flist_max_count);
+static pj_status_t jb_framelist_reset(jb_framelist_t *framelist)
+{
+ framelist->head = 0;
+ framelist->origin = INVALID_OFFSET;
+ framelist->size = 0;
- framelist->flist_empty = 1;
+ //pj_bzero(framelist->content,
+ // framelist->frame_size *
+ // framelist->max_count);
- return PJ_SUCCESS;
+ pj_memset(framelist->frame_type,
+ PJMEDIA_JB_MISSING_FRAME,
+ sizeof(framelist->frame_type[0]) *
+ framelist->max_count);
-}
+ pj_bzero(framelist->content_len,
+ sizeof(framelist->content_len[0]) *
+ framelist->max_count);
+
+ //pj_bzero(framelist->bit_info,
+ // sizeof(framelist->bit_info[0]) *
+ // framelist->max_count);
-static pj_status_t jb_framelist_destroy(jb_framelist_t *framelist)
-{
- PJ_UNUSED_ARG(framelist);
return PJ_SUCCESS;
}
static unsigned jb_framelist_size(jb_framelist_t *framelist)
{
- if (framelist->flist_tail == framelist->flist_head) {
- return framelist->flist_empty ? 0 : framelist->flist_max_count;
- } else {
- return (framelist->flist_tail - framelist->flist_head +
- framelist->flist_max_count) % framelist->flist_max_count;
- }
+ return framelist->size;
+}
+
+static unsigned jb_framelist_origin(jb_framelist_t *framelist)
+{
+ return framelist->origin;
}
@@ -148,147 +227,145 @@ static pj_bool_t jb_framelist_get(jb_framelist_t *framelist,
pjmedia_jb_frame_type *p_type,
pj_uint32_t *bit_info)
{
- if (!framelist->flist_empty) {
+ if (framelist->size) {
pj_memcpy(frame,
- framelist->flist_buffer +
- framelist->flist_head * framelist->flist_frame_size,
- framelist->flist_frame_size);
+ framelist->content +
+ framelist->head * framelist->frame_size,
+ framelist->frame_size);
*p_type = (pjmedia_jb_frame_type)
- framelist->flist_frame_type[framelist->flist_head];
+ framelist->frame_type[framelist->head];
if (size)
- *size = framelist->flist_content_len[framelist->flist_head];
+ *size = framelist->content_len[framelist->head];
if (bit_info)
- *bit_info = framelist->flist_bit_info[framelist->flist_head];
-
- pj_bzero(framelist->flist_buffer +
- framelist->flist_head * framelist->flist_frame_size,
- framelist->flist_frame_size);
- framelist->flist_frame_type[framelist->flist_head] =
- PJMEDIA_JB_MISSING_FRAME;
- framelist->flist_content_len[framelist->flist_head] = 0;
-
- framelist->flist_origin++;
- framelist->flist_head = (framelist->flist_head + 1 ) %
- framelist->flist_max_count;
- if (framelist->flist_head == framelist->flist_tail)
- framelist->flist_empty = PJ_TRUE;
+ *bit_info = framelist->bit_info[framelist->head];
+
+ //pj_bzero(framelist->content +
+ // framelist->head * framelist->frame_size,
+ // framelist->frame_size);
+ framelist->frame_type[framelist->head] = PJMEDIA_JB_MISSING_FRAME;
+ framelist->content_len[framelist->head] = 0;
+ framelist->bit_info[framelist->head] = 0;
+
+ framelist->origin++;
+ framelist->head = (framelist->head + 1) % framelist->max_count;
+ framelist->size--;
return PJ_TRUE;
-
} else {
- pj_bzero(frame, framelist->flist_frame_size);
+ pj_bzero(frame, framelist->frame_size);
+
return PJ_FALSE;
}
}
-static void jb_framelist_remove_head( jb_framelist_t *framelist,
- unsigned count)
+static unsigned jb_framelist_remove_head(jb_framelist_t *framelist,
+ unsigned count)
{
- unsigned cur_size;
-
- cur_size = jb_framelist_size(framelist);
- if (count > cur_size)
- count = cur_size;
+ if (count > framelist->size)
+ count = framelist->size;
if (count) {
- // may be done in two steps if overlapping
+ /* may be done in two steps if overlapping */
unsigned step1,step2;
- unsigned tmp = framelist->flist_head+count;
+ unsigned tmp = framelist->head+count;
- if (tmp > framelist->flist_max_count) {
- step1 = framelist->flist_max_count - framelist->flist_head;
+ if (tmp > framelist->max_count) {
+ step1 = framelist->max_count - framelist->head;
step2 = count-step1;
} else {
step1 = count;
step2 = 0;
}
- pj_bzero(framelist->flist_buffer +
- framelist->flist_head * framelist->flist_frame_size,
- step1*framelist->flist_frame_size);
- pj_memset(framelist->flist_frame_type+framelist->flist_head,
+ //pj_bzero(framelist->content +
+ // framelist->head * framelist->frame_size,
+ // step1*framelist->frame_size);
+ pj_memset(framelist->frame_type+framelist->head,
PJMEDIA_JB_MISSING_FRAME,
- step1*sizeof(framelist->flist_frame_type[0]));
- pj_bzero(framelist->flist_content_len+framelist->flist_head,
- step1*sizeof(framelist->flist_content_len[0]));
+ step1*sizeof(framelist->frame_type[0]));
+ pj_bzero(framelist->content_len+framelist->head,
+ step1*sizeof(framelist->content_len[0]));
if (step2) {
- pj_bzero( framelist->flist_buffer,
- step2*framelist->flist_frame_size);
- pj_memset(framelist->flist_frame_type,
+ //pj_bzero( framelist->content,
+ // step2*framelist->frame_size);
+ pj_memset(framelist->frame_type,
PJMEDIA_JB_MISSING_FRAME,
- step2*sizeof(framelist->flist_frame_type[0]));
- pj_bzero (framelist->flist_content_len,
- step2*sizeof(framelist->flist_content_len[0]));
+ step2*sizeof(framelist->frame_type[0]));
+ pj_bzero (framelist->content_len,
+ step2*sizeof(framelist->content_len[0]));
}
- // update pointers
- framelist->flist_origin += count;
- framelist->flist_head = (framelist->flist_head + count) %
- framelist->flist_max_count;
- if (framelist->flist_head == framelist->flist_tail)
- framelist->flist_empty = PJ_TRUE;
+ /* update states */
+ framelist->origin += count;
+ framelist->head = (framelist->head + count) % framelist->max_count;
+ framelist->size -= count;
}
+
+ return count;
}
-static pj_bool_t jb_framelist_put_at(jb_framelist_t *framelist,
- unsigned index,
- const void *frame,
- unsigned frame_size,
- pj_uint32_t bit_info)
+static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist,
+ int index,
+ const void *frame,
+ unsigned frame_size,
+ pj_uint32_t bit_info)
{
+ int distance;
unsigned where;
+ enum { MAX_MISORDER = 100 };
+ enum { MAX_DROPOUT = 3000 };
- assert(frame_size <= framelist->flist_frame_size);
+ assert(frame_size <= framelist->frame_size);
- if (!framelist->flist_empty) {
- unsigned max_index;
- unsigned cur_size;
+ /* the first ever frame since inited/resetted. */
+ if (framelist->origin == INVALID_OFFSET)
+ framelist->origin = index;
- // too late
- if (index < framelist->flist_origin)
- return PJ_FALSE;
-
- // too soon
- max_index = framelist->flist_origin + framelist->flist_max_count - 1;
- if (index > max_index)
- return PJ_FALSE;
-
- where = (index - framelist->flist_origin + framelist->flist_head) %
- framelist->flist_max_count;
-
- // update framelist->flist_tail pointer
- cur_size = jb_framelist_size(framelist);
- if (index >= framelist->flist_origin + cur_size) {
- unsigned diff = (index - (framelist->flist_origin + cur_size));
- framelist->flist_tail = (framelist->flist_tail + diff + 1) %
- framelist->flist_max_count;
- }
- } else {
- // check if frame is not too late, but watch out for sequence restart.
- if (index < framelist->flist_origin &&
- framelist->flist_origin - index < 0x7FFF)
- {
- return PJ_FALSE;
+ /* too late or duplicated or sequence restart */
+ if (index < framelist->origin) {
+ if (framelist->origin - index < MAX_MISORDER) {
+ /* too late or duplicated */
+ return PJ_ETOOSMALL;
+ } else {
+ /* sequence restart */
+ framelist->origin = index - framelist->size;
}
+ }
- where = framelist->flist_tail;
- framelist->flist_origin = index;
- framelist->flist_tail = (framelist->flist_tail + 1) %
- framelist->flist_max_count;
- framelist->flist_empty = PJ_FALSE;
+ /* too soon or jump too far */
+ distance = index - framelist->origin;
+ if (distance >= (int)framelist->max_count) {
+ if (distance > MAX_DROPOUT) {
+ /* jump too far */
+ jb_framelist_reset(framelist);
+ framelist->origin = index;
+ distance = 0;
+ } else {
+ /* too soon, buffer is not enough */
+ return PJ_ETOOMANY;
+ }
}
- pj_memcpy(framelist->flist_buffer + where * framelist->flist_frame_size,
- frame, frame_size);
+ /* get the slot position */
+ where = (framelist->head + distance) % framelist->max_count;
- framelist->flist_frame_type[where] = PJMEDIA_JB_NORMAL_FRAME;
- framelist->flist_content_len[where] = frame_size;
- framelist->flist_bit_info[where] = bit_info;
+ /* if the slot is occupied, it must be duplicated frame, ignore it. */
+ if (framelist->frame_type[where] != PJMEDIA_JB_MISSING_FRAME)
+ return PJ_EEXISTS;
- return PJ_TRUE;
+ /* put the frame into the slot */
+ pj_memcpy(framelist->content + where * framelist->frame_size,
+ frame, frame_size);
+ framelist->frame_type[where] = PJMEDIA_JB_NORMAL_FRAME;
+ framelist->content_len[where] = frame_size;
+ framelist->bit_info[where] = bit_info;
+ if (framelist->origin + (int)framelist->size <= index)
+ framelist->size = distance + 1;
+
+ return PJ_SUCCESS;
}
@@ -317,23 +394,19 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool,
if (status != PJ_SUCCESS)
return status;
- pj_strdup_with_null(pool, &jb->name, name);
+ pj_strdup_with_null(pool, &jb->jb_name, name);
jb->jb_frame_size = frame_size;
jb->jb_frame_ptime = ptime;
- jb->jb_last_seq_no = -1;
- jb->jb_level = 0;
- jb->jb_last_op = JB_OP_INIT;
jb->jb_prefetch = PJ_MIN(PJMEDIA_JB_DEFAULT_INIT_DELAY,max_count*4/5);
- jb->jb_prefetch_cnt = 0;
jb->jb_min_prefetch = 0;
jb->jb_max_prefetch = max_count*4/5;
- jb->jb_stable_hist = 0;
- jb->jb_status = JB_STATUS_INITIALIZING;
- jb->jb_max_hist_level = 0;
jb->jb_max_count = max_count;
jb->jb_min_shrink_gap= MIN_SHRINK_GAP_MSEC / ptime;
-
+ jb->jb_max_burst = MAX_BURST_MSEC / ptime;
pj_math_stat_init(&jb->jb_delay);
+ pj_math_stat_init(&jb->jb_burst);
+
+ pjmedia_jbuf_reset(jb);
*p_jb = jb;
return PJ_SUCCESS;
@@ -382,19 +455,15 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb,
PJ_DEF(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb)
{
- jb->jb_last_seq_no = -1;
jb->jb_level = 0;
jb->jb_last_op = JB_OP_INIT;
- jb->jb_prefetch_cnt = 0;
jb->jb_stable_hist = 0;
jb->jb_status = JB_STATUS_INITIALIZING;
- jb->jb_max_hist_level = 0;
+ jb->jb_init_cycle_cnt= 0;
+ jb->jb_max_hist_level= 0;
- jb_framelist_remove_head(&jb->jb_framelist,
- jb_framelist_size(&jb->jb_framelist));
+ jb_framelist_reset(&jb->jb_framelist);
- pj_math_stat_init(&jb->jb_delay);
-
return PJ_SUCCESS;
}
@@ -410,99 +479,135 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb)
int diff, cur_size;
cur_size = jb_framelist_size(&jb->jb_framelist);
+ pj_math_stat_update(&jb->jb_burst, jb->jb_level);
+ jb->jb_max_hist_level = PJ_MAX(jb->jb_max_hist_level, jb->jb_level);
+
+ /* Burst level is decreasing */
+ if (jb->jb_level < jb->jb_prefetch) {
+
+ enum { STABLE_HISTORY_LIMIT = 100 };
+
+ jb->jb_stable_hist++;
+
+ /* Only update the prefetch if 'stable' condition is reached
+ * (not just short time impulse)
+ */
+ if (jb->jb_stable_hist > STABLE_HISTORY_LIMIT) {
+
+ diff = (jb->jb_prefetch - jb->jb_max_hist_level) / 3;
- /* Only apply burst-level calculation on PUT operation since if VAD is
- * active the burst-level may not be accurate.
- */
- if (jb->jb_last_op == JB_OP_PUT) {
+ if (diff < 1)
+ diff = 1;
- jb->jb_max_hist_level = PJ_MAX(jb->jb_max_hist_level,jb->jb_level);
+ jb->jb_prefetch -= diff;
+ if (jb->jb_prefetch < jb->jb_min_prefetch)
+ jb->jb_prefetch = jb->jb_min_prefetch;
- /* Level is decreasing */
- if (jb->jb_level < jb->jb_prefetch) {
+ /* Reset history */
+ jb->jb_max_hist_level = 0;
+ jb->jb_stable_hist = 0;
- enum { STABLE_HISTORY_LIMIT = 100 };
-
- jb->jb_stable_hist++;
-
- /* Only update the prefetch if 'stable' condition is reached
- * (not just short time impulse)
- */
- if (jb->jb_stable_hist > STABLE_HISTORY_LIMIT) {
-
- diff = (jb->jb_prefetch - jb->jb_max_hist_level) / 3;
+ TRACE__((jb->jb_name.ptr,"jb updated(1), prefetch=%d, size=%d",
+ jb->jb_prefetch, cur_size));
+ }
+ }
+
+ /* Burst level is increasing */
+ else if (jb->jb_level > jb->jb_prefetch) {
+
+ /* Instaneous set prefetch to recent maximum level (max_hist_level) */
+ jb->jb_prefetch = PJ_MIN(jb->jb_max_hist_level,
+ (int)(jb->jb_max_count*4/5));
+ if (jb->jb_prefetch > jb->jb_max_prefetch)
+ jb->jb_prefetch = jb->jb_max_prefetch;
+
+ jb->jb_stable_hist = 0;
+ /* Do not reset max_hist_level. */
+ //jb->jb_max_hist_level = 0;
- if (diff < 1)
- diff = 1;
+ TRACE__((jb->jb_name.ptr,"jb updated(2), prefetch=%d, size=%d",
+ jb->jb_prefetch, cur_size));
+ }
- /* Update max_hist_level. */
- jb->jb_max_hist_level = jb->jb_prefetch;
+ /* Level is unchanged */
+ else {
+ jb->jb_stable_hist = 0;
+ }
+}
- jb->jb_prefetch -= diff;
- if (jb->jb_prefetch < jb->jb_min_prefetch)
- jb->jb_prefetch = jb->jb_min_prefetch;
+PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper)
+{
+ int diff, burst_level;
- jb->jb_stable_hist = 0;
+ if(jb->jb_last_op != oper) {
+ jb->jb_last_op = oper;
- TRACE__((jb->name.ptr,"jb updated(1), prefetch=%d, size=%d",
- jb->jb_prefetch, cur_size));
+ if (jb->jb_status == JB_STATUS_INITIALIZING) {
+ /* Switch status 'initializing' -> 'processing' after some OP
+ * switch cycles and current OP is GET (burst level is calculated
+ * based on PUT burst), so burst calculation is guaranted to be
+ * performed right after the status switching.
+ */
+ if (++jb->jb_init_cycle_cnt >= INIT_CYCLE && oper == JB_OP_GET) {
+ jb->jb_status = JB_STATUS_PROCESSING;
+ } else {
+ jb->jb_level = 0;
+ return;
}
}
- /* Level is increasing */
- else if (jb->jb_level > jb->jb_prefetch) {
+ /* Perform jitter calculation based on PUT burst-level only, since
+ * GET burst-level may not be accurate, e.g: when VAD is active.
+ * Note that when burst-level is too big, i.e: exceeds jb_max_burst,
+ * the GET op may be idle, in this case, we better skip the jitter
+ * calculation.
+ */
+ if (oper == JB_OP_GET && jb->jb_level < jb->jb_max_burst)
+ jbuf_calculate_jitter(jb);
- /* Instaneous set prefetch */
- jb->jb_prefetch = PJ_MIN(jb->jb_max_hist_level,
- (int)(jb->jb_max_count*4/5));
- if (jb->jb_prefetch > jb->jb_max_prefetch)
- jb->jb_prefetch = jb->jb_max_prefetch;
+ jb->jb_level = 0;
+ }
- jb->jb_stable_hist = 0;
- // Keep max_hist_level.
- //jb->jb_max_hist_level = 0;
+ /* These code is used for shortening the delay in the jitter buffer.
+ * It needs shrink only when there is possibility of drift. Drift
+ * detection is performed by inspecting the jitter buffer size, if
+ * its size is twice of current burst level, there can be drift.
+ *
+ * Moreover, normally drift level is quite low, so JB shouldn't need
+ * to shrink aggresively, it will shrink maximum one frame per
+ * MIN_SHRINK_GAP_MSEC ms. Theoritically, JB may handle drift level
+ * as much as = FRAME_PTIME/MIN_SHRINK_GAP_MSEC * 100%
+ *
+ * Whenever there is drift, where PUT > GET, this method will keep
+ * the latency (JB size) as much as twice of burst level.
+ */
- TRACE__((jb->name.ptr,"jb updated(2), prefetch=%d, size=%d",
- jb->jb_prefetch, cur_size));
- }
+ if (jb->jb_status != JB_STATUS_PROCESSING)
+ return;
- /* Level is unchanged */
- else {
- jb->jb_stable_hist = 0;
- }
- }
+ burst_level = PJ_MAX(jb->jb_prefetch, jb->jb_level);
+ diff = jb_framelist_size(&jb->jb_framelist) - burst_level*2;
- /* These code is used for shortening the delay in the jitter buffer. */
- // Shrinking based on max_hist_level (recent max level).
- //diff = cur_size - jb->jb_prefetch;
- diff = cur_size - jb->jb_max_hist_level;
- if (diff > SAFE_SHRINKING_DIFF &&
- jb->jb_framelist.flist_origin-jb->jb_last_del_seq > jb->jb_min_shrink_gap)
- {
- /* Shrink slowly */
- diff = 1;
-
- /* Drop frame(s)! */
- jb_framelist_remove_head(&jb->jb_framelist, diff);
- jb->jb_last_del_seq = jb->jb_framelist.flist_origin;
-
- pj_math_stat_update(&jb->jb_delay, cur_size - diff);
-
- TRACE__((jb->name.ptr,
- "JB shrinking %d frame(s), size=%d", diff,
- jb_framelist_size(&jb->jb_framelist)));
- } else {
- pj_math_stat_update(&jb->jb_delay, cur_size);
- }
+ if (diff >= SAFE_SHRINKING_DIFF) {
+ /* Check and adjust jb_last_del_seq, in case there was seq restart */
+ if (jb->jb_framelist.origin < jb->jb_last_del_seq)
+ jb->jb_last_del_seq = jb->jb_framelist.origin;
- jb->jb_level = 0;
-}
+ if (jb->jb_framelist.origin - jb->jb_last_del_seq >=
+ jb->jb_min_shrink_gap)
+ {
+ /* Shrink slowly, one frame per cycle */
+ diff = 1;
-PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper)
-{
- if(jb->jb_last_op != oper) {
- jbuf_calculate_jitter(jb);
- jb->jb_last_op = oper;
+ /* Drop frame(s)! */
+ diff = jb_framelist_remove_head(&jb->jb_framelist, diff);
+ jb->jb_last_del_seq = jb->jb_framelist.origin;
+ jb->jb_discard += diff;
+
+ TRACE__((jb->jb_name.ptr,
+ "JB shrinking %d frame(s), cur size=%d", diff,
+ jb_framelist_size(&jb->jb_framelist)));
+ }
}
}
@@ -522,59 +627,47 @@ PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb,
pj_bool_t *discarded)
{
pj_size_t min_frame_size;
- int seq_diff;
+ int prev_size, cur_size;
+ pj_status_t status;
- if (jb->jb_last_seq_no == -1) {
- jb->jb_last_seq_no = frame_seq - 1;
- }
+ /* Get JB size before PUT */
+ prev_size = jb_framelist_size(&jb->jb_framelist);
+
+ /* Attempt to store the frame */
+ min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size);
+ status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
+ min_frame_size, bit_info);
+
+ /* Jitter buffer is full, cannot store the frame */
+ while (status == PJ_ETOOMANY) {
+ unsigned removed;
- seq_diff = frame_seq - jb->jb_last_seq_no;
- jb->jb_last_seq_no = PJ_MAX(jb->jb_last_seq_no, frame_seq);
- if (seq_diff > 0) jb->jb_level += seq_diff;
+ removed = jb_framelist_remove_head(&jb->jb_framelist,
+ PJ_MAX(jb->jb_max_count/4, 1));
+ status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
+ min_frame_size, bit_info);
- if(jb->jb_status == JB_STATUS_INITIALIZING) {
- jb->jb_status = JB_STATUS_PROCESSING;
- jb->jb_level = 0;
- } else {
- jbuf_update(jb, JB_OP_PUT);
+ jb->jb_discard += removed;
}
- min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size);
- if (seq_diff > 0) {
-
- while (jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
- min_frame_size, bit_info) == PJ_FALSE)
- {
- jb_framelist_remove_head(&jb->jb_framelist,
- PJ_MAX(jb->jb_max_count/4,1) );
- }
+ /* Get JB size after PUT */
+ cur_size = jb_framelist_size(&jb->jb_framelist);
- if (jb->jb_prefetch_cnt < jb->jb_prefetch) {
- jb->jb_prefetch_cnt += seq_diff;
-
- TRACE__((jb->name.ptr, "PUT prefetch_cnt=%d/%d",
- jb->jb_prefetch_cnt, jb->jb_prefetch));
+ /* Return the flag if this frame is discarded */
+ if (discarded)
+ *discarded = (status != PJ_SUCCESS);
- if (jb->jb_status == JB_STATUS_PREFETCHING &&
- jb->jb_prefetch_cnt >= jb->jb_prefetch)
- {
+ if (status == PJ_SUCCESS) {
+ if (jb->jb_status == JB_STATUS_PREFETCHING) {
+ TRACE__((jb->jb_name.ptr, "PUT prefetch_cnt=%d/%d",
+ cur_size, jb->jb_prefetch));
+ if (cur_size >= jb->jb_prefetch)
jb->jb_status = JB_STATUS_PROCESSING;
- }
}
-
-
-
- if (discarded)
- *discarded = PJ_FALSE;
- }
- else
- {
- pj_bool_t res;
- res = jb_framelist_put_at(&jb->jb_framelist,frame_seq,frame,
- min_frame_size, bit_info);
- if (discarded)
- *discarded = !res;
- }
+ jb->jb_level += (cur_size > prev_size ? cur_size-prev_size : 1);
+ jbuf_update(jb, JB_OP_PUT);
+ } else
+ jb->jb_discard++;
}
/*
@@ -596,58 +689,63 @@ PJ_DEF(void) pjmedia_jbuf_get_frame2(pjmedia_jbuf *jb,
char *p_frame_type,
pj_uint32_t *bit_info)
{
- pjmedia_jb_frame_type ftype;
+ int cur_size;
- jb->jb_level++;
+ cur_size = jb_framelist_size(&jb->jb_framelist);
- jbuf_update(jb, JB_OP_GET);
+ if (cur_size == 0) {
+ /* jitter buffer empty */
- if (jb_framelist_size(&jb->jb_framelist) == 0) {
- jb->jb_prefetch_cnt = 0;
if (jb->jb_def_prefetch)
jb->jb_status = JB_STATUS_PREFETCHING;
- }
- if (jb->jb_status == JB_STATUS_PREFETCHING &&
- jb->jb_prefetch_cnt < jb->jb_prefetch)
- {
+ //pj_bzero(frame, jb->jb_frame_size);
+ *p_frame_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
+ if (size)
+ *size = 0;
+
+ jb->jb_empty++;
+
+ } else if (jb->jb_status == JB_STATUS_PREFETCHING) {
+
/* Can't return frame because jitter buffer is filling up
* minimum prefetch.
*/
- pj_bzero(frame, jb->jb_frame_size);
- if (jb_framelist_size(&jb->jb_framelist) == 0)
- *p_frame_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
- else
- *p_frame_type = PJMEDIA_JB_ZERO_PREFETCH_FRAME;
+ //pj_bzero(frame, jb->jb_frame_size);
+ *p_frame_type = PJMEDIA_JB_ZERO_PREFETCH_FRAME;
if (size)
*size = 0;
- TRACE__((jb->name.ptr, "GET prefetch_cnt=%d/%d",
- jb->jb_prefetch_cnt, jb->jb_prefetch));
- return;
- }
+ TRACE__((jb->jb_name.ptr, "GET prefetch_cnt=%d/%d",
+ cur_size, jb->jb_prefetch));
- /* Retrieve a frame from frame list */
- if (jb_framelist_get(&jb->jb_framelist,frame,size,&ftype,bit_info) ==
- PJ_FALSE)
- {
- /* Can't return frame because jitter buffer is empty! */
- pj_bzero(frame, jb->jb_frame_size);
- *p_frame_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
- if (size)
- *size = 0;
+ jb->jb_empty++;
- return;
+ } else {
+
+ pjmedia_jb_frame_type ftype;
+ pj_bool_t res;
+
+ /* Retrieve a frame from frame list */
+ res = jb_framelist_get(&jb->jb_framelist, frame, size, &ftype,
+ bit_info);
+ pj_assert(res);
+
+ /* We've successfully retrieved a frame from the frame list, but
+ * the frame could be a blank frame!
+ */
+ if (ftype == PJMEDIA_JB_NORMAL_FRAME) {
+ *p_frame_type = PJMEDIA_JB_NORMAL_FRAME;
+ pj_math_stat_update(&jb->jb_delay, cur_size * jb->jb_frame_ptime);
+ } else {
+ *p_frame_type = PJMEDIA_JB_MISSING_FRAME;
+ jb->jb_lost++;
+ }
}
- /* We've successfully retrieved a frame from the frame list, but
- * the frame could be a blank frame!
- */
- if (ftype == PJMEDIA_JB_NORMAL_FRAME)
- *p_frame_type = PJMEDIA_JB_NORMAL_FRAME;
- else
- *p_frame_type = PJMEDIA_JB_MISSING_FRAME;
+ jb->jb_level++;
+ jbuf_update(jb, JB_OP_GET);
}
/*
@@ -659,15 +757,21 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_get_state( pjmedia_jbuf *jb,
PJ_ASSERT_RETURN(jb && state, PJ_EINVAL);
state->frame_size = jb->jb_frame_size;
- state->prefetch = jb->jb_prefetch;
state->min_prefetch = jb->jb_min_prefetch;
state->max_prefetch = jb->jb_max_prefetch;
+
+ state->prefetch = jb->jb_prefetch;
state->size = jb_framelist_size(&jb->jb_framelist);
- state->avg_delay = jb->jb_delay.mean * jb->jb_frame_ptime;
- state->min_delay = jb->jb_delay.min * jb->jb_frame_ptime;
- state->max_delay = jb->jb_delay.max * jb->jb_frame_ptime;
- state->dev_delay = pj_math_stat_get_stddev(&jb->jb_delay) *
- jb->jb_frame_ptime;
+
+ state->avg_delay = jb->jb_delay.mean;
+ state->min_delay = jb->jb_delay.min;
+ state->max_delay = jb->jb_delay.max;
+ state->dev_delay = pj_math_stat_get_stddev(&jb->jb_delay);
+
+ state->avg_burst = jb->jb_burst.mean;
+ state->empty = jb->jb_empty;
+ state->discard = jb->jb_discard;
+ state->lost = jb->jb_lost;
return PJ_SUCCESS;
}