summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-08-04 18:27:19 +0000
committerBenny Prijono <bennylp@teluu.com>2006-08-04 18:27:19 +0000
commit3474adb4cba6d8f0e3a2858a59f64683ae8153ad (patch)
treea0524cf2c80fafe1691a2da330bf5352b2b5c087 /pjmedia
parentca972f8eb9678ff162c6e7acc4059b8bec5fb43c (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.h4
-rw-r--r--pjmedia/include/pjmedia/aec_port.h4
-rw-r--r--pjmedia/include/pjmedia/sound_port.h34
-rw-r--r--pjmedia/src/pjmedia/aec_port.c6
-rw-r--r--pjmedia/src/pjmedia/aec_speex.c60
-rw-r--r--pjmedia/src/pjmedia/dsound.c46
-rw-r--r--pjmedia/src/pjmedia/sound_port.c78
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,