From d7ee0066d767c259fba9c51d3a6e1c00d1261533 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Fri, 27 Jun 2008 21:19:44 +0000 Subject: Fix handling of multiple media lines in the incoming SDP offer. Now pjsua-lib will be able to select the best media line to handle git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2079 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/include/pjsua-lib/pjsua_internal.h | 1 + pjsip/src/pjsua-lib/pjsua_call.c | 1 + pjsip/src/pjsua-lib/pjsua_media.c | 151 ++++++++++++++++++++++++++----- 3 files changed, 131 insertions(+), 22 deletions(-) diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index 6865344d..8d5a7b9d 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -63,6 +63,7 @@ typedef struct pjsua_call pjsua_call_media_status media_st;/**< Media state. */ pjmedia_dir media_dir; /**< Media direction. */ pjmedia_session *session; /**< The media session. */ + int audio_idx; /**< Index of m=audio in SDP. */ pj_uint32_t ssrc; /**< RTP SSRC */ int conf_slot; /**< Slot # in conference bridge. */ pjsip_evsub *xfer_sub; /**< Xfer server subscription, if this diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 53035f5f..f493c9d8 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -104,6 +104,7 @@ static void reset_call(pjsua_call_id id) call->inv = NULL; call->user_data = NULL; call->session = NULL; + call->audio_idx = -1; call->ssrc = pj_rand(); call->xfer_sub = NULL; call->last_code = (pjsip_status_code) 0; diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 919f0c35..89cc9a40 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -851,14 +851,13 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, const pjmedia_sdp_session *rem_sdp, int *sip_err_code) { - enum { MEDIA_IDX = 0 }; pjsua_call *call = &pjsua_var.calls[call_id]; pj_status_t status; #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) pjsua_acc *acc = &pjsua_var.acc[call->acc_id]; pjmedia_srtp_setting srtp_opt; - pjmedia_transport *srtp; + pjmedia_transport *srtp = NULL; #endif PJ_UNUSED_ARG(role); @@ -914,9 +913,80 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, PJ_UNUSED_ARG(security_level); #endif + /* Find out which media line in SDP that we support. If we are offerer, + * audio will be at index 0 in SDP. + */ + if (rem_sdp == 0) { + call->audio_idx = 0; + } + /* Otherwise find out the candidate audio media line in SDP */ + else { + unsigned i; + pj_bool_t srtp_active; + +#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) + srtp_active = acc->cfg.use_srtp && srtp != NULL; +#else + srtp_active = PJ_FALSE; +#endif + + /* Media count must have been checked */ + pj_assert(rem_sdp->media_count != 0); + + for (i=0; imedia_count; ++i) { + const pjmedia_sdp_media *m = rem_sdp->media[i]; + + /* Skip if media is not audio */ + if (pj_stricmp2(&m->desc.media, "audio") != 0) + continue; + + /* Skip if media is disabled */ + if (m->desc.port == 0) + continue; + + /* Skip if transport is not supported */ + if (pj_stricmp2(&m->desc.transport, "RTP/AVP") != 0 && + pj_stricmp2(&m->desc.transport, "RTP/SAVP") != 0) + { + continue; + } + + if (call->audio_idx == -1) { + call->audio_idx = i; + } else { + /* We've found multiple candidates. This could happen + * e.g. when remote is offering both RTP/AVP and RTP/AVP, + * or when remote for some reason offers two audio. + */ + + if (srtp_active && + pj_stricmp2(&m->desc.transport, "RTP/SAVP")==0) + { + /* Prefer RTP/SAVP when our media transport is SRTP */ + call->audio_idx = i; + } else if (!srtp_active && + pj_stricmp2(&m->desc.transport, "RTP/AVP")==0) + { + /* Prefer RTP/AVP when our media transport is NOT SRTP */ + call->audio_idx = i; + } + } + } + } + + /* Reject offer if we couldn't find a good m=audio line in offer */ + if (call->audio_idx < 0) { + if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE; + pjsua_media_channel_deinit(call_id); + return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); + } + + PJ_LOG(4,(THIS_FILE, "Media index %d selected for call %d", + call->audio_idx, call->index)); + /* Create the media transport */ status = pjmedia_transport_media_create(call->med_tp, tmp_pool, 0, - rem_sdp, MEDIA_IDX); + rem_sdp, call->audio_idx); if (status != PJ_SUCCESS) { if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE; pjsua_media_channel_deinit(call_id); @@ -933,7 +1003,7 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, pjmedia_sdp_session **p_sdp, int *sip_status_code) { - enum { MAX_MEDIA = 1, MEDIA_IDX = 0 }; + enum { MAX_MEDIA = 1 }; pjmedia_sdp_session *sdp; pjmedia_transport_info tpinfo; pjsua_call *call = &pjsua_var.calls[call_id]; @@ -946,6 +1016,9 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, return PJ_EBUSY; } + /* Media index must have been determined before */ + pj_assert(call->audio_idx != -1); + /* Create media if it's not created. This could happen when call is * currently on-hold */ @@ -970,6 +1043,53 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, return status; } + /* If we're answering and the selected media is not the first media + * in SDP, then fill in the unselected media with with zero port. + * Otherwise we'll crash in transport_encode_sdp() because the media + * lines are not aligned between offer and answer. + */ + if (rem_sdp && call->audio_idx != 0) { + unsigned i; + + for (i=0; imedia_count; ++i) { + const pjmedia_sdp_media *rem_m = rem_sdp->media[i]; + pjmedia_sdp_media *m; + const pjmedia_sdp_attr *a; + + if ((int)i == call->audio_idx) + continue; + + m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media); + pj_strdup(pool, &m->desc.media, &rem_m->desc.media); + pj_strdup(pool, &m->desc.transport, &rem_m->desc.transport); + m->desc.port = 0; + + /* Add one format, copy from the offer. And copy the corresponding + * rtpmap and fmtp attributes too. + */ + m->desc.fmt_count = 1; + pj_strdup(pool, &m->desc.fmt[0], &rem_m->desc.fmt[0]); + if ((a=pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, + "rtpmap", &m->desc.fmt[0])) != NULL) + { + m->attr[m->attr_count++] = pjmedia_sdp_attr_clone(pool, a); + } + if ((a=pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, + "fmtp", &m->desc.fmt[0])) != NULL) + { + m->attr[m->attr_count++] = pjmedia_sdp_attr_clone(pool, a); + } + + if (i==sdp->media_count) + sdp->media[sdp->media_count++] = m; + else { + pj_array_insert(sdp->media, sizeof(sdp->media[0]), + sdp->media_count, i, &m); + ++sdp->media_count; + } + } + } + /* Add NAT info in the SDP */ if (pjsua_var.ua_cfg.nat_type_in_sdp) { pjmedia_sdp_attr *a; @@ -996,7 +1116,7 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, /* Give the SDP to media transport */ status = pjmedia_transport_encode_sdp(call->med_tp, pool, sdp, rem_sdp, - MEDIA_IDX); + call->audio_idx); if (status != PJ_SUCCESS) { if (sip_status_code) *sip_status_code = PJSIP_SC_NOT_ACCEPTABLE; return status; @@ -1078,7 +1198,6 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *remote_sdp) { - unsigned i; int prev_media_st = 0; pjsua_call *call = &pjsua_var.calls[call_id]; pjmedia_session_info sess_info; @@ -1099,22 +1218,10 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, if (status != PJ_SUCCESS) return status; - /* Find which session is audio (we only support audio for now) */ - for (i=0; i < sess_info.stream_cnt; ++i) { - if (sess_info.stream_info[i].type == PJMEDIA_TYPE_AUDIO && - (sess_info.stream_info[i].proto == PJMEDIA_TP_PROTO_RTP_AVP || - sess_info.stream_info[i].proto == PJMEDIA_TP_PROTO_RTP_SAVP)) - { - si = &sess_info.stream_info[i]; - break; - } - } - - if (si == NULL) { - /* Not found */ - return PJMEDIA_EINVALIMEDIATYPE; - } - + /* Find which session is audio */ + PJ_ASSERT_RETURN(call->audio_idx != -1, PJ_EBUG); + PJ_ASSERT_RETURN(call->audio_idx < (int)sess_info.stream_cnt, PJ_EBUG); + si = &sess_info.stream_info[call->audio_idx]; /* Reset session info with only one media stream */ sess_info.stream_cnt = 1; -- cgit v1.2.3