Index: /pjproject/trunk/pjmedia/build/Jbtest.dat
===================================================================
--- /pjproject/trunk/pjmedia/build/Jbtest.dat	(revision 3064)
+++ /pjproject/trunk/pjmedia/build/Jbtest.dat	(revision 3065)
@@ -1,2 +1,46 @@
+#
+# ======================================================================
+# Jitter Buffer test data, containing one or more test sessions
+# ======================================================================
+#
+# A test session format:
+#
+# 1. Session title, started with '=', example:
+#    = Bursty environment
+# 
+# 2. Session setting, started with '%', followed by params:
+#    - mode, possible values: 'adaptive' or 'fixed'
+#    - initial prefetch, in frames
+#    - minimum prefetch (for adaptive mode only), in frames
+#    - maximum prefetch (for adaptive mode only), in frames
+#    Example:
+#    %adaptive 0 0 40
+#    %fixed 10
+#
+# 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.
+#    Example:
+#    !delay 10  <- maximum 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:
+#    - P: PUT a frame into jitter buffer
+#    - G: GET a frame from jitter buffer
+#    - L: generate a Lost frame, i.e: sequence jump by 1
+#    - R: sequence Restart
+#    - J: sequence Jump by 20
+#    - D: generate a Duplicated frame
+#    - O: generate an Old/late (and perhaps also duplicated) frame
+#    Example:
+#    PGPGPGPGPG <- ideal condition, PUT and GET one after another
+#
+# 5. End of session test data, marked by '.'
+#
+# ======================================================================
+#
+
 = Ideal condition
 %adaptive 0 0 10
@@ -303,22 +347,215 @@
 %fixed 10
 !burst	    1
-!discard    35 <- frames discarded for delay adaptation
+!discard    50 <- frames discarded for delay adaptation
 !lost	    0
 !empty	    0
-!delay	    30 <- average delay
-PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
-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
-.
+!delay	    20 <- average delay, twice of minimal prefetch
+PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
+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
+.
+
+= Large PUT burst at beginning, then normal with burst level 10 and periodic burst spikes
+%adaptive 0 0 40
+!burst	    10
+!discard    300 <- not so relevant for long period session with many delay adjustments needed (i.e: for first burst and periodic spikes)
+!lost	    0
+!empty	    60 <- delay adjustment effect, as there is actually no drift
+!delay	    20 <- twice of burst level average
+PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
+PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
+PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
+PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
+GGGGGGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGG GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
+GGGGGGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGG GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
+GGGGGGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGG GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
+GGGGGGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGG GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP
+GGGGGGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGG GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+PPPPPPPPPP GGGGGGGGGG PPPPPPPPPP GGGGGGGGGG
+.
Index: /pjproject/trunk/pjmedia/include/pjmedia/jbuf.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/jbuf.h	(revision 3064)
+++ /pjproject/trunk/pjmedia/include/pjmedia/jbuf.h	(revision 3065)
@@ -77,4 +77,5 @@
 
     /* Status */
+    unsigned	burst;		    /**< Current burst level, in frames	    */
     unsigned	prefetch;	    /**< Current prefetch value, in frames  */
     unsigned	size;		    /**< Current buffer size, in frames.    */
Index: /pjproject/trunk/pjmedia/src/pjmedia/jbuf.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 3064)
+++ /pjproject/trunk/pjmedia/src/pjmedia/jbuf.c	(revision 3065)
@@ -73,6 +73,10 @@
     unsigned	     head;		/**< index of head, pointed frame
 					     will be returned by next GET   */
-    unsigned	     size;		/**< current size of framelist.	    */
+    unsigned	     size;		/**< current size of framelist, 
+					     including discarded frames.    */
+    unsigned	     discarded_num;	/**< current number of discarded 
+					     frames.			    */
     int		     origin;		/**< original index of flist_head   */
+
 } jb_framelist_t;
 
@@ -86,5 +90,5 @@
     pj_size_t	    jb_max_count;	/**< capacity of jitter buffer, 
 					     in frames			    */
-    int		    jb_def_prefetch;	/**< Default prefetch		    */
+    int		    jb_init_prefetch;	/**< Initial prefetch		    */
     int		    jb_min_prefetch;	/**< Minimum allowable prefetch	    */
     int		    jb_max_prefetch;	/**< Maximum allowable prefetch	    */
@@ -109,4 +113,5 @@
     int		    jb_last_op;		/**< last operation executed 
 					     (put/get)			    */
+    int		    jb_eff_level;	/**< effective burst level	    */
     int		    jb_prefetch;	/**< no. of frame to insert before 
 					     removing some (at the beginning 
@@ -121,4 +126,6 @@
     int		    jb_last_del_seq;	/**< Seq # of last frame deleted    */
 
+    int		    jb_last_discard_seq;/**< Seq # of last frame discarded  */
+
     /* Statistics */
     pj_math_stat    jb_delay;		/**< Delay statistics of jitter buffer 
@@ -136,4 +143,21 @@
 #define JB_STATUS_PREFETCHING	2
 
+
+/* === Experimental feature === */
+
+/* Progressive discard algorithm introduced to reduce JB latency
+ * by discarding incoming frames with adaptive aggressiveness based on
+ * actual burst level.
+ */
+#define PROGRESSIVE_DISCARD 1
+
+/* Internal JB frame flag, discarded frame will not be returned by JB to
+ * application, it's just simply discarded.
+ */
+#define PJMEDIA_JB_DISCARDED_FRAME 1024
+
+/* == End of experimental feature == */
+
+
 /* Enabling this would log the jitter buffer state about once per 
  * second.
@@ -146,4 +170,6 @@
 
 static pj_status_t jb_framelist_reset(jb_framelist_t *framelist);
+static unsigned jb_framelist_remove_head(jb_framelist_t *framelist,
+					 unsigned count);
 
 static pj_status_t jb_framelist_init( pj_pool_t *pool,
@@ -175,4 +201,5 @@
 					    framelist->max_count);
 
+
     return jb_framelist_reset(framelist);
 
@@ -190,4 +217,6 @@
     framelist->origin = INVALID_OFFSET;
     framelist->size = 0;
+    framelist->discarded_num = 0;
+
 
     //pj_bzero(framelist->content, 
@@ -218,4 +247,15 @@
 
 
+static unsigned jb_framelist_eff_size(const jb_framelist_t *framelist) 
+{
+    return (framelist->size - framelist->discarded_num);
+}
+
+static int jb_framelist_origin(const jb_framelist_t *framelist) 
+{
+    return framelist->origin;
+}
+
+
 static pj_bool_t jb_framelist_get(jb_framelist_t *framelist,
 				  void *frame, pj_size_t *size,
@@ -224,35 +264,48 @@
 {
     if (framelist->size) {
-	pj_memcpy(frame, 
-		  framelist->content + 
-		  framelist->head * framelist->frame_size,
-		  framelist->frame_size);
-	*p_type = (pjmedia_jb_frame_type) 
-		  framelist->frame_type[framelist->head];
-	if (size)
-	    *size   = framelist->content_len[framelist->head];
-	if (bit_info)
-	    *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->frame_size);
-
-	return PJ_FALSE;
-    }
-}
-
-
+
+	/* Skip discarded frames */
+	while (framelist->frame_type[framelist->head] ==
+	       PJMEDIA_JB_DISCARDED_FRAME)
+	{
+	    jb_framelist_remove_head(framelist, 1);
+	}
+
+	/* Return the head frame if any */
+	if (framelist->size) {
+	    pj_memcpy(frame, 
+		      framelist->content + 
+		      framelist->head * framelist->frame_size,
+		      framelist->frame_size);
+	    *p_type = (pjmedia_jb_frame_type) 
+		      framelist->frame_type[framelist->head];
+	    if (size)
+		*size   = framelist->content_len[framelist->head];
+	    if (bit_info)
+		*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;
+	}
+    }
+
+    /* No frame available */
+    pj_bzero(frame, framelist->frame_size);
+
+    return PJ_FALSE;
+}
+
+
+/* Remove oldest frames as many as param 'count' */
 static unsigned jb_framelist_remove_head(jb_framelist_t *framelist,
 					 unsigned count) 
@@ -265,4 +318,5 @@
 	unsigned step1,step2;
 	unsigned tmp = framelist->head+count;
+	unsigned i;
 
 	if (tmp > framelist->max_count) {
@@ -272,4 +326,11 @@
 	    step1 = count;
 	    step2 = 0;
+	}
+
+	for (i = framelist->head; i < (framelist->head + step1); ++i) {
+	    if (framelist->frame_type[i] == PJMEDIA_JB_DISCARDED_FRAME) {
+		pj_assert(framelist->discarded_num > 0);
+		framelist->discarded_num--;
+	    }
 	}
 
@@ -281,7 +342,13 @@
 		  step1*sizeof(framelist->frame_type[0]));
 	pj_bzero(framelist->content_len+framelist->head,
-		  step1*sizeof(framelist->content_len[0]));
+		 step1*sizeof(framelist->content_len[0]));
 
 	if (step2) {
+	    for (i = 0; i < step2; ++i) {
+		if (framelist->frame_type[i] == PJMEDIA_JB_DISCARDED_FRAME) {
+		    pj_assert(framelist->discarded_num > 0);
+		    framelist->discarded_num--;
+		}
+	    }
 	    //pj_bzero( framelist->content,
 	    //	      step2*framelist->frame_size);
@@ -307,8 +374,9 @@
 				       const void *frame,
 				       unsigned frame_size,
-				       pj_uint32_t bit_info)
+				       pj_uint32_t bit_info,
+				       unsigned frame_type)
 {
     int distance;
-    unsigned where;
+    unsigned pos;
     enum { MAX_MISORDER = 100 };
     enum { MAX_DROPOUT = 3000 };
@@ -316,8 +384,8 @@
     assert(frame_size <= framelist->frame_size);
 
-    /* too late or duplicated or sequence restart */
+    /* too late or sequence restart */
     if (index < framelist->origin) {
 	if (framelist->origin - index < MAX_MISORDER) {
-	    /* too late or duplicated */
+	    /* too late */
 	    return PJ_ETOOSMALL;
 	} else {
@@ -329,4 +397,5 @@
     /* if jbuf is empty, just reset the origin */
     if (framelist->size == 0) {
+	pj_assert(framelist->discarded_num == 0);
 	framelist->origin = index;
     }
@@ -349,20 +418,29 @@
 
     /* get the slot position */
-    where = (framelist->head + distance) % framelist->max_count;
+    pos = (framelist->head + distance) % framelist->max_count;
 
     /* if the slot is occupied, it must be duplicated frame, ignore it. */
-    if (framelist->frame_type[where] != PJMEDIA_JB_MISSING_FRAME)
+    if (framelist->frame_type[pos] != PJMEDIA_JB_MISSING_FRAME)
 	return PJ_EEXISTS;
 
     /* 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;
+    framelist->frame_type[pos] = frame_type;
+    framelist->content_len[pos] = frame_size;
+    framelist->bit_info[pos] = bit_info;
+
+    /* update framelist size */
     if (framelist->origin + (int)framelist->size <= index)
 	framelist->size = distance + 1;
 
-    return PJ_SUCCESS;
+    if(PJMEDIA_JB_NORMAL_FRAME == frame_type) {
+	/* 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;
+    }
 }
 
@@ -402,4 +480,6 @@
     jb->jb_min_shrink_gap= MIN_SHRINK_GAP_MSEC / ptime;
     jb->jb_max_burst	 = MAX_BURST_MSEC / ptime;
+    jb->jb_last_discard_seq = 0;
+
     pj_math_stat_init(&jb->jb_delay);
     pj_math_stat_init(&jb->jb_burst);
@@ -424,5 +504,5 @@
 
     jb->jb_min_prefetch = jb->jb_max_prefetch = 
-	jb->jb_prefetch = jb->jb_def_prefetch = prefetch;
+	jb->jb_prefetch = jb->jb_init_prefetch = prefetch;
 
     return PJ_SUCCESS;
@@ -444,5 +524,5 @@
 		     PJ_EINVAL);
 
-    jb->jb_prefetch = jb->jb_def_prefetch = prefetch;
+    jb->jb_prefetch = jb->jb_init_prefetch = prefetch;
     jb->jb_min_prefetch = min_prefetch;
     jb->jb_max_prefetch = max_prefetch;
@@ -469,16 +549,16 @@
 PJ_DEF(pj_status_t) pjmedia_jbuf_destroy(pjmedia_jbuf *jb)
 {
-    TRACE__((jb->jb_name.ptr, ""
-	    "JB summary:"
-	    " size=%d prefetch=%d,"
-	    " delay (min/max/avg/dev)=%d/%d/%d/%d ms,"
-	    " burst (min/max/avg/dev)=%d/%d/%d/%d frames,"
-	    " lost=%d discard=%d empty=%d",
-	    jb->jb_framelist.size, jb->jb_prefetch,
-	    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, 
-	    pj_math_stat_get_stddev(&jb->jb_burst),
-	    jb->jb_lost, jb->jb_discard, jb->jb_empty));
+    PJ_LOG(5, (jb->jb_name.ptr, ""
+	       "JB summary:\n"
+	       "  size=%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->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, 
+	       pj_math_stat_get_stddev(&jb->jb_burst),
+	       jb->jb_lost, jb->jb_discard, jb->jb_empty));
 
     return jb_framelist_destroy(&jb->jb_framelist);
@@ -490,28 +570,34 @@
     int diff, cur_size;
 
-    cur_size = jb_framelist_size(&jb->jb_framelist);
+    cur_size = jb_framelist_eff_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 };
+    if (jb->jb_level < jb->jb_eff_level) {
+
+	enum { STABLE_HISTORY_LIMIT = 20 };
         
 	jb->jb_stable_hist++;
         
-	/* Only update the prefetch if 'stable' condition is reached 
-	 * (not just short time impulse)
+	/* Only update the effective level (and 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;
+	    diff = (jb->jb_eff_level - jb->jb_max_hist_level) / 3;
 
 	    if (diff < 1)
 		diff = 1;
 
-	    jb->jb_prefetch -= diff;
-	    if (jb->jb_prefetch < jb->jb_min_prefetch) 
-		jb->jb_prefetch = jb->jb_min_prefetch;
+	    /* Update effective burst level */
+	    jb->jb_eff_level -= diff;
+
+	    /* Update prefetch based on level */
+	    if (jb->jb_init_prefetch) {
+		jb->jb_prefetch = jb->jb_eff_level;
+		if (jb->jb_prefetch < jb->jb_min_prefetch) 
+		    jb->jb_prefetch = jb->jb_min_prefetch;
+	    }
 
 	    /* Reset history */
@@ -519,17 +605,22 @@
 	    jb->jb_stable_hist = 0;
 
-	    TRACE__((jb->jb_name.ptr,"jb updated(1), prefetch=%d, size=%d",
-		     jb->jb_prefetch, cur_size));
+	    TRACE__((jb->jb_name.ptr,"jb updated(1), lvl=%d pre=%d, size=%d",
+		     jb->jb_eff_level, 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;
+    else if (jb->jb_level > jb->jb_eff_level) {
+
+	/* Instaneous set effective burst level to recent maximum level */
+	jb->jb_eff_level = PJ_MIN(jb->jb_max_hist_level,
+				  (int)(jb->jb_max_count*4/5));
+
+	/* Update prefetch based on level */
+	if (jb->jb_init_prefetch) {
+	    jb->jb_prefetch = jb->jb_eff_level;
+	    if (jb->jb_prefetch > jb->jb_max_prefetch)
+		jb->jb_prefetch = jb->jb_max_prefetch;
+	}
 
 	jb->jb_stable_hist = 0;
@@ -537,6 +628,6 @@
 	//jb->jb_max_hist_level = 0;
 
-	TRACE__((jb->jb_name.ptr,"jb updated(2), prefetch=%d, size=%d", 
-		 jb->jb_prefetch, cur_size));
+	TRACE__((jb->jb_name.ptr,"jb updated(2), lvl=%d pre=%d, size=%d", 
+		 jb->jb_eff_level, jb->jb_prefetch, cur_size));
     }
 
@@ -549,6 +640,4 @@
 PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper)
 {
-    int diff, burst_level;
-
     if(jb->jb_last_op != oper) {
 	jb->jb_last_op = oper;
@@ -594,31 +683,47 @@
      */
 
+    /* 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;
 
-    burst_level = PJ_MAX(jb->jb_prefetch, jb->jb_level);
-    diff = jb_framelist_size(&jb->jb_framelist) - burst_level*2;
-
-    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;
-
-	if (jb->jb_framelist.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->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)));
-	}
-    }
+    {
+	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)));
+	    }
+	}
+    }
+
+#endif /* !PROGRESSIVE_DISCARD */
+
 }
 
@@ -639,29 +744,100 @@
 {
     pj_size_t min_frame_size;
-    int prev_size, cur_size;
+    int new_size, cur_size, frame_type = PJMEDIA_JB_NORMAL_FRAME;
     pj_status_t status;
 
-    /* Get JB size before PUT */
-    prev_size = jb_framelist_size(&jb->jb_framelist);
+    cur_size = jb_framelist_eff_size(&jb->jb_framelist);
+
+#if PROGRESSIVE_DISCARD
+    {
+	unsigned interval, seq_delta;
+	unsigned burst_level, burst_factor;
+
+	/* Calculating discard interval (aggressiveness) based on
+	 * (current size / burst level).
+	 */
+	if (jb->jb_status == JB_STATUS_PROCESSING) {
+	    burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level);
+	    burst_factor = cur_size / burst_level;
+	    /* Tolerate small spikes */
+	    if ((burst_level <= 5) && (burst_factor < 3))
+		burst_factor = 0;
+	} else {
+	    burst_factor = 0;
+	}
+
+	switch (burst_factor) {
+	case 0:
+	    interval = 0;
+	    break;
+	case 1:
+	    interval = 7;
+	    break;
+	case 2:
+	    interval = 5;
+	    break;
+	default:
+	    interval = 4;
+	    break;
+	}
+
+	/* 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);
+				 min_frame_size, bit_info, frame_type);
     
-    /* Jitter buffer is full, cannot store the frame */
+    /* Jitter buffer is full, remove some older frames */
     while (status == PJ_ETOOMANY) {
+	int distance;
 	unsigned removed;
 
-	removed = jb_framelist_remove_head(&jb->jb_framelist,
-					   PJ_MAX(jb->jb_max_count/4, 1));
+	/* 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
+	 * 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);
+				     min_frame_size, bit_info, frame_type);
 
 	jb->jb_discard += removed;
     }
 
-    /* Get JB size after PUT */
-    cur_size = jb_framelist_size(&jb->jb_framelist);
+    /* Get new JB size after PUT */
+    new_size = jb_framelist_eff_size(&jb->jb_framelist);
 
     /* Return the flag if this frame is discarded */
@@ -672,9 +848,9 @@
 	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)
+		     new_size, jb->jb_prefetch));
+	    if (new_size >= jb->jb_prefetch)
 		jb->jb_status = JB_STATUS_PROCESSING;
 	}
-	jb->jb_level += (cur_size > prev_size ? cur_size-prev_size : 1);
+	jb->jb_level += (new_size > cur_size ? new_size-cur_size : 1);
 	jbuf_update(jb, JB_OP_PUT);
     } else
@@ -701,22 +877,5 @@
 				     pj_uint32_t *bit_info)
 {
-    int cur_size;
-
-    cur_size = jb_framelist_size(&jb->jb_framelist);
-
-    if (cur_size == 0) {
-	/* jitter buffer empty */
-
-	if (jb->jb_def_prefetch)
-	    jb->jb_status = JB_STATUS_PREFETCHING;
-
-	//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) {
+    if (jb->jb_status == JB_STATUS_PREFETCHING) {
 
 	/* Can't return frame because jitter buffer is filling up
@@ -730,5 +889,5 @@
 
 	TRACE__((jb->jb_name.ptr, "GET prefetch_cnt=%d/%d",
-		 cur_size, jb->jb_prefetch));
+		 jb_framelist_eff_size(&jb->jb_framelist), jb->jb_prefetch));
 
 	jb->jb_empty++;
@@ -736,25 +895,42 @@
     } else {
 
-	pjmedia_jb_frame_type ftype = PJMEDIA_JB_MISSING_FRAME;
+	pjmedia_jb_frame_type ftype = PJMEDIA_JB_NORMAL_FRAME;
 	pj_bool_t res;
 
-	/* Retrieve a frame from frame list */
+	/* Try to 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;
+	if (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;
+	    } else {
+		*p_frame_type = PJMEDIA_JB_MISSING_FRAME;
+		jb->jb_lost++;
+	    }
+
+	    /* Store delay history at the first GET */
+	    if (jb->jb_last_op == JB_OP_PUT) {
+		unsigned cur_size;
+
+		/* We've just retrieved one frame, so add one to cur_size */
+		cur_size = jb_framelist_eff_size(&jb->jb_framelist) + 1;
+		pj_math_stat_update(&jb->jb_delay, 
+				    cur_size*jb->jb_frame_ptime);
+	    }
 	} else {
-	    *p_frame_type = PJMEDIA_JB_MISSING_FRAME;
-	    jb->jb_lost++;
-	}
-
-	/* Calculate delay on the first GET */
-	if (jb->jb_last_op == JB_OP_PUT)
-	    pj_math_stat_update(&jb->jb_delay, cur_size * jb->jb_frame_ptime);
+	    /* Jitter buffer is empty */
+	    if (jb->jb_prefetch)
+		jb->jb_status = JB_STATUS_PREFETCHING;
+
+	    //pj_bzero(frame, jb->jb_frame_size);
+	    *p_frame_type = PJMEDIA_JB_ZERO_EMPTY_FRAME;
+	    if (size)
+		*size = 0;
+
+	    jb->jb_empty++;
+	}
     }
 
@@ -775,6 +951,7 @@
     state->max_prefetch = jb->jb_max_prefetch;
     
+    state->burst = jb->jb_eff_level;
     state->prefetch = jb->jb_prefetch;
-    state->size = jb_framelist_size(&jb->jb_framelist);
+    state->size = jb_framelist_eff_size(&jb->jb_framelist);
     
     state->avg_delay = jb->jb_delay.mean;
@@ -790,2 +967,3 @@
     return PJ_SUCCESS;
 }
+
Index: /pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 3064)
+++ /pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 3065)
@@ -121,4 +121,5 @@
     pjmedia_jbuf	    *jb;	    /**< Jitter buffer.		    */
     char		     jb_last_frm;   /**< Last frame type from jb    */
+    unsigned		     jb_last_frm_cnt;/**< Last JB frame type counter*/
 
     pjmedia_rtcp_session     rtcp;	    /**< RTCP for incoming RTP.	    */
@@ -326,12 +327,20 @@
 		pjmedia_zero_samples(p_out_samp + samples_count,
 				     samples_required - samples_count);
-		PJ_LOG(5,(stream->port.info.name.ptr,  "Frame lost!"));
-
+	    }
+
+	    if (frame_type != stream->jb_last_frm) {
+		/* Report changing frame type event */
+		PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost%s!",
+		          (status == PJ_SUCCESS? ", recovered":"")));
+
+		stream->jb_last_frm = frame_type;
+		stream->jb_last_frm_cnt = 1;
 	    } else {
-		PJ_LOG(5,(stream->port.info.name.ptr, 
-			  "Lost frame recovered"));
+		stream->jb_last_frm_cnt++;
 	    }
 
 	} else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
+
+	    const char *with_plc = "";
 
 	    /* Jitter buffer is empty. If this is the first "empty" state,
@@ -343,7 +352,4 @@
 	    //if (frame_type != stream->jb_last_frm) {
 	    if (1) {
-		pjmedia_jb_state jb_state;
-		const char *with_plc = "";
-
 		/* Activate PLC to smoothen the missing frame */
 		if (stream->codec->op->recover && 
@@ -370,11 +376,4 @@
 		    with_plc = ", plc invoked";
 		} 
-
-		/* Report the state of jitter buffer */
-		pjmedia_jbuf_get_state(stream->jb, &jb_state);
-		PJ_LOG(5,(stream->port.info.name.ptr, 
-			  "Jitter buffer empty (prefetch=%d)%s", 
-			  jb_state.prefetch, with_plc));
-
 	    }
 
@@ -385,16 +384,26 @@
 	    }
 
-	    stream->jb_last_frm = frame_type;
+	    if (stream->jb_last_frm != frame_type) {
+		pjmedia_jb_state jb_state;
+
+		/* Report changing frame type event */
+		pjmedia_jbuf_get_state(stream->jb, &jb_state);
+		PJ_LOG(5,(stream->port.info.name.ptr, 
+			  "Jitter buffer empty (prefetch=%d)%s", 
+			  jb_state.prefetch, with_plc));
+
+		stream->jb_last_frm = frame_type;
+		stream->jb_last_frm_cnt = 1;
+	    } else {
+		stream->jb_last_frm_cnt++;
+	    }
 	    break;
 
 	} else if (frame_type != PJMEDIA_JB_NORMAL_FRAME) {
 
-	    pjmedia_jb_state jb_state;
+	    const char *with_plc = "";
 
 	    /* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */
 	    pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME);
-
-	    /* Get the state of jitter buffer */
-	    pjmedia_jbuf_get_state(stream->jb, &jb_state);
 
 	    /* Always activate PLC when it's available.. */
@@ -420,11 +429,5 @@
 			 stream->plc_cnt < stream->max_plc_cnt);
 
-		//if (stream->jb_last_frm != frame_type) {
-		if (1) {
-		    PJ_LOG(5,(stream->port.info.name.ptr, 
-			      "Jitter buffer is bufferring with plc (prefetch=%d)",
-			      jb_state.prefetch));
-		}
-
+		with_plc = ", plc invoked";
 	    } 
 
@@ -433,10 +436,20 @@
 				     samples_required - samples_count);
 		samples_count = samples_required;
+	    }
+
+	    if (stream->jb_last_frm != frame_type) {
+		pjmedia_jb_state jb_state;
+
+		/* Report changing frame type event */
+		pjmedia_jbuf_get_state(stream->jb, &jb_state);
 		PJ_LOG(5,(stream->port.info.name.ptr, 
-			  "Jitter buffer is bufferring (prefetch=%d)..", 
-			  jb_state.prefetch));
+			  "Jitter buffer is bufferring (prefetch=%d)%s", 
+			  jb_state.prefetch, with_plc));
+
+		stream->jb_last_frm = frame_type;
+		stream->jb_last_frm_cnt = 1;
+	    } else {
+		stream->jb_last_frm_cnt++;
 	    }
-
-	    stream->jb_last_frm = frame_type;
 	    break;
 
@@ -464,7 +477,18 @@
 				     samples_per_frame);
 	    }
+
+	    if (stream->jb_last_frm != frame_type) {
+		/* Report changing frame type event */
+		PJ_LOG(5,(stream->port.info.name.ptr, 
+			  "Jitter buffer starts returning normal frames "
+			  "(after %d empty/lost)",
+			  stream->jb_last_frm_cnt, stream->jb_last_frm));
+
+		stream->jb_last_frm = frame_type;
+		stream->jb_last_frm_cnt = 1;
+	    } else {
+		stream->jb_last_frm_cnt++;
+	    }
 	}
-
-	stream->jb_last_frm = frame_type;
     }
 
@@ -552,4 +576,18 @@
 					    (pj_uint16_t)samples_per_frame);
 	    }
+
+	    if (stream->jb_last_frm != frame_type) {
+		/* Report changing frame type event */
+		PJ_LOG(5,(stream->port.info.name.ptr, 
+			  "Jitter buffer starts returning normal frames "
+			  "(after %d empty/lost)",
+			  stream->jb_last_frm_cnt, stream->jb_last_frm));
+
+		stream->jb_last_frm = frame_type;
+		stream->jb_last_frm_cnt = 1;
+	    } else {
+		stream->jb_last_frm_cnt++;
+	    }
+
 	} else {
 	    status = (*stream->codec->op->recover)(stream->codec,
@@ -561,36 +599,49 @@
 
 	    if (frame_type == PJMEDIA_JB_MISSING_FRAME) {
-		PJ_LOG(5,(stream->port.info.name.ptr,  "Frame lost!"));
+		if (frame_type != stream->jb_last_frm) {
+		    /* Report changing frame type event */
+		    PJ_LOG(5,(stream->port.info.name.ptr, "Frame lost!"));
+
+		    stream->jb_last_frm = frame_type;
+		    stream->jb_last_frm_cnt = 1;
+		} else {
+		    stream->jb_last_frm_cnt++;
+		}
 	    } else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
-		/* Jitter buffer is empty. Check if this is the first "empty" 
-		 * state.
-		 */
 		if (frame_type != stream->jb_last_frm) {
 		    pjmedia_jb_state jb_state;
 
-		    /* Report the state of jitter buffer */
+		    /* Report changing frame type event */
 		    pjmedia_jbuf_get_state(stream->jb, &jb_state);
 		    PJ_LOG(5,(stream->port.info.name.ptr, 
 			      "Jitter buffer empty (prefetch=%d)", 
 			      jb_state.prefetch));
+
+		    stream->jb_last_frm = frame_type;
+		    stream->jb_last_frm_cnt = 1;
+		} else {
+		    stream->jb_last_frm_cnt++;
 		}
 	    } else {
-		pjmedia_jb_state jb_state;
 
 		/* It can only be PJMEDIA_JB_ZERO_PREFETCH frame */
 		pj_assert(frame_type == PJMEDIA_JB_ZERO_PREFETCH_FRAME);
 
-		/* Get the state of jitter buffer */
-		pjmedia_jbuf_get_state(stream->jb, &jb_state);
-
 		if (stream->jb_last_frm != frame_type) {
+		    pjmedia_jb_state jb_state;
+
+		    /* Report changing frame type event */
+		    pjmedia_jbuf_get_state(stream->jb, &jb_state);
 		    PJ_LOG(5,(stream->port.info.name.ptr, 
 			      "Jitter buffer is bufferring (prefetch=%d)",
 			      jb_state.prefetch));
+
+		    stream->jb_last_frm = frame_type;
+		    stream->jb_last_frm_cnt = 1;
+		} else {
+		    stream->jb_last_frm_cnt++;
 		}
 	    }
 	}
-
-	stream->jb_last_frm = frame_type;
     }
 
@@ -1716,4 +1767,5 @@
     stream->rx_event_pt = info->rx_event_pt ? info->rx_event_pt : -1;
     stream->last_dtmf = -1;
+    stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME;
 
     /* Build random RTCP CNAME. CNAME has user@host format */
Index: /pjproject/trunk/pjmedia/src/test/jbuf_test.c
===================================================================
--- /pjproject/trunk/pjmedia/src/test/jbuf_test.c	(revision 3064)
+++ /pjproject/trunk/pjmedia/src/test/jbuf_test.c	(revision 3065)
@@ -82,4 +82,8 @@
 	while (*p && isspace(*p)) ++p;
 	printf("%s", p);
+
+    } else if (*p == '#') {
+	/* Ignore comment line. */
+
     } else {
 	/* Unknown header, perhaps this is the test data */
@@ -123,5 +127,5 @@
 	break;
     case 'J': /* Sequence jumps */
-	(*seq) += 5000;
+	(*seq) += 20;
 	printf("Sequence jumping, from %u to %u\n", *last_seq, *seq);
 	break;
