/* 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; }
/* Create complete answer for remote's offer. */ static pj_status_t create_answer( pj_pool_t *pool, pj_bool_t prefer_remote_codec_order, const pjmedia_sdp_session *initial, const pjmedia_sdp_session *offer, pjmedia_sdp_session **p_answer) { pj_status_t status = PJMEDIA_SDPNEG_ENOMEDIA; pj_bool_t has_active = PJ_FALSE; pjmedia_sdp_session *answer; char media_used[PJMEDIA_MAX_SDP_MEDIA]; unsigned i; /* Validate remote offer. * This should have been validated before. */ PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(offer))==PJ_SUCCESS, status); /* Create initial answer by duplicating initial SDP, * but clear all media lines. The media lines will be filled up later. */ answer = pjmedia_sdp_session_clone(pool, initial); PJ_ASSERT_RETURN(answer != NULL, PJ_ENOMEM); answer->media_count = 0; pj_bzero(media_used, sizeof(media_used)); /* For each media line, create our answer based on our initial * capability. */ for (i=0; i<offer->media_count; ++i) { const pjmedia_sdp_media *om; /* offer */ const pjmedia_sdp_media *im; /* initial media */ pjmedia_sdp_media *am = NULL; /* answer/result */ unsigned j; om = offer->media[i]; /* Find media description in our initial capability that matches * the media type and transport type of offer's media, has * matching codec, and has not been used to answer other offer. */ for (im=NULL, j=0; j<initial->media_count; ++j) { im = initial->media[j]; if (pj_strcmp(&om->desc.media, &im->desc.media)==0 && pj_strcmp(&om->desc.transport, &im->desc.transport)==0 && media_used[j] == 0) { /* See if it has matching codec. */ if (prefer_remote_codec_order) { status = match_offer(pool, om, im, im, &am); } else { status = match_offer(pool, im, om, im, &am); } if (status == PJ_SUCCESS) { /* Mark media as used. */ media_used[j] = 1; break; } } } if (j==initial->media_count) { /* No matching media. * Reject the offer by setting the port to zero in the answer. */ //pjmedia_sdp_attr *a; /* For simplicity in the construction of the answer, we'll * just clone the media from the offer. Anyway receiver will * ignore anything in the media once it sees that the port * number is zero. */ am = pjmedia_sdp_media_clone(pool, om); am->desc.port = 0; // Just set port zero to disable stream without set it to inactive. /* Remove direction attribute, and replace with inactive */ remove_all_media_directions(am); //a = pjmedia_sdp_attr_create(pool, "inactive", NULL); //pjmedia_sdp_media_add_attr(am, a); /* Then update direction */ update_media_direction(pool, om, am); } else { /* The answer is in am */ pj_assert(am != NULL); } /* Add the media answer */ answer->media[answer->media_count++] = am; /* Check if this media is active.*/ if (am->desc.port != 0) has_active = PJ_TRUE; } *p_answer = answer; return has_active ? PJ_SUCCESS : status; }
/* 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; }