/* Try to match offer with answer. */ static pj_status_t match_offer(int inst_id, pj_pool_t *pool, pj_bool_t prefer_remote_codec_order, const pjmedia_sdp_media *offer, const pjmedia_sdp_media *preanswer, pjmedia_sdp_media **p_answer) { unsigned i; pj_bool_t master_has_codec = 0, master_has_telephone_event = 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]; pjmedia_sdp_media *answer; const pjmedia_sdp_media *master, *slave; pj_str_t pt_amr_need_adapt = {NULL, 0}; /* If offer has zero port, just clone the offer */ if (offer->desc.port == 0) { answer = pjmedia_sdp_media_clone_deactivate(pool, offer); *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 || pt == 5000) { // dean : 5000 is for WebRTC data channel. /* For static payload type, it's enough to compare just * the payload number. */ master_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<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_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(inst_id, a, &or_); if (!pj_stricmp2(&or_.enc_name, "telephone-event")) { master_has_telephone_event = 1; if (found_matching_telephone_event) continue; is_codec = 0; } else { master_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<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(inst_id, 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) { /* Further check for G7221, negotiate bitrate */ if (pj_stricmp2(&or_.enc_name, "G7221") == 0 && !match_g7221(master, i, slave, j)) { continue; } else /* Further check for AMR, negotiate fmtp */ if (pj_stricmp2(&or_.enc_name, "AMR")==0 || pj_stricmp2(&or_.enc_name, "AMR-WB")==0) { unsigned o_med_idx, a_med_idx; o_med_idx = prefer_remote_codec_order? i:j; a_med_idx = prefer_remote_codec_order? j:i; if (!match_amr(offer, o_med_idx, preanswer, a_med_idx, PJ_TRUE, &pt_amr_need_adapt)) continue; } found_matching_codec = 1; } else { found_matching_telephone_event = 1; } 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_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]); /* For AMR/AMRWB format, adapt octet-align setting if required. */ if (!pj_strcmp(&pt_amr_need_adapt, &pt_answer[i])) amr_toggle_octet_align(pool, answer, i); } /* 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; /* Update media direction. */ update_media_direction(pool, offer, answer); *p_answer = answer; return PJ_SUCCESS; }
PJ_DEF(pj_status_t) pjmedia_codec_amr_match_sdp( pj_pool_t *pool, pjmedia_sdp_media *offer, unsigned o_fmt_idx, pjmedia_sdp_media *answer, unsigned a_fmt_idx, unsigned option) { /* Negotiated format-param field-names constants. */ const pj_str_t STR_OCTET_ALIGN = {"octet-align=", 12}; const pj_str_t STR_CRC = {"crc=", 4}; const pj_str_t STR_ROBUST_SORTING = {"robust-sorting=", 15}; const pj_str_t STR_INTERLEAVING = {"interleaving=", 13}; /* Negotiated params and their default values. */ unsigned a_octet_align = 0, o_octet_align = 0; unsigned a_crc = 0, o_crc = 0; unsigned a_robust_sorting = 0, o_robust_sorting = 0; unsigned a_interleaving = 0, o_interleaving = 0; const pjmedia_sdp_attr *attr_ans; const pjmedia_sdp_attr *attr_ofr; pjmedia_sdp_fmtp fmtp; pj_status_t status; /* Parse offerer FMTP */ attr_ofr = pjmedia_sdp_media_find_attr2(offer, "fmtp", &offer->desc.fmt[o_fmt_idx]); if (attr_ofr) { status = pjmedia_sdp_attr_get_fmtp(attr_ofr, &fmtp); if (status != PJ_SUCCESS) return status; GET_FMTP_IVAL(o_octet_align, fmtp, STR_OCTET_ALIGN, 0); GET_FMTP_IVAL(o_crc, fmtp, STR_CRC, 0); GET_FMTP_IVAL(o_robust_sorting, fmtp, STR_ROBUST_SORTING, 0); GET_FMTP_IVAL(o_interleaving, fmtp, STR_INTERLEAVING, 0); } /* Parse answerer FMTP */ attr_ans = pjmedia_sdp_media_find_attr2(answer, "fmtp", &answer->desc.fmt[a_fmt_idx]); if (attr_ans) { status = pjmedia_sdp_attr_get_fmtp(attr_ans, &fmtp); if (status != PJ_SUCCESS) return status; GET_FMTP_IVAL(a_octet_align, fmtp, STR_OCTET_ALIGN, 0); GET_FMTP_IVAL(a_crc, fmtp, STR_CRC, 0); GET_FMTP_IVAL(a_robust_sorting, fmtp, STR_ROBUST_SORTING, 0); GET_FMTP_IVAL(a_interleaving, fmtp, STR_INTERLEAVING, 0); } /* First, match crc, robust-sorting, interleaving settings */ if (a_crc != o_crc || a_robust_sorting != o_robust_sorting || a_interleaving != o_interleaving) { return PJMEDIA_SDP_EFORMATNOTEQUAL; } /* Match octet-align setting */ if (a_octet_align != o_octet_align) { /* Check if answer can be modified to match to the offer */ if (option & PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER) { status = amr_toggle_octet_align(pool, answer, a_fmt_idx); return status; } else { return PJMEDIA_SDP_EFORMATNOTEQUAL; } } return PJ_SUCCESS; }