diff options
author | Nanang Izzuddin <nanang@teluu.com> | 2010-01-20 01:02:37 +0000 |
---|---|---|
committer | Nanang Izzuddin <nanang@teluu.com> | 2010-01-20 01:02:37 +0000 |
commit | 7ece324274c1012b34fb41dbee67eb5f51924f03 (patch) | |
tree | 63ebd56f92760098fc4f76eb018617db5fb0b5d8 | |
parent | d8789cab8ce1fa54b1f02f4ab448fc83f9af2a44 (diff) |
Ticket #969:
- implemented progressive discard algorithm, discard rate is calculated from ratio of effective size to effective burst level.
- updated jbuf to clarify prefetch and burst level distinction, previously they are stored in same var, i.e: prefetch, while the semantic is actually different.
- updated STABLE_HISTORY_LIMIT in jbuf, it is now 20 (was 100), to adjust burst level faster.
- added test case of periodic-spike-burst-case in jbtest.dat for testing the new algorithm.
- updated stream to limit the rate of jbuf empty/lost log messages, it will only log first empty/lost event, then log again once jbuf returning normal frame (also counter of previous empty/lost frames).
- minor updates on jbuf.c: variable names, logs, added burst to jbuf state.
- minor updates on jbuf_test.c: handle comment in test session header, seq jump is now 20 (was 5000).
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3065 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r-- | pjmedia/build/Jbtest.dat | 241 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/jbuf.h | 1 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/jbuf.c | 458 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/stream.c | 136 | ||||
-rw-r--r-- | pjmedia/src/test/jbuf_test.c | 6 |
5 files changed, 657 insertions, 185 deletions
diff --git a/pjmedia/build/Jbtest.dat b/pjmedia/build/Jbtest.dat index 3e2169c4..41eb0ed7 100644 --- a/pjmedia/build/Jbtest.dat +++ b/pjmedia/build/Jbtest.dat @@ -1,3 +1,47 @@ +# +# ====================================================================== +# 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 !burst 1 @@ -302,10 +346,10 @@ PGGGGPPPGPPGPPPGGPGG PGPGPPGGPPGGPPPGGGPG PGGGGPPPGPPGPPPGGPGG = Fixed mode prefetch 10, PUT burst at the beginning %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 +!delay 20 <- average delay, twice of minimal prefetch PPPPPPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPPPPPP PPPPPPPPPP PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPGPGPGPGPGPG PGPGPGPGPG @@ -322,3 +366,196 @@ 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 +. diff --git a/pjmedia/include/pjmedia/jbuf.h b/pjmedia/include/pjmedia/jbuf.h index cd5e9f13..4f5a12bc 100644 --- a/pjmedia/include/pjmedia/jbuf.h +++ b/pjmedia/include/pjmedia/jbuf.h @@ -76,6 +76,7 @@ struct pjmedia_jb_state unsigned max_prefetch; /**< Maximum allowed prefetch, in frms. */ /* Status */ + unsigned burst; /**< Current burst level, in frames */ unsigned prefetch; /**< Current prefetch value, in frames */ unsigned size; /**< Current buffer size, in frames. */ diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c index 139fd043..861aa261 100644 --- a/pjmedia/src/pjmedia/jbuf.c +++ b/pjmedia/src/pjmedia/jbuf.c @@ -72,8 +72,12 @@ typedef struct jb_framelist_t /* States */ 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; @@ -85,7 +89,7 @@ struct pjmedia_jbuf 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_init_prefetch; /**< Initial prefetch */ int jb_min_prefetch; /**< Minimum allowable prefetch */ int jb_max_prefetch; /**< Maximum allowable prefetch */ int jb_max_burst; /**< maximum possible burst, whenever @@ -108,6 +112,7 @@ struct pjmedia_jbuf lower then the prefetch num */ 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 of the framelist->content @@ -120,6 +125,8 @@ struct pjmedia_jbuf 'put' operation */ 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 (in ms) */ @@ -135,6 +142,23 @@ struct pjmedia_jbuf #define JB_STATUS_PROCESSING 1 #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. */ @@ -145,6 +169,8 @@ struct pjmedia_jbuf #endif 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, jb_framelist_t *framelist, @@ -174,6 +200,7 @@ static pj_status_t jb_framelist_init( pj_pool_t *pool, sizeof(framelist->bit_info[0])* framelist->max_count); + return jb_framelist_reset(framelist); } @@ -189,6 +216,8 @@ static pj_status_t jb_framelist_reset(jb_framelist_t *framelist) framelist->head = 0; framelist->origin = INVALID_OFFSET; framelist->size = 0; + framelist->discarded_num = 0; + //pj_bzero(framelist->content, // framelist->frame_size * @@ -217,43 +246,67 @@ static unsigned jb_framelist_size(const jb_framelist_t *framelist) } +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, pjmedia_jb_frame_type *p_type, pj_uint32_t *bit_info) { 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); + /* Skip discarded frames */ + while (framelist->frame_type[framelist->head] == + PJMEDIA_JB_DISCARDED_FRAME) + { + jb_framelist_remove_head(framelist, 1); + } - return PJ_FALSE; + /* 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) { @@ -264,6 +317,7 @@ static unsigned jb_framelist_remove_head(jb_framelist_t *framelist, /* may be done in two steps if overlapping */ unsigned step1,step2; unsigned tmp = framelist->head+count; + unsigned i; if (tmp > framelist->max_count) { step1 = framelist->max_count - framelist->head; @@ -273,6 +327,13 @@ static unsigned jb_framelist_remove_head(jb_framelist_t *framelist, 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--; + } + } + //pj_bzero(framelist->content + // framelist->head * framelist->frame_size, // step1*framelist->frame_size); @@ -280,9 +341,15 @@ static unsigned jb_framelist_remove_head(jb_framelist_t *framelist, PJMEDIA_JB_MISSING_FRAME, 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); pj_memset(framelist->frame_type, @@ -306,19 +373,20 @@ 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) + pj_uint32_t bit_info, + unsigned frame_type) { int distance; - unsigned where; + unsigned pos; enum { MAX_MISORDER = 100 }; enum { MAX_DROPOUT = 3000 }; 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 { /* sequence restart */ @@ -328,6 +396,7 @@ static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist, /* if jbuf is empty, just reset the origin */ if (framelist->size == 0) { + pj_assert(framelist->discarded_num == 0); framelist->origin = index; } @@ -348,22 +417,31 @@ static pj_status_t jb_framelist_put_at(jb_framelist_t *framelist, } /* 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; + } } @@ -401,6 +479,8 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_create(pj_pool_t *pool, jb->jb_max_count = max_count; 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); @@ -423,7 +503,7 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_set_fixed( pjmedia_jbuf *jb, 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; + jb->jb_prefetch = jb->jb_init_prefetch = prefetch; return PJ_SUCCESS; } @@ -443,7 +523,7 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_set_adaptive( pjmedia_jbuf *jb, max_prefetch <= jb->jb_max_count, 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; @@ -468,18 +548,18 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_reset(pjmedia_jbuf *jb) 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); } @@ -489,55 +569,66 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb) { 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) { + if (jb->jb_level < jb->jb_eff_level) { - enum { STABLE_HISTORY_LIMIT = 100 }; + 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 */ jb->jb_max_hist_level = 0; 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) { + else if (jb->jb_level > jb->jb_eff_level) { - /* 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; + /* 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; /* Do not reset max_hist_level. */ //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)); } /* Level is unchanged */ @@ -548,8 +639,6 @@ static void jbuf_calculate_jitter(pjmedia_jbuf *jb) 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; @@ -593,33 +682,49 @@ PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper) * 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; - burst_level = PJ_MAX(jb->jb_prefetch, jb->jb_level); - diff = jb_framelist_size(&jb->jb_framelist) - burst_level*2; + { + int diff, burst_level; - 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; + burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level); + diff = jb_framelist_eff_size(&jb->jb_framelist) - burst_level*2; - if (jb->jb_framelist.origin - jb->jb_last_del_seq >= - jb->jb_min_shrink_gap) - { - /* Shrink slowly, one frame per cycle */ - diff = 1; + if (diff >= SAFE_SHRINKING_DIFF) { + int seq_origin; - /* 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; + /* 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; - TRACE__((jb->jb_name.ptr, - "JB shrinking %d frame(s), cur size=%d", diff, - jb_framelist_size(&jb->jb_framelist))); + 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 */ + } PJ_DEF(void) pjmedia_jbuf_put_frame( pjmedia_jbuf *jb, @@ -638,31 +743,102 @@ PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb, pj_bool_t *discarded) { 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 */ if (discarded) @@ -671,11 +847,11 @@ PJ_DEF(void) pjmedia_jbuf_put_frame2(pjmedia_jbuf *jb, 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) + 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 jb->jb_discard++; @@ -700,24 +876,7 @@ PJ_DEF(void) pjmedia_jbuf_get_frame2(pjmedia_jbuf *jb, char *p_frame_type, 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 * minimum prefetch. @@ -729,33 +888,50 @@ PJ_DEF(void) pjmedia_jbuf_get_frame2(pjmedia_jbuf *jb, *size = 0; 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++; } 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); + 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++; + } - /* 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; + /* 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++; - } + /* 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; - /* 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); + jb->jb_empty++; + } } jb->jb_level++; @@ -774,8 +950,9 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_get_state( const pjmedia_jbuf *jb, state->min_prefetch = jb->jb_min_prefetch; 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; state->min_delay = jb->jb_delay.min; @@ -789,3 +966,4 @@ PJ_DEF(pj_status_t) pjmedia_jbuf_get_state( const pjmedia_jbuf *jb, return PJ_SUCCESS; } + diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index fcaa725b..9e85054b 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -120,6 +120,7 @@ struct pjmedia_stream pj_mutex_t *jb_mutex; 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. */ @@ -325,15 +326,23 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) /* Either PLC failed or PLC not supported/enabled */ 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, * activate PLC to smoothen the fade-out, otherwise zero * the frame. @@ -342,9 +351,6 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) //lost and not the subsequent ones. //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 && stream->codec_param.setting.plc && @@ -369,13 +375,6 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) 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)); - } if (samples_count < samples_required) { @@ -384,19 +383,29 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) samples_count = samples_required; } - 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.. */ if (stream->codec->op->recover && stream->codec_param.setting.plc && @@ -419,25 +428,29 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) } while (samples_count < samples_required && 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"; } if (samples_count < samples_required) { pjmedia_zero_samples(p_out_samp + samples_count, samples_required - samples_count); samples_count = samples_required; - PJ_LOG(5,(stream->port.info.name.ptr, - "Jitter buffer is bufferring (prefetch=%d)..", - jb_state.prefetch)); } - 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 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++; + } break; } else { @@ -463,9 +476,20 @@ static pj_status_t get_frame( pjmedia_port *port, pjmedia_frame *frame) pjmedia_zero_samples(p_out_samp + samples_count, samples_per_frame); } - } - stream->jb_last_frm = frame_type; + 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++; + } + } } @@ -551,6 +575,20 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) pjmedia_frame_ext_append_subframe(f, NULL, 0, (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, 0, frame); @@ -560,38 +598,51 @@ static pj_status_t get_frame_ext( pjmedia_port *port, pjmedia_frame *frame) } 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; } return PJ_SUCCESS; @@ -1715,6 +1766,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, stream->tx_event_pt = info->tx_event_pt ? info->tx_event_pt : -1; 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 */ stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20); diff --git a/pjmedia/src/test/jbuf_test.c b/pjmedia/src/test/jbuf_test.c index 976995dd..c2a3b1d4 100644 --- a/pjmedia/src/test/jbuf_test.c +++ b/pjmedia/src/test/jbuf_test.c @@ -81,6 +81,10 @@ static pj_bool_t parse_test_headers(char *line, test_param_t *param, ++p; while (*p && isspace(*p)) ++p; printf("%s", p); + + } else if (*p == '#') { + /* Ignore comment line. */ + } else { /* Unknown header, perhaps this is the test data */ @@ -122,7 +126,7 @@ static pj_bool_t process_test_data(char data, pjmedia_jbuf *jb, printf("Sequence restarting, from %u to %u\n", *last_seq, *seq); break; case 'J': /* Sequence jumps */ - (*seq) += 5000; + (*seq) += 20; printf("Sequence jumping, from %u to %u\n", *last_seq, *seq); break; case 'D': /* Frame duplicated */ |