summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pjmedia/include/pjmedia/echo.h4
-rw-r--r--pjmedia/include/pjmedia/echo_port.h4
-rw-r--r--pjmedia/src/pjmedia/echo_common.c11
-rw-r--r--pjmedia/src/pjmedia/echo_port.c3
-rw-r--r--pjmedia/src/pjmedia/echo_speex.c346
-rw-r--r--pjmedia/src/pjmedia/sound_port.c3
-rw-r--r--pjsip-apps/src/samples/aectest.c3
-rw-r--r--pjsip-apps/src/samples/debug.c2
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h22
9 files changed, 330 insertions, 68 deletions
diff --git a/pjmedia/include/pjmedia/echo.h b/pjmedia/include/pjmedia/echo.h
index c872bce9..96e2bffe 100644
--- a/pjmedia/include/pjmedia/echo.h
+++ b/pjmedia/include/pjmedia/echo.h
@@ -82,6 +82,9 @@ typedef enum pjmedia_echo_flag
* @param clock_rate Media clock rate/sampling rate.
* @param samples_per_frame Number of samples per frame.
* @param tail_ms Tail length, miliseconds.
+ * @param latency_ms Total lacency introduced by playback and
+ * recording device. Set to zero if the latency
+ * is not known.
* @param options Options. If PJMEDIA_ECHO_SIMPLE is specified,
* then a simple echo suppressor implementation
* will be used instead of an accoustic echo
@@ -95,6 +98,7 @@ PJ_DECL(pj_status_t) pjmedia_echo_create(pj_pool_t *pool,
unsigned clock_rate,
unsigned samples_per_frame,
unsigned tail_ms,
+ unsigned latency_ms,
unsigned options,
pjmedia_echo_state **p_echo );
diff --git a/pjmedia/include/pjmedia/echo_port.h b/pjmedia/include/pjmedia/echo_port.h
index d05c1a43..9b2d89e4 100644
--- a/pjmedia/include/pjmedia/echo_port.h
+++ b/pjmedia/include/pjmedia/echo_port.h
@@ -46,6 +46,9 @@ PJ_BEGIN_DECL
* @param pool Pool to allocate memory.
* @param dn_port Downstream port.
* @param tail_ms Tail length in miliseconds.
+ * @param latency_ms Total lacency introduced by playback and
+ * recording device. Set to zero if the latency
+ * is not known.
* @param options Options, as in #pjmedia_echo_create().
* @param p_port Pointer to receive the port instance.
*
@@ -54,6 +57,7 @@ PJ_BEGIN_DECL
PJ_DECL(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool,
pjmedia_port *dn_port,
unsigned tail_ms,
+ unsigned latency_ms,
unsigned options,
pjmedia_port **p_port );
diff --git a/pjmedia/src/pjmedia/echo_common.c b/pjmedia/src/pjmedia/echo_common.c
index 7dad3037..af5b038f 100644
--- a/pjmedia/src/pjmedia/echo_common.c
+++ b/pjmedia/src/pjmedia/echo_common.c
@@ -37,6 +37,7 @@ struct ec_operations
unsigned clock_rate,
unsigned samples_per_frame,
unsigned tail_ms,
+ unsigned latency_ms,
unsigned options,
void **p_state );
pj_status_t (*ec_destroy)(void *state );
@@ -61,6 +62,7 @@ PJ_DECL(pj_status_t) echo_supp_create(pj_pool_t *pool,
unsigned clock_rate,
unsigned samples_per_frame,
unsigned tail_ms,
+ unsigned latency_ms,
unsigned options,
void **p_state );
PJ_DECL(pj_status_t) echo_supp_destroy(void *state);
@@ -94,6 +96,7 @@ PJ_DECL(pj_status_t) speex_aec_create(pj_pool_t *pool,
unsigned clock_rate,
unsigned samples_per_frame,
unsigned tail_ms,
+ unsigned latency_ms,
unsigned options,
void **p_state );
PJ_DECL(pj_status_t) speex_aec_destroy(void *state );
@@ -130,6 +133,7 @@ PJ_DEF(pj_status_t) pjmedia_echo_create( pj_pool_t *pool,
unsigned clock_rate,
unsigned samples_per_frame,
unsigned tail_ms,
+ unsigned latency_ms,
unsigned options,
pjmedia_echo_state **p_echo )
{
@@ -145,15 +149,14 @@ PJ_DEF(pj_status_t) pjmedia_echo_create( pj_pool_t *pool,
if (options & PJMEDIA_ECHO_SIMPLE) {
ec->op = &echo_supp_op;
- status = (*echo_supp_op.ec_create)(pool, clock_rate,
- samples_per_frame,
- tail_ms, options,
+ status = (*echo_supp_op.ec_create)(pool, clock_rate, samples_per_frame,
+ tail_ms, latency_ms, options,
&ec->state);
} else {
ec->op = &aec_op;
status = (*aec_op.ec_create)(pool, clock_rate,
samples_per_frame,
- tail_ms, options,
+ tail_ms, latency_ms, options,
&ec->state);
}
diff --git a/pjmedia/src/pjmedia/echo_port.c b/pjmedia/src/pjmedia/echo_port.c
index 82fbca11..c9128c06 100644
--- a/pjmedia/src/pjmedia/echo_port.c
+++ b/pjmedia/src/pjmedia/echo_port.c
@@ -46,6 +46,7 @@ static pj_status_t ec_on_destroy(pjmedia_port *this_port);
PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool,
pjmedia_port *dn_port,
unsigned tail_ms,
+ unsigned latency_ms,
unsigned options,
pjmedia_port **p_port )
{
@@ -68,7 +69,7 @@ PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool,
status = pjmedia_echo_create(pool, dn_port->info.clock_rate,
dn_port->info.samples_per_frame,
- tail_ms, options, &ec->ec);
+ tail_ms, latency_ms, options, &ec->ec);
if (status != PJ_SUCCESS)
return status;
diff --git a/pjmedia/src/pjmedia/echo_speex.c b/pjmedia/src/pjmedia/echo_speex.c
index bf8f3f72..af5f2a95 100644
--- a/pjmedia/src/pjmedia/echo_speex.c
+++ b/pjmedia/src/pjmedia/echo_speex.c
@@ -29,8 +29,201 @@
#include <speex/speex_preprocess.h>
-#define THIS_FILE "echo_speex.c"
-#define BUF_COUNT 8
+#define THIS_FILE "echo_speex.c"
+#define BUF_COUNT 20
+#define MIN_PREFETCH 4
+#define MAX_PREFETCH 12
+
+
+
+#if 0
+# define TRACE_(expr) PJ_LOG(5,expr)
+#else
+# define TRACE_(expr)
+#endif
+
+
+typedef struct pjmedia_frame_queue pjmedia_frame_queue;
+
+struct fq_frame
+{
+ PJ_DECL_LIST_MEMBER(struct fq_frame);
+ void *buf;
+ unsigned size;
+ pj_uint32_t seq;
+};
+
+struct pjmedia_frame_queue
+{
+ char obj_name[PJ_MAX_OBJ_NAME];
+ unsigned frame_size;
+ int samples_per_frame;
+ unsigned count;
+ unsigned max_count;
+ struct fq_frame frame_list;
+ struct fq_frame free_list;
+
+ int seq_delay;
+ int prefetch_count;
+};
+
+PJ_DEF(pj_status_t) pjmedia_frame_queue_create( pj_pool_t *pool,
+ const char *name,
+ unsigned frame_size,
+ unsigned samples_per_frame,
+ unsigned max_count,
+ pjmedia_frame_queue **p_fq)
+{
+ pjmedia_frame_queue *fq;
+ unsigned i;
+
+ fq = pj_pool_zalloc(pool, sizeof(pjmedia_frame_queue));
+
+ pj_ansi_snprintf(fq->obj_name, sizeof(fq->obj_name), name, fq);
+ fq->obj_name[sizeof(fq->obj_name)-1] = '\0';
+
+ fq->max_count = max_count;
+ fq->frame_size = frame_size;
+ fq->samples_per_frame = samples_per_frame;
+ fq->count = 0;
+
+ pj_list_init(&fq->frame_list);
+ pj_list_init(&fq->free_list);
+
+ for (i=0; i<max_count; ++i) {
+ struct fq_frame *f;
+
+ f = pj_pool_zalloc(pool, sizeof(struct fq_frame));
+ f->buf = pj_pool_alloc(pool, frame_size);
+
+ pj_list_push_back(&fq->free_list, f);
+
+ }
+
+ *p_fq = fq;
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_frame_queue_init( pjmedia_frame_queue *fq,
+ int seq_delay,
+ int prefetch_count)
+{
+ if (prefetch_count > MAX_PREFETCH)
+ prefetch_count = MAX_PREFETCH;
+
+ fq->seq_delay = seq_delay;
+ fq->prefetch_count = prefetch_count;
+ fq->count = 0;
+ pj_list_merge_first(&fq->free_list, &fq->frame_list);
+
+ PJ_LOG(5,(fq->obj_name, "AEC reset, delay=%d, prefetch=%d",
+ fq->seq_delay, fq->prefetch_count));
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_bool_t) pjmedia_frame_queue_empty( pjmedia_frame_queue *fq )
+{
+ return pj_list_empty(&fq->frame_list);
+}
+
+PJ_DEF(int) pjmedia_frame_queue_get_prefetch( pjmedia_frame_queue *fq )
+{
+ return fq->prefetch_count;
+}
+
+PJ_DEF(pj_status_t) pjmedia_frame_queue_put( pjmedia_frame_queue *fq,
+ const void *framebuf,
+ unsigned size,
+ pj_uint32_t timestamp )
+{
+ struct fq_frame *f;
+
+ TRACE_((fq->obj_name, "PUT seq=%d, count=%d",
+ timestamp / fq->samples_per_frame, fq->count));
+
+ if (pj_list_empty(&fq->free_list)) {
+ PJ_LOG(5,(fq->obj_name,
+ " AEC info: queue is full, frame discarded "
+ "[count=%d, seq=%d]",
+ fq->max_count, timestamp / fq->samples_per_frame));
+ return PJ_ETOOMANY;
+ }
+
+ PJ_ASSERT_RETURN(size <= fq->frame_size, PJ_ETOOBIG);
+
+ f = fq->free_list.next;
+ pj_list_erase(f);
+
+ pj_memcpy(f->buf, framebuf, size);
+ f->size = size;
+ f->seq = timestamp / fq->samples_per_frame;
+
+ pj_list_push_back(&fq->frame_list, f);
+ ++fq->count;
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_frame_queue_get( pjmedia_frame_queue *fq,
+ pj_uint32_t get_timestamp,
+ void **framebuf,
+ unsigned *size )
+{
+ pj_uint32_t frame_seq;
+ struct fq_frame *f;
+
+ frame_seq = get_timestamp/fq->samples_per_frame + fq->seq_delay -
+ fq->prefetch_count;
+
+ TRACE_((fq->obj_name, "GET seq=%d for seq=%d delay=%d, prefetch=%d",
+ get_timestamp/fq->samples_per_frame, frame_seq, fq->seq_delay,
+ fq->prefetch_count));
+
+ *size = 0;
+
+ /* Remove old frames */
+ for (;!pj_list_empty(&fq->frame_list);) {
+ f = fq->frame_list.next;
+ if (f->seq >= frame_seq)
+ break;
+
+ PJ_LOG(5,(fq->obj_name,
+ " AEC Info: old frame removed (seq=%d, want=%d, count=%d)",
+ f->seq, frame_seq, fq->count));
+ pj_list_erase(f);
+ --fq->count;
+ pj_list_push_back(&fq->free_list, f);
+ }
+
+ if (pj_list_empty(&fq->frame_list)) {
+ PJ_LOG(5,(fq->obj_name,
+ " AEC Info: empty queue for seq=%d!",
+ frame_seq));
+ return PJ_ENOTFOUND;
+ }
+
+ f = fq->frame_list.next;
+
+ if (f->seq > frame_seq) {
+ PJ_LOG(5,(fq->obj_name,
+ " AEC Info: prefetching (first seq=%d)",
+ f->seq));
+ return -1;
+ }
+
+ pj_list_erase(f);
+ --fq->count;
+
+ *framebuf = (void*)f->buf;
+ *size = f->size;
+
+ TRACE_((fq->obj_name, " returning frame with seq=%d, count=%d",
+ f->seq, fq->count));
+
+ pj_list_push_front(&fq->free_list, f);
+ return PJ_SUCCESS;
+}
/*
* Prototypes
@@ -39,6 +232,7 @@ PJ_DECL(pj_status_t) speex_aec_create(pj_pool_t *pool,
unsigned clock_rate,
unsigned samples_per_frame,
unsigned tail_ms,
+ unsigned latency_ms,
unsigned options,
void **p_state );
PJ_DECL(pj_status_t) speex_aec_destroy(void *state );
@@ -54,9 +248,11 @@ PJ_DECL(pj_status_t) speex_aec_cancel_echo(void *state,
void *reserved );
-struct frame
+enum
{
- pj_int16_t *buf;
+ TS_FLAG_PLAY = 1,
+ TS_FLAG_REC = 2,
+ TS_FLAG_OK = 3,
};
typedef struct speex_ec
@@ -64,16 +260,18 @@ typedef struct speex_ec
SpeexEchoState *state;
SpeexPreprocessState *preprocess;
- unsigned samples_per_frame;
- unsigned options;
- pj_int16_t *tmp_frame;
- spx_int32_t *residue;
+ unsigned samples_per_frame;
+ unsigned prefetch;
+ unsigned options;
+ pj_int16_t *tmp_frame;
+ spx_int32_t *residue;
- pj_lock_t *lock; /* To protect buffers, if required */
+ pj_uint32_t play_ts,
+ rec_ts,
+ ts_flag;
- unsigned rpos; /* Index to get oldest frame. */
- unsigned wpos; /* Index to put newest frame. */
- struct frame frames[BUF_COUNT]; /* Playback frame buffers. */
+ pjmedia_frame_queue *frame_queue;
+ pj_lock_t *lock; /* To protect buffers, if required */
} speex_ec;
@@ -85,13 +283,13 @@ PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool,
unsigned clock_rate,
unsigned samples_per_frame,
unsigned tail_ms,
+ unsigned latency_ms,
unsigned options,
void **p_echo )
{
speex_ec *echo;
int sampling_rate;
- unsigned i;
- int disabled;
+ int disabled, enabled;
pj_status_t status;
*p_echo = NULL;
@@ -110,6 +308,11 @@ PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool,
}
echo->samples_per_frame = samples_per_frame;
+ echo->prefetch = (latency_ms * clock_rate / 1000) / samples_per_frame;
+ if (echo->prefetch < MIN_PREFETCH)
+ echo->prefetch = MIN_PREFETCH;
+ if (echo->prefetch > MAX_PREFETCH)
+ echo->prefetch = MAX_PREFETCH;
echo->options = options;
echo->state = speex_echo_state_init(samples_per_frame,
@@ -129,8 +332,9 @@ PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool,
/* Disable all preprocessing, we only want echo cancellation */
disabled = 0;
+ enabled = 1;
speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DENOISE,
- &disabled);
+ &enabled);
speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_AGC,
&disabled);
speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_VAD,
@@ -149,24 +353,27 @@ PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool,
/* Create temporary frame to receive residue */
echo->residue = pj_pool_zalloc(pool, sizeof(spx_int32_t) *
- samples_per_frame);
+ (samples_per_frame+1));
PJ_ASSERT_RETURN(echo->residue != NULL, PJ_ENOMEM);
- /* Create internal playback buffers */
- for (i=0; i<BUF_COUNT; ++i) {
- echo->frames[i].buf = pj_pool_zalloc(pool, samples_per_frame * 2);
- PJ_ASSERT_RETURN(echo->frames[i].buf != NULL, PJ_ENOMEM);
+ /* Create frame queue */
+ status = pjmedia_frame_queue_create(pool, "aec%p", samples_per_frame*2,
+ samples_per_frame, BUF_COUNT,
+ &echo->frame_queue);
+ if (status != PJ_SUCCESS) {
+ speex_preprocess_state_destroy(echo->preprocess);
+ speex_echo_state_destroy(echo->state);
+ pj_lock_destroy(echo->lock);
+ return status;
}
-
/* Done */
*p_echo = echo;
PJ_LOG(4,(THIS_FILE, "Speex Echo canceller/AEC created, clock_rate=%d, "
- "samples per frame=%d, tail length=%d ms",
- clock_rate,
- samples_per_frame,
- tail_ms));
+ "samples per frame=%d, tail length=%d ms, "
+ "latency=%d ms",
+ clock_rate, samples_per_frame, tail_ms, latency_ms));
return PJ_SUCCESS;
}
@@ -215,23 +422,29 @@ PJ_DEF(pj_status_t) speex_aec_playback(void *state,
PJ_ASSERT_RETURN(echo && play_frm, PJ_EINVAL);
/* The AEC must be configured to support internal playback buffer */
- PJ_ASSERT_RETURN(echo->frames[0].buf != NULL, PJ_EINVALIDOP);
+ PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP);
pj_lock_acquire(echo->lock);
- /* Check for overflows */
- if (echo->wpos == echo->rpos) {
- PJ_LOG(5,(THIS_FILE, "Speex AEC overflow (playback runs faster, "
- "wpos=%d, rpos=%d)",
- echo->wpos, echo->rpos));
- echo->rpos = (echo->wpos - BUF_COUNT/2) % BUF_COUNT;
- speex_echo_state_reset(echo->state);
+ /* Inc timestamp */
+ echo->play_ts += echo->samples_per_frame;
+
+ /* Initialize frame delay. */
+ if ((echo->ts_flag & TS_FLAG_PLAY) == 0) {
+ echo->ts_flag |= TS_FLAG_PLAY;
+
+ if (echo->ts_flag == TS_FLAG_OK) {
+ int seq_delay;
+
+ seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /
+ (int)echo->samples_per_frame;
+ pjmedia_frame_queue_init(echo->frame_queue, seq_delay,
+ echo->prefetch);
+ }
}
- /* Save fhe frame */
- pjmedia_copy_samples(echo->frames[echo->wpos].buf,
- play_frm, echo->samples_per_frame);
- echo->wpos = (echo->wpos+1) % BUF_COUNT;
+ pjmedia_frame_queue_put(echo->frame_queue, play_frm,
+ echo->samples_per_frame*2, echo->play_ts);
pj_lock_release(echo->lock);
@@ -247,42 +460,64 @@ PJ_DEF(pj_status_t) speex_aec_capture( void *state,
unsigned options )
{
speex_ec *echo = state;
- pj_status_t status;
+ pj_status_t status = PJ_SUCCESS;
/* Sanity checks */
PJ_ASSERT_RETURN(echo && rec_frm, PJ_EINVAL);
/* The AEC must be configured to support internal playback buffer */
- PJ_ASSERT_RETURN(echo->frames[0].buf != NULL, PJ_EINVALIDOP);
+ PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP);
/* Lock mutex */
pj_lock_acquire(echo->lock);
+ /* Inc timestamp */
+ echo->rec_ts += echo->samples_per_frame;
- /* Check for underflow */
- if (echo->rpos == echo->wpos) {
- /* Return frame as it is */
- pj_lock_release(echo->lock);
+ /* Init frame delay. */
+ if ((echo->ts_flag & TS_FLAG_REC) == 0) {
+ echo->ts_flag |= TS_FLAG_REC;
- PJ_LOG(5,(THIS_FILE, "Speex AEC underflow (capture runs faster than "
- "playback, wpos=%d, rpos=%d)",
- echo->wpos, echo->rpos));
- echo->rpos = (echo->wpos - BUF_COUNT/2) % BUF_COUNT;
- speex_echo_state_reset(echo->state);
+ if (echo->ts_flag == TS_FLAG_OK) {
+ int seq_delay;
- return PJ_SUCCESS;
+ seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /
+ (int)echo->samples_per_frame;
+ pjmedia_frame_queue_init(echo->frame_queue, seq_delay,
+ echo->prefetch);
+ }
}
-
/* Cancel echo */
- status = speex_aec_cancel_echo(echo, rec_frm,
- echo->frames[echo->rpos].buf, options,
- NULL);
+ if (echo->ts_flag == TS_FLAG_OK) {
+ void *play_buf;
+ unsigned size = 0;
+
+ if (pjmedia_frame_queue_empty(echo->frame_queue)) {
+ int seq_delay, prefetch;
+
+ seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /
+ (int)echo->samples_per_frame;
+ prefetch = pjmedia_frame_queue_get_prefetch(echo->frame_queue);
+ //++prefetch;
+ pjmedia_frame_queue_init(echo->frame_queue, seq_delay, prefetch);
+ status = -1;
+
+ } else {
+ status = pjmedia_frame_queue_get(echo->frame_queue, echo->rec_ts,
+ &play_buf, &size);
+ if (size != 0) {
+ speex_aec_cancel_echo(echo, rec_frm, (pj_int16_t*)play_buf,
+ options, NULL);
+ }
+ }
- echo->rpos = (echo->rpos + 1) % BUF_COUNT;
+ if (status != PJ_SUCCESS)
+ speex_echo_state_reset(echo->state);
+ }
pj_lock_release(echo->lock);
- return status;
+ return PJ_SUCCESS;
}
@@ -319,3 +554,4 @@ PJ_DEF(pj_status_t) speex_aec_cancel_echo( void *state,
}
+ \ No newline at end of file
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 02478e06..afb4effa 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -492,7 +492,8 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port,
snd_port->clock_rate;
status = pjmedia_echo_create(pool, snd_port->clock_rate,
snd_port->samples_per_frame,
- tail_ms, options, &snd_port->ec_state);
+ tail_ms, delay_ms,
+ options, &snd_port->ec_state);
if (status != PJ_SUCCESS)
snd_port->ec_state = NULL;
else
diff --git a/pjsip-apps/src/samples/aectest.c b/pjsip-apps/src/samples/aectest.c
index 5f150682..286abcbf 100644
--- a/pjsip-apps/src/samples/aectest.c
+++ b/pjsip-apps/src/samples/aectest.c
@@ -35,6 +35,7 @@
/* For logging purpose. */
#define THIS_FILE "playfile.c"
#define PTIME 20
+#define TAIL_LENGTH 800
static const char *desc =
" FILE \n"
@@ -174,7 +175,7 @@ int main(int argc, char *argv[])
/* Customize AEC */
- pjmedia_snd_port_set_aec(snd, pool, 800);
+ pjmedia_snd_port_set_ec(snd, pool, TAIL_LENGTH, 0);
/* Connect sound to the port */
pjmedia_snd_port_connect(snd, bidir_port);
diff --git a/pjsip-apps/src/samples/debug.c b/pjsip-apps/src/samples/debug.c
index 13dafc0e..ea771f97 100644
--- a/pjsip-apps/src/samples/debug.c
+++ b/pjsip-apps/src/samples/debug.c
@@ -27,4 +27,4 @@
* E.g.:
* #include "playfile.c"
*/
-#include "tonegen.c"
+#include "aectest.c"
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index ec040fb9..011acd96 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -2215,13 +2215,25 @@ PJ_DECL(pj_status_t) pjsua_im_typing(pjsua_acc_id acc_id,
* Max ports in the conference bridge.
*/
#ifndef PJSUA_MAX_CONF_PORTS
-# define PJSUA_MAX_CONF_PORTS 254
+# define PJSUA_MAX_CONF_PORTS 254
#endif
-#define PJSUA_DEFAULT_CLOCK_RATE 16000
-#define PJSUA_DEFAULT_CODEC_QUALITY 5
-#define PJSUA_DEFAULT_ILBC_MODE 20
-#define PJSUA_DEFAULT_EC_TAIL_LEN 0
+#ifndef PJSUA_DEFAULT_CLOCK_RATE
+# define PJSUA_DEFAULT_CLOCK_RATE 16000
+#endif
+
+#ifndef PJSUA_DEFAULT_CODEC_QUALITY
+# define PJSUA_DEFAULT_CODEC_QUALITY 5
+#endif
+
+#ifndef PJSUA_DEFAULT_ILBC_MODE
+# define PJSUA_DEFAULT_ILBC_MODE 20
+#endif
+
+
+#ifndef PJSUA_DEFAULT_EC_TAIL_LEN
+# define PJSUA_DEFAULT_EC_TAIL_LEN 600
+#endif
/**