More works on Trickle ICE (#2667)
- Improve trickling state management (fix no SIP INFO when initial INVITE responded immediately with 200, strayed SIP INFO after trickling is done, etc). - Fix issues when rtcp-mux is enabled. - Allow process incoming SIP INFO before receiving remote SDP. - Use regular ICE on re-INVITE (with reinit media flag). - Avoid calling pj_ice_strans_get_running_comp_cnt() for loop condition. - Fix bug in pjnath-test: TURN server set wrong peer channel number. - Added timer for end-of-candidate indication from remote & don't flag ice-mismatch if remote uses default address in trickle ICE
This commit is contained in:
parent
b3e51c7a7d
commit
79531cbc05
|
@ -323,15 +323,18 @@ PJ_DECL(pj_bool_t) pjmedia_ice_sdp_has_trickle(const pjmedia_sdp_session *sdp,
|
|||
|
||||
|
||||
/**
|
||||
* Update check list after new local or remote ICE candidates are added,
|
||||
* or signal ICE session that trickling is done. Application typically would
|
||||
* call this function after finding (and conveying) new local ICE candidates
|
||||
* to remote, after receiving remote ICE candidates, or after receiving
|
||||
* end-of-candidates indication.
|
||||
* Update check list after remote ICE candidates list are received or after
|
||||
* or local ICE candidates are conveyed. This function may also be called
|
||||
* after end-of-candidates indication is received or conveyed. ICE
|
||||
* connectivity checks will automatically be started if both sides have
|
||||
* conveyed ICE info (ICE user fragment and/or candidate list).
|
||||
*
|
||||
* This function is only applicable when trickle ICE is not disabled and
|
||||
* after ICE connectivity checks are started, i.e: after
|
||||
* pjmedia_transport_media_start() has been invoked.
|
||||
* To update the check list after conveying any new local candidates,
|
||||
* application can set the remote ICE parameters to NULL or zero. Note that
|
||||
* the checklist should only be updated after any newly found local candidates
|
||||
* are conveyed to remote, instead of immediately after the finding.
|
||||
*
|
||||
* This function is only applicable when trickle ICE is not disabled.
|
||||
*
|
||||
* @param tp The ICE media transport.
|
||||
* @param rem_ufrag Remote ufrag, as seen in the SDP received from
|
||||
|
@ -412,7 +415,8 @@ PJ_DECL(pj_status_t) pjmedia_ice_trickle_encode_sdp(
|
|||
|
||||
|
||||
/**
|
||||
* Check if trickling ICE has found any new local candidates.
|
||||
* Check if trickling ICE has found any new local candidates since the last
|
||||
* conveyance (via pjmedia_ice_trickle_send_local_cand()).
|
||||
*
|
||||
* @param tp The ICE media transport.
|
||||
*
|
||||
|
@ -422,9 +426,7 @@ PJ_DECL(pj_bool_t) pjmedia_ice_trickle_has_new_cand(pjmedia_transport *tp);
|
|||
|
||||
|
||||
/**
|
||||
* Check for new local candidates, and if any new or forced, update the
|
||||
* specified SDP with all current local candidates to be conveyed to remote
|
||||
* (e.g: via SIP INFO).
|
||||
* Convey all local candidates via the specified SDP.
|
||||
*
|
||||
* @param tp The ICE media transport.
|
||||
* @param sdp_pool The memory pool for generating SDP attributes.
|
||||
|
@ -432,10 +434,7 @@ PJ_DECL(pj_bool_t) pjmedia_ice_trickle_has_new_cand(pjmedia_transport *tp);
|
|||
* @param p_end_of_cand Optional, pointer to receive the indication that
|
||||
* candidate gathering has been completed.
|
||||
*
|
||||
* @return PJ_SUCCESS if any new local candidates is found and
|
||||
* SDP is updated with the candidates,
|
||||
* PJ_ENOTFOUND if no new local candidate is found,
|
||||
* or the appropriate error code.
|
||||
* @return PJ_SUCCESS, or the appropriate error code.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjmedia_ice_trickle_send_local_cand(
|
||||
pjmedia_transport *tp,
|
||||
|
|
|
@ -90,7 +90,8 @@ struct transport_ice
|
|||
unsigned rx_drop_pct; /**< Percent of rx pkts to drop. */
|
||||
|
||||
pj_ice_sess_trickle trickle_ice; /**< Trickle ICE mode. */
|
||||
unsigned last_cand_cnt; /**< Last local candidate count. */
|
||||
unsigned last_send_cand_cnt[PJ_ICE_MAX_COMP];
|
||||
/**< Last local candidate count. */
|
||||
pj_bool_t end_of_cand; /**< Local cand gathering done? */
|
||||
pj_str_t sdp_mid; /**< SDP "a=mid" attribute. */
|
||||
|
||||
|
@ -458,12 +459,42 @@ PJ_DEF(pj_status_t) pjmedia_ice_trickle_update(
|
|||
pj_bool_t rcand_end)
|
||||
{
|
||||
struct transport_ice *tp_ice = (struct transport_ice*)tp;
|
||||
pj_status_t status;
|
||||
|
||||
PJ_ASSERT_RETURN(tp_ice && tp_ice->ice_st, PJ_EINVAL);
|
||||
PJ_ASSERT_RETURN(tp_ice->trickle_ice != PJ_ICE_SESS_TRICKLE_DISABLED,
|
||||
PJ_EINVALIDOP);
|
||||
|
||||
return pj_ice_strans_update_check_list(tp_ice->ice_st,
|
||||
rem_ufrag, rem_passwd,
|
||||
rcand_cnt, rcand, rcand_end);
|
||||
|
||||
/* Update the checklist */
|
||||
status = pj_ice_strans_update_check_list(tp_ice->ice_st,
|
||||
rem_ufrag, rem_passwd,
|
||||
rcand_cnt, rcand, rcand_end);
|
||||
if (status != PJ_SUCCESS)
|
||||
return status;
|
||||
|
||||
/* Start ICE if both sides have sent their (initial) SDPs */
|
||||
if (!pj_ice_strans_sess_is_running(tp_ice->ice_st)) {
|
||||
unsigned i, comp_cnt;
|
||||
|
||||
comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st);
|
||||
for (i = 0; i < comp_cnt; ++i) {
|
||||
if (tp_ice->last_send_cand_cnt[i] > 0)
|
||||
break;
|
||||
}
|
||||
if (i != comp_cnt) {
|
||||
pj_str_t rufrag;
|
||||
pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, NULL, NULL,
|
||||
&rufrag, NULL);
|
||||
if (rufrag.slen > 0) {
|
||||
PJ_LOG(3,(THIS_FILE,"Trickle ICE starts connectivity check"));
|
||||
status = pj_ice_strans_start_ice(tp_ice->ice_st, NULL, NULL,
|
||||
0, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
@ -656,17 +687,22 @@ PJ_DEF(pj_status_t) pjmedia_ice_trickle_encode_sdp(
|
|||
PJ_DEF(pj_bool_t) pjmedia_ice_trickle_has_new_cand(pjmedia_transport *tp)
|
||||
{
|
||||
struct transport_ice *tp_ice = (struct transport_ice*)tp;
|
||||
unsigned i, cand_cnt = 0;
|
||||
unsigned i, comp_cnt;
|
||||
|
||||
/* Make sure ICE transport has session already */
|
||||
if (!tp_ice->ice_st || !pj_ice_strans_has_sess(tp_ice->ice_st))
|
||||
return PJ_FALSE;
|
||||
|
||||
/* Count all local candidates */
|
||||
for (i = 0; i < tp_ice->comp_cnt; ++i) {
|
||||
cand_cnt += pj_ice_strans_get_cands_count(tp_ice->ice_st, i+1);
|
||||
comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st);
|
||||
for (i = 0; i < comp_cnt; ++i) {
|
||||
if (tp_ice->last_send_cand_cnt[i] <
|
||||
pj_ice_strans_get_cands_count(tp_ice->ice_st, i+1))
|
||||
{
|
||||
return PJ_TRUE;
|
||||
}
|
||||
}
|
||||
return (cand_cnt > tp_ice->last_cand_cnt);
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
@ -681,9 +717,8 @@ PJ_DEF(pj_status_t) pjmedia_ice_trickle_send_local_cand(
|
|||
{
|
||||
struct transport_ice *tp_ice = (struct transport_ice*)tp;
|
||||
pj_str_t ufrag, pwd;
|
||||
pj_ice_strans_state ice_state;
|
||||
pj_ice_sess_cand cand[PJ_ICE_MAX_CAND];
|
||||
unsigned cand_cnt, i;
|
||||
unsigned cand_cnt, i, comp_cnt;
|
||||
pj_bool_t end_of_cand;
|
||||
pj_status_t status;
|
||||
|
||||
|
@ -693,14 +728,14 @@ PJ_DEF(pj_status_t) pjmedia_ice_trickle_send_local_cand(
|
|||
if (!tp_ice->ice_st || !pj_ice_strans_has_sess(tp_ice->ice_st))
|
||||
return PJ_EINVALIDOP;
|
||||
|
||||
ice_state = pj_ice_strans_get_state(tp_ice->ice_st);
|
||||
end_of_cand = tp_ice->end_of_cand;
|
||||
|
||||
/* Get ufrag and pwd from current session */
|
||||
pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, &ufrag, &pwd, NULL, NULL);
|
||||
|
||||
cand_cnt = 0;
|
||||
for (i = 0; i < tp_ice->comp_cnt; ++i) {
|
||||
comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st);
|
||||
for (i = 0; i < comp_cnt; ++i) {
|
||||
unsigned cnt = PJ_ICE_MAX_CAND - cand_cnt;
|
||||
|
||||
/* Get all local candidates for this comp */
|
||||
|
@ -713,6 +748,8 @@ PJ_DEF(pj_status_t) pjmedia_ice_trickle_send_local_cand(
|
|||
continue;
|
||||
}
|
||||
cand_cnt += cnt;
|
||||
|
||||
tp_ice->last_send_cand_cnt[i] = cnt;
|
||||
}
|
||||
|
||||
/* Update the SDP with all local candidates (not just the new ones).
|
||||
|
@ -727,21 +764,9 @@ PJ_DEF(pj_status_t) pjmedia_ice_trickle_send_local_cand(
|
|||
end_of_cand);
|
||||
if (status != PJ_SUCCESS) {
|
||||
PJ_PERROR(3,(tp_ice->base.name, status,
|
||||
"Failed adding new local candidates to SDP"));
|
||||
"Failed encoding local candidates to SDP"));
|
||||
}
|
||||
|
||||
/* Update ICE checklist if there is any new local candidate and
|
||||
* checklist has been created (e.g: ICE nego is running).
|
||||
*/
|
||||
if (tp_ice->last_cand_cnt < cand_cnt &&
|
||||
pj_ice_strans_sess_is_running(tp_ice->ice_st))
|
||||
{
|
||||
pjmedia_ice_trickle_update(tp, NULL, NULL, 0, NULL, PJ_FALSE);
|
||||
}
|
||||
|
||||
pj_assert(tp_ice->last_cand_cnt <= cand_cnt);
|
||||
tp_ice->last_cand_cnt = cand_cnt;
|
||||
|
||||
if (p_end_of_cand)
|
||||
*p_end_of_cand = end_of_cand;
|
||||
|
||||
|
@ -1134,7 +1159,6 @@ static pj_status_t encode_session_in_sdp(struct transport_ice *tp_ice,
|
|||
|
||||
/* Add trickle ICE attributes */
|
||||
if (trickle) {
|
||||
pj_ice_strans_state ice_state;
|
||||
pj_bool_t end_of_cand;
|
||||
|
||||
/* Add media ID attribute "a=mid" */
|
||||
|
@ -1144,7 +1168,6 @@ static pj_status_t encode_session_in_sdp(struct transport_ice *tp_ice,
|
|||
pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
|
||||
}
|
||||
|
||||
ice_state = pj_ice_strans_get_state(tp_ice->ice_st);
|
||||
end_of_cand = tp_ice->end_of_cand;
|
||||
status = pjmedia_ice_trickle_encode_sdp(sdp_pool, sdp_local,
|
||||
&tp_ice->sdp_mid, NULL, NULL,
|
||||
|
@ -1535,6 +1558,14 @@ static pj_status_t verify_ice_sdp(struct transport_ice *tp_ice,
|
|||
if (tp_ice->trickle_ice != PJ_ICE_SESS_TRICKLE_DISABLED) {
|
||||
sdp_state->has_trickle = pjmedia_ice_sdp_has_trickle(rem_sdp,
|
||||
media_index);
|
||||
|
||||
/* Reset ICE mismatch flag if conn addr is default address */
|
||||
if (sdp_state->ice_mismatch && sdp_state->has_trickle) {
|
||||
pj_sockaddr def_addr;
|
||||
pj_sockaddr_init(rem_af, &def_addr, NULL, 9);
|
||||
if (pj_sockaddr_cmp(&rem_conn_addr, &def_addr)==0)
|
||||
sdp_state->ice_mismatch = PJ_FALSE;
|
||||
}
|
||||
} else {
|
||||
sdp_state->has_trickle = PJ_FALSE;
|
||||
}
|
||||
|
@ -1837,6 +1868,34 @@ static pj_status_t transport_media_create(pjmedia_transport *tp,
|
|||
PJ_ICE_SESS_ROLE_CONTROLLED);
|
||||
status = pj_ice_strans_init_ice(tp_ice->ice_st, ice_role, NULL, NULL);
|
||||
|
||||
/* For trickle ICE, if remote SDP has been received, process any remote
|
||||
* ICE info now (ICE user fragment and/or initial ICE candidate list).
|
||||
*/
|
||||
if (rem_sdp && status == PJ_SUCCESS) {
|
||||
if (tp_ice->trickle_ice != PJ_ICE_SESS_TRICKLE_DISABLED &&
|
||||
pjmedia_ice_sdp_has_trickle(rem_sdp, media_index))
|
||||
{
|
||||
pj_str_t ufrag, pwd;
|
||||
unsigned cand_cnt = PJ_ICE_ST_MAX_CAND;
|
||||
pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND];
|
||||
pj_bool_t end_of_cand;
|
||||
|
||||
status = pjmedia_ice_trickle_decode_sdp(rem_sdp, media_index,
|
||||
NULL, &ufrag, &pwd,
|
||||
&cand_cnt, cand,
|
||||
&end_of_cand);
|
||||
if (status == PJ_SUCCESS)
|
||||
status = pj_ice_strans_update_check_list(
|
||||
tp_ice->ice_st, &ufrag, &pwd,
|
||||
cand_cnt, cand, end_of_cand);
|
||||
if (status != PJ_SUCCESS) {
|
||||
PJ_PERROR(1,(tp_ice->base.name, status,
|
||||
"Failed create checklist for trickling ICE"));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Done */
|
||||
return status;
|
||||
}
|
||||
|
@ -1904,10 +1963,10 @@ static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
|
|||
/* Update last local candidate count, so trickle ICE can identify
|
||||
* if there is any new local candidate.
|
||||
*/
|
||||
unsigned i;
|
||||
tp_ice->last_cand_cnt = 0;
|
||||
for (i = 0; i < tp_ice->comp_cnt; ++i) {
|
||||
tp_ice->last_cand_cnt +=
|
||||
unsigned i, comp_cnt;
|
||||
comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st);
|
||||
for (i = 0; i < comp_cnt; ++i) {
|
||||
tp_ice->last_send_cand_cnt[i] =
|
||||
pj_ice_strans_get_cands_count(tp_ice->ice_st, i+1);
|
||||
}
|
||||
}
|
||||
|
@ -2166,11 +2225,15 @@ static pj_status_t transport_media_start(pjmedia_transport *tp,
|
|||
}
|
||||
}
|
||||
|
||||
/* Now start ICE */
|
||||
status = start_ice(tp_ice, tmp_pool, rem_sdp, media_index);
|
||||
if (status != PJ_SUCCESS) {
|
||||
PJ_PERROR(1,(tp_ice->base.name, status, "ICE restart failed!"));
|
||||
return status;
|
||||
/* Now start ICE, if not yet (trickle ICE may have started it earlier) */
|
||||
if (!pj_ice_strans_sess_is_running(tp_ice->ice_st) &&
|
||||
!pj_ice_strans_sess_is_complete(tp_ice->ice_st))
|
||||
{
|
||||
status = start_ice(tp_ice, tmp_pool, rem_sdp, media_index);
|
||||
if (status != PJ_SUCCESS) {
|
||||
PJ_PERROR(1,(tp_ice->base.name, status, "ICE restart failed!"));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Done */
|
||||
|
|
|
@ -468,6 +468,30 @@
|
|||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* For trickle ICE, this macro specifies the maximum time of waiting for
|
||||
* end-of-candidates indication from remote once ICE connectivity checks
|
||||
* is started, in seconds. When the timer expires, ICE will assume that
|
||||
* end-of-candidates indication is received so any further remote candidate
|
||||
* update will be ignored.
|
||||
*
|
||||
* Note that without remote end-of-candidates indication, ICE will not be
|
||||
* able to conclude that the ICE negotiation has failed when all pair checks
|
||||
* are completed but there is no valid pair (on the other hand, the ICE
|
||||
* negotiation may be completed as successful before the end-of-candidates
|
||||
* indication is received when valid pairs are found very quickly).
|
||||
*
|
||||
* Also note that the ICE connectivity checks should only be started after
|
||||
* both agents have started trickling ICE candidates (e.g: both have sent
|
||||
* their SDPs, either via normal SDP offer/answer or SIP INFO).
|
||||
*
|
||||
* Default: 40 seconds.
|
||||
*/
|
||||
#ifndef PJ_TRICKLE_ICE_END_OF_CAND_TIMEOUT
|
||||
# define PJ_TRICKLE_ICE_END_OF_CAND_TIMEOUT 40
|
||||
#endif
|
||||
|
||||
|
||||
/** ICE session pool initial size. */
|
||||
#ifndef PJNATH_POOL_LEN_ICE_SESS
|
||||
# define PJNATH_POOL_LEN_ICE_SESS 512
|
||||
|
|
|
@ -704,6 +704,7 @@ struct pj_ice_sess
|
|||
sent/received? */
|
||||
pj_status_t ice_status; /**< Error status. */
|
||||
pj_timer_entry timer; /**< ICE timer. */
|
||||
pj_timer_entry timer_end_of_cand; /**< End-of-cand timer. */
|
||||
pj_ice_sess_cb cb; /**< Callback. */
|
||||
|
||||
pj_stun_config stun_cfg; /**< STUN settings. */
|
||||
|
@ -990,14 +991,11 @@ pj_ice_sess_create_check_list(pj_ice_sess *ice,
|
|||
|
||||
|
||||
/**
|
||||
* Update check list after new local or remote ICE candidates are added,
|
||||
* or signal ICE session that trickling is done. Application typically would
|
||||
* call this function after finding (and conveying) new local ICE candidates
|
||||
* to remote, after receiving remote ICE candidates, or after receiving
|
||||
* end-of-candidates indication.
|
||||
*
|
||||
* After check list is updated, ICE connectivity check will automatically
|
||||
* start if check list has any candidate pair.
|
||||
* Update check list after receiving new remote ICE candidates or after
|
||||
* new local ICE candidates are found and conveyed to remote. This function
|
||||
* can also be called to indicate that trickling has completed, i.e:
|
||||
* local candidates gathering completed and remote has sent end-of-candidate
|
||||
* indication.
|
||||
*
|
||||
* This function is only applicable when trickle ICE is not disabled.
|
||||
*
|
||||
|
|
|
@ -945,15 +945,15 @@ PJ_DECL(pj_status_t) pj_ice_strans_start_ice(pj_ice_strans *ice_st,
|
|||
unsigned rcand_cnt,
|
||||
const pj_ice_sess_cand rcand[]);
|
||||
|
||||
|
||||
/**
|
||||
* Update check list after new local or remote ICE candidates are added,
|
||||
* or signal ICE session that trickling is done. Application typically would
|
||||
* call this function after finding (and conveying) new local ICE candidates
|
||||
* to remote, after receiving remote ICE candidates, or after receiving
|
||||
* end-of-candidates indication.
|
||||
* Update check list after receiving new remote ICE candidates or after
|
||||
* new local ICE candidates are found and conveyed to remote. This function
|
||||
* can also be called after receiving end of candidate indication from
|
||||
* either remote or local agent.
|
||||
*
|
||||
* This function is only applicable when trickle ICE is not disabled and
|
||||
* after ICE connectivity checks are started using pj_ice_strans_start_ice().
|
||||
* after ICE session has been created using pj_ice_strans_init_ice().
|
||||
*
|
||||
* @param ice_st The ICE stream transport.
|
||||
* @param rem_ufrag Remote ufrag, as seen in the SDP received from
|
||||
|
|
|
@ -584,7 +584,9 @@ static pj_bool_t turn_on_data_read(test_server *test_srv,
|
|||
/* Not STUN message, this probably is a ChannelData */
|
||||
pj_turn_channel_data cd;
|
||||
const pj_turn_channel_data *pcd = (const pj_turn_channel_data*)data;
|
||||
char peer_info[PJ_INET6_ADDRSTRLEN];
|
||||
pj_ssize_t sent;
|
||||
unsigned j;
|
||||
|
||||
if (i==test_srv->turn_alloc_cnt) {
|
||||
/* Invalid data */
|
||||
|
@ -606,23 +608,26 @@ static pj_bool_t turn_on_data_read(test_server *test_srv,
|
|||
}
|
||||
|
||||
/* Lookup peer */
|
||||
for (i=0; i<alloc->perm_cnt; ++i) {
|
||||
if (alloc->chnum[i] == cd.ch_number)
|
||||
for (j=0; j<alloc->perm_cnt; ++j) {
|
||||
if (alloc->chnum[j] == cd.ch_number)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i==alloc->perm_cnt) {
|
||||
if (j==alloc->perm_cnt) {
|
||||
PJ_LOG(1,(THIS_FILE,
|
||||
"TURN Server: ChannelData discarded: invalid channel number"));
|
||||
goto on_return;
|
||||
}
|
||||
|
||||
/* Relay the data to peer */
|
||||
pj_sockaddr_print(&alloc->perm[j], peer_info, sizeof(peer_info), 3);
|
||||
PJ_LOG(5,(THIS_FILE, "Relaying %d bytes data from client %s to peer %s",
|
||||
cd.length, client_info, peer_info));
|
||||
sent = cd.length;
|
||||
pj_activesock_sendto(alloc->sock, &alloc->send_key,
|
||||
pcd+1, &sent, 0,
|
||||
&alloc->perm[i],
|
||||
pj_sockaddr_get_len(&alloc->perm[i]));
|
||||
&alloc->perm[j],
|
||||
pj_sockaddr_get_len(&alloc->perm[j]));
|
||||
|
||||
/* Done */
|
||||
goto on_return;
|
||||
|
@ -879,15 +884,15 @@ static pj_bool_t turn_on_data_read(test_server *test_srv,
|
|||
break;
|
||||
}
|
||||
|
||||
if (i==alloc->perm_cnt) {
|
||||
if (j==alloc->perm_cnt) {
|
||||
if (alloc->perm_cnt==MAX_TURN_PERM) {
|
||||
pj_stun_msg_create_response(pool, req, PJ_STUN_SC_INSUFFICIENT_CAPACITY, NULL, &resp);
|
||||
goto send_pkt;
|
||||
}
|
||||
pj_sockaddr_cp(&alloc->perm[i], &pa->sockaddr);
|
||||
pj_sockaddr_cp(&alloc->perm[j], &pa->sockaddr);
|
||||
++alloc->perm_cnt;
|
||||
}
|
||||
alloc->chnum[i] = cn;
|
||||
alloc->chnum[j] = cn;
|
||||
|
||||
resp = create_success_response(test_srv, alloc, req, pool, 0, &auth_key);
|
||||
|
||||
|
|
|
@ -145,6 +145,8 @@ static void periodic_timer(pj_timer_heap_t *th,
|
|||
pj_timer_entry *te);
|
||||
static void handle_incoming_check(pj_ice_sess *ice,
|
||||
const pj_ice_rx_check *rcheck);
|
||||
static void end_of_cand_ind_timer(pj_timer_heap_t *th,
|
||||
pj_timer_entry *te);
|
||||
|
||||
/* These are the callbacks registered to the STUN sessions */
|
||||
static pj_status_t on_stun_send_msg(pj_stun_session *sess,
|
||||
|
@ -936,6 +938,25 @@ static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice,
|
|||
return prio;
|
||||
}
|
||||
|
||||
PJ_INLINE(int) CMP_CHECK_STATE(const pj_ice_sess_check *c1,
|
||||
const pj_ice_sess_check *c2)
|
||||
{
|
||||
/* SUCCEEDED has higher state than FAILED */
|
||||
if (c1->state == PJ_ICE_SESS_CHECK_STATE_SUCCEEDED &&
|
||||
c2->state == PJ_ICE_SESS_CHECK_STATE_FAILED)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (c2->state == PJ_ICE_SESS_CHECK_STATE_SUCCEEDED &&
|
||||
c1->state == PJ_ICE_SESS_CHECK_STATE_FAILED)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Other state, just compare the state value */
|
||||
return (c1->state - c2->state);
|
||||
}
|
||||
|
||||
|
||||
PJ_INLINE(int) CMP_CHECK_PRIO(const pj_ice_sess_check *c1,
|
||||
const pj_ice_sess_check *c2)
|
||||
|
@ -1027,7 +1048,9 @@ static void clist_set_state(pj_ice_sess *ice, pj_ice_sess_checklist *clist,
|
|||
}
|
||||
}
|
||||
|
||||
/* Sort checklist based on priority */
|
||||
/* Sort checklist based on state & priority, we need to put Successful pairs
|
||||
* on top of the list for pruning.
|
||||
*/
|
||||
static void sort_checklist(pj_ice_sess *ice, pj_ice_sess_checklist *clist)
|
||||
{
|
||||
unsigned i;
|
||||
|
@ -1048,7 +1071,12 @@ static void sort_checklist(pj_ice_sess *ice, pj_ice_sess_checklist *clist)
|
|||
unsigned j, highest = i;
|
||||
|
||||
for (j=i+1; j<clist->count; ++j) {
|
||||
if (CMP_CHECK_PRIO(&clist->checks[j], &clist->checks[highest]) > 0) {
|
||||
int cmp_state = CMP_CHECK_STATE(&clist->checks[j],
|
||||
&clist->checks[highest]);
|
||||
if (cmp_state > 0 ||
|
||||
(cmp_state==0 && CMP_CHECK_PRIO(&clist->checks[j],
|
||||
&clist->checks[highest]) > 0))
|
||||
{
|
||||
highest = j;
|
||||
}
|
||||
}
|
||||
|
@ -1113,7 +1141,9 @@ static pj_status_t prune_checklist(pj_ice_sess *ice,
|
|||
for (i=0; i<clist->count; ++i) {
|
||||
pj_ice_sess_cand *srflx = clist->checks[i].lcand;
|
||||
|
||||
if (clist->checks[i].lcand->type == PJ_ICE_CAND_TYPE_SRFLX) {
|
||||
if (srflx->type == PJ_ICE_CAND_TYPE_SRFLX ||
|
||||
srflx->type == PJ_ICE_CAND_TYPE_PRFLX)
|
||||
{
|
||||
/* Find the base for this candidate */
|
||||
unsigned j;
|
||||
for (j=0; j<ice->lcand_cnt; ++j) {
|
||||
|
@ -1123,7 +1153,7 @@ static pj_status_t prune_checklist(pj_ice_sess *ice,
|
|||
continue;
|
||||
|
||||
if (pj_sockaddr_cmp(&srflx->base_addr, &host->addr) == 0) {
|
||||
/* Replace this SRFLX with its BASE */
|
||||
/* Replace this SRFLX/PRFLX with its BASE */
|
||||
clist->checks[i].lcand = host;
|
||||
break;
|
||||
}
|
||||
|
@ -1137,7 +1167,7 @@ static pj_status_t prune_checklist(pj_ice_sess *ice,
|
|||
pj_sockaddr_print(&srflx->base_addr, baddr,
|
||||
sizeof(baddr), 2),
|
||||
pj_sockaddr_get_port(&srflx->base_addr),
|
||||
GET_LCAND_ID(clist->checks[i].lcand)));
|
||||
GET_LCAND_ID(srflx)));
|
||||
return PJNATH_EICENOHOSTCAND;
|
||||
}
|
||||
}
|
||||
|
@ -1757,10 +1787,9 @@ static int discard_check(pj_ice_sess *ice, pj_ice_sess_checklist *clist,
|
|||
/* Re-sort before discarding the last */
|
||||
sort_checklist(ice, clist);
|
||||
if (!prio_lower_than ||
|
||||
pj_cmp_timestamp(&clist->checks[k].prio, prio_lower_than) < 0)
|
||||
pj_cmp_timestamp(&clist->checks[k-1].prio, prio_lower_than) < 0)
|
||||
{
|
||||
remove_check(ice, clist, clist->count-1,
|
||||
"too many, drop low-prio");
|
||||
remove_check(ice, clist, k-1, "too many, drop low-prio");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1768,14 +1797,37 @@ static int discard_check(pj_ice_sess *ice, pj_ice_sess_checklist *clist,
|
|||
}
|
||||
|
||||
|
||||
/* Timer callback for end of candidate indication from remote */
|
||||
static void end_of_cand_ind_timer(pj_timer_heap_t *th,
|
||||
pj_timer_entry *te)
|
||||
{
|
||||
pj_ice_sess *ice = (pj_ice_sess*)te->user_data;
|
||||
PJ_UNUSED_ARG(th);
|
||||
|
||||
pj_grp_lock_acquire(ice->grp_lock);
|
||||
|
||||
if (ice->is_trickling && !ice->is_complete) {
|
||||
LOG5((ice->obj_name, "End-of-candidate timer timeout, any future "
|
||||
"remote candidate update will be ignored"));
|
||||
ice->is_trickling = PJ_FALSE;
|
||||
|
||||
/* ICE checks may have been completed/failed */
|
||||
check_ice_complete(ice);
|
||||
}
|
||||
|
||||
pj_grp_lock_release(ice->grp_lock);
|
||||
}
|
||||
|
||||
|
||||
/* Add remote candidates and create/update checklist */
|
||||
static pj_status_t add_rcand_and_update_checklist(
|
||||
pj_ice_sess *ice,
|
||||
unsigned rem_cand_cnt,
|
||||
const pj_ice_sess_cand rem_cand[])
|
||||
const pj_ice_sess_cand rem_cand[],
|
||||
pj_bool_t trickle_done)
|
||||
{
|
||||
pj_ice_sess_checklist *clist;
|
||||
unsigned i, j;
|
||||
unsigned i, j, new_pair = 0;
|
||||
pj_status_t status;
|
||||
|
||||
/* Save remote candidates */
|
||||
|
@ -1937,11 +1989,12 @@ static pj_status_t add_rcand_and_update_checklist(
|
|||
}
|
||||
|
||||
clist->count++;
|
||||
new_pair++;
|
||||
}
|
||||
}
|
||||
|
||||
/* This could happen if candidates have no matching address families */
|
||||
if (clist->count==0 && !ice->is_trickling) {
|
||||
if (clist->count==0 && trickle_done) {
|
||||
LOG4((ice->obj_name, "Error: no checklist can be created"));
|
||||
return PJ_ENOTFOUND;
|
||||
}
|
||||
|
@ -1950,11 +2003,13 @@ static pj_status_t add_rcand_and_update_checklist(
|
|||
ice->lcand_paired = ice->lcand_cnt;
|
||||
ice->rcand_paired = ice->rcand_cnt;
|
||||
|
||||
if (clist->count > 0) {
|
||||
if (new_pair) {
|
||||
/* Sort checklist based on priority */
|
||||
//dump_checklist("Checklist before sort:", ice, &ice->clist);
|
||||
sort_checklist(ice, clist);
|
||||
|
||||
/* Prune the checklist */
|
||||
//dump_checklist("Checklist before prune:", ice, &ice->clist);
|
||||
status = prune_checklist(ice, clist);
|
||||
if (status != PJ_SUCCESS)
|
||||
return status;
|
||||
|
@ -1963,7 +2018,7 @@ static pj_status_t add_rcand_and_update_checklist(
|
|||
/* Regular ICE or trickle ICE after end-of-candidates indication:
|
||||
* Disable our components which don't have matching component
|
||||
*/
|
||||
if (!ice->is_trickling) {
|
||||
if (trickle_done) {
|
||||
unsigned highest_comp = 0;
|
||||
|
||||
for (i=0; i<ice->rcand_cnt; ++i) {
|
||||
|
@ -1980,7 +2035,7 @@ static pj_status_t add_rcand_and_update_checklist(
|
|||
ice->comp_cnt = highest_comp;
|
||||
|
||||
/* If using trickle ICE and end-of-candidate has been signalled,
|
||||
* check if ICE nego completion.
|
||||
* check for ICE nego completion.
|
||||
*/
|
||||
if (ice->opt.trickle != PJ_ICE_SESS_TRICKLE_DISABLED)
|
||||
check_ice_complete(ice);
|
||||
|
@ -1990,7 +2045,7 @@ static pj_status_t add_rcand_and_update_checklist(
|
|||
* there is no available check pair.
|
||||
*/
|
||||
if (ice->opt.trickle != PJ_ICE_SESS_TRICKLE_DISABLED &&
|
||||
clist->count > 0 &&
|
||||
clist->count > 0 && !ice->is_complete &&
|
||||
clist->state == PJ_ICE_SESS_CHECKLIST_ST_RUNNING)
|
||||
{
|
||||
if (!pj_timer_entry_running(&clist->timer)) {
|
||||
|
@ -2008,6 +2063,12 @@ static pj_status_t add_rcand_and_update_checklist(
|
|||
}
|
||||
}
|
||||
|
||||
/* Stop the end-of-candidates indication timer if trickling is done */
|
||||
if (trickle_done && pj_timer_entry_running(&ice->timer_end_of_cand)) {
|
||||
pj_timer_heap_cancel_if_active(ice->stun_cfg.timer_heap,
|
||||
&ice->timer_end_of_cand, 0);
|
||||
}
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -2026,12 +2087,16 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
|
|||
timer_data *td;
|
||||
pj_status_t status;
|
||||
|
||||
PJ_ASSERT_RETURN(ice && rem_ufrag && rem_passwd &&
|
||||
((rem_cand_cnt && rem_cand) || ice->is_trickling),
|
||||
PJ_EINVAL);
|
||||
PJ_ASSERT_RETURN(ice && rem_ufrag && rem_passwd, PJ_EINVAL);
|
||||
|
||||
pj_grp_lock_acquire(ice->grp_lock);
|
||||
|
||||
if (ice->tx_ufrag.slen) {
|
||||
/* Checklist has been created */
|
||||
pj_grp_lock_release(ice->grp_lock);
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/* Save credentials */
|
||||
username.ptr = buf;
|
||||
|
||||
|
@ -2061,14 +2126,19 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
|
|||
|
||||
ice->clist.count = 0;
|
||||
ice->lcand_paired = ice->rcand_paired = 0;
|
||||
status = add_rcand_and_update_checklist(ice, rem_cand_cnt, rem_cand);
|
||||
if (status != PJ_SUCCESS) {
|
||||
pj_grp_lock_release(ice->grp_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Log checklist */
|
||||
dump_checklist("Checklist created:", ice, clist);
|
||||
/* Build checklist only if both sides have candidates already */
|
||||
if (ice->lcand_cnt > 0 && rem_cand_cnt > 0) {
|
||||
status = add_rcand_and_update_checklist(ice, rem_cand_cnt, rem_cand,
|
||||
!ice->is_trickling);
|
||||
if (status != PJ_SUCCESS) {
|
||||
pj_grp_lock_release(ice->grp_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Log checklist */
|
||||
dump_checklist("Checklist created:", ice, clist);
|
||||
}
|
||||
|
||||
pj_grp_lock_release(ice->grp_lock);
|
||||
|
||||
|
@ -2090,40 +2160,50 @@ PJ_DEF(pj_status_t) pj_ice_sess_update_check_list(
|
|||
PJ_ASSERT_RETURN(ice && ((rem_cand_cnt==0) ||
|
||||
(rem_ufrag && rem_passwd && rem_cand)),
|
||||
PJ_EINVAL);
|
||||
PJ_ASSERT_RETURN(ice->tx_ufrag.slen, PJ_EINVALIDOP);
|
||||
|
||||
pj_grp_lock_acquire(ice->grp_lock);
|
||||
|
||||
/* Ignore if remote ufrag has not known yet */
|
||||
if (ice->tx_ufrag.slen == 0) {
|
||||
LOG5((ice->obj_name,
|
||||
"Cannot update ICE checklist when remote ufrag is unknown"));
|
||||
pj_grp_lock_release(ice->grp_lock);
|
||||
return PJ_EINVALIDOP;
|
||||
}
|
||||
|
||||
/* Ignore if trickle has been stopped (e.g: received end-of-candidate) */
|
||||
if (!ice->is_trickling && rem_cand_cnt) {
|
||||
LOG5((ice->obj_name,
|
||||
"Cannot update checklist when ICE trickling is disabled or"
|
||||
" has been ended"));
|
||||
return PJ_EINVALIDOP;
|
||||
"Ignored remote candidate update as ICE trickling has ended"));
|
||||
pj_grp_lock_release(ice->grp_lock);
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
pj_grp_lock_acquire(ice->grp_lock);
|
||||
|
||||
if (trickle_done && ice->is_trickling) {
|
||||
LOG5((ice->obj_name, "Trickling done."));
|
||||
ice->is_trickling = PJ_FALSE;
|
||||
}
|
||||
|
||||
/* Verify remote ufrag & passwd, if remote candidate specified */
|
||||
if (rem_cand_cnt && (pj_strcmp(&ice->tx_ufrag, rem_ufrag) ||
|
||||
pj_strcmp(&ice->tx_pass, rem_passwd)))
|
||||
{
|
||||
LOG5((ice->obj_name, "Invalid remote ufrag/pwd in adding "
|
||||
"remote candidates."));
|
||||
status = PJ_EINVAL;
|
||||
LOG5((ice->obj_name, "Ignored remote candidate update due to remote "
|
||||
"ufrag/pwd mismatch"));
|
||||
rem_cand_cnt = 0;
|
||||
}
|
||||
|
||||
if (status == PJ_SUCCESS) {
|
||||
status = add_rcand_and_update_checklist(ice, rem_cand_cnt, rem_cand);
|
||||
status = add_rcand_and_update_checklist(ice, rem_cand_cnt, rem_cand,
|
||||
trickle_done);
|
||||
}
|
||||
|
||||
/* Log checklist */
|
||||
if (status == PJ_SUCCESS)
|
||||
dump_checklist("Checklist updated:", ice, &ice->clist);
|
||||
|
||||
if (trickle_done && ice->is_trickling) {
|
||||
LOG5((ice->obj_name, "Remote signalled end-of-candidates "
|
||||
"and local candidates gathering completed, "
|
||||
"will ignore any candidate update"));
|
||||
ice->is_trickling = PJ_FALSE;
|
||||
}
|
||||
|
||||
pj_grp_lock_release(ice->grp_lock);
|
||||
|
||||
return status;
|
||||
|
@ -2498,6 +2578,25 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice)
|
|||
PJ_TRUE, ice->grp_lock);
|
||||
}
|
||||
|
||||
/* For trickle ICE, start timer for end-of-candidates indication from
|
||||
* remote.
|
||||
*/
|
||||
if (ice->is_trickling && !pj_timer_entry_running(&ice->timer_end_of_cand))
|
||||
{
|
||||
pj_time_val delay = {PJ_TRICKLE_ICE_END_OF_CAND_TIMEOUT, 0};
|
||||
pj_timer_entry_init(&ice->timer_end_of_cand, 0, ice,
|
||||
&end_of_cand_ind_timer);
|
||||
status = pj_timer_heap_schedule_w_grp_lock(
|
||||
ice->stun_cfg.timer_heap,
|
||||
&ice->timer_end_of_cand,
|
||||
&delay, PJ_TRUE,
|
||||
ice->grp_lock);
|
||||
if (status != PJ_SUCCESS) {
|
||||
LOG4((ice->obj_name,
|
||||
"Failed to schedule end-of-candidate indication timer"));
|
||||
}
|
||||
}
|
||||
|
||||
pj_grp_lock_release(ice->grp_lock);
|
||||
pj_log_pop_indent();
|
||||
return status;
|
||||
|
@ -2587,7 +2686,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
|
|||
}
|
||||
}
|
||||
if (i == clist->count) {
|
||||
/* The check may have been pruned */
|
||||
/* The check may have been pruned (due to low prio) */
|
||||
check->tdata = NULL;
|
||||
pj_grp_lock_release(ice->grp_lock);
|
||||
return;
|
||||
|
@ -2595,6 +2694,7 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
|
|||
}
|
||||
|
||||
/* Mark STUN transaction as complete */
|
||||
// Find 'corner case ...'.
|
||||
//pj_assert(tdata == check->tdata);
|
||||
check->tdata = NULL;
|
||||
|
||||
|
|
|
@ -1383,7 +1383,9 @@ PJ_DEF(pj_bool_t) pj_ice_strans_has_sess(pj_ice_strans *ice_st)
|
|||
*/
|
||||
PJ_DEF(pj_bool_t) pj_ice_strans_sess_is_running(pj_ice_strans *ice_st)
|
||||
{
|
||||
return ice_st && ice_st->ice && ice_st->ice->rcand_cnt &&
|
||||
// Trickle ICE can start ICE before remote candidate list is received
|
||||
return ice_st && ice_st->ice && /* ice_st->ice->rcand_cnt && */
|
||||
ice_st->ice->clist.state == PJ_ICE_SESS_CHECKLIST_ST_RUNNING &&
|
||||
!pj_ice_strans_sess_is_complete(ice_st);
|
||||
}
|
||||
|
||||
|
@ -1427,7 +1429,8 @@ PJ_DEF(pj_status_t) pj_ice_strans_get_ufrag_pwd( pj_ice_strans *ice_st,
|
|||
if (loc_pwd) *loc_pwd = ice_st->ice->rx_pass;
|
||||
|
||||
if (rem_ufrag || rem_pwd) {
|
||||
PJ_ASSERT_RETURN(ice_st->ice->rcand_cnt != 0, PJ_EINVALIDOP);
|
||||
// In trickle ICE, remote may send initial SDP with empty candidates
|
||||
//PJ_ASSERT_RETURN(ice_st->ice->rcand_cnt != 0, PJ_EINVALIDOP);
|
||||
if (rem_ufrag) *rem_ufrag = ice_st->ice->tx_ufrag;
|
||||
if (rem_pwd) *rem_pwd = ice_st->ice->tx_pass;
|
||||
}
|
||||
|
@ -1525,21 +1528,22 @@ PJ_DEF(pj_status_t) pj_ice_strans_change_role( pj_ice_strans *ice_st,
|
|||
return pj_ice_sess_change_role(ice_st->ice, new_role);
|
||||
}
|
||||
|
||||
static pj_status_t setup_turn_perm( pj_ice_strans *ice_st,
|
||||
unsigned rem_cand_cnt,
|
||||
const pj_ice_sess_cand rem_cand[])
|
||||
static pj_status_t setup_turn_perm( pj_ice_strans *ice_st)
|
||||
{
|
||||
unsigned n;
|
||||
pj_status_t status;
|
||||
|
||||
for (n = 0; n < ice_st->cfg.turn_tp_cnt && rem_cand_cnt; ++n) {
|
||||
unsigned i;
|
||||
for (n = 0; n < ice_st->cfg.turn_tp_cnt; ++n) {
|
||||
unsigned i, comp_cnt;
|
||||
|
||||
for (i=0; i<ice_st->comp_cnt; ++i) {
|
||||
comp_cnt = pj_ice_strans_get_running_comp_cnt(ice_st);
|
||||
for (i=0; i<comp_cnt; ++i) {
|
||||
pj_ice_strans_comp *comp = ice_st->comp[i];
|
||||
pj_turn_session_info info;
|
||||
pj_sockaddr addrs[PJ_ICE_ST_MAX_CAND];
|
||||
unsigned j, count=0;
|
||||
unsigned rem_cand_cnt;
|
||||
const pj_ice_sess_cand *rem_cand;
|
||||
|
||||
if (!comp->turn[n].sock)
|
||||
continue;
|
||||
|
@ -1549,6 +1553,11 @@ static pj_status_t setup_turn_perm( pj_ice_strans *ice_st,
|
|||
continue;
|
||||
|
||||
/* Gather remote addresses for this component */
|
||||
rem_cand_cnt = ice_st->ice->rcand_cnt;
|
||||
rem_cand = ice_st->ice->rcand;
|
||||
if (status != PJ_SUCCESS)
|
||||
continue;
|
||||
|
||||
for (j=0; j<rem_cand_cnt && count<PJ_ARRAY_SIZE(addrs); ++j) {
|
||||
if (rem_cand[j].comp_id==i+1 &&
|
||||
rem_cand[j].addr.addr.sa_family==
|
||||
|
@ -1584,21 +1593,21 @@ PJ_DEF(pj_status_t) pj_ice_strans_start_ice( pj_ice_strans *ice_st,
|
|||
{
|
||||
pj_status_t status;
|
||||
|
||||
PJ_ASSERT_RETURN(ice_st && rem_ufrag && rem_passwd &&
|
||||
((ice_st->ice && ice_st->ice->is_trickling) ||
|
||||
(rem_cand_cnt && rem_cand)), PJ_EINVAL);
|
||||
PJ_ASSERT_RETURN(ice_st, PJ_EINVAL);
|
||||
PJ_ASSERT_RETURN(ice_st->ice, PJ_EINVALIDOP);
|
||||
|
||||
/* Mark start time */
|
||||
pj_gettimeofday(&ice_st->start_time);
|
||||
|
||||
/* Build check list */
|
||||
status = pj_ice_sess_create_check_list(ice_st->ice, rem_ufrag, rem_passwd,
|
||||
rem_cand_cnt, rem_cand);
|
||||
/* Update check list */
|
||||
status = pj_ice_strans_update_check_list(ice_st, rem_ufrag, rem_passwd,
|
||||
rem_cand_cnt, rem_cand,
|
||||
!ice_st->ice->is_trickling);
|
||||
if (status != PJ_SUCCESS)
|
||||
return status;
|
||||
|
||||
/* If we have TURN candidate, now is the time to create the permissions */
|
||||
status = setup_turn_perm(ice_st, rem_cand_cnt, rem_cand);
|
||||
status = setup_turn_perm(ice_st);
|
||||
if (status != PJ_SUCCESS) {
|
||||
pj_ice_strans_stop_ice(ice_st);
|
||||
return status;
|
||||
|
@ -1615,6 +1624,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_start_ice( pj_ice_strans *ice_st,
|
|||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Update check list after discovering and conveying new local ICE candidate,
|
||||
* or receiving update of remote ICE candidates in trickle ICE.
|
||||
|
@ -1627,35 +1637,57 @@ PJ_DEF(pj_status_t) pj_ice_strans_update_check_list(
|
|||
const pj_ice_sess_cand rem_cand[],
|
||||
pj_bool_t rcand_end)
|
||||
{
|
||||
pj_bool_t checklist_created;
|
||||
pj_status_t status;
|
||||
|
||||
PJ_ASSERT_RETURN(ice_st && ((rem_cand_cnt==0) ||
|
||||
(rem_ufrag && rem_passwd && rem_cand)),
|
||||
PJ_EINVAL);
|
||||
PJ_ASSERT_RETURN(ice_st->ice, PJ_EINVALIDOP);
|
||||
|
||||
pj_grp_lock_acquire(ice_st->grp_lock);
|
||||
|
||||
/* If we have TURN candidate, update the permissions */
|
||||
status = setup_turn_perm(ice_st, rem_cand_cnt, rem_cand);
|
||||
if (status != PJ_SUCCESS) {
|
||||
pj_ice_strans_stop_ice(ice_st);
|
||||
return status;
|
||||
checklist_created = ice_st->ice->tx_ufrag.slen > 0;
|
||||
|
||||
/* Create checklist (if not yet) */
|
||||
if (rem_ufrag && !checklist_created) {
|
||||
status = pj_ice_sess_create_check_list(ice_st->ice, rem_ufrag,
|
||||
rem_passwd, rem_cand_cnt,
|
||||
rem_cand);
|
||||
if (status != PJ_SUCCESS) {
|
||||
PJ_PERROR(4,(ice_st->obj_name, status,
|
||||
"Failed setting up remote ufrag"));
|
||||
pj_grp_lock_release(ice_st->grp_lock);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update checklist */
|
||||
if (rcand_end && !ice_st->rem_cand_end)
|
||||
ice_st->rem_cand_end = PJ_TRUE;
|
||||
status = pj_ice_sess_update_check_list(ice_st->ice, rem_ufrag, rem_passwd,
|
||||
rem_cand_cnt, rem_cand,
|
||||
(ice_st->rem_cand_end &&
|
||||
ice_st->loc_cand_end));
|
||||
if (status != PJ_SUCCESS) {
|
||||
/* Should not stop ICE here as the checklist should remain intact and
|
||||
* failure may not be fatal, e.g: wrong/old ufrag, too many rem cand.
|
||||
*/
|
||||
PJ_PERROR(4,(ice_st->obj_name, status, "Failed updating checklist"));
|
||||
pj_grp_lock_release(ice_st->grp_lock);
|
||||
return status;
|
||||
/* Update checklist for trickling ICE */
|
||||
if (ice_st->ice->is_trickling) {
|
||||
if (rcand_end && !ice_st->rem_cand_end)
|
||||
ice_st->rem_cand_end = PJ_TRUE;
|
||||
|
||||
status = pj_ice_sess_update_check_list(
|
||||
ice_st->ice, rem_ufrag, rem_passwd,
|
||||
(checklist_created? rem_cand_cnt:0), rem_cand,
|
||||
(ice_st->rem_cand_end && ice_st->loc_cand_end));
|
||||
if (status != PJ_SUCCESS) {
|
||||
PJ_PERROR(4,(ice_st->obj_name, status,
|
||||
"Failed updating checklist"));
|
||||
pj_grp_lock_release(ice_st->grp_lock);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update TURN permissions if periodic check has been started. */
|
||||
if (pj_ice_strans_sess_is_running(ice_st)) {
|
||||
status = setup_turn_perm(ice_st);
|
||||
if (status != PJ_SUCCESS) {
|
||||
PJ_PERROR(4,(ice_st->obj_name, status,
|
||||
"Failed setting up TURN permission"));
|
||||
pj_grp_lock_release(ice_st->grp_lock);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
pj_grp_lock_release(ice_st->grp_lock);
|
||||
|
|
|
@ -118,6 +118,14 @@ typedef struct call_answer
|
|||
} call_answer;
|
||||
|
||||
|
||||
/* Generic states */
|
||||
typedef enum pjsua_op_state {
|
||||
PJSUA_OP_STATE_NULL,
|
||||
PJSUA_OP_STATE_READY,
|
||||
PJSUA_OP_STATE_RUNNING,
|
||||
PJSUA_OP_STATE_DONE,
|
||||
} pjsua_op_state;
|
||||
|
||||
/**
|
||||
* Structure to be attached to invite dialog.
|
||||
* Given a dialog "dlg", application can retrieve this structure
|
||||
|
@ -211,7 +219,7 @@ struct pjsua_call
|
|||
pj_bool_t enabled;
|
||||
pj_bool_t remote_sup;
|
||||
pj_bool_t remote_dlg_est;
|
||||
pj_bool_t trickling;
|
||||
pjsua_op_state trickling;
|
||||
int retrans18x_count;
|
||||
pj_bool_t pending_info;
|
||||
pj_timer_entry timer;
|
||||
|
@ -714,7 +722,9 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
|
|||
const pjmedia_sdp_session *remote_sdp);
|
||||
pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id);
|
||||
|
||||
void pjsua_ice_check_start_trickling(pjsua_call *call, pjsip_event *e);
|
||||
void pjsua_ice_check_start_trickling(pjsua_call *call,
|
||||
pj_bool_t forceful,
|
||||
pjsip_event *e);
|
||||
|
||||
/*
|
||||
* Error message when media operation is requested while another is in progress
|
||||
|
|
|
@ -176,6 +176,7 @@ static void reset_call(pjsua_call_id id)
|
|||
pjsua_call_setting_default(&call->opt);
|
||||
pj_timer_entry_init(&call->reinv_timer, PJ_FALSE,
|
||||
(void*)(pj_size_t)id, &reinv_timer_cb);
|
||||
pj_bzero(&call->trickle_ice, sizeof(call->trickle_ice));
|
||||
pj_timer_entry_init(&call->trickle_ice.timer, 0, call,
|
||||
&trickle_ice_send_sip_info);
|
||||
}
|
||||
|
@ -4309,7 +4310,7 @@ static void trickle_ice_retrans_18x(pj_timer_heap_t *th,
|
|||
struct pj_timer_entry *te)
|
||||
{
|
||||
pjsua_call *call = (pjsua_call*)te->user_data;
|
||||
pjsip_tx_data *tdata;
|
||||
pjsip_tx_data *tdata = NULL;
|
||||
pj_time_val delay;
|
||||
|
||||
PJ_UNUSED_ARG(th);
|
||||
|
@ -4317,12 +4318,16 @@ static void trickle_ice_retrans_18x(pj_timer_heap_t *th,
|
|||
/* If trickling has been started or dialog has been established on
|
||||
* both sides, stop 18x retransmission.
|
||||
*/
|
||||
if (call->trickle_ice.trickling || call->trickle_ice.remote_dlg_est)
|
||||
if (call->trickle_ice.trickling >= PJSUA_OP_STATE_RUNNING ||
|
||||
call->trickle_ice.remote_dlg_est)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure last tdata is 18x response */
|
||||
tdata = call->inv->invite_tsx->last_tx;
|
||||
if (tdata->msg->type != PJSIP_RESPONSE_MSG ||
|
||||
if (call->inv->invite_tsx)
|
||||
tdata = call->inv->invite_tsx->last_tx;
|
||||
if (!tdata || tdata->msg->type != PJSIP_RESPONSE_MSG ||
|
||||
tdata->msg->line.status.code/10 != 18)
|
||||
{
|
||||
return;
|
||||
|
@ -4405,8 +4410,13 @@ static void trickle_ice_recv_sip_info(pjsua_call *call, pjsip_rx_data *rdata)
|
|||
}
|
||||
}
|
||||
|
||||
if (j == med_cnt)
|
||||
if (j == med_cnt) {
|
||||
pjsua_perror(THIS_FILE, "Cannot add remote candidates from SDP in "
|
||||
"incoming INFO because media ID (SDP a=mid) is not "
|
||||
"recognized",
|
||||
PJ_EIGNORED);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Update ICE checklist */
|
||||
status = pjmedia_ice_trickle_update(tp, &ufrag, &pwd, cand_cnt, cand,
|
||||
|
@ -4472,8 +4482,8 @@ static void trickle_ice_send_sip_info(pj_timer_heap_t *th,
|
|||
goto on_return;
|
||||
}
|
||||
|
||||
PJ_LOG(4,(THIS_FILE, "Call %d: ICE trickle sending SIP INFO",
|
||||
call->index));
|
||||
PJ_LOG(4,(THIS_FILE, "Call %d: ICE trickle sending SIP INFO%s",
|
||||
call->index, (forced? " (forced)":"")));
|
||||
|
||||
/* Create temporary pool */
|
||||
tmp_pool = pjsua_pool_create("tmp_ice", 128, 128);
|
||||
|
@ -4533,11 +4543,23 @@ static void trickle_ice_send_sip_info(pj_timer_heap_t *th,
|
|||
/* Set flag for pending SIP INFO */
|
||||
call->trickle_ice.pending_info = PJ_TRUE;
|
||||
|
||||
/* Stop trickling if local candidate gathering for all media is done */
|
||||
if (all_end_of_cand) {
|
||||
PJ_LOG(4,(THIS_FILE, "Call %d: ICE trickle stopped trickling "
|
||||
"as local candidate gathering completed",
|
||||
call->index));
|
||||
call->trickle_ice.trickling = PJ_FALSE;
|
||||
call->trickle_ice.trickling = PJSUA_OP_STATE_DONE;
|
||||
}
|
||||
|
||||
/* Update ICE checklist after conveying local candidates. */
|
||||
for (i = 0; i < med_cnt; ++i) {
|
||||
pjsua_call_media *cm = use_med_prov? &call->media_prov[i] :
|
||||
&call->media[i];
|
||||
pjmedia_transport *tp = cm->tp_orig;
|
||||
if (!tp || tp->type != PJMEDIA_TRANSPORT_TYPE_ICE)
|
||||
continue;
|
||||
|
||||
pjmedia_ice_trickle_update(tp, NULL, NULL, 0, NULL, PJ_FALSE);
|
||||
}
|
||||
|
||||
on_return:
|
||||
|
@ -4545,7 +4567,7 @@ on_return:
|
|||
pj_pool_release(tmp_pool);
|
||||
|
||||
/* Reschedule if we are trickling */
|
||||
if (call->trickle_ice.trickling) {
|
||||
if (call->trickle_ice.trickling == PJSUA_OP_STATE_RUNNING) {
|
||||
pj_time_val delay = {0, PJSUA_TRICKLE_ICE_NEW_CAND_CHECK_INTERVAL};
|
||||
|
||||
/* Reset forced mode after successfully sending forced SIP INFO */
|
||||
|
@ -4571,13 +4593,14 @@ on_return:
|
|||
* - UAC/UAS receiving remote SDP (and check for trickle ICE support),
|
||||
* to start trickling.
|
||||
*/
|
||||
void pjsua_ice_check_start_trickling(pjsua_call *call, pjsip_event *e)
|
||||
void pjsua_ice_check_start_trickling(pjsua_call *call,
|
||||
pj_bool_t forceful,
|
||||
pjsip_event *e)
|
||||
{
|
||||
pjsip_inv_session *inv = call->inv;
|
||||
pj_bool_t forced_trickling = PJ_FALSE;
|
||||
|
||||
/* Make sure trickling/sending-INFO has not been started */
|
||||
if (call->trickle_ice.trickling)
|
||||
if (!forceful && call->trickle_ice.trickling >= PJSUA_OP_STATE_RUNNING)
|
||||
return;
|
||||
|
||||
/* Make sure trickle ICE is enabled */
|
||||
|
@ -4641,10 +4664,10 @@ void pjsua_ice_check_start_trickling(pjsua_call *call, pjsip_event *e)
|
|||
}
|
||||
} else {
|
||||
/* Start sending SIP INFO forcefully */
|
||||
forced_trickling = PJ_TRUE;
|
||||
forceful = PJ_TRUE;
|
||||
}
|
||||
|
||||
if (forced_trickling || call->trickle_ice.remote_sup) {
|
||||
if (forceful || call->trickle_ice.remote_sup) {
|
||||
PJ_LOG(4,(THIS_FILE,
|
||||
"Call %d: ICE trickle started after UAC "
|
||||
"receiving 18x (with%s SDP)",
|
||||
|
@ -4728,21 +4751,23 @@ void pjsua_ice_check_start_trickling(pjsua_call *call, pjsip_event *e)
|
|||
}
|
||||
|
||||
/* Check if ICE trickling can be started */
|
||||
if (!forced_trickling &&
|
||||
if (!forceful &&
|
||||
(!call->trickle_ice.remote_dlg_est || !call->trickle_ice.remote_sup))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Let's start trickling (or sending SIP INFO) */
|
||||
if (!call->trickle_ice.trickling) {
|
||||
if (forceful || call->trickle_ice.trickling < PJSUA_OP_STATE_RUNNING)
|
||||
{
|
||||
pj_timer_entry *te = &call->trickle_ice.timer;
|
||||
pj_time_val delay = {0,0};
|
||||
|
||||
call->trickle_ice.trickling = PJ_TRUE;
|
||||
if (call->trickle_ice.trickling < PJSUA_OP_STATE_RUNNING)
|
||||
call->trickle_ice.trickling = PJSUA_OP_STATE_RUNNING;
|
||||
|
||||
pjsua_cancel_timer(te);
|
||||
te->id = forced_trickling? 2 : 0;
|
||||
te->id = forceful? 2 : 0;
|
||||
te->cb = &trickle_ice_send_sip_info;
|
||||
pjsua_schedule_timer(te, &delay);
|
||||
|
||||
|
@ -4788,6 +4813,11 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
|
|||
case PJSIP_INV_STATE_CONFIRMED:
|
||||
pj_gettimeofday(&call->conn_time);
|
||||
|
||||
if (call->trickle_ice.enabled) {
|
||||
call->trickle_ice.remote_dlg_est = PJ_TRUE;
|
||||
pjsua_ice_check_start_trickling(call, PJ_FALSE, NULL);
|
||||
}
|
||||
|
||||
/* See if auto reinvite was pended as media update was done in the
|
||||
* EARLY state and remote does not support UPDATE.
|
||||
*/
|
||||
|
@ -5115,6 +5145,20 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
|
|||
|
||||
call->med_update_success = (status == PJ_SUCCESS);
|
||||
|
||||
/* Trickle ICE tasks:
|
||||
* - Check remote SDP for trickle ICE support & start sending SIP INFO.
|
||||
*/
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < remote_sdp->media_count; ++i) {
|
||||
if (pjmedia_ice_sdp_has_trickle(remote_sdp, i))
|
||||
break;
|
||||
}
|
||||
call->trickle_ice.remote_sup = (i < remote_sdp->media_count);
|
||||
if (call->trickle_ice.remote_sup)
|
||||
pjsua_ice_check_start_trickling(call, PJ_FALSE, NULL);
|
||||
}
|
||||
|
||||
/* Update remote's NAT type */
|
||||
if (pjsua_var.ua_cfg.nat_type_in_sdp) {
|
||||
update_remote_nat_type(call, remote_sdp);
|
||||
|
@ -6362,7 +6406,7 @@ static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
|
|||
* - UAS receiving INFO, cease 18x retrans & start trickling
|
||||
*/
|
||||
if (call->trickle_ice.enabled) {
|
||||
pjsua_ice_check_start_trickling(call, e);
|
||||
pjsua_ice_check_start_trickling(call, PJ_FALSE, e);
|
||||
|
||||
/* Process the SIP INFO content */
|
||||
trickle_ice_recv_sip_info(call, rdata);
|
||||
|
@ -6430,7 +6474,8 @@ static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
|
|||
* - UAS sending 18x, start 18x retrans
|
||||
* - UAC receiving 18x, forcefully send SIP INFO & start trickling
|
||||
*/
|
||||
pjsua_ice_check_start_trickling(call, e);
|
||||
pj_bool_t force = call->trickle_ice.trickling<PJSUA_OP_STATE_RUNNING;
|
||||
pjsua_ice_check_start_trickling(call, force, e);
|
||||
} else if (tsx->role == PJSIP_ROLE_UAS &&
|
||||
pjsip_method_cmp(&tsx->method, pjsip_get_prack_method())==0 &&
|
||||
tsx->state==PJSIP_TSX_STATE_TRYING)
|
||||
|
@ -6438,10 +6483,9 @@ static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
|
|||
/* Trickle ICE tasks:
|
||||
* - UAS receiving PRACK, start trickling
|
||||
*/
|
||||
pjsua_ice_check_start_trickling(call, e);
|
||||
pjsua_ice_check_start_trickling(call, PJ_FALSE, e);
|
||||
}
|
||||
|
||||
|
||||
on_return:
|
||||
pj_log_pop_indent();
|
||||
}
|
||||
|
|
|
@ -854,8 +854,8 @@ static void on_ice_complete(pjmedia_transport *tp,
|
|||
}
|
||||
|
||||
/* Stop trickling */
|
||||
if (call->trickle_ice.trickling) {
|
||||
call->trickle_ice.trickling = PJ_FALSE;
|
||||
if (call->trickle_ice.trickling < PJSUA_OP_STATE_DONE) {
|
||||
call->trickle_ice.trickling = PJSUA_OP_STATE_DONE;
|
||||
pjsua_cancel_timer(&call->trickle_ice.timer);
|
||||
PJ_LOG(4,(THIS_FILE, "Call %d: ICE trickle stopped trickling as "
|
||||
"ICE nego completed",
|
||||
|
@ -987,7 +987,10 @@ static pj_status_t create_ice_media_transport(
|
|||
}
|
||||
|
||||
/* Should not wait for ICE STUN/TURN ready when trickle ICE is enabled */
|
||||
if (ice_cfg.opt.trickle != PJ_ICE_SESS_TRICKLE_DISABLED) {
|
||||
if (ice_cfg.opt.trickle != PJ_ICE_SESS_TRICKLE_DISABLED &&
|
||||
(call_med->call->inv == NULL ||
|
||||
call_med->call->inv->state < PJSIP_INV_STATE_CONFIRMED))
|
||||
{
|
||||
if (rem_sdp) {
|
||||
/* As answerer: and when remote signals trickle ICE in SDP */
|
||||
trickle = pjmedia_ice_sdp_has_trickle(rem_sdp, call_med->idx);
|
||||
|
@ -1002,7 +1005,10 @@ static pj_status_t create_ice_media_transport(
|
|||
}
|
||||
|
||||
/* Check if trickle ICE can start trickling/sending SIP INFO */
|
||||
pjsua_ice_check_start_trickling(call_med->call, NULL);
|
||||
pjsua_ice_check_start_trickling(call_med->call, PJ_FALSE, NULL);
|
||||
} else {
|
||||
/* For non-initial INVITE, always use regular ICE */
|
||||
ice_cfg.opt.trickle = PJ_ICE_SESS_TRICKLE_DISABLED;
|
||||
}
|
||||
|
||||
/* If STUN transport is configured, initialize STUN transport settings */
|
||||
|
@ -2492,6 +2498,21 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
|
|||
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
/* Find and save "a=mid". Currently this is for trickle ICE.
|
||||
* Trickle ICE match media in SDP of SIP INFO by comparing this
|
||||
* attribute, so remote SDP must be received first before remote
|
||||
* SDP in SIP INFO can be processed.
|
||||
*/
|
||||
if (rem_sdp && call_med->rem_mid.slen == 0) {
|
||||
const pjmedia_sdp_media *m = rem_sdp->media[mi];
|
||||
pjmedia_sdp_attr *a;
|
||||
|
||||
a = pjmedia_sdp_media_find_attr2(m, "mid", NULL);
|
||||
if (a)
|
||||
call_med->rem_mid = a->value;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* By convention, the media is disabled if transport is NULL
|
||||
* or transport state is PJSUA_MED_TP_DISABLED.
|
||||
|
@ -2875,6 +2896,19 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find and save "a=mid". Currently this is for trickle ICE. Trickle
|
||||
* ICE match media in SDP of SIP INFO by comparing this attribute,
|
||||
* so remote SDP must be received first before remote SDP in SIP INFO
|
||||
* can be processed.
|
||||
*/
|
||||
if (call_med->rem_mid.slen == 0) {
|
||||
pjmedia_sdp_attr *a;
|
||||
|
||||
a = pjmedia_sdp_media_find_attr2(m, "mid", NULL);
|
||||
if (a)
|
||||
call_med->rem_mid = a->value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add NAT info in the SDP */
|
||||
|
@ -3089,10 +3123,14 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
|
|||
stop_media_session(call_id);
|
||||
|
||||
/* Stop trickle ICE timer */
|
||||
if (call->trickle_ice.trickling) {
|
||||
call->trickle_ice.trickling = PJ_FALSE;
|
||||
if (call->trickle_ice.trickling > PJSUA_OP_STATE_NULL) {
|
||||
call->trickle_ice.trickling = PJSUA_OP_STATE_NULL;
|
||||
pjsua_cancel_timer(&call->trickle_ice.timer);
|
||||
}
|
||||
call->trickle_ice.enabled = PJ_FALSE;
|
||||
call->trickle_ice.pending_info = PJ_FALSE;
|
||||
call->trickle_ice.remote_sup = PJ_FALSE;
|
||||
call->trickle_ice.retrans18x_count = 0;
|
||||
|
||||
/* Clean up media transports */
|
||||
pjsua_media_prov_clean_up(call_id);
|
||||
|
@ -3456,22 +3494,6 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Find and save "a=mid". Currently this is for trickle ICE. Trickle
|
||||
* ICE match media in SDP of SIP INFO by comparing this attribute,
|
||||
* so remote SDP must be received first before remote SDP in SIP INFO
|
||||
* can be processed.
|
||||
*/
|
||||
{
|
||||
const pjmedia_sdp_media *m = remote_sdp->media[mi];
|
||||
pjmedia_sdp_attr *a;
|
||||
|
||||
a = pjmedia_sdp_media_find_attr2(m, "mid", NULL);
|
||||
if (a)
|
||||
call_med->rem_mid = a->value;
|
||||
else
|
||||
pj_bzero(&call_med->rem_mid, sizeof(call_med->rem_mid));
|
||||
}
|
||||
|
||||
/* Apply media update action */
|
||||
if (call_med->type==PJMEDIA_TYPE_AUDIO) {
|
||||
pjmedia_stream_info the_si, *si = &the_si;
|
||||
|
|
Loading…
Reference in New Issue