Ignore:
Timestamp:
Nov 23, 2006 7:32:13 AM (17 years ago)
Author:
bennylp
Message:

Worked on the AEC. Apply constant delay bufferring for the AEC,
and also consider sound device latency when applying EC. It
sounds like working although it still doesn't perfectly cancel
the echo.

EC is now by default enabled in PJSUA.

File:
1 edited

Legend:

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

    r653 r822  
    3030 
    3131 
    32 #define THIS_FILE   "echo_speex.c" 
    33 #define BUF_COUNT   8 
     32#define THIS_FILE       "echo_speex.c" 
     33#define BUF_COUNT       20 
     34#define MIN_PREFETCH    4 
     35#define MAX_PREFETCH    12 
     36 
     37 
     38 
     39#if 0 
     40# define TRACE_(expr)  PJ_LOG(5,expr) 
     41#else 
     42# define TRACE_(expr) 
     43#endif 
     44 
     45 
     46typedef struct pjmedia_frame_queue pjmedia_frame_queue; 
     47 
     48struct fq_frame 
     49{ 
     50    PJ_DECL_LIST_MEMBER(struct fq_frame); 
     51    void        *buf; 
     52    unsigned     size; 
     53    pj_uint32_t  seq; 
     54}; 
     55 
     56struct pjmedia_frame_queue 
     57{ 
     58    char             obj_name[PJ_MAX_OBJ_NAME]; 
     59    unsigned         frame_size; 
     60    int              samples_per_frame; 
     61    unsigned         count; 
     62    unsigned         max_count; 
     63    struct fq_frame  frame_list; 
     64    struct fq_frame  free_list; 
     65 
     66    int              seq_delay; 
     67    int              prefetch_count; 
     68}; 
     69 
     70PJ_DEF(pj_status_t) pjmedia_frame_queue_create( pj_pool_t *pool, 
     71                                                const char *name, 
     72                                                unsigned frame_size, 
     73                                                unsigned samples_per_frame, 
     74                                                unsigned max_count, 
     75                                                pjmedia_frame_queue **p_fq) 
     76{ 
     77    pjmedia_frame_queue *fq; 
     78    unsigned i; 
     79 
     80    fq = pj_pool_zalloc(pool, sizeof(pjmedia_frame_queue)); 
     81 
     82    pj_ansi_snprintf(fq->obj_name, sizeof(fq->obj_name), name, fq); 
     83    fq->obj_name[sizeof(fq->obj_name)-1] = '\0'; 
     84 
     85    fq->max_count = max_count; 
     86    fq->frame_size = frame_size; 
     87    fq->samples_per_frame = samples_per_frame; 
     88    fq->count = 0; 
     89 
     90    pj_list_init(&fq->frame_list); 
     91    pj_list_init(&fq->free_list); 
     92 
     93    for (i=0; i<max_count; ++i) { 
     94        struct fq_frame *f; 
     95 
     96        f = pj_pool_zalloc(pool, sizeof(struct fq_frame)); 
     97        f->buf = pj_pool_alloc(pool, frame_size); 
     98 
     99        pj_list_push_back(&fq->free_list, f); 
     100         
     101    } 
     102 
     103    *p_fq = fq; 
     104    return PJ_SUCCESS; 
     105} 
     106 
     107PJ_DEF(pj_status_t) pjmedia_frame_queue_init( pjmedia_frame_queue *fq, 
     108                                              int seq_delay, 
     109                                              int prefetch_count) 
     110{ 
     111    if (prefetch_count > MAX_PREFETCH) 
     112        prefetch_count = MAX_PREFETCH; 
     113 
     114    fq->seq_delay = seq_delay; 
     115    fq->prefetch_count = prefetch_count; 
     116    fq->count = 0; 
     117    pj_list_merge_first(&fq->free_list, &fq->frame_list); 
     118 
     119    PJ_LOG(5,(fq->obj_name, "AEC reset, delay=%d, prefetch=%d",  
     120              fq->seq_delay, fq->prefetch_count)); 
     121 
     122    return PJ_SUCCESS; 
     123} 
     124 
     125PJ_DEF(pj_bool_t) pjmedia_frame_queue_empty( pjmedia_frame_queue *fq ) 
     126{ 
     127    return pj_list_empty(&fq->frame_list); 
     128} 
     129 
     130PJ_DEF(int) pjmedia_frame_queue_get_prefetch( pjmedia_frame_queue *fq ) 
     131{ 
     132    return fq->prefetch_count; 
     133} 
     134 
     135PJ_DEF(pj_status_t) pjmedia_frame_queue_put( pjmedia_frame_queue *fq, 
     136                                             const void *framebuf, 
     137                                             unsigned size, 
     138                                             pj_uint32_t timestamp ) 
     139{ 
     140    struct fq_frame *f; 
     141 
     142    TRACE_((fq->obj_name, "PUT seq=%d, count=%d",  
     143            timestamp / fq->samples_per_frame, fq->count)); 
     144 
     145    if (pj_list_empty(&fq->free_list)) { 
     146        PJ_LOG(5,(fq->obj_name,  
     147                  " AEC info: queue is full, frame discarded " 
     148                  "[count=%d, seq=%d]", 
     149                  fq->max_count, timestamp / fq->samples_per_frame)); 
     150        return PJ_ETOOMANY; 
     151    } 
     152 
     153    PJ_ASSERT_RETURN(size <= fq->frame_size, PJ_ETOOBIG); 
     154 
     155    f = fq->free_list.next; 
     156    pj_list_erase(f); 
     157 
     158    pj_memcpy(f->buf, framebuf, size); 
     159    f->size = size; 
     160    f->seq = timestamp / fq->samples_per_frame; 
     161 
     162    pj_list_push_back(&fq->frame_list, f); 
     163    ++fq->count; 
     164 
     165    return PJ_SUCCESS; 
     166} 
     167 
     168PJ_DEF(pj_status_t) pjmedia_frame_queue_get( pjmedia_frame_queue *fq, 
     169                                             pj_uint32_t get_timestamp, 
     170                                             void **framebuf, 
     171                                             unsigned *size ) 
     172{ 
     173    pj_uint32_t frame_seq; 
     174    struct fq_frame *f; 
     175 
     176    frame_seq = get_timestamp/fq->samples_per_frame + fq->seq_delay - 
     177                fq->prefetch_count; 
     178 
     179    TRACE_((fq->obj_name, "GET seq=%d for seq=%d delay=%d, prefetch=%d",  
     180            get_timestamp/fq->samples_per_frame, frame_seq, fq->seq_delay,  
     181            fq->prefetch_count)); 
     182 
     183    *size = 0; 
     184 
     185    /* Remove old frames */ 
     186    for (;!pj_list_empty(&fq->frame_list);) { 
     187        f = fq->frame_list.next; 
     188        if (f->seq >= frame_seq) 
     189            break; 
     190 
     191        PJ_LOG(5,(fq->obj_name,  
     192                  " AEC Info: old frame removed (seq=%d, want=%d, count=%d)", 
     193                  f->seq, frame_seq, fq->count)); 
     194        pj_list_erase(f); 
     195        --fq->count; 
     196        pj_list_push_back(&fq->free_list, f); 
     197    } 
     198 
     199    if (pj_list_empty(&fq->frame_list)) { 
     200        PJ_LOG(5,(fq->obj_name,  
     201                  " AEC Info: empty queue for seq=%d!", 
     202                  frame_seq)); 
     203        return PJ_ENOTFOUND; 
     204    } 
     205 
     206    f = fq->frame_list.next; 
     207 
     208    if (f->seq > frame_seq) { 
     209        PJ_LOG(5,(fq->obj_name,  
     210                  " AEC Info: prefetching (first seq=%d)", 
     211                  f->seq)); 
     212        return -1; 
     213    } 
     214 
     215    pj_list_erase(f); 
     216    --fq->count; 
     217 
     218    *framebuf = (void*)f->buf; 
     219    *size = f->size; 
     220 
     221    TRACE_((fq->obj_name, " returning frame with seq=%d, count=%d",  
     222            f->seq, fq->count)); 
     223 
     224    pj_list_push_front(&fq->free_list, f); 
     225    return PJ_SUCCESS; 
     226} 
    34227 
    35228/* 
     
    40233                                      unsigned samples_per_frame, 
    41234                                      unsigned tail_ms, 
     235                                      unsigned latency_ms, 
    42236                                      unsigned options, 
    43237                                      void **p_state ); 
     
    55249 
    56250 
    57 struct frame 
    58 { 
    59     pj_int16_t     *buf; 
     251enum 
     252{ 
     253    TS_FLAG_PLAY = 1, 
     254    TS_FLAG_REC  = 2, 
     255    TS_FLAG_OK   = 3, 
    60256}; 
    61257 
     
    65261    SpeexPreprocessState *preprocess; 
    66262 
    67     unsigned         samples_per_frame; 
    68     unsigned         options; 
    69     pj_int16_t      *tmp_frame; 
    70     spx_int32_t     *residue; 
    71  
    72     pj_lock_t       *lock;              /* To protect buffers, if required  */ 
    73  
    74     unsigned         rpos;              /* Index to get oldest frame.       */ 
    75     unsigned         wpos;              /* Index to put newest frame.       */ 
    76     struct frame     frames[BUF_COUNT]; /* Playback frame buffers.          */ 
     263    unsigned              samples_per_frame; 
     264    unsigned              prefetch; 
     265    unsigned              options; 
     266    pj_int16_t           *tmp_frame; 
     267    spx_int32_t          *residue; 
     268 
     269    pj_uint32_t           play_ts, 
     270                          rec_ts, 
     271                          ts_flag; 
     272 
     273    pjmedia_frame_queue  *frame_queue; 
     274    pj_lock_t            *lock;         /* To protect buffers, if required  */ 
    77275} speex_ec; 
    78276 
     
    86284                                     unsigned samples_per_frame, 
    87285                                     unsigned tail_ms, 
     286                                     unsigned latency_ms, 
    88287                                     unsigned options, 
    89288                                     void **p_echo ) 
     
    91290    speex_ec *echo; 
    92291    int sampling_rate; 
    93     unsigned i; 
    94     int disabled; 
     292    int disabled, enabled; 
    95293    pj_status_t status; 
    96294 
     
    111309 
    112310    echo->samples_per_frame = samples_per_frame; 
     311    echo->prefetch = (latency_ms * clock_rate / 1000) / samples_per_frame; 
     312    if (echo->prefetch < MIN_PREFETCH) 
     313        echo->prefetch = MIN_PREFETCH; 
     314    if (echo->prefetch > MAX_PREFETCH) 
     315        echo->prefetch = MAX_PREFETCH; 
    113316    echo->options = options; 
    114317 
     
    130333    /* Disable all preprocessing, we only want echo cancellation */ 
    131334    disabled = 0; 
     335    enabled = 1; 
    132336    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DENOISE,  
    133                          &disabled); 
     337                         &enabled); 
    134338    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_AGC,  
    135339                         &disabled); 
     
    150354    /* Create temporary frame to receive residue */ 
    151355    echo->residue = pj_pool_zalloc(pool, sizeof(spx_int32_t) *  
    152                                             samples_per_frame); 
     356                                            (samples_per_frame+1)); 
    153357    PJ_ASSERT_RETURN(echo->residue != NULL, PJ_ENOMEM); 
    154358 
    155     /* Create internal playback buffers */ 
    156     for (i=0; i<BUF_COUNT; ++i) { 
    157         echo->frames[i].buf = pj_pool_zalloc(pool, samples_per_frame * 2); 
    158         PJ_ASSERT_RETURN(echo->frames[i].buf != NULL, PJ_ENOMEM); 
    159     } 
    160  
     359    /* Create frame queue */ 
     360    status = pjmedia_frame_queue_create(pool, "aec%p", samples_per_frame*2, 
     361                                        samples_per_frame, BUF_COUNT,  
     362                                        &echo->frame_queue); 
     363    if (status != PJ_SUCCESS) { 
     364        speex_preprocess_state_destroy(echo->preprocess); 
     365        speex_echo_state_destroy(echo->state); 
     366        pj_lock_destroy(echo->lock); 
     367        return status; 
     368    } 
    161369 
    162370    /* Done */ 
     
    164372 
    165373    PJ_LOG(4,(THIS_FILE, "Speex Echo canceller/AEC created, clock_rate=%d, " 
    166                          "samples per frame=%d, tail length=%d ms",  
    167                          clock_rate, 
    168                          samples_per_frame, 
    169                          tail_ms)); 
     374                         "samples per frame=%d, tail length=%d ms, " 
     375                         "latency=%d ms",  
     376                         clock_rate, samples_per_frame, tail_ms, latency_ms)); 
    170377    return PJ_SUCCESS; 
    171378 
     
    216423 
    217424    /* The AEC must be configured to support internal playback buffer */ 
    218     PJ_ASSERT_RETURN(echo->frames[0].buf != NULL, PJ_EINVALIDOP); 
     425    PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP); 
    219426 
    220427    pj_lock_acquire(echo->lock); 
    221428 
    222     /* Check for overflows */ 
    223     if (echo->wpos == echo->rpos) { 
    224         PJ_LOG(5,(THIS_FILE, "Speex AEC overflow (playback runs faster, " 
    225                              "wpos=%d, rpos=%d)", 
    226                              echo->wpos, echo->rpos)); 
    227         echo->rpos = (echo->wpos - BUF_COUNT/2) % BUF_COUNT; 
    228         speex_echo_state_reset(echo->state); 
    229     } 
    230  
    231     /* Save fhe frame */ 
    232     pjmedia_copy_samples(echo->frames[echo->wpos].buf, 
    233                          play_frm, echo->samples_per_frame); 
    234     echo->wpos = (echo->wpos+1) % BUF_COUNT; 
     429    /* Inc timestamp */ 
     430    echo->play_ts += echo->samples_per_frame; 
     431 
     432    /* Initialize frame delay. */ 
     433    if ((echo->ts_flag & TS_FLAG_PLAY) == 0) { 
     434        echo->ts_flag |= TS_FLAG_PLAY; 
     435 
     436        if (echo->ts_flag == TS_FLAG_OK) { 
     437            int seq_delay; 
     438 
     439            seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /  
     440                            (int)echo->samples_per_frame; 
     441            pjmedia_frame_queue_init(echo->frame_queue, seq_delay,  
     442                                     echo->prefetch); 
     443        } 
     444    } 
     445 
     446    pjmedia_frame_queue_put(echo->frame_queue, play_frm,  
     447                            echo->samples_per_frame*2, echo->play_ts); 
    235448 
    236449    pj_lock_release(echo->lock); 
     
    248461{ 
    249462    speex_ec *echo = state; 
    250     pj_status_t status; 
     463    pj_status_t status = PJ_SUCCESS; 
    251464 
    252465    /* Sanity checks */ 
     
    254467 
    255468    /* The AEC must be configured to support internal playback buffer */ 
    256     PJ_ASSERT_RETURN(echo->frames[0].buf != NULL, PJ_EINVALIDOP); 
     469    PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP); 
    257470 
    258471    /* Lock mutex */ 
    259472    pj_lock_acquire(echo->lock); 
    260473 
    261  
    262     /* Check for underflow */ 
    263     if (echo->rpos == echo->wpos) { 
    264         /* Return frame as it is */ 
    265         pj_lock_release(echo->lock); 
    266  
    267         PJ_LOG(5,(THIS_FILE, "Speex AEC underflow (capture runs faster than " 
    268                              "playback, wpos=%d, rpos=%d)",  
    269                              echo->wpos, echo->rpos)); 
    270         echo->rpos = (echo->wpos - BUF_COUNT/2) % BUF_COUNT; 
    271         speex_echo_state_reset(echo->state); 
    272  
    273         return PJ_SUCCESS; 
    274     } 
    275      
     474    /* Inc timestamp */ 
     475    echo->rec_ts += echo->samples_per_frame; 
     476 
     477    /* Init frame delay. */ 
     478    if ((echo->ts_flag & TS_FLAG_REC) == 0) { 
     479        echo->ts_flag |= TS_FLAG_REC; 
     480 
     481        if (echo->ts_flag == TS_FLAG_OK) { 
     482            int seq_delay; 
     483 
     484            seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /  
     485                            (int)echo->samples_per_frame; 
     486            pjmedia_frame_queue_init(echo->frame_queue, seq_delay,  
     487                                     echo->prefetch); 
     488        } 
     489    } 
    276490 
    277491    /* Cancel echo */ 
    278     status = speex_aec_cancel_echo(echo, rec_frm,  
    279                                    echo->frames[echo->rpos].buf, options, 
    280                                    NULL); 
    281  
    282     echo->rpos = (echo->rpos + 1) % BUF_COUNT; 
     492    if (echo->ts_flag == TS_FLAG_OK) { 
     493        void *play_buf; 
     494        unsigned size = 0; 
     495         
     496        if (pjmedia_frame_queue_empty(echo->frame_queue)) { 
     497            int seq_delay, prefetch; 
     498 
     499            seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /  
     500                            (int)echo->samples_per_frame; 
     501            prefetch = pjmedia_frame_queue_get_prefetch(echo->frame_queue); 
     502            //++prefetch; 
     503            pjmedia_frame_queue_init(echo->frame_queue, seq_delay, prefetch); 
     504            status = -1; 
     505 
     506        } else { 
     507            status = pjmedia_frame_queue_get(echo->frame_queue, echo->rec_ts, 
     508                                             &play_buf, &size); 
     509            if (size != 0) { 
     510                speex_aec_cancel_echo(echo, rec_frm, (pj_int16_t*)play_buf, 
     511                                      options, NULL); 
     512            }    
     513        } 
     514 
     515        if (status != PJ_SUCCESS) 
     516            speex_echo_state_reset(echo->state); 
     517    } 
    283518 
    284519    pj_lock_release(echo->lock); 
    285     return status; 
     520    return PJ_SUCCESS; 
    286521} 
    287522 
     
    320555} 
    321556 
     557  
Note: See TracChangeset for help on using the changeset viewer.