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_speex.c

    r2039 r2198  
    2020#include <pjmedia/echo.h> 
    2121#include <pjmedia/errno.h> 
    22 #include <pjmedia/silencedet.h> 
    2322#include <pj/assert.h> 
    24 #include <pj/lock.h> 
    25 #include <pj/log.h> 
    26 #include <pj/os.h> 
    2723#include <pj/pool.h> 
    2824#include <speex/speex_echo.h> 
     
    3026 
    3127#include "echo_internal.h" 
    32  
    33 #define THIS_FILE       "echo_speex.c" 
    34 #define BUF_COUNT       PJMEDIA_SOUND_BUFFER_COUNT 
    35 #define MIN_PREFETCH    2 
    36 #define MAX_PREFETCH    (BUF_COUNT*2/3) 
    37  
    38  
    39  
    40 #if 0 
    41 # define TRACE_(expr)  PJ_LOG(5,expr) 
    42 #else 
    43 # define TRACE_(expr) 
    44 #endif 
    45  
    46  
    47 typedef struct pjmedia_frame_queue pjmedia_frame_queue; 
    48  
    49 struct fq_frame 
    50 { 
    51     PJ_DECL_LIST_MEMBER(struct fq_frame); 
    52     void        *buf; 
    53     unsigned     size; 
    54     pj_uint32_t  seq; 
    55 }; 
    56  
    57 struct pjmedia_frame_queue 
    58 { 
    59     char             obj_name[PJ_MAX_OBJ_NAME]; 
    60     unsigned         frame_size; 
    61     int              samples_per_frame; 
    62     unsigned         count; 
    63     unsigned         max_count; 
    64     struct fq_frame  frame_list; 
    65     struct fq_frame  free_list; 
    66  
    67     int              seq_delay; 
    68     int              prefetch_count; 
    69 }; 
    70  
    71 PJ_DEF(pj_status_t) pjmedia_frame_queue_create( pj_pool_t *pool, 
    72                                                 const char *name, 
    73                                                 unsigned frame_size, 
    74                                                 unsigned samples_per_frame, 
    75                                                 unsigned max_count, 
    76                                                 pjmedia_frame_queue **p_fq) 
    77 { 
    78     pjmedia_frame_queue *fq; 
    79     unsigned i; 
    80  
    81     fq = PJ_POOL_ZALLOC_T(pool, pjmedia_frame_queue); 
    82  
    83     pj_ansi_snprintf(fq->obj_name, sizeof(fq->obj_name), name, fq); 
    84     fq->obj_name[sizeof(fq->obj_name)-1] = '\0'; 
    85  
    86     fq->max_count = max_count; 
    87     fq->frame_size = frame_size; 
    88     fq->samples_per_frame = samples_per_frame; 
    89     fq->count = 0; 
    90  
    91     pj_list_init(&fq->frame_list); 
    92     pj_list_init(&fq->free_list); 
    93  
    94     for (i=0; i<max_count; ++i) { 
    95         struct fq_frame *f; 
    96  
    97         f = PJ_POOL_ZALLOC_T(pool, struct fq_frame); 
    98         f->buf = pj_pool_alloc(pool, frame_size); 
    99  
    100         pj_list_push_back(&fq->free_list, f); 
    101          
    102     } 
    103  
    104     *p_fq = fq; 
    105     return PJ_SUCCESS; 
    106 } 
    107  
    108 PJ_DEF(pj_status_t) pjmedia_frame_queue_init( pjmedia_frame_queue *fq, 
    109                                               int seq_delay, 
    110                                               int prefetch_count) 
    111 { 
    112     if (prefetch_count > MAX_PREFETCH) 
    113         prefetch_count = MAX_PREFETCH; 
    114  
    115     fq->seq_delay = seq_delay; 
    116     fq->prefetch_count = prefetch_count; 
    117     fq->count = 0; 
    118     pj_list_merge_first(&fq->free_list, &fq->frame_list); 
    119  
    120     PJ_LOG(5,(fq->obj_name, "AEC reset, delay=%d, prefetch=%d",  
    121               fq->seq_delay, fq->prefetch_count)); 
    122  
    123     return PJ_SUCCESS; 
    124 } 
    125  
    126 PJ_DEF(pj_bool_t) pjmedia_frame_queue_empty( pjmedia_frame_queue *fq ) 
    127 { 
    128     return pj_list_empty(&fq->frame_list); 
    129 } 
    130  
    131 PJ_DEF(int) pjmedia_frame_queue_get_prefetch( pjmedia_frame_queue *fq ) 
    132 { 
    133     return fq->prefetch_count; 
    134 } 
    135  
    136 PJ_DEF(pj_status_t) pjmedia_frame_queue_put( pjmedia_frame_queue *fq, 
    137                                              const void *framebuf, 
    138                                              unsigned size, 
    139                                              pj_uint32_t timestamp ) 
    140 { 
    141     struct fq_frame *f; 
    142  
    143     TRACE_((fq->obj_name, "PUT seq=%d, count=%d",  
    144             timestamp / fq->samples_per_frame, fq->count)); 
    145  
    146     if (pj_list_empty(&fq->free_list)) { 
    147         PJ_LOG(5,(fq->obj_name,  
    148                   " AEC info: queue is full, frame discarded " 
    149                   "[count=%d, seq=%d]", 
    150                   fq->max_count, timestamp / fq->samples_per_frame)); 
    151         //pjmedia_frame_queue_init(fq, fq->seq_delay, fq->prefetch_count); 
    152         return PJ_ETOOMANY; 
    153     } 
    154  
    155     PJ_ASSERT_RETURN(size <= fq->frame_size, PJ_ETOOBIG); 
    156  
    157     f = fq->free_list.next; 
    158     pj_list_erase(f); 
    159  
    160     pj_memcpy(f->buf, framebuf, size); 
    161     f->size = size; 
    162     f->seq = timestamp / fq->samples_per_frame; 
    163  
    164     pj_list_push_back(&fq->frame_list, f); 
    165     ++fq->count; 
    166  
    167     return PJ_SUCCESS; 
    168 } 
    169  
    170 PJ_DEF(pj_status_t) pjmedia_frame_queue_get( pjmedia_frame_queue *fq, 
    171                                              pj_uint32_t get_timestamp, 
    172                                              void **framebuf, 
    173                                              unsigned *size ) 
    174 { 
    175     pj_uint32_t frame_seq; 
    176     struct fq_frame *f; 
    177  
    178     frame_seq = get_timestamp/fq->samples_per_frame + fq->seq_delay - 
    179                 fq->prefetch_count; 
    180  
    181     TRACE_((fq->obj_name, "GET seq=%d for seq=%d delay=%d, prefetch=%d",  
    182             get_timestamp/fq->samples_per_frame, frame_seq, fq->seq_delay,  
    183             fq->prefetch_count)); 
    184  
    185     *size = 0; 
    186  
    187     /* Remove old frames */ 
    188     for (;!pj_list_empty(&fq->frame_list);) { 
    189         f = fq->frame_list.next; 
    190         if (f->seq >= frame_seq) 
    191             break; 
    192  
    193         PJ_LOG(5,(fq->obj_name,  
    194                   " AEC Info: old frame removed (seq=%d, want=%d, count=%d)", 
    195                   f->seq, frame_seq, fq->count)); 
    196         pj_list_erase(f); 
    197         --fq->count; 
    198         pj_list_push_back(&fq->free_list, f); 
    199     } 
    200  
    201     if (pj_list_empty(&fq->frame_list)) { 
    202         PJ_LOG(5,(fq->obj_name,  
    203                   " AEC Info: empty queue for seq=%d!", 
    204                   frame_seq)); 
    205         return PJ_ENOTFOUND; 
    206     } 
    207  
    208     f = fq->frame_list.next; 
    209  
    210     if (f->seq > frame_seq) { 
    211         PJ_LOG(5,(fq->obj_name,  
    212                   " AEC Info: prefetching (first seq=%d)", 
    213                   f->seq)); 
    214         return -1; 
    215     } 
    216  
    217     pj_list_erase(f); 
    218     --fq->count; 
    219  
    220     *framebuf = (void*)f->buf; 
    221     *size = f->size; 
    222  
    223     TRACE_((fq->obj_name, " returning frame with seq=%d, count=%d",  
    224             f->seq, fq->count)); 
    225  
    226     pj_list_push_front(&fq->free_list, f); 
    227     return PJ_SUCCESS; 
    228 } 
    229  
    230 enum 
    231 { 
    232     TS_FLAG_PLAY = 1, 
    233     TS_FLAG_REC  = 2, 
    234     TS_FLAG_OK   = 3, 
    235 }; 
    23628 
    23729typedef struct speex_ec 
     
    24436    unsigned              options; 
    24537    pj_int16_t           *tmp_frame; 
    246     spx_int32_t          *residue; 
    247  
    248     pj_uint32_t           play_ts, 
    249                           rec_ts, 
    250                           ts_flag; 
    251  
    252     pjmedia_frame_queue  *frame_queue; 
    253     pj_lock_t            *lock;         /* To protect buffers, if required  */ 
    25438} speex_ec; 
    25539 
     
    26145PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool, 
    26246                                     unsigned clock_rate, 
     47                                     unsigned channel_count, 
    26348                                     unsigned samples_per_frame, 
    26449                                     unsigned tail_ms, 
    265                                      unsigned latency_ms, 
    26650                                     unsigned options, 
    26751                                     void **p_echo ) 
     
    26953    speex_ec *echo; 
    27054    int sampling_rate; 
    271     pj_status_t status; 
    27255 
    27356    *p_echo = NULL; 
     
    27659    PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM); 
    27760 
    278     if (options & PJMEDIA_ECHO_NO_LOCK) { 
    279         status = pj_lock_create_null_mutex(pool, "aec%p", &echo->lock); 
    280         if (status != PJ_SUCCESS) 
    281             return status; 
    282     } else { 
    283         status = pj_lock_create_simple_mutex(pool, "aec%p", &echo->lock); 
    284         if (status != PJ_SUCCESS) 
    285             return status; 
    286     } 
    287  
    28861    echo->samples_per_frame = samples_per_frame; 
    289     echo->prefetch = (latency_ms * clock_rate / 1000) / samples_per_frame; 
    290     if (echo->prefetch < MIN_PREFETCH) 
    291         echo->prefetch = MIN_PREFETCH; 
    292     if (echo->prefetch > MAX_PREFETCH) 
    293         echo->prefetch = MAX_PREFETCH; 
    29462    echo->options = options; 
    29563 
    296     echo->state = speex_echo_state_init(samples_per_frame, 
    297                                         clock_rate * tail_ms / 1000); 
     64#if 0 
     65    echo->state = speex_echo_state_init_mc(echo->samples_per_frame, 
     66                                           clock_rate * tail_ms / 1000, 
     67                                           channel_count, channel_count); 
     68#else 
     69    PJ_ASSERT_RETURN(channel_count==1, PJ_EINVAL); 
     70    echo->state = speex_echo_state_init(echo->samples_per_frame, 
     71                                        clock_rate * tail_ms / 1000); 
     72#endif 
    29873    if (echo->state == NULL) { 
    299         pj_lock_destroy(echo->lock); 
    30074        return PJ_ENOMEM; 
    30175    } 
     
    30680                   &sampling_rate); 
    30781 
    308     echo->preprocess = speex_preprocess_state_init(samples_per_frame,  
     82    echo->preprocess = speex_preprocess_state_init(echo->samples_per_frame, 
    30983                                                   clock_rate); 
    31084    if (echo->preprocess == NULL) { 
    31185        speex_echo_state_destroy(echo->state); 
    312         pj_lock_destroy(echo->lock); 
    31386        return PJ_ENOMEM; 
    31487    } 
     
    32598                         &disabled); 
    32699    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DEREVERB,  
    327                          &disabled); 
     100                         &enabled); 
    328101#endif 
    329102 
     
    334107 
    335108    /* Create temporary frame for echo cancellation */ 
    336     echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2 * samples_per_frame); 
     109    echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2*samples_per_frame); 
    337110    PJ_ASSERT_RETURN(echo->tmp_frame != NULL, PJ_ENOMEM); 
    338  
    339     /* Create temporary frame to receive residue */ 
    340     echo->residue = (spx_int32_t*) 
    341                     pj_pool_zalloc(pool, sizeof(spx_int32_t) *  
    342                                             (samples_per_frame+1)); 
    343     PJ_ASSERT_RETURN(echo->residue != NULL, PJ_ENOMEM); 
    344  
    345     /* Create frame queue */ 
    346     status = pjmedia_frame_queue_create(pool, "aec%p", samples_per_frame*2, 
    347                                         samples_per_frame, BUF_COUNT,  
    348                                         &echo->frame_queue); 
    349     if (status != PJ_SUCCESS) { 
    350         speex_preprocess_state_destroy(echo->preprocess); 
    351         speex_echo_state_destroy(echo->state); 
    352         pj_lock_destroy(echo->lock); 
    353         return status; 
    354     } 
    355111 
    356112    /* Done */ 
    357113    *p_echo = echo; 
    358  
    359     PJ_LOG(4,(THIS_FILE, "Speex Echo canceller/AEC created, clock_rate=%d, " 
    360                          "samples per frame=%d, tail length=%d ms, " 
    361                          "latency=%d ms",  
    362                          clock_rate, samples_per_frame, tail_ms, latency_ms)); 
    363114    return PJ_SUCCESS; 
    364115 
     
    375126    PJ_ASSERT_RETURN(echo && echo->state, PJ_EINVAL); 
    376127 
    377     if (echo->lock) 
    378         pj_lock_acquire(echo->lock); 
    379  
    380128    if (echo->state) { 
    381129        speex_echo_state_destroy(echo->state); 
     
    388136    } 
    389137 
    390     if (echo->lock) { 
    391         pj_lock_destroy(echo->lock); 
    392         echo->lock = NULL; 
    393     } 
    394  
    395138    return PJ_SUCCESS; 
    396139} 
     
    398141 
    399142/* 
    400  * Let the AEC knows that a frame has been played to the speaker. 
     143 * Reset AEC 
    401144 */ 
    402 PJ_DEF(pj_status_t) speex_aec_playback(void *state, 
    403                                        pj_int16_t *play_frm ) 
     145PJ_DEF(void) speex_aec_reset(void *state ) 
    404146{ 
    405147    speex_ec *echo = (speex_ec*) state; 
    406  
    407     /* Sanity checks */ 
    408     PJ_ASSERT_RETURN(echo && play_frm, PJ_EINVAL); 
    409  
    410     /* The AEC must be configured to support internal playback buffer */ 
    411     PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP); 
    412  
    413     pj_lock_acquire(echo->lock); 
    414  
    415     /* Inc timestamp */ 
    416     echo->play_ts += echo->samples_per_frame; 
    417  
    418     /* Initialize frame delay. */ 
    419     if ((echo->ts_flag & TS_FLAG_PLAY) == 0) { 
    420         echo->ts_flag |= TS_FLAG_PLAY; 
    421  
    422         if (echo->ts_flag == TS_FLAG_OK) { 
    423             int seq_delay; 
    424  
    425             seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /  
    426                             (int)echo->samples_per_frame; 
    427             pjmedia_frame_queue_init(echo->frame_queue, seq_delay,  
    428                                      echo->prefetch); 
    429         } 
    430     } 
    431  
    432     if (pjmedia_frame_queue_put(echo->frame_queue, play_frm,  
    433                                 echo->samples_per_frame*2,  
    434                                 echo->play_ts) != PJ_SUCCESS) 
    435     { 
    436         int seq_delay; 
    437  
    438         /* On full reset frame queue */ 
    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         /* And re-put */ 
    445         pjmedia_frame_queue_put(echo->frame_queue, play_frm,  
    446                                 echo->samples_per_frame*2,  
    447                                 echo->play_ts); 
    448     } 
    449  
    450     pj_lock_release(echo->lock); 
    451  
    452     return PJ_SUCCESS; 
    453 } 
    454  
    455  
    456 /* 
    457  * Let the AEC knows that a frame has been captured from the microphone. 
    458  */ 
    459 PJ_DEF(pj_status_t) speex_aec_capture( void *state, 
    460                                        pj_int16_t *rec_frm, 
    461                                        unsigned options ) 
    462 { 
    463     speex_ec *echo = (speex_ec*) state; 
    464     pj_status_t status = PJ_SUCCESS; 
    465  
    466     /* Sanity checks */ 
    467     PJ_ASSERT_RETURN(echo && rec_frm, PJ_EINVAL); 
    468  
    469     /* The AEC must be configured to support internal playback buffer */ 
    470     PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP); 
    471  
    472     /* Lock mutex */ 
    473     pj_lock_acquire(echo->lock); 
    474  
    475     /* Inc timestamp */ 
    476     echo->rec_ts += echo->samples_per_frame; 
    477  
    478     /* Init frame delay. */ 
    479     if ((echo->ts_flag & TS_FLAG_REC) == 0) { 
    480         echo->ts_flag |= TS_FLAG_REC; 
    481  
    482         if (echo->ts_flag == TS_FLAG_OK) { 
    483             int seq_delay; 
    484  
    485             seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /  
    486                             (int)echo->samples_per_frame; 
    487             pjmedia_frame_queue_init(echo->frame_queue, seq_delay,  
    488                                      echo->prefetch); 
    489         } 
    490     } 
    491  
    492     /* Cancel echo */ 
    493     if (echo->ts_flag == TS_FLAG_OK) { 
    494         void *play_buf; 
    495         unsigned size = 0; 
    496          
    497         if (pjmedia_frame_queue_empty(echo->frame_queue)) { 
    498             int seq_delay; 
    499  
    500             seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /  
    501                             (int)echo->samples_per_frame; 
    502             pjmedia_frame_queue_init(echo->frame_queue, seq_delay,  
    503                                      echo->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     } 
    518  
    519     pj_lock_release(echo->lock); 
    520     return PJ_SUCCESS; 
     148    speex_echo_state_reset(echo->state); 
    521149} 
    522150 
Note: See TracChangeset for help on using the changeset viewer.