Ignore:
Timestamp:
Oct 13, 2011 9:02:41 AM (13 years ago)
Author:
nanang
Message:

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.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/branches/1.x/pjmedia/src/pjmedia/jbuf.c

    r3779 r3814  
    3333 
    3434 
    35 /* Minimal difference between JB size and 2*burst-level to perform  
    36  * JB shrinking.  
    37  */ 
    38 #define SAFE_SHRINKING_DIFF     1 
    39  
    40 /* Minimal gap (in ms) between JB shrinking */ 
    41 #define MIN_SHRINK_GAP_MSEC     200 
    42  
    4335/* Invalid sequence number, used as the initial value. */ 
    4436#define INVALID_OFFSET          -9999 
     
    5345 */ 
    5446#define INIT_CYCLE              10 
     47 
     48 
     49/* Minimal difference between JB size and 2*burst-level to perform  
     50 * JB shrinking in static discard algorithm.  
     51 */ 
     52#define STA_DISC_SAFE_SHRINKING_DIFF    1 
    5553 
    5654 
     
    8280 
    8381 
     82typedef void (*discard_algo)(pjmedia_jbuf *jb); 
     83static void jbuf_discard_static(pjmedia_jbuf *jb); 
     84static void jbuf_discard_progressive(pjmedia_jbuf *jb); 
     85 
     86 
    8487struct pjmedia_jbuf 
    8588{ 
     
    98101                                             calculation                    */ 
    99102    int             jb_min_shrink_gap;  /**< How often can we shrink        */ 
     103    discard_algo    jb_discard_algo;    /**< Discard algorithm              */ 
    100104 
    101105    /* Buffer */ 
     
    125129    int             jb_init_cycle_cnt;  /**< status is 'init' until the first  
    126130                                             'put' operation                */ 
    127     int             jb_last_del_seq;    /**< Seq # of last frame deleted    */ 
    128  
    129     int             jb_last_discard_seq;/**< Seq # of last frame discarded  */ 
     131 
     132    int             jb_discard_ref;     /**< Seq # of last frame deleted or 
     133                                             discarded                      */ 
     134    unsigned        jb_discard_dist;    /**< Distance from jb_discard_ref 
     135                                             to perform discard (in frm)    */ 
    130136 
    131137    /* Statistics */ 
     
    393399    enum { MAX_DROPOUT = 3000 }; 
    394400 
    395     assert(frame_size <= framelist->frame_size); 
     401    PJ_ASSERT_RETURN(frame_size <= framelist->frame_size, PJ_EINVAL); 
    396402 
    397403    /* too late or sequence restart */ 
     
    448454        pj_memcpy(framelist->content + pos * framelist->frame_size, 
    449455                  frame, frame_size); 
    450         return PJ_SUCCESS; 
    451     } else { 
    452         /* frame is being discarded */ 
    453         framelist->discarded_num++; 
    454         return PJ_EIGNORED; 
    455     } 
    456 } 
    457  
     456    } 
     457 
     458    return PJ_SUCCESS; 
     459} 
     460 
     461 
     462static pj_status_t jb_framelist_discard(jb_framelist_t *framelist, 
     463                                        int index) 
     464{ 
     465    unsigned pos; 
     466 
     467    PJ_ASSERT_RETURN(index >= framelist->origin && 
     468                     index <  framelist->origin + (int)framelist->size, 
     469                     PJ_EINVAL); 
     470 
     471    /* Get the slot position */ 
     472    pos = (framelist->head + (index - framelist->origin)) % 
     473          framelist->max_count; 
     474 
     475    /* Discard the frame */ 
     476    framelist->frame_type[pos] = PJMEDIA_JB_DISCARDED_FRAME; 
     477    framelist->discarded_num++; 
     478 
     479    return PJ_SUCCESS; 
     480} 
    458481 
    459482 
     
    489512    jb->jb_max_prefetch  = max_count*4/5; 
    490513    jb->jb_max_count     = max_count; 
    491     jb->jb_min_shrink_gap= MIN_SHRINK_GAP_MSEC / ptime; 
     514    jb->jb_min_shrink_gap= PJMEDIA_JBUF_DISC_MIN_GAP / ptime; 
    492515    jb->jb_max_burst     = PJ_MAX(MAX_BURST_MSEC / ptime, max_count*3/4); 
    493     jb->jb_last_discard_seq = 0; 
    494516 
    495517    pj_math_stat_init(&jb->jb_delay); 
    496518    pj_math_stat_init(&jb->jb_burst); 
    497519 
     520    pjmedia_jbuf_set_discard(jb, PJMEDIA_JB_DISCARD_PROGRESSIVE); 
    498521    pjmedia_jbuf_reset(jb); 
    499522 
     
    538561    jb->jb_min_prefetch = min_prefetch; 
    539562    jb->jb_max_prefetch = max_prefetch; 
     563 
     564    return PJ_SUCCESS; 
     565} 
     566 
     567 
     568PJ_DEF(pj_status_t) pjmedia_jbuf_set_discard( pjmedia_jbuf *jb, 
     569                                              pjmedia_jb_discard_algo algo) 
     570{ 
     571    PJ_ASSERT_RETURN(jb, PJ_EINVAL); 
     572    PJ_ASSERT_RETURN(algo >= PJMEDIA_JB_DISCARD_NONE && 
     573                     algo <= PJMEDIA_JB_DISCARD_PROGRESSIVE, 
     574                     PJ_EINVAL); 
     575 
     576    switch(algo) { 
     577    case PJMEDIA_JB_DISCARD_PROGRESSIVE: 
     578        jb->jb_discard_algo = &jbuf_discard_progressive; 
     579        break; 
     580    case PJMEDIA_JB_DISCARD_STATIC: 
     581        jb->jb_discard_algo = &jbuf_discard_static; 
     582        break; 
     583    default: 
     584        jb->jb_discard_algo = NULL; 
     585        break; 
     586    } 
    540587 
    541588    return PJ_SUCCESS; 
     
    552599    jb->jb_max_hist_level= 0; 
    553600    jb->jb_prefetching   = (jb->jb_prefetch != 0); 
     601    jb->jb_discard_dist  = 0; 
    554602 
    555603    jb_framelist_reset(&jb->jb_framelist); 
     
    563611    PJ_LOG(5, (jb->jb_name.ptr, "" 
    564612               "JB summary:\n" 
    565                "  size=%d prefetch=%d level=%d\n" 
     613               "  size=%d/eff=%d prefetch=%d level=%d\n" 
    566614               "  delay (min/max/avg/dev)=%d/%d/%d/%d ms\n" 
    567615               "  burst (min/max/avg/dev)=%d/%d/%d/%d frames\n" 
    568616               "  lost=%d discard=%d empty=%d", 
    569                jb->jb_framelist.size, jb->jb_prefetch, jb->jb_eff_level, 
     617               jb_framelist_size(&jb->jb_framelist),  
     618               jb_framelist_eff_size(&jb->jb_framelist),  
     619               jb->jb_prefetch, jb->jb_eff_level, 
    570620               jb->jb_delay.min, jb->jb_delay.max, jb->jb_delay.mean,  
    571621               pj_math_stat_get_stddev(&jb->jb_delay), 
     
    649699    } 
    650700} 
     701 
     702 
     703static void jbuf_discard_static(pjmedia_jbuf *jb) 
     704{ 
     705    /* These code is used for shortening the delay in the jitter buffer. 
     706     * It needs shrink only when there is possibility of drift. Drift 
     707     * detection is performed by inspecting the jitter buffer size, if 
     708     * its size is twice of current burst level, there can be drift. 
     709     * 
     710     * Moreover, normally drift level is quite low, so JB shouldn't need 
     711     * to shrink aggresively, it will shrink maximum one frame per  
     712     * PJMEDIA_JBUF_DISC_MIN_GAP ms. Theoritically, JB may handle drift level  
     713     * as much as = FRAME_PTIME/PJMEDIA_JBUF_DISC_MIN_GAP * 100% 
     714     * 
     715     * Whenever there is drift, where PUT > GET, this method will keep  
     716     * the latency (JB size) as much as twice of burst level. 
     717     */ 
     718 
     719    /* Shrinking due of drift will be implicitly done by progressive discard, 
     720     * so just disable it when progressive discard is active. 
     721     */ 
     722    int diff, burst_level; 
     723 
     724    burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level); 
     725    diff = jb_framelist_eff_size(&jb->jb_framelist) - burst_level*2; 
     726 
     727    if (diff >= STA_DISC_SAFE_SHRINKING_DIFF) { 
     728        int seq_origin; 
     729 
     730        /* Check and adjust jb_discard_ref, in case there was  
     731         * seq restart  
     732         */ 
     733        seq_origin = jb_framelist_origin(&jb->jb_framelist); 
     734        if (seq_origin < jb->jb_discard_ref) 
     735            jb->jb_discard_ref = seq_origin; 
     736 
     737        if (seq_origin - jb->jb_discard_ref >= jb->jb_min_shrink_gap) 
     738        { 
     739            /* Shrink slowly, one frame per cycle */ 
     740            diff = 1; 
     741 
     742            /* Drop frame(s)! */ 
     743            diff = jb_framelist_remove_head(&jb->jb_framelist, diff); 
     744            jb->jb_discard_ref = jb_framelist_origin(&jb->jb_framelist); 
     745            jb->jb_discard += diff; 
     746 
     747            TRACE__((jb->jb_name.ptr,  
     748                     "JB shrinking %d frame(s), cur size=%d", diff, 
     749                     jb_framelist_eff_size(&jb->jb_framelist))); 
     750        } 
     751    } 
     752} 
     753 
     754 
     755static void jbuf_discard_progressive(pjmedia_jbuf *jb) 
     756{ 
     757    unsigned cur_size, burst_level, overflow, T, discard_dist; 
     758    int last_seq; 
     759 
     760    /* Should be done in PUT operation */ 
     761    if (jb->jb_last_op != JB_OP_PUT) 
     762        return; 
     763 
     764    /* Check if latency is longer than burst */ 
     765    cur_size = jb_framelist_eff_size(&jb->jb_framelist); 
     766    burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level); 
     767    if (cur_size <= burst_level) { 
     768        /* Reset any scheduled discard */ 
     769        jb->jb_discard_dist = 0; 
     770        return; 
     771    } 
     772 
     773    /* Estimate discard duration needed for adjusting latency */ 
     774    if (burst_level <= PJMEDIA_JBUF_PRO_DISC_MIN_BURST) 
     775        T = PJMEDIA_JBUF_PRO_DISC_T1; 
     776    else if (burst_level >= PJMEDIA_JBUF_PRO_DISC_MAX_BURST) 
     777        T = PJMEDIA_JBUF_PRO_DISC_T2; 
     778    else 
     779        T = PJMEDIA_JBUF_PRO_DISC_T1 +  
     780            (PJMEDIA_JBUF_PRO_DISC_T2 - PJMEDIA_JBUF_PRO_DISC_T1) * 
     781            (burst_level - PJMEDIA_JBUF_PRO_DISC_MIN_BURST) / 
     782            (PJMEDIA_JBUF_PRO_DISC_MAX_BURST-PJMEDIA_JBUF_PRO_DISC_MIN_BURST); 
     783 
     784    /* Calculate current discard distance */ 
     785    overflow = cur_size - burst_level; 
     786    discard_dist = T / overflow / jb->jb_frame_ptime; 
     787 
     788    /* Get last seq number in the JB */ 
     789    last_seq = jb_framelist_origin(&jb->jb_framelist) + 
     790               jb_framelist_size(&jb->jb_framelist) - 1; 
     791 
     792    /* Setup new discard schedule if none, otherwise, update the existing 
     793     * discard schedule (can be delayed or accelerated). 
     794     */ 
     795    if (jb->jb_discard_dist == 0) { 
     796        /* Setup new discard schedule */ 
     797        jb->jb_discard_ref = last_seq; 
     798    } else if (last_seq < jb->jb_discard_ref) { 
     799        /* Seq restarted, update discard reference */ 
     800        jb->jb_discard_ref = last_seq; 
     801    } 
     802    jb->jb_discard_dist = PJ_MAX(jb->jb_min_shrink_gap, (int)discard_dist); 
     803 
     804    /* Check if we need to discard now */ 
     805    if (last_seq >= (jb->jb_discard_ref + (int)jb->jb_discard_dist)) { 
     806        int discard_seq; 
     807         
     808        discard_seq = jb->jb_discard_ref + jb->jb_discard_dist; 
     809        if (discard_seq < jb_framelist_origin(&jb->jb_framelist)) 
     810            discard_seq = jb_framelist_origin(&jb->jb_framelist); 
     811 
     812        jb_framelist_discard(&jb->jb_framelist, discard_seq); 
     813 
     814        TRACE__((jb->jb_name.ptr,  
     815                "Discard #%d: ref=#%d dist=%d orig=%d size=%d/%d " 
     816                "burst=%d/%d", 
     817                discard_seq, 
     818                jb->jb_discard_ref, 
     819                jb->jb_discard_dist, 
     820                jb_framelist_origin(&jb->jb_framelist), 
     821                cur_size, 
     822                jb_framelist_size(&jb->jb_framelist), 
     823                jb->jb_eff_level, 
     824                burst_level)); 
     825 
     826        /* Update discard reference */ 
     827        jb->jb_discard_ref = discard_seq; 
     828    } 
     829} 
     830     
    651831 
    652832PJ_INLINE(void) jbuf_update(pjmedia_jbuf *jb, int oper) 
     
    685865    } 
    686866 
    687     /* These code is used for shortening the delay in the jitter buffer. 
    688      * It needs shrink only when there is possibility of drift. Drift 
    689      * detection is performed by inspecting the jitter buffer size, if 
    690      * its size is twice of current burst level, there can be drift. 
    691      * 
    692      * Moreover, normally drift level is quite low, so JB shouldn't need 
    693      * to shrink aggresively, it will shrink maximum one frame per  
    694      * MIN_SHRINK_GAP_MSEC ms. Theoritically, JB may handle drift level  
    695      * as much as = FRAME_PTIME/MIN_SHRINK_GAP_MSEC * 100% 
    696      * 
    697      * Whenever there is drift, where PUT > GET, this method will keep  
    698      * the latency (JB size) as much as twice of burst level. 
    699      */ 
    700  
    701     /* Shrinking due of drift will be implicitly done by progressive discard, 
    702      * so just disable it when progressive discard is active. 
    703      */ 
    704 #if !PROGRESSIVE_DISCARD 
    705  
    706     if (jb->jb_status != JB_STATUS_PROCESSING) 
    707         return; 
    708  
    709     { 
    710         int diff, burst_level; 
    711  
    712         burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level); 
    713         diff = jb_framelist_eff_size(&jb->jb_framelist) - burst_level*2; 
    714  
    715         if (diff >= SAFE_SHRINKING_DIFF) { 
    716             int seq_origin; 
    717  
    718             /* Check and adjust jb_last_del_seq, in case there was  
    719              * seq restart  
    720              */ 
    721             seq_origin = jb_framelist_origin(&jb->jb_framelist); 
    722             if (seq_origin < jb->jb_last_del_seq) 
    723                 jb->jb_last_del_seq = seq_origin; 
    724  
    725             if (seq_origin - jb->jb_last_del_seq >= jb->jb_min_shrink_gap) 
    726             { 
    727                 /* Shrink slowly, one frame per cycle */ 
    728                 diff = 1; 
    729  
    730                 /* Drop frame(s)! */ 
    731                 diff = jb_framelist_remove_head(&jb->jb_framelist, diff); 
    732                 jb->jb_last_del_seq = jb_framelist_origin(&jb->jb_framelist); 
    733                 jb->jb_discard += diff; 
    734  
    735                 TRACE__((jb->jb_name.ptr,  
    736                          "JB shrinking %d frame(s), cur size=%d", diff, 
    737                          jb_framelist_eff_size(&jb->jb_framelist))); 
    738             } 
    739         } 
    740     } 
    741  
    742 #endif /* !PROGRESSIVE_DISCARD */ 
    743  
     867    /* Call discard algorithm */ 
     868    if (jb->jb_status == JB_STATUS_PROCESSING && jb->jb_discard_algo) { 
     869        (*jb->jb_discard_algo)(jb); 
     870    } 
    744871} 
    745872 
     
    760887{ 
    761888    pj_size_t min_frame_size; 
    762     int new_size, cur_size, frame_type = PJMEDIA_JB_NORMAL_FRAME; 
     889    int new_size, cur_size; 
    763890    pj_status_t status; 
    764891 
    765892    cur_size = jb_framelist_eff_size(&jb->jb_framelist); 
    766  
    767 #if PROGRESSIVE_DISCARD 
    768     { 
    769         unsigned interval, seq_delta; 
    770         unsigned burst_level = 0, overflow_pct = 0; 
    771  
    772         /* Calculating percentage of burst overflow */ 
    773         if (jb->jb_status == JB_STATUS_PROCESSING) { 
    774             burst_level = PJ_MAX(jb->jb_eff_level, jb->jb_level); 
    775             if (cur_size > (int)burst_level) 
    776                 overflow_pct = (cur_size - burst_level) * 100 / burst_level; 
    777         } 
    778  
    779         /* Deciding discard interval (aggressiveness) based on 
    780          * burst overflow percentage. 
    781          */ 
    782         if (burst_level <= 5 && overflow_pct < 200) { 
    783             /* Tolerate spikes on relatively small burst level */ 
    784             interval = 0; 
    785         } else if (overflow_pct >= 200) { 
    786             /* Overflow >= 200% */ 
    787             interval = 4; 
    788         } else if (overflow_pct >= 100) { 
    789             /* Overflow >= 100% */ 
    790             interval = 5; 
    791         } else if (overflow_pct >= 10) { 
    792             /* Overflow >= 10% */ 
    793             interval = 7; 
    794         } else { 
    795             /* Overflow < 10%, tolerable */ 
    796             interval = 0; 
    797         } 
    798  
    799         /* Do the math now to see if we should discard this packet. 
    800          * Calculate the distance from the last sequence 
    801          * discarded. If negative, then this is an out of 
    802          * order frame so just proceed with discard. Else 
    803          * see if the delta is at least the intervals worth away 
    804          * from the last frame discarded. 
    805          */ 
    806         seq_delta = (pj_uint16_t)(frame_seq - jb->jb_last_discard_seq); 
    807         if ((0 != interval) && (seq_delta >= interval)) { 
    808             frame_type = PJMEDIA_JB_DISCARDED_FRAME; 
    809             jb->jb_last_discard_seq = frame_seq; 
    810  
    811             TRACE__((jb->jb_name.ptr,  
    812                     "Discarding frame #%d: eff=%d disc=%d orig:%d" 
    813                     " seq_delta:%d", 
    814                     frame_seq, 
    815                     cur_size, 
    816                     jb_framelist_size(&jb->jb_framelist) - cur_size, 
    817                     jb_framelist_origin(&jb->jb_framelist), 
    818                     (int)seq_delta)); 
    819         } 
    820     } 
    821 #endif /* PROGRESSIVE_DISCARD */ 
    822      
    823893 
    824894    /* Attempt to store the frame */ 
    825895    min_frame_size = PJ_MIN(frame_size, jb->jb_frame_size); 
    826896    status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame, 
    827                                  min_frame_size, bit_info, frame_type); 
     897                                 min_frame_size, bit_info, 
     898                                 PJMEDIA_JB_NORMAL_FRAME); 
    828899     
    829900    /* Jitter buffer is full, remove some older frames */ 
     
    832903        unsigned removed; 
    833904 
    834         /* When progressive discard activated, just remove as few as possible 
    835          * just to make this frame in. 
    836          */ 
    837 #if PROGRESSIVE_DISCARD 
    838         /* The cases of seq-jump, out-of-order, and seq restart should have 
     905        /* Remove as few as possible just to make this frame in. Note that 
     906         * the cases of seq-jump, out-of-order, and seq restart should have 
    839907         * been handled/normalized by previous call of jb_framelist_put_at(). 
    840908         * So we're confident about 'distance' value here. 
     
    843911                   jb->jb_max_count + 1; 
    844912        pj_assert(distance > 0); 
    845 #else 
    846         distance = PJ_MAX(jb->jb_max_count/4, 1); 
    847 #endif 
     913 
    848914        removed = jb_framelist_remove_head(&jb->jb_framelist, distance); 
    849915        status = jb_framelist_put_at(&jb->jb_framelist, frame_seq, frame, 
    850                                      min_frame_size, bit_info, frame_type); 
     916                                     min_frame_size, bit_info, 
     917                                     PJMEDIA_JB_NORMAL_FRAME); 
    851918 
    852919        jb->jb_discard += removed; 
Note: See TracChangeset for help on using the changeset viewer.