Index: pjproject/trunk/pjmedia/include/pjmedia/conference.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/conference.h	(revision 5791)
+++ pjproject/trunk/pjmedia/include/pjmedia/conference.h	(revision 5792)
@@ -72,4 +72,6 @@
     unsigned		listener_cnt;	    /**< Number of listeners.	    */
     unsigned	       *listener_slots;	    /**< Array of listeners.	    */
+    unsigned	       *listener_adj_level; /**< Array of listeners' level
+    						 adjustment		    */
     unsigned		transmitter_cnt;    /**< Number of transmitter.	    */
     unsigned		clock_rate;	    /**< Clock rate of the port.    */
@@ -296,14 +298,33 @@
 
 /**
- * Enable unidirectional audio from the specified source slot to the
- * specified sink slot.
+ * Enable unidirectional audio from the specified source slot to the specified
+ * sink slot.
+ * Application may adjust the level to make signal transmitted from the source
+ * slot to the sink slot either louder or more quiet. The level adjustment is
+ * calculated with this formula:
+ * <b><tt>output = input * (adj_level+128) / 128</tt></b>. Using this, zero
+ * indicates no adjustment, the value -128 will mute the signal, and the value
+ * of +128 will make the signal 100% louder, +256 will make it 200% louder,
+ * etc.
+ *
+ * The level adjustment will apply to a specific connection only (i.e. only
+ * for the signal from the source to the sink), as compared to
+ * pjmedia_conf_adjust_tx_level()/pjmedia_conf_adjust_rx_level() which
+ * applies to all signals from/to that port. The signal adjustment
+ * will be cumulative, in this following order:
+ * signal from the source will be adjusted with the level specified
+ * in pjmedia_conf_adjust_rx_level(), then with the level specified
+ * via this API, and finally with the level specified to the sink's
+ * pjmedia_conf_adjust_tx_level(). 
  *
  * @param conf		The conference bridge.
  * @param src_slot	Source slot.
  * @param sink_slot	Sink slot.
- * @param level		This argument is reserved for future improvements
- *			where it is possible to adjust the level of signal
- *			transmitted in a specific connection. For now,
- *			this argument MUST be zero.
+ * @param adj_level	Adjustment level, which must be greater than or equal
+ *			to -128. A value of zero means there is no level
+ *			adjustment to be made, the value -128 will mute the
+ *			signal, and the value of +128 will make the signal
+ *			100% louder, +256 will make it 200% louder, etc.
+ *			See the function description for the formula.
  *
  * @return		PJ_SUCCES on success.
@@ -312,5 +333,5 @@
 						unsigned src_slot,
 						unsigned sink_slot,
-						int level );
+						int adj_level );
 
 
@@ -497,4 +518,38 @@
 
 
+/**
+ * Adjust the level of signal to be transmitted from the source slot to the
+ * sink slot.
+ * Application may adjust the level to make signal transmitted from the source
+ * slot to the sink slot either louder or more quiet. The level adjustment is
+ * calculated with this formula:
+ * <b><tt>output = input * (adj_level+128) / 128</tt></b>. Using this, zero
+ * indicates no adjustment, the value -128 will mute the signal, and the value
+ * of +128 will make the signal 100% louder, +256 will make it 200% louder,
+ * etc.
+ *
+ * The level adjustment value will stay with the connection until the
+ * connection is removed or new adjustment value is set. The current level
+ * adjustment value is reported in the media port info when the
+ * #pjmedia_conf_get_port_info() function is called.
+ *
+ * @param conf		The conference bridge.
+ * @param src_slot	Source slot.
+ * @param sink_slot	Sink slot.
+ * @param adj_level	Adjustment level, which must be greater than or equal
+ *			to -128. A value of zero means there is no level
+ *			adjustment to be made, the value -128 will mute the 
+ *			signal, and the value of +128 will make the signal 
+ *			100% louder, +256 will make it 200% louder, etc. 
+ *			See the function description for the formula.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_conf_adjust_conn_level( pjmedia_conf *conf,
+						     unsigned src_slot,
+						     unsigned sink_slot,
+						     int adj_level );
+
+
 
 PJ_END_DECL
Index: pjproject/trunk/pjmedia/src/pjmedia/conference.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 5791)
+++ pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 5792)
@@ -118,4 +118,7 @@
     unsigned		 listener_cnt;	/**< Number of listeners.	    */
     SLOT_TYPE		*listener_slots;/**< Array of listeners.	    */
+    unsigned		*listener_adj_level;
+    					/**< Array of listeners' level
+    					     adjustment.  		    */
     unsigned		 transmitter_cnt;/**<Number of transmitters.	    */
 
@@ -134,4 +137,5 @@
     unsigned		 tx_adj_level;	/**< Adjustment for TX.		    */
     unsigned		 rx_adj_level;	/**< Adjustment for RX.		    */
+    pj_int16_t		*adj_level_buf;	/**< The adjustment buffer.	    */
 
     /* Resample, for converting clock rate, if they're different. */
@@ -282,8 +286,12 @@
 
     /* Create transmit flag array */
-    conf_port->listener_slots = (SLOT_TYPE*)
-				pj_pool_zalloc(pool, 
-					  conf->max_ports * sizeof(SLOT_TYPE));
+    conf_port->listener_slots = (SLOT_TYPE*) pj_pool_zalloc(pool, 
+				          conf->max_ports * sizeof(SLOT_TYPE));
     PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM);
+
+    /* Create adjustment level array */
+    conf_port->listener_adj_level = (unsigned *) pj_pool_zalloc(pool, 
+				       conf->max_ports * sizeof(unsigned));
+    PJ_ASSERT_RETURN(conf_port->listener_adj_level, PJ_ENOMEM);
 
     /* Save some port's infos, for convenience. */
@@ -302,4 +310,8 @@
 	conf_port->channel_count = conf->channel_count;
     }
+
+    /* Create adjustment level buffer. */
+    conf_port->adj_level_buf = (pj_int16_t*) pj_pool_zalloc(pool, 
+			       conf->samples_per_frame * sizeof(pj_int16_t));
 
     /* If port's clock rate is different than conference's clock rate,
@@ -949,5 +961,5 @@
 					       unsigned src_slot,
 					       unsigned sink_slot,
-					       int level )
+					       int adj_level )
 {
     struct conf_port *src_port, *dst_port;
@@ -959,6 +971,9 @@
 		     sink_slot<conf->max_ports, PJ_EINVAL);
 
-    /* For now, level MUST be zero. */
-    PJ_ASSERT_RETURN(level == 0, PJ_EINVAL);
+    /* Value must be from -128 to +127 */
+    /* Disabled, you can put more than +127, at your own risk:
+     PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
+     */
+    PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
 
     pj_mutex_lock(conf->mutex);
@@ -980,4 +995,7 @@
     if (i == src_port->listener_cnt) {
 	src_port->listener_slots[src_port->listener_cnt] = sink_slot;
+	/* Set normalized adjustment level. */
+	src_port->listener_adj_level[src_port->listener_cnt] = adj_level +
+						      	       NORMAL_LEVEL;
 	++conf->connect_cnt;
 	++src_port->listener_cnt;
@@ -1387,4 +1405,53 @@
     pj_mutex_unlock(conf->mutex);
 
+    return PJ_SUCCESS;
+}
+
+/*
+ * Adjust level of individual connection.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_adjust_conn_level( pjmedia_conf *conf,
+						    unsigned src_slot,
+						    unsigned sink_slot,
+						    int adj_level )
+{
+    struct conf_port *src_port, *dst_port;
+    unsigned i;
+
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && src_slot<conf->max_ports &&
+		     sink_slot<conf->max_ports, PJ_EINVAL);
+
+    /* Value must be from -128 to +127 */
+    /* Disabled, you can put more than +127, at your own risk:
+     PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
+     */
+    PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL);
+
+    pj_mutex_lock(conf->mutex);
+
+    /* Ports must be valid. */
+    src_port = conf->ports[src_slot];
+    dst_port = conf->ports[sink_slot];
+    if (!src_port || !dst_port) {
+	pj_mutex_unlock(conf->mutex);
+	return PJ_EINVAL;
+    }
+
+    /* Check if connection has been made */
+    for (i=0; i<src_port->listener_cnt; ++i) {
+	if (src_port->listener_slots[i] == sink_slot)
+	    break;
+    }
+
+    if (i == src_port->listener_cnt) {
+	/* connection hasn't been made */
+	pj_mutex_unlock(conf->mutex);
+	return PJ_EINVAL;
+    } 
+    /* Set normalized adjustment level. */
+    src_port->listener_adj_level[i] = adj_level + NORMAL_LEVEL;
+
+    pj_mutex_unlock(conf->mutex);
     return PJ_SUCCESS;
 }
@@ -1953,5 +2020,6 @@
 	{
 	    struct conf_port *listener;
-	    pj_int32_t *mix_buf;
+	    pj_int32_t *mix_buf;	    
+	    pj_int16_t *p_in_conn_leveled;
 
 	    listener = conf->ports[conf_port->listener_slots[cj]];
@@ -1962,4 +2030,36 @@
 
 	    mix_buf = listener->mix_buf;
+
+	    /* apply connection level, if not normal */
+	    if (conf_port->listener_adj_level[cj] != NORMAL_LEVEL) {
+		unsigned k = 0;
+		for (; k < conf->samples_per_frame; ++k) {
+		    /* For the level adjustment, we need to store the sample to
+		     * a temporary 32bit integer value to avoid overflowing the
+		     * 16bit sample storage.
+		     */
+		    pj_int32_t itemp;
+
+		    itemp = p_in[k];
+		    /*itemp = itemp * adj / NORMAL_LEVEL;*/
+		    /* bad code (signed/unsigned badness):
+		     *  itemp = (itemp * conf_port->listsener_adj_level) >> 7;
+		     */
+		    itemp *= conf_port->listener_adj_level[cj];
+		    itemp >>= 7;
+
+		    /* Clip the signal if it's too loud */
+		    if (itemp > MAX_LEVEL) itemp = MAX_LEVEL;
+		    else if (itemp < MIN_LEVEL) itemp = MIN_LEVEL;
+
+		    conf_port->adj_level_buf[k] = (pj_int16_t)itemp;
+		}
+
+		/* take the leveled frame */
+		p_in_conn_leveled = conf_port->adj_level_buf;
+	    } else {
+		/* take the frame as-is */
+		p_in_conn_leveled = p_in;
+	    }
 
 	    if (listener->transmitter_cnt > 1) {
@@ -1973,5 +2073,5 @@
 
 		for (k = 0; k < samples_per_frame; ++k) {
-		    mix_buf[k] += p_in[k];
+		    mix_buf[k] += p_in_conn_leveled[k];
 		    if (mix_buf[k] < mix_buf_min)
 			mix_buf_min = mix_buf[k];
@@ -2000,5 +2100,5 @@
 
 		for (k = 0; k < samples_per_frame; ++k) {
-		    mix_buf[k] = p_in[k];
+		    mix_buf[k] = p_in_conn_leveled[k];
 		}
 	    }
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 5791)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 5792)
@@ -6614,4 +6614,31 @@
 
 /**
+ * This structure specifies the parameters for conference ports connection.
+ * Use pjsua_conf_connect_param_default() to initialize this structure with
+ * default values.
+ */
+typedef struct pjsua_conf_connect_param
+{
+    /*
+     * Signal level adjustment from the source to the sink to make it
+     * louder or quieter. Value 1.0 means no level adjustment,
+     * while value 0 means to mute the port.
+     *
+     * Default: 1.0
+     */
+    float		level;
+
+} pjsua_conf_connect_param;
+
+
+/**
+ * Initialize pjsua_conf_connect_param with default values.
+ *
+ * @param prm		The parameter.
+ */
+PJ_DECL(void) pjsua_conf_connect_param_default(pjsua_conf_connect_param *prm);
+
+
+/**
  * Get maxinum number of conference ports.
  *
@@ -6704,4 +6731,38 @@
 PJ_DECL(pj_status_t) pjsua_conf_connect(pjsua_conf_port_id source,
 					pjsua_conf_port_id sink);
+
+/**
+ * Establish unidirectional media flow from source to sink. One source
+ * may transmit to multiple destinations/sink. And if multiple
+ * sources are transmitting to the same sink, the media will be mixed
+ * together. Source and sink may refer to the same ID, effectively
+ * looping the media.
+ *
+ * Signal level from the source to the sink can be adjusted by making
+ * it louder or quieter via the parameter param. The level adjustment
+ * will apply to a specific connection only (i.e. only for the signal
+ * from the source to the sink), as compared to
+ * pjsua_conf_adjust_tx_level()/pjsua_conf_adjust_rx_level() which
+ * applies to all signals from/to that port. The signal adjustment
+ * will be cumulative, in this following order:
+ * signal from the source will be adjusted with the level specified
+ * in pjsua_conf_adjust_rx_level(), then with the level specified
+ * via this API, and finally with the level specified to the sink's
+ * pjsua_conf_adjust_tx_level().
+ *
+ * If bidirectional media flow is desired, application needs to call
+ * this function twice, with the second one having the arguments
+ * reversed.
+ *
+ * @param source	Port ID of the source media/transmitter.
+ * @param sink		Port ID of the destination media/received.
+ * @param prm		Conference port connection param. If set to
+ *			NULL, default values will be used.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsua_conf_connect2(pjsua_conf_port_id source,
+					 pjsua_conf_port_id sink,
+					 const pjsua_conf_connect_param *prm);
 
 
Index: pjproject/trunk/pjsip/include/pjsua2/media.hpp
===================================================================
--- pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 5791)
+++ pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 5792)
@@ -193,4 +193,21 @@
 };
 
+struct AudioMediaTransmitParam
+{
+    /**
+     * Signal level adjustment. Value 1.0 means no level adjustment,
+     * while value 0 means to mute the port.
+     *
+     * Default: 1.0
+     */
+    float		level;
+
+public:
+    /**
+     * Default constructor
+     */
+    AudioMediaTransmitParam();
+};
+
 /**
  * Audio Media.
@@ -228,4 +245,33 @@
      */
     void startTransmit(const AudioMedia &sink) const throw(Error);
+
+    /**
+     * Establish unidirectional media flow to sink. This media port
+     * will act as a source, and it may transmit to multiple destinations/sink.
+     * And if multiple sources are transmitting to the same sink, the media
+     * will be mixed together. Source and sink may refer to the same Media,
+     * effectively looping the media.
+     *
+     * Signal level from this source to the sink can be adjusted by making
+     * it louder or quieter via the parameter param. The level adjustment
+     * will apply to a specific connection only (i.e. only for signal
+     * from this source to the sink), as compared to
+     * adjustTxLevel()/adjustRxLevel() which applies to all signals from/to
+     * this media port. The signal adjustment
+     * will be cumulative, in this following order:
+     * signal from this source will be adjusted with the level specified
+     * in adjustTxLevel(), then with the level specified via this API,
+     * and finally with the level specified to the sink's adjustRxLevel().
+     *
+     * If bidirectional media flow is desired, application needs to call
+     * this method twice, with the second one called from the opposite source
+     * media.
+     *
+     * @param sink		The destination Media.
+     * @param param		The parameter.
+     */
+    void startTransmit2(const AudioMedia &sink, 
+			const AudioMediaTransmitParam &param) const
+         throw(Error);
 
     /**
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c	(revision 5791)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c	(revision 5792)
@@ -715,4 +715,10 @@
 }
 
+PJ_DEF(void) pjsua_conf_connect_param_default(pjsua_conf_connect_param *prm)
+{
+    pj_bzero(prm, sizeof(*prm));
+    prm->level = 1.0;
+}
+
 /*
  * Get maxinum number of conference ports.
@@ -825,4 +831,18 @@
 PJ_DEF(pj_status_t) pjsua_conf_connect( pjsua_conf_port_id source,
 					pjsua_conf_port_id sink)
+{
+    pjsua_conf_connect_param prm;
+
+    pjsua_conf_connect_param_default(&prm);
+    return pjsua_conf_connect2(source, sink, &prm);
+}
+					
+/*
+ * Establish unidirectional media flow from souce to sink, with signal
+ * level adjustment.
+ */
+PJ_DEF(pj_status_t) pjsua_conf_connect2( pjsua_conf_port_id source,
+					 pjsua_conf_port_id sink,
+					 const pjsua_conf_connect_param *prm)
 {
     pj_status_t status = PJ_SUCCESS;
@@ -958,5 +978,12 @@
 
     if (status == PJ_SUCCESS) {
-	status = pjmedia_conf_connect_port(pjsua_var.mconf, source, sink, 0);
+    	pjsua_conf_connect_param cc_param;
+    	
+    	if (!prm)
+    	    pjsua_conf_connect_param_default(&cc_param);
+    	else
+    	    pj_memcpy(&cc_param, prm, sizeof(cc_param));
+	status = pjmedia_conf_connect_port(pjsua_var.mconf, source, sink, 
+					   (int)((cc_param.level-1) * 128));
     }
 
Index: pjproject/trunk/pjsip/src/pjsua2/media.cpp
===================================================================
--- pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 5791)
+++ pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 5792)
@@ -118,4 +118,9 @@
 
 ///////////////////////////////////////////////////////////////////////////////
+AudioMediaTransmitParam::AudioMediaTransmitParam()
+: level(1.0)
+{
+}
+
 AudioMedia::AudioMedia() 
 : Media(PJMEDIA_TYPE_AUDIO), id(PJSUA_INVALID_ID), mediaPool(NULL)
@@ -197,4 +202,15 @@
 {
     PJSUA2_CHECK_EXPR( pjsua_conf_connect(id, sink.id) );
+}
+
+void AudioMedia::startTransmit2(const AudioMedia &sink,
+				const AudioMediaTransmitParam &param) const
+     throw(Error)
+{
+    pjsua_conf_connect_param pj_param;
+    
+    pjsua_conf_connect_param_default(&pj_param);
+    pj_param.level = param.level;
+    PJSUA2_CHECK_EXPR( pjsua_conf_connect2(id, sink.id, &pj_param) );
 }
 
