/* $Id$ */ /* * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Based on implementation kindly contributed by Switchlab, Ltd. */ #include #include #include #include #include #include #include #define THIS_FILE "jbuf.c" #define SAFE_SHRINKING_DIFF 1 #define MIN_SHRINK_GAP_MSEC 200 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; } 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 }; #define JB_STATUS_INITIALIZING 0 #define JB_STATUS_PROCESSING 1 #define JB_STATUS_PREFETCHING 2 /* Enabling this would log the jitter buffer state about once per * second. */ #if 1 # define TRACE__(args) PJ_LOG(5,args) #else # define TRACE__(args) #endif static pj_status_t jb_framelist_init( pj_pool_t *pool, jb_framelist_t *framelist, unsigned frame_size, unsigned max_count) { PJ_ASSERT_RETURN(pool && framelist, PJ_EINVAL); 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->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); framelist->flist_bit_info = (pj_uint32_t*) pj_pool_zalloc(pool, sizeof(framelist->flist_bit_info[0]) * framelist->flist_max_count); framelist->flist_empty = 1; return PJ_SUCCESS; } 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; } } static pj_bool_t jb_framelist_get(jb_framelist_t *framelist, void *frame, pj_size_t *size, pjmedia_jb_frame_type *p_type, pj_uint32_t *bit_info) { if (!framelist->flist_empty) { pj_memcpy(frame, framelist->flist_buffer + framelist->flist_head * framelist->flist_frame_size, framelist->flist_frame_size); *p_type = (pjmedia_jb_frame_type) framelist->flist_frame_type[framelist->flist_head]; if (size) *size = framelist->flist_content_len[framelist->flist_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; return PJ_TRUE; } else { pj_bzero(frame, framelist->flist_frame_size); return PJ_FALSE; } } static void 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) { // may be done in two steps if overlapping unsigned step1,step2; unsigned tmp = framelist->flist_head+count; if (tmp > framelist->flist_max_count) { step1 = framelist->flist_max_count - framelist->flist_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, 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])); if (step2) { pj_bzero( framelist->flist_buffer, step2*framelist->flist_frame_size); pj_memset(framelist->flist_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])); } // 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; } } 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) { unsigned where; assert(frame_size <= framelist->flist_frame_size); if (!framelist->flist_empty) { unsigned max_index; unsigned cur_size; // 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 { where = framelist->flist_tail; framelist->flist_origin = index; framelist->flist_tail = (framelist->flist_tail + 1) % framelist->flist_max_count; framelist->flist_empty = PJ_FALSE; } pj_memcpy(framelist->flist_buffer + where * framelist->flist_frame_size, frame, frame_size); framelist->flist_frame_type[where] = PJMEDIA_JB_NORMAL_FRAME; framelist->flist_content_len[where] = frame_size; framelist->flist_bit_info[where] = bit_info; return PJ_TRUE; } enum pjmedia_jb_op { JB_OP_INIT = -1, JB_OP_PUT = 1, JB_OP_GET = 2 }; PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, const pj_str_t *name, unsigned frame_size, unsigned ptime, unsigned max_count, pjmedia_jbuf **p_jb) { pjmedia_jbuf *jb; pj_status_t status; jb = PJ_POOL_ZALLOC_T(pool, pjmedia_jbuf); status = jb_framelist_init(pool, &jb->jb_framelist, frame_size, max_count); if (status != PJ_SUCCESS) return status; pj_strdup_with_null(pool, &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; pj_math_stat_init(&jb->jb_delay); *p_jb = jb; return PJ_SUCCESS; } /* * Set the jitter buffer to fixed delay mode. The default behavior * is to adapt the delay with actual packet delay. * */ PJ_DEF(pj_status_t) pjmedia_jbuf_set_fixed( pjmedia_jbuf *jb, unsigned prefetch) { PJ_ASSERT_RETURN(jb, PJ_EINVAL); PJ_ASSERT_RETURN(prefetch <= jb->jb_max_count, PJ_EINVAL); jb->jb_min_prefetch = jb->jb_max_prefetch = jb->jb_prefetch = jb->jb_def_prefetch = prefetch; return PJ_SUCCESS; } /* * Set the jitter buffer to adaptive mode. */ PJ_DEF(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb, unsigned prefetch, unsigned min_prefetch, unsigned max_prefetch) { PJ_ASSERT_RETURN(jb, PJ_EINVAL); PJ_ASSERT_RETURN(min_prefetch < max_prefetch && prefetch <= max_prefetch && max_prefetch <= jb->jb_max_count, PJ_EINVAL); jb->jb_prefetch = jb->jb_def_prefetch = prefetch; jb->jb_min_prefetch = min_prefetch; jb->jb_max_prefetch = max_prefetch; return PJ_SUCCESS; } 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_framelist_remove_head(&jb->jb_framelist, jb_framelist_size(&jb->jb_framelist)); pj_math_stat_init(&jb->jb_delay); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb) { return jb_framelist_destroy(&jb->jb_framelist); } static void jbuf_calculate_jitter(pjmedia_jbuf *jb) { int diff, cur_size; cur_size = jb_framelist_size(&jb->jb_framelist); /* 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) { jb->jb_max_hist_level = PJ_MAX(jb->jb_max_hist_level,jb->jb_level); /* 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; if (diff < 1) diff = 1; /* Update max_hist_level. */ jb->jb_max_hist_level = jb->jb_prefetch; jb->jb_prefetch -= diff; if (jb->jb_prefetch < jb->jb_min_prefetch) jb->jb_prefetch = jb->jb_min_prefetch; jb->jb_stable_hist = 0; TRACE__((jb->name.ptr,"jb updated(1), prefetch=%d, size=%d", jb->jb_prefetch, cur_size)); } } /* Level is increasing */ else if (jb->jb_level > jb->jb_prefetch) { /* 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_stable_hist = 0; // Keep max_hist_level. //jb->jb_max_hist_level = 0; TRACE__((jb->name.ptr,"jb updated(2), prefetch=%d, size=%d", jb->jb_prefetch, cur_size)); } /* Level is unchanged */ else { jb->jb_stable_hist = 0; } } /* 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); } jb->jb_level = 0; } 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; } } PJ_DEF(void) pjmedia_jbuf_put_frame( pjmedia_jbuf *jb, const void *frame, pj_size_t frame_size, int frame_seq) { pjmedia_jbuf_put_frame2(jb, frame, frame_size, 0, frame_seq, NULL); } PJ_DEF(void) 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) { pj_size_t min_frame_size; int seq_diff; if (jb->jb_last_seq_no == -1) { jb->jb_last_seq_no = frame_seq - 1; } 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; if(jb->jb_status == JB_STATUS_INITIALIZING) { jb->jb_status = JB_STATUS_PROCESSING; jb->jb_level = 0; } else { jbuf_update(jb, JB_OP_PUT); } 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) ); } 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)); if (jb->jb_status == JB_STATUS_PREFETCHING && jb->jb_prefetch_cnt >= 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; } } /* * Get frame from jitter buffer. */ PJ_DEF(void) pjmedia_jbuf_get_frame( pjmedia_jbuf *jb, void *frame, char *p_frame_type) { pjmedia_jbuf_get_frame2(jb, frame, NULL, p_frame_type, NULL); } /* * Get frame from jitter buffer. */ PJ_DEF(void) pjmedia_jbuf_get_frame2(pjmedia_jbuf *jb, void *frame, pj_size_t *size, char *p_frame_type, pj_uint32_t *bit_info) { pjmedia_jb_frame_type ftype; jb->jb_level++; jbuf_update(jb, JB_OP_GET); 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) { /* 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; if (size) *size = 0; TRACE__((jb->name.ptr, "GET prefetch_cnt=%d/%d", jb->jb_prefetch_cnt, jb->jb_prefetch)); return; } /* 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; return; } /* 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; } /* * Get jitter buffer state. */ PJ_DEF(pj_status_t) pjmedia_jbuf_get_state( pjmedia_jbuf *jb, pjmedia_jb_state *state ) { 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->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; return PJ_SUCCESS; }