From bbdeaae143a7707d8dcf8a5ff77f06d342ea1393 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 6 Apr 2009 17:05:34 +0000 Subject: 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 --- pjmedia/src/pjmedia/jbuf.c | 768 +++++++++++++++++++++++++-------------------- 1 file changed, 436 insertions(+), 332 deletions(-) (limited to 'pjmedia/src') 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; } -- cgit v1.2.3