Ticket #851: initial code to support regular nomination in ICE:
- Added option to change nomination strategy in ICE strans and session. Default is still aggressive. - Added option to control nomination timer - Renamed no_host_cand to max_host_cands in ICE config - Updated icedemo - Also added timer for controlled agent to wait for nomination from controlling agent git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/sipit24@2705 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
parent
2efb80bba6
commit
f4b8e93569
|
@ -323,6 +323,39 @@
|
|||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* For a controlled agent, specify how long it wants to wait (in milliseconds)
|
||||
* for the controlling agent to complete sending connectivity check with
|
||||
* nominated flag set to true for all components after the controlled agent
|
||||
* has found that all connectivity checks in its checklist have been completed
|
||||
* and there is at least one successful (but not nominated) check for every
|
||||
* component.
|
||||
*
|
||||
* When selecting the value, bear in mind that the connectivity check from
|
||||
* controlling agent may be delayed because of delay in receiving SDP answer
|
||||
* from the controlled agent.
|
||||
*
|
||||
* Application may set this value to -1 to disable this timer.
|
||||
*
|
||||
* Default: 10000 (milliseconds)
|
||||
*/
|
||||
#ifndef ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT
|
||||
# define ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT 10000
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* For controlling agent if it uses regular nomination, specify the delay to
|
||||
* perform nominated check (connectivity check with USE-CANDIDATE attribute)
|
||||
* after all components have a valid pair.
|
||||
*
|
||||
* Default: 4*PJ_STUN_RTO_VALUE (milliseconds)
|
||||
*/
|
||||
#ifndef PJ_ICE_NOMINATED_CHECK_DELAY
|
||||
# define PJ_ICE_NOMINATED_CHECK_DELAY (4*PJ_STUN_RTO_VALUE)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Minimum interval value to be used for sending STUN keep-alive on the ICE
|
||||
* stream transport, in seconds. This minimum interval, plus a random value
|
||||
|
|
|
@ -196,7 +196,12 @@
|
|||
* host candidate.
|
||||
*/
|
||||
#define PJNATH_EICENOHOSTCAND (PJNATH_ERRNO_START+92) /* 370092 */
|
||||
|
||||
/**
|
||||
* @hideinitializer
|
||||
* Controlled agent timed-out in waiting for the controlling agent to
|
||||
* send nominated check after all connectivity checks have completed.
|
||||
*/
|
||||
#define PJNATH_EICENOMTIMEOUT (PJNATH_ERRNO_START+93) /* 370093 */
|
||||
|
||||
/************************************************************
|
||||
* TURN ERROR CODES
|
||||
|
|
|
@ -176,12 +176,19 @@ typedef struct pj_ice_sess_check pj_ice_sess_check;
|
|||
typedef struct pj_ice_sess_comp
|
||||
{
|
||||
/**
|
||||
* The pointer to ICE check which was nominated for this component.
|
||||
* The value will be NULL if a nominated check has not been found
|
||||
* for this component.
|
||||
* Pointer to ICE check with highest priority which connectivity check
|
||||
* has been successful. The value will be NULL if a no successful check
|
||||
* has not been found for this component.
|
||||
*/
|
||||
pj_ice_sess_check *valid_check;
|
||||
|
||||
/**
|
||||
* Pointer to ICE check with highest priority which connectivity check
|
||||
* has been successful and it has been nominated. The value may be NULL
|
||||
* if there is no such check yet.
|
||||
*/
|
||||
pj_ice_sess_check *nominated_check;
|
||||
|
||||
/**
|
||||
* The STUN session to be used to send and receive STUN messages for this
|
||||
* component.
|
||||
|
@ -552,6 +559,44 @@ typedef struct pj_ice_rx_check
|
|||
} pj_ice_rx_check;
|
||||
|
||||
|
||||
/**
|
||||
* This structure describes various ICE session options. Application
|
||||
* configure the ICE session with these options by calling
|
||||
* #pj_ice_sess_set_options().
|
||||
*/
|
||||
typedef struct pj_ice_sess_options
|
||||
{
|
||||
/**
|
||||
* Specify whether to use aggressive nomination.
|
||||
*/
|
||||
pj_bool_t aggressive;
|
||||
|
||||
/**
|
||||
* For controlling agent if it uses regular nomination, specify the delay
|
||||
* to perform nominated check (connectivity check with USE-CANDIDATE
|
||||
* attribute) after all components have a valid pair.
|
||||
*
|
||||
* Default value is PJ_ICE_NOMINATED_CHECK_DELAY.
|
||||
*/
|
||||
unsigned nominated_check_delay;
|
||||
|
||||
/**
|
||||
* For a controlled agent, specify how long it wants to wait (in
|
||||
* milliseconds) for the controlling agent to complete sending
|
||||
* connectivity check with nominated flag set to true for all components
|
||||
* after the controlled agent has found that all connectivity checks in
|
||||
* its checklist have been completed and there is at least one successful
|
||||
* (but not nominated) check for every component.
|
||||
*
|
||||
* Default value for this option is
|
||||
* ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT. Specify -1 to disable
|
||||
* this timer.
|
||||
*/
|
||||
int controlled_agent_want_nom_timeout;
|
||||
|
||||
} pj_ice_sess_options;
|
||||
|
||||
|
||||
/**
|
||||
* This structure describes the ICE session. For this version of PJNATH,
|
||||
* an ICE session corresponds to a single media stream (unlike the ICE
|
||||
|
@ -569,11 +614,13 @@ struct pj_ice_sess
|
|||
void *user_data; /**< App. data. */
|
||||
pj_mutex_t *mutex; /**< Mutex. */
|
||||
pj_ice_sess_role role; /**< ICE role. */
|
||||
pj_ice_sess_options opt; /**< Options */
|
||||
pj_timestamp tie_breaker; /**< Tie breaker value */
|
||||
pj_uint8_t *prefs; /**< Type preference. */
|
||||
pj_bool_t is_nominating; /**< Nominating stage */
|
||||
pj_bool_t is_complete; /**< Complete? */
|
||||
pj_status_t ice_status; /**< Error status. */
|
||||
pj_timer_entry completion_timer; /**< To call callback. */
|
||||
pj_timer_entry timer; /**< ICE timer. */
|
||||
pj_ice_sess_cb cb; /**< Callback. */
|
||||
|
||||
pj_stun_config stun_cfg; /**< STUN settings. */
|
||||
|
@ -654,6 +701,12 @@ PJ_DECL(void) pj_ice_calc_foundation(pj_pool_t *pool,
|
|||
pj_ice_cand_type type,
|
||||
const pj_sockaddr *base_addr);
|
||||
|
||||
/**
|
||||
* Initialize ICE session options with library default values.
|
||||
*
|
||||
* @param opt ICE session options.
|
||||
*/
|
||||
PJ_DECL(void) pj_ice_sess_options_default(pj_ice_sess_options *opt);
|
||||
|
||||
/**
|
||||
* Create ICE session with the specified role and number of components.
|
||||
|
@ -688,6 +741,34 @@ PJ_DECL(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
|
|||
const pj_str_t *local_passwd,
|
||||
pj_ice_sess **p_ice);
|
||||
|
||||
/**
|
||||
* Get the value of various options of the ICE session.
|
||||
*
|
||||
* @param ice The ICE session.
|
||||
* @param opt The options to be initialized with the values
|
||||
* from the ICE session.
|
||||
*
|
||||
* @return PJ_SUCCESS on success, or the appropriate error.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pj_ice_sess_get_options(pj_ice_sess *ice,
|
||||
pj_ice_sess_options *opt);
|
||||
|
||||
/**
|
||||
* Specify various options for this ICE session. Application MUST only
|
||||
* call this function after the ICE session has been created but before
|
||||
* any connectivity check is started.
|
||||
*
|
||||
* Application should call #pj_ice_sess_get_options() to initialize the
|
||||
* options with their default values.
|
||||
*
|
||||
* @param ice The ICE session.
|
||||
* @param opt Options to be applied to the ICE session.
|
||||
*
|
||||
* @return PJ_SUCCESS on success, or the appropriate error.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice,
|
||||
const pj_ice_sess_options *opt);
|
||||
|
||||
/**
|
||||
* Destroy ICE session. This will cancel any connectivity checks currently
|
||||
* running, if any, and any other events scheduled by this session, as well
|
||||
|
|
|
@ -209,12 +209,12 @@ typedef struct pj_ice_strans_cfg
|
|||
pj_stun_sock_cfg cfg;
|
||||
|
||||
/**
|
||||
* Disable host candidates. When this option is set, no
|
||||
* host candidates will be added.
|
||||
* Maximum number of host candidates to be added. If the
|
||||
* value is zero, no host candidates will be added.
|
||||
*
|
||||
* Default: PJ_FALSE
|
||||
* Default: 64
|
||||
*/
|
||||
pj_bool_t no_host_cands;
|
||||
unsigned max_host_cands;
|
||||
|
||||
/**
|
||||
* Include loopback addresses in the host candidates.
|
||||
|
@ -385,6 +385,32 @@ PJ_DECL(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st);
|
|||
PJ_DECL(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st);
|
||||
|
||||
|
||||
/**
|
||||
* Get the value of various options of the ICE stream transport.
|
||||
*
|
||||
* @param ice_st The ICE stream transport.
|
||||
* @param opt The options to be initialized with the values
|
||||
* from the ICE stream transport.
|
||||
*
|
||||
* @return PJ_SUCCESS on success, or the appropriate error.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pj_ice_strans_get_options(pj_ice_strans *ice_st,
|
||||
pj_ice_sess_options *opt);
|
||||
|
||||
/**
|
||||
* Specify various options for this ICE stream transport. Application
|
||||
* should call #pj_ice_strans_get_options() to initialize the options
|
||||
* with their default values.
|
||||
*
|
||||
* @param ice_st The ICE stream transport.
|
||||
* @param opt Options to be applied to this ICE stream transport.
|
||||
*
|
||||
* @return PJ_SUCCESS on success, or the appropriate error.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pj_ice_strans_set_options(pj_ice_strans *ice_st,
|
||||
const pj_ice_sess_options *opt);
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the ICE session in the ICE stream transport.
|
||||
* When application is about to send an offer containing ICE capability,
|
||||
|
|
|
@ -141,9 +141,9 @@ static int create_ice_strans(struct test_sess *test_sess,
|
|||
}
|
||||
|
||||
if (ept->cfg.enable_host == 0) {
|
||||
ice_cfg.stun.no_host_cands = PJ_TRUE;
|
||||
ice_cfg.stun.max_host_cands = 0;
|
||||
} else {
|
||||
ice_cfg.stun.no_host_cands = PJ_FALSE;
|
||||
//ice_cfg.stun.no_host_cands = PJ_FALSE;
|
||||
ice_cfg.stun.loop_addr = PJ_TRUE;
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ static const struct
|
|||
PJ_BUILD_ERR( PJNATH_EICEMISSINGSDP, "Missing ICE SDP attribute"),
|
||||
PJ_BUILD_ERR( PJNATH_EICEINCANDSDP, "Invalid SDP \"candidate\" attribute"),
|
||||
PJ_BUILD_ERR( PJNATH_EICENOHOSTCAND, "No host candidate associated with srflx"),
|
||||
PJ_BUILD_ERR( PJNATH_EICENOMTIMEOUT, "Controlled agent timed out waiting for nomination"),
|
||||
|
||||
/* TURN related errors */
|
||||
PJ_BUILD_ERR( PJNATH_ETURNINTP, "Invalid/unsupported transport"),
|
||||
|
|
|
@ -66,6 +66,20 @@ static const char *role_names[] =
|
|||
"Controlling"
|
||||
};
|
||||
|
||||
typedef enum timer_type
|
||||
{
|
||||
TIMER_NONE, /**< Timer not active */
|
||||
TIMER_COMPLETION_CALLBACK, /**< Call on_ice_complete() callback */
|
||||
TIMER_CONTROLLED_WAIT_NOM, /**< Controlled agent is waiting for
|
||||
controlling agent to send connectivity
|
||||
check with nominated flag after it has
|
||||
valid check for every components. */
|
||||
TIMER_START_NOMINATED_CHECK,/**< Controlling agent start connectivity
|
||||
checks with USE-CANDIDATE flag. */
|
||||
|
||||
|
||||
};
|
||||
|
||||
/* Candidate type preference */
|
||||
static pj_uint8_t cand_type_prefs[4] =
|
||||
{
|
||||
|
@ -118,10 +132,13 @@ typedef struct timer_data
|
|||
|
||||
|
||||
/* Forward declarations */
|
||||
static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te);
|
||||
static void on_ice_complete(pj_ice_sess *ice, pj_status_t status);
|
||||
static void destroy_ice(pj_ice_sess *ice,
|
||||
pj_status_t reason);
|
||||
static pj_status_t start_periodic_check(pj_timer_heap_t *th,
|
||||
pj_timer_entry *te);
|
||||
static void start_nominated_check(pj_ice_sess *ice);
|
||||
static void periodic_timer(pj_timer_heap_t *th,
|
||||
pj_timer_entry *te);
|
||||
static void handle_incoming_check(pj_ice_sess *ice,
|
||||
|
@ -296,6 +313,15 @@ static pj_status_t init_comp(pj_ice_sess *ice,
|
|||
}
|
||||
|
||||
|
||||
/* Init options with default values */
|
||||
PJ_DEF(void) pj_ice_sess_options_default(pj_ice_sess_options *opt)
|
||||
{
|
||||
opt->aggressive = PJ_TRUE;
|
||||
opt->nominated_check_delay = PJ_ICE_NOMINATED_CHECK_DELAY;
|
||||
opt->controlled_agent_want_nom_timeout =
|
||||
ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create ICE session.
|
||||
*/
|
||||
|
@ -326,6 +352,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
|
|||
ice->tie_breaker.u32.hi = pj_rand();
|
||||
ice->tie_breaker.u32.lo = pj_rand();
|
||||
ice->prefs = cand_type_prefs;
|
||||
pj_ice_sess_options_default(&ice->opt);
|
||||
|
||||
pj_timer_entry_init(&ice->timer, TIMER_NONE, (void*)ice, &on_timer);
|
||||
|
||||
pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name),
|
||||
name, ice);
|
||||
|
@ -345,6 +374,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
|
|||
pj_ice_sess_comp *comp;
|
||||
comp = &ice->comp[i];
|
||||
comp->valid_check = NULL;
|
||||
comp->nominated_check = NULL;
|
||||
|
||||
status = init_comp(ice, i+1, comp);
|
||||
if (status != PJ_SUCCESS) {
|
||||
|
@ -388,6 +418,29 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the value of various options of the ICE session.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pj_ice_sess_get_options(pj_ice_sess *ice,
|
||||
pj_ice_sess_options *opt)
|
||||
{
|
||||
PJ_ASSERT_RETURN(ice, PJ_EINVAL);
|
||||
pj_memcpy(opt, &ice->opt, sizeof(*opt));
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Specify various options for this ICE session.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice,
|
||||
const pj_ice_sess_options *opt)
|
||||
{
|
||||
PJ_ASSERT_RETURN(ice && opt, PJ_EINVAL);
|
||||
pj_memcpy(&ice->opt, opt, sizeof(*opt));
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Destroy
|
||||
*/
|
||||
|
@ -406,10 +459,10 @@ static void destroy_ice(pj_ice_sess *ice,
|
|||
pj_mutex_unlock(ice->mutex);
|
||||
}
|
||||
|
||||
if (ice->completion_timer.id) {
|
||||
if (ice->timer.id) {
|
||||
pj_timer_heap_cancel(ice->stun_cfg.timer_heap,
|
||||
&ice->completion_timer);
|
||||
ice->completion_timer.id = PJ_FALSE;
|
||||
&ice->timer);
|
||||
ice->timer.id = PJ_FALSE;
|
||||
}
|
||||
|
||||
for (i=0; i<ice->comp_cnt; ++i) {
|
||||
|
@ -1039,18 +1092,31 @@ static pj_status_t prune_checklist(pj_ice_sess *ice,
|
|||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/* Timer callback to call on_ice_complete() callback */
|
||||
static void on_completion_timer(pj_timer_heap_t *th,
|
||||
pj_timer_entry *te)
|
||||
/* Timer callback */
|
||||
static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te)
|
||||
{
|
||||
pj_ice_sess *ice = (pj_ice_sess*) te->user_data;
|
||||
enum timer_time type = (enum timer_type)te->id;
|
||||
|
||||
PJ_UNUSED_ARG(th);
|
||||
|
||||
te->id = PJ_FALSE;
|
||||
te->id = TIMER_NONE;
|
||||
|
||||
if (ice->cb.on_ice_complete)
|
||||
(*ice->cb.on_ice_complete)(ice, ice->ice_status);
|
||||
switch (type) {
|
||||
case TIMER_CONTROLLED_WAIT_NOM:
|
||||
LOG4((ice->obj_name,
|
||||
"Controlled agent timed-out in waiting for the controlling "
|
||||
"agent to send nominated check. Setting state to fail now.."));
|
||||
on_ice_complete(ice, PJNATH_EICENOMTIMEOUT);
|
||||
break;
|
||||
case TIMER_COMPLETION_CALLBACK:
|
||||
if (ice->cb.on_ice_complete)
|
||||
(*ice->cb.on_ice_complete)(ice, ice->ice_status);
|
||||
break;
|
||||
case TIMER_START_NOMINATED_CHECK:
|
||||
start_nominated_check(ice);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is called when ICE processing completes */
|
||||
|
@ -1060,6 +1126,11 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
|
|||
ice->is_complete = PJ_TRUE;
|
||||
ice->ice_status = status;
|
||||
|
||||
if (ice->timer.id != TIMER_NONE) {
|
||||
pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
|
||||
ice->timer.id = TIMER_NONE;
|
||||
}
|
||||
|
||||
/* Log message */
|
||||
LOG4((ice->obj_name, "ICE process complete, status=%s",
|
||||
pj_strerror(status, ice->tmp.errmsg,
|
||||
|
@ -1071,13 +1142,9 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
|
|||
if (ice->cb.on_ice_complete) {
|
||||
pj_time_val delay = {0, 0};
|
||||
|
||||
ice->completion_timer.cb = &on_completion_timer;
|
||||
ice->completion_timer.user_data = (void*) ice;
|
||||
ice->completion_timer.id = PJ_TRUE;
|
||||
|
||||
ice->timer.id = TIMER_COMPLETION_CALLBACK;
|
||||
pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
|
||||
&ice->completion_timer,
|
||||
&delay);
|
||||
&ice->timer, &delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1087,10 +1154,13 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
|
|||
static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
||||
pj_ice_sess_check *check)
|
||||
{
|
||||
pj_ice_sess_comp *comp;
|
||||
unsigned i;
|
||||
|
||||
pj_assert(check->state >= PJ_ICE_SESS_CHECK_STATE_SUCCEEDED);
|
||||
|
||||
comp = find_comp(ice, check->lcand->comp_id);
|
||||
|
||||
/* 7.1.2.2.2. Updating Pair States
|
||||
*
|
||||
* The agent sets the state of the pair that generated the check to
|
||||
|
@ -1104,6 +1174,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
* always.
|
||||
*/
|
||||
if (check->err_code==PJ_SUCCESS) {
|
||||
|
||||
for (i=0; i<ice->clist.count; ++i) {
|
||||
pj_ice_sess_check *c = &ice->clist.checks[i];
|
||||
if (pj_strcmp(&c->lcand->foundation, &check->lcand->foundation)==0
|
||||
|
@ -1112,6 +1183,19 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update valid check */
|
||||
if (comp->valid_check == NULL) {
|
||||
comp->valid_check = check;
|
||||
} else {
|
||||
if (CMP_CHECK_PRIO(comp->valid_check, check) < 0)
|
||||
comp->valid_check = check;
|
||||
}
|
||||
|
||||
LOG5((ice->obj_name, "Check %d is successful%s",
|
||||
GET_CHECK_ID(&ice->clist, check),
|
||||
(check->nominated ? " and nominated" : "")));
|
||||
|
||||
}
|
||||
|
||||
/* 8.2. Updating States
|
||||
|
@ -1136,12 +1220,6 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
* than the lowest priority nominated pair for that component
|
||||
*/
|
||||
if (check->err_code==PJ_SUCCESS && check->nominated) {
|
||||
pj_ice_sess_comp *comp;
|
||||
|
||||
LOG5((ice->obj_name, "Check %d is successful and nominated",
|
||||
GET_CHECK_ID(&ice->clist, check)));
|
||||
|
||||
comp = find_comp(ice, check->lcand->comp_id);
|
||||
|
||||
for (i=0; i<ice->clist.count; ++i) {
|
||||
|
||||
|
@ -1181,11 +1259,11 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
}
|
||||
|
||||
/* Update the nominated check for the component */
|
||||
if (comp->valid_check == NULL) {
|
||||
comp->valid_check = check;
|
||||
if (comp->nominated_check == NULL) {
|
||||
comp->nominated_check = check;
|
||||
} else {
|
||||
if (CMP_CHECK_PRIO(comp->valid_check, check) < 0)
|
||||
comp->valid_check = check;
|
||||
if (CMP_CHECK_PRIO(comp->nominated_check, check) < 0)
|
||||
comp->nominated_check = check;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1211,7 +1289,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
* ICE processing as success, otherwise wait.
|
||||
*/
|
||||
for (i=0; i<ice->comp_cnt; ++i) {
|
||||
if (ice->comp[i].valid_check == NULL)
|
||||
if (ice->comp[i].nominated_check == NULL)
|
||||
break;
|
||||
}
|
||||
if (i == ice->comp_cnt) {
|
||||
|
@ -1258,23 +1336,16 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
/* All checks have completed, but we don't have nominated pair.
|
||||
* If agent's role is controlled, check if all components have
|
||||
* valid pair. If it does, this means the controlled agent has
|
||||
* finished the check list early and it's waiting for controlling
|
||||
* agent to send a check with USE-CANDIDATE flag set.
|
||||
* finished the check list and it's waiting for controlling
|
||||
* agent to send checks with USE-CANDIDATE flag set.
|
||||
*/
|
||||
if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED) {
|
||||
unsigned comp_id;
|
||||
for (comp_id=1; comp_id <= ice->comp_cnt; ++comp_id) {
|
||||
unsigned j;
|
||||
for (j=0; j<ice->valid_list.count; ++j) {
|
||||
pj_ice_sess_check *vc = &ice->valid_list.checks[j];
|
||||
if (vc->lcand->comp_id == comp_id)
|
||||
break;
|
||||
}
|
||||
if (j == ice->valid_list.count)
|
||||
for (i=0; i < ice->comp_cnt; ++i) {
|
||||
if (ice->comp[i].valid_check == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (comp_id <= ice->comp_cnt) {
|
||||
if (i < ice->comp_cnt) {
|
||||
/* This component ID doesn't have valid pair.
|
||||
* Mark ICE as failed.
|
||||
*/
|
||||
|
@ -1284,12 +1355,104 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
/* All components have a valid pair.
|
||||
* We should wait until we receive nominated checks.
|
||||
*/
|
||||
if (ice->timer.id == TIMER_NONE &&
|
||||
ice->opt.controlled_agent_want_nom_timeout >= 0)
|
||||
{
|
||||
pj_time_val delay;
|
||||
|
||||
delay.sec = 0;
|
||||
delay.msec = ice->opt.controlled_agent_want_nom_timeout;
|
||||
pj_time_val_normalize(&delay);
|
||||
|
||||
ice->timer.id = TIMER_CONTROLLED_WAIT_NOM;
|
||||
pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
|
||||
&ice->timer,
|
||||
&delay);
|
||||
|
||||
LOG5((ice->obj_name,
|
||||
"All checks have completed. Controlled agent now "
|
||||
"waits for nomination from controlling agent "
|
||||
"(timeout=%d msec)",
|
||||
ice->opt.controlled_agent_want_nom_timeout));
|
||||
}
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
/* Unreached */
|
||||
|
||||
} else if (ice->is_nominating) {
|
||||
/* We are controlling agent and all checks have completed but
|
||||
* there's at least one component without nominated pair (or
|
||||
* more likely we don't have any nominated pairs at all).
|
||||
*/
|
||||
on_ice_complete(ice, PJNATH_EICEFAILED);
|
||||
return PJ_TRUE;
|
||||
|
||||
} else {
|
||||
/* We are controlling agent and all checks have completed. If
|
||||
* we have valid list for every component, then move on to
|
||||
* sending nominated check, otherwise we have failed.
|
||||
*/
|
||||
for (i=0; i<ice->comp_cnt; ++i) {
|
||||
if (ice->comp[i].valid_check == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < ice->comp_cnt) {
|
||||
/* At least one component doesn't have a valid check. Mark
|
||||
* ICE as failed.
|
||||
*/
|
||||
on_ice_complete(ice, PJNATH_EICEFAILED);
|
||||
return PJ_TRUE;
|
||||
}
|
||||
|
||||
/* Now it's time to send connectivity check with nomination
|
||||
* flag set.
|
||||
*/
|
||||
LOG5((ice->obj_name,
|
||||
"All checks have completed, starting nominated checks now"));
|
||||
start_nominated_check(ice);
|
||||
return PJ_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this connectivity check has been successful, scan all components
|
||||
* and see if they have a valid pair, if we are controlling and we haven't
|
||||
* started our nominated check yet.
|
||||
*/
|
||||
if (check->err_code == PJ_SUCCESS &&
|
||||
ice->role==PJ_ICE_SESS_ROLE_CONTROLLING &&
|
||||
!ice->is_nominating &&
|
||||
ice->timer.id == TIMER_NONE)
|
||||
{
|
||||
pj_time_val delay;
|
||||
|
||||
for (i=0; i<ice->comp_cnt; ++i) {
|
||||
if (ice->comp[i].valid_check == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
on_ice_complete(ice, PJNATH_EICEFAILED);
|
||||
return PJ_TRUE;
|
||||
if (i < ice->comp_cnt) {
|
||||
/* Some components still don't have valid pair, continue
|
||||
* processing.
|
||||
*/
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
LOG5((ice->obj_name,
|
||||
"Scheduling nominated check in %d ms",
|
||||
ice->opt.nominated_check_delay));
|
||||
|
||||
/* All components have valid pair. Let connectivity checks run for
|
||||
* a little bit more time, then start our nominated check.
|
||||
*/
|
||||
delay.sec = 0;
|
||||
delay.msec = ice->opt.nominated_check_delay;
|
||||
pj_time_val_normalize(&delay);
|
||||
|
||||
ice->timer.id = TIMER_START_NOMINATED_CHECK;
|
||||
pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay);
|
||||
return PJ_FALSE;
|
||||
}
|
||||
|
||||
/* We still have checks to perform */
|
||||
|
@ -1297,7 +1460,6 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice,
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* Create checklist by pairing local candidates with remote candidates */
|
||||
PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
|
||||
pj_ice_sess *ice,
|
||||
|
@ -1430,10 +1592,11 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
|
|||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/* Perform check on the specified candidate pair */
|
||||
/* Perform check on the specified candidate pair. */
|
||||
static pj_status_t perform_check(pj_ice_sess *ice,
|
||||
pj_ice_sess_checklist *clist,
|
||||
unsigned check_id)
|
||||
unsigned check_id,
|
||||
pj_bool_t nominate)
|
||||
{
|
||||
pj_ice_sess_comp *comp;
|
||||
pj_ice_msg_data *msg_data;
|
||||
|
@ -1481,9 +1644,11 @@ static pj_status_t perform_check(pj_ice_sess *ice,
|
|||
* Also add ICE-CONTROLLING or ICE-CONTROLLED
|
||||
*/
|
||||
if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) {
|
||||
pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg,
|
||||
PJ_STUN_ATTR_USE_CANDIDATE);
|
||||
check->nominated = PJ_TRUE;
|
||||
if (nominate) {
|
||||
pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg,
|
||||
PJ_STUN_ATTR_USE_CANDIDATE);
|
||||
check->nominated = PJ_TRUE;
|
||||
}
|
||||
|
||||
pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg,
|
||||
PJ_STUN_ATTR_ICE_CONTROLLING,
|
||||
|
@ -1549,7 +1714,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
|
|||
pj_ice_sess_check *check = &clist->checks[i];
|
||||
|
||||
if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) {
|
||||
status = perform_check(ice, clist, i);
|
||||
status = perform_check(ice, clist, i, ice->is_nominating);
|
||||
if (status != PJ_SUCCESS) {
|
||||
pj_mutex_unlock(ice->mutex);
|
||||
return status;
|
||||
|
@ -1568,7 +1733,7 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
|
|||
pj_ice_sess_check *check = &clist->checks[i];
|
||||
|
||||
if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) {
|
||||
status = perform_check(ice, clist, i);
|
||||
status = perform_check(ice, clist, i, ice->is_nominating);
|
||||
if (status != PJ_SUCCESS) {
|
||||
pj_mutex_unlock(ice->mutex);
|
||||
return status;
|
||||
|
@ -1596,6 +1761,49 @@ static pj_status_t start_periodic_check(pj_timer_heap_t *th,
|
|||
}
|
||||
|
||||
|
||||
/* Start sending connectivity check with USE-CANDIDATE */
|
||||
static void start_nominated_check(pj_ice_sess *ice)
|
||||
{
|
||||
pj_time_val delay;
|
||||
unsigned i;
|
||||
pj_status_t status;
|
||||
|
||||
LOG4((ice->obj_name, "Starting nominated check.."));
|
||||
|
||||
pj_assert(ice->is_nominating == PJ_FALSE);
|
||||
|
||||
/* Stop our timer if it's active */
|
||||
if (ice->timer.id == TIMER_START_NOMINATED_CHECK) {
|
||||
pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer);
|
||||
ice->timer.id = TIMER_NONE;
|
||||
}
|
||||
|
||||
/* For each component, set the check state of valid check with
|
||||
* highest priority to Waiting (it should have Success state now).
|
||||
*/
|
||||
for (i=0; i<ice->comp_cnt; ++i) {
|
||||
pj_assert(ice->comp[i].nominated_check == NULL);
|
||||
pj_assert(ice->comp[i].valid_check->err_code == PJ_SUCCESS);
|
||||
|
||||
ice->comp[i].valid_check->state = PJ_ICE_SESS_CHECK_STATE_FROZEN;
|
||||
check_set_state(ice, ice->comp[i].valid_check,
|
||||
PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS);
|
||||
}
|
||||
|
||||
/* And (re)start the periodic check */
|
||||
if (!ice->clist.timer.id) {
|
||||
ice->clist.timer.id = PJ_TRUE;
|
||||
delay.sec = delay.msec = 0;
|
||||
status = pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
|
||||
&ice->clist.timer, &delay);
|
||||
if (status != PJ_SUCCESS) {
|
||||
ice->clist.timer.id = PJ_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
ice->is_nominating = PJ_TRUE;
|
||||
}
|
||||
|
||||
/* Timer callback to perform periodic check */
|
||||
static void periodic_timer(pj_timer_heap_t *th,
|
||||
pj_timer_entry *te)
|
||||
|
@ -1642,6 +1850,10 @@ PJ_DEF(pj_status_t) pj_ice_sess_start_check(pj_ice_sess *ice)
|
|||
|
||||
LOG4((ice->obj_name, "Starting ICE check.."));
|
||||
|
||||
/* If we are using aggressive nomination, set the is_nominating state */
|
||||
if (ice->opt.aggressive)
|
||||
ice->is_nominating = PJ_TRUE;
|
||||
|
||||
/* The agent examines the check list for the first media stream (a
|
||||
* media stream is the first media stream when it is described by
|
||||
* the first m-line in the SDP offer and answer). For that media
|
||||
|
@ -1826,7 +2038,8 @@ static void on_stun_request_complete(pj_stun_session *stun_sess,
|
|||
/* Resend request */
|
||||
LOG4((ice->obj_name, "Resending check because of role conflict"));
|
||||
check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_WAITING, 0);
|
||||
perform_check(ice, clist, msg_data->data.req.ckid);
|
||||
perform_check(ice, clist, msg_data->data.req.ckid,
|
||||
check->nominated);
|
||||
pj_mutex_unlock(ice->mutex);
|
||||
return;
|
||||
}
|
||||
|
@ -2312,8 +2525,11 @@ static void handle_incoming_check(pj_ice_sess *ice,
|
|||
if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN ||
|
||||
c->state == PJ_ICE_SESS_CHECK_STATE_WAITING)
|
||||
{
|
||||
/* See if we shall nominate this check */
|
||||
pj_bool_t nominate = (c->nominated || ice->is_nominating);
|
||||
|
||||
LOG5((ice->obj_name, "Performing triggered check for check %d",i));
|
||||
perform_check(ice, &ice->clist, i);
|
||||
perform_check(ice, &ice->clist, i, nominate);
|
||||
|
||||
} else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) {
|
||||
/* Should retransmit immediately
|
||||
|
@ -2361,6 +2577,7 @@ static void handle_incoming_check(pj_ice_sess *ice,
|
|||
else if (ice->clist.count < PJ_ICE_MAX_CHECKS) {
|
||||
|
||||
pj_ice_sess_check *c = &ice->clist.checks[ice->clist.count];
|
||||
pj_bool_t nominate;
|
||||
|
||||
c->lcand = lcand;
|
||||
c->rcand = rcand;
|
||||
|
@ -2369,9 +2586,11 @@ static void handle_incoming_check(pj_ice_sess *ice,
|
|||
c->nominated = rcheck->use_candidate;
|
||||
c->err_code = PJ_SUCCESS;
|
||||
|
||||
nominate = (c->nominated || ice->is_nominating);
|
||||
|
||||
LOG4((ice->obj_name, "New triggered check added: %d",
|
||||
ice->clist.count));
|
||||
perform_check(ice, &ice->clist, ice->clist.count++);
|
||||
perform_check(ice, &ice->clist, ice->clist.count++, nominate);
|
||||
|
||||
} else {
|
||||
LOG4((ice->obj_name, "Error: unable to perform triggered check: "
|
||||
|
|
|
@ -158,6 +158,7 @@ struct pj_ice_strans
|
|||
pj_ice_strans_cfg cfg; /**< Configuration. */
|
||||
pj_ice_strans_cb cb; /**< Application callback. */
|
||||
pj_lock_t *init_lock; /**< Initialization mutex. */
|
||||
pj_ice_sess_options opt; /**< ICE session options */
|
||||
|
||||
pj_ice_sess *ice; /**< ICE session. */
|
||||
pj_time_val start_time;/**< Time when ICE was started */
|
||||
|
@ -200,6 +201,8 @@ PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg)
|
|||
cfg->af = pj_AF_INET();
|
||||
cfg->stun.port = PJ_STUN_PORT;
|
||||
cfg->turn.conn_type = PJ_TURN_TP_UDP;
|
||||
|
||||
cfg->stun.max_host_cands = 64;
|
||||
}
|
||||
|
||||
|
||||
|
@ -245,7 +248,7 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
|
|||
comp->default_cand = 0;
|
||||
|
||||
/* Create STUN transport if configured */
|
||||
if (ice_st->cfg.stun.server.slen || !ice_st->cfg.stun.no_host_cands) {
|
||||
if (ice_st->cfg.stun.server.slen || ice_st->cfg.stun.max_host_cands) {
|
||||
pj_stun_sock_cb stun_sock_cb;
|
||||
pj_ice_sess_cand *cand;
|
||||
|
||||
|
@ -309,10 +312,10 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
|
|||
|
||||
}
|
||||
|
||||
/* Add local addresses to host candidates, unless no_host_cands
|
||||
* flag is set.
|
||||
/* Add local addresses to host candidates, unless max_host_cands
|
||||
* is set to zero.
|
||||
*/
|
||||
if (ice_st->cfg.stun.no_host_cands == PJ_FALSE) {
|
||||
if (ice_st->cfg.stun.max_host_cands) {
|
||||
pj_stun_sock_info stun_sock_info;
|
||||
unsigned i;
|
||||
|
||||
|
@ -321,7 +324,9 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id)
|
|||
if (status != PJ_SUCCESS)
|
||||
return status;
|
||||
|
||||
for (i=0; i<stun_sock_info.alias_cnt; ++i) {
|
||||
for (i=0; i<stun_sock_info.alias_cnt &&
|
||||
i<ice_st->cfg.stun.max_host_cands; ++i)
|
||||
{
|
||||
char addrinfo[PJ_INET6_ADDRSTRLEN+10];
|
||||
const pj_sockaddr *addr = &stun_sock_info.aliases[i];
|
||||
|
||||
|
@ -444,6 +449,8 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name,
|
|||
ice_st->obj_name = pool->obj_name;
|
||||
ice_st->user_data = user_data;
|
||||
|
||||
pj_ice_sess_options_default(&ice_st->opt);
|
||||
|
||||
PJ_LOG(4,(ice_st->obj_name,
|
||||
"Creating ICE stream transport with %d component(s)",
|
||||
comp_cnt));
|
||||
|
@ -646,6 +653,30 @@ PJ_DEF(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the value of various options of the ICE stream transport.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pj_ice_strans_get_options( pj_ice_strans *ice_st,
|
||||
pj_ice_sess_options *opt)
|
||||
{
|
||||
PJ_ASSERT_RETURN(ice_st && opt, PJ_EINVAL);
|
||||
pj_memcpy(opt, &ice_st->opt, sizeof(*opt));
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Specify various options for this ICE stream transport.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pj_ice_strans_set_options(pj_ice_strans *ice_st,
|
||||
const pj_ice_sess_options *opt)
|
||||
{
|
||||
PJ_ASSERT_RETURN(ice_st && opt, PJ_EINVAL);
|
||||
pj_memcpy(&ice_st->opt, opt, sizeof(*opt));
|
||||
if (ice_st->ice)
|
||||
pj_ice_sess_set_options(ice_st->ice, &ice_st->opt);
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create ICE!
|
||||
*/
|
||||
|
@ -682,6 +713,9 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st,
|
|||
/* Associate user data */
|
||||
ice_st->ice->user_data = (void*)ice_st;
|
||||
|
||||
/* Set options */
|
||||
pj_ice_sess_set_options(ice_st->ice, &ice_st->opt);
|
||||
|
||||
/* If default candidate for components are SRFLX one, upload a custom
|
||||
* type priority to ICE session so that SRFLX candidates will get
|
||||
* checked first.
|
||||
|
|
|
@ -4728,7 +4728,7 @@ static pj_status_t create_ipv6_media_transports(void)
|
|||
for (j=0; j<MAX_RETRY; ++j) {
|
||||
status = pjmedia_transport_udp_create3(pjsua_get_pjmedia_endpt(),
|
||||
pj_AF_INET6(),
|
||||
NULL,
|
||||
app_config.rtp_cfg.public_addr.ptr,
|
||||
&app_config.rtp_cfg.bound_addr,
|
||||
port,
|
||||
0, &tp[i].transport);
|
||||
|
|
|
@ -39,7 +39,8 @@ static struct app_t
|
|||
{
|
||||
unsigned comp_cnt;
|
||||
pj_str_t ns;
|
||||
pj_bool_t no_host;
|
||||
int max_host;
|
||||
pj_bool_t regular;
|
||||
pj_str_t stun_srv;
|
||||
pj_str_t turn_srv;
|
||||
pj_bool_t turn_tcp;
|
||||
|
@ -306,8 +307,9 @@ static pj_status_t icedemo_init(void)
|
|||
|
||||
/* -= Start initializing ICE stream transport config =- */
|
||||
|
||||
/* Disable host candidates? */
|
||||
icedemo.ice_cfg.stun.no_host_cands = icedemo.opt.no_host;
|
||||
/* Maximum number of host candidates */
|
||||
if (icedemo.opt.max_host != -1)
|
||||
icedemo.ice_cfg.stun.max_host_cands = icedemo.opt.max_host;
|
||||
|
||||
/* Configure STUN/srflx candidate resolution */
|
||||
if (icedemo.opt.stun_srv.slen) {
|
||||
|
@ -374,6 +376,7 @@ static pj_status_t icedemo_init(void)
|
|||
static void icedemo_create_instance(void)
|
||||
{
|
||||
pj_ice_strans_cb icecb;
|
||||
pj_ice_sess_options opt;
|
||||
pj_status_t status;
|
||||
|
||||
if (icedemo.icest != NULL) {
|
||||
|
@ -398,6 +401,17 @@ static void icedemo_create_instance(void)
|
|||
icedemo_perror("error creating ice", status);
|
||||
else
|
||||
PJ_LOG(3,(THIS_FILE, "ICE instance successfully created"));
|
||||
|
||||
status = pj_ice_strans_get_options(icedemo.icest, &opt);
|
||||
pj_assert(status == PJ_SUCCESS);
|
||||
|
||||
if (icedemo.opt.regular)
|
||||
opt.aggressive = PJ_FALSE;
|
||||
else
|
||||
opt.aggressive = PJ_TRUE;
|
||||
|
||||
status = pj_ice_strans_set_options(icedemo.icest, &opt);
|
||||
pj_assert(status == PJ_SUCCESS);
|
||||
}
|
||||
|
||||
/* Utility to nullify parsed remote info */
|
||||
|
@ -960,7 +974,7 @@ static void icedemo_send_data(unsigned comp_id, const char *data)
|
|||
}
|
||||
*/
|
||||
|
||||
if (comp_id > pj_ice_strans_get_running_comp_cnt(icedemo.icest)) {
|
||||
if (comp_id<1||comp_id>pj_ice_strans_get_running_comp_cnt(icedemo.icest)) {
|
||||
PJ_LOG(1,(THIS_FILE, "Error: invalid component ID"));
|
||||
return;
|
||||
}
|
||||
|
@ -1137,7 +1151,8 @@ static void icedemo_usage()
|
|||
puts(" --comp-cnt, -c N Component count (default=1)");
|
||||
puts(" --nameserver, -n IP Configure nameserver to activate DNS SRV");
|
||||
puts(" resolution");
|
||||
puts(" --no-host, -H Disable host candidates");
|
||||
puts(" --max-host, -H N Set max number of host candidates to N");
|
||||
puts(" --regular, -R Use regular nomination (default aggressive)");
|
||||
puts(" --help, -h Display this screen.");
|
||||
puts("");
|
||||
puts("STUN related options:");
|
||||
|
@ -1165,21 +1180,23 @@ int main(int argc, char *argv[])
|
|||
struct pj_getopt_option long_options[] = {
|
||||
{ "comp-cnt", 1, 0, 'c'},
|
||||
{ "nameserver", 1, 0, 'n'},
|
||||
{ "no-host", 0, 0, 'H'},
|
||||
{ "max-host", 1, 0, 'H'},
|
||||
{ "help", 0, 0, 'h'},
|
||||
{ "stun-srv", 1, 0, 's'},
|
||||
{ "turn-srv", 1, 0, 't'},
|
||||
{ "turn-tcp", 0, 0, 'T'},
|
||||
{ "turn-username", 1, 0, 'u'},
|
||||
{ "turn-password", 1, 0, 'p'},
|
||||
{ "turn-fingerprint", 0, 0, 'F'}
|
||||
{ "turn-fingerprint", 0, 0, 'F'},
|
||||
{ "regular", 0, 0, 'R'}
|
||||
};
|
||||
int c, opt_id;
|
||||
pj_status_t status;
|
||||
|
||||
icedemo.opt.comp_cnt = 1;
|
||||
icedemo.opt.max_host = -1;
|
||||
|
||||
while((c=pj_getopt_long(argc,argv, "n:s:t:u:p:HhTF", long_options, &opt_id))!=-1) {
|
||||
while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:hTFR", long_options, &opt_id))!=-1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
icedemo.opt.comp_cnt = atoi(pj_optarg);
|
||||
|
@ -1192,7 +1209,7 @@ int main(int argc, char *argv[])
|
|||
icedemo.opt.ns = pj_str(pj_optarg);
|
||||
break;
|
||||
case 'H':
|
||||
icedemo.opt.no_host = PJ_TRUE;
|
||||
icedemo.opt.max_host = atoi(pj_optarg);
|
||||
break;
|
||||
case 'h':
|
||||
icedemo_usage();
|
||||
|
@ -1215,6 +1232,9 @@ int main(int argc, char *argv[])
|
|||
case 'F':
|
||||
icedemo.opt.turn_fingerprint = PJ_TRUE;
|
||||
break;
|
||||
case 'R':
|
||||
icedemo.opt.regular = PJ_TRUE;
|
||||
break;
|
||||
default:
|
||||
printf("Argument \"%s\" is not valid. Use -h to see help",
|
||||
argv[pj_optind]);
|
||||
|
|
|
@ -810,7 +810,8 @@ static pj_status_t create_ice_media_transports(void)
|
|||
ice_cfg.stun.server = pj_str(stunip);
|
||||
ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
|
||||
}
|
||||
ice_cfg.stun.no_host_cands = pjsua_var.media_cfg.ice_no_host_cands;
|
||||
if (pjsua_var.media_cfg.ice_no_host_cands)
|
||||
ice_cfg.stun.max_host_cands = 0;
|
||||
|
||||
/* Configure TURN settings */
|
||||
if (pjsua_var.media_cfg.enable_turn) {
|
||||
|
|
Loading…
Reference in New Issue