Ignore:
Timestamp:
Aug 9, 2008 5:40:22 AM (16 years ago)
Author:
bennylp
Message:

Ticket #588: Improvements to echo cancellation framework

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/src/pjmedia/echo_common.c

    r2039 r2198  
    1818 */ 
    1919 
    20 #include <pjmedia/config.h> 
    2120#include <pjmedia/echo.h> 
     21#include <pjmedia/delaybuf.h> 
     22#include <pjmedia/errno.h> 
    2223#include <pj/assert.h> 
     24#include <pj/list.h> 
     25#include <pj/log.h> 
    2326#include <pj/pool.h> 
    2427#include "echo_internal.h" 
    2528 
     29#define THIS_FILE   "echo_common.c" 
     30 
    2631typedef struct ec_operations ec_operations; 
    2732 
     33struct frame 
     34{ 
     35    PJ_DECL_LIST_MEMBER(struct frame); 
     36    short   buf[1]; 
     37}; 
     38 
    2839struct pjmedia_echo_state 
    2940{ 
     41    pj_pool_t       *pool; 
     42    char            *obj_name; 
     43    unsigned         samples_per_frame; 
    3044    void            *state; 
    3145    ec_operations   *op; 
     46 
     47    pj_bool_t        lat_ready;     /* lat_buf has been filled in.          */ 
     48    unsigned         lat_target_cnt;/* Target number of frames in lat_buf   */ 
     49    unsigned         lat_buf_cnt;   /* Actual number of frames in lat_buf   */ 
     50    struct frame     lat_buf;       /* Frame queue for delayed playback     */ 
     51    struct frame     lat_free;      /* Free frame list.                     */ 
     52 
     53    pjmedia_delay_buf   *delay_buf; 
    3254}; 
    3355 
     
    3557struct ec_operations 
    3658{ 
     59    const char *name; 
     60 
    3761    pj_status_t (*ec_create)(pj_pool_t *pool, 
    38                             unsigned clock_rate, 
    39                             unsigned samples_per_frame, 
    40                             unsigned tail_ms, 
    41                             unsigned latency_ms, 
    42                             unsigned options, 
    43                             void **p_state ); 
     62                             unsigned clock_rate, 
     63                             unsigned channel_count, 
     64                             unsigned samples_per_frame, 
     65                             unsigned tail_ms, 
     66                             unsigned options, 
     67                             void **p_state ); 
    4468    pj_status_t (*ec_destroy)(void *state ); 
    45     pj_status_t (*ec_playback)(void *state, 
    46                               pj_int16_t *play_frm ); 
    47     pj_status_t (*ec_capture)(void *state, 
    48                               pj_int16_t *rec_frm, 
    49                               unsigned options ); 
     69    void        (*ec_reset)(void *state ); 
    5070    pj_status_t (*ec_cancel)(void *state, 
    5171                             pj_int16_t *rec_frm, 
     
    5878static struct ec_operations echo_supp_op =  
    5979{ 
     80    "Echo suppressor", 
    6081    &echo_supp_create, 
    6182    &echo_supp_destroy, 
    62     &echo_supp_playback, 
    63     &echo_supp_capture, 
     83    &echo_supp_reset, 
    6484    &echo_supp_cancel_echo 
    6585}; 
     
    7191 */ 
    7292#if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC!=0 
    73 static struct ec_operations aec_op =  
    74 { 
     93static struct ec_operations speex_aec_op =  
     94{ 
     95    "AEC", 
    7596    &speex_aec_create, 
    7697    &speex_aec_destroy, 
    77     &speex_aec_playback, 
    78     &speex_aec_capture, 
     98    &speex_aec_reset, 
    7999    &speex_aec_cancel_echo 
    80100}; 
    81  
    82 #else 
    83 #define aec_op echo_supp_op 
    84101#endif 
    85102 
    86103 
     104/* 
     105 * IPP AEC prototypes 
     106 */ 
     107#if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 
     108static struct ec_operations ipp_aec_op =  
     109{ 
     110    "IPP AEC", 
     111    &ipp_aec_create, 
     112    &ipp_aec_destroy, 
     113    &ipp_aec_reset, 
     114    &ipp_aec_cancel_echo 
     115}; 
     116#endif 
    87117 
    88118/* 
     
    97127                                         pjmedia_echo_state **p_echo ) 
    98128{ 
     129    return pjmedia_echo_create2(pool, clock_rate, 1, samples_per_frame, 
     130                                tail_ms, latency_ms, options, p_echo); 
     131} 
     132 
     133/* 
     134 * Create the echo canceller.  
     135 */ 
     136PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool, 
     137                                         unsigned clock_rate, 
     138                                         unsigned channel_count, 
     139                                         unsigned samples_per_frame, 
     140                                         unsigned tail_ms, 
     141                                         unsigned latency_ms, 
     142                                         unsigned options, 
     143                                         pjmedia_echo_state **p_echo ) 
     144{ 
     145    unsigned ptime; 
    99146    pjmedia_echo_state *ec; 
    100147    pj_status_t status; 
    101148 
    102     /* Force to use simple echo suppressor if AEC is not available */ 
    103 #if !defined(PJMEDIA_HAS_SPEEX_AEC) || PJMEDIA_HAS_SPEEX_AEC==0 
    104     options |= PJMEDIA_ECHO_SIMPLE; 
     149    /* Create new pool and instantiate and init the EC */ 
     150    pool = pj_pool_create(pool->factory, "ec%p", 256, 256, NULL); 
     151    ec = PJ_POOL_ZALLOC_T(pool, struct pjmedia_echo_state); 
     152    ec->pool = pool; 
     153    ec->obj_name = pool->obj_name; 
     154    pj_list_init(&ec->lat_buf); 
     155    pj_list_init(&ec->lat_free); 
     156 
     157    /* Select the backend algorithm */ 
     158    if (0) { 
     159        /* Dummy */ 
     160        ; 
     161#if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC!=0 
     162    } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_SPEEX || 
     163               (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT)  
     164    { 
     165        ec->op = &speex_aec_op; 
    105166#endif 
    106167 
    107     ec = PJ_POOL_ZALLOC_T(pool, struct pjmedia_echo_state); 
    108  
    109     if (options & PJMEDIA_ECHO_SIMPLE) { 
     168#if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 
     169    } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_IPP || 
     170               (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) 
     171    { 
     172        ec->op = &ipp_aec_op; 
     173 
     174#endif 
     175 
     176    } else { 
    110177        ec->op = &echo_supp_op; 
    111         status = (*echo_supp_op.ec_create)(pool, clock_rate, samples_per_frame, 
    112                                            tail_ms, latency_ms, options, 
    113                                            &ec->state); 
     178    } 
     179 
     180    PJ_LOG(5,(ec->obj_name, "Creating %s", ec->op->name)); 
     181 
     182    /* Instantiate EC object */ 
     183    status = (*ec->op->ec_create)(pool, clock_rate, channel_count,  
     184                                  samples_per_frame, tail_ms,  
     185                                  options, &ec->state); 
     186    if (status != PJ_SUCCESS) { 
     187        pj_pool_release(pool); 
     188        return status; 
     189    } 
     190 
     191    /* Create latency buffers */ 
     192    ptime = samples_per_frame * 1000 / clock_rate; 
     193    if (latency_ms == 0) { 
     194        /* Give at least one frame delay to simplify programming */ 
     195        latency_ms = ptime; 
     196    } 
     197    ec->lat_target_cnt = latency_ms / ptime; 
     198    if (ec->lat_target_cnt != 0) { 
     199        unsigned i; 
     200        for (i=0; i < ec->lat_target_cnt; ++i)  { 
     201            struct frame *frm; 
     202 
     203            frm = (struct frame*) pj_pool_alloc(pool, (samples_per_frame<<1) + 
     204                                                      sizeof(struct frame)); 
     205            pj_list_push_back(&ec->lat_free, frm); 
     206        } 
    114207    } else { 
    115         ec->op = &aec_op; 
    116         status = (*aec_op.ec_create)(pool, clock_rate,  
    117                                      samples_per_frame, 
    118                                      tail_ms, latency_ms, options, 
    119                                      &ec->state); 
    120     } 
    121  
    122     if (status != PJ_SUCCESS) 
     208        ec->lat_ready = PJ_TRUE; 
     209    } 
     210 
     211    /* Create delay buffer to compensate drifts */ 
     212    status = pjmedia_delay_buf_create(ec->pool, ec->obj_name, clock_rate,  
     213                                      samples_per_frame, channel_count, 
     214                                      (PJMEDIA_SOUND_BUFFER_COUNT+1) * ptime, 
     215                                      0, &ec->delay_buf); 
     216    if (status != PJ_SUCCESS) { 
     217        pj_pool_release(pool); 
    123218        return status; 
    124  
    125     pj_assert(ec->state != NULL); 
    126  
     219    } 
     220 
     221    PJ_LOG(4,(ec->obj_name,  
     222              "%s created, clock_rate=%d, channel=%d, " 
     223              "samples per frame=%d, tail length=%d ms, " 
     224              "latency=%d ms",  
     225              ec->op->name, clock_rate, channel_count, samples_per_frame, 
     226              tail_ms, latency_ms)); 
     227 
     228    /* Done */ 
    127229    *p_echo = ec; 
    128230 
     
    136238PJ_DEF(pj_status_t) pjmedia_echo_destroy(pjmedia_echo_state *echo ) 
    137239{ 
    138     return (*echo->op->ec_destroy)(echo->state); 
    139 } 
    140  
    141  
    142  
    143 /* 
    144  * Let the Echo Canceller knows that a frame has been played to the speaker. 
     240    (*echo->op->ec_destroy)(echo->state); 
     241    pj_pool_release(echo->pool); 
     242    return PJ_SUCCESS; 
     243} 
     244 
     245 
     246/* 
     247 * Reset the echo canceller. 
     248 */ 
     249PJ_DEF(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo ) 
     250{ 
     251    while (!pj_list_empty(&echo->lat_buf)) { 
     252        struct frame *frm; 
     253        frm = echo->lat_buf.next; 
     254        pj_list_erase(frm); 
     255        pj_list_push_back(&echo->lat_free, frm); 
     256    } 
     257    echo->lat_ready = PJ_FALSE; 
     258    pjmedia_delay_buf_reset(echo->delay_buf); 
     259    echo->op->ec_reset(echo->state); 
     260    return PJ_SUCCESS; 
     261} 
     262 
     263 
     264/* 
     265 * Let the Echo Canceller know that a frame has been played to the speaker. 
    145266 */ 
    146267PJ_DEF(pj_status_t) pjmedia_echo_playback( pjmedia_echo_state *echo, 
    147268                                           pj_int16_t *play_frm ) 
    148269{ 
    149     return (*echo->op->ec_playback)(echo->state, play_frm); 
     270    if (!echo->lat_ready) { 
     271        /* We've not built enough latency in the buffer, so put this frame 
     272         * in the latency buffer list. 
     273         */ 
     274        struct frame *frm; 
     275 
     276        if (pj_list_empty(&echo->lat_free)) { 
     277            echo->lat_ready = PJ_TRUE; 
     278            PJ_LOG(5,(echo->obj_name, "Latency bufferring complete")); 
     279            pjmedia_delay_buf_put(echo->delay_buf, play_frm); 
     280            return PJ_SUCCESS; 
     281        } 
     282             
     283        frm = echo->lat_free.prev; 
     284        pj_list_erase(frm); 
     285 
     286        pjmedia_copy_samples(frm->buf, play_frm, echo->samples_per_frame); 
     287        pj_list_push_back(&echo->lat_buf, frm); 
     288 
     289    } else { 
     290        /* Latency buffer is ready (full), so we put this frame in the 
     291         * delay buffer. 
     292         */ 
     293        pjmedia_delay_buf_put(echo->delay_buf, play_frm); 
     294    } 
     295 
     296    return PJ_SUCCESS; 
    150297} 
    151298 
     
    159306                                          unsigned options ) 
    160307{ 
    161     return (*echo->op->ec_capture)(echo->state, rec_frm, options); 
     308    struct frame *oldest_frm; 
     309    pj_status_t status, rc; 
     310 
     311    if (!echo->lat_ready) { 
     312        /* Prefetching to fill in the desired latency */ 
     313        PJ_LOG(5,(echo->obj_name, "Prefetching..")); 
     314        return PJ_SUCCESS; 
     315    } 
     316 
     317    /* Retrieve oldest frame from the latency buffer */ 
     318    oldest_frm = echo->lat_buf.next; 
     319    pj_list_erase(oldest_frm); 
     320 
     321    /* Cancel echo using this reference frame */ 
     322    status = pjmedia_echo_cancel(echo, rec_frm, oldest_frm->buf,  
     323                                 options, NULL); 
     324 
     325    /* Move one frame from delay buffer to the latency buffer. */ 
     326    rc = pjmedia_delay_buf_get(echo->delay_buf, oldest_frm->buf); 
     327    if (rc != PJ_SUCCESS) { 
     328        /* Ooops.. no frame! */ 
     329        PJ_LOG(5,(echo->obj_name,  
     330                  "No frame from delay buffer. This will upset EC later")); 
     331        pjmedia_zero_samples(oldest_frm->buf, echo->samples_per_frame); 
     332    } 
     333    pj_list_push_back(&echo->lat_buf, oldest_frm); 
     334     
     335    return status; 
    162336} 
    163337 
Note: See TracChangeset for help on using the changeset viewer.