Index: pjproject/trunk/pjmedia/include/pjmedia-codec/types.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia-codec/types.h	(revision 2235)
+++ pjproject/trunk/pjmedia/include/pjmedia-codec/types.h	(revision 2236)
@@ -62,5 +62,7 @@
     /* PJMEDIA_RTP_PT_G726_32,*/		/**< G726 @ 32Kbps, static? */
     PJMEDIA_RTP_PT_G726_40,			/**< G726 @ 40Kbps	    */
-    PJMEDIA_RTP_PT_G722_1,			/**< G722.1 (16-32Kbps)	    */
+    PJMEDIA_RTP_PT_G722_1_16,			/**< G722.1 (16Kbps)	    */
+    PJMEDIA_RTP_PT_G722_1_24,			/**< G722.1 (24Kbps)	    */
+    PJMEDIA_RTP_PT_G722_1_32,			/**< G722.1 (32Kbps)	    */
 };
 
Index: pjproject/trunk/pjmedia/include/pjmedia/codec.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/codec.h	(revision 2235)
+++ pjproject/trunk/pjmedia/include/pjmedia/codec.h	(revision 2236)
@@ -239,4 +239,19 @@
 } pjmedia_codec_info;
 
+#define PJMEDIA_CODEC_MAX_FMTP_CNT  8
+
+/** 
+ * Structure of codec specific parameters which contains name=value pairs.
+ * The codec specific parameters are to be used with SDP according to 
+ * the standards (e.g: RFC 3555).
+ */
+typedef struct pjmedia_codec_fmtp
+{
+    pj_uint8_t	    cnt;
+    struct param {
+	pj_str_t    name;
+	pj_str_t    val;
+    } param [PJMEDIA_CODEC_MAX_FMTP_CNT];
+} pjmedia_codec_fmtp;
 
 /** 
@@ -278,6 +293,6 @@
 	unsigned    plc:1;	    /**< Packet loss concealment	*/
 	unsigned    reserved:1;	    /**< Reserved, must be zero.	*/
-	pj_uint8_t  enc_fmtp_mode;  /**< Mode param in fmtp (def:0)	*/
-	pj_uint8_t  dec_fmtp_mode;  /**< Mode param in fmtp (def:0)	*/
+	pjmedia_codec_fmtp enc_fmtp;/**< Encoder's fmtp params.		*/
+	pjmedia_codec_fmtp dec_fmtp;/**< Decoder's fmtp params.		*/
     } setting;
 } pjmedia_codec_param;
Index: pjproject/trunk/pjmedia/include/pjmedia/stream.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/stream.h	(revision 2235)
+++ pjproject/trunk/pjmedia/include/pjmedia/stream.h	(revision 2236)
@@ -109,4 +109,5 @@
     pjmedia_codec_param *param;	    /**< Optional codec param.		    */
     unsigned		tx_pt;	    /**< Outgoing codec paylaod type.	    */
+    unsigned		tx_maxptime;/**< Outgoing codec max ptime.	    */
     int		        tx_event_pt;/**< Outgoing pt for telephone-events.  */
     int			rx_event_pt;/**< Incoming pt for telephone-events.  */
Index: pjproject/trunk/pjmedia/src/pjmedia-codec/ilbc.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia-codec/ilbc.c	(revision 2235)
+++ pjproject/trunk/pjmedia/src/pjmedia-codec/ilbc.c	(revision 2236)
@@ -144,5 +144,5 @@
 };
 
-
+static pj_str_t STR_MODE = {"mode", 4};
 
 /*
@@ -274,5 +274,10 @@
     attr->setting.plc = 1;
     attr->setting.penh = 1;
-    attr->setting.dec_fmtp_mode = (pj_uint8_t)ilbc_factory.mode;
+    attr->setting.dec_fmtp.cnt = 1;
+    attr->setting.dec_fmtp.param[0].name = STR_MODE;
+    if (ilbc_factory.mode == 30)
+	attr->setting.dec_fmtp.param[0].val = pj_str("30");
+    else
+	attr->setting.dec_fmtp.param[0].val = pj_str("20");
 
     return PJ_SUCCESS;
@@ -371,4 +376,5 @@
     struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec;
     pj_status_t status;
+    unsigned i, dec_fmtp_mode = 0, enc_fmtp_mode = 0;
 
     pj_assert(ilbc_codec != NULL);
@@ -376,7 +382,27 @@
 	      ilbc_codec->dec_ready == PJ_FALSE);
 
+    /* Get decoder mode */
+    for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) {
+	if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, &STR_MODE) == 0)
+	{
+	    dec_fmtp_mode = (unsigned)
+			    pj_strtoul(&attr->setting.dec_fmtp.param[i].val);
+	    break;
+	}
+    }
+
     /* Decoder mode must be set */
-    PJ_ASSERT_RETURN(attr->setting.dec_fmtp_mode==20 ||
-		     attr->setting.dec_fmtp_mode==30, PJMEDIA_CODEC_EINMODE);
+    PJ_ASSERT_RETURN(dec_fmtp_mode == 20 || dec_fmtp_mode == 30, 
+		     PJMEDIA_CODEC_EINMODE);
+
+    /* Get encoder mode */
+    for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+	if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_MODE) == 0)
+	{
+	    enc_fmtp_mode = (unsigned)
+			    pj_strtoul(&attr->setting.enc_fmtp.param[i].val);
+	    break;
+	}
+    }
 
     /* The enc mode must be set in the attribute 
@@ -384,13 +410,13 @@
      * received from remote) 
      */
-    if (attr->setting.enc_fmtp_mode == 0)
-	attr->setting.enc_fmtp_mode = attr->setting.dec_fmtp_mode;
-
-    PJ_ASSERT_RETURN(attr->setting.enc_fmtp_mode==20 ||
-		     attr->setting.enc_fmtp_mode==30, PJMEDIA_CODEC_EINMODE);
+    if (enc_fmtp_mode == 0)
+	enc_fmtp_mode = dec_fmtp_mode;
+
+    PJ_ASSERT_RETURN(enc_fmtp_mode==20 ||
+		     enc_fmtp_mode==30, PJMEDIA_CODEC_EINMODE);
 
     /* Update enc_ptime in the param */
-    if (attr->setting.enc_fmtp_mode != attr->setting.dec_fmtp_mode) {
-	attr->info.enc_ptime = attr->setting.enc_fmtp_mode;
+    if (enc_fmtp_mode != dec_fmtp_mode) {
+	attr->info.enc_ptime = (pj_uint16_t)enc_fmtp_mode;
     } else {
 	attr->info.enc_ptime = 0;
@@ -398,17 +424,15 @@
 
     /* Create enc */
-    ilbc_codec->enc_frame_size = initEncode(&ilbc_codec->enc, 
-					    attr->setting.enc_fmtp_mode);
-    ilbc_codec->enc_samples_per_frame = CLOCK_RATE*attr->setting.enc_fmtp_mode/
-					1000;
+    ilbc_codec->enc_frame_size = initEncode(&ilbc_codec->enc, enc_fmtp_mode);
+    ilbc_codec->enc_samples_per_frame = CLOCK_RATE * enc_fmtp_mode / 1000;
     ilbc_codec->enc_ready = PJ_TRUE;
 
     /* Create decoder */
     ilbc_codec->dec_samples_per_frame = initDecode(&ilbc_codec->dec,
-						   attr->setting.dec_fmtp_mode,
+						   dec_fmtp_mode,
 						   attr->setting.penh);
-    if (attr->setting.dec_fmtp_mode == 20)
+    if (dec_fmtp_mode == 20)
 	ilbc_codec->dec_frame_size = 38;
-    else if (attr->setting.dec_fmtp_mode == 30)
+    else if (dec_fmtp_mode == 30)
 	ilbc_codec->dec_frame_size = 50;
     else {
@@ -436,5 +460,5 @@
     PJ_LOG(5,(ilbc_codec->obj_name, 
 	      "iLBC codec opened, encoder mode=%d, decoder mode=%d",
-	      attr->setting.enc_fmtp_mode, attr->setting.dec_fmtp_mode));
+	      enc_fmtp_mode, dec_fmtp_mode));
 
     return PJ_SUCCESS;
Index: pjproject/trunk/pjmedia/src/pjmedia-codec/ipp_codecs.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia-codec/ipp_codecs.c	(revision 2235)
+++ pjproject/trunk/pjmedia/src/pjmedia-codec/ipp_codecs.c	(revision 2236)
@@ -30,4 +30,5 @@
 #include <pj/os.h>
 
+
 /*
  * Only build this file if PJMEDIA_HAS_INTEL_IPP != 0
@@ -211,8 +212,10 @@
 
     predecode_cb     predecode;		/* Callback to translate RTP frame
-					   into USC frame		    */
-    parse_cb	     parse;		/* Callback to parse bitstream	    */
-    pack_cb	     pack;		/* Callback to pack bitstream	    */
-} 
+					   into USC frame.		    */
+    parse_cb	     parse;		/* Callback to parse bitstream.	    */
+    pack_cb	     pack;		/* Callback to pack bitstream.	    */
+
+    pjmedia_codec_fmtp dec_fmtp;	/* Decoder's fmtp params.	    */
+}
 
 ipp_codec[] = 
@@ -233,12 +236,6 @@
 
 #   if PJMEDIA_HAS_INTEL_IPP_CODEC_G729
-    /* G.729 actually has internal VAD, but for now we need to disable it, 
-     * since its RTP packaging (multiple frames per packet) requires 
-     * SID frame to only be occured in the last frame, while controling 
-     * encoder on each loop (to enable/disable VAD) is considered inefficient.
-     * This should still be interoperable with other implementations.
-     */
     {1, "G729",	    PJMEDIA_RTP_PT_G729,      &USC_G729AFP_Fxns, 8000, 1,  80,  
-		    8000, 11800, 2, 0, 1, 
+		    8000, 11800, 2, 1, 1, 
 		    &predecode_g729, NULL, NULL
     },
@@ -280,7 +277,18 @@
 
 #   if PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1
-    {0, "G7221",    PJMEDIA_RTP_PT_G722_1,    &USC_G722_Fxns,	16000, 1, 320, 
-		    16000, 32000, 1, 0, 1,
-		    NULL, NULL, NULL
+    {0, "G7221",    PJMEDIA_RTP_PT_G722_1_16, &USC_G722_Fxns,	16000, 1, 320, 
+		    16000, 16000, 1, 0, 1,
+		    NULL, NULL, NULL,
+		    {1, {{{"bitrate", 7}, {"16000", 5}}} }
+    },
+    {1, "G7221",    PJMEDIA_RTP_PT_G722_1_24, &USC_G722_Fxns,	16000, 1, 320, 
+		    24000, 24000, 1, 0, 1,
+		    NULL, NULL, NULL,
+		    {1, {{{"bitrate", 7}, {"24000", 5}}} }
+    },
+    {1, "G7221",    PJMEDIA_RTP_PT_G722_1_32, &USC_G722_Fxns,	16000, 1, 320, 
+		    32000, 32000, 1, 0, 1,
+		    NULL, NULL, NULL,
+		    {1, {{{"bitrate", 7}, {"32000", 5}}} }
     },
 #   endif
@@ -423,5 +431,6 @@
 	if ((pj_stricmp(&id->encoding_name, &name) == 0) &&
 	    (id->clock_rate == (unsigned)ipp_codec[i].clock_rate) &&
-	    (id->channel_cnt == (unsigned)ipp_codec[i].channel_count))
+	    (id->channel_cnt == (unsigned)ipp_codec[i].channel_count) &&
+	    (id->pt == (unsigned)ipp_codec[i].pt))
 	{
 	    attr->info.pt = (pj_uint8_t)id->pt;
@@ -438,8 +447,20 @@
 
 	    /* Default flags. */
-	    attr->setting.cng = 0;
 	    attr->setting.plc = 1;
 	    attr->setting.penh= 0;
-	    attr->setting.vad = 1; /* Always disable for now */
+	    attr->setting.vad = 1;
+	    attr->setting.cng = attr->setting.vad;
+	    attr->setting.dec_fmtp = ipp_codec[i].dec_fmtp;
+
+	    if (attr->setting.vad == 0) {
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G729
+		if (id->pt == PJMEDIA_RTP_PT_G729) {
+		    /* Signal G729 Annex B is being disabled */
+		    attr->setting.dec_fmtp.cnt = 1;
+		    pj_strset2(&attr->setting.dec_fmtp.param[0].name, "annexb");
+		    pj_strset2(&attr->setting.dec_fmtp.param[0].val, "no");
+		}
+#endif
+	    }
 
 	    return PJ_SUCCESS;
@@ -632,7 +653,21 @@
     codec_data->info->params.direction = USC_ENCODE;
     codec_data->info->params.modes.vad = attr->setting.vad && 
-					   ippc->has_native_vad;
+					 ippc->has_native_vad;
     codec_data->info->params.modes.bitrate = attr->info.avg_bps;
     codec_data->info->params.law = 0; /* Linear PCM input */
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G729
+    if (ippc->pt == PJMEDIA_RTP_PT_G729) {
+	/* Check if G729 Annex B is signaled to be disabled */
+	for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) {
+	    if (pj_stricmp2(&attr->setting.enc_fmtp.param[i].name, "annexb")==0)
+	    {
+		if (pj_stricmp2(&attr->setting.enc_fmtp.param[i].val, "no")==0)
+		    codec_data->info->params.modes.vad = 0;
+		break;
+	    }
+	}
+    }
+#endif
 
     /* Get number of memory blocks needed by the encoder */
@@ -673,4 +708,7 @@
     /* Setting the decoder params */
     codec_data->info->params.direction = USC_DECODE;
+
+    /* Not sure if VAD affects decoder, just try to be safe */
+    codec_data->info->params.modes.vad = ippc->has_native_vad;
 
     /* Get number of memory blocks needed by the decoder */
@@ -926,4 +964,16 @@
 	tx += out.nbytes;
 	bits_out += out.nbytes;
+
+#if PJMEDIA_HAS_INTEL_IPP_CODEC_G729
+	if (out.frametype == 1) {
+	    /* SID */
+	    break;
+	} else if (out.frametype == 0) {
+	    /* Untransmitted */
+	    tx -= out.nbytes;
+	    break;
+	}
+#endif
+
     }
 
Index: pjproject/trunk/pjmedia/src/pjmedia/endpoint.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/endpoint.c	(revision 2235)
+++ pjproject/trunk/pjmedia/src/pjmedia/endpoint.c	(revision 2236)
@@ -425,16 +425,53 @@
 	}
 
-	/* Add fmtp mode where applicable */
-	if (codec_param.setting.dec_fmtp_mode != 0) {
-	    const pj_str_t fmtp = { "fmtp", 4 };
+	/* Add fmtp params */
+	if (codec_param.setting.dec_fmtp.cnt > 0) {
+	    enum { MAX_FMTP_STR_LEN = 160 };
+	    char buf[MAX_FMTP_STR_LEN];
+	    unsigned buf_len = 0, i;
+	    pjmedia_codec_fmtp *dec_fmtp = &codec_param.setting.dec_fmtp;
+
+	    /* Print codec PT */
+	    buf_len += pj_ansi_snprintf(buf, 
+					MAX_FMTP_STR_LEN - buf_len, 
+					"%d", 
+					codec_info->pt);
+
+	    for (i = 0; i < dec_fmtp->cnt; ++i) {
+		unsigned test_len = 2;
+
+		/* Check if buf still available */
+		test_len = dec_fmtp->param[i].val.slen + 
+			   dec_fmtp->param[i].name.slen;
+		if (test_len + buf_len >= MAX_FMTP_STR_LEN)
+		    return PJ_ETOOBIG;
+
+		/* Print delimiter */
+		buf_len += pj_ansi_snprintf(&buf[buf_len], 
+					    MAX_FMTP_STR_LEN - buf_len,
+					    (i == 0?" ":";"));
+
+		/* Print an fmtp param */
+		if (dec_fmtp->param[i].name.slen)
+		    buf_len += pj_ansi_snprintf(
+					    &buf[buf_len],
+					    MAX_FMTP_STR_LEN - buf_len,
+					    "%.*s=%.*s",
+					    (int)dec_fmtp->param[i].name.slen,
+					    dec_fmtp->param[i].name.ptr,
+					    (int)dec_fmtp->param[i].val.slen,
+					    dec_fmtp->param[i].val.ptr);
+		else
+		    buf_len += pj_ansi_snprintf(&buf[buf_len], 
+					    MAX_FMTP_STR_LEN - buf_len,
+					    "%.*s", 
+					    (int)dec_fmtp->param[i].val.slen,
+					    dec_fmtp->param[i].val.ptr);
+	    }
+
 	    attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
 
-	    attr->name = fmtp;
-	    attr->value.ptr = (char*) pj_pool_alloc(pool, 32);
-	    attr->value.slen = 
-		pj_ansi_snprintf( attr->value.ptr, 32,
-				  "%d mode=%d",
-				  codec_info->pt,
-				  codec_param.setting.dec_fmtp_mode);
+	    attr->name = pj_str("fmtp");
+	    attr->value = pj_strdup3(pool, buf);
 	    m->attr[m->attr_count++] = attr;
 	}
Index: pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c	(revision 2235)
+++ pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c	(revision 2236)
@@ -534,4 +534,49 @@
 }
 
+/* Matching G722.1 bitrates between offer and answer.
+ */
+static pj_bool_t match_g7221( const pjmedia_sdp_media *offer,
+			      unsigned o_fmt_idx,
+			      const pjmedia_sdp_media *answer,
+			      unsigned a_fmt_idx)
+{
+    const pjmedia_sdp_attr *a_ans;
+    const pjmedia_sdp_attr *a_off;
+    pjmedia_sdp_fmtp fmtp;
+    unsigned a_bitrate = 0, o_bitrate = 0;
+    const pj_str_t bitrate = {"bitrate=", 8};
+    const char *p;
+
+    a_ans = pjmedia_sdp_media_find_attr2(answer, "fmtp", 
+					 &answer->desc.fmt[a_fmt_idx]);
+    if (!a_ans)
+	return PJ_FALSE;
+
+    if (pjmedia_sdp_attr_get_fmtp(a_ans, &fmtp) != PJ_SUCCESS)
+	return PJ_FALSE;
+
+    p = pj_stristr(&fmtp.fmt_param, &bitrate);
+    if (p == NULL)
+	return PJ_FALSE;
+
+    a_bitrate = atoi(p + bitrate.slen);
+
+    a_off = pjmedia_sdp_media_find_attr2(offer, "fmtp", 
+					 &offer->desc.fmt[o_fmt_idx]);
+    if (!a_off)
+	return PJ_FALSE;
+
+    if (pjmedia_sdp_attr_get_fmtp(a_off, &fmtp) != PJ_SUCCESS)
+	return PJ_FALSE;
+
+    p = pj_stristr(&fmtp.fmt_param, &bitrate);
+    if (p == NULL)
+	return PJ_FALSE;
+
+    o_bitrate = atoi(p + bitrate.slen);
+
+    return (a_bitrate == o_bitrate);
+}
+
 /* Update single local media description to after receiving answer
  * from remote.
@@ -658,6 +703,12 @@
 			     (ar.param.slen==1 && *ar.param.ptr=='1')))
 			{
-			    /* Match! */
-			    break;
+			    /* Further check for G7221, negotiate bitrate. */
+			    if (pj_strcmp2(&or_.enc_name, "G7221") == 0) {
+				if (match_g7221(offer, i, answer, j))
+				    break;
+			    } else {
+				/* Match! */
+				break;
+			    }
 			}
 		    }
@@ -872,8 +923,16 @@
 			{
 			    /* Match! */
-			    if (is_codec)
+			    if (is_codec) {
+				/* Further check for G7221, negotiate bitrate. */
+				if (pj_strcmp2(&or_.enc_name, "G7221")  == 0 &&
+				    match_g7221(offer, i, preanswer, j) == 0)
+				{
+				    continue;
+				}
 				found_matching_codec = 1;
-			    else
+			    } else {
 				found_matching_telephone_event = 1;
+			    }
+
 			    pt_answer[pt_answer_count++] = preanswer->desc.fmt[j];
 			    break;
Index: pjproject/trunk/pjmedia/src/pjmedia/session.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/session.c	(revision 2235)
+++ pjproject/trunk/pjmedia/src/pjmedia/session.c	(revision 2236)
@@ -68,49 +68,74 @@
 
 /*
- * Get fmtp mode parameter associated with the codec.
- */
-static pj_status_t get_fmtp_mode(const pjmedia_sdp_media *m,
-				 const pj_str_t *fmt,
-				 int *p_mode)
+ * Parse fmtp for specified format/payload type.
+ */
+static void parse_fmtp( pj_pool_t *pool,
+			const pjmedia_sdp_media *m,
+			unsigned pt,
+			pjmedia_codec_fmtp *fmtp)
 {
     const pjmedia_sdp_attr *attr;
-    pjmedia_sdp_fmtp fmtp;
-    const pj_str_t str_mode = { "mode=", 5 };
-    char *pos;
+    pjmedia_sdp_fmtp sdp_fmtp;
+    char *p, *p_end, fmt_buf[8];
+    pj_str_t fmt;
+
+    pj_assert(m && fmtp);
+
+    pj_bzero(fmtp, sizeof(pjmedia_codec_fmtp));
 
     /* Get "fmtp" attribute for the format */
-    attr = pjmedia_sdp_media_find_attr2(m, "fmtp", fmt);
+    pj_ansi_sprintf(fmt_buf, "%d", pt);
+    fmt = pj_str(fmt_buf);
+    attr = pjmedia_sdp_media_find_attr2(m, "fmtp", &fmt);
     if (attr == NULL)
-	return -1;
+	return;
 
     /* Parse "fmtp" attribute */
-    if (pjmedia_sdp_attr_get_fmtp(attr, &fmtp) != PJ_SUCCESS)
-	return -1;
-
-    /* Look for "mode=" string in the fmtp */
-    while (fmtp.fmt_param.slen >= str_mode.slen + 1) {
-	if (pj_strnicmp(&fmtp.fmt_param, &str_mode, str_mode.slen)==0) {
-	    /* Found "mode=" string */
+    if (pjmedia_sdp_attr_get_fmtp(attr, &sdp_fmtp) != PJ_SUCCESS)
+	return;
+
+    /* Prepare parsing */
+    p = sdp_fmtp.fmt_param.ptr;
+    p_end = p + sdp_fmtp.fmt_param.slen;
+
+    /* Parse */
+    while (p < p_end) {
+	char *token, *start, *end;
+
+	/* Skip whitespaces */
+	while (p < p_end && (*p == ' ' || *p == '\t')) ++p;
+	if (p == p_end)
 	    break;
-	}
-
-	fmtp.fmt_param.ptr++;
-	fmtp.fmt_param.slen--;
-    }
-
-    if (fmtp.fmt_param.slen < str_mode.slen + 1) {
-	/* "mode=" param not found */
-	return -1;
-    }
-
-    /* Get the mode */
-    pos = fmtp.fmt_param.ptr + str_mode.slen;
-    *p_mode = 0;
-    while (pj_isdigit(*pos)) {
-	*p_mode = *p_mode * 10 + (*pos - '0');
-	++pos;
-    }
-
-    return PJ_SUCCESS;
+
+	/* Get token */
+	start = p;
+	while (p < p_end && *p != ';' && *p != '=') ++p;
+	end = p - 1;
+
+	/* Right trim */
+	while (end >= start && (*end == ' '  || *end == '\t' || 
+				*end == '\r' || *end == '\n' ))
+	    --end;
+
+	/* Forward a char after trimming */
+	++end;
+
+	/* Store token */
+	if (end > start) {
+	    token = (char*)pj_pool_alloc(pool, end - start);
+	    pj_ansi_strncpy(token, start, end - start);
+	    if (*p == '=')
+		/* Got param name */
+		pj_strset(&fmtp->param[fmtp->cnt].name, token, end - start);
+	    else
+		/* Got param value */
+		pj_strset(&fmtp->param[fmtp->cnt++].val, token, end - start);
+	} else if (*p != '=') {
+	    ++fmtp->cnt;
+	}
+
+	/* Next */
+	++p;
+    }
 }
 
@@ -136,5 +161,4 @@
     pj_sockaddr local_addr;
     pjmedia_sdp_rtpmap *rtpmap;
-    int local_fmtp_mode = 0, rem_fmtp_mode = 0;
     unsigned i, pt, fmti;
     pj_status_t status;
@@ -401,5 +425,6 @@
 #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 */
+	     * this info is used for calculationg buffer size, etc in stream 
+	     */
 	    if (si->fmt.pt == PJMEDIA_RTP_PT_G722)
 		si->fmt.clock_rate = 16000;
@@ -464,7 +489,4 @@
 	}
 
-	/* Get fmtp mode= param in local SDP, if any */
-	get_fmtp_mode(local_m, &local_m->desc.fmt[fmti], &local_fmtp_mode);
-
 	/* Determine payload type for outgoing channel, by finding
 	 * dynamic payload type in remote SDP that matches the answer.
@@ -494,7 +516,4 @@
 		si->tx_pt = rpt;
 
-		/* Get fmtp mode param in remote SDP, if any */
-		get_fmtp_mode(rem_m, &rtpmap->pt, &rem_fmtp_mode);
-
 		break;
 	    }
@@ -509,4 +528,20 @@
     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. */
+    parse_fmtp(pool, rem_m, si->tx_pt, &si->param->setting.enc_fmtp);
+
+    /* Get local fmtp for our decoder. */
+    parse_fmtp(pool, local_m, si->fmt.pt, &si->param->setting.dec_fmtp);
+
+    /* 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
@@ -520,10 +555,4 @@
 	return status;
 
-    /* Set fmtp mode for both local and remote */
-    if (local_fmtp_mode != 0)
-	si->param->setting.dec_fmtp_mode = (pj_int8_t)local_fmtp_mode;
-    if (rem_fmtp_mode != 0)
-	si->param->setting.enc_fmtp_mode = (pj_int8_t)rem_fmtp_mode;
-
 
     /* Get incomming payload type for telephone-events */
Index: pjproject/trunk/pjsip-apps/src/test-pjsua/scripts-sendto/400_fmtp_g7221_with_bitrate.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/scripts-sendto/400_fmtp_g7221_with_bitrate.py	(revision 2236)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/scripts-sendto/400_fmtp_g7221_with_bitrate.py	(revision 2236)
@@ -0,0 +1,34 @@
+# $Id $
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Answer for codec G722.1 should contain fmtp bitrate
+
+sdp = \
+"""
+v=0
+o=- 3428650655 3428650655 IN IP4 192.168.1.9
+s=pjmedia
+c=IN IP4 192.168.1.9
+t=0 0
+a=X-nat:0
+m=audio 4000 RTP/AVP 99 100 101
+a=rtcp:4001 IN IP4 192.168.1.9
+a=rtpmap:99 G7221/16000
+a=fmtp:99 bitrate=24000
+a=rtpmap:100 G7221/16000
+a=fmtp:100 bitrate=32000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+"""
+
+pjsua_args = "--null-audio --auto-answer 200 --add-codec G7221"
+extra_headers = ""
+include = ["fmtp:[\d]+ bitrate="]	# response must include fmtp bitrate
+exclude = []
+
+sendto_cfg = sip.SendtoCfg("Answer should contain fmtp bitrate for codec G722.1", pjsua_args, sdp, 200,
+			   extra_headers=extra_headers,
+			   resp_inc=include, resp_exc=exclude) 
+
Index: pjproject/trunk/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_24000.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_24000.py	(revision 2236)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_24000.py	(revision 2236)
@@ -0,0 +1,35 @@
+# $Id $
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Answer with codec G722.1 should choose the same bitrate
+# which in this test is 24000
+
+sdp = \
+"""
+v=0
+o=- 3428650655 3428650655 IN IP4 192.168.1.9
+s=pjmedia
+c=IN IP4 192.168.1.9
+t=0 0
+a=X-nat:0
+m=audio 4000 RTP/AVP 100 101
+a=rtcp:4001 IN IP4 192.168.1.9
+a=rtpmap:100 G7221/16000
+a=fmtp:100 bitrate=24000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+"""
+
+pjsua_args = "--null-audio --auto-answer 200 --add-codec G7221"
+extra_headers = ""
+include = ["a=rtpmap:[\d]+ G7221/16000",  # response must choose G722.1
+	   "fmtp:[\d]+ bitrate=24000"	  # response must choose the same bitrate
+	  ]
+exclude = []
+
+sendto_cfg = sip.SendtoCfg("Answer with G722.1 should choose bitrate 24000", pjsua_args, sdp, 200,
+			   extra_headers=extra_headers,
+			   resp_inc=include, resp_exc=exclude) 
+
Index: pjproject/trunk/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_32000.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_32000.py	(revision 2236)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_32000.py	(revision 2236)
@@ -0,0 +1,35 @@
+# $Id $
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Answer with codec G722.1 should choose the same bitrate
+# which in this test is 32000
+
+sdp = \
+"""
+v=0
+o=- 3428650655 3428650655 IN IP4 192.168.1.9
+s=pjmedia
+c=IN IP4 192.168.1.9
+t=0 0
+a=X-nat:0
+m=audio 4000 RTP/AVP 100 101
+a=rtcp:4001 IN IP4 192.168.1.9
+a=rtpmap:100 G7221/16000
+a=fmtp:100 bitrate=32000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+"""
+
+pjsua_args = "--null-audio --auto-answer 200 --add-codec G7221"
+extra_headers = ""
+include = ["a=rtpmap:[\d]+ G7221/16000",  # response must choose G722.1
+	   "fmtp:[\d]+ bitrate=32000"	  # response must choose the same bitrate
+	  ]
+exclude = []
+
+sendto_cfg = sip.SendtoCfg("Answer with G722.1 should choose bitrate 32000", pjsua_args, sdp, 200,
+			   extra_headers=extra_headers,
+			   resp_inc=include, resp_exc=exclude) 
+
