Ticket #370: Implemented callback notification to application when ICE negotiation fails (via on_call_media_state callback)
git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@1435 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
parent
4a5d7708a0
commit
096c56c1a5
|
@ -39,6 +39,22 @@
|
||||||
PJ_BEGIN_DECL
|
PJ_BEGIN_DECL
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure containing callbacks to receive ICE notifications.
|
||||||
|
*/
|
||||||
|
typedef struct pjmedia_ice_cb
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This callback will be called when ICE negotiation completes.
|
||||||
|
*
|
||||||
|
* @param tp PJMEDIA ICE transport.
|
||||||
|
* @param status ICE negotiation result, PJ_SUCCESS on success.
|
||||||
|
*/
|
||||||
|
void (*on_ice_complete)(pjmedia_transport *tp,
|
||||||
|
pj_status_t status);
|
||||||
|
|
||||||
|
} pjmedia_ice_cb;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the media transport.
|
* Create the media transport.
|
||||||
*
|
*
|
||||||
|
@ -47,6 +63,7 @@ PJ_BEGIN_DECL
|
||||||
* for logging purposes.
|
* for logging purposes.
|
||||||
* @param comp_cnt Number of components to be created.
|
* @param comp_cnt Number of components to be created.
|
||||||
* @param stun_cfg Pointer to STUN configuration settings.
|
* @param stun_cfg Pointer to STUN configuration settings.
|
||||||
|
* @param cb Optional callbacks.
|
||||||
* @param p_tp Pointer to receive the media transport instance.
|
* @param p_tp Pointer to receive the media transport instance.
|
||||||
*
|
*
|
||||||
* @return PJ_SUCCESS on success, or the appropriate error code.
|
* @return PJ_SUCCESS on success, or the appropriate error code.
|
||||||
|
@ -55,6 +72,7 @@ PJ_DECL(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
|
||||||
const char *name,
|
const char *name,
|
||||||
unsigned comp_cnt,
|
unsigned comp_cnt,
|
||||||
pj_stun_config *stun_cfg,
|
pj_stun_config *stun_cfg,
|
||||||
|
const pjmedia_ice_cb *cb,
|
||||||
pjmedia_transport **p_tp);
|
pjmedia_transport **p_tp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,6 +25,7 @@ struct transport_ice
|
||||||
{
|
{
|
||||||
pjmedia_transport base;
|
pjmedia_transport base;
|
||||||
pj_ice_strans *ice_st;
|
pj_ice_strans *ice_st;
|
||||||
|
pjmedia_ice_cb cb;
|
||||||
|
|
||||||
pj_time_val start_ice;
|
pj_time_val start_ice;
|
||||||
|
|
||||||
|
@ -95,6 +96,7 @@ PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
|
||||||
const char *name,
|
const char *name,
|
||||||
unsigned comp_cnt,
|
unsigned comp_cnt,
|
||||||
pj_stun_config *stun_cfg,
|
pj_stun_config *stun_cfg,
|
||||||
|
const pjmedia_ice_cb *cb,
|
||||||
pjmedia_transport **p_tp)
|
pjmedia_transport **p_tp)
|
||||||
{
|
{
|
||||||
pj_ice_strans *ice_st;
|
pj_ice_strans *ice_st;
|
||||||
|
@ -123,6 +125,9 @@ PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
|
||||||
tp_ice->base.op = &tp_ice_op;
|
tp_ice->base.op = &tp_ice_op;
|
||||||
tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE;
|
tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE;
|
||||||
|
|
||||||
|
if (cb)
|
||||||
|
pj_memcpy(&tp_ice->cb, cb, sizeof(pjmedia_ice_cb));
|
||||||
|
|
||||||
ice_st->user_data = (void*)tp_ice;
|
ice_st->user_data = (void*)tp_ice;
|
||||||
|
|
||||||
/* Done */
|
/* Done */
|
||||||
|
@ -686,7 +691,7 @@ static void ice_on_rx_data(pj_ice_strans *ice_st, unsigned comp_id,
|
||||||
|
|
||||||
|
|
||||||
static void ice_on_ice_complete(pj_ice_strans *ice_st,
|
static void ice_on_ice_complete(pj_ice_strans *ice_st,
|
||||||
pj_status_t status)
|
pj_status_t result)
|
||||||
{
|
{
|
||||||
struct transport_ice *tp_ice = (struct transport_ice*) ice_st->user_data;
|
struct transport_ice *tp_ice = (struct transport_ice*) ice_st->user_data;
|
||||||
pj_time_val end_ice;
|
pj_time_val end_ice;
|
||||||
|
@ -698,16 +703,14 @@ static void ice_on_ice_complete(pj_ice_strans *ice_st,
|
||||||
pj_gettimeofday(&end_ice);
|
pj_gettimeofday(&end_ice);
|
||||||
PJ_TIME_VAL_SUB(end_ice, tp_ice->start_ice);
|
PJ_TIME_VAL_SUB(end_ice, tp_ice->start_ice);
|
||||||
|
|
||||||
if (status != PJ_SUCCESS) {
|
if (result != PJ_SUCCESS) {
|
||||||
char errmsg[PJ_ERR_MSG_SIZE];
|
char errmsg[PJ_ERR_MSG_SIZE];
|
||||||
pj_strerror(status, errmsg, sizeof(errmsg));
|
pj_strerror(result, errmsg, sizeof(errmsg));
|
||||||
PJ_LOG(1,(ice_st->obj_name,
|
PJ_LOG(1,(ice_st->obj_name,
|
||||||
"ICE negotiation failed after %d:%03ds: %s",
|
"ICE negotiation failed after %d:%03ds: %s",
|
||||||
(int)end_ice.sec, (int)end_ice.msec,
|
(int)end_ice.sec, (int)end_ice.msec,
|
||||||
errmsg));
|
errmsg));
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
check = &ice_st->ice->valid_list.checks[0];
|
check = &ice_st->ice->valid_list.checks[0];
|
||||||
|
|
||||||
lcand = check->lcand;
|
lcand = check->lcand;
|
||||||
|
@ -716,7 +719,7 @@ static void ice_on_ice_complete(pj_ice_strans *ice_st,
|
||||||
pj_ansi_strcpy(src_addr, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
|
pj_ansi_strcpy(src_addr, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
|
||||||
pj_ansi_strcpy(dst_addr, pj_inet_ntoa(rcand->addr.ipv4.sin_addr));
|
pj_ansi_strcpy(dst_addr, pj_inet_ntoa(rcand->addr.ipv4.sin_addr));
|
||||||
|
|
||||||
PJ_LOG(3,(ice_st->obj_name,
|
PJ_LOG(4,(ice_st->obj_name,
|
||||||
"ICE negotiation completed in %d.%03ds. Sending from "
|
"ICE negotiation completed in %d.%03ds. Sending from "
|
||||||
"%s:%d to %s:%d",
|
"%s:%d to %s:%d",
|
||||||
(int)end_ice.sec, (int)end_ice.msec,
|
(int)end_ice.sec, (int)end_ice.msec,
|
||||||
|
@ -724,4 +727,9 @@ static void ice_on_ice_complete(pj_ice_strans *ice_st,
|
||||||
dst_addr, pj_ntohs(rcand->addr.ipv4.sin_port)));
|
dst_addr, pj_ntohs(rcand->addr.ipv4.sin_port)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Notify application */
|
||||||
|
if (tp_ice->cb.on_ice_complete)
|
||||||
|
(*tp_ice->cb.on_ice_complete)(&tp_ice->base, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -464,6 +464,7 @@ struct pj_ice_sess
|
||||||
pj_uint8_t *prefs; /**< Type preference. */
|
pj_uint8_t *prefs; /**< Type preference. */
|
||||||
pj_bool_t is_complete; /**< Complete? */
|
pj_bool_t is_complete; /**< Complete? */
|
||||||
pj_status_t ice_status; /**< Error status. */
|
pj_status_t ice_status; /**< Error status. */
|
||||||
|
pj_timer_entry completion_timer; /**< To call callback. */
|
||||||
pj_ice_sess_cb cb; /**< Callback. */
|
pj_ice_sess_cb cb; /**< Callback. */
|
||||||
|
|
||||||
pj_stun_config stun_cfg; /**< STUN settings. */
|
pj_stun_config stun_cfg; /**< STUN settings. */
|
||||||
|
|
|
@ -336,6 +336,12 @@ static void destroy_ice(pj_ice_sess *ice,
|
||||||
LOG4((ice->obj_name, "Destroying ICE session"));
|
LOG4((ice->obj_name, "Destroying ICE session"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ice->completion_timer.id) {
|
||||||
|
pj_timer_heap_cancel(ice->stun_cfg.timer_heap,
|
||||||
|
&ice->completion_timer);
|
||||||
|
ice->completion_timer.id = PJ_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
for (i=0; i<ice->comp_cnt; ++i) {
|
for (i=0; i<ice->comp_cnt; ++i) {
|
||||||
if (ice->comp[i].stun_sess) {
|
if (ice->comp[i].stun_sess) {
|
||||||
pj_stun_session_destroy(ice->comp[i].stun_sess);
|
pj_stun_session_destroy(ice->comp[i].stun_sess);
|
||||||
|
@ -945,6 +951,20 @@ static pj_status_t prune_checklist(pj_ice_sess *ice,
|
||||||
return PJ_SUCCESS;
|
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)
|
||||||
|
{
|
||||||
|
pj_ice_sess *ice = (pj_ice_sess*) te->user_data;
|
||||||
|
|
||||||
|
PJ_UNUSED_ARG(th);
|
||||||
|
|
||||||
|
te->id = PJ_FALSE;
|
||||||
|
|
||||||
|
if (ice->cb.on_ice_complete)
|
||||||
|
(*ice->cb.on_ice_complete)(ice, ice->ice_status);
|
||||||
|
}
|
||||||
|
|
||||||
/* This function is called when ICE processing completes */
|
/* This function is called when ICE processing completes */
|
||||||
static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
|
static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
|
||||||
{
|
{
|
||||||
|
@ -962,7 +982,15 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
|
||||||
|
|
||||||
/* Call callback */
|
/* Call callback */
|
||||||
if (ice->cb.on_ice_complete) {
|
if (ice->cb.on_ice_complete) {
|
||||||
(*ice->cb.on_ice_complete)(ice, status);
|
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;
|
||||||
|
|
||||||
|
pj_timer_heap_schedule(ice->stun_cfg.timer_heap,
|
||||||
|
&ice->completion_timer,
|
||||||
|
&delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1675,6 +1675,14 @@ static void on_call_media_state(pjsua_call_id call_id)
|
||||||
PJ_LOG(3,(THIS_FILE,
|
PJ_LOG(3,(THIS_FILE,
|
||||||
"Media for call %d is suspended (hold) by remote",
|
"Media for call %d is suspended (hold) by remote",
|
||||||
call_id));
|
call_id));
|
||||||
|
} else if (call_info.media_status == PJSUA_CALL_MEDIA_ERROR) {
|
||||||
|
pj_str_t reason = pj_str("ICE negotiation failed");
|
||||||
|
|
||||||
|
PJ_LOG(1,(THIS_FILE,
|
||||||
|
"Media has reported error, disconnecting call"));
|
||||||
|
|
||||||
|
pjsua_call_hangup(call_id, 500, &reason, NULL);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
PJ_LOG(3,(THIS_FILE,
|
PJ_LOG(3,(THIS_FILE,
|
||||||
"Media for call %d is inactive",
|
"Media for call %d is inactive",
|
||||||
|
|
|
@ -2375,6 +2375,9 @@ typedef enum pjsua_call_media_status
|
||||||
/** The media is currently put on hold by remote endpoint */
|
/** The media is currently put on hold by remote endpoint */
|
||||||
PJSUA_CALL_MEDIA_REMOTE_HOLD,
|
PJSUA_CALL_MEDIA_REMOTE_HOLD,
|
||||||
|
|
||||||
|
/** The media has reported error (e.g. ICE negotiation) */
|
||||||
|
PJSUA_CALL_MEDIA_ERROR
|
||||||
|
|
||||||
} pjsua_call_media_status;
|
} pjsua_call_media_status;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -537,6 +537,42 @@ on_error:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This callback is called when ICE negotiation completes */
|
||||||
|
static void on_ice_complete(pjmedia_transport *tp, pj_status_t result)
|
||||||
|
{
|
||||||
|
unsigned id, c;
|
||||||
|
pj_bool_t found = PJ_FALSE;
|
||||||
|
|
||||||
|
/* We're only interested with failure case */
|
||||||
|
if (result == PJ_SUCCESS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Find call which has this media transport */
|
||||||
|
|
||||||
|
PJSUA_LOCK();
|
||||||
|
|
||||||
|
for (id=0, c=0; id<PJSUA_MAX_CALLS && c<pjsua_var.call_cnt; ++id) {
|
||||||
|
pjsua_call *call = &pjsua_var.calls[id];
|
||||||
|
if (call->inv) {
|
||||||
|
++c;
|
||||||
|
|
||||||
|
if (call->med_tp == tp) {
|
||||||
|
call->media_st = PJSUA_CALL_MEDIA_ERROR;
|
||||||
|
call->media_dir = PJMEDIA_DIR_NONE;
|
||||||
|
found = PJ_TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PJSUA_UNLOCK();
|
||||||
|
|
||||||
|
if (found && pjsua_var.ua_cfg.cb.on_call_media_state) {
|
||||||
|
pjsua_var.ua_cfg.cb.on_call_media_state(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Create ICE media transports (when ice is enabled) */
|
/* Create ICE media transports (when ice is enabled) */
|
||||||
static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
|
static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
|
||||||
{
|
{
|
||||||
|
@ -556,6 +592,7 @@ static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
|
||||||
/* Create each media transport */
|
/* Create each media transport */
|
||||||
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
|
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
|
||||||
pj_ice_strans_comp comp;
|
pj_ice_strans_comp comp;
|
||||||
|
pjmedia_ice_cb ice_cb;
|
||||||
int next_port;
|
int next_port;
|
||||||
#if PJMEDIA_ADVERTISE_RTCP
|
#if PJMEDIA_ADVERTISE_RTCP
|
||||||
enum { COMP_CNT=2 };
|
enum { COMP_CNT=2 };
|
||||||
|
@ -563,8 +600,11 @@ static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
|
||||||
enum { COMP_CNT=1 };
|
enum { COMP_CNT=1 };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
|
||||||
|
ice_cb.on_ice_complete = &on_ice_complete;
|
||||||
|
|
||||||
status = pjmedia_ice_create(pjsua_var.med_endpt, NULL, COMP_CNT,
|
status = pjmedia_ice_create(pjsua_var.med_endpt, NULL, COMP_CNT,
|
||||||
&pjsua_var.stun_cfg,
|
&pjsua_var.stun_cfg, &ice_cb,
|
||||||
&pjsua_var.calls[i].med_tp);
|
&pjsua_var.calls[i].med_tp);
|
||||||
if (status != PJ_SUCCESS) {
|
if (status != PJ_SUCCESS) {
|
||||||
pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
|
pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
|
||||||
|
|
Loading…
Reference in New Issue