From 7b7c7c8b42a8c25b30b07a8cd524cccbb60173b3 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Wed, 6 Feb 2013 13:48:45 +0000 Subject: Close #1242: Single re-INVITE/UPDATE for both lock codec and ICE negotiation updates. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4342 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/include/pjsua-lib/pjsua.h | 20 ++ pjsip/include/pjsua-lib/pjsua_internal.h | 11 +- pjsip/src/pjsua-lib/pjsua_call.c | 520 ++++++++++++++++++------------- pjsip/src/pjsua-lib/pjsua_core.c | 2 + pjsip/src/pjsua-lib/pjsua_media.c | 102 ++---- 5 files changed, 352 insertions(+), 303 deletions(-) (limited to 'pjsip') diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 40975e4e..5114189d 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -2656,6 +2656,16 @@ typedef struct pjsua_ice_config */ pj_bool_t ice_no_rtcp; + /** + * Send re-INVITE/UPDATE every after ICE connectivity check regardless + * the default ICE transport address is changed or not. When this is set + * to PJ_FALSE, re-INVITE/UPDATE will be sent only when the default ICE + * transport address is changed. + * + * Default: yes + */ + pj_bool_t ice_always_update; + } pjsua_ice_config; /** @@ -5550,6 +5560,16 @@ struct pjsua_media_config */ pj_bool_t ice_no_rtcp; + /** + * Send re-INVITE/UPDATE every after ICE connectivity check regardless + * the default ICE transport address is changed or not. When this is set + * to PJ_FALSE, re-INVITE/UPDATE will be sent only when the default ICE + * transport address is changed. + * + * Default: yes + */ + pj_bool_t ice_always_update; + /** * Enable TURN relay candidate in ICE. */ diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index d08b599d..12048ade 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -155,10 +155,7 @@ struct pjsua_call char last_text_buf_[128]; /**< Buffer for last_text. */ struct { - pj_timer_entry reinv_timer;/**< Reinvite retry timer. */ - pj_uint32_t sdp_ver; /**< SDP version of the bad answer */ int retry_cnt; /**< Retry count. */ - pj_bool_t pending; /**< Pending until CONFIRMED state */ } lock_codec; /**< Data for codec locking when answer contains multiple codecs. */ @@ -185,6 +182,10 @@ struct pjsua_call offer. */ unsigned rem_vid_cnt; /**< No of active video in last remote offer. */ + + pj_timer_entry reinv_timer; /**< Reinvite retry timer. */ + pj_bool_t reinv_pending;/**< Pending until CONFIRMED state. */ + pj_bool_t reinv_ice_sent;/**< Has reinvite for ICE upd sent? */ }; @@ -816,6 +817,10 @@ PJ_DECL(void) pjsua_vid_win_reset(pjsua_vid_win_id wid); # define pjsua_vid_win_reset(wid) #endif +/* + * Schedule check for the need of re-INVITE/UPDATE after media update + */ +void pjsua_call_schedule_reinvite_check(pjsua_call *call, unsigned delay_ms); PJ_END_DECL diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 29fbf6e7..bd72485f 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -106,6 +106,12 @@ static pj_status_t create_sdp_of_call_hold(pjsua_call *call, static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); +/* Timer callback to send re-INVITE/UPDATE to lock codec or ICE update */ +static void reinv_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry); + +/* Check and send reinvite for lock codec and ICE update */ +static pj_status_t process_pending_reinvite(pjsua_call *call); + /* * Reset call descriptor. */ @@ -128,6 +134,8 @@ static void reset_call(pjsua_call_id id) call_med->tp_auto_del = PJ_TRUE; } pjsua_call_setting_default(&call->opt); + pj_timer_entry_init(&call->reinv_timer, PJ_FALSE, + (void*)(pj_size_t)id, &reinv_timer_cb); } @@ -2200,11 +2208,10 @@ PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id, goto on_return; } - /* Stop lock codec timer, if it is active */ - if (call->lock_codec.reinv_timer.id) { - pjsip_endpt_cancel_timer(pjsua_var.endpt, - &call->lock_codec.reinv_timer); - call->lock_codec.reinv_timer.id = PJ_FALSE; + /* Stop reinvite timer, if it is active */ + if (call->reinv_timer.id) { + pjsua_cancel_timer(&call->reinv_timer); + call->reinv_timer.id = PJ_FALSE; } on_return: @@ -2894,12 +2901,8 @@ PJ_DEF(void) pjsua_call_hangup_all(void) } -/* Proto */ -static pj_status_t perform_lock_codec(pjsua_call *call); - -/* Timer callback to send re-INVITE or UPDATE to lock codec */ -static void reinv_timer_cb(pj_timer_heap_t *th, - pj_timer_entry *entry) +/* Timer callback to send re-INVITE/UPDATE to lock codec or ICE update */ +static void reinv_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry) { pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data; pjsip_dialog *dlg; @@ -2908,21 +2911,27 @@ static void reinv_timer_cb(pj_timer_heap_t *th, PJ_UNUSED_ARG(th); - pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE; + pjsua_var.calls[call_id].reinv_timer.id = PJ_FALSE; + + pj_log_push_indent(); status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg); - if (status != PJ_SUCCESS) + if (status != PJ_SUCCESS) { + pj_log_pop_indent(); return; + } - status = perform_lock_codec(call); + process_pending_reinvite(call); pjsip_dlg_dec_lock(dlg); + + pj_log_pop_indent(); } /* Check if the specified format can be skipped in counting codecs */ static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m, - const pj_str_t *fmt) + const pj_str_t *fmt) { const pj_str_t STR_TEL = {"telephone-event", 15}; unsigned pt; @@ -2955,62 +2964,62 @@ static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m, } -/* Send re-INVITE or UPDATE with new SDP offer to select only one codec - * out of several codecs presented by callee in his answer. +/* Schedule check for the need of re-INVITE/UPDATE after media update, cases: + * - lock codec if remote answerer has given us more than one codecs + * - update ICE default transport address if it has changed after ICE + * connectivity check. */ -static pj_status_t perform_lock_codec(pjsua_call *call) +void pjsua_call_schedule_reinvite_check(pjsua_call *call, unsigned delay_ms) { - const pj_str_t STR_UPDATE = {"UPDATE", 6}; - const pjmedia_sdp_session *local_sdp = NULL, *new_sdp; + pj_time_val delay; + + /* Stop reinvite timer, if it is active */ + if (call->reinv_timer.id) + pjsua_cancel_timer(&call->reinv_timer); + + delay.sec = 0; + delay.msec = delay_ms; + pj_time_val_normalize(&delay); + call->reinv_timer.id = PJ_TRUE; + pjsua_schedule_timer(&call->reinv_timer, &delay); +} + + +/* Check if lock codec is needed */ +static pj_bool_t check_lock_codec(pjsua_call *call) +{ + const pjmedia_sdp_session *local_sdp, *remote_sdp; + pj_bool_t has_mult_fmt = PJ_FALSE; unsigned i; - pj_bool_t rem_can_update; - pj_bool_t need_lock_codec = PJ_FALSE; - pjsip_tx_data *tdata; pj_status_t status; - PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE, - PJ_EINVALIDOP); + /* Check if lock codec is disabled */ + if (!pjsua_var.acc[call->acc_id].cfg.lock_codec) + return PJ_FALSE; - /* Verify if another SDP negotiation is in progress, e.g: session timer - * or another re-INVITE. - */ - if (call->inv==NULL || call->inv->neg==NULL || - pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) - { - return PJMEDIA_SDPNEG_EINSTATE; - } + /* Check lock codec retry count */ + if (call->lock_codec.retry_cnt >= LOCK_CODEC_MAX_RETRY) + return PJ_FALSE; - /* Don't do this if call is disconnecting! */ - if (call->inv->state > PJSIP_INV_STATE_CONFIRMED || - call->inv->cause >= 200) - { - return PJ_EINVALIDOP; - } + /* Check if we are the answerer, we shouldn't need to lock codec */ + if (!call->inv->neg || !pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) + return PJ_FALSE; - /* Verify if another SDP negotiation has been completed by comparing - * the SDP version. - */ + /* Check if remote answerer has given us more than one codecs. */ status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp); if (status != PJ_SUCCESS) - return status; - if (local_sdp->origin.version > call->lock_codec.sdp_ver) - return PJMEDIA_SDP_EINVER; - - PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec..")); - - /* Update the new offer so it contains only a codec. Note that formats - * order in the offer should have been matched to the answer, so we can - * just directly update the offer without looking-up the answer. - */ - new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp); + return PJ_FALSE; + status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp); + if (status != PJ_SUCCESS) + return PJ_FALSE; - for (i = 0; i < call->med_cnt; ++i) { - unsigned j = 0, codec_cnt = 0; - const pjmedia_sdp_media *ref_m; - pjmedia_sdp_media *m; + for (i = 0; i < call->med_cnt && !has_mult_fmt; ++i) { pjsua_call_media *call_med = &call->media[i]; + const pjmedia_sdp_media *rem_m, *loc_m; + unsigned codec_cnt = 0; + unsigned j; - /* Verify if media is deactivated */ + /* Skip this if the media is inactive or error */ if (call_med->state == PJSUA_CALL_MEDIA_NONE || call_med->state == PJSUA_CALL_MEDIA_ERROR || call_med->dir == PJMEDIA_DIR_NONE) @@ -3018,196 +3027,279 @@ static pj_status_t perform_lock_codec(pjsua_call *call) continue; } - ref_m = local_sdp->media[i]; - m = new_sdp->media[i]; - - /* Verify that media must be active. */ - pj_assert(ref_m->desc.port); + /* Remote may answer with less media lines. */ + if (i >= remote_sdp->media_count) + continue; - while (j < m->desc.fmt_count) { - pjmedia_sdp_attr *a; - pj_str_t *fmt = &m->desc.fmt[j]; + rem_m = remote_sdp->media[i]; + loc_m = local_sdp->media[i]; - if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) { - ++j; - continue; - } + /* Verify that media must be active. */ + pj_assert(loc_m->desc.port && rem_m->desc.port); - /* Remove format */ - a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt); - if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); - a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt); - if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); - pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]), - m->desc.fmt_count, j); - --m->desc.fmt_count; + /* Count the formats in the answer. */ + for (j=0; jdesc.fmt_count && codec_cnt <= 1; ++j) { + if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]) && ++codec_cnt > 1) + has_mult_fmt = PJ_TRUE; } - - need_lock_codec |= (ref_m->desc.fmt_count > m->desc.fmt_count); } - /* Last check if SDP trully needs to be updated. It is possible that OA - * negotiations have completed and SDP has changed but we didn't - * increase the SDP version (should not happen!). - */ - if (!need_lock_codec) - return PJ_SUCCESS; + /* Reset retry count when remote answer has one codec */ + if (!has_mult_fmt) + call->lock_codec.retry_cnt = 0; - /* Send UPDATE or re-INVITE */ - rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg, - PJSIP_H_ALLOW, - NULL, &STR_UPDATE) == - PJSIP_DIALOG_CAP_SUPPORTED; - if (rem_can_update) { - status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata); - } else { - status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata); - } + return has_mult_fmt; +} - if (status==PJ_EINVALIDOP && - ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY) - { - /* Ups, let's reschedule again */ - pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL}; - pj_time_val_normalize(&delay); - call->lock_codec.reinv_timer.id = PJ_TRUE; - pjsip_endpt_schedule_timer(pjsua_var.endpt, - &call->lock_codec.reinv_timer, &delay); - return status; - } else if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec", - status); - return status; - } +/* Check if ICE setup is complete and if it needs to send reinvite */ +static pj_bool_t check_ice_complete(pjsua_call *call, pj_bool_t *need_reinv) +{ + pj_bool_t ice_need_reinv = PJ_FALSE; + pj_bool_t ice_complete = PJ_TRUE; + unsigned i; - /* Send the UPDATE/re-INVITE request */ - status = pjsip_inv_send_msg(call->inv, tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec", - status); - return status; + /* Check if ICE setup is complete and if it needs reinvite */ + for (i = 0; i < call->med_cnt && ice_complete; ++i) { + pjsua_call_media *call_med = &call->media[i]; + pjmedia_transport_info tpinfo; + unsigned j; + + if (call_med->tp_st == PJSUA_MED_TP_NULL || + call_med->tp_st == PJSUA_MED_TP_DISABLED || + call_med->state == PJSUA_CALL_MEDIA_ERROR) + { + continue; + } + + pjmedia_transport_info_init(&tpinfo); + pjmedia_transport_get_info(call_med->tp, &tpinfo); + for (j = 0; j < tpinfo.specific_info_cnt; ++j) { + pjmedia_ice_transport_info *ice_info; + + if (tpinfo.spc_info[j].type != PJMEDIA_TRANSPORT_TYPE_ICE) + continue; + + ice_info = (pjmedia_ice_transport_info*)tpinfo.spc_info[j].buffer; + + /* Check if ICE setup not completed yet */ + if (ice_info->sess_state < PJ_ICE_STRANS_STATE_RUNNING) { + ice_complete = PJ_FALSE; + break; + } + + /* Check if ICE needs to send reinvite */ + if (!ice_need_reinv && + ice_info->sess_state == PJ_ICE_STRANS_STATE_RUNNING && + ice_info->role == PJ_ICE_SESS_ROLE_CONTROLLING) + { + pjsua_ice_config *cfg=&pjsua_var.acc[call->acc_id].cfg.ice_cfg; + if ((cfg->ice_always_update && !call->reinv_ice_sent) || + pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name, + &call_med->rtp_addr)) + { + ice_need_reinv = PJ_TRUE; + } + } + break; + } } - - return status; + + if (ice_complete && need_reinv) + *need_reinv = ice_need_reinv; + + return ice_complete; } -/* Check if remote answerer has given us more than one codecs. If so, - * create another offer with one codec only to lock down the codec. - */ -static pj_status_t lock_codec(pjsua_call *call) +/* Check and send reinvite for lock codec and ICE update */ +static pj_status_t process_pending_reinvite(pjsua_call *call) { + const pj_str_t ST_UPDATE = {"UPDATE", 6}; + pj_pool_t *pool = call->inv->pool_prov; pjsip_inv_session *inv = call->inv; - const pjmedia_sdp_session *local_sdp, *remote_sdp; - pj_time_val delay = {0, 0}; - const pj_str_t st_update = {"UPDATE", 6}; + pj_bool_t ice_need_reinv; + pj_bool_t ice_completed; + pj_bool_t need_lock_codec; + pj_bool_t rem_can_update; + pjmedia_sdp_session *new_offer; + pjsip_tx_data *tdata; unsigned i; - pj_bool_t has_mult_fmt = PJ_FALSE; pj_status_t status; - if (!pjsua_var.acc[call->acc_id].cfg.lock_codec) { - return PJ_SUCCESS; - } - - /* Stop lock codec timer, if it is active */ - if (call->lock_codec.reinv_timer.id) { - pjsip_endpt_cancel_timer(pjsua_var.endpt, - &call->lock_codec.reinv_timer); - call->lock_codec.reinv_timer.id = PJ_FALSE; + /* Verify if another SDP negotiation is in progress, e.g: session timer + * or another re-INVITE. + */ + if (inv==NULL || inv->neg==NULL || + pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) + { + return PJMEDIA_SDPNEG_EINSTATE; } - /* Skip this if we are the answerer */ - if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) { - return PJ_SUCCESS; + /* Don't do this if call is disconnecting! */ + if (inv->state > PJSIP_INV_STATE_CONFIRMED || inv->cause >= 200) + { + return PJ_EINVALIDOP; } /* Delay this when the SDP negotiation done in call state EARLY and * remote does not support UPDATE method. */ if (inv->state == PJSIP_INV_STATE_EARLY && - pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!= + pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &ST_UPDATE)!= PJSIP_DIALOG_CAP_SUPPORTED) { - call->lock_codec.pending = PJ_TRUE; - return PJ_SUCCESS; + call->reinv_pending = PJ_TRUE; + return PJ_EPENDING; } - status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp); - if (status != PJ_SUCCESS) - return status; - status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp); - if (status != PJ_SUCCESS) - return status; + /* Check if ICE setup is complete and if it needs reinvite */ + ice_completed = check_ice_complete(call, &ice_need_reinv); + if (!ice_completed) + return PJ_EPENDING; - /* Find multiple codecs answer in all media */ - for (i = 0; i < call->med_cnt; ++i) { - pjsua_call_media *call_med = &call->media[i]; - const pjmedia_sdp_media *rem_m, *loc_m; - unsigned codec_cnt = 0; + /* Check if we need to lock codec */ + need_lock_codec = check_lock_codec(call); - /* Skip this if the media is inactive or error */ - if (call_med->state == PJSUA_CALL_MEDIA_NONE || - call_med->state == PJSUA_CALL_MEDIA_ERROR || - call_med->dir == PJMEDIA_DIR_NONE) - { - continue; - } + /* Check if reinvite is really needed */ + if (!need_lock_codec && !ice_need_reinv) + return PJ_SUCCESS; - /* Remote may answer with less media lines. */ - if (i >= remote_sdp->media_count) - continue; + + /* Okay! So we need to send re-INVITE/UPDATE */ - rem_m = remote_sdp->media[i]; - loc_m = local_sdp->media[i]; + /* Check if remote support UPDATE */ + rem_can_update = pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, + &ST_UPDATE) == + PJSIP_DIALOG_CAP_SUPPORTED; - /* Verify that media must be active. */ - pj_assert(loc_m->desc.port && rem_m->desc.port); + /* Logging stuff */ + { + const char *ST_ICE_UPDATE = "ICE transport address after " + "ICE negotiation"; + const char *ST_LOCK_CODEC = "media session to use only one codec"; + PJ_LOG(4,(THIS_FILE, "Call %d sending %s for updating %s%s%s", + call->index, + (rem_can_update? "UPDATE" : "re-INVITE"), + (ice_need_reinv? ST_ICE_UPDATE : ST_LOCK_CODEC), + (ice_need_reinv && need_lock_codec? " and " : ""), + (ice_need_reinv && need_lock_codec? ST_LOCK_CODEC : "") + )); + } + + /* Generate SDP re-offer */ + status = pjsua_media_channel_create_sdp(call->index, pool, NULL, + &new_offer, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create local SDP", status); + return status; + } - /* Count the formats in the answer. */ - if (rem_m->desc.fmt_count==1) { - codec_cnt = 1; - } else { - unsigned j; - for (j=0; jdesc.fmt_count && codec_cnt <= 1; ++j) { - if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j])) - ++codec_cnt; - } + /* Update the new offer so it contains only a codec. Note that + * SDP nego has removed unmatched codecs from the offer and the codec + * order in the offer has been matched to the answer, so we'll override + * the codecs in the just generated SDP with the ones from the active + * local SDP and leave just one codec for the next SDP re-offer. + */ + if (need_lock_codec) { + const pjmedia_sdp_session *ref_sdp; + + /* Get local active SDP as reference */ + status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &ref_sdp); + if (status != PJ_SUCCESS) + return status; + + /* Verify media count. Note that remote may add/remove media line + * in the answer. When answer has less media, it must have been + * handled by pjsua_media_channel_update() as disabled media. + * When answer has more media, it must have been ignored (treated + * as non-exist) anywhere. Local media count should not be updated + * at this point, as modifying media count operation (i.e: reinvite, + * update, vid_set_strm) is currently blocking, protected with + * dialog mutex, and eventually reset SDP nego state to LOCAL OFFER. + */ + if (call->med_cnt != ref_sdp->media_count || + ref_sdp->media_count != new_offer->media_count) + { + /* Anyway, just in case, let's just return error */ + return PJMEDIA_SDPNEG_EINSTATE; } - if (codec_cnt > 1) { - has_mult_fmt = PJ_TRUE; - break; + for (i = 0; i < call->med_cnt; ++i) { + unsigned j, codec_cnt = 0; + const pjmedia_sdp_media *ref_m = ref_sdp->media[i]; + pjmedia_sdp_media *m = new_offer->media[i]; + pjsua_call_media *call_med = &call->media[i]; + + /* Verify if media is deactivated */ + if (call_med->state == PJSUA_CALL_MEDIA_NONE || + call_med->state == PJSUA_CALL_MEDIA_ERROR || + call_med->dir == PJMEDIA_DIR_NONE) + { + continue; + } + + /* Reset formats */ + m->desc.fmt_count = 0; + pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, "rtpmap"); + pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, "fmtp"); + + /* Copy only the first format + any non-AV formats from + * the active local SDP. + */ + for (j = 0; j < ref_m->desc.fmt_count; ++j) { + const pj_str_t *fmt = &ref_m->desc.fmt[j]; + + if (is_non_av_fmt(ref_m, fmt) || (++codec_cnt == 1)) { + pjmedia_sdp_attr *a; + + m->desc.fmt[m->desc.fmt_count++] = *fmt; + a = pjmedia_sdp_attr_find2(ref_m->attr_count, ref_m->attr, + "rtpmap", fmt); + if (a) pjmedia_sdp_attr_add(&m->attr_count, m->attr, a); + a = pjmedia_sdp_attr_find2(ref_m->attr_count, ref_m->attr, + "fmtp", fmt); + if (a) pjmedia_sdp_attr_add(&m->attr_count, m->attr, a); + } + + } } } + + if (rem_can_update) { + status = pjsip_inv_update(inv, NULL, new_offer, &tdata); + } else { + status = pjsip_inv_reinvite(inv, NULL, new_offer, &tdata); + } - /* Each media in the answer already contains single codec. */ - if (!has_mult_fmt) { - call->lock_codec.retry_cnt = 0; + if (status==PJ_EINVALIDOP && + ++call->lock_codec.retry_cnt < LOCK_CODEC_MAX_RETRY) + { + /* Ups, let's reschedule again */ + pjsua_call_schedule_reinvite_check(call, LOCK_CODEC_RETRY_INTERVAL); return PJ_SUCCESS; + } else if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE", + status); + return status; } - /* Remote keeps answering with multiple codecs, let's just give up - * locking codec to avoid infinite retry loop. - */ - if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY) - return PJ_SUCCESS; - - PJ_LOG(4, (THIS_FILE, "Got answer with multiple codecs, scheduling " - "updating media session to use only one codec..")); - - call->lock_codec.sdp_ver = local_sdp->origin.version; - - /* Can't send UPDATE or re-INVITE now, so just schedule it immediately. - * See: https://trac.pjsip.org/repos/ticket/1149 - */ - pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE, - (void*)(pj_size_t)call->index, - &reinv_timer_cb); - pjsip_endpt_schedule_timer(pjsua_var.endpt, - &call->lock_codec.reinv_timer, &delay); + /* Send the UPDATE/re-INVITE request */ + status = pjsip_inv_send_msg(inv, tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE", + status); + return status; + } + /* Update flags */ + if (ice_need_reinv) + call->reinv_ice_sent = PJ_TRUE; + if (need_lock_codec) + ++call->lock_codec.retry_cnt; + return PJ_SUCCESS; } + /* * This callback receives notification from invite session when the * session state has changed. @@ -3242,16 +3334,12 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv, case PJSIP_INV_STATE_CONFIRMED: pj_gettimeofday(&call->conn_time); - /* See if lock codec was pended as media update was done in the + /* See if auto reinvite was pended as media update was done in the * EARLY state and remote does not support UPDATE. */ - if (call->lock_codec.pending) { - pj_status_t status; - status = lock_codec(call); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to lock codec", status); - } - call->lock_codec.pending = PJ_FALSE; + if (call->reinv_pending) { + call->reinv_pending = PJ_FALSE; + pjsua_call_schedule_reinvite_check(call, 0); } break; case PJSIP_INV_STATE_DISCONNECTED: @@ -3273,11 +3361,10 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv, sizeof(call->last_text_buf_)); } - /* Stop lock codec timer, if it is active */ - if (call->lock_codec.reinv_timer.id) { - pjsip_endpt_cancel_timer(pjsua_var.endpt, - &call->lock_codec.reinv_timer); - call->lock_codec.reinv_timer.id = PJ_FALSE; + /* Stop reinvite timer, if it is active */ + if (call->reinv_timer.id) { + pjsua_cancel_timer(&call->reinv_timer); + call->reinv_timer.id = PJ_FALSE; } break; default: @@ -3560,10 +3647,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv, } /* Ticket #476: make sure only one codec is specified in the answer. */ - status = lock_codec(call); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to lock codec", status); - } + pjsua_call_schedule_reinvite_check(call, 0); /* Call application callback, if any */ if (pjsua_var.ua_cfg.cb.on_call_media_state) diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 2a0065e6..149de86a 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -211,6 +211,7 @@ PJ_DEF(void) pjsua_ice_config_from_media_config( pj_pool_t *pool, dst->ice_max_host_cands = src->ice_max_host_cands; dst->ice_opt = src->ice_opt; dst->ice_no_rtcp = src->ice_no_rtcp; + dst->ice_always_update = src->ice_always_update; } PJ_DEF(void) pjsua_ice_config_dup( pj_pool_t *pool, @@ -324,6 +325,7 @@ PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg) cfg->snd_auto_close_time = 1; cfg->ice_max_host_cands = -1; + cfg->ice_always_update = PJ_TRUE; pj_ice_sess_options_default(&cfg->ice_opt); cfg->turn_conn_type = PJ_TURN_TP_UDP; diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index b2bf2989..f891201a 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -585,75 +585,6 @@ static void med_tp_timer_cb(void *user_data) } } -static void med_tp_nego_timer_cb(void *user_data) -{ - pjsua_call *call; - unsigned med_idx = (unsigned)(((long)user_data) & 0xFFFF); - pjsua_call_media *call_med; - pjmedia_transport *tp; - pjmedia_transport_info tpinfo; - pjmedia_ice_transport_info *ii = NULL; - unsigned i; - pjsip_dialog *dlg = NULL; - - if ((acquire_call("med_tp_nego_timer_cb", ((long)user_data) >> 16, - &call, &dlg) != PJ_SUCCESS) || - (med_idx >= call->med_cnt)) - { - /* Call have been terminated or media has been removed */ - return; - } - - call_med = &call->media[med_idx]; - tp = call_med->tp; - - if (!tp) - return; - - /* Send UPDATE if default transport address is different than - * what was advertised (ticket #881) - */ - - pjmedia_transport_info_init(&tpinfo); - pjmedia_transport_get_info(tp, &tpinfo); - for (i=0; irole==PJ_ICE_SESS_ROLE_CONTROLLING && - pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name, - &call_med->rtp_addr)) - { - pj_bool_t use_update; - const pj_str_t STR_UPDATE = { "UPDATE", 6 }; - pjsip_dialog_cap_status support_update; - pjsip_dialog *dlg; - - dlg = call_med->call->inv->dlg; - support_update = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_ALLOW, - NULL, &STR_UPDATE); - use_update = (support_update == PJSIP_DIALOG_CAP_SUPPORTED); - - PJ_LOG(4,(THIS_FILE, - "ICE default transport address has changed for " - "call %d, sending %s", - call_med->call->index, - (use_update ? "UPDATE" : "re-INVITE"))); - - if (use_update) - pjsua_call_update(call_med->call->index, 0, NULL); - else - pjsua_call_reinvite(call_med->call->index, 0, NULL); - } - - if (dlg) - pjsip_dlg_dec_lock(dlg); -} - /* This callback is called when ICE negotiation completes */ static void on_ice_complete(pjmedia_transport *tp, @@ -661,34 +592,41 @@ static void on_ice_complete(pjmedia_transport *tp, pj_status_t result) { pjsua_call_media *call_med = (pjsua_call_media*)tp->user_data; + pjsua_call *call; if (!call_med) return; + call = call_med->call; + switch (op) { case PJ_ICE_STRANS_OP_INIT: call_med->tp_result = result; pjsua_schedule_timer2(&med_tp_timer_cb, call_med, 1); break; case PJ_ICE_STRANS_OP_NEGOTIATION: - if (result != PJ_SUCCESS) { + if (result == PJ_SUCCESS) { + /* Update RTP address */ + pjmedia_transport_info tpinfo; + pjmedia_transport_info_init(&tpinfo); + pjmedia_transport_get_info(call_med->tp, &tpinfo); + pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name); + } else { call_med->state = PJSUA_CALL_MEDIA_ERROR; call_med->dir = PJMEDIA_DIR_NONE; - - if (call_med->call && pjsua_var.ua_cfg.cb.on_call_media_state) { - pjsua_var.ua_cfg.cb.on_call_media_state(call_med->call->index); + if (call && pjsua_var.ua_cfg.cb.on_call_media_state) { + pjsua_var.ua_cfg.cb.on_call_media_state(call->index); } - } else if (call_med->call) { - void *data = (void*)(long)( (call_med->call->index<<16) | - (call_med->idx & 0xFFFF) ); - pjsua_schedule_timer2(&med_tp_nego_timer_cb, data, 1); } + /* Check if default ICE transport address is changed */ + call->reinv_ice_sent = PJ_FALSE; + pjsua_call_schedule_reinvite_check(call, 0); break; case PJ_ICE_STRANS_OP_KEEP_ALIVE: if (result != PJ_SUCCESS) { PJ_PERROR(4,(THIS_FILE, result, "ICE keep alive failure for transport %d:%d", - call_med->call->index, call_med->idx)); + call->index, call_med->idx)); } if (pjsua_var.ua_cfg.cb.on_call_media_transport_state) { pjsua_med_tp_state_info info; @@ -699,10 +637,10 @@ static void on_ice_complete(pjmedia_transport *tp, info.status = result; info.ext_info = &op; (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)( - call_med->call->index, &info); + call->index, &info); } if (pjsua_var.ua_cfg.cb.on_ice_transport_error) { - pjsua_call_id id = call_med->call->index; + pjsua_call_id id = call->index; (*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result, NULL); } @@ -855,7 +793,7 @@ static pj_status_t create_ice_media_transport( pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING, pjsua_var.media_cfg.rx_drop_pct); - + return PJ_SUCCESS; on_error: @@ -1339,7 +1277,7 @@ pj_status_t pjsua_call_media_init(pjsua_call_media *call_med, */ call_med->med_create_cb = &call_media_init_cb; call_med->med_init_cb = cb; - + return PJ_EPENDING; } } else { -- cgit v1.2.3