diff options
author | Benny Prijono <bennylp@teluu.com> | 2006-08-04 18:27:19 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2006-08-04 18:27:19 +0000 |
commit | 3474adb4cba6d8f0e3a2858a59f64683ae8153ad (patch) | |
tree | a0524cf2c80fafe1691a2da330bf5352b2b5c087 /pjmedia | |
parent | ca972f8eb9678ff162c6e7acc4059b8bec5fb43c (diff) |
More work on the AEC (including changes in PJSUA), embed the AEC in sound_port, reduce DirectSound buffer from 32 to 16, and fixed ARM compilation for MSVC WinCE target.
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@648 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r-- | pjmedia/include/pjmedia/aec.h | 4 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/aec_port.h | 4 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/sound_port.h | 34 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/aec_port.c | 6 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/aec_speex.c | 60 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/dsound.c | 46 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/sound_port.c | 78 |
7 files changed, 159 insertions, 73 deletions
diff --git a/pjmedia/include/pjmedia/aec.h b/pjmedia/include/pjmedia/aec.h index a2354ef3..84ed7e0f 100644 --- a/pjmedia/include/pjmedia/aec.h +++ b/pjmedia/include/pjmedia/aec.h @@ -51,14 +51,14 @@ typedef struct pjmedia_aec pjmedia_aec; * @param pool Pool to allocate memory. * @param clock_rate Media clock rate/sampling rate. * @param samples_per_frame Number of samples per frame. - * @param tail_size Tail length, in number of samples. + * @param tail_ms Tail length, miliseconds. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_aec_create( pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, - unsigned tail_size, + unsigned tail_ms, unsigned options, pjmedia_aec **p_aec ); diff --git a/pjmedia/include/pjmedia/aec_port.h b/pjmedia/include/pjmedia/aec_port.h index f18b1378..2968c915 100644 --- a/pjmedia/include/pjmedia/aec_port.h +++ b/pjmedia/include/pjmedia/aec_port.h @@ -43,14 +43,14 @@ PJ_BEGIN_DECL * * @param pool Pool to allocate memory. * @param dn_port Downstream port. - * @param tail_length Tail length in samples. + * @param tail_ms Tail length in miliseconds. * @param p_port Pointer to receive the port instance. * * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjmedia_aec_port_create( pj_pool_t *pool, pjmedia_port *dn_port, - unsigned tail_length, + unsigned tail_ms, pjmedia_port **p_port ); diff --git a/pjmedia/include/pjmedia/sound_port.h b/pjmedia/include/pjmedia/sound_port.h index 15339bcc..c144275d 100644 --- a/pjmedia/include/pjmedia/sound_port.h +++ b/pjmedia/include/pjmedia/sound_port.h @@ -183,6 +183,40 @@ PJ_DECL(pjmedia_snd_stream*) pjmedia_snd_port_get_snd_stream( /** + * Enable accoustic echo cancellation (AEC) to the specified sound. + * The AEC can only be enabled for sound streams with full-duplex direction. + * + * And note, you should only change the AEC settings when the sound port + * is not connected to any downstream ports. + * + * @param snd_port The sound device port. + * @param pool Pool to re-create the AEC if necessary. + * @param tail_ms Maximum echo tail length to be supported, in + * miliseconds. If zero is specified, the AEC would + * be disabled. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_snd_port_set_aec(pjmedia_snd_port *snd_port, + pj_pool_t *pool, + unsigned tail_ms); + + +/** + * Get current AEC tail length, in miliseconds. The tail length will be zero + * if AEC is not enabled. + * + * @param snd_port The sound device port. + * @param p_length Pointer to receive the tail length. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjmedia_snd_port_get_aec_tail(pjmedia_snd_port *snd_port, + unsigned *p_length); + + + +/** * Connect a port to the sound device port. If the sound device port has a * sound recorder device, then this will start periodic function call to * the port's put_frame() function. If the sound device has a sound player diff --git a/pjmedia/src/pjmedia/aec_port.c b/pjmedia/src/pjmedia/aec_port.c index aa56b175..70d3a217 100644 --- a/pjmedia/src/pjmedia/aec_port.c +++ b/pjmedia/src/pjmedia/aec_port.c @@ -45,7 +45,7 @@ static pj_status_t aec_on_destroy(pjmedia_port *this_port); PJ_DEF(pj_status_t) pjmedia_aec_port_create( pj_pool_t *pool, pjmedia_port *dn_port, - unsigned tail_length, + unsigned tail_ms, pjmedia_port **p_port ) { const pj_str_t AEC = { "AEC", 3 }; @@ -53,7 +53,7 @@ PJ_DEF(pj_status_t) pjmedia_aec_port_create( pj_pool_t *pool, pj_status_t status; PJ_ASSERT_RETURN(pool && dn_port && p_port, PJ_EINVAL); - PJ_ASSERT_RETURN(dn_port->info.bits_per_sample==16 && tail_length, + PJ_ASSERT_RETURN(dn_port->info.bits_per_sample==16 && tail_ms, PJ_EINVAL); /* Create the port and the AEC itself */ @@ -67,7 +67,7 @@ PJ_DEF(pj_status_t) pjmedia_aec_port_create( pj_pool_t *pool, status = pjmedia_aec_create(pool, dn_port->info.clock_rate, dn_port->info.samples_per_frame, - tail_length, 0, &aec->aec); + tail_ms, 0, &aec->aec); if (status != PJ_SUCCESS) return status; diff --git a/pjmedia/src/pjmedia/aec_speex.c b/pjmedia/src/pjmedia/aec_speex.c index 76a8dd44..95cc15b4 100644 --- a/pjmedia/src/pjmedia/aec_speex.c +++ b/pjmedia/src/pjmedia/aec_speex.c @@ -26,10 +26,11 @@ #include <pj/os.h> #include <pj/pool.h> #include <speex/speex_echo.h> +#include <speex/speex_preprocess.h> #define THIS_FILE "aec_speex.c" -#define BUF_COUNT 16 +#define BUF_COUNT 8 struct frame @@ -39,10 +40,13 @@ struct frame struct pjmedia_aec { - SpeexEchoState *state; + SpeexEchoState *state; + SpeexPreprocessState *preprocess; + unsigned samples_per_frame; unsigned options; pj_int16_t *tmp_frame; + spx_int32_t *residue; pj_lock_t *lock; /* To protect buffers, if required */ @@ -59,7 +63,7 @@ struct pjmedia_aec PJ_DEF(pj_status_t) pjmedia_aec_create( pj_pool_t *pool, unsigned clock_rate, unsigned samples_per_frame, - unsigned tail_size, + unsigned tail_ms, unsigned options, pjmedia_aec **p_aec ) { @@ -68,6 +72,8 @@ PJ_DEF(pj_status_t) pjmedia_aec_create( pj_pool_t *pool, unsigned i; pj_status_t status; + *p_aec = NULL; + aec = pj_pool_zalloc(pool, sizeof(pjmedia_aec)); PJ_ASSERT_RETURN(aec != NULL, PJ_ENOMEM); @@ -78,22 +84,35 @@ PJ_DEF(pj_status_t) pjmedia_aec_create( pj_pool_t *pool, aec->samples_per_frame = samples_per_frame; aec->options = options; - aec->state = speex_echo_state_init(samples_per_frame,tail_size); + aec->state = speex_echo_state_init(samples_per_frame, + clock_rate * tail_ms / 1000); if (aec->state == NULL) { pj_lock_destroy(aec->lock); return PJ_ENOMEM; } + aec->preprocess = speex_preprocess_state_init(samples_per_frame, + clock_rate); + if (aec->preprocess == NULL) { + speex_echo_state_destroy(aec->state); + pj_lock_destroy(aec->lock); + return PJ_ENOMEM; + } + /* Set sampling rate */ sampling_rate = clock_rate; speex_echo_ctl(aec->state, SPEEX_ECHO_SET_SAMPLING_RATE, &sampling_rate); /* Create temporary frame for echo cancellation */ - aec->tmp_frame = pj_pool_zalloc(pool, sizeof(pj_int16_t) * - samples_per_frame); + aec->tmp_frame = pj_pool_zalloc(pool, 2 * samples_per_frame); PJ_ASSERT_RETURN(aec->tmp_frame != NULL, PJ_ENOMEM); + /* Create temporary frame to receive residue */ + aec->residue = pj_pool_zalloc(pool, sizeof(spx_int32_t) * + samples_per_frame); + PJ_ASSERT_RETURN(aec->residue != NULL, PJ_ENOMEM); + /* Create internal playback buffers */ for (i=0; i<BUF_COUNT; ++i) { aec->frames[i].buf = pj_pool_zalloc(pool, samples_per_frame * 2); @@ -108,7 +127,7 @@ PJ_DEF(pj_status_t) pjmedia_aec_create( pj_pool_t *pool, "samples per frame=%d, tail length=%d ms", clock_rate, samples_per_frame, - tail_size * 1000 / clock_rate)); + tail_ms)); return PJ_SUCCESS; } @@ -121,11 +140,19 @@ PJ_DEF(pj_status_t) pjmedia_aec_destroy(pjmedia_aec *aec ) { PJ_ASSERT_RETURN(aec && aec->state, PJ_EINVAL); + if (aec->lock) + pj_lock_acquire(aec->lock); + if (aec->state) { speex_echo_state_destroy(aec->state); aec->state = NULL; } + if (aec->preprocess) { + speex_preprocess_state_destroy(aec->preprocess); + aec->preprocess = NULL; + } + if (aec->lock) { pj_lock_destroy(aec->lock); aec->lock = NULL; @@ -224,8 +251,6 @@ PJ_DEF(pj_status_t) pjmedia_aec_cancel_echo( pjmedia_aec *aec, unsigned options, void *reserved ) { - unsigned level0, level1; - /* Sanity checks */ PJ_ASSERT_RETURN(aec && rec_frm && play_frm && options==0 && reserved==NULL, PJ_EINVAL); @@ -233,20 +258,13 @@ PJ_DEF(pj_status_t) pjmedia_aec_cancel_echo( pjmedia_aec *aec, /* Cancel echo, put output in temporary buffer */ speex_echo_cancel(aec->state, (const spx_int16_t*)rec_frm, (const spx_int16_t*)play_frm, - (spx_int16_t*)aec->tmp_frame, NULL); + (spx_int16_t*)aec->tmp_frame, + aec->residue); -#if 0 - level0 = pjmedia_calc_avg_signal(rec_frm, aec->samples_per_frame); - level1 = pjmedia_calc_avg_signal(aec->tmp_frame, aec->samples_per_frame); - if (level1 < level0) { - PJ_LOG(5,(THIS_FILE, "Input signal reduced from %d to %d", - level0, level1)); - } -#else - PJ_UNUSED_ARG(level0); - PJ_UNUSED_ARG(level1); -#endif + /* Preprocess output */ + speex_preprocess(aec->preprocess, (spx_int16_t*)aec->tmp_frame, + aec->residue); /* Copy temporary buffer back to original rec_frm */ pjmedia_copy_samples(rec_frm, aec->tmp_frame, aec->samples_per_frame); diff --git a/pjmedia/src/pjmedia/dsound.c b/pjmedia/src/pjmedia/dsound.c index 762378f0..0f9c49a1 100644 --- a/pjmedia/src/pjmedia/dsound.c +++ b/pjmedia/src/pjmedia/dsound.c @@ -452,7 +452,6 @@ static int dsound_dev_thread(void *arg) HANDLE events[2]; unsigned eventCount; unsigned bytes_per_frame; - int excess_rec = 0; pj_status_t status; @@ -502,7 +501,6 @@ static int dsound_dev_thread(void *arg) if (signalled_dir == PJMEDIA_DIR_PLAYBACK) { struct dsound_stream *dsound_strm; - int i; /* * DirectSound has requested us to feed some frames to @@ -512,31 +510,26 @@ static int dsound_dev_thread(void *arg) dsound_strm = &strm->play_strm; status = PJ_SUCCESS; - for (i=0; i <= excess_rec; ++i) { - /* Get frame from application. */ - status = (*strm->play_cb)(strm->user_data, - dsound_strm->timestamp.u32.lo, - strm->buffer, - bytes_per_frame); - if (status != PJ_SUCCESS) - break; - - /* Write to DirectSound buffer. */ - AppWriteDataToBuffer( dsound_strm->ds.play.lpDsBuffer, - dsound_strm->dwBytePos, - (LPBYTE)strm->buffer, + /* Get frame from application. */ + status = (*strm->play_cb)(strm->user_data, + dsound_strm->timestamp.u32.lo, + strm->buffer, bytes_per_frame); - - /* Increment position. */ - dsound_strm->dwBytePos += bytes_per_frame; - if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize) - dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize; - dsound_strm->timestamp.u64 += strm->samples_per_frame; - } - if (status != PJ_SUCCESS) break; + /* Write to DirectSound buffer. */ + AppWriteDataToBuffer( dsound_strm->ds.play.lpDsBuffer, + dsound_strm->dwBytePos, + (LPBYTE)strm->buffer, + bytes_per_frame); + + /* Increment position. */ + dsound_strm->dwBytePos += bytes_per_frame; + if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize) + dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize; + dsound_strm->timestamp.u64 += strm->samples_per_frame; + } else { /* * DirectSound has indicated that it has some frames ready @@ -544,7 +537,6 @@ static int dsound_dev_thread(void *arg) * prevent overflows. */ struct dsound_stream *dsound_strm; - int captured = 0; BOOL rc; dsound_strm = &strm->rec_strm; @@ -558,9 +550,7 @@ static int dsound_dev_thread(void *arg) if (!rc) { pj_bzero(strm->buffer, bytes_per_frame); - } else { - captured++; - } + } /* Call callback */ status = (*strm->rec_cb)(strm->user_data, @@ -582,8 +572,6 @@ static int dsound_dev_thread(void *arg) /* Fetch while we have more than 1 frame */ } while (dsound_captured_size(dsound_strm) > bytes_per_frame); - excess_rec = captured-1; - if (excess_rec < 0) excess_rec = 0; } } diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c index ee3e61c9..37338704 100644 --- a/pjmedia/src/pjmedia/sound_port.c +++ b/pjmedia/src/pjmedia/sound_port.c @@ -34,7 +34,7 @@ #endif //#define SIMULATE_LOST_PCT 20 -#define AEC_TAIL 500 /* in ms */ +#define AEC_TAIL 128 /* default AEC length in ms */ #define THIS_FILE "sound_port.c" @@ -57,6 +57,7 @@ struct pjmedia_snd_port unsigned options; pjmedia_aec *aec; + unsigned aec_tail_len; pjmedia_plc *plc; unsigned clock_rate; @@ -135,10 +136,6 @@ no_frame: } - if (snd_port->aec) { - pjmedia_aec_playback(snd_port->aec, output); - } - return PJ_SUCCESS; } @@ -156,11 +153,6 @@ static pj_status_t rec_cb(/* in */ void *user_data, pjmedia_port *port; pjmedia_frame frame; - /* Cancel echo */ - if (snd_port->aec) { - pjmedia_aec_capture(snd_port->aec, input, 0); - } - /* We're risking accessing the port without holding any mutex. * It's possible that port is disconnected then destroyed while * we're trying to access it. @@ -171,6 +163,11 @@ static pj_status_t rec_cb(/* in */ void *user_data, if (port == NULL) return PJ_SUCCESS; + /* Cancel echo */ + if (snd_port->aec) { + pjmedia_aec_capture(snd_port->aec, input, 0); + } + frame.buf = (void*)input; frame.size = size; frame.type = PJMEDIA_FRAME_TYPE_AUDIO; @@ -246,18 +243,19 @@ static pj_status_t start_sound_device( pj_pool_t *pool, snd_port->samples_per_frame * snd_port->channel_count, 0, &snd_port->plc); - if (status != PJ_SUCCESS) + if (status != PJ_SUCCESS) { + PJ_LOG(4,(THIS_FILE, "Unable to create PLC")); snd_port->plc = NULL; + } } /* Create AEC only when direction is full duplex */ if (snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) { - status = pjmedia_aec_create(pool, snd_port->clock_rate, - snd_port->samples_per_frame, - snd_port->clock_rate * AEC_TAIL / 1000, - 0, &snd_port->aec); - if (status != PJ_SUCCESS) + status = pjmedia_snd_port_set_aec(snd_port, pool, AEC_TAIL); + if (status != PJ_SUCCESS) { + PJ_LOG(4,(THIS_FILE, "Unable to create AEC")); snd_port->aec = NULL; + } } /* Start sound stream. */ @@ -432,6 +430,54 @@ PJ_DEF(pjmedia_snd_stream*) pjmedia_snd_port_get_snd_stream( /* + * Enable AEC + */ +PJ_DEF(pj_status_t) pjmedia_snd_port_set_aec( pjmedia_snd_port *snd_port, + pj_pool_t *pool, + unsigned tail_ms) +{ + pj_status_t status; + + /* Sound must be opened in full-duplex mode */ + PJ_ASSERT_RETURN(snd_port && + snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK, + PJ_EINVALIDOP); + + /* Destroy AEC */ + if (snd_port->aec) { + pjmedia_aec_destroy(snd_port->aec); + snd_port->aec = NULL; + } + + snd_port->aec_tail_len = tail_ms; + + if (tail_ms != 0) { + status = pjmedia_aec_create(pool, snd_port->clock_rate, + snd_port->samples_per_frame, + snd_port->clock_rate * tail_ms / 1000, + 0, &snd_port->aec); + if (status != PJ_SUCCESS) + snd_port->aec = NULL; + } else { + status = PJ_SUCCESS; + } + + return status; +} + + +/* Get AEC tail length */ +PJ_DEF(pj_status_t) pjmedia_snd_port_get_aec_tail( pjmedia_snd_port *snd_port, + unsigned *p_length) +{ + PJ_ASSERT_RETURN(snd_port && p_length, PJ_EINVAL); + *p_length = snd_port->aec ? snd_port->aec_tail_len : 0; + return PJ_SUCCESS; +} + + + +/* * Connect a port. */ PJ_DEF(pj_status_t) pjmedia_snd_port_connect( pjmedia_snd_port *snd_port, |