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:
Benny Prijono 2007-09-15 08:30:16 +00:00
parent 4a5d7708a0
commit 096c56c1a5
7 changed files with 126 additions and 20 deletions

View File

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

View File

@ -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,30 +703,33 @@ 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];
lcand = check->lcand;
rcand = check->rcand;
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_LOG(4,(ice_st->obj_name,
"ICE negotiation completed in %d.%03ds. Sending from "
"%s:%d to %s:%d",
(int)end_ice.sec, (int)end_ice.msec,
src_addr, pj_ntohs(lcand->addr.ipv4.sin_port),
dst_addr, pj_ntohs(rcand->addr.ipv4.sin_port)));
} }
check = &ice_st->ice->valid_list.checks[0]; /* Notify application */
if (tp_ice->cb.on_ice_complete)
lcand = check->lcand; (*tp_ice->cb.on_ice_complete)(&tp_ice->base, result);
rcand = check->rcand;
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_LOG(3,(ice_st->obj_name,
"ICE negotiation completed in %d.%03ds. Sending from "
"%s:%d to %s:%d",
(int)end_ice.sec, (int)end_ice.msec,
src_addr, pj_ntohs(lcand->addr.ipv4.sin_port),
dst_addr, pj_ntohs(rcand->addr.ipv4.sin_port)));
} }

View File

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

View File

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

View File

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

View File

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

View File

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