Index: pjproject/trunk/pjmedia/src/pjmedia/sound_port.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/sound_port.c	(revision 2685)
+++ pjproject/trunk/pjmedia/src/pjmedia/sound_port.c	(revision 2747)
@@ -20,7 +20,7 @@
 #include <pjmedia/sound_port.h>
 #include <pjmedia/alaw_ulaw.h>
-#include <pjmedia/delaybuf.h>
-#include <pjmedia/echo.h>
+#include <pjmedia/echo_port.h>
 #include <pjmedia/errno.h>
+#include <pjmedia/sync_port.h>
 #include <pj/assert.h>
 #include <pj/log.h>
@@ -29,12 +29,10 @@
 
 #define AEC_TAIL	    128	    /* default AEC length in ms */
-#define AEC_SUSPEND_LIMIT   5	    /* seconds of no activity	*/
 
 #define THIS_FILE	    "sound_port.c"
 
-//#define TEST_OVERFLOW_UNDERFLOW
-
 struct pjmedia_snd_port
 {
+    pj_pool_t		*pool;
     int			 rec_id;
     int			 play_id;
@@ -44,4 +42,6 @@
     pjmedia_dir		 dir;
     pjmedia_port	*port;
+    pjmedia_port	*dn_port;
+    pjmedia_port	*sync_port;
 
     unsigned		 clock_rate;
@@ -51,10 +51,7 @@
 
     /* software ec */
-    pjmedia_echo_state	*ec_state;
+    pjmedia_port	*echo_port;
     unsigned		 ec_options;
     unsigned		 ec_tail_len;
-    pj_bool_t		 ec_suspended;
-    unsigned		 ec_suspend_count;
-    unsigned		 ec_suspend_limit;
 };
 
@@ -70,5 +67,5 @@
     pj_status_t status;
 
-    port = snd_port->port;
+    port = snd_port->dn_port;
     if (port == NULL)
 	goto no_frame;
@@ -83,14 +80,4 @@
     /* Must supply the required samples */
     pj_assert(frame->size == required_size);
-
-    if (snd_port->ec_state) {
-	if (snd_port->ec_suspended) {
-	    snd_port->ec_suspended = PJ_FALSE;
-	    //pjmedia_echo_state_reset(snd_port->ec_state);
-	    PJ_LOG(4,(THIS_FILE, "EC activated"));
-	}
-	snd_port->ec_suspend_count = 0;
-	pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
-    }
 
 
@@ -102,16 +89,4 @@
     pj_bzero(frame->buf, frame->size);
 
-    if (snd_port->ec_state && !snd_port->ec_suspended) {
-	++snd_port->ec_suspend_count;
-	if (snd_port->ec_suspend_count > snd_port->ec_suspend_limit) {
-	    snd_port->ec_suspended = PJ_TRUE;
-	    PJ_LOG(4,(THIS_FILE, "EC suspended because of inactivity"));
-	}
-	if (snd_port->ec_state) {
-	    /* To maintain correct delay in EC */
-	    pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
-	}
-    }
-
     return PJ_SUCCESS;
 }
@@ -123,4 +98,42 @@
  */
 static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
+{
+    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
+    pjmedia_port *port;
+
+    port = snd_port->dn_port;
+    if (port == NULL)
+	return PJ_SUCCESS;
+
+    pjmedia_port_put_frame(port, frame);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * The callback called by sound player when it needs more samples to be
+ * played. This version is for non-PCM data.
+ */
+static pj_status_t play_cb_ext(void *user_data, pjmedia_frame *frame)
+{
+    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
+    pjmedia_port *port = snd_port->port;
+
+    if (port == NULL) {
+	frame->type = PJMEDIA_FRAME_TYPE_NONE;
+	return PJ_SUCCESS;
+    }
+
+    pjmedia_port_get_frame(port, frame);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * The callback called by sound recorder when it has finished capturing a
+ * frame. This version is for non-PCM data.
+ */
+static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame)
 {
     pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
@@ -131,47 +144,4 @@
 	return PJ_SUCCESS;
 
-    /* Cancel echo */
-    if (snd_port->ec_state && !snd_port->ec_suspended) {
-	pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0);
-    }
-
-    pjmedia_port_put_frame(port, frame);
-
-    return PJ_SUCCESS;
-}
-
-/*
- * The callback called by sound player when it needs more samples to be
- * played. This version is for non-PCM data.
- */
-static pj_status_t play_cb_ext(void *user_data, pjmedia_frame *frame)
-{
-    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
-    pjmedia_port *port = snd_port->port;
-
-    if (port == NULL) {
-	frame->type = PJMEDIA_FRAME_TYPE_NONE;
-	return PJ_SUCCESS;
-    }
-
-    pjmedia_port_get_frame(port, frame);
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * The callback called by sound recorder when it has finished capturing a
- * frame. This version is for non-PCM data.
- */
-static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame)
-{
-    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
-    pjmedia_port *port;
-
-    port = snd_port->port;
-    if (port == NULL)
-	return PJ_SUCCESS;
-
     pjmedia_port_put_frame(port, frame);
 
@@ -183,10 +153,9 @@
  * This may be called even when the sound stream has already been started.
  */
-static pj_status_t start_sound_device( pj_pool_t *pool,
-				       pjmedia_snd_port *snd_port )
+static pj_status_t start_sound_device( pjmedia_snd_port *snd_port )
 {
     pjmedia_aud_rec_cb snd_rec_cb;
     pjmedia_aud_play_cb snd_play_cb;
-    pjmedia_aud_param param_copy;
+    pjmedia_aud_param *param;
     pj_status_t status;
 
@@ -200,10 +169,11 @@
 		     PJ_EBUG);
 
+    param = &snd_port->aud_param;
+
     /* Get device caps */
-    if (snd_port->aud_param.dir & PJMEDIA_DIR_CAPTURE) {
+    if (param->dir & PJMEDIA_DIR_CAPTURE) {
 	pjmedia_aud_dev_info dev_info;
 
-	status = pjmedia_aud_dev_get_info(snd_port->aud_param.rec_id, 
-					  &dev_info);
+	status = pjmedia_aud_dev_get_info(param->rec_id, &dev_info);
 	if (status != PJ_SUCCESS)
 	    return status;
@@ -214,22 +184,6 @@
     }
 
-    /* Process EC settings */
-    pj_memcpy(&param_copy, &snd_port->aud_param, sizeof(param_copy));
-    if (param_copy.flags & PJMEDIA_AUD_DEV_CAP_EC) {
-	/* EC is wanted */
-	if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) {
-	    /* Device supports EC */
-	    /* Nothing to do */
-	} else {
-	    /* Device doesn't support EC, remove EC settings from
-	     * device parameters
-	     */
-	    param_copy.flags &= ~(PJMEDIA_AUD_DEV_CAP_EC |
-				  PJMEDIA_AUD_DEV_CAP_EC_TAIL);
-	}
-    }
-
     /* Use different callback if format is not PCM */
-    if (snd_port->aud_param.ext_fmt.id == PJMEDIA_FORMAT_L16) {
+    if (param->ext_fmt.id == PJMEDIA_FORMAT_L16) {
 	snd_rec_cb = &rec_cb;
 	snd_play_cb = &play_cb;
@@ -240,5 +194,5 @@
 
     /* Open the device */
-    status = pjmedia_aud_stream_create(&param_copy,
+    status = pjmedia_aud_stream_create(param,
 				       snd_rec_cb,
 				       snd_play_cb,
@@ -248,32 +202,4 @@
     if (status != PJ_SUCCESS)
 	return status;
-
-    /* Inactivity limit before EC is suspended. */
-    snd_port->ec_suspend_limit = AEC_SUSPEND_LIMIT *
-				 (snd_port->clock_rate / 
-				  snd_port->samples_per_frame);
-
-    /* Create software EC if parameter specifies EC but device 
-     * doesn't support EC. Only do this if the format is PCM!
-     */
-    if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC) &&
-	(snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)==0 &&
-	param_copy.ext_fmt.id == PJMEDIA_FORMAT_PCM)
-    {
-	if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) {
-	    snd_port->aud_param.flags |= PJMEDIA_AUD_DEV_CAP_EC_TAIL;
-	    snd_port->aud_param.ec_tail_ms = AEC_TAIL;
-	    PJ_LOG(4,(THIS_FILE, "AEC tail is set to default %u ms",
-				 snd_port->aud_param.ec_tail_ms));
-	}
-	    
-	status = pjmedia_snd_port_set_ec(snd_port, pool, 
-					 snd_port->aud_param.ec_tail_ms, 0);
-	if (status != PJ_SUCCESS) {
-	    pjmedia_aud_stream_destroy(snd_port->aud_stream);
-	    snd_port->aud_stream = NULL;
-	    return status;
-	}
-    }
 
     /* Start sound stream. */
@@ -300,10 +226,4 @@
 	pjmedia_aud_stream_destroy(snd_port->aud_stream);
 	snd_port->aud_stream = NULL;
-    }
-
-    /* Destroy AEC */
-    if (snd_port->ec_state) {
-	pjmedia_echo_destroy(snd_port->ec_state);
-	snd_port->ec_state = NULL;
     }
 
@@ -421,7 +341,9 @@
     PJ_ASSERT_RETURN(pool && prm && p_port, PJ_EINVAL);
 
+    pool = pj_pool_create(pool->factory, pool->obj_name, 256, 256, NULL);
     snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
     PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
 
+    snd_port->pool = pool;
     snd_port->dir = prm->dir;
     snd_port->rec_id = prm->rec_id;
@@ -438,5 +360,5 @@
      * empty signal.
      */
-    status = start_sound_device( pool, snd_port );
+    status = start_sound_device( snd_port );
     if (status != PJ_SUCCESS) {
 	pjmedia_snd_port_destroy(snd_port);
@@ -454,7 +376,17 @@
 PJ_DEF(pj_status_t) pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port)
 {
+    pj_status_t status;
+
     PJ_ASSERT_RETURN(snd_port, PJ_EINVAL);
 
-    return stop_sound_device(snd_port);
+    /* Stop sound port */
+    status = stop_sound_device(snd_port);
+
+    /* Disconnect sound port */
+    pjmedia_snd_port_disconnect(snd_port);
+
+    pj_pool_release(snd_port->pool);
+
+    return status;
 }
 
@@ -552,7 +484,7 @@
 
 	/* Destroy AEC */
-	if (snd_port->ec_state) {
-	    pjmedia_echo_destroy(snd_port->ec_state);
-	    snd_port->ec_state = NULL;
+	if (snd_port->echo_port) {
+	    pjmedia_port_destroy(snd_port->echo_port);
+	    snd_port->echo_port = NULL;
 	}
 
@@ -565,13 +497,9 @@
 	    //	   snd_port->clock_rate;
 	    delay_ms = prm.output_latency_ms;
-	    status = pjmedia_echo_create2(pool, snd_port->clock_rate, 
-					  snd_port->channel_count,
-					  snd_port->samples_per_frame, 
-					  tail_ms, delay_ms,
-					  options, &snd_port->ec_state);
+	    status = pjmedia_echo_port_create(pool, snd_port->port,
+					      tail_ms, delay_ms,
+					      options, &snd_port->echo_port);
 	    if (status != PJ_SUCCESS)
-		snd_port->ec_state = NULL;
-	    else
-		snd_port->ec_suspended = PJ_FALSE;
+		snd_port->echo_port = NULL;
 	} else {
 	    PJ_LOG(4,(THIS_FILE, "Echo canceller is now disabled in the "
@@ -623,5 +551,5 @@
     } else {
 	/* We use software EC */
-	*p_length =  snd_port->ec_state ? snd_port->ec_tail_len : 0;
+	*p_length =  snd_port->echo_port ? snd_port->ec_tail_len : 0;
     }
     return PJ_SUCCESS;
@@ -636,4 +564,7 @@
 {
     pjmedia_port_info *pinfo;
+    pjmedia_aud_param *param;
+    pjmedia_sync_param sync_param;
+    pj_status_t status;
 
     PJ_ASSERT_RETURN(snd_port && port, PJ_EINVAL);
@@ -657,4 +588,39 @@
     /* Port is okay. */
     snd_port->port = port;
+
+    /* Create software EC if parameter specifies EC but device 
+     * doesn't support EC. Only do this if the format is PCM!
+     */
+    param = &snd_port->aud_param;
+    if ((param->flags & PJMEDIA_AUD_DEV_CAP_EC) &&
+	(snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)==0 &&
+	param->ext_fmt.id == PJMEDIA_FORMAT_PCM)
+    {
+	if ((param->flags & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) {
+	    param->flags |= PJMEDIA_AUD_DEV_CAP_EC_TAIL;
+	    param->ec_tail_ms = AEC_TAIL;
+	    PJ_LOG(4,(THIS_FILE, "AEC tail is set to default %u ms",
+				 param->ec_tail_ms));
+	}
+	    
+	status = pjmedia_snd_port_set_ec(snd_port, snd_port->pool, 
+					 param->ec_tail_ms, 0);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    /* Create sync port */
+    pj_bzero(&sync_param, sizeof(sync_param));
+    sync_param.options = PJMEDIA_SYNC_DONT_DESTROY_DN;
+    status = pjmedia_sync_port_create(snd_port->pool, 
+				      (snd_port->echo_port?
+				       snd_port->echo_port:snd_port->port),
+				      &sync_param, &snd_port->sync_port);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Update down port of sound port */
+    snd_port->dn_port = snd_port->sync_port;
+
     return PJ_SUCCESS;
 }
@@ -679,7 +645,22 @@
 
     snd_port->port = NULL;
-
-    return PJ_SUCCESS;
-}
-
-
+    snd_port->dn_port = NULL;
+
+    /* Destroy sync port */
+    if (snd_port->sync_port) {
+	pjmedia_port_destroy(snd_port->sync_port);
+	snd_port->sync_port = NULL;
+    }
+
+    /* Destroy EC port */
+    if (snd_port->echo_port) {
+	pjmedia_port_destroy(snd_port->echo_port);
+	snd_port->echo_port = NULL;
+	snd_port->ec_tail_len = 0;
+	snd_port->ec_options = 0;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
