Index: pjproject/trunk/pjmedia/src/pjmedia/conference.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 1839)
+++ pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 1898)
@@ -25,4 +25,5 @@
 #include <pjmedia/silencedet.h>
 #include <pjmedia/sound_port.h>
+#include <pjmedia/stereo.h>
 #include <pjmedia/stream.h>
 #include <pj/array.h>
@@ -113,9 +114,9 @@
     SLOT_TYPE		*listener_slots;/**< Array of listeners.	    */
     unsigned		 transmitter_cnt;/**<Number of transmitters.	    */
-    pjmedia_silence_det	*vad;		/**< VAD for this port.		    */
 
     /* Shortcut for port info. */
     unsigned		 clock_rate;	/**< Port's clock rate.		    */
     unsigned		 samples_per_frame; /**< Port's samples per frame.  */
+    unsigned		 channel_count;	/**< Port's channel count.	    */
 
     /* Calculated signal levels: */
@@ -286,23 +287,11 @@
 	conf_port->clock_rate = port->info.clock_rate;
 	conf_port->samples_per_frame = port->info.samples_per_frame;
+	conf_port->channel_count = port->info.channel_count;
     } else {
 	conf_port->port = NULL;
 	conf_port->clock_rate = conf->clock_rate;
 	conf_port->samples_per_frame = conf->samples_per_frame;
-    }
-
-    /* Create and init vad. */
-    status = pjmedia_silence_det_create( pool, 
-					 conf_port->clock_rate,
-					 conf_port->samples_per_frame,
-					 &conf_port->vad);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    /* Set fixed */
-    pjmedia_silence_det_set_fixed(conf_port->vad, 2);
-
-    /* Set VAD name */
-    pjmedia_silence_det_set_name(conf_port->vad, conf_port->name.ptr);
+	conf_port->channel_count = conf->channel_count;
+    }
 
     /* If port's clock rate is different than conference's clock rate,
@@ -347,14 +336,46 @@
     /*
      * Initialize rx and tx buffer, only when port's samples per frame or 
-     * port's clock rate is different then the conference bridge settings.
+     * port's clock rate or channel number is different then the conference
+     * bridge settings.
      */
     if (conf_port->clock_rate != conf->clock_rate ||
+	conf_port->channel_count != conf->channel_count ||
 	conf_port->samples_per_frame != conf->samples_per_frame)
     {
+	unsigned port_ptime, conf_ptime, buff_ptime;
+
+	port_ptime = conf_port->samples_per_frame / conf_port->channel_count *
+	    1000 / conf_port->clock_rate;
+	conf_ptime = conf->samples_per_frame / conf->channel_count *
+	    1000 / conf->clock_rate;
+
+	/* Calculate the size (in ptime) for the port buffer according to
+	 * this formula:
+	 *   - if either ptime is an exact multiple of the other, then use
+	 *     the larger ptime (e.g. 20ms and 40ms, use 40ms).
+	 *   - if not, then the ptime is sum of both ptimes (e.g. 20ms
+	 *     and 30ms, use 50ms)
+	 */
+	if (port_ptime > conf_ptime) {
+	    buff_ptime = conf_ptime * (port_ptime / conf_ptime);
+	    if (port_ptime % conf_ptime)
+		buff_ptime += conf_ptime;
+	} else {
+	    buff_ptime = port_ptime * (conf_ptime / port_ptime);
+	    if (conf_ptime % port_ptime)
+		buff_ptime += port_ptime;
+	}
+
 	/* Create RX buffer. */
-	conf_port->rx_buf_cap = (unsigned)(conf_port->samples_per_frame +
-					   conf->samples_per_frame * 
-					   conf_port->clock_rate * 1.0 /
-					   conf->clock_rate);
+	//conf_port->rx_buf_cap = (unsigned)(conf_port->samples_per_frame +
+	//				   conf->samples_per_frame * 
+	//				   conf_port->clock_rate * 1.0 /
+	//				   conf->clock_rate);
+	conf_port->rx_buf_cap = conf_port->clock_rate * buff_ptime / 1000;
+	if (conf_port->channel_count > conf->channel_count)
+	    conf_port->rx_buf_cap *= conf_port->channel_count;
+	else
+	    conf_port->rx_buf_cap *= conf->channel_count;
+
 	conf_port->rx_buf_count = 0;
 	conf_port->rx_buf = (pj_int16_t*)
@@ -699,8 +720,11 @@
 	port_name = &strm_port->info.name;
 
-    /* For this version of PJMEDIA, port MUST have the same number of
-     * PCM channels.
-     */
-    if (strm_port->info.channel_count != conf->channel_count) {
+    /* For this version of PJMEDIA, channel(s) number MUST be:
+     * - same between port & conference bridge.
+     * - monochannel on port or conference bridge.
+     */
+    if (strm_port->info.channel_count != conf->channel_count && 
+	(strm_port->info.channel_count != 1 && conf->channel_count != 1)) 
+    {
 	pj_assert(!"Number of channels mismatch");
 	return PJMEDIA_ENCCHANNEL;
@@ -767,8 +791,11 @@
     PJ_ASSERT_RETURN(conf && pool, PJ_EINVAL);
 
-    /* For this version of PJMEDIA, port MUST have the same number of
-     * PCM channels.
-     */
-    if (channel_count != conf->channel_count) {
+    /* For this version of PJMEDIA, channel(s) number MUST be:
+     * - same between port & conference bridge.
+     * - monochannel on port or conference bridge.
+     */
+    if (channel_count != conf->channel_count && 
+	(channel_count != 1 && conf->channel_count != 1)) 
+    {
 	pj_assert(!"Number of channels mismatch");
 	return PJMEDIA_ENCCHANNEL;
@@ -1141,5 +1168,5 @@
     info->listener_slots = conf_port->listener_slots;
     info->clock_rate = conf_port->clock_rate;
-    info->channel_count = conf->channel_count;
+    info->channel_count = conf_port->channel_count;
     info->samples_per_frame = conf_port->samples_per_frame;
     info->bits_per_sample = conf->bits_per_sample;
@@ -1275,6 +1302,8 @@
 		       count));
 
-    /* If port's samples per frame and sampling rate matches conference
-     * bridge's settings, get the frame directly from the port.
+    /* 
+     * If port's samples per frame and sampling rate and channel count
+     * matche conference bridge's settings, get the frame directly from
+     * the port.
      */
     if (cport->rx_buf_cap == 0) {
@@ -1296,4 +1325,5 @@
 
     } else {
+	unsigned samples_req;
 
 	/* Initialize frame type */
@@ -1307,8 +1337,12 @@
 	/*
 	 * If we don't have enough samples in rx_buf, read from the port 
-	 * first. Remember that rx_buf may be in different clock rate!
+	 * first. Remember that rx_buf may be in different clock rate and
+	 * channel count!
 	 */
-	while (cport->rx_buf_count < count * 1.0 *
-		cport->clock_rate / conf->clock_rate) {
+
+	samples_req = (unsigned) (count * 1.0 * 
+		      cport->clock_rate / conf->clock_rate);
+
+	while (cport->rx_buf_count < samples_req) {
 
 	    pjmedia_frame f;
@@ -1337,5 +1371,24 @@
 	    }
 
-	    cport->rx_buf_count += cport->samples_per_frame;
+	    /* Adjust channels */
+	    if (cport->channel_count != conf->channel_count) {
+		if (cport->channel_count == 1) {
+		    pjmedia_convert_channel_1ton(f.buf, f.buf,
+						 conf->channel_count, 
+						 cport->samples_per_frame,
+						 0);
+		    cport->rx_buf_count += (cport->samples_per_frame * 
+					    conf->channel_count);
+		} else { /* conf->channel_count == 1 */
+		    pjmedia_convert_channel_nto1(f.buf, f.buf,
+						 cport->channel_count, 
+						 cport->samples_per_frame, 
+						 PJMEDIA_STEREO_MIX, 0);
+		    cport->rx_buf_count += (cport->samples_per_frame / 
+					    cport->channel_count);
+		}
+	    } else {
+		cport->rx_buf_count += cport->samples_per_frame;
+	    }
 
 	    TRACE_((THIS_FILE, "  rx buffer size is now %d",
@@ -1362,5 +1415,5 @@
 	    cport->rx_buf_count -= src_count;
 	    if (cport->rx_buf_count) {
-		pjmedia_copy_samples(cport->rx_buf, cport->rx_buf+src_count,
+		pjmedia_move_samples(cport->rx_buf, cport->rx_buf+src_count,
 				     cport->rx_buf_count);
 	    }
@@ -1374,5 +1427,5 @@
 	    cport->rx_buf_count -= count;
 	    if (cport->rx_buf_count) {
-		pjmedia_copy_samples(cport->rx_buf, cport->rx_buf+count,
+		pjmedia_move_samples(cport->rx_buf, cport->rx_buf+count,
 				     cport->rx_buf_count);
 	    }
@@ -1396,4 +1449,5 @@
     pj_int32_t adj_level;
     pj_int32_t tx_level;
+    unsigned dst_count;
 
     *frm_type = PJMEDIA_FRAME_TYPE_AUDIO;
@@ -1413,5 +1467,6 @@
 	/* Add sample counts to heart-beat samples */
 	cport->tx_heart_beat += conf->samples_per_frame * cport->clock_rate /
-				conf->clock_rate;
+				conf->clock_rate * 
+				cport->channel_count / conf->channel_count;
 
 	/* Set frame timestamp */
@@ -1477,6 +1532,6 @@
     tx_level = 0;
 
-    for (j=0; j<conf->samples_per_frame; ++j) {
-	if (adj_level != NORMAL_LEVEL) {
+    if (adj_level != NORMAL_LEVEL) {
+	for (j=0; j<conf->samples_per_frame; ++j) {
 	    pj_int32_t itemp = cport->mix_buf[j];
 
@@ -1491,9 +1546,12 @@
 	    /* Put back in the buffer. */
 	    buf[j] = (pj_int16_t) itemp;
-	} else {
+
+	    tx_level += (buf[j]>=0? buf[j] : -buf[j]);
+	}
+    } else {
+	for (j=0; j<conf->samples_per_frame; ++j) {
 	    buf[j] = (pj_int16_t) cport->mix_buf[j];
+	    tx_level += (buf[j]>=0? buf[j] : -buf[j]);
 	}
-
-	tx_level += (buf[j]>0? buf[j] : -buf[j]);
     }
 
@@ -1505,9 +1563,11 @@
     cport->tx_level = tx_level;
 
-    /* If port has the same clock_rate and samples_per_frame settings as
-     * the conference bridge, transmit the frame as is.
+    /* If port has the same clock_rate and samples_per_frame and 
+     * number of channels as the conference bridge, transmit the 
+     * frame as is.
      */
     if (cport->clock_rate == conf->clock_rate &&
-	cport->samples_per_frame == conf->samples_per_frame)
+	cport->samples_per_frame == conf->samples_per_frame &&
+	cport->channel_count == conf->channel_count)
     {
 	if (cport->port != NULL) {
@@ -1533,14 +1593,8 @@
     /* If it has different clock_rate, must resample. */
     if (cport->clock_rate != conf->clock_rate) {
-
-	unsigned dst_count;
-
 	pjmedia_resample_run( cport->tx_resample, buf, 
 			      cport->tx_buf + cport->tx_buf_count );
-
 	dst_count = (unsigned)(conf->samples_per_frame * 1.0 *
 			       cport->clock_rate / conf->clock_rate);
-	cport->tx_buf_count += dst_count;
-
     } else {
 	/* Same clock rate.
@@ -1549,6 +1603,26 @@
 	pjmedia_copy_samples( cport->tx_buf + cport->tx_buf_count,
 			      buf, conf->samples_per_frame );
-	cport->tx_buf_count += conf->samples_per_frame;
-    }
+	dst_count = conf->samples_per_frame;
+    }
+
+    /* Adjust channels */
+    if (cport->channel_count != conf->channel_count) {
+	pj_int16_t *tx_buf = cport->tx_buf + cport->tx_buf_count;
+	if (conf->channel_count == 1) {
+	    pjmedia_convert_channel_1ton(tx_buf, tx_buf,
+					 cport->channel_count, 
+					 dst_count, 0);
+	    dst_count *= cport->channel_count;
+	} else { /* cport->channel_count == 1 */
+	    pjmedia_convert_channel_nto1(tx_buf, tx_buf,
+					 conf->channel_count, 
+					 dst_count, PJMEDIA_STEREO_MIX, 0);
+	    dst_count /= conf->channel_count;
+	}
+    }
+
+    cport->tx_buf_count += dst_count;
+
+    pj_assert(cport->tx_buf_count <= cport->tx_buf_cap);
 
     /* Transmit while we have enough frame in the tx_buf. */
@@ -1590,5 +1664,5 @@
 	cport->tx_buf_count -= cport->samples_per_frame;
 	if (cport->tx_buf_count) {
-	    pjmedia_copy_samples(cport->tx_buf, 
+	    pjmedia_move_samples(cport->tx_buf, 
 				 cport->tx_buf + cport->samples_per_frame,
 				 cport->tx_buf_count);
@@ -1716,6 +1790,6 @@
 	 * and calculate the average level at the same time.
 	 */
-	for (j=0; j<conf->samples_per_frame; ++j) {
-	    if (conf_port->rx_adj_level != NORMAL_LEVEL) {
+	if (conf_port->rx_adj_level != NORMAL_LEVEL) {
+	    for (j=0; j<conf->samples_per_frame; ++j) {
 		/* For the level adjustment, we need to store the sample to
 		 * a temporary 32bit integer value to avoid overflowing the
@@ -1737,8 +1811,11 @@
 
 		p_in[j] = (pj_int16_t) itemp;
+		level += (p_in[j]>=0? p_in[j] : -p_in[j]);
 	    }
-
-	    level += (p_in[j]>0? p_in[j] : -p_in[j]);
-        }
+	} else {
+	    for (j=0; j<conf->samples_per_frame; ++j) {
+		level += (p_in[j]>=0? p_in[j] : -p_in[j]);
+	    }
+	}
 
 	level /= conf->samples_per_frame;
