summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h1
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c137
2 files changed, 82 insertions, 56 deletions
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index dc91d231..188c49dc 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -95,6 +95,7 @@ typedef struct pjsua_call
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. */
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index c3ff7561..5a4ab2f7 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -123,6 +123,7 @@ static void reset_call(pjsua_call_id id)
call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
call->rem_srtp_use = PJMEDIA_SRTP_DISABLED;
call->local_hold = PJ_FALSE;
+ pj_bzero(&call->lock_codec, sizeof(call->lock_codec));
}
@@ -3060,7 +3061,7 @@ static pj_status_t perform_lock_codec(pjsua_call *call)
{
const pj_str_t STR_UPDATE = {"UPDATE", 6};
const pjmedia_sdp_session *local_sdp = NULL, *new_sdp;
- const pjmedia_sdp_media *rem_m;
+ const pjmedia_sdp_media *ref_m;
pjmedia_sdp_media *m;
unsigned i, codec_cnt = 0;
pj_bool_t rem_can_update;
@@ -3076,14 +3077,14 @@ static pj_status_t perform_lock_codec(pjsua_call *call)
if (call->inv==NULL || call->inv->neg==NULL ||
pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
{
- return PJ_SUCCESS;
+ return PJMEDIA_SDPNEG_EINSTATE;
}
/* Don't do this if call is disconnecting! */
if (call->inv->state > PJSIP_INV_STATE_CONFIRMED ||
call->inv->cause >= 200)
{
- return PJ_SUCCESS;
+ return PJ_EINVALIDOP;
}
/* Verify if another SDP negotiation has been completed by comparing
@@ -3095,6 +3096,14 @@ static pj_status_t perform_lock_codec(pjsua_call *call)
if (local_sdp->origin.version > call->lock_codec.sdp_ver)
return PJMEDIA_SDP_EINVER;
+ /* Verify if media is deactivated */
+ if (call->media_st == PJSUA_CALL_MEDIA_NONE ||
+ call->media_st == PJSUA_CALL_MEDIA_ERROR ||
+ call->media_dir == PJMEDIA_DIR_NONE)
+ {
+ return PJ_EINVALIDOP;
+ }
+
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
@@ -3102,8 +3111,9 @@ static pj_status_t perform_lock_codec(pjsua_call *call)
* just directly update the offer without looking-up the answer.
*/
new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp);
- rem_m = local_sdp->media[call->audio_idx];
m = new_sdp->media[call->audio_idx];
+ ref_m = local_sdp->media[call->audio_idx];
+ pj_assert(ref_m->desc.port);
codec_cnt = 0;
i = 0;
while (i < m->desc.fmt_count) {
@@ -3125,15 +3135,12 @@ static pj_status_t perform_lock_codec(pjsua_call *call)
--m->desc.fmt_count;
}
- /* Last check if SDP trully needs to be udpated. It is possible that OA
- * negotiations have completed and SDP has changed but remote didn't
- * increase it's SDP version.
+ /* 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 (rem_m->desc.fmt_count == m->desc.fmt_count ||
- rem_m->desc.port == 0 || m->desc.port == 0)
- {
+ if (ref_m->desc.fmt_count == m->desc.fmt_count)
return PJ_SUCCESS;
- }
/* Send UPDATE or re-INVITE */
rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg,
@@ -3179,20 +3186,48 @@ static pj_status_t perform_lock_codec(pjsua_call *call)
static pj_status_t lock_codec(pjsua_call *call)
{
pjsip_inv_session *inv = call->inv;
- const pjmedia_sdp_session *local_sdp;
- const pjmedia_sdp_session *remote_sdp;
- const pjmedia_sdp_media *rem_m;
+ const pjmedia_sdp_session *local_sdp, *remote_sdp;
+ const pjmedia_sdp_media *rem_m, *loc_m;
unsigned codec_cnt=0, i;
pj_time_val delay = {0, 0};
+ const pj_str_t st_update = {"UPDATE", 6};
pj_status_t status;
- if (!pjmedia_sdp_neg_was_answer_remote(inv->neg))
- 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;
+ }
- status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
+ /* Skip this if we are the answerer */
+ if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
+ return PJ_SUCCESS;
+ }
+
+ /* Skip this if the media is inactive or error */
+ if (call->media_st == PJSUA_CALL_MEDIA_NONE ||
+ call->media_st == PJSUA_CALL_MEDIA_ERROR ||
+ call->media_dir == PJMEDIA_DIR_NONE)
+ {
+ return PJ_SUCCESS;
+ }
+
+ /* 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_DIALOG_CAP_SUPPORTED)
+ {
+ call->lock_codec.pending = PJ_TRUE;
+ return PJ_SUCCESS;
+ }
+
+ status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
if (status != PJ_SUCCESS)
return status;
- status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
+ status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
if (status != PJ_SUCCESS)
return status;
@@ -3201,34 +3236,36 @@ static pj_status_t lock_codec(pjsua_call *call)
PJ_EINVALIDOP);
rem_m = remote_sdp->media[call->audio_idx];
+ loc_m = local_sdp->media[call->audio_idx];
- /* Check if media is disabled or only one format in the answer. */
- if (rem_m->desc.port==0 || rem_m->desc.fmt_count==1)
- return PJ_SUCCESS;
+ /* Verify that media must be active. */
+ pj_assert(loc_m->desc.port && rem_m->desc.port);
/* Count the formats in the answer. */
- for (i=0; i<rem_m->desc.fmt_count && codec_cnt <= 1; ++i) {
- if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[i]))
- ++codec_cnt;
+ if (rem_m->desc.fmt_count==1) {
+ codec_cnt = 1;
+ } else {
+ for (i=0; i<rem_m->desc.fmt_count && codec_cnt <= 1; ++i) {
+ if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[i]))
+ ++codec_cnt;
+ }
}
-
if (codec_cnt <= 1) {
/* Answer contains single codec. */
+ call->lock_codec.retry_cnt = 0;
return PJ_SUCCESS;
}
+ /* 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(3, (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;
- call->lock_codec.retry_cnt = 0;
-
- /* 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;
- }
/* Can't send UPDATE or re-INVITE now, so just schedule it immediately.
* See: https://trac.pjsip.org/repos/ticket/1149
@@ -3276,15 +3313,17 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
case PJSIP_INV_STATE_CONFIRMED:
pj_gettimeofday(&call->conn_time);
- /* Ticket #476, locking a codec in the media session. */
- {
+ /* See if lock codec 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;
}
-
break;
case PJSIP_INV_STATE_DISCONNECTED:
pj_gettimeofday(&call->dis_time);
@@ -3515,7 +3554,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
pjsua_call *call;
const pjmedia_sdp_session *local_sdp;
const pjmedia_sdp_session *remote_sdp;
- const pj_str_t st_update = {"UPDATE", 6};
+ //const pj_str_t st_update = {"UPDATE", 6};
PJSUA_LOCK();
@@ -3587,24 +3626,10 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
return;
}
- /* Ticket #476, handle the case of early media and remote support UPDATE */
- if (inv->state == PJSIP_INV_STATE_EARLY &&
- pjmedia_sdp_neg_was_answer_remote(inv->neg) &&
- pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)==
- PJSIP_DIALOG_CAP_SUPPORTED)
- {
- status = lock_codec(call);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to lock codec", status);
- }
- } else if (inv->state == PJSIP_INV_STATE_CONFIRMED &&
- call->media_st == PJSUA_CALL_MEDIA_ACTIVE)
- {
- /* https://trac.pjsip.org/repos/ticket/1149 */
- status = lock_codec(call);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to lock codec", status);
- }
+ /* 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);
}
/* Call application callback, if any */