Index: pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c	(revision 3553)
+++ pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c	(revision 3664)
@@ -53,5 +53,5 @@
 };
 
-#define GET_FMTP_IVAL(ival, fmtp, param, default_val) \
+#define GET_FMTP_IVAL_BASE(ival, base, fmtp, param, default_val) \
     do { \
 	pj_str_t s; \
@@ -64,6 +64,36 @@
 	pj_strset(&s, p + param.slen, fmtp.fmt_param.slen - \
 		  (p - fmtp.fmt_param.ptr) - param.slen); \
-	ival = pj_strtoul(&s); \
+	ival = pj_strtoul2(&s, NULL, base); \
     } while (0)
+
+#define GET_FMTP_IVAL(ival, fmtp, param, default_val) \
+	GET_FMTP_IVAL_BASE(ival, 10, fmtp, param, default_val)
+
+
+/* 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);
+
 
 /*
@@ -232,4 +262,29 @@
 }
 
+static pjmedia_sdp_media *sdp_media_clone_deactivate(
+				    pj_pool_t *pool,
+                                    const pjmedia_sdp_media *rem_med,
+                                    const pjmedia_sdp_media *local_med,
+                                    const pjmedia_sdp_session *local_sess)
+{
+    pjmedia_sdp_media *res;
+
+    res = pjmedia_sdp_media_clone_deactivate(pool, rem_med);
+    if (!res)
+	return NULL;
+
+    if (!res->conn && (!local_sess || !local_sess->conn)) {
+	if (local_med && local_med->conn)
+	    res->conn = pjmedia_sdp_conn_clone(pool, local_med->conn);
+	else {
+	    res->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
+	    res->conn->net_type = pj_str("IN");
+	    res->conn->addr_type = pj_str("IP4");
+	    res->conn->addr = pj_str("127.0.0.1");
+	}
+    }
+
+    return res;
+}
 
 /*
@@ -312,5 +367,5 @@
 	    pjmedia_sdp_media *m;
 
-	    m = pjmedia_sdp_media_clone_deactivate(pool, om);
+	    m = sdp_media_clone_deactivate(pool, om, om, local);
 
 	    pj_array_insert(new_offer->media, sizeof(new_offer->media[0]),
@@ -877,8 +932,10 @@
 			{
 			    /* Further check for G7221, negotiate bitrate. */
-			    if (pj_stricmp2(&or_.enc_name, "G7221") == 0) {
+			    if (pj_stricmp2(&or_.enc_name, "G7221") == 0)
+			    {
 				if (match_g7221(offer, i, answer, j))
 				    break;
 			    } else
+
 			    /* Further check for AMR, negotiate fmtp. */
 			    if (pj_stricmp2(&or_.enc_name, "AMR") == 0 ||
@@ -888,5 +945,11 @@
 					      NULL))
 				    break;
-			    } else {
+			    } else
+			    
+			    /* Call custom format matching callbacks */
+			    if (custom_fmt_match(pool, &or_.enc_name,
+						 offer, i, answer, j, 0) ==
+				PJ_SUCCESS)
+			    {
 				/* Match! */
 				break;
@@ -1017,5 +1080,6 @@
 
 	    /* Generate matching-but-disabled-media for the answer */
-	    am = pjmedia_sdp_media_clone_deactivate(pool, offer->media[omi]);
+	    am = sdp_media_clone_deactivate(pool, offer->media[omi],
+	                                    offer->media[omi], offer);
 	    answer->media[answer->media_count++] = am;
 	    ++ami;
@@ -1079,5 +1143,5 @@
     /* If offer has zero port, just clone the offer */
     if (offer->desc.port == 0) {
-	answer = pjmedia_sdp_media_clone_deactivate(pool, offer);
+	answer = sdp_media_clone_deactivate(pool, offer, preanswer, NULL);
 	*p_answer = answer;
 	return PJ_SUCCESS;
@@ -1182,4 +1246,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 &&
@@ -1187,5 +1269,6 @@
 				{
 				    continue;
-				} else 
+				} else
+
 				/* Further check for AMR, negotiate fmtp */
 				if (pj_stricmp2(&or_.enc_name, "AMR")==0 ||
@@ -1201,4 +1284,5 @@
 					continue;
 				}
+
 				found_matching_codec = 1;
 			    } else {
@@ -1372,5 +1456,5 @@
 	     * number is zero.
 	     */
-	    am = pjmedia_sdp_media_clone_deactivate(pool, om);
+	    am = sdp_media_clone_deactivate(pool, om, om, answer);
 	} else {
 	    /* The answer is in am */
@@ -1474,2 +1558,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);
+}
+
