Changeset 2850


Ignore:
Timestamp:
Aug 1, 2009 9:20:59 AM (11 years ago)
Author:
bennylp
Message:

Initial commit for ticket #929: Improve packet lost concealment (PLC) when handling burst of lost packets

WSOLA improvements:

  • Introduce fade-out and fade-in effect
  • Limit the number of continuous synthetic samples (only take effect when fading is used)
  • Export many settings as macros:
    • PJMEDIA_WSOLA_DELAY_MSEC (was HANNING_PTIME)
    • PJMEDIA_WSOLA_TEMPLATE_LENGTH_MSEC (was TEMPLATE_PTIME)
    • PJMEDIA_WSOLA_MAX_EXPAND_MSEC

PLC:

  • added compile time macro PJMEDIA_WSOLA_PLC_NO_FADING to disable fading (default enabled)

Stream:

  • fixed bug when stream is not PLC-ing subsequent packet loss (only the first)
  • also add maximum PLC limit just as precaution if PLC doesn't limit number of synthetic frames
  • unrelated: fixed warning about unused send_keep_alive() function

Delaybuf:

  • modified to NOT use fading in WSOLA since we don't expect it to generate many continuous synthetic frames
Location:
pjproject/trunk/pjmedia
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/include/pjmedia/config.h

    r2759 r2850  
    150150#ifndef PJMEDIA_WSOLA_IMP 
    151151#   define PJMEDIA_WSOLA_IMP                PJMEDIA_WSOLA_IMP_WSOLA 
     152#endif 
     153 
     154 
     155/** 
     156 * Specify the default maximum duration of synthetic audio that is generated 
     157 * by WSOLA. This value should be long enough to cover burst of packet losses.  
     158 * but not too long, because as the duration increases the quality would  
     159 * degrade considerably. 
     160 * 
     161 * Note that this limit is only applied when fading is enabled in the WSOLA 
     162 * session. 
     163 * 
     164 * Default: 80 
     165 */ 
     166#ifndef PJMEDIA_WSOLA_MAX_EXPAND_MSEC 
     167#   define PJMEDIA_WSOLA_MAX_EXPAND_MSEC    80 
     168#endif 
     169 
     170 
     171/** 
     172 * Specify WSOLA template length, in milliseconds. The longer the template, 
     173 * the smoother signal to be generated at the expense of more computation 
     174 * needed, since the algorithm will have to compare more samples to find 
     175 * the most similar pitch. 
     176 * 
     177 * Default: 5 
     178 */ 
     179#ifndef PJMEDIA_WSOLA_TEMPLATE_LENGTH_MSEC 
     180#   define PJMEDIA_WSOLA_TEMPLATE_LENGTH_MSEC   5 
     181#endif 
     182 
     183 
     184/** 
     185 * Specify WSOLA algorithm delay, in milliseconds. The algorithm delay is 
     186 * used to merge synthetic samples with real samples in the transition 
     187 * between real to synthetic and vice versa. The longer the delay, the  
     188 * smoother signal to be generated, at the expense of longer latency and 
     189 * a slighty more computation. 
     190 * 
     191 * Default: 5 
     192 */ 
     193#ifndef PJMEDIA_WSOLA_DELAY_MSEC 
     194#   define PJMEDIA_WSOLA_DELAY_MSEC         5 
     195#endif 
     196 
     197 
     198/** 
     199 * Set this to non-zero to disable fade-out/in effect in the PLC when it 
     200 * instructs WSOLA to generate synthetic frames. The use of fading may 
     201 * or may not improve the quality of audio, depending on the nature of 
     202 * packet loss and the type of audio input (e.g. speech vs music). 
     203 * Disabling fading also implicitly remove the maximum limit of synthetic 
     204 * audio samples generated by WSOLA (see PJMEDIA_WSOLA_MAX_EXPAND_MSEC). 
     205 * 
     206 * Default: 0 
     207 */ 
     208#ifndef PJMEDIA_WSOLA_PLC_NO_FADING 
     209#   define PJMEDIA_WSOLA_PLC_NO_FADING      0 
    152210#endif 
    153211 
  • pjproject/trunk/pjmedia/include/pjmedia/wsola.h

    r2394 r2850  
    7070     * non-contiguous buffer. 
    7171     */ 
    72     PJMEDIA_WSOLA_NO_DISCARD = 4 
     72    PJMEDIA_WSOLA_NO_DISCARD = 4, 
     73 
     74    /** 
     75     * Disable fade-in and fade-out feature in the transition between 
     76     * actual and synthetic frames in WSOLA. With fade feature enabled,  
     77     * WSOLA will only generate a limited number of synthetic frames  
     78     * (configurable with #pjmedia_wsola_set_max_expand()), fading out  
     79     * the volume on every more samples it generates, and when it reaches 
     80     * the limit it will only generate silence. 
     81     */ 
     82    PJMEDIA_WSOLA_NO_FADING = 8 
    7383}; 
    7484 
     
    94104                                          unsigned options, 
    95105                                          pjmedia_wsola **p_wsola); 
     106 
     107 
     108/** 
     109 * Specify maximum number of continuous synthetic frames that can be 
     110 * generated by WSOLA, in milliseconds. This option will only take 
     111 * effect if fading is not disabled via the option when the WSOLA 
     112 * session was created. Default value is PJMEDIA_WSOLA_MAX_EXPAND_MSEC 
     113 * (see also the documentation of PJMEDIA_WSOLA_MAX_EXPAND_MSEC for 
     114 * more information). 
     115 * 
     116 * @param wsola     The WSOLA session 
     117 * @param msec      The duration. 
     118 * 
     119 * @return          PJ_SUCCESS normally. 
     120 */ 
     121PJ_DECL(pj_status_t) pjmedia_wsola_set_max_expand(pjmedia_wsola *wsola, 
     122                                                  unsigned msec); 
    96123 
    97124 
  • pjproject/trunk/pjmedia/src/pjmedia/delaybuf.c

    r2728 r2850  
    129129    /* Create WSOLA */ 
    130130    status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1, 
    131                                   0, &b->wsola); 
     131                                  PJMEDIA_WSOLA_NO_FADING, &b->wsola); 
    132132    if (status != PJ_SUCCESS) 
    133133        return status; 
  • pjproject/trunk/pjmedia/src/pjmedia/plc_common.c

    r2394 r2850  
    126126{ 
    127127    struct wsola_plc *o; 
     128    unsigned flag; 
    128129    pj_status_t status; 
    129130 
     
    133134    o->prev_lost = PJ_FALSE; 
    134135 
     136    flag = PJMEDIA_WSOLA_NO_DISCARD; 
     137    if (PJMEDIA_WSOLA_PLC_NO_FADING) 
     138        flag |= PJMEDIA_WSOLA_NO_FADING; 
     139 
    135140    status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1, 
    136                                   PJMEDIA_WSOLA_NO_DISCARD, &o->wsola); 
     141                                  flag, &o->wsola); 
    137142    if (status != PJ_SUCCESS) 
    138143        return NULL; 
  • pjproject/trunk/pjmedia/src/pjmedia/stream.c

    r2844 r2850  
    4444#define BYTES_PER_SAMPLE                2 
    4545 
     46/* Limit the number of synthetic audio samples that are generated by PLC. 
     47 * Normally PLC should have it's own means to limit the number of 
     48 * synthetic frames, so we need to set this to a reasonably large value 
     49 * just as precaution 
     50 */ 
     51#define MAX_PLC_MSEC                    240 
     52 
    4653/** 
    4754 * Media channel. 
     
    99106    unsigned                 enc_buf_count; /**< Number of samples in the 
    100107                                                 encoding buffer.           */ 
     108 
     109    unsigned                 plc_cnt;       /**< # of consecutive PLC frames*/ 
     110    unsigned                 max_plc_cnt;   /**< Max # of PLC frames        */ 
    101111 
    102112    unsigned                 vad_enabled;   /**< VAD enabled in param.      */ 
     
    198208} 
    199209 
     210#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0 
    200211/* 
    201  * Send keep-alive packet. 
     212 * Send keep-alive packet using non-codec frame. 
    202213 */ 
    203214static void send_keep_alive_packet(pjmedia_stream *stream) 
    204215{ 
    205 #if defined(PJMEDIA_STREAM_ENABLE_KA) && \ 
    206     PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP 
     216#if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP 
    207217 
    208218    /* Keep-alive packet is empty RTP */ 
     
    225235    TRC_((stream->port.info.name.ptr, "Keep-alive sent (empty RTP)")); 
    226236 
    227 #elif defined(PJMEDIA_STREAM_ENABLE_KA) && \ 
    228       PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER 
     237#elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER 
    229238 
    230239    /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */ 
     
    244253#endif 
    245254} 
     255#endif  /* defined(PJMEDIA_STREAM_ENABLE_KA) */ 
    246256 
    247257/* 
     
    295305            /* Activate PLC */ 
    296306            if (stream->codec->op->recover &&  
    297                 stream->codec_param.setting.plc)  
     307                stream->codec_param.setting.plc && 
     308                stream->plc_cnt < stream->max_plc_cnt)  
    298309            { 
    299310                pjmedia_frame frame_out; 
     
    305316                                                       &frame_out); 
    306317 
     318                ++stream->plc_cnt; 
     319 
    307320            } else { 
    308321                status = -1; 
     
    319332                          "Lost frame recovered")); 
    320333            } 
    321              
     334 
    322335        } else if (frame_type == PJMEDIA_JB_ZERO_EMPTY_FRAME) { 
    323336 
     
    326339             * the frame.  
    327340             */ 
    328             if (frame_type != stream->jb_last_frm) { 
     341            //Using this "if" will only invoke PLC for the first packet 
     342            //lost and not the subsequent ones. 
     343            //if (frame_type != stream->jb_last_frm) { 
     344            if (1) { 
    329345                pjmedia_jb_state jb_state; 
    330346                const char *with_plc = ""; 
     
    332348                /* Activate PLC to smoothen the missing frame */ 
    333349                if (stream->codec->op->recover &&  
    334                     stream->codec_param.setting.plc)  
     350                    stream->codec_param.setting.plc && 
     351                    stream->plc_cnt < stream->max_plc_cnt)  
    335352                { 
    336353                    pjmedia_frame frame_out; 
     
    344361                        if (status != PJ_SUCCESS) 
    345362                            break; 
     363 
    346364                        samples_count += samples_per_frame; 
    347  
    348                     } while (samples_count < samples_required); 
     365                        ++stream->plc_cnt; 
     366 
     367                    } while (samples_count < samples_required && 
     368                             stream->plc_cnt < stream->max_plc_cnt); 
    349369 
    350370                    with_plc = ", plc invoked"; 
     
    380400            /* Always activate PLC when it's available.. */ 
    381401            if (stream->codec->op->recover &&  
    382                 stream->codec_param.setting.plc)  
     402                stream->codec_param.setting.plc && 
     403                stream->plc_cnt < stream->max_plc_cnt)  
    383404            { 
    384405                pjmedia_frame frame_out; 
     
    394415                    samples_count += samples_per_frame; 
    395416 
    396                 } while (samples_count < samples_required); 
    397  
    398                 if (stream->jb_last_frm != frame_type) { 
     417                    ++stream->plc_cnt; 
     418 
     419                } while (samples_count < samples_required && 
     420                         stream->plc_cnt < stream->max_plc_cnt); 
     421 
     422                //if (stream->jb_last_frm != frame_type) { 
     423                if (1) { 
    399424                    PJ_LOG(5,(stream->port.info.name.ptr,  
    400425                              "Jitter buffer is bufferring with plc (prefetch=%d)", 
     
    419444            /* Got "NORMAL" frame from jitter buffer */ 
    420445            pjmedia_frame frame_in, frame_out; 
     446 
     447            stream->plc_cnt = 0; 
    421448 
    422449            /* Decode */ 
     
    18221849        ++stream->frame_size; 
    18231850    } 
     1851 
     1852    /* How many consecutive PLC frames can be generated */ 
     1853    stream->max_plc_cnt = (MAX_PLC_MSEC+stream->codec_param.info.frm_ptime-1)/ 
     1854                            stream->codec_param.info.frm_ptime; 
    18241855 
    18251856#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0) 
  • pjproject/trunk/pjmedia/src/pjmedia/wsola.c

    r2660 r2850  
    7171 
    7272/* Template size, in msec */ 
    73 #define TEMPLATE_PTIME  5 
     73#define TEMPLATE_PTIME  PJMEDIA_WSOLA_TEMPLATE_LENGTH_MSEC 
    7474 
    7575/* Hanning window size, in msec */ 
    76 #define HANNING_PTIME   5 
     76#define HANNING_PTIME   PJMEDIA_WSOLA_DELAY_MSEC 
    7777 
    7878/* Number of frames in erase buffer */ 
     
    8484/* Maximum distance from template for find_pitch() of expansion, in frames */ 
    8585#define EXP_MAX_DIST    HIST_CNT 
     86 
     87/* Duration of a continuous synthetic frames after which the volume  
     88 * of the synthetic frame will be set to zero with fading-out effect. 
     89 */ 
     90#define MAX_EXPAND_MSEC PJMEDIA_WSOLA_MAX_EXPAND_MSEC 
    8691 
    8792 
     
    132137 
    133138    pj_uint16_t          min_extra;         /* Minimum extra (const)        */ 
    134     pj_uint16_t          expand_cnt;        /* Consecutive expansion count  */ 
     139    unsigned             max_expand_cnt;    /* Max # of synthetic samples   */ 
     140    unsigned             fade_out_pos;      /* Last fade-out position       */ 
    135141    pj_uint16_t          expand_sr_min_dist;/* Minimum distance from template  
    136142                                               for find_pitch() on expansion 
     
    147153 
    148154    pj_timestamp         ts;                /* Running timestamp.           */ 
     155 
    149156}; 
    150157 
     
    433440#endif  /* PJ_HAS_FLOATING_POINT */ 
    434441 
     442/* Apply fade-in to the buffer. 
     443 *  - fade_cnt is the number of samples on which the volume 
     444 *       will go from zero to 100% 
     445 *  - fade_pos is current sample position within fade_cnt range. 
     446 *       It is zero for the first sample, so the first sample will 
     447 *       have zero volume. This value is increasing. 
     448 */ 
     449static void fade_in(pj_int16_t buf[], int count, 
     450                    int fade_in_pos, int fade_cnt) 
     451{ 
     452#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0 
     453    float fade_pos = (float)fade_in_pos; 
     454#else 
     455    int fade_pos = fade_in_pos; 
     456#endif 
     457 
     458    if (fade_cnt - fade_pos < count) { 
     459        for (; fade_pos < fade_cnt; ++fade_pos, ++buf) { 
     460            *buf = (pj_int16_t)(*buf * fade_pos / fade_cnt); 
     461        } 
     462        /* Leave the remaining samples as is */ 
     463    } else { 
     464        pj_int16_t *end = buf + count; 
     465        for (; buf != end; ++fade_pos, ++buf) { 
     466            *buf = (pj_int16_t)(*buf * fade_pos / fade_cnt); 
     467        } 
     468    } 
     469} 
     470 
     471/* Apply fade-out to the buffer. */ 
     472static void wsola_fade_out(pjmedia_wsola *wsola,  
     473                           pj_int16_t buf[], int count) 
     474{ 
     475    pj_int16_t *end = buf + count; 
     476    int fade_cnt = wsola->max_expand_cnt; 
     477#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0 
     478    float fade_pos = (float)wsola->fade_out_pos; 
     479#else 
     480    int fade_pos = wsola->fade_out_pos; 
     481#endif 
     482 
     483    if (wsola->fade_out_pos == 0) { 
     484        pjmedia_zero_samples(buf, count); 
     485    } else if (fade_pos < count) { 
     486        for (; fade_pos; --fade_pos, ++buf) { 
     487            *buf = (pj_int16_t)(*buf * fade_pos / fade_cnt); 
     488        } 
     489        if (buf != end) 
     490            pjmedia_zero_samples(buf, end - buf); 
     491        wsola->fade_out_pos = 0; 
     492    } else { 
     493        for (; buf != end; --fade_pos, ++buf) { 
     494            *buf = (pj_int16_t)(*buf * fade_pos / fade_cnt); 
     495        } 
     496        wsola->fade_out_pos -= count; 
     497    } 
     498} 
     499 
    435500 
    436501PJ_DEF(pj_status_t) pjmedia_wsola_create( pj_pool_t *pool,  
     
    456521    wsola->channel_count = (pj_uint16_t) channel_count; 
    457522    wsola->options = (pj_uint16_t) options; 
     523    wsola->max_expand_cnt = clock_rate * MAX_EXPAND_MSEC / 1000; 
     524    wsola->fade_out_pos = wsola->max_expand_cnt; 
    458525 
    459526    /* Create circular buffer */ 
     
    524591} 
    525592 
     593PJ_DEF(pj_status_t) pjmedia_wsola_set_max_expand(pjmedia_wsola *wsola, 
     594                                                  unsigned msec) 
     595{ 
     596    PJ_ASSERT_RETURN(wsola, PJ_EINVAL); 
     597    wsola->max_expand_cnt = msec * wsola->clock_rate / 1000; 
     598    return PJ_SUCCESS; 
     599} 
    526600 
    527601PJ_DEF(pj_status_t) pjmedia_wsola_reset( pjmedia_wsola *wsola, 
     
    534608    pjmedia_circ_buf_set_len(wsola->buf, wsola->hist_size + wsola->min_extra); 
    535609    pjmedia_zero_samples(wsola->buf->start, wsola->buf->len);  
     610    wsola->fade_out_pos = wsola->max_expand_cnt; 
    536611 
    537612    return PJ_SUCCESS; 
     
    683758 
    684759    /* Update vars */ 
    685     wsola->expand_cnt = 0; 
    686760    wsola->ts.u64 += wsola->samples_per_frame; 
    687761 
     
    692766        pj_int16_t *ola_left; 
    693767 
     768        /* Trim excessive len */ 
     769        if ((int)buf_len > wsola->hist_size + (wsola->min_extra<<1)) { 
     770            buf_len = wsola->hist_size + (wsola->min_extra<<1); 
     771            pjmedia_circ_buf_set_len(wsola->buf, buf_len); 
     772        } 
     773 
    694774        pjmedia_circ_buf_get_read_regions(wsola->buf, &reg1, &reg1_len,  
    695775                                          &reg2, &reg2_len); 
     
    698778               (unsigned)(wsola->hist_size + (wsola->min_extra<<1))); 
    699779 
     780        /* Continue applying fade out to the extra samples */ 
     781        if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0) { 
     782            if (reg2_len == 0) { 
     783                wsola_fade_out(wsola, reg1 + reg1_len - (wsola->min_extra<<1), 
     784                               (wsola->min_extra<<1)); 
     785            } else if ((int)reg2_len >= (wsola->min_extra<<1)) { 
     786                wsola_fade_out(wsola, reg2 + reg2_len - (wsola->min_extra<<1), 
     787                               (wsola->min_extra<<1)); 
     788            } else { 
     789                unsigned tmp = (wsola->min_extra<<1) - reg2_len; 
     790                wsola_fade_out(wsola, reg1 + reg1_len - tmp, tmp); 
     791                wsola_fade_out(wsola, reg2, reg2_len); 
     792            } 
     793        } 
     794 
     795        /* Get the region in buffer to be merged with the frame */ 
    700796        if (reg2_len == 0) { 
    701797            ola_left = reg1 + reg1_len - wsola->min_extra; 
     
    711807        } 
    712808 
     809        /* Apply fade-in to the frame before merging */ 
     810        if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0) { 
     811            unsigned count = wsola->min_extra; 
     812            int fade_in_pos; 
     813 
     814            /* Scale fade_in position based on last fade-out */ 
     815            fade_in_pos = wsola->fade_out_pos * count / 
     816                          wsola->max_expand_cnt; 
     817 
     818            /* Fade-in it */ 
     819            fade_in(frm, wsola->samples_per_frame, 
     820                    fade_in_pos, count); 
     821        } 
     822 
     823        /* Merge it */ 
    713824        overlapp_add_simple(frm, wsola->min_extra, ola_left, frm); 
    714825 
     826        /* Trim len */ 
    715827        buf_len -= wsola->min_extra; 
    716828        pjmedia_circ_buf_set_len(wsola->buf, buf_len); 
    717     } 
     829 
     830    } else if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0 && 
     831               wsola->fade_out_pos != wsola->max_expand_cnt)  
     832    { 
     833        unsigned count = wsola->min_extra; 
     834        int fade_in_pos; 
     835 
     836        /* Fade out the remaining synthetic samples */ 
     837        if (buf_len > wsola->hist_size) { 
     838            pj_int16_t *reg1, *reg2; 
     839            unsigned reg1_len, reg2_len; 
     840 
     841            /* Number of samples to fade out */ 
     842            count = buf_len - wsola->hist_size; 
     843 
     844            pjmedia_circ_buf_get_read_regions(wsola->buf, &reg1, &reg1_len,  
     845                                              &reg2, &reg2_len); 
     846 
     847            CHECK_(pjmedia_circ_buf_get_len(wsola->buf) >=  
     848                   (unsigned)(wsola->hist_size + (wsola->min_extra<<1))); 
     849 
     850            /* Continue applying fade out to the extra samples */ 
     851            if (reg2_len == 0) { 
     852                wsola_fade_out(wsola, reg1 + reg1_len - count, count); 
     853            } else if ((int)reg2_len >= count) { 
     854                wsola_fade_out(wsola, reg2 + reg2_len - count, count); 
     855            } else { 
     856                unsigned tmp = count - reg2_len; 
     857                wsola_fade_out(wsola, reg1 + reg1_len - tmp, tmp); 
     858                wsola_fade_out(wsola, reg2, reg2_len); 
     859            } 
     860        } 
     861 
     862        /* Apply fade-in to the frame */ 
     863        count = wsola->min_extra; 
     864 
     865        /* Scale fade_in position based on last fade-out */ 
     866        fade_in_pos = wsola->fade_out_pos * count / 
     867                      wsola->max_expand_cnt; 
     868 
     869        /* Fade it in */ 
     870        fade_in(frm, wsola->samples_per_frame, 
     871                fade_in_pos, count); 
     872 
     873    } 
     874 
     875    wsola->fade_out_pos = wsola->max_expand_cnt; 
    718876 
    719877    status = pjmedia_circ_buf_write(wsola->buf, frm, wsola->samples_per_frame); 
     
    738896{ 
    739897    unsigned samples_len, samples_req; 
    740  
    741  
    742898    pj_status_t status = PJ_SUCCESS; 
    743899 
     
    758914        TRACE_((THIS_FILE, "Buf size after expanded = %d",  
    759915                pjmedia_circ_buf_get_len(wsola->buf))); 
    760         wsola->expand_cnt++; 
    761916    } 
    762917 
     
    769924 
    770925    pjmedia_circ_buf_adv_read_ptr(wsola->buf, wsola->samples_per_frame); 
     926 
     927    /* Apply fade-out to the frame */ 
     928    if ((wsola->options & PJMEDIA_WSOLA_NO_FADING)==0) { 
     929        wsola_fade_out(wsola, frm, wsola->samples_per_frame); 
     930    } 
    771931 
    772932    return PJ_SUCCESS; 
Note: See TracChangeset for help on using the changeset viewer.