Index: pjproject/trunk/pjmedia/src/pjmedia/endpoint.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/endpoint.c	(revision 6102)
+++ pjproject/trunk/pjmedia/src/pjmedia/endpoint.c	(revision 6103)
@@ -417,4 +417,11 @@
     unsigned max_bitrate = 0;
     pj_status_t status;
+#if defined(PJMEDIA_RTP_PT_TELEPHONE_EVENTS) && \
+	    PJMEDIA_RTP_PT_TELEPHONE_EVENTS != 0
+    unsigned televent_num = 0;
+    unsigned televent_clockrates[8];
+    unsigned used_pt_num = 0;
+    unsigned used_pt[PJMEDIA_MAX_SDP_FMT];
+#endif
 
     PJ_UNUSED_ARG(options);
@@ -541,31 +548,80 @@
 	if (max_bitrate < codec_param.info.max_bps)
 	    max_bitrate = codec_param.info.max_bps;
-    }
-
+
+	/* List clock rate & channel count of audio codecs for generating
+	 * telephone-event later.
+	 */
 #if defined(PJMEDIA_RTP_PT_TELEPHONE_EVENTS) && \
-    PJMEDIA_RTP_PT_TELEPHONE_EVENTS != 0
+	    PJMEDIA_RTP_PT_TELEPHONE_EVENTS != 0
+	{
+	    unsigned j;
+
+	    /* Take a note of used dynamic PT */
+	    if (codec_info->pt >= 96)
+		used_pt[used_pt_num++] = codec_info->pt;
+
+	    for (j=0; j<televent_num; ++j) {
+		if (televent_clockrates[j] == rtpmap.clock_rate)
+		    break;
+	    }
+	    if (j==televent_num &&
+		televent_num<PJ_ARRAY_SIZE(televent_clockrates))
+	    {
+		/* List this clockrate for tel-event generation */
+		televent_clockrates[televent_num++] = rtpmap.clock_rate;
+	    }
+	}
+#endif
+    }
+
+#if defined(PJMEDIA_RTP_PT_TELEPHONE_EVENTS) && \
+	    PJMEDIA_RTP_PT_TELEPHONE_EVENTS != 0
     /*
      * Add support telephony event
      */
     if (endpt->has_telephone_event) {
-	m->desc.fmt[m->desc.fmt_count++] =
-	    pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR);
-
-	/* Add rtpmap. */
-	attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
-	attr->name = pj_str("rtpmap");
-	attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR
-			     " telephone-event/8000");
-	m->attr[m->attr_count++] = attr;
-
-	/* Add fmtp */
-	attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
-	attr->name = pj_str("fmtp");
+	for (i=0; i<televent_num; i++) {
+	    char buf[160];
+	    unsigned j = 0;
+	    unsigned pt = PJMEDIA_RTP_PT_TELEPHONE_EVENTS;
+
+	    /* Find PT for this tel-event */
+	    while (j < used_pt_num && pt <= 127) {
+		if (pt == used_pt[j]) {
+		    pt++;
+		    j = 0;
+		} else {
+		    j++;
+		}
+	    }
+	    if (pt > 127) {
+		/* No more available PT */
+		break;
+	    }
+	    used_pt[used_pt_num++] = pt;
+
+	    /* Print tel-event PT */
+	    pj_ansi_snprintf(buf, sizeof(buf), "%d", pt);
+	    m->desc.fmt[m->desc.fmt_count++] = pj_strdup3(pool, buf);
+
+	    /* Add rtpmap. */
+	    attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
+	    attr->name = pj_str("rtpmap");
+	    pj_ansi_snprintf(buf, sizeof(buf), "%d telephone-event/%d",
+			     pt, televent_clockrates[i]);
+	    attr->value = pj_strdup3(pool, buf);
+	    m->attr[m->attr_count++] = attr;
+
+	    /* Add fmtp */
+	    attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
+	    attr->name = pj_str("fmtp");
 #if defined(PJMEDIA_HAS_DTMF_FLASH) && PJMEDIA_HAS_DTMF_FLASH!= 0
-	attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " 0-16");
+	    pj_ansi_snprintf(buf, sizeof(buf), "%d 0-16", pt);
 #else
-	attr->value = pj_str(PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR " 0-15");
+	    pj_ansi_snprintf(buf, sizeof(buf), "%d 0-15", pt);
 #endif
-	m->attr[m->attr_count++] = attr;
+	    attr->value = pj_strdup3(pool, buf);
+	    m->attr[m->attr_count++] = attr;
+	}
     }
 #endif
Index: pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c	(revision 6102)
+++ pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c	(revision 6103)
@@ -1071,4 +1071,5 @@
     pjmedia_sdp_media *answer;
     const pjmedia_sdp_media *master, *slave;
+    unsigned nclockrate = 0, clockrate[PJMEDIA_MAX_SDP_FMT];
 
     /* If offer has zero port, just clone the offer */
@@ -1131,7 +1132,18 @@
 		    p = pj_strtoul(&slave->desc.fmt[j]);
 		    if (p == pt && pj_isdigit(*slave->desc.fmt[j].ptr)) {
+			unsigned k;
+
 			found_matching_codec = 1;
 			pt_offer[pt_answer_count] = slave->desc.fmt[j];
 			pt_answer[pt_answer_count++] = slave->desc.fmt[j];
+
+			/* Take note of clock rate for tel-event. Note: for
+			 * static PT, we assume the clock rate is 8000.
+			 */
+			for (k=0; k<nclockrate; ++k)
+			    if (clockrate[k] == 8000)
+				break;
+			if (k == nclockrate)
+			    clockrate[nclockrate++] = 8000;
 			break;
 		    }
@@ -1145,5 +1157,5 @@
 		const pjmedia_sdp_attr *a;
 		pjmedia_sdp_rtpmap or_;
-		pj_bool_t is_codec;
+		pj_bool_t is_codec = 0;
 
 		/* Get the rtpmap for the payload type in the master. */
@@ -1156,9 +1168,5 @@
 		pjmedia_sdp_attr_get_rtpmap(a, &or_);
 
-		if (!pj_stricmp2(&or_.enc_name, "telephone-event")) {
-		    if (found_matching_telephone_event)
-			continue;
-		    is_codec = 0;
-		} else {
+		if (pj_stricmp2(&or_.enc_name, "telephone-event")) {
 		    master_has_codec = 1;
 		    if (!answer_with_multiple_codecs && found_matching_codec)
@@ -1192,4 +1200,5 @@
 				pjmedia_sdp_media *o_med, *a_med;
 				unsigned o_fmt_idx, a_fmt_idx;
+				unsigned k;
 
 				o_med = (pjmedia_sdp_media*)offer;
@@ -1208,4 +1217,11 @@
 				}
 				found_matching_codec = 1;
+
+				/* Take note of clock rate for tel-event */
+				for (k=0; k<nclockrate; ++k)
+				    if (clockrate[k] == or_.clock_rate)
+					break;
+				if (k == nclockrate)
+				    clockrate[nclockrate++] = or_.clock_rate;
 			    } else {
 				found_matching_telephone_event = 1;
@@ -1270,6 +1286,69 @@
     }
 
-    /* Seems like everything is in order.
-     * Build the answer by cloning from preanswer, but rearrange the payload
+    /* Seems like everything is in order. */
+
+    /* Remove unwanted telephone-event formats. */
+    if (found_matching_telephone_event) {
+	pj_str_t first_televent_offer = {0};
+	pj_str_t first_televent_answer = {0};
+	unsigned matched_cnt = 0;
+
+	for (i=0; i<pt_answer_count; ) {
+	    const pjmedia_sdp_attr *a;
+	    pjmedia_sdp_rtpmap r;
+	    unsigned j;
+
+	    /* Skip static PT, as telephone-event uses dynamic PT */
+	    if (!pj_isdigit(*pt_answer[i].ptr) || pj_strtol(&pt_answer[i])<96)
+	    {
+		++i;
+		continue;
+	    }
+
+	    /* Get the rtpmap for format. */
+	    a = pjmedia_sdp_media_find_attr2(preanswer, "rtpmap",
+					     &pt_answer[i]);
+	    pj_assert(a);
+	    pjmedia_sdp_attr_get_rtpmap(a, &r);
+
+	    /* Only care for telephone-event format */
+	    if (pj_stricmp2(&r.enc_name, "telephone-event")) {
+		++i;
+		continue;
+	    }
+
+	    if (first_televent_offer.slen == 0) {
+		first_televent_offer = pt_offer[i];
+		first_televent_answer = pt_answer[i];
+	    }
+
+	    for (j=0; j<nclockrate; ++j) {
+		if (r.clock_rate==clockrate[j])
+		    break;
+	    }
+
+	    /* This tel-event's clockrate is unwanted, remove the tel-event */
+	    if (j==nclockrate) {
+		pj_array_erase(pt_answer, sizeof(pt_answer[0]),
+			       pt_answer_count, i);
+		pj_array_erase(pt_offer, sizeof(pt_offer[0]),
+			       pt_answer_count, i);
+		pt_answer_count--;
+	    } else {
+		++matched_cnt;
+		++i;
+	    }
+	}
+
+	/* Tel-event is wanted, but no matched clock rate (to the selected
+	 * audio codec), just put back any first matched tel-event formats.
+	 */
+	if (!matched_cnt) {
+	    pt_offer[pt_answer_count] = first_televent_offer;
+	    pt_answer[pt_answer_count++] = first_televent_answer;
+	}
+    }
+
+    /* Build the answer by cloning from preanswer, and reorder the payload
      * to suit the offer.
      */
Index: pjproject/trunk/pjmedia/src/test/sdp_neg_test.c
===================================================================
--- pjproject/trunk/pjmedia/src/test/sdp_neg_test.c	(revision 6102)
+++ pjproject/trunk/pjmedia/src/test/sdp_neg_test.c	(revision 6103)
@@ -1246,4 +1246,88 @@
     },
 
+    /* test 17: */
+    {
+	/*********************************************************************
+	 * Ticket #2088: : Handle multiple telephone-event formats.
+	 */
+
+	"Ticket #2088: Handle multiple telephone-event formats",
+	2,
+	{
+	  {
+	    REMOTE_OFFER,
+	    /* Bob sends offer: */
+	    "v=0\r\n"
+	    "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
+	    "s=bob\r\n"
+	    "c=IN IP4 host.biloxi.example.com\r\n"
+	    "t=0 0\r\n"
+	    "m=audio 3000 RTP/AVP 97 0 98 99\r\n"
+	    "a=rtpmap:97 Speex/16000\r\n"
+	    "a=rtpmap:0 PCMU/8000\r\n"
+	    "a=rtpmap:98 telephone-event/8000\r\n"
+	    "a=rtpmap:99 telephone-event/16000\r\n"
+	    "",
+	    /* Alice initial capability: */
+	    "v=0\r\n"
+	    "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
+	    "s=alice\r\n"
+	    "c=IN IP4 host.atlanta.example.com\r\n"
+	    "t=0 0\r\n"
+	    "m=audio 4000 RTP/AVP 0 100 96 98\r\n"
+	    "a=rtpmap:0 PCMU/8000\r\n"
+	    "a=rtpmap:100 Speex/16000\r\n"
+	    "a=rtpmap:96 telephone-event/8000\r\n"
+	    "a=rtpmap:98 telephone-event/16000\r\n"
+	    "",
+	    /* Alice's local SDP should be: */
+	    "v=0\r\n"
+	    "o=alice 2890844526 2890844527 IN IP4 host.atlanta.example.com\r\n"
+	    "s=alice\r\n"
+	    "c=IN IP4 host.atlanta.example.com\r\n"
+	    "t=0 0\r\n"
+	    "m=audio 4000 RTP/AVP 97 99\r\n"
+	    "a=rtpmap:97 Speex/16000\r\n"
+	    "a=rtpmap:99 telephone-event/16000\r\n"
+	    "",
+	  },
+	  {
+	    LOCAL_OFFER,
+	    /* Alice updates offer */
+	    "v=0\r\n"
+	    "o=alice 2890844526 2890844528 IN IP4 host.atlanta.example.com\r\n"
+	    "s=alice\r\n"
+	    "c=IN IP4 host.atlanta.example.com\r\n"
+	    "t=0 0\r\n"
+	    "m=audio 4000 RTP/AVP 0 97 98 99\r\n"
+	    "a=rtpmap:0 PCMU/8000\r\n"
+	    "a=rtpmap:97 Speex/16000\r\n"
+	    "a=rtpmap:98 telephone-event/8000\r\n"
+	    "a=rtpmap:99 telephone-event/16000\r\n"
+	    "",
+	    /* Receive Bob's answer: */
+	    "v=0\r\n"
+	    "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
+	    "s=bob\r\n"
+	    "c=IN IP4 host.biloxi.example.com\r\n"
+	    "t=0 0\r\n"
+	    "m=audio 3000 RTP/AVP 99 100\r\n"
+	    "a=rtpmap:99 Speex/16000\r\n"
+	    "a=rtpmap:100 telephone-event/16000\r\n"
+	    "",
+	    /* Alice's local SDP should be: */
+	    "v=0\r\n"
+	    "o=alice 2890844526 2890844528 IN IP4 host.atlanta.example.com\r\n"
+	    "s=alice\r\n"
+	    "c=IN IP4 host.atlanta.example.com\r\n"
+	    "t=0 0\r\n"
+	    "m=audio 4000 RTP/AVP 97 99\r\n"
+	    "a=rtpmap:97 Speex/16000\r\n"
+	    "a=rtpmap:99 telephone-event/16000\r\n"
+	    "",
+	  }
+	}
+    },
+
 };
 
