summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2011-10-13 09:02:41 +0000
committerNanang Izzuddin <nanang@teluu.com>2011-10-13 09:02:41 +0000
commit2a7c23f6630314b2bee18066deb1442b8937671d (patch)
treebd967ca8c56af8585ff8cce3d33b7535bae4a8ee
parente9dc1709d61e89a6208680775c9345a69609ee1b (diff)
Re #1378:
- Implemented new algorithm for JB progressive discard. - Added new API and for setting JB discard algorithm at run-time. - Updated JB test for the new algorithm. git-svn-id: http://svn.pjsip.org/repos/pjproject/branches/1.x@3814 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/build/Jbtest.dat31
-rw-r--r--pjmedia/include/pjmedia/config.h59
-rw-r--r--pjmedia/include/pjmedia/jbuf.h63
-rw-r--r--pjmedia/src/pjmedia/jbuf.c355
-rw-r--r--pjmedia/src/test/jbuf_test.c11
5 files changed, 353 insertions, 166 deletions
diff --git a/pjmedia/build/Jbtest.dat b/pjmedia/build/Jbtest.dat
index e41f861a..4f5a67fd 100644
--- a/pjmedia/build/Jbtest.dat
+++ b/pjmedia/build/Jbtest.dat
@@ -19,11 +19,11 @@
#
# 3. Success conditions, started with '!', followed by condition name
# and its maximum tolerable value, in frames unit. Recognized condition
-# names are: burst, discard, lost, empty, delay. These conditions will
-# be verified with jitter buffer statistics after all session test data
-# are executed.
+# names are: burst, discard, lost, empty, delay, delay_min. These
+# conditions will be verified with jitter buffer statistics after all
+# session test data are executed.
# Example:
-# !delay 10 <- maximum average delay of jbuf is 10 frames
+# !delay 10 <- average delay of jbuf is 10 frames
#
# 4. Session test data, containing sequence of jitter buffer events,
# an event is represented by a character as follow:
@@ -295,7 +295,7 @@ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG
!discard 50 <- frames discarded for delay adaptation
!lost 50 <- ticket #1188, normal frame after discarded frame is flagged 'lost' to align signal
!empty 0
-!delay 25 <- average delay, JB is able to adapt the delay
+!delay_min 2 <- minimum delay, JB is able to adapt the delay
PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
@@ -311,6 +311,23 @@ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
+PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
.
= Fixed mode prefetch 5, with two empty events
@@ -349,7 +366,7 @@ PGGGGPPPGPPGPPPGGPGG PGPGPPGGPPGGPPPGGGPG PGGGGPPPGPPGPPPGGPGG
!discard 50 <- frames discarded for delay adaptation
!lost 50 <- ticket #1188, normal frame after discarded frame is flagged 'lost' to align signal
!empty 0
-!delay 20 <- average delay, twice of minimal prefetch
+!delay_min 20 <- minimum delay, twice of minimal prefetch
PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
@@ -369,7 +386,7 @@ PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG
= Large PUT burst at beginning, then normal with burst level 10 and periodic burst spikes
%adaptive 0 0 40
-!burst 10
+!burst 12
!discard 300 <- not so relevant for long period session with many delay adjustments needed (i.e: for first burst and periodic spikes)
!lost 300 <- ticket #1188, normal frame after discarded frame is flagged 'lost' to align signal
!empty 60 <- delay adjustment effect, as there is actually no drift
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index b0c3d6f7..c3ba7052 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -886,7 +886,64 @@
* Default: 5 seconds
*/
#ifndef PJMEDIA_STREAM_KA_INTERVAL
-# define PJMEDIA_STREAM_KA_INTERVAL 5
+# define PJMEDIA_STREAM_KA_INTERVAL 5
+#endif
+
+
+/**
+ * Minimum gap between two consecutive discards in jitter buffer,
+ * in milliseconds.
+ *
+ * Default: 200 ms
+ */
+#ifndef PJMEDIA_JBUF_DISC_MIN_GAP
+# define PJMEDIA_JBUF_DISC_MIN_GAP 200
+#endif
+
+
+/**
+ * Minimum burst level reference used for calculating discard duration
+ * in jitter buffer progressive discard algorithm, in frames.
+ *
+ * Default: 1 frame
+ */
+#ifndef PJMEDIA_JBUF_PRO_DISC_MIN_BURST
+# define PJMEDIA_JBUF_PRO_DISC_MIN_BURST 1
+#endif
+
+
+/**
+ * Maximum burst level reference used for calculating discard duration
+ * in jitter buffer progressive discard algorithm, in frames.
+ *
+ * Default: 200 frames
+ */
+#ifndef PJMEDIA_JBUF_PRO_DISC_MAX_BURST
+# define PJMEDIA_JBUF_PRO_DISC_MAX_BURST 100
+#endif
+
+
+/**
+ * Duration for progressive discard algotithm in jitter buffer to discard
+ * an excessive frame when burst is equal to or lower than
+ * PJMEDIA_JBUF_PRO_DISC_MIN_BURST, in milliseconds.
+ *
+ * Default: 2000 ms
+ */
+#ifndef PJMEDIA_JBUF_PRO_DISC_T1
+# define PJMEDIA_JBUF_PRO_DISC_T1 2000
+#endif
+
+
+/**
+ * Duration for progressive discard algotithm in jitter buffer to discard
+ * an excessive frame when burst is equal to or lower than
+ * PJMEDIA_JBUF_PRO_DISC_MAX_BURST, in milliseconds.
+ *
+ * Default: 10000 ms
+ */
+#ifndef PJMEDIA_JBUF_PRO_DISC_T2
+# define PJMEDIA_JBUF_PRO_DISC_T2 10000
#endif
diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h
index 18576ef9..f9468b72 100644
--- a/pjmedia/include/pjmedia/jbuf.h
+++ b/pjmedia/include/pjmedia/jbuf.h
@@ -48,7 +48,7 @@ PJ_BEGIN_DECL
/**
* Types of frame returned by the jitter buffer.
*/
-enum pjmedia_jb_frame_type
+typedef enum pjmedia_jb_frame_type
{
PJMEDIA_JB_MISSING_FRAME = 0, /**< No frame because it's missing */
PJMEDIA_JB_NORMAL_FRAME = 1, /**< Normal frame is being returned */
@@ -56,19 +56,47 @@ enum pjmedia_jb_frame_type
because JB is bufferring. */
PJMEDIA_JB_ZERO_EMPTY_FRAME = 3 /**< Zero frame is being returned
because JB is empty. */
-};
+} pjmedia_jb_frame_type;
/**
- * @see pjmedia_jb_frame_type.
+ * Enumeration of jitter buffer discard algorithm. The jitter buffer
+ * continuously calculates the jitter level to get the optimum latency at
+ * any time and in order to adjust the latency, the jitter buffer may need
+ * to discard some frames.
*/
-typedef enum pjmedia_jb_frame_type pjmedia_jb_frame_type;
+typedef enum pjmedia_jb_discard_algo
+{
+ /**
+ * Jitter buffer should not discard any frame, except when the jitter
+ * buffer is full and a new frame arrives, one frame will be discarded
+ * to make space for the new frame.
+ */
+ PJMEDIA_JB_DISCARD_NONE = 0,
+
+ /**
+ * Only discard one frame in at least 200ms when the latency is considered
+ * much higher than it should be. When the jitter buffer is full and a new
+ * frame arrives, one frame will be discarded to make space for the new
+ * frame.
+ */
+ PJMEDIA_JB_DISCARD_STATIC,
+
+ /**
+ * The discard rate is dynamically calculated based on actual parameters
+ * such as jitter level and latency. When the jitter buffer is full and
+ * a new frame arrives, one frame will be discarded to make space for the
+ * new frame.
+ */
+ PJMEDIA_JB_DISCARD_PROGRESSIVE
+
+} pjmedia_jb_discard_algo;
/**
* This structure describes jitter buffer state.
*/
-struct pjmedia_jb_state
+typedef struct pjmedia_jb_state
{
/* Setting */
unsigned frame_size; /**< Individual frame size, in bytes. */
@@ -89,13 +117,7 @@ struct pjmedia_jb_state
unsigned lost; /**< Number of lost frames. */
unsigned discard; /**< Number of discarded frames. */
unsigned empty; /**< Number of empty on GET events. */
-};
-
-
-/**
- * @see pjmedia_jb_state
- */
-typedef struct pjmedia_jb_state pjmedia_jb_state;
+} pjmedia_jb_state;
/**
@@ -113,7 +135,9 @@ typedef struct pjmedia_jbuf pjmedia_jbuf;
/**
* Create an adaptive jitter buffer according to the specification. If
* application wants to have a fixed jitter buffer, it may call
- * #pjmedia_jbuf_set_fixed() after the jitter buffer is created.
+ * #pjmedia_jbuf_set_fixed() after the jitter buffer is created. Also
+ * if application wants to alter the discard algorithm, which the default
+ * PJMEDIA_JB_DISCARD_PROGRESSIVE, it may call #pjmedia_jbuf_set_discard().
*
* This function may allocate large chunk of memory to keep the frames in
* the buffer.
@@ -180,6 +204,19 @@ PJ_DECL(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb,
/**
+ * Set the jitter buffer discard algorithm. The default discard algorithm,
+ * set in jitter buffer creation, is PJMEDIA_JB_DISCARD_PROGRESSIVE.
+ *
+ * @param jb The jitter buffer.
+ * @param algo The discard algorithm to be used.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_jbuf_set_discard(pjmedia_jbuf *jb,
+ pjmedia_jb_discard_algo algo);
+
+
+/**
* Destroy jitter buffer instance.
*
* @param jb The jitter buffer.
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index e5295869..db4491a2 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -32,14 +32,6 @@
#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
@@ -54,6 +46,12 @@
#define INIT_CYCLE 10
+/* Minimal difference between JB size and 2*burst-level to perform
+ * JB shrinking in static discard algorithm.
+ */
+#define STA_DISC_SAFE_SHRINKING_DIFF 1
+
+
/* Struct of JB internal buffer, represented in a circular buffer containing
* frame content, frame type, frame length, and frame bit info.
*/
@@ -81,6 +79,11 @@ typedef struct jb_framelist_t
} jb_framelist_t;
+typedef void (*discard_algo)(pjmedia_jbuf *jb);
+static void jbuf_discard_static(pjmedia_jbuf *jb);
+static void jbuf_discard_progressive(pjmedia_jbuf *jb);
+
+
struct pjmedia_jbuf
{
/* Settings (consts) */
@@ -97,6 +100,7 @@ struct pjmedia_jbuf
won't be included in level
calculation */
int jb_min_shrink_gap; /**< How often can we shrink */
+ discard_algo jb_discard_algo; /**< Discard algorithm */
/* Buffer */
jb_framelist_t jb_framelist; /**< the buffer */
@@ -124,9 +128,11 @@ struct pjmedia_jbuf
'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 */
- int jb_last_discard_seq;/**< Seq # of last frame discarded */
+ int jb_discard_ref; /**< Seq # of last frame deleted or
+ discarded */
+ unsigned jb_discard_dist; /**< Distance from jb_discard_ref
+ to perform discard (in frm) */
/* Statistics */
pj_math_stat jb_delay; /**< Delay statistics of jitter buffer
@@ -392,7 +398,7 @@ static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist,
enum { MAX_MISORDER = 100 };
enum { MAX_DROPOUT = 3000 };
- assert(frame_size <= framelist->frame_size);
+ PJ_ASSERT_RETURN(frame_size <= framelist->frame_size, PJ_EINVAL);
/* too late or sequence restart */
if (index < framelist->origin) {
@@ -447,15 +453,32 @@ static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist,
/* copy frame content */
pj_memcpy(framelist->content + pos * framelist->frame_size,
frame, frame_size);
- return PJ_SUCCESS;
- } else {
- /* frame is being discarded */
- framelist->discarded_num++;
- return PJ_EIGNORED;
}
+
+ return PJ_SUCCESS;
}
+static pj_status_t jb_framelist_discard(jb_framelist_t *framelist,
+ int index)
+{
+ unsigned pos;
+
+ PJ_ASSERT_RETURN(index >= framelist->origin &&
+ index < framelist->origin + (int)framelist->size,
+ PJ_EINVAL);
+
+ /* Get the slot position */
+ pos = (framelist->head + (index - framelist->origin)) %
+ framelist->max_count;
+
+ /* Discard the frame */
+ framelist->frame_type[pos] = PJMEDIA_JB_DISCARDED_FRAME;
+ framelist->discarded_num++;
+
+ return PJ_SUCCESS;
+}
+
enum pjmedia_jb_op
{
@@ -488,13 +511,13 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool,
jb->jb_min_prefetch = 0;
jb->jb_max_prefetch = max_count*4/5;
jb->jb_max_count = max_count;
- jb->jb_min_shrink_gap= MIN_SHRINK_GAP_MSEC / ptime;
+ jb->jb_min_shrink_gap= PJMEDIA_JBUF_DISC_MIN_GAP / ptime;
jb->jb_max_burst = PJ_MAX(MAX_BURST_MSEC / ptime, max_count*3/4);
- jb->jb_last_discard_seq = 0;
pj_math_stat_init(&jb->jb_delay);
pj_math_stat_init(&jb->jb_burst);
+ pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_PROGRESSIVE);
pjmedia_jbuf_reset(jb);
*p_jb = jb;
@@ -542,6 +565,30 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb,
}
+PJ_DEF(pj_status_t) pjmedia_jbuf_set_discard( pjmedia_jbuf *jb,
+ pjmedia_jb_discard_algo algo)
+{
+ PJ_ASSERT_RETURN(jb, PJ_EINVAL);
+ PJ_ASSERT_RETURN(algo >= PJMEDIA_JB_DISCARD_NONE &&
+ algo <= PJMEDIA_JB_DISCARD_PROGRESSIVE,
+ PJ_EINVAL);
+
+ switch(algo) {
+ case PJMEDIA_JB_DISCARD_PROGRESSIVE:
+ jb->jb_discard_algo = &jbuf_discard_progressive;
+ break;
+ case PJMEDIA_JB_DISCARD_STATIC:
+ jb->jb_discard_algo = &jbuf_discard_static;
+ break;
+ default:
+ jb->jb_discard_algo = NULL;
+ break;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
PJ_DEF(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb)
{
jb->jb_level = 0;
@@ -551,6 +598,7 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb)
jb->jb_init_cycle_cnt= 0;
jb->jb_max_hist_level= 0;
jb->jb_prefetching = (jb->jb_prefetch != 0);
+ jb->jb_discard_dist = 0;
jb_framelist_reset(&jb->jb_framelist);
@@ -562,11 +610,13 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb)
{
PJ_LOG(5, (jb->jb_name.ptr, ""
"JB summary:\n"
- " size=%d prefetch=%d level=%d\n"
+ " size=%d/eff=%d prefetch=%d level=%d\n"
" delay (min/max/avg/dev)=%d/%d/%d/%d ms\n"
" burst (min/max/avg/dev)=%d/%d/%d/%d frames\n"
" lost=%d discard=%d empty=%d",
- jb->jb_framelist.size, jb->jb_prefetch, jb->jb_eff_level,
+ jb_framelist_size(&jb->jb_framelist),
+ jb_framelist_eff_size(&jb->jb_framelist),
+ jb->jb_prefetch, jb->jb_eff_level,
jb->jb_delay.min, jb->jb_delay.max, jb->jb_delay.mean,
pj_math_stat_get_stddev(&jb->jb_delay),
jb->jb_burst.min, jb->jb_burst.max, jb->jb_burst.mean,
@@ -649,6 +699,136 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb)
}
}
+
+static void jbuf_discard_static(pjmedia_jbuf *jb)
+{
+ /* 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
+ * PJMEDIA_JBUF_DISC_MIN_GAP ms. Theoritically, JB may handle drift level
+ * as much as = FRAME_PTIME/PJMEDIA_JBUF_DISC_MIN_GAP * 100%
+ *
+ * Whenever there is drift, where PUT > GET, this method will keep
+ * the latency (JB size) as much as twice of burst level.
+ */
+
+ /* Shrinking due of drift will be implicitly done by progressive discard,
+ * so just disable it when progressive discard is active.
+ */
+ int diff, burst_level;
+
+ burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level);
+ diff = jb_framelist_eff_size(&jb->jb_framelist) - burst_level*2;
+
+ if (diff >= STA_DISC_SAFE_SHRINKING_DIFF) {
+ int seq_origin;
+
+ /* Check and adjust jb_discard_ref, in case there was
+ * seq restart
+ */
+ seq_origin = jb_framelist_origin(&jb->jb_framelist);
+ if (seq_origin < jb->jb_discard_ref)
+ jb->jb_discard_ref = seq_origin;
+
+ if (seq_origin - jb->jb_discard_ref >= jb->jb_min_shrink_gap)
+ {
+ /* Shrink slowly, one frame per cycle */
+ diff = 1;
+
+ /* Drop frame(s)! */
+ diff = jb_framelist_remove_head(&jb->jb_framelist, diff);
+ jb->jb_discard_ref = jb_framelist_origin(&jb->jb_framelist);
+ jb->jb_discard += diff;
+
+ TRACE__((jb->jb_name.ptr,
+ "JB shrinking %d frame(s), cur size=%d", diff,
+ jb_framelist_eff_size(&jb->jb_framelist)));
+ }
+ }
+}
+
+
+static void jbuf_discard_progressive(pjmedia_jbuf *jb)
+{
+ unsigned cur_size, burst_level, overflow, T, discard_dist;
+ int last_seq;
+
+ /* Should be done in PUT operation */
+ if (jb->jb_last_op != JB_OP_PUT)
+ return;
+
+ /* Check if latency is longer than burst */
+ cur_size = jb_framelist_eff_size(&jb->jb_framelist);
+ burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level);
+ if (cur_size <= burst_level) {
+ /* Reset any scheduled discard */
+ jb->jb_discard_dist = 0;
+ return;
+ }
+
+ /* Estimate discard duration needed for adjusting latency */
+ if (burst_level <= PJMEDIA_JBUF_PRO_DISC_MIN_BURST)
+ T = PJMEDIA_JBUF_PRO_DISC_T1;
+ else if (burst_level >= PJMEDIA_JBUF_PRO_DISC_MAX_BURST)
+ T = PJMEDIA_JBUF_PRO_DISC_T2;
+ else
+ T = PJMEDIA_JBUF_PRO_DISC_T1 +
+ (PJMEDIA_JBUF_PRO_DISC_T2 - PJMEDIA_JBUF_PRO_DISC_T1) *
+ (burst_level - PJMEDIA_JBUF_PRO_DISC_MIN_BURST) /
+ (PJMEDIA_JBUF_PRO_DISC_MAX_BURST-PJMEDIA_JBUF_PRO_DISC_MIN_BURST);
+
+ /* Calculate current discard distance */
+ overflow = cur_size - burst_level;
+ discard_dist = T / overflow / jb->jb_frame_ptime;
+
+ /* Get last seq number in the JB */
+ last_seq = jb_framelist_origin(&jb->jb_framelist) +
+ jb_framelist_size(&jb->jb_framelist) - 1;
+
+ /* Setup new discard schedule if none, otherwise, update the existing
+ * discard schedule (can be delayed or accelerated).
+ */
+ if (jb->jb_discard_dist == 0) {
+ /* Setup new discard schedule */
+ jb->jb_discard_ref = last_seq;
+ } else if (last_seq < jb->jb_discard_ref) {
+ /* Seq restarted, update discard reference */
+ jb->jb_discard_ref = last_seq;
+ }
+ jb->jb_discard_dist = PJ_MAX(jb->jb_min_shrink_gap, (int)discard_dist);
+
+ /* Check if we need to discard now */
+ if (last_seq >= (jb->jb_discard_ref + (int)jb->jb_discard_dist)) {
+ int discard_seq;
+
+ discard_seq = jb->jb_discard_ref + jb->jb_discard_dist;
+ if (discard_seq < jb_framelist_origin(&jb->jb_framelist))
+ discard_seq = jb_framelist_origin(&jb->jb_framelist);
+
+ jb_framelist_discard(&jb->jb_framelist, discard_seq);
+
+ TRACE__((jb->jb_name.ptr,
+ "Discard #%d: ref=#%d dist=%d orig=%d size=%d/%d "
+ "burst=%d/%d",
+ discard_seq,
+ jb->jb_discard_ref,
+ jb->jb_discard_dist,
+ jb_framelist_origin(&jb->jb_framelist),
+ cur_size,
+ jb_framelist_size(&jb->jb_framelist),
+ jb->jb_eff_level,
+ burst_level));
+
+ /* Update discard reference */
+ jb->jb_discard_ref = discard_seq;
+ }
+}
+
+
PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper)
{
if(jb->jb_last_op != oper) {
@@ -684,63 +864,10 @@ PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper)
jb->jb_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.
- */
-
- /* Shrinking due of drift will be implicitly done by progressive discard,
- * so just disable it when progressive discard is active.
- */
-#if !PROGRESSIVE_DISCARD
-
- if (jb->jb_status != JB_STATUS_PROCESSING)
- return;
-
- {
- int diff, burst_level;
-
- burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level);
- diff = jb_framelist_eff_size(&jb->jb_framelist) - burst_level*2;
-
- if (diff >= SAFE_SHRINKING_DIFF) {
- int seq_origin;
-
- /* Check and adjust jb_last_del_seq, in case there was
- * seq restart
- */
- seq_origin = jb_framelist_origin(&jb->jb_framelist);
- if (seq_origin < jb->jb_last_del_seq)
- jb->jb_last_del_seq = seq_origin;
-
- if (seq_origin - jb->jb_last_del_seq >= jb->jb_min_shrink_gap)
- {
- /* Shrink slowly, one frame per cycle */
- diff = 1;
-
- /* Drop frame(s)! */
- diff = jb_framelist_remove_head(&jb->jb_framelist, diff);
- jb->jb_last_del_seq = jb_framelist_origin(&jb->jb_framelist);
- jb->jb_discard += diff;
-
- TRACE__((jb->jb_name.ptr,
- "JB shrinking %d frame(s), cur size=%d", diff,
- jb_framelist_eff_size(&jb->jb_framelist)));
- }
- }
+ /* Call discard algorithm */
+ if (jb->jb_status == JB_STATUS_PROCESSING && jb->jb_discard_algo) {
+ (*jb->jb_discard_algo)(jb);
}
-
-#endif /* !PROGRESSIVE_DISCARD */
-
}
PJ_DEF(void) pjmedia_jbuf_put_frame( pjmedia_jbuf *jb,
@@ -759,95 +886,35 @@ PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb,
pj_bool_t *discarded)
{
pj_size_t min_frame_size;
- int new_size, cur_size, frame_type = PJMEDIA_JB_NORMAL_FRAME;
+ int new_size, cur_size;
pj_status_t status;
cur_size = jb_framelist_eff_size(&jb->jb_framelist);
-#if PROGRESSIVE_DISCARD
- {
- unsigned interval, seq_delta;
- unsigned burst_level = 0, overflow_pct = 0;
-
- /* Calculating percentage of burst overflow */
- if (jb->jb_status == JB_STATUS_PROCESSING) {
- burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level);
- if (cur_size > (int)burst_level)
- overflow_pct = (cur_size - burst_level) * 100 / burst_level;
- }
-
- /* Deciding discard interval (aggressiveness) based on
- * burst overflow percentage.
- */
- if (burst_level <= 5 && overflow_pct < 200) {
- /* Tolerate spikes on relatively small burst level */
- interval = 0;
- } else if (overflow_pct >= 200) {
- /* Overflow >= 200% */
- interval = 4;
- } else if (overflow_pct >= 100) {
- /* Overflow >= 100% */
- interval = 5;
- } else if (overflow_pct >= 10) {
- /* Overflow >= 10% */
- interval = 7;
- } else {
- /* Overflow < 10%, tolerable */
- interval = 0;
- }
-
- /* Do the math now to see if we should discard this packet.
- * Calculate the distance from the last sequence
- * discarded. If negative, then this is an out of
- * order frame so just proceed with discard. Else
- * see if the delta is at least the intervals worth away
- * from the last frame discarded.
- */
- seq_delta = (pj_uint16_t)(frame_seq - jb->jb_last_discard_seq);
- if ((0 != interval) && (seq_delta >= interval)) {
- frame_type = PJMEDIA_JB_DISCARDED_FRAME;
- jb->jb_last_discard_seq = frame_seq;
-
- TRACE__((jb->jb_name.ptr,
- "Discarding frame #%d: eff=%d disc=%d orig:%d"
- " seq_delta:%d",
- frame_seq,
- cur_size,
- jb_framelist_size(&jb->jb_framelist) - cur_size,
- jb_framelist_origin(&jb->jb_framelist),
- (int)seq_delta));
- }
- }
-#endif /* PROGRESSIVE_DISCARD */
-
-
/* 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, frame_type);
+ min_frame_size, bit_info,
+ PJMEDIA_JB_NORMAL_FRAME);
/* Jitter buffer is full, remove some older frames */
while (status == PJ_ETOOMANY) {
int distance;
unsigned removed;
- /* When progressive discard activated, just remove as few as possible
- * just to make this frame in.
- */
-#if PROGRESSIVE_DISCARD
- /* The cases of seq-jump, out-of-order, and seq restart should have
+ /* Remove as few as possible just to make this frame in. Note that
+ * the cases of seq-jump, out-of-order, and seq restart should have
* been handled/normalized by previous call of jb_framelist_put_at().
* So we're confident about 'distance' value here.
*/
distance = (frame_seq - jb_framelist_origin(&jb->jb_framelist)) -
jb->jb_max_count + 1;
pj_assert(distance > 0);
-#else
- distance = PJ_MAX(jb->jb_max_count/4, 1);
-#endif
+
removed = jb_framelist_remove_head(&jb->jb_framelist, distance);
status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame,
- min_frame_size, bit_info, frame_type);
+ min_frame_size, bit_info,
+ PJMEDIA_JB_NORMAL_FRAME);
jb->jb_discard += removed;
}
diff --git a/pjmedia/src/test/jbuf_test.c b/pjmedia/src/test/jbuf_test.c
index 9cffa921..309459b0 100644
--- a/pjmedia/src/test/jbuf_test.c
+++ b/pjmedia/src/test/jbuf_test.c
@@ -43,7 +43,8 @@ typedef struct test_cond_t {
int discard;
int lost;
int empty;
- int delay; /**< Maximum delay, in frames. */
+ int delay; /**< Average delay, in frames. */
+ int delay_min; /**< Minimum delay, in frames. */
} test_cond_t;
static pj_bool_t parse_test_headers(char *line, test_param_t *param,
@@ -69,6 +70,8 @@ static pj_bool_t parse_test_headers(char *line, test_param_t *param,
cond->burst = cond_val;
else if (pj_ansi_stricmp(cond_st, "delay") == 0)
cond->delay = cond_val;
+ else if (pj_ansi_stricmp(cond_st, "delay_min") == 0)
+ cond->delay_min = cond_val;
else if (pj_ansi_stricmp(cond_st, "discard") == 0)
cond->discard = cond_val;
else if (pj_ansi_stricmp(cond_st, "empty") == 0)
@@ -217,6 +220,7 @@ int jbuf_main(void)
cond.burst = -1;
cond.delay = -1;
+ cond.delay_min = -1;
cond.discard = -1;
cond.empty = -1;
cond.lost = -1;
@@ -313,6 +317,11 @@ int jbuf_main(void)
cond.delay, state.avg_delay/JB_PTIME);
rc |= 2;
}
+ if (cond.delay_min >= 0 && (int)state.min_delay/JB_PTIME > cond.delay_min) {
+ printf("! 'Minimum delay' should be %d, it is %d\n",
+ cond.delay_min, state.min_delay/JB_PTIME);
+ rc |= 32;
+ }
if (cond.discard >= 0 && (int)state.discard > cond.discard) {
printf("! 'Discard' should be %d, it is %d\n",
cond.discard, state.discard);