/* Internal function to apply symmetric PT for the local answer. */ static void apply_answer_symmetric_pt(pj_pool_t *pool, pjmedia_sdp_media *answer, unsigned pt_cnt, const pj_str_t pt_offer[], const pj_str_t pt_answer[]) { pjmedia_sdp_attr *a_tmp[PJMEDIA_MAX_SDP_ATTR]; unsigned i, a_tmp_cnt = 0; /* Rewrite the payload types in the answer if different to * the ones in the offer. */ for (i = 0; i < pt_cnt; ++i) { pjmedia_sdp_attr *a; /* Skip if the PTs are the same already, e.g: static PT. */ if (pj_strcmp(&pt_answer[i], &pt_offer[i]) == 0) continue; /* Rewrite payload type in the answer to match to the offer */ pj_strdup(pool, &answer->desc.fmt[i], &pt_offer[i]); /* Also update payload type in rtpmap */ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &pt_answer[i]); if (a) { rewrite_pt(pool, &a->value, &pt_answer[i], &pt_offer[i]); /* Temporarily remove the attribute in case the new payload * type is being used by another format in the media. */ pjmedia_sdp_media_remove_attr(answer, a); a_tmp[a_tmp_cnt++] = a; } /* Also update payload type in fmtp */ a = pjmedia_sdp_media_find_attr2(answer, "fmtp", &pt_answer[i]); if (a) { rewrite_pt(pool, &a->value, &pt_answer[i], &pt_offer[i]); /* Temporarily remove the attribute in case the new payload * type is being used by another format in the media. */ pjmedia_sdp_media_remove_attr(answer, a); a_tmp[a_tmp_cnt++] = a; } } /* Return back 'rtpmap' and 'fmtp' attributes */ for (i = 0; i < a_tmp_cnt; ++i) pjmedia_sdp_media_add_attr(answer, a_tmp[i]); }
/* Try to match offer with answer. */ static pj_status_t match_offer(pj_pool_t *pool, const pjmedia_sdp_media *offer, const pjmedia_sdp_media *preanswer, const pjmedia_sdp_media *orig_local, pjmedia_sdp_media **p_answer) { unsigned i; pj_bool_t offer_has_codec = 0, offer_has_telephone_event = 0, offer_has_other = 0, found_matching_codec = 0, found_matching_telephone_event = 0, found_matching_other = 0; unsigned pt_answer_count = 0; pj_str_t pt_answer[PJMEDIA_MAX_SDP_FMT]; pjmedia_sdp_media *answer; /* With the addition of telephone-event and dodgy MS RTC SDP, * the answer generation algorithm looks really shitty... */ for (i=0; i<offer->desc.fmt_count; ++i) { unsigned j; if (pj_isdigit(*offer->desc.fmt[i].ptr)) { /* This is normal/standard payload type, where it's identified * by payload number. */ unsigned pt; pt = pj_strtoul(&offer->desc.fmt[i]); if (pt < 96) { /* For static payload type, it's enough to compare just * the payload number. */ offer_has_codec = 1; /* We just need to select one codec. * Continue if we have selected matching codec for previous * payload. */ if (found_matching_codec) continue; /* Find matching codec in local descriptor. */ for (j=0; j<preanswer->desc.fmt_count; ++j) { unsigned p; p = pj_strtoul(&preanswer->desc.fmt[j]); if (p == pt && pj_isdigit(*preanswer->desc.fmt[j].ptr)) { found_matching_codec = 1; pt_answer[pt_answer_count++] = preanswer->desc.fmt[j]; break; } } } else { /* This is dynamic payload type. * For dynamic payload type, we must look the rtpmap and * compare the encoding name. */ const pjmedia_sdp_attr *a; pjmedia_sdp_rtpmap or_; pj_bool_t is_codec; /* Get the rtpmap for the payload type in the offer. */ a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", &offer->desc.fmt[i]); if (!a) { pj_assert(!"Bug! Offer should have been validated"); return PJMEDIA_SDP_EMISSINGRTPMAP; } pjmedia_sdp_attr_get_rtpmap(a, &or_); if (!pj_strcmp2(&or_.enc_name, "telephone-event")) { offer_has_telephone_event = 1; if (found_matching_telephone_event) continue; is_codec = 0; } else { offer_has_codec = 1; if (found_matching_codec) continue; is_codec = 1; } /* Find paylaod in our initial SDP with matching * encoding name and clock rate. */ for (j=0; j<preanswer->desc.fmt_count; ++j) { a = pjmedia_sdp_media_find_attr2(preanswer, "rtpmap", &preanswer->desc.fmt[j]); if (a) { pjmedia_sdp_rtpmap lr; pjmedia_sdp_attr_get_rtpmap(a, &lr); /* See if encoding name, clock rate, and * channel count match */ if (!pj_stricmp(&or_.enc_name, &lr.enc_name) && or_.clock_rate == lr.clock_rate && (pj_strcmp(&or_.param, &lr.param)==0 || (or_.param.slen==1 && *or_.param.ptr=='1'))) { /* Match! */ 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 { found_matching_telephone_event = 1; } pt_answer[pt_answer_count++] = preanswer->desc.fmt[j]; break; } } } } } else { /* This is a non-standard, brain damaged SDP where the payload * type is non-numeric. It exists e.g. in Microsoft RTC based * UA, to indicate instant messaging capability. * Example: * - m=x-ms-message 5060 sip null */ offer_has_other = 1; if (found_matching_other) continue; for (j=0; j<preanswer->desc.fmt_count; ++j) { if (!pj_strcmp(&offer->desc.fmt[i], &preanswer->desc.fmt[j])) { /* Match */ found_matching_other = 1; pt_answer[pt_answer_count++] = preanswer->desc.fmt[j]; break; } } } } /* See if all types of offer can be matched. */ if (offer_has_codec && !found_matching_codec) { return PJMEDIA_SDPNEG_NOANSCODEC; } /* If this comment is removed, negotiation will fail if remote has offered telephone-event and local is not configured with telephone-event if (offer_has_telephone_event && !found_matching_telephone_event) { return PJMEDIA_SDPNEG_NOANSTELEVENT; } */ if (offer_has_other && !found_matching_other) { return PJMEDIA_SDPNEG_NOANSUNKNOWN; } /* Seems like everything is in order. * Build the answer by cloning from local media, but rearrange the payload * to suit the offer. */ answer = pjmedia_sdp_media_clone(pool, orig_local); for (i=0; i<pt_answer_count; ++i) { unsigned j; for (j=i; j<answer->desc.fmt_count; ++j) { if (!pj_strcmp(&answer->desc.fmt[j], &pt_answer[i])) break; } pj_assert(j != answer->desc.fmt_count); str_swap(&answer->desc.fmt[i], &answer->desc.fmt[j]); } /* Remove unwanted local formats. */ for (i=pt_answer_count; i<answer->desc.fmt_count; ++i) { pjmedia_sdp_attr *a; /* Remove rtpmap for this format */ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &answer->desc.fmt[i]); if (a) { pjmedia_sdp_media_remove_attr(answer, a); } /* Remove fmtp for this format */ a = pjmedia_sdp_media_find_attr2(answer, "fmtp", &answer->desc.fmt[i]); if (a) { pjmedia_sdp_media_remove_attr(answer, a); } } answer->desc.fmt_count = pt_answer_count; /* If offer has zero port, set our answer with zero port too */ if (offer->desc.port==0) answer->desc.port = 0; /* Update media direction. */ update_media_direction(pool, offer, answer); *p_answer = answer; return PJ_SUCCESS; }
/* Update single local media description to after receiving answer * from remote. */ static pj_status_t process_m_answer( pj_pool_t *pool, pjmedia_sdp_media *offer, pjmedia_sdp_media *answer, pj_bool_t allow_asym) { unsigned i; /* Check that the media type match our offer. */ if (pj_strcmp(&answer->desc.media, &offer->desc.media)!=0) { /* The media type in the answer is different than the offer! */ return PJMEDIA_SDPNEG_EINVANSMEDIA; } /* Check that transport in the answer match our offer. */ /* At this point, transport type must be compatible, * the transport instance will do more validation later. */ if (pjmedia_sdp_transport_cmp(&answer->desc.transport, &offer->desc.transport) != PJ_SUCCESS) { return PJMEDIA_SDPNEG_EINVANSTP; } /* Check if remote has rejected our offer */ if (answer->desc.port == 0) { /* Remote has rejected our offer. * Set our port to zero too in active SDP. */ offer->desc.port = 0; } /* Process direction attributes */ update_media_direction(pool, answer, offer); /* If asymetric media is allowed, then just check that remote answer has * codecs that are within the offer. * * Otherwise if asymetric media is not allowed, then we will choose only * one codec in our initial offer to match the answer. */ if (allow_asym) { for (i=0; i<answer->desc.fmt_count; ++i) { unsigned j; pj_str_t *rem_fmt = &answer->desc.fmt[i]; for (j=0; j<offer->desc.fmt_count; ++j) { if (pj_strcmp(rem_fmt, &answer->desc.fmt[j])==0) break; } if (j != offer->desc.fmt_count) { /* Found at least one common codec. */ break; } } if (i == answer->desc.fmt_count) { /* No common codec in the answer! */ return PJMEDIA_SDPNEG_EANSNOMEDIA; } PJ_TODO(CHECK_SDP_NEGOTIATION_WHEN_ASYMETRIC_MEDIA_IS_ALLOWED); } else { /* Remove all format in the offer that has no matching answer */ for (i=0; i<offer->desc.fmt_count;) { unsigned pt; pj_uint32_t j; pj_str_t *fmt = &offer->desc.fmt[i]; /* Find matching answer */ pt = pj_strtoul(fmt); if (pt < 96) { for (j=0; j<answer->desc.fmt_count; ++j) { if (pj_strcmp(fmt, &answer->desc.fmt[j])==0) break; } } else { /* This is dynamic payload type. * For dynamic payload type, we must look the rtpmap and * compare the encoding name. */ const pjmedia_sdp_attr *a; pjmedia_sdp_rtpmap or_; /* Get the rtpmap for the payload type in the offer. */ a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt); if (!a) { pj_assert(!"Bug! Offer should have been validated"); return PJ_EBUG; } pjmedia_sdp_attr_get_rtpmap(a, &or_); /* Find paylaod in answer SDP with matching * encoding name and clock rate. */ for (j=0; j<answer->desc.fmt_count; ++j) { a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &answer->desc.fmt[j]); if (a) { pjmedia_sdp_rtpmap ar; pjmedia_sdp_attr_get_rtpmap(a, &ar); /* See if encoding name, clock rate, and channel * count match */ if (!pj_stricmp(&or_.enc_name, &ar.enc_name) && or_.clock_rate == ar.clock_rate && (pj_stricmp(&or_.param, &ar.param)==0 || (ar.param.slen==1 && *ar.param.ptr=='1'))) { /* 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; } } } } } if (j == answer->desc.fmt_count) { /* This format has no matching answer. * Remove it from our offer. */ pjmedia_sdp_attr *a; /* Remove rtpmap associated with this format */ a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt); if (a) pjmedia_sdp_media_remove_attr(offer, a); /* Remove fmtp associated with this format */ a = pjmedia_sdp_media_find_attr2(offer, "fmtp", fmt); if (a) pjmedia_sdp_media_remove_attr(offer, a); /* Remove this format from offer's array */ pj_array_erase(offer->desc.fmt, sizeof(offer->desc.fmt[0]), offer->desc.fmt_count, i); --offer->desc.fmt_count; } else { ++i; } } /* Arrange format in the offer so the order match the priority * in the answer */ for (i=0; i<answer->desc.fmt_count; ++i) { unsigned j; pj_str_t *fmt = &answer->desc.fmt[i]; for (j=i; j<offer->desc.fmt_count; ++j) { if (pj_strcmp(fmt, &offer->desc.fmt[j])==0) { str_swap(&offer->desc.fmt[i], &offer->desc.fmt[j]); break; } } } } /* Looks okay */ return PJ_SUCCESS; }
/* Update single local media description to after receiving answer * from remote. */ static pj_status_t process_m_answer( pj_pool_t *pool, pjmedia_sdp_media *offer, pjmedia_sdp_media *answer, pj_bool_t allow_asym) { unsigned i; /* Check that the media type match our offer. */ if (pj_strcmp(&answer->desc.media, &offer->desc.media)!=0) { /* The media type in the answer is different than the offer! */ return PJMEDIA_SDPNEG_EINVANSMEDIA; } /* Check that transport in the answer match our offer. */ /* At this point, transport type must be compatible, * the transport instance will do more validation later. */ if (pjmedia_sdp_transport_cmp(&answer->desc.transport, &offer->desc.transport) != PJ_SUCCESS) { return PJMEDIA_SDPNEG_EINVANSTP; } /* Check if remote has rejected our offer */ if (answer->desc.port == 0) { /* Remote has rejected our offer. * Deactivate our media too. */ pjmedia_sdp_media_deactivate(pool, offer); /* Don't need to proceed */ return PJ_SUCCESS; } /* Ticket #1148: check if remote answer does not set port to zero when * offered with port zero. Let's just tolerate it. */ if (offer->desc.port == 0) { /* Don't need to proceed */ return PJ_SUCCESS; } /* Process direction attributes */ update_media_direction(pool, answer, offer); /* If asymetric media is allowed, then just check that remote answer has * codecs that are within the offer. * * Otherwise if asymetric media is not allowed, then we will choose only * one codec in our initial offer to match the answer. */ if (allow_asym) { for (i=0; i<answer->desc.fmt_count; ++i) { unsigned j; pj_str_t *rem_fmt = &answer->desc.fmt[i]; for (j=0; j<offer->desc.fmt_count; ++j) { if (pj_strcmp(rem_fmt, &answer->desc.fmt[j])==0) break; } if (j != offer->desc.fmt_count) { /* Found at least one common codec. */ break; } } if (i == answer->desc.fmt_count) { /* No common codec in the answer! */ return PJMEDIA_SDPNEG_EANSNOMEDIA; } PJ_TODO(CHECK_SDP_NEGOTIATION_WHEN_ASYMETRIC_MEDIA_IS_ALLOWED); } else { /* Offer format priority based on answer format index/priority */ unsigned offer_fmt_prior[PJMEDIA_MAX_SDP_FMT]; /* Remove all format in the offer that has no matching answer */ for (i=0; i<offer->desc.fmt_count;) { unsigned pt; pj_uint32_t j; pj_str_t *fmt = &offer->desc.fmt[i]; /* Find matching answer */ pt = pj_strtoul(fmt); if (pt < 96) { for (j=0; j<answer->desc.fmt_count; ++j) { if (pj_strcmp(fmt, &answer->desc.fmt[j])==0) break; } } else { /* This is dynamic payload type. * For dynamic payload type, we must look the rtpmap and * compare the encoding name. */ const pjmedia_sdp_attr *a; pjmedia_sdp_rtpmap or_; /* Get the rtpmap for the payload type in the offer. */ a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt); if (!a) { pj_assert(!"Bug! Offer should have been validated"); return PJ_EBUG; } pjmedia_sdp_attr_get_rtpmap(a, &or_); /* Find paylaod in answer SDP with matching * encoding name and clock rate. */ for (j=0; j<answer->desc.fmt_count; ++j) { a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &answer->desc.fmt[j]); if (a) { pjmedia_sdp_rtpmap ar; pjmedia_sdp_attr_get_rtpmap(a, &ar); /* See if encoding name, clock rate, and channel * count match */ if (!pj_stricmp(&or_.enc_name, &ar.enc_name) && or_.clock_rate == ar.clock_rate && (pj_stricmp(&or_.param, &ar.param)==0 || (ar.param.slen==1 && *ar.param.ptr=='1'))) { /* Call custom format matching callbacks */ if (custom_fmt_match(pool, &or_.enc_name, offer, i, answer, j, 0) == PJ_SUCCESS) { /* Match! */ break; } } } } } if (j == answer->desc.fmt_count) { /* This format has no matching answer. * Remove it from our offer. */ pjmedia_sdp_attr *a; /* Remove rtpmap associated with this format */ a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt); if (a) pjmedia_sdp_media_remove_attr(offer, a); /* Remove fmtp associated with this format */ a = pjmedia_sdp_media_find_attr2(offer, "fmtp", fmt); if (a) pjmedia_sdp_media_remove_attr(offer, a); /* Remove this format from offer's array */ pj_array_erase(offer->desc.fmt, sizeof(offer->desc.fmt[0]), offer->desc.fmt_count, i); --offer->desc.fmt_count; } else { offer_fmt_prior[i] = j; ++i; } } if (0 == offer->desc.fmt_count) { /* No common codec in the answer! */ return PJMEDIA_SDPNEG_EANSNOMEDIA; } /* Post process: * - Resort offer formats so the order match to the answer. * - Remove answer formats that unmatches to the offer. */ /* Resort offer formats */ for (i=0; i<offer->desc.fmt_count; ++i) { unsigned j; for (j=i+1; j<offer->desc.fmt_count; ++j) { if (offer_fmt_prior[i] > offer_fmt_prior[j]) { unsigned tmp = offer_fmt_prior[i]; offer_fmt_prior[i] = offer_fmt_prior[j]; offer_fmt_prior[j] = tmp; str_swap(&offer->desc.fmt[i], &offer->desc.fmt[j]); } } } /* Remove unmatched answer formats */ { unsigned del_cnt = 0; for (i=0; i<answer->desc.fmt_count;) { /* The offer is ordered now, also the offer_fmt_prior */ if (i >= offer->desc.fmt_count || offer_fmt_prior[i]-del_cnt != i) { pj_str_t *fmt = &answer->desc.fmt[i]; pjmedia_sdp_attr *a; /* Remove rtpmap associated with this format */ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", fmt); if (a) pjmedia_sdp_media_remove_attr(answer, a); /* Remove fmtp associated with this format */ a = pjmedia_sdp_media_find_attr2(answer, "fmtp", fmt); if (a) pjmedia_sdp_media_remove_attr(answer, a); /* Remove this format from answer's array */ pj_array_erase(answer->desc.fmt, sizeof(answer->desc.fmt[0]), answer->desc.fmt_count, i); --answer->desc.fmt_count; ++del_cnt; } else { ++i; } } } } /* Looks okay */ return PJ_SUCCESS; }
/* Try to match offer with answer. */ static pj_status_t match_offer(pj_pool_t *pool, pj_bool_t prefer_remote_codec_order, pj_bool_t answer_with_multiple_codecs, const pjmedia_sdp_media *offer, const pjmedia_sdp_media *preanswer, const pjmedia_sdp_session *preanswer_sdp, pjmedia_sdp_media **p_answer) { unsigned i; pj_bool_t master_has_codec = 0, master_has_other = 0, found_matching_codec = 0, found_matching_telephone_event = 0, found_matching_other = 0; unsigned pt_answer_count = 0; pj_str_t pt_answer[PJMEDIA_MAX_SDP_FMT]; pj_str_t pt_offer[PJMEDIA_MAX_SDP_FMT]; pjmedia_sdp_media *answer; const pjmedia_sdp_media *master, *slave; /* If offer has zero port, just clone the offer */ if (offer->desc.port == 0) { answer = sdp_media_clone_deactivate(pool, offer, preanswer, preanswer_sdp); *p_answer = answer; return PJ_SUCCESS; } /* If the preanswer define zero port, this media is being rejected, * just clone the preanswer. */ if (preanswer->desc.port == 0) { answer = pjmedia_sdp_media_clone(pool, preanswer); *p_answer = answer; return PJ_SUCCESS; } /* Set master/slave negotiator based on prefer_remote_codec_order. */ if (prefer_remote_codec_order) { master = offer; slave = preanswer; } else { master = preanswer; slave = offer; } /* With the addition of telephone-event and dodgy MS RTC SDP, * the answer generation algorithm looks really shitty... */ for (i=0; i<master->desc.fmt_count; ++i) { unsigned j; if (pj_isdigit(*master->desc.fmt[i].ptr)) { /* This is normal/standard payload type, where it's identified * by payload number. */ unsigned pt; pt = pj_strtoul(&master->desc.fmt[i]); if (pt < 96) { /* For static payload type, it's enough to compare just * the payload number. */ master_has_codec = 1; /* We just need to select one codec if not allowing multiple. * Continue if we have selected matching codec for previous * payload. */ if (!answer_with_multiple_codecs && found_matching_codec) continue; /* Find matching codec in local descriptor. */ for (j=0; j<slave->desc.fmt_count; ++j) { unsigned p; p = pj_strtoul(&slave->desc.fmt[j]); if (p == pt && pj_isdigit(*slave->desc.fmt[j].ptr)) { found_matching_codec = 1; pt_offer[pt_answer_count] = slave->desc.fmt[j]; pt_answer[pt_answer_count++] = slave->desc.fmt[j]; break; } } } else { /* This is dynamic payload type. * For dynamic payload type, we must look the rtpmap and * compare the encoding name. */ const pjmedia_sdp_attr *a; pjmedia_sdp_rtpmap or_; pj_bool_t is_codec; /* Get the rtpmap for the payload type in the master. */ a = pjmedia_sdp_media_find_attr2(master, "rtpmap", &master->desc.fmt[i]); if (!a) { pj_assert(!"Bug! Offer should have been validated"); return PJMEDIA_SDP_EMISSINGRTPMAP; } 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 { master_has_codec = 1; if (!answer_with_multiple_codecs && found_matching_codec) continue; is_codec = 1; } /* Find paylaod in our initial SDP with matching * encoding name and clock rate. */ for (j=0; j<slave->desc.fmt_count; ++j) { a = pjmedia_sdp_media_find_attr2(slave, "rtpmap", &slave->desc.fmt[j]); if (a) { pjmedia_sdp_rtpmap lr; pjmedia_sdp_attr_get_rtpmap(a, &lr); /* See if encoding name, clock rate, and * channel count match */ if (!pj_stricmp(&or_.enc_name, &lr.enc_name) && or_.clock_rate == lr.clock_rate && (pj_stricmp(&or_.param, &lr.param)==0 || (lr.param.slen==0 && or_.param.slen==1 && *or_.param.ptr=='1') || (or_.param.slen==0 && lr.param.slen==1 && *lr.param.ptr=='1'))) { /* Match! */ if (is_codec) { pjmedia_sdp_media *o_med, *a_med; unsigned o_fmt_idx, a_fmt_idx; o_med = (pjmedia_sdp_media*)offer; a_med = (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_med, o_fmt_idx, a_med, a_fmt_idx, ALLOW_MODIFY_ANSWER) != PJ_SUCCESS) { continue; } found_matching_codec = 1; } else { found_matching_telephone_event = 1; } pt_offer[pt_answer_count] = prefer_remote_codec_order? offer->desc.fmt[i]: offer->desc.fmt[j]; pt_answer[pt_answer_count++] = prefer_remote_codec_order? preanswer->desc.fmt[j]: preanswer->desc.fmt[i]; break; } } } } } else { /* This is a non-standard, brain damaged SDP where the payload * type is non-numeric. It exists e.g. in Microsoft RTC based * UA, to indicate instant messaging capability. * Example: * - m=x-ms-message 5060 sip null */ master_has_other = 1; if (found_matching_other) continue; for (j=0; j<slave->desc.fmt_count; ++j) { if (!pj_strcmp(&master->desc.fmt[i], &slave->desc.fmt[j])) { /* Match */ found_matching_other = 1; pt_offer[pt_answer_count] = prefer_remote_codec_order? offer->desc.fmt[i]: offer->desc.fmt[j]; pt_answer[pt_answer_count++] = prefer_remote_codec_order? preanswer->desc.fmt[j]: preanswer->desc.fmt[i]; break; } } } } /* See if all types of master can be matched. */ if (master_has_codec && !found_matching_codec) { return PJMEDIA_SDPNEG_NOANSCODEC; } /* If this comment is removed, negotiation will fail if remote has offered telephone-event and local is not configured with telephone-event if (offer_has_telephone_event && !found_matching_telephone_event) { return PJMEDIA_SDPNEG_NOANSTELEVENT; } */ if (master_has_other && !found_matching_other) { return PJMEDIA_SDPNEG_NOANSUNKNOWN; } /* Seems like everything is in order. * Build the answer by cloning from preanswer, but rearrange the payload * to suit the offer. */ answer = pjmedia_sdp_media_clone(pool, preanswer); for (i=0; i<pt_answer_count; ++i) { unsigned j; for (j=i; j<answer->desc.fmt_count; ++j) { if (!pj_strcmp(&answer->desc.fmt[j], &pt_answer[i])) break; } pj_assert(j != answer->desc.fmt_count); str_swap(&answer->desc.fmt[i], &answer->desc.fmt[j]); } /* Remove unwanted local formats. */ for (i=pt_answer_count; i<answer->desc.fmt_count; ++i) { pjmedia_sdp_attr *a; /* Remove rtpmap for this format */ a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &answer->desc.fmt[i]); if (a) { pjmedia_sdp_media_remove_attr(answer, a); } /* Remove fmtp for this format */ a = pjmedia_sdp_media_find_attr2(answer, "fmtp", &answer->desc.fmt[i]); if (a) { pjmedia_sdp_media_remove_attr(answer, a); } } answer->desc.fmt_count = pt_answer_count; #if PJMEDIA_SDP_NEG_ANSWER_SYMMETRIC_PT apply_answer_symmetric_pt(pool, answer, pt_answer_count, pt_offer, pt_answer); #endif /* Update media direction. */ update_media_direction(pool, offer, answer); *p_answer = answer; return PJ_SUCCESS; }