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:
Nanang Izzuddin 2021-03-17 13:00:01 +07:00 committed by GitHub
parent b3e51c7a7d
commit 79531cbc05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 492 additions and 195 deletions

View File

@ -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,

View File

@ -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 */

View File

@ -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

View File

@ -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.
*

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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();
}

View File

@ -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;