Index: pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 3553)
+++ pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 3664)
@@ -23,4 +23,5 @@
 #include <pjmedia/rtcp.h>
 #include <pjmedia/jbuf.h>
+#include <pjmedia/stream_common.h>
 #include <pj/array.h>
 #include <pj/assert.h>
@@ -65,4 +66,11 @@
 #endif
 
+#ifndef PJMEDIA_STREAM_SIZE
+#   define PJMEDIA_STREAM_SIZE	1000
+#endif
+
+#ifndef PJMEDIA_STREAM_INC
+#   define PJMEDIA_STREAM_INC	1000
+#endif
 
 
@@ -78,4 +86,5 @@
     unsigned		    out_pkt_size;   /**< Size of output buffer.	    */
     void		   *out_pkt;	    /**< Output buffer.		    */
+    unsigned                out_pkt_len;    /**< Length of data in buffer.  */
     pjmedia_rtp_session	    rtp;	    /**< RTP session.		    */
 };
@@ -99,8 +108,10 @@
     pjmedia_endpt	    *endpt;	    /**< Media endpoint.	    */
     pjmedia_codec_mgr	    *codec_mgr;	    /**< Codec manager instance.    */
-
+    pjmedia_stream_info	     si;	    /**< Creation parameter.        */
     pjmedia_port	     port;	    /**< Port interface.	    */
     pjmedia_channel	    *enc;	    /**< Encoding channel.	    */
     pjmedia_channel	    *dec;	    /**< Decoding channel.	    */
+
+    pj_pool_t		    *own_pool;	    /**< Only created if not given  */
 
     pjmedia_dir		     dir;	    /**< Stream direction.	    */
@@ -178,6 +189,4 @@
 						 received according to 
 						 'erroneous' definition	    */
-    pj_uint32_t		     rtp_rx_last_ts;/**< Last received RTP timestamp
-						 for timestamp checking	    */
     unsigned		     rtp_rx_last_cnt;/**< Nb of frames in last pkt  */
     unsigned		     rtp_rx_check_cnt;
@@ -209,4 +218,6 @@
     char		   *trace_jb_buf;	    /**< Jitter tracing buffer.	    */
 #endif
+
+    pj_uint32_t		     rtp_rx_last_ts;        /**< Last received RTP timestamp*/
 };
 
@@ -472,5 +483,5 @@
     pj_mutex_lock( stream->jb_mutex );
 
-    samples_required = stream->port.info.samples_per_frame;
+    samples_required = PJMEDIA_PIA_SPF(&stream->port.info);
     samples_per_frame = stream->codec_param.info.frm_ptime *
 			stream->codec_param.info.clock_rate *
@@ -505,7 +516,7 @@
 		frame_out.buf = p_out_samp + samples_count;
 		frame_out.size = frame->size - samples_count*2;
-		status = (*stream->codec->op->recover)(stream->codec,
-						       frame_out.size,
-						       &frame_out);
+		status = pjmedia_codec_recover(stream->codec,
+					       frame_out.size,
+					       &frame_out);
 
 		++stream->plc_cnt;
@@ -554,7 +565,7 @@
 			frame_out.buf = p_out_samp + samples_count;
 			frame_out.size = frame->size - samples_count*2;
-			status = (*stream->codec->op->recover)(stream->codec,
-							       frame_out.size,
-							       &frame_out);
+			status = pjmedia_codec_recover(stream->codec,
+						       frame_out.size,
+						       &frame_out);
 			if (status != PJ_SUCCESS)
 			    break;
@@ -609,7 +620,7 @@
 		    frame_out.buf = p_out_samp + samples_count;
 		    frame_out.size = frame->size - samples_count*2;
-		    status = (*stream->codec->op->recover)(stream->codec,
-							   frame_out.size,
-							   &frame_out);
+		    status = pjmedia_codec_recover(stream->codec,
+						   frame_out.size,
+						   &frame_out);
 		    if (status != PJ_SUCCESS)
 			break;
@@ -660,6 +671,6 @@
 	    frame_out.buf = p_out_samp + samples_count;
 	    frame_out.size = frame->size - samples_count*BYTES_PER_SAMPLE;
-	    status = stream->codec->op->decode( stream->codec, &frame_in,
-						frame_out.size, &frame_out);
+	    status = pjmedia_codec_decode( stream->codec, &frame_in,
+					   frame_out.size, &frame_out);
 	    if (status != 0) {
 		LOGERR_((port->info.name.ptr, "codec decode() error", 
@@ -726,5 +737,5 @@
      */
 
-    samples_required = stream->port.info.samples_per_frame;
+    samples_required = PJMEDIA_PIA_SPF(&stream->port.info);
     samples_per_frame = stream->codec_param.info.frm_ptime *
 			stream->codec_param.info.clock_rate *
@@ -764,6 +775,6 @@
 	    frame_in.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
-	    status = stream->codec->op->decode( stream->codec, &frame_in,
-						0, frame);
+	    status = pjmedia_codec_decode( stream->codec, &frame_in,
+					   0, frame);
 	    if (status != PJ_SUCCESS) {
 		LOGERR_((port->info.name.ptr, "codec decode() error", 
@@ -791,6 +802,5 @@
 	    status = PJ_SUCCESS;
 	    if (stream->codec->op->recover) {
-		status = (*stream->codec->op->recover)(stream->codec,
-						       0, frame);
+		status = pjmedia_codec_recover(stream->codec, 0, frame);
 	    }
 	    
@@ -877,5 +887,5 @@
     }
 
-    digit->duration += stream->port.info.samples_per_frame;
+    digit->duration += PJMEDIA_PIA_SPF(&stream->port.info);
 
     event->event = (pj_uint8_t)digit->event;
@@ -1088,5 +1098,5 @@
     /* How many samples are needed */
     count = stream->codec_param.info.enc_ptime * 
-		stream->port.info.clock_rate / 1000;
+	    PJMEDIA_PIA_SRATE(&stream->port.info) / 1000;
 
     /* See if we have enough samples */
@@ -1111,5 +1121,5 @@
  */
 static pj_status_t put_frame_imp( pjmedia_port *port, 
-				  const pjmedia_frame *frame )
+				  pjmedia_frame *frame )
 {
     pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
@@ -1152,6 +1162,6 @@
 	ts_len = (frame->size >> 1) / stream->codec_param.info.channel_cnt;
     else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED)
-	ts_len = stream->port.info.samples_per_frame / 
-		 stream->port.info.channel_count;
+	ts_len = PJMEDIA_PIA_SPF(&stream->port.info) /
+		 PJMEDIA_PIA_CCNT(&stream->port.info);
     else
 	ts_len = 0;
@@ -1220,5 +1230,5 @@
     } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO &&
 	       frame->buf == NULL &&
-	       stream->port.info.format.id == PJMEDIA_FORMAT_L16 &&
+	       stream->port.info.fmt.id == PJMEDIA_FORMAT_L16 &&
 	       (stream->dir & PJMEDIA_DIR_ENCODING) &&
 	       stream->codec_param.info.frm_ptime *
@@ -1238,8 +1248,8 @@
 	
 	/* Encode! */
-	status = stream->codec->op->encode( stream->codec, &silence_frame,
-					    channel->out_pkt_size - 
-					    sizeof(pjmedia_rtp_hdr),
-					    &frame_out);
+	status = pjmedia_codec_encode( stream->codec, &silence_frame,
+				       channel->out_pkt_size - 
+				       sizeof(pjmedia_rtp_hdr),
+				       &frame_out);
 	if (status != PJ_SUCCESS) {
 	    LOGERR_((stream->port.info.name.ptr, 
@@ -1262,8 +1272,8 @@
     {
 	/* Encode! */
-	status = stream->codec->op->encode( stream->codec, frame, 
-					    channel->out_pkt_size - 
-					    sizeof(pjmedia_rtp_hdr),
-					    &frame_out);
+	status = pjmedia_codec_encode( stream->codec, frame, 
+				       channel->out_pkt_size - 
+				       sizeof(pjmedia_rtp_hdr),
+				       &frame_out);
 	if (status != PJ_SUCCESS) {
 	    LOGERR_((stream->port.info.name.ptr, 
@@ -1363,5 +1373,5 @@
  */
 static pj_status_t put_frame( pjmedia_port *port, 
-			      const pjmedia_frame *frame )
+			      pjmedia_frame *frame )
 {
     pjmedia_stream *stream = (pjmedia_stream*) port->port_data.pdata;
@@ -1411,8 +1421,9 @@
     if (stream->vad_enabled != stream->codec_param.setting.vad &&
 	(stream->tx_duration - stream->ts_vad_disabled) > 
-	stream->port.info.clock_rate * PJMEDIA_STREAM_VAD_SUSPEND_MSEC / 1000)
+           PJMEDIA_PIA_SRATE(&stream->port.info) *
+	  PJMEDIA_STREAM_VAD_SUSPEND_MSEC / 1000)
     {
 	stream->codec_param.setting.vad = stream->vad_enabled;
-	stream->codec->op->modify(stream->codec, &stream->codec_param);
+	pjmedia_codec_modify(stream->codec, &stream->codec_param);
 	PJ_LOG(4,(stream->port.info.name.ptr,"VAD re-enabled"));
     }
@@ -1574,5 +1585,4 @@
     unsigned payloadlen;
     pjmedia_rtp_status seq_st;
-    pj_bool_t check_pt;
     pj_status_t status;
     pj_bool_t pkt_discarded = PJ_FALSE;
@@ -1604,13 +1614,6 @@
      * the incoming packet.
      */
-    check_pt = (hdr->pt != stream->rx_event_pt) && PJMEDIA_STREAM_CHECK_RTP_PT;
-    pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, check_pt);
-#if !PJMEDIA_STREAM_CHECK_RTP_PT
-    if (!check_pt && hdr->pt != channel->rtp.out_pt &&
-	hdr->pt != stream->rx_event_pt)
-    {
-	seq_st.status.flag.badpt = 1;
-    }
-#endif
+    pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st,
+			        hdr->pt != stream->rx_event_pt);
     if (seq_st.status.value) {
 	TRC_  ((stream->port.info.name.ptr, 
@@ -1672,5 +1675,4 @@
 	status = pjmedia_jbuf_reset(stream->jb);
 	PJ_LOG(4,(stream->port.info.name.ptr, "Jitter buffer reset"));
-
     } else {
 	/*
@@ -1689,10 +1691,6 @@
 
 	/* Parse the payload. */
-	status = (*stream->codec->op->parse)(stream->codec,
-					     (void*)payload,
-					     payloadlen,
-					     &ts,
-					     &count,
-					     frames);
+	status = pjmedia_codec_parse(stream->codec, (void*)payload,
+				     payloadlen, &ts, &count, frames);
 	if (status != PJ_SUCCESS) {
 	    LOGERR_((stream->port.info.name.ptr, 
@@ -1722,7 +1720,7 @@
 
 		    /* Calculate actual frame timestamp span */
-		    frm_ts_span = stream->port.info.samples_per_frame /
+		    frm_ts_span = PJMEDIA_PIA_SPF(&stream->port.info) /
 				  stream->codec_param.setting.frm_per_pkt/
-				  stream->port.info.channel_count;
+				  PJMEDIA_PIA_CCNT(&stream->port.info);
 
 		    /* Get remote frame timestamp span */
@@ -1896,11 +1894,18 @@
     /* Allocate buffer for outgoing packet. */
 
-    channel->out_pkt_size = sizeof(pjmedia_rtp_hdr) + 
-			    stream->codec_param.info.max_bps * 
-			    PJMEDIA_MAX_FRAME_DURATION_MS / 
-			    8 / 1000;
-
-    if (channel->out_pkt_size > PJMEDIA_MAX_MTU)
-	channel->out_pkt_size = PJMEDIA_MAX_MTU;
+    if (param->type == PJMEDIA_TYPE_AUDIO) {
+        channel->out_pkt_size = sizeof(pjmedia_rtp_hdr) + 
+			        stream->codec_param.info.max_bps * 
+			        PJMEDIA_MAX_FRAME_DURATION_MS / 
+			        8 / 1000;
+        if (channel->out_pkt_size > PJMEDIA_MAX_MTU -
+				    PJMEDIA_STREAM_RESV_PAYLOAD_LEN)
+	{
+	    channel->out_pkt_size = PJMEDIA_MAX_MTU -
+				    PJMEDIA_STREAM_RESV_PAYLOAD_LEN;
+	}
+    } else {
+        return PJ_ENOTSUP;
+    }
 
     /* It should big enough to hold (minimally) RTCP SR with an SDES. */
@@ -1956,9 +1961,18 @@
     pj_str_t name;
     unsigned jb_init, jb_max, jb_min_pre, jb_max_pre, len;
+    pjmedia_audio_format_detail *afd;
+    pj_pool_t *own_pool = NULL;
     char *p;
     pj_status_t status;
 
-    PJ_ASSERT_RETURN(pool && info && p_stream, PJ_EINVAL);
-
+    PJ_ASSERT_RETURN(endpt && info && p_stream, PJ_EINVAL);
+
+    if (pool == NULL) {
+	own_pool = pjmedia_endpt_create_pool( endpt, "strm%p",
+					      PJMEDIA_STREAM_SIZE,
+					      PJMEDIA_STREAM_INC);
+	PJ_ASSERT_RETURN(own_pool != NULL, PJ_ENOMEM);
+	pool = own_pool;
+    }
 
     /* Allocate the media stream: */
@@ -1966,4 +1980,6 @@
     stream = PJ_POOL_ZALLOC_T(pool, pjmedia_stream);
     PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM);
+    stream->own_pool = own_pool;
+    pj_memcpy(&stream->si, info, sizeof(*info));
 
     /* Init stream/port name */
@@ -1975,13 +1991,15 @@
      */
     pjmedia_port_info_init(&stream->port.info, &name,
-			   PJMEDIA_PORT_SIGNATURE('S', 'T', 'R', 'M'),
+                           PJMEDIA_SIG_PORT_STREAM,
 			   info->fmt.clock_rate, info->fmt.channel_cnt,
 			   16, 80);
+    afd = pjmedia_format_get_audio_format_detail(&stream->port.info.fmt, 1);
 
     /* Init port. */
 
-    pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name);
-    stream->port.info.clock_rate = info->fmt.clock_rate;
-    stream->port.info.channel_count = info->fmt.channel_cnt;
+    //No longer there in 2.0
+    //pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name);
+    afd->clock_rate = info->fmt.clock_rate;
+    afd->channel_count = info->fmt.channel_cnt;
     stream->port.port_data.pdata = stream;
 
@@ -2049,20 +2067,18 @@
 
     /* Open the codec. */
-    status = stream->codec->op->open(stream->codec, &stream->codec_param);
+    status = pjmedia_codec_open(stream->codec, &stream->codec_param);
     if (status != PJ_SUCCESS)
 	goto err_cleanup;
 
     /* Set additional info and callbacks. */
-    stream->port.info.bits_per_sample = 16;
-    stream->port.info.samples_per_frame = info->fmt.clock_rate * 
-					  stream->codec_param.info.channel_cnt *
-					  stream->codec_param.info.frm_ptime *
-					  stream->codec_param.setting.frm_per_pkt /
-					  1000;
-    stream->port.info.format.id = stream->codec_param.info.fmt_id;
+    afd->bits_per_sample = 16;
+    afd->frame_time_usec = stream->codec_param.info.frm_ptime *
+		           stream->codec_param.setting.frm_per_pkt * 1000;
+    stream->port.info.fmt.id = stream->codec_param.info.fmt_id;
     if (stream->codec_param.info.fmt_id == PJMEDIA_FORMAT_L16) {
 	/* Raw format */
-	stream->port.info.bytes_per_frame = stream->port.info.samples_per_frame *
-					    stream->port.info.bits_per_sample / 8;
+	afd->avg_bps = afd->max_bps = afd->clock_rate *
+				      afd->bits_per_sample / 8;
+
 
 	stream->port.put_frame = &put_frame;
@@ -2070,10 +2086,11 @@
     } else {
 	/* Encoded format */
-	stream->port.info.bytes_per_frame = stream->codec_param.info.max_bps * 
-					    stream->codec_param.info.frm_ptime *
-					    stream->codec_param.setting.frm_per_pkt /
-					    8 / 1000;
-	if ((stream->codec_param.info.max_bps * stream->codec_param.info.frm_ptime *
-	    stream->codec_param.setting.frm_per_pkt) % 8000 != 0)
+	afd->avg_bps = stream->codec_param.info.avg_bps;
+	afd->max_bps = stream->codec_param.info.max_bps;
+
+	/* Not applicable for 2.0
+	if ((stream->codec_param.info.max_bps *
+	     stream->codec_param.info.frm_ptime *
+	     stream->codec_param.setting.frm_per_pkt) % 8000 != 0)
 	{
 	    ++stream->port.info.bytes_per_frame;
@@ -2081,4 +2098,5 @@
 	stream->port.info.format.bitrate = stream->codec_param.info.avg_bps;
 	stream->port.info.format.vad = (stream->codec_param.setting.vad != 0);
+	*/
 
 	stream->port.put_frame = &put_frame;
@@ -2097,5 +2115,5 @@
 	stream->enc_samples_per_pkt = stream->codec_param.info.enc_ptime *
 				      stream->codec_param.info.channel_cnt *
-				      stream->port.info.clock_rate / 1000;
+				      afd->clock_rate / 1000;
 
 	/* Set buffer size as twice the largest ptime value between
@@ -2103,6 +2121,5 @@
 	 */
 
-	ptime = stream->port.info.samples_per_frame * 1000 /
-		stream->port.info.clock_rate;
+	ptime = afd->frame_time_usec / 1000;
 
 	if (stream->codec_param.info.enc_ptime > ptime)
@@ -2115,10 +2132,10 @@
 
 	/* Allocate buffer */
-	stream->enc_buf_size = stream->port.info.clock_rate * ptime / 1000;
+	stream->enc_buf_size = afd->clock_rate * ptime / 1000;
 	stream->enc_buf = (pj_int16_t*)
 			  pj_pool_alloc(pool, stream->enc_buf_size * 2);
 
     } else {
-	stream->enc_samples_per_pkt = stream->port.info.samples_per_frame;
+	stream->enc_samples_per_pkt = PJMEDIA_AFD_SPF(afd);
     }
 
@@ -2129,5 +2146,5 @@
 	stream->codec_param.setting.vad = 0;
 	stream->ts_vad_disabled = 0;
-	stream->codec->op->modify(stream->codec, &stream->codec_param);
+	pjmedia_codec_modify(stream->codec, &stream->codec_param);
 	PJ_LOG(4,(stream->port.info.name.ptr,"VAD temporarily disabled"));
     }
@@ -2153,5 +2170,5 @@
     stream->rtp_tx_ts_len_per_pkt = stream->enc_samples_per_pkt /
 				     stream->codec_param.info.channel_cnt;
-    stream->rtp_rx_ts_len_per_frame = stream->port.info.samples_per_frame /
+    stream->rtp_rx_ts_len_per_frame = PJMEDIA_AFD_SPF(afd) /
 				      stream->codec_param.setting.frm_per_pkt /
 				      stream->codec_param.info.channel_cnt;
@@ -2227,5 +2244,5 @@
 	rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts);
 	rtcp_setting.clock_rate = info->fmt.clock_rate;
-	rtcp_setting.samples_per_frame = stream->port.info.samples_per_frame;
+	rtcp_setting.samples_per_frame = PJMEDIA_AFD_SPF(afd);
 
 #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG!=0)
@@ -2433,5 +2450,5 @@
 
     if (stream->codec) {
-	stream->codec->op->close(stream->codec);
+	pjmedia_codec_close(stream->codec);
 	pjmedia_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec);
 	stream->codec = NULL;
@@ -2456,4 +2473,9 @@
 #endif
 
+    if (stream->own_pool) {
+	pj_pool_t *pool = stream->own_pool;
+	stream->own_pool = NULL;
+	pj_pool_release(pool);
+    }
     return PJ_SUCCESS;
 }
@@ -2516,4 +2538,13 @@
 }
 
+
+PJ_DEF(pj_status_t) pjmedia_stream_get_info( const pjmedia_stream *stream,
+					     pjmedia_stream_info *info)
+{
+    PJ_ASSERT_RETURN(stream && info, PJ_EINVAL);
+
+    pj_memcpy(info, &stream->si, sizeof(pjmedia_stream_info));
+    return PJ_SUCCESS;
+}
 
 /*
@@ -2761,2 +2792,486 @@
 }
 
+
+static const pj_str_t ID_AUDIO = { "audio", 5};
+static const pj_str_t ID_IN = { "IN", 2 };
+static const pj_str_t ID_IP4 = { "IP4", 3};
+static const pj_str_t ID_IP6 = { "IP6", 3};
+static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
+static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
+//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
+static const pj_str_t ID_RTPMAP = { "rtpmap", 6 };
+static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 };
+
+static const pj_str_t STR_INACTIVE = { "inactive", 8 };
+static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
+static const pj_str_t STR_SENDONLY = { "sendonly", 8 };
+static const pj_str_t STR_RECVONLY = { "recvonly", 8 };
+
+
+/*
+ * Internal function for collecting codec info and param from the SDP media.
+ */
+static pj_status_t get_audio_codec_info_param(pjmedia_stream_info *si,
+					      pj_pool_t *pool,
+					      pjmedia_codec_mgr *mgr,
+					      const pjmedia_sdp_media *local_m,
+					      const pjmedia_sdp_media *rem_m)
+{
+    const pjmedia_sdp_attr *attr;
+    pjmedia_sdp_rtpmap *rtpmap;
+    unsigned i, fmti, pt = 0;
+    pj_status_t status;
+
+    /* Find the first codec which is not telephone-event */
+    for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) {
+	if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) )
+	    return PJMEDIA_EINVALIDPT;
+	pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+	if ( PJMEDIA_RTP_PT_TELEPHONE_EVENTS == 0 ||
+		pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS )
+		break;
+    }
+    if ( fmti >= local_m->desc.fmt_count )
+	return PJMEDIA_EINVALIDPT;
+
+    /* Get codec info.
+     * For static payload types, get the info from codec manager.
+     * For dynamic payload types, MUST get the rtpmap.
+     */
+    if (pt < 96) {
+	pj_bool_t has_rtpmap;
+
+	rtpmap = NULL;
+	has_rtpmap = PJ_TRUE;
+
+	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
+					   &local_m->desc.fmt[fmti]);
+	if (attr == NULL) {
+	    has_rtpmap = PJ_FALSE;
+	}
+	if (attr != NULL) {
+	    status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
+	    if (status != PJ_SUCCESS)
+		has_rtpmap = PJ_FALSE;
+	}
+
+	/* Build codec format info: */
+	if (has_rtpmap) {
+	    si->fmt.type = si->type;
+	    si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+	    pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
+	    si->fmt.clock_rate = rtpmap->clock_rate;
+	    
+#if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0)
+	    /* The session info should have the actual clock rate, because 
+	     * this info is used for calculationg buffer size, etc in stream 
+	     */
+	    if (si->fmt.pt == PJMEDIA_RTP_PT_G722)
+		si->fmt.clock_rate = 16000;
+#endif
+
+	    /* For audio codecs, rtpmap parameters denotes the number of
+	     * channels.
+	     */
+	    if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
+		si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
+	    } else {
+		si->fmt.channel_cnt = 1;
+	    }
+
+	} else {	    
+	    const pjmedia_codec_info *p_info;
+
+	    status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info));
+	}
+
+	/* For static payload type, pt's are symetric */
+	si->tx_pt = pt;
+
+    } else {
+
+	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
+					   &local_m->desc.fmt[fmti]);
+	if (attr == NULL)
+	    return PJMEDIA_EMISSINGRTPMAP;
+
+	status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	/* Build codec format info: */
+
+	si->fmt.type = si->type;
+	si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]);
+	pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name);
+	si->fmt.clock_rate = rtpmap->clock_rate;
+
+	/* For audio codecs, rtpmap parameters denotes the number of
+	 * channels.
+	 */
+	if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) {
+	    si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param);
+	} else {
+	    si->fmt.channel_cnt = 1;
+	}
+
+	/* Determine payload type for outgoing channel, by finding
+	 * dynamic payload type in remote SDP that matches the answer.
+	 */
+	si->tx_pt = 0xFFFF;
+	for (i=0; i<rem_m->desc.fmt_count; ++i) {
+	    unsigned rpt;
+	    pjmedia_sdp_attr *r_attr;
+	    pjmedia_sdp_rtpmap r_rtpmap;
+
+	    rpt = pj_strtoul(&rem_m->desc.fmt[i]);
+	    if (rpt < 96)
+		continue;
+
+	    r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP,
+						 &rem_m->desc.fmt[i]);
+	    if (!r_attr)
+		continue;
+
+	    if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS)
+		continue;
+
+	    if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) &&
+		rtpmap->clock_rate == r_rtpmap.clock_rate)
+	    {
+		/* Found matched codec. */
+		si->tx_pt = rpt;
+
+		break;
+	    }
+	}
+
+	if (si->tx_pt == 0xFFFF)
+	    return PJMEDIA_EMISSINGRTPMAP;
+    }
+
+  
+    /* Now that we have codec info, get the codec param. */
+    si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param);
+    status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt,
+					         si->param);
+
+    /* Get remote fmtp for our encoder. */
+    pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt,
+				   &si->param->setting.enc_fmtp);
+
+    /* Get local fmtp for our decoder. */
+    pjmedia_stream_info_parse_fmtp(pool, local_m, si->fmt.pt,
+				   &si->param->setting.dec_fmtp);
+
+    /* Get the remote ptime for our encoder. */
+    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+				  "ptime", NULL);
+    if (attr) {
+	pj_str_t tmp_val = attr->value;
+	unsigned frm_per_pkt;
+ 
+	pj_strltrim(&tmp_val);
+
+	/* Round up ptime when the specified is not multiple of frm_ptime */
+	frm_per_pkt = (pj_strtoul(&tmp_val) + 
+		      si->param->info.frm_ptime/2) /
+		      si->param->info.frm_ptime;
+	if (frm_per_pkt != 0) {
+            si->param->setting.frm_per_pkt = (pj_uint8_t)frm_per_pkt;
+        }
+    }
+
+    /* Get remote maxptime for our encoder. */
+    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+				  "maxptime", NULL);
+    if (attr) {
+	pj_str_t tmp_val = attr->value;
+
+	pj_strltrim(&tmp_val);
+	si->tx_maxptime = pj_strtoul(&tmp_val);
+    }
+
+    /* When direction is NONE (it means SDP negotiation has failed) we don't
+     * need to return a failure here, as returning failure will cause
+     * the whole SDP to be rejected. See ticket #:
+     *	http://
+     *
+     * Thanks Alain Totouom 
+     */
+    if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE)
+	return status;
+
+
+    /* Get incomming payload type for telephone-events */
+    si->rx_event_pt = -1;
+    for (i=0; i<local_m->attr_count; ++i) {
+	pjmedia_sdp_rtpmap r;
+
+	attr = local_m->attr[i];
+	if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
+	    continue;
+	if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
+	    continue;
+	if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
+	    si->rx_event_pt = pj_strtoul(&r.pt);
+	    break;
+	}
+    }
+
+    /* Get outgoing payload type for telephone-events */
+    si->tx_event_pt = -1;
+    for (i=0; i<rem_m->attr_count; ++i) {
+	pjmedia_sdp_rtpmap r;
+
+	attr = rem_m->attr[i];
+	if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0)
+	    continue;
+	if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS)
+	    continue;
+	if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) {
+	    si->tx_event_pt = pj_strtoul(&r.pt);
+	    break;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Create stream info from SDP media line.
+ */
+PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
+					   pjmedia_stream_info *si,
+					   pj_pool_t *pool,
+					   pjmedia_endpt *endpt,
+					   const pjmedia_sdp_session *local,
+					   const pjmedia_sdp_session *remote,
+					   unsigned stream_idx)
+{
+    pjmedia_codec_mgr *mgr;
+    const pjmedia_sdp_attr *attr;
+    const pjmedia_sdp_media *local_m;
+    const pjmedia_sdp_media *rem_m;
+    const pjmedia_sdp_conn *local_conn;
+    const pjmedia_sdp_conn *rem_conn;
+    int rem_af, local_af;
+    pj_sockaddr local_addr;
+    pj_status_t status;
+
+    
+    /* Validate arguments: */
+    PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
+    PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL);
+    PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);
+
+    /* Keep SDP shortcuts */
+    local_m = local->media[stream_idx];
+    rem_m = remote->media[stream_idx];
+
+    local_conn = local_m->conn ? local_m->conn : local->conn;
+    if (local_conn == NULL)
+	return PJMEDIA_SDP_EMISSINGCONN;
+
+    rem_conn = rem_m->conn ? rem_m->conn : remote->conn;
+    if (rem_conn == NULL)
+	return PJMEDIA_SDP_EMISSINGCONN;
+
+    /* Media type must be audio */
+    if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) != 0)
+	return PJMEDIA_EINVALIMEDIATYPE;
+
+    /* Get codec manager. */
+    mgr = pjmedia_endpt_get_codec_mgr(endpt);
+
+    /* Reset: */
+
+    pj_bzero(si, sizeof(*si));
+
+#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
+    /* Set default RTCP XR enabled/disabled */
+    si->rtcp_xr_enabled = PJ_TRUE;
+#endif
+
+    /* Media type: */
+    si->type = PJMEDIA_TYPE_AUDIO;
+
+    /* Transport protocol */
+
+    /* At this point, transport type must be compatible, 
+     * the transport instance will do more validation later.
+     */
+    status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, 
+				       &local_m->desc.transport);
+    if (status != PJ_SUCCESS)
+	return PJMEDIA_SDPNEG_EINVANSTP;
+
+    if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) {
+
+	si->proto = PJMEDIA_TP_PROTO_RTP_AVP;
+
+    } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) {
+
+	si->proto = PJMEDIA_TP_PROTO_RTP_SAVP;
+
+    } else {
+
+	si->proto = PJMEDIA_TP_PROTO_UNKNOWN;
+	return PJ_SUCCESS;
+    }
+
+
+    /* Check address family in remote SDP */
+    rem_af = pj_AF_UNSPEC();
+    if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) {
+	if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) {
+	    rem_af = pj_AF_INET();
+	} else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) {
+	    rem_af = pj_AF_INET6();
+	}
+    }
+
+    if (rem_af==pj_AF_UNSPEC()) {
+	/* Unsupported address family */
+	return PJ_EAFNOTSUP;
+    }
+
+    /* Set remote address: */
+    status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, 
+			      rem_m->desc.port);
+    if (status != PJ_SUCCESS) {
+	/* Invalid IP address. */
+	return PJMEDIA_EINVALIDIP;
+    }
+
+    /* Check address family of local info */
+    local_af = pj_AF_UNSPEC();
+    if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) {
+	if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) {
+	    local_af = pj_AF_INET();
+	} else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) {
+	    local_af = pj_AF_INET6();
+	}
+    }
+
+    if (local_af==pj_AF_UNSPEC()) {
+	/* Unsupported address family */
+	return PJ_SUCCESS;
+    }
+
+    /* Set remote address: */
+    status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, 
+			      local_m->desc.port);
+    if (status != PJ_SUCCESS) {
+	/* Invalid IP address. */
+	return PJMEDIA_EINVALIDIP;
+    }
+
+    /* Local and remote address family must match */
+    if (local_af != rem_af)
+	return PJ_EAFNOTSUP;
+
+    /* Media direction: */
+
+    if (local_m->desc.port == 0 || 
+	pj_sockaddr_has_addr(&local_addr)==PJ_FALSE ||
+	pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE ||
+	pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL)
+    {
+	/* Inactive stream. */
+
+	si->dir = PJMEDIA_DIR_NONE;
+
+    } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) {
+
+	/* Send only stream. */
+
+	si->dir = PJMEDIA_DIR_ENCODING;
+
+    } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) {
+
+	/* Recv only stream. */
+
+	si->dir = PJMEDIA_DIR_DECODING;
+
+    } else {
+
+	/* Send and receive stream. */
+
+	si->dir = PJMEDIA_DIR_ENCODING_DECODING;
+
+    }
+
+    /* No need to do anything else if stream is rejected */
+    if (local_m->desc.port == 0) {
+	return PJ_SUCCESS;
+    }
+
+    /* If "rtcp" attribute is present in the SDP, set the RTCP address
+     * from that attribute. Otherwise, calculate from RTP address.
+     */
+    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+				  "rtcp", NULL);
+    if (attr) {
+	pjmedia_sdp_rtcp_attr rtcp;
+	status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
+	if (status == PJ_SUCCESS) {
+	    if (rtcp.addr.slen) {
+		status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr,
+					  (pj_uint16_t)rtcp.port);
+	    } else {
+		pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, 
+				 (pj_uint16_t)rtcp.port);
+		pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp),
+		          pj_sockaddr_get_addr(&si->rem_addr),
+			  pj_sockaddr_get_addr_len(&si->rem_addr));
+	    }
+	}
+    }
+    
+    if (!pj_sockaddr_has_addr(&si->rem_rtcp)) {
+	int rtcp_port;
+
+	pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr));
+	rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1;
+	pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port);
+    }
+
+
+    /* Get the payload number for receive channel. */
+    /*
+       Previously we used to rely on fmt[0] being the selected codec,
+       but some UA sends telephone-event as fmt[0] and this would
+       cause assert failure below.
+
+       Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch.
+
+    // And codec must be numeric!
+    if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || 
+	!pj_isdigit(*rem_m->desc.fmt[0].ptr))
+    {
+	return PJMEDIA_EINVALIDPT;
+    }
+
+    pt = pj_strtoul(&local_m->desc.fmt[0]);
+    pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 ||
+	      pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS);
+    */
+
+    /* Get codec info and param */
+    status = get_audio_codec_info_param(si, pool, mgr, local_m, rem_m);
+
+    /* Leave SSRC to random. */
+    si->ssrc = pj_rand();
+
+    /* Set default jitter buffer parameter. */
+    si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;
+
+    return status;
+}
