Index: /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia-codec/types.h
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia-codec/types.h	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia-codec/types.h	(revision 3500)
@@ -108,4 +108,8 @@
      PJMEDIA_RTP_PT_H263P,
      PJMEDIA_RTP_PT_H264,
+     PJMEDIA_RTP_PT_H264_RSV1,
+     PJMEDIA_RTP_PT_H264_RSV2,
+     PJMEDIA_RTP_PT_H264_RSV3,
+     PJMEDIA_RTP_PT_H264_RSV4,
 
      /* Caution!
Index: /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/config.h
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/config.h	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/config.h	(revision 3500)
@@ -603,4 +603,13 @@
 
 /**
+ * This specifies the maximum number of the customized SDP format
+ * negotiation callbacks.
+ */
+#ifndef PJMEDIA_SDP_NEG_MAX_CUSTOM_FMT_NEG_CB
+#   define PJMEDIA_SDP_NEG_MAX_CUSTOM_FMT_NEG_CB	8
+#endif
+
+
+/**
  * Support for sending and decoding RTCP port in SDP (RFC 3605).
  * Default is equal to PJMEDIA_ADVERTISE_RTCP setting.
Index: /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/sdp_neg.h
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/sdp_neg.h	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/sdp_neg.h	(revision 3500)
@@ -664,4 +664,93 @@
 
 
+/**
+ * Enumeration of customized SDP format matching option flags. See
+ * #pjmedia_sdp_neg_register_fmt_match_cb() for more info.
+ */
+typedef enum pjmedia_sdp_neg_fmt_match_flag
+{
+    /**
+     * In generating answer, the SDP fmtp in the answer candidate may need
+     * to be modified by the customized SDP format matching callback to
+     * achieve flexible SDP negotiation, e.g: AMR fmtp 'octet-align' field
+     * can be adjusted with the offer when the codec implementation support
+     * both packetization modes octet-aligned and bandwidth-efficient.
+     */
+    PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER = 1,
+
+} pjmedia_sdp_neg_fmt_match_flag;
+
+
+/**
+ * The declaration of customized SDP format matching callback. See
+ * #pjmedia_sdp_neg_register_fmt_match_cb() for more info.
+ *
+ * @param pool		The memory pool.
+ * @param offer		The SDP media offer.
+ * @param o_fmt_idx	Index of the format in the SDP media offer.
+ * @param answer	The SDP media answer.
+ * @param a_fmt_idx	Index of the format in the SDP media answer.
+ * @param option	The format matching option, see
+ *			#pjmedia_sdp_neg_fmt_match_flag.
+ *
+ * @return		PJ_SUCCESS when the formats in offer and answer match.
+ */
+typedef pj_status_t (*pjmedia_sdp_neg_fmt_match_cb)(pj_pool_t *pool,
+						    pjmedia_sdp_media *offer,
+						    unsigned o_fmt_idx,
+						    pjmedia_sdp_media *answer,
+						    unsigned a_fmt_idx,
+						    unsigned option);
+
+
+/**
+ * Register customized SDP format matching callback function for the specified
+ * format. The customized SDP format matching is needed when the format
+ * identification in a media stream session cannot be simply determined by
+ * encoding name and clock rate, but also involves one or more format specific
+ * parameters, which are specified in SDP fmtp attribute. For example,
+ * an H.264 video stream is also identified by profile, level, and
+ * packetization-mode parameters. As those parameters are format specifics,
+ * the negotiation must be done by the format or codec implementation.
+ *
+ * To unregister the callback of specific format, just call this function with
+ * parameter #cb set to NULL.
+ *
+ * @param fmt_name	The format name, e.g: "H.264", "AMR", "G7221". Note
+ *			that the string buffer must remain valid until the
+ *			callback is unregistered.
+ * @param cb		The customized SDP format negotiation callback or
+ *			NULL to unregister the specified format callback.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_neg_register_fmt_match_cb(
+					const pj_str_t *fmt_name,
+					pjmedia_sdp_neg_fmt_match_cb cb);
+
+
+/**
+ * Match format in the SDP media offer and answer. The matching mechanism
+ * will be done by comparing the encoding name and clock rate, and if the
+ * custom format matching callback for the specified format is registered,
+ * see #pjmedia_sdp_neg_register_fmt_match_cb(), it will be called for more
+ * detail verification, e.g: format parameters specified in SDP fmtp.
+ *
+ * @param pool		The memory pool.
+ * @param offer		The SDP media offer.
+ * @param o_fmt_idx	Index of the format in the SDP media offer.
+ * @param answer	The SDP media answer.
+ * @param a_fmt_idx	Index of the format in the SDP media answer.
+ * @param option	The format matching option, see
+ *			#pjmedia_sdp_neg_fmt_match_flag.
+ *
+ * @return		PJ_SUCCESS when the formats in offer and answer match.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_neg_fmt_match( pj_pool_t *pool,
+					        pjmedia_sdp_media *offer,
+					        unsigned o_fmt_idx,
+					        pjmedia_sdp_media *answer,
+					        unsigned a_fmt_idx,
+					        unsigned option);
 
 
Index: /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/stream_common.h
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/stream_common.h	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/stream_common.h	(revision 3500)
@@ -33,8 +33,10 @@
 
 /**
- * This is internal function for parsing SDP format parameter of specific format
- * or payload type, used by stream in generating stream info from SDP.
+ * This is internal function for parsing SDP format parameter of specific
+ * format or payload type, used by stream in generating stream info from SDP.
  *
- * @param pool		Pool to allocate memory.
+ * @param pool		Pool to allocate memory, if pool is NULL, the fmtp
+ *			string pointers will point to the original string in
+ *			the SDP media descriptor.
  * @param m		The SDP media containing the format parameter to
  *			be parsed.
Index: /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/vid_codec.h
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/vid_codec.h	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/vid_codec.h	(revision 3500)
@@ -45,7 +45,7 @@
 {
     pjmedia_format_id   fmt_id;         /**< Encoded format ID              */
+    unsigned            pt;             /**< Payload type		    */
     pj_str_t	        encoding_name;  /**< Encoding name                  */
-    unsigned            pt;             /**< Payload type (may be 255 for
-                                             dynamic payload type)          */
+    pj_str_t	        encoding_desc;	/**< Encoding desc		    */
     unsigned            clock_rate;     /**< Clock rate			    */
     pjmedia_dir         dir;            /**< Direction                      */
@@ -535,9 +535,10 @@
 
 /**
- * Get codec info for the specified static payload type.
- *
- * @param mgr	    The codec manager instance. If NULL, the default codec
- *		    manager instance will be used.
- * @param pt	    Static payload type/number.
+ * Get codec info for the specified payload type. The payload type must be
+ * static or locally defined in #pjmedia_video_pt.
+ *
+ * @param mgr	    The codec manager instance. If NULL, the default codec
+ *		    manager instance will be used.
+ * @param pt	    The payload type/number.
  * @param info	    Pointer to receive codec info.
  *
Index: /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/vid_codec_util.h
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/vid_codec_util.h	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/include/pjmedia/vid_codec_util.h	(revision 3500)
@@ -28,5 +28,5 @@
 
 #include <pjmedia/vid_codec.h>
-
+#include <pjmedia/sdp_neg.h>
 
 PJ_BEGIN_DECL
@@ -108,9 +108,34 @@
  * @return		PJ_SUCCESS on success.
  */
-PJ_DECL(pj_status_t) pjmedia_vid_codec_parse_h264_fmtp(
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp(
 				const pjmedia_codec_fmtp *fmtp,
 				pjmedia_vid_codec_h264_fmtp *h264_fmtp);
 
 
+/**
+ * Match H.264 format in the SDP media offer and answer. This will compare
+ * H.264 identifier parameters in SDP fmtp, i.e: "profile-level-id" and
+ * "packetization-mode" fields. For better interoperability, when the option
+ * #PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER is set, this function
+ * may update the answer so the parameters in the answer match to ones
+ * in the offer.
+ *
+ * @param pool		The memory pool.
+ * @param offer		The SDP media offer.
+ * @param o_fmt_idx	Index of the H.264 format in the SDP media offer.
+ * @param answer	The SDP media answer.
+ * @param a_fmt_idx	Index of the H.264 format in the SDP media answer.
+ * @param option	The format matching option, see
+ *			#pjmedia_sdp_neg_fmt_match_flag.
+ *
+ * @return		PJ_SUCCESS when the formats in offer and answer match.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_codec_h264_match_sdp(
+						pj_pool_t *pool,
+						pjmedia_sdp_media *offer,
+						unsigned o_fmt_idx,
+						pjmedia_sdp_media *answer,
+						unsigned a_fmt_idx,
+						unsigned option);
 
 
Index: /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia-codec/ffmpeg_codecs.c	(revision 3500)
@@ -176,8 +176,17 @@
 		      pj_size_t bits_len, unsigned *bits_pos)
 
+#define FUNC_FMT_MATCH(name) \
+    pj_status_t(name)(pj_pool_t *pool, \
+		      pjmedia_sdp_media *offer, unsigned o_fmt_idx, \
+		      pjmedia_sdp_media *answer, unsigned a_fmt_idx, \
+		      unsigned option)
+
+
+/* Type definition of codec specific functions */
 typedef FUNC_PACKETIZE(*func_packetize);
 typedef FUNC_UNPACKETIZE(*func_unpacketize);
 typedef pj_status_t (*func_preopen)	(ffmpeg_private *ff);
 typedef pj_status_t (*func_postopen)	(ffmpeg_private *ff);
+typedef FUNC_FMT_MATCH(*func_sdp_fmt_match);
 
 
@@ -187,5 +196,10 @@
     /* Predefined info */
     pjmedia_vid_codec_info       info;
-    pjmedia_format_id		 base_fmt_id;
+    pjmedia_format_id		 base_fmt_id;	/**< Some codecs may be exactly
+						     same or compatible with
+						     another codec, base format
+						     will tell the initializer
+						     to copy this codec desc
+						     from its base format   */
     pj_uint32_t			 avg_bps;
     pj_uint32_t			 max_bps;
@@ -194,4 +208,5 @@
     func_preopen		 preopen;
     func_preopen		 postopen;
+    func_sdp_fmt_match		 sdp_fmt_match;
     pjmedia_codec_fmtp		 dec_fmtp;
 
@@ -217,32 +232,43 @@
 {
     {
-	{PJMEDIA_FORMAT_H264,	{"H264",4},	    PJMEDIA_RTP_PT_H264},
-	0,	500000,    1000000,
+	{PJMEDIA_FORMAT_H264, PJMEDIA_RTP_PT_H264, {"H264",4},
+	 {"Constrained Baseline (level=30, pack=1)", 39}},
+	0,	128000,    1000000,
 	&h264_packetize, &h264_unpacketize, &h264_preopen, &h264_postopen,
+	&pjmedia_vid_codec_h264_match_sdp,
 	/* Leading space for better compatibility (strange indeed!) */
-	{2, { {{" profile-level-id",17},    {"42e01e",6}}, 
+	{2, { {{"profile-level-id",16},    {"42e01e",6}}, 
 	      {{" packetization-mode",19},  {"1",1}}, } },
     },
     {
-	{PJMEDIA_FORMAT_H263P,	{"H263-1998",9},    PJMEDIA_RTP_PT_H263P},
+	{PJMEDIA_FORMAT_H264, PJMEDIA_RTP_PT_H264_RSV1, {"H264",4},
+	 {"Baseline (level=30, pack=1)", 27}},
+	PJMEDIA_FORMAT_H264,	128000,    1000000,
+	&h264_packetize, &h264_unpacketize, &h264_preopen, &h264_postopen,
+	&pjmedia_vid_codec_h264_match_sdp,
+	{2, { {{"profile-level-id",16},    {"42001e",6}}, 
+	      {{" packetization-mode",19},  {"1",1}}, } },
+    },
+    {
+	{PJMEDIA_FORMAT_H263P, PJMEDIA_RTP_PT_H263P, {"H263-1998",9}},
 	PJMEDIA_FORMAT_H263,	1000000,    2000000,
-	&h263_packetize, &h263_unpacketize, &h263_preopen, NULL,
+	&h263_packetize, &h263_unpacketize, &h263_preopen, NULL, NULL,
 	{2, { {{"CIF",3},   {"1",1}}, 
 	      {{"QCIF",4},  {"1",1}}, } },
     },
     {
-	{PJMEDIA_FORMAT_H263,	{"H263",4},	    PJMEDIA_RTP_PT_H263},
+	{PJMEDIA_FORMAT_H263,	PJMEDIA_RTP_PT_H263,	{"H263",4}},
     },
     {
-	{PJMEDIA_FORMAT_H261,	{"H261",4},	    PJMEDIA_RTP_PT_H261},
+	{PJMEDIA_FORMAT_H261,	PJMEDIA_RTP_PT_H261,	{"H261",4}},
     },
     {
-	{PJMEDIA_FORMAT_MJPEG,	{"JPEG",4},	    PJMEDIA_RTP_PT_JPEG},
+	{PJMEDIA_FORMAT_MJPEG,	PJMEDIA_RTP_PT_JPEG,	{"JPEG",4}},
     },
     {
-	{PJMEDIA_FORMAT_MPEG4,	{"MP4V",4}},
+	{PJMEDIA_FORMAT_MPEG4,	0,			{"MP4V",4}},
     },
     {
-	{PJMEDIA_FORMAT_XVID,	{"XVID",4}},
+	{PJMEDIA_FORMAT_XVID,	0,			{"XVID",4}},
 	PJMEDIA_FORMAT_MPEG4,
     },
@@ -266,7 +292,19 @@
     ff->data = data;
 
+    /* Parse remote fmtp */
+    status = pjmedia_vid_codec_h264_parse_fmtp(&ff->param.enc_fmtp,
+					       &data->fmtp);
+    if (status != PJ_SUCCESS)
+	return status;
+
     /* Create packetizer */
     pktz_cfg.mtu = ff->param.enc_mtu;
-    pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED;
+    if (data->fmtp.packetization_mode == 0)
+	pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_SINGLE_NAL;
+    else if (data->fmtp.packetization_mode == 1)
+	pktz_cfg.mode = PJMEDIA_H264_PACKETIZER_MODE_NON_INTERLEAVED;
+    else
+	return PJ_ENOTSUP;
+
     status = pjmedia_h264_packetizer_create(ff->pool, &pktz_cfg, &data->pktz);
     if (status != PJ_SUCCESS)
@@ -276,10 +314,24 @@
 	AVCodecContext *ctx = ff->enc_ctx;
 
-	status = pjmedia_vid_codec_parse_h264_fmtp(&ff->param.enc_fmtp,
-						   &data->fmtp);
-	if (status != PJ_SUCCESS)
-	    return status;
-
+	/* Apply profile. Note that, for x264 backend, ffmpeg doesn't seem to
+	 * use this profile param field, so let's try to apply it "manually".
+	 */
 	ctx->profile  = data->fmtp.profile_idc;
+	if (ctx->profile == FF_PROFILE_H264_BASELINE) {
+	    /* Baseline profile settings (the most used profile in
+	     * conversational/real-time communications).
+	     */
+	    ctx->coder_type = FF_CODER_TYPE_VLC;
+	    ctx->max_b_frames = 0;
+	    ctx->flags2 &= ~(CODEC_FLAG2_WPRED | CODEC_FLAG2_8X8DCT);
+	    ctx->weighted_p_pred = 0;
+	} else if (ctx->profile == FF_PROFILE_H264_MAIN) {
+	    ctx->flags2 &= ~CODEC_FLAG2_8X8DCT;
+	}
+
+	/* Apply profile constraint bits. */
+	// The x264 doesn't seem to support non-constrained (baseline) profile
+	// so this shouldn't be a problem (for now).
+	//PJ_TODO(set_h264_constraint_bits_properly_in_ffmpeg);
 	if (data->fmtp.profile_iop) {
 #if defined(FF_PROFILE_H264_CONSTRAINED)
@@ -287,6 +339,8 @@
 #endif
 	}
+
+	/* Apply profile level. */
+	PJ_TODO(apply_h264_profile_level_in_pjmedia_vid_codec_param);
 	ctx->level    = data->fmtp.level;
-	PJ_TODO(set_h264_constrain_bits_properly_in_ffmpeg);
 
 	/* Libx264 rejects the "broken" ffmpeg defaults, so just change some */
@@ -383,5 +437,5 @@
 	    (desc->info.fmt_id == info->fmt_id) &&
             ((desc->info.dir & info->dir) == info->dir) &&
-            pj_stricmp(&desc->info.encoding_name, &info->encoding_name)==0)
+	    (desc->info.pt == info->pt))
         {
             return desc;
@@ -566,8 +620,11 @@
     }
 
-    /* Init unassigned encoder/decoder description from base codec */
+    /* Review all codecs for applying base format, registering format match for
+     * SDP negotiation, etc.
+     */
     for (i = 0; i < PJ_ARRAY_SIZE(codec_desc); ++i) {
 	ffmpeg_codec_desc *desc = &codec_desc[i];
 
+	/* Init encoder/decoder description from base format */
 	if (desc->base_fmt_id && (!desc->dec || !desc->enc)) {
 	    ffmpeg_codec_desc *base_desc = NULL;
@@ -606,4 +663,6 @@
 	    desc->info.dir |= copied_dir;
 	    desc->enabled = (desc->info.dir != PJMEDIA_DIR_NONE);
+	    desc->info.has_rtp_pack = (desc->packetize != NULL) &&
+				      (desc->unpacketize != NULL);
 
 	    if (copied_dir != PJMEDIA_DIR_NONE) {
@@ -617,4 +676,12 @@
 	    }
         }
+
+	/* Registering format match for SDP negotiation */
+	if (desc->sdp_fmt_match) {
+	    status = pjmedia_sdp_neg_register_fmt_match_cb(
+						&desc->info.encoding_name,
+						desc->sdp_fmt_match);
+	    pj_assert(status == PJ_SUCCESS);
+	}
     }
 
Index: /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/sdp_neg.c
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/sdp_neg.c	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/sdp_neg.c	(revision 3500)
@@ -71,4 +71,30 @@
 
 
+/* Definition of customized SDP format negotiation callback */
+struct fmt_match_cb_t
+{
+    pj_str_t			    fmt_name;
+    pjmedia_sdp_neg_fmt_match_cb    cb;
+};
+
+/* Number of registered customized SDP format negotiation callbacks */
+static unsigned fmt_match_cb_cnt;
+
+/* The registered customized SDP format negotiation callbacks */
+static struct fmt_match_cb_t 
+	      fmt_match_cb[PJMEDIA_SDP_NEG_MAX_CUSTOM_FMT_NEG_CB];
+
+/* Redefining a very long identifier name, just for convenience */
+#define ALLOW_MODIFY_ANSWER PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER
+
+static pj_status_t custom_fmt_match( pj_pool_t *pool,
+				   const pj_str_t *fmt_name,
+				   pjmedia_sdp_media *offer,
+				   unsigned o_fmt_idx,
+				   pjmedia_sdp_media *answer,
+				   unsigned a_fmt_idx,
+				   unsigned option);
+
+
 /*
  * Get string representation of negotiator state.
@@ -683,47 +709,4 @@
 
 
-/* Matching H.264 between offer and answer. */
-static pj_bool_t match_h264( const pjmedia_sdp_media *offer,
-			     unsigned o_fmt_idx,
-			     const pjmedia_sdp_media *answer,
-			     unsigned a_fmt_idx)
-{
-    const pjmedia_sdp_attr *attr_ans;
-    const pjmedia_sdp_attr *attr_ofr;
-    pjmedia_sdp_fmtp fmtp;
-    pj_uint32_t profile1, profile2;
-    unsigned pack_mode1, pack_mode2;
-    const pj_str_t STR_PROFILE   = {"profile-level-id=", 17};
-    const pj_str_t STR_PACK_MODE = {"packetization-mode=", 19};
-
-    /* Parse offer */
-    attr_ofr = pjmedia_sdp_media_find_attr2(offer, "fmtp", 
-					    &offer->desc.fmt[o_fmt_idx]);
-    if (!attr_ofr)
-	return PJ_FALSE;
-
-    if (pjmedia_sdp_attr_get_fmtp(attr_ofr, &fmtp) != PJ_SUCCESS)
-	return PJ_FALSE;
-
-    GET_FMTP_IVAL_BASE(profile1, 16, fmtp, STR_PROFILE, 0x42000A);
-    GET_FMTP_IVAL(pack_mode1, fmtp, STR_PACK_MODE, 0);
-
-    /* Parse answer */
-    attr_ans = pjmedia_sdp_media_find_attr2(answer, "fmtp", 
-					    &answer->desc.fmt[a_fmt_idx]);
-    if (!attr_ans)
-	return PJ_FALSE;
-
-    if (pjmedia_sdp_attr_get_fmtp(attr_ans, &fmtp) != PJ_SUCCESS)
-	return PJ_FALSE;
-
-    GET_FMTP_IVAL_BASE(profile2, 16, fmtp, STR_PROFILE, 0x42000A);
-    GET_FMTP_IVAL(pack_mode2, fmtp, STR_PACK_MODE, 0);
-
-    /* Compare bitrate in answer and offer. */
-    return ((profile1 == profile2) && (pack_mode1 == pack_mode2));
-}
-
-
 /* Toggle AMR octet-align setting in the fmtp.
  */
@@ -929,4 +912,5 @@
 				    break;
 			    } else
+
 			    /* Further check for AMR, negotiate fmtp. */
 			    if (pj_stricmp2(&or_.enc_name, "AMR") == 0 ||
@@ -937,10 +921,10 @@
 				    break;
 			    } else
-			    /* Further check for H264, negotiate fmtp. */
-			    if (pj_stricmp2(&or_.enc_name, "H264") == 0)
+			    
+			    /* Call custom format matching callbacks */
+			    if (custom_fmt_match(pool, &or_.enc_name,
+						 offer, i, answer, j, 0) ==
+				PJ_SUCCESS)
 			    {
-				if (match_h264(offer, i, answer, j))
-				    break;
-			    } else {
 				/* Match! */
 				break;
@@ -1236,4 +1220,22 @@
 			    /* Match! */
 			    if (is_codec) {
+				pjmedia_sdp_media *o, *a;
+				unsigned o_fmt_idx, a_fmt_idx;
+
+				o = (pjmedia_sdp_media*)offer;
+				a = (pjmedia_sdp_media*)preanswer;
+				o_fmt_idx = prefer_remote_codec_order? i:j;
+				a_fmt_idx = prefer_remote_codec_order? j:i;
+
+				/* Call custom format matching callbacks */
+				if (custom_fmt_match(pool, &or_.enc_name,
+						     o, o_fmt_idx,
+						     a, a_fmt_idx,
+						     ALLOW_MODIFY_ANSWER) !=
+				    PJ_SUCCESS)
+				{
+				    continue;
+				} else
+
 				/* Further check for G7221, negotiate bitrate */
 				if (pj_stricmp2(&or_.enc_name, "G7221") == 0 &&
@@ -1241,5 +1243,6 @@
 				{
 				    continue;
-				} else 
+				} else
+
 				/* Further check for AMR, negotiate fmtp */
 				if (pj_stricmp2(&or_.enc_name, "AMR")==0 ||
@@ -1254,10 +1257,6 @@
 						   PJ_TRUE, &pt_amr_need_adapt))
 					continue;
-				} else
-				if (pj_stricmp2(&or_.enc_name, "H264") == 0 &&
-				    !match_h264(master, i, slave, j))
-				{
-				    continue;
 				}
+
 				found_matching_codec = 1;
 			    } else {
@@ -1533,2 +1532,133 @@
 }
 
+
+static pj_status_t custom_fmt_match(pj_pool_t *pool,
+				    const pj_str_t *fmt_name,
+				    pjmedia_sdp_media *offer,
+				    unsigned o_fmt_idx,
+				    pjmedia_sdp_media *answer,
+				    unsigned a_fmt_idx,
+				    unsigned option)
+{
+    unsigned i;
+
+    for (i = 0; i < fmt_match_cb_cnt; ++i) {
+	if (pj_stricmp(fmt_name, &fmt_match_cb[i].fmt_name) == 0) {
+	    pj_assert(fmt_match_cb[i].cb);
+	    return (*fmt_match_cb[i].cb)(pool, offer, o_fmt_idx,
+					 answer, a_fmt_idx,
+					 option);
+	}
+    }
+
+    /* Not customized format matching found, should be matched */
+    return PJ_SUCCESS;
+}
+
+/* Register customized SDP format negotiation callback function. */
+PJ_DECL(pj_status_t) pjmedia_sdp_neg_register_fmt_match_cb(
+					const pj_str_t *fmt_name,
+					pjmedia_sdp_neg_fmt_match_cb cb)
+{
+    struct fmt_match_cb_t *f = NULL;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(fmt_name, PJ_EINVAL);
+
+    /* Check if the callback for the format name has been registered */
+    for (i = 0; i < fmt_match_cb_cnt; ++i) {
+	if (pj_stricmp(fmt_name, &fmt_match_cb[i].fmt_name) == 0)
+	    break;
+    }
+
+    /* Unregistration */
+    
+    if (cb == NULL) {
+	if (i == fmt_match_cb_cnt)
+	    return PJ_ENOTFOUND;
+
+	pj_array_erase(fmt_match_cb, sizeof(fmt_match_cb[0]),
+		       fmt_match_cb_cnt, i);
+	fmt_match_cb_cnt--;
+
+	return PJ_SUCCESS;
+    }
+
+    /* Registration */
+
+    if (i < fmt_match_cb_cnt) {
+	/* The same format name has been registered before */
+	if (cb != fmt_match_cb[i].cb)
+	    return PJ_EEXISTS;
+	else
+	    return PJ_SUCCESS;
+    }
+
+    if (fmt_match_cb_cnt >= PJ_ARRAY_SIZE(fmt_match_cb))
+	return PJ_ETOOMANY;
+
+    f = &fmt_match_cb[fmt_match_cb_cnt++];
+    f->fmt_name = *fmt_name;
+    f->cb = cb;
+
+    return PJ_SUCCESS;
+}
+
+
+/* Match format in the SDP media offer and answer. */
+PJ_DEF(pj_bool_t) pjmedia_sdp_neg_fmt_match( pj_pool_t *pool,
+					     pjmedia_sdp_media *offer,
+					     unsigned o_fmt_idx,
+					     pjmedia_sdp_media *answer,
+					     unsigned a_fmt_idx,
+					     unsigned option)
+{
+    const pjmedia_sdp_attr *attr;
+    pjmedia_sdp_rtpmap o_rtpmap, a_rtpmap;
+
+    /* Get the format rtpmap from the offer. */
+    attr = pjmedia_sdp_media_find_attr2(offer, "rtpmap", 
+					&offer->desc.fmt[o_fmt_idx]);
+    if (!attr) {
+	pj_assert(!"Bug! Offer haven't been validated");
+	return PJ_EBUG;
+    }
+    pjmedia_sdp_attr_get_rtpmap(attr, &o_rtpmap);
+
+    /* Get the format rtpmap from the answer. */
+    attr = pjmedia_sdp_media_find_attr2(answer, "rtpmap", 
+					&answer->desc.fmt[a_fmt_idx]);
+    if (!attr) {
+	pj_assert(!"Bug! Answer haven't been validated");
+	return PJ_EBUG;
+    }
+    pjmedia_sdp_attr_get_rtpmap(attr, &a_rtpmap);
+
+    if (pj_stricmp(&o_rtpmap.enc_name, &a_rtpmap.enc_name) != 0 ||
+	o_rtpmap.clock_rate != a_rtpmap.clock_rate)
+    {
+	return PJMEDIA_SDP_EFORMATNOTEQUAL;
+    }
+
+    /* Further check for G7221, negotiate bitrate. */
+    if (pj_stricmp2(&o_rtpmap.enc_name, "G7221") == 0) {
+	if (match_g7221(offer, o_fmt_idx, answer, a_fmt_idx))
+	    return PJ_SUCCESS;
+	else
+	    return PJMEDIA_SDP_EFORMATNOTEQUAL;
+    } else
+    /* Further check for AMR, negotiate fmtp. */
+    if (pj_stricmp2(&o_rtpmap.enc_name, "AMR") == 0 ||
+	pj_stricmp2(&o_rtpmap.enc_name, "AMR-WB") == 0) 
+    {
+	if (match_amr(offer, o_fmt_idx, answer, a_fmt_idx, PJ_FALSE, NULL))
+	    return PJ_SUCCESS;
+	else
+	    return PJMEDIA_SDP_EFORMATNOTEQUAL;
+    }
+    PJ_TODO(replace_hardcoded_fmt_match_in_sdp_neg_with_custom_fmt_match_cb);
+    
+    return custom_fmt_match(pool, &o_rtpmap.enc_name,
+			    offer, o_fmt_idx, answer, a_fmt_idx, option);
+}
+
Index: /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/stream.c
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/stream.c	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/stream.c	(revision 3500)
@@ -2948,5 +2948,5 @@
 
     /* Get local fmtp for our decoder. */
-    pjmedia_stream_info_parse_fmtp(pool, local_m, si->fmt.pt, 
+    pjmedia_stream_info_parse_fmtp(pool, local_m, si->fmt.pt,
 				   &si->param->setting.dec_fmtp);
 
Index: /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/stream_common.c
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/stream_common.c	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/stream_common.c	(revision 3500)
@@ -87,6 +87,10 @@
 	/* Store token */
 	if (end > start) {
-	    token = (char*)pj_pool_alloc(pool, end - start);
-	    pj_ansi_strncpy(token, start, end - start);
+	    if (pool) {
+		token = (char*)pj_pool_alloc(pool, end - start);
+		pj_ansi_strncpy(token, start, end - start);
+	    } else {
+		token = start;
+	    }
 	    if (*p == '=')
 		/* Got param name */
Index: /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/vid_codec.c
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/vid_codec.c	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/vid_codec.c	(revision 3500)
@@ -307,5 +307,5 @@
 
 /*
- * Get codec info for static payload type.
+ * Get codec info for the specified payload type.
  */
 PJ_DEF(pj_status_t) pjmedia_vid_codec_mgr_get_codec_info(
@@ -316,5 +316,5 @@
     unsigned i;
 
-    PJ_ASSERT_RETURN(p_info && pt>=0 && pt < 96, PJ_EINVAL);
+    PJ_ASSERT_RETURN(p_info, PJ_EINVAL);
 
     if (!mgr) mgr = def_vid_codec_mgr;
@@ -369,5 +369,5 @@
 /*
  * Convert codec info struct into a unique codec identifier.
- * A codec identifier looks something like "L16/44100/2".
+ * A codec identifier looks something like "H263/34".
  */
 PJ_DEF(char*) pjmedia_vid_codec_info_to_id(
@@ -382,5 +382,5 @@
 			   (int)info->encoding_name.slen,
 			   info->encoding_name.ptr,
-			   info->clock_rate);
+			   info->pt);
 
     if (len < 1 || len >= (int)max_len) {
Index: /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/vid_codec_util.c
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/vid_codec_util.c	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/vid_codec_util.c	(revision 3500)
@@ -20,5 +20,7 @@
 #include <pjmedia/vid_codec_util.h>
 #include <pjmedia/errno.h>
+#include <pjmedia/stream_common.h>
 #include <pjlib-util/base64.h>
+#include <pj/ctype.h>
 #include <pj/math.h>
 
@@ -259,5 +261,5 @@
 
 /* H264 fmtp parser */
-PJ_DEF(pj_status_t) pjmedia_vid_codec_parse_h264_fmtp(
+PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_parse_fmtp(
 				    const pjmedia_codec_fmtp *fmtp,
 				    pjmedia_vid_codec_h264_fmtp *h264_fmtp)
@@ -289,23 +291,26 @@
 	    h264_fmtp->profile_idc = (pj_uint8_t)((tmp >> 16) & 0xFF);
 	    h264_fmtp->profile_iop = (pj_uint8_t)((tmp >> 8) & 0xFF);
-	    h264_fmtp->profile_idc = (pj_uint8_t)(tmp & 0xFF);
+	    h264_fmtp->level = (pj_uint8_t)(tmp & 0xFF);
 	} else if (pj_stricmp(&fmtp->param[i].name, &PACKETIZATION_MODE)==0) {
 	    tmp = pj_strtoul(&fmtp->param[i].val);
-	    if (tmp) h264_fmtp->max_br = tmp;
+	    if (tmp >= 0 && tmp <= 2) 
+		h264_fmtp->packetization_mode = (pj_uint8_t)tmp;
+	    else
+		return PJMEDIA_SDP_EINFMTP;
 	} else if (pj_stricmp(&fmtp->param[i].name, &MAX_MBPS)==0) {
 	    tmp = pj_strtoul(&fmtp->param[i].val);
-	    if (tmp) h264_fmtp->max_mbps = tmp;
+	    h264_fmtp->max_mbps = tmp;
 	} else if (pj_stricmp(&fmtp->param[i].name, &MAX_FS)==0) {
 	    tmp = pj_strtoul(&fmtp->param[i].val);
-	    if (tmp) h264_fmtp->max_fs = tmp;
+	    h264_fmtp->max_fs = tmp;
 	} else if (pj_stricmp(&fmtp->param[i].name, &MAX_CPB)==0) {
 	    tmp = pj_strtoul(&fmtp->param[i].val);
-	    if (tmp) h264_fmtp->max_cpb = tmp;
+	    h264_fmtp->max_cpb = tmp;
 	} else if (pj_stricmp(&fmtp->param[i].name, &MAX_DPB)==0) {
 	    tmp = pj_strtoul(&fmtp->param[i].val);
-	    if (tmp) h264_fmtp->max_dpb = tmp;
+	    h264_fmtp->max_dpb = tmp;
 	} else if (pj_stricmp(&fmtp->param[i].name, &MAX_BR)==0) {
 	    tmp = pj_strtoul(&fmtp->param[i].val);
-	    if (tmp) h264_fmtp->max_br = tmp;
+	    h264_fmtp->max_br = tmp;
 	} else if (pj_stricmp(&fmtp->param[i].name, &SPROP_PARAMETER_SETS)==0)
 	{
@@ -351,4 +356,105 @@
     }
 
+    /* When profile-level-id is not specified, use default value "42000A" */
+    if (h264_fmtp->profile_idc == 0) {
+	h264_fmtp->profile_idc = 0x42;
+	h264_fmtp->profile_iop = 0x00;
+	h264_fmtp->level = 0x0A;
+    }
+
     return PJ_SUCCESS;
 }
+
+PJ_DEF(pj_status_t) pjmedia_vid_codec_h264_match_sdp(pj_pool_t *pool,
+						     pjmedia_sdp_media *offer,
+						     unsigned o_fmt_idx,
+						     pjmedia_sdp_media *answer,
+						     unsigned a_fmt_idx,
+						     unsigned option)
+{
+    const pj_str_t PROFILE_LEVEL_ID	= {"profile-level-id", 16};
+    const pj_str_t PACKETIZATION_MODE	= {"packetization-mode", 18};
+    pjmedia_codec_fmtp o_fmtp_raw, a_fmtp_raw;
+    pjmedia_vid_codec_h264_fmtp o_fmtp, a_fmtp;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(pool);
+
+    /* Parse offer */
+    status = pjmedia_stream_info_parse_fmtp(
+				    NULL, offer, 
+				    pj_strtoul(&offer->desc.fmt[o_fmt_idx]),
+				    &o_fmtp_raw);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pjmedia_vid_codec_h264_parse_fmtp(&o_fmtp_raw, &o_fmtp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Parse answer */
+    status = pjmedia_stream_info_parse_fmtp(
+				    NULL, answer, 
+				    pj_strtoul(&answer->desc.fmt[a_fmt_idx]),
+				    &a_fmtp_raw);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pjmedia_vid_codec_h264_parse_fmtp(&a_fmtp_raw, &a_fmtp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if (option & PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER) {
+	unsigned i;
+
+	/* Flexible negotiation, if the answer has higher capability than
+	 * the offer, adjust the answer capability to be match to the offer.
+	 */
+	if (a_fmtp.profile_idc >= o_fmtp.profile_idc)
+	    a_fmtp.profile_idc = o_fmtp.profile_idc;
+	if (a_fmtp.profile_iop != o_fmtp.profile_iop)
+	    a_fmtp.profile_iop = o_fmtp.profile_iop;
+	if (a_fmtp.level >= o_fmtp.level)
+	    a_fmtp.level = o_fmtp.level;
+	if (a_fmtp.packetization_mode >= o_fmtp.packetization_mode)
+	    a_fmtp.packetization_mode = o_fmtp.packetization_mode;
+
+	/* Match them now */
+	if (a_fmtp.profile_idc != o_fmtp.profile_idc ||
+	    a_fmtp.profile_iop != o_fmtp.profile_iop ||
+	    a_fmtp.level != o_fmtp.level ||
+	    a_fmtp.packetization_mode != o_fmtp.packetization_mode)
+	{
+	    return PJMEDIA_SDP_EFORMATNOTEQUAL;
+	}
+
+	/* Update the answer */
+	for (i = 0; i < a_fmtp_raw.cnt; ++i) {
+	    if (pj_stricmp(&a_fmtp_raw.param[i].name, &PROFILE_LEVEL_ID) == 0)
+	    {
+		char *p = a_fmtp_raw.param[i].val.ptr;
+		pj_val_to_hex_digit(a_fmtp.profile_idc, p);
+		p += 2;
+		pj_val_to_hex_digit(a_fmtp.profile_iop, p);
+		p += 2;
+		pj_val_to_hex_digit(a_fmtp.level, p);
+	    }
+	    else if (pj_stricmp(&a_fmtp_raw.param[i].name, &PACKETIZATION_MODE) == 0)
+	    {
+		char *p = a_fmtp_raw.param[i].val.ptr;
+		*p = '0' + a_fmtp.packetization_mode;
+	    }
+	}
+    } else {
+	/* Strict negotiation */
+	if (a_fmtp.profile_idc != o_fmtp.profile_idc ||
+	    a_fmtp.profile_iop != o_fmtp.profile_iop ||
+	    a_fmtp.level != o_fmtp.level ||
+	    a_fmtp.packetization_mode != o_fmtp.packetization_mode)
+	{
+	    return PJMEDIA_SDP_EFORMATNOTEQUAL;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
Index: /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/vid_stream.c
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/vid_stream.c	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/vid_stream.c	(revision 3500)
@@ -22,4 +22,5 @@
 #include <pjmedia/rtcp.h>
 #include <pjmedia/jbuf.h>
+#include <pjmedia/sdp_neg.h>
 #include <pjmedia/stream_common.h>
 #include <pj/array.h>
@@ -1722,48 +1723,27 @@
 					      const pjmedia_sdp_media *rem_m)
 {
-    const pjmedia_sdp_attr *attr;
-    pjmedia_sdp_rtpmap *rtpmap;
-    unsigned i, pt = 0;
+    unsigned pt = 0;
+    const pjmedia_vid_codec_info *p_info;
     pj_status_t status;
 
-    /* Get codec info.
-     * For static payload types, get the info from codec manager.
-     * For dynamic payload types, MUST get the rtpmap.
-     */
     pt = pj_strtoul(&local_m->desc.fmt[0]);
+
+    /* Get codec info. */
+    status = pjmedia_vid_codec_mgr_get_codec_info(mgr, pt, &p_info);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    si->codec_info = *p_info;
+
+    /* Get payload type for receiving direction */
     si->rx_pt = pt;
+
+    /* Get payload type for transmitting direction */
     if (pt < 96) {
-	const pjmedia_vid_codec_info *p_info;
-
-	status = pjmedia_vid_codec_mgr_get_codec_info(mgr, pt, &p_info);
-	if (status != PJ_SUCCESS)
-	    return status;
-
-	si->codec_info = *p_info;
-
 	/* For static payload type, pt's are symetric */
 	si->tx_pt = pt;
 
     } else {
-	unsigned info_cnt = 1;
-	const pjmedia_vid_codec_info *p_info;
-
-	attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
-					   &local_m->desc.fmt[0]);
-	if (attr == NULL)
-	    return PJMEDIA_EMISSINGRTPMAP;
-
-	status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
-	if (status != PJ_SUCCESS)
-	    return status;
-
-	/* Get codec info from codec id */
-        status = pjmedia_vid_codec_mgr_find_codecs_by_id(
-					    mgr, &rtpmap->enc_name,
-					    &info_cnt, &p_info, NULL);
-	if (status != PJ_SUCCESS)
-	    return status;
-
-	si->codec_info = *p_info;
+	unsigned i;
 
 	/* Determine payload type for outgoing channel, by finding
@@ -1772,26 +1752,11 @@
 	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)
+	    if (pjmedia_sdp_neg_fmt_match(NULL,
+					  (pjmedia_sdp_media*)local_m, 0,
+					  (pjmedia_sdp_media*)rem_m, i, 0) ==
+		PJ_SUCCESS)
 	    {
 		/* Found matched codec. */
-		si->tx_pt = rpt;
-
+		si->tx_pt = pj_strtoul(&rem_m->desc.fmt[i]);
 		break;
 	    }
Index: /pjproject/branches/projects/2.0-dev/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjsip-apps/src/pjsua/pjsua_app.c	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjsip-apps/src/pjsua/pjsua_app.c	(revision 3500)
@@ -3543,6 +3543,10 @@
     pjsua_vid_enum_codecs(c, &count);
     for (i=0; i<count; ++i) {
-	printf("  %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen,
-			       c[i].codec_id.ptr);
+	printf("  %d\t%.*s%s%.*s\n", c[i].priority,
+				     (int)c[i].codec_id.slen,
+				     c[i].codec_id.ptr,
+				     c[i].desc.slen? " - ":"",
+				     (int)c[i].desc.slen,
+				     c[i].desc.ptr);
     }
 #endif
Index: /pjproject/branches/projects/2.0-dev/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjsip/include/pjsua-lib/pjsua.h	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjsip/include/pjsua-lib/pjsua.h	(revision 3500)
@@ -4380,7 +4380,12 @@
 
     /**
+     * Codec description.
+     */
+    pj_str_t		desc;
+
+    /**
      * Internal buffer.
      */
-    char		buf_[32];
+    char		buf_[64];
 
 } pjsua_codec_info;
Index: /pjproject/branches/projects/2.0-dev/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- /pjproject/branches/projects/2.0-dev/pjsip/src/pjsua-lib/pjsua_media.c	(revision 3499)
+++ /pjproject/branches/projects/2.0-dev/pjsip/src/pjsua-lib/pjsua_media.c	(revision 3500)
@@ -3758,4 +3758,6 @@
 
     for (i=0; i<count; ++i) {
+	pj_bzero(&id[i], sizeof(pjsua_codec_info));
+
 	pjmedia_codec_info_to_id(&info[i], id[i].buf_, sizeof(id[i].buf_));
 	id[i].codec_id = pj_str(id[i].buf_);
@@ -3875,7 +3877,16 @@
     for (i=0, j=0; i<count && j<*p_count; ++i) {
 	if (info[i].has_rtp_pack) {
+	    pj_bzero(&id[j], sizeof(pjsua_codec_info));
+
 	    pjmedia_vid_codec_info_to_id(&info[i], id[j].buf_, sizeof(id[j].buf_));
 	    id[j].codec_id = pj_str(id[j].buf_);
 	    id[j].priority = (pj_uint8_t) prio[i];
+	    
+	    if (id[j].codec_id.slen < sizeof(id[j].buf_)) {
+		id[j].desc.ptr = id[j].codec_id.ptr + id[j].codec_id.slen + 1;
+		pj_strncpy(&id[j].desc, &info[i].encoding_desc,
+			   sizeof(id[j].buf_) - id[j].codec_id.slen - 1);
+	    }
+
 	    ++j;
 	}
