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
/**
* 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.
*
@ -47,6 +63,7 @@ PJ_BEGIN_DECL
* for logging purposes.
* @param comp_cnt Number of components to be created.
* @param stun_cfg Pointer to STUN configuration settings.
* @param cb Optional callbacks.
* @param p_tp Pointer to receive the media transport instance.
*
* @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,
unsigned comp_cnt,
pj_stun_config *stun_cfg,
const pjmedia_ice_cb *cb,
pjmedia_transport **p_tp);
/**

View File

@ -25,6 +25,7 @@ struct transport_ice
{
pjmedia_transport base;
pj_ice_strans *ice_st;
pjmedia_ice_cb cb;
pj_time_val start_ice;
@ -95,6 +96,7 @@ PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
const char *name,
unsigned comp_cnt,
pj_stun_config *stun_cfg,
const pjmedia_ice_cb *cb,
pjmedia_transport **p_tp)
{
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.type = PJMEDIA_TRANSPORT_TYPE_ICE;
if (cb)
pj_memcpy(&tp_ice->cb, cb, sizeof(pjmedia_ice_cb));
ice_st->user_data = (void*)tp_ice;
/* 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,
pj_status_t status)
pj_status_t result)
{
struct transport_ice *tp_ice = (struct transport_ice*) ice_st->user_data;
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_TIME_VAL_SUB(end_ice, tp_ice->start_ice);
if (status != PJ_SUCCESS) {
if (result != PJ_SUCCESS) {
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,
"ICE negotiation failed after %d:%03ds: %s",
(int)end_ice.sec, (int)end_ice.msec,
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];
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(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)));
/* Notify application */
if (tp_ice->cb.on_ice_complete)
(*tp_ice->cb.on_ice_complete)(&tp_ice->base, result);
}

View File

@ -464,6 +464,7 @@ struct pj_ice_sess
pj_uint8_t *prefs; /**< Type preference. */
pj_bool_t is_complete; /**< Complete? */
pj_status_t ice_status; /**< Error status. */
pj_timer_entry completion_timer; /**< To call callback. */
pj_ice_sess_cb cb; /**< Callback. */
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"));
}
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) {
if (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;
}
/* 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 */
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 */
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,
"Media for call %d is suspended (hold) by remote",
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 {
PJ_LOG(3,(THIS_FILE,
"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 */
PJSUA_CALL_MEDIA_REMOTE_HOLD,
/** The media has reported error (e.g. ICE negotiation) */
PJSUA_CALL_MEDIA_ERROR
} 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) */
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 */
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
pj_ice_strans_comp comp;
pjmedia_ice_cb ice_cb;
int next_port;
#if PJMEDIA_ADVERTISE_RTCP
enum { COMP_CNT=2 };
@ -563,8 +600,11 @@ static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
enum { COMP_CNT=1 };
#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,
&pjsua_var.stun_cfg,
&pjsua_var.stun_cfg, &ice_cb,
&pjsua_var.calls[i].med_tp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create ICE media transport",