diff options
-rw-r--r-- | pjmedia/include/pjmedia/transport_ice.h | 18 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/transport_ice.c | 44 | ||||
-rw-r--r-- | pjnath/include/pjnath/ice_session.h | 1 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice_session.c | 30 | ||||
-rw-r--r-- | pjsip-apps/src/pjsua/pjsua_app.c | 8 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua.h | 3 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_media.c | 42 |
7 files changed, 126 insertions, 20 deletions
diff --git a/pjmedia/include/pjmedia/transport_ice.h b/pjmedia/include/pjmedia/transport_ice.h index cf72886d..2a51ae76 100644 --- a/pjmedia/include/pjmedia/transport_ice.h +++ b/pjmedia/include/pjmedia/transport_ice.h @@ -40,6 +40,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. * * @param endpt The media endpoint. @@ -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); /** diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c index 019ba04c..4fa21c29 100644 --- a/pjmedia/src/pjmedia/transport_ice.c +++ b/pjmedia/src/pjmedia/transport_ice.c @@ -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); } diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h index 14dba41f..b601385c 100644 --- a/pjnath/include/pjnath/ice_session.h +++ b/pjnath/include/pjnath/ice_session.h @@ -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. */ diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index 78422af1..27634442 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -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); } } } diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index a586b4dd..13f1dbd2 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -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", diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index a82141b5..f493de5d 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -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; diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 8700bd59..0e58d100 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -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", |