summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2013-02-06 13:48:45 +0000
committerNanang Izzuddin <nanang@teluu.com>2013-02-06 13:48:45 +0000
commit7b7c7c8b42a8c25b30b07a8cd524cccbb60173b3 (patch)
treef36841faa9176c48253b75b1a049956ce50d314c
parentb561a5d018b621b07690a8183d5e1f54322fd8fc (diff)
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
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h20
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h11
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c520
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c2
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c102
5 files changed, 352 insertions, 303 deletions
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;
/**
@@ -5551,6 +5561,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.
*/
pj_bool_t enable_turn;
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; j<rem_m->desc.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; j<rem_m->desc.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; i<tpinfo.specific_info_cnt; ++i) {
- if (tpinfo.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
- ii = (pjmedia_ice_transport_info*)
- tpinfo.spc_info[i].buffer;
- break;
- }
- }
-
- if (ii && ii->role==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 {