Pjsip在使用过程中,如果网络环境不好,sip信令在交互过程中,会出现超时的情况,此时,sip信令会重复发送信令,直到收到信令的反馈或者sip会话超时退出通话。
针对上诉的情况,需要修改sip信令的超时时间,以便适应复杂的网络情况。
sip信令的传输代码主要在:pjsip/src/pjsip/sip_transaction.c中。
sip_transaction.c实现了一个状态机,根据sip信令交互的不同的信令,切换状态机。
UAC (呼叫方)状态机转换如下:
刚开始呼叫时,sip_transaction的状态机处于tsx_on_state_null状态,拨打电话发出INVITE信令后,状态机转为Calling状态,处理函数:tsx_on_state_calling,应用可以收到Calling的状态通知。
之后会收到被叫方发来的100 try信令,状态机转为Processing状态,处理函数:tsx_on_state_proceeding_uac
在收到180 ringing的信令后,还是在tsx_on_state_proceeding_uac中处理,应用会收到Early的状态通知。
如果被叫方接听了该呼叫,会收到被叫方的200 OK信令,状态机会转为Terminated状态,处理函数:tsx_on_state_terminated,应用会收到Connecting的状态通知,之后会通过timeout方式,把状态转为Destroyed。Call的状态也转为Confirmed。
Bye信令状态转换:
发出Bye信令时,状态机转为Calling状态(tsx_on_state_calling),在收到200OK信令时,转为Completed状态(tsx_on_state_completed_uac),应用会收到Disconnected的状态通知
,之后通过timeout的方式,状态转为Terminated和Destroyed
UAS(被叫方)状态机转换如下:
Bye状态转换:
被叫方的转换和呼叫方类似。
sip的超时时间修改:
INVITE发送超时时间修改,在函数:tsx_on_state_null中
sip信令一般使用UDP进行传输,属于不可靠的传输,所以在发送完INVITE信令后,会加入超时机制。超时时间为t1_timer_val时间间隔。
/* Start Timer A (or timer E) for retransmission only if unreliable
* transport is being used.
*/
if (!tsx->is_reliable) {
tsx->retransmit_count = 0;
if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
} else {
tsx_schedule_timer(tsx, &tsx->retransmit_timer,
&t1_timer_val, RETRANSMIT_TIMER);
}
}
/* Move state. */
tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING,
PJSIP_EVENT_TX_MSG, tdata, 0);
t1_timer_val的定义在,文件的开头:
/* Timer timeout value constants */
static pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000,
PJSIP_T1_TIMEOUT%1000 };
/** Transaction T1 timeout value. */
#if !defined(PJSIP_T1_TIMEOUT)
# define PJSIP_T1_TIMEOUT 500
#endif
所以INVITE发送后,首次的超时时间为500ms,第二次超时在tsx_on_state_calling函数中
if (event->type == PJSIP_EVENT_TIMER &&
event->body.timer.entry == &tsx->retransmit_timer)
{
pj_status_t status;
/* Retransmit the request. */
status = tsx_retransmit( tsx, 1 );
if (status != PJ_SUCCESS) {
return status;
}
}
/*
* Retransmit last message sent.
*/
static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched)
{
pj_status_t status;
if (resched && pj_timer_entry_running(&tsx->retransmit_timer)) {
/* We've been asked to reschedule but the timer is already rerunning.
* This can only happen in a race condition where, between removing
* this retransmit timer from the heap and actually scheduling it,
* another thread has got in and rescheduled the timer itself. In
* this scenario, the transmission has already happened and so we
* should just quit out immediately, without either resending the
* message or restarting the timer.
*/
return PJ_SUCCESS;
}
PJ_ASSERT_RETURN(tsx->last_tx!=NULL, PJ_EBUG);
PJ_LOG(5,(tsx->obj_name, "Retransmiting %s, count=%d, restart?=%d",
pjsip_tx_data_get_info(tsx->last_tx),
tsx->retransmit_count, resched));
++tsx->retransmit_count;
/* Restart timer T1 first before sending the message to ensure that
* retransmission timer is not engaged when loop transport is used.
*/
if (resched) {
pj_assert(tsx->state != PJSIP_TSX_STATE_CONFIRMED);
if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
tsx->transport_flag |= TSX_HAS_PENDING_RESCHED;
} else {
tsx_resched_retransmission(tsx);
}
}
status = tsx_send_msg( tsx, tsx->last_tx);
if (status != PJ_SUCCESS) {
return status;
}
return PJ_SUCCESS;
}
/*
* Retransmit last message sent.
*/
static void tsx_resched_retransmission( pjsip_transaction *tsx )
{
pj_uint32_t msec_time;
pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0);
if (tsx->role==PJSIP_ROLE_UAC && tsx->status_code >= 100)
msec_time = pjsip_cfg()->tsx.t2;
else
msec_time = (1 << (tsx->retransmit_count)) * pjsip_cfg()->tsx.t1;
......
}
第二次的超时:msec_time = (1 << (tsx->retransmit_count)) * pjsip_cfg()->tsx.t1 ;就是第一次的超时*2,在1秒钟,第三次,在*2,知道timeout触发。
INVITE周期的timeout时间修改,在tsx_on_state_null函数中,只要修改timeout_timer_val的值即可:
lock_timer(tsx);
tsx_cancel_timer( tsx, &tsx->timeout_timer );
tsx_schedule_timer( tsx, &tsx->timeout_timer, &timeout_timer_val,
TIMEOUT_TIMER);
unlock_timer(tsx);