diff options
-rw-r--r-- | pjmedia/include/pjmedia.h | 1 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/transport.h | 37 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/transport_ice.h | 12 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/transport_ice.c | 68 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/transport_udp.c | 1 | ||||
-rw-r--r-- | pjnath/include/pjnath/ice_stream_transport.h | 3 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice.c | 7 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice_stream_transport.c | 42 | ||||
-rw-r--r-- | pjsip-apps/build/pjsua.dsp | 4 | ||||
-rw-r--r-- | pjsip-apps/src/pjsua/pjsua_app.c | 77 | ||||
-rw-r--r-- | pjsip/build/pjsua_lib.dsp | 4 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_endpoint.h | 9 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua.h | 130 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua_internal.h | 24 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_endpoint.c | 8 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_call.c | 251 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_core.c | 95 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_media.c | 460 |
18 files changed, 725 insertions, 508 deletions
diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h index 10d81c02..6db9bbe7 100644 --- a/pjmedia/include/pjmedia.h +++ b/pjmedia/include/pjmedia.h @@ -54,6 +54,7 @@ #include <pjmedia/splitcomb.h> #include <pjmedia/tonegen.h> #include <pjmedia/transport.h> +#include <pjmedia/transport_ice.h> #include <pjmedia/transport_udp.h> #include <pjmedia/wav_playlist.h> #include <pjmedia/wav_port.h> diff --git a/pjmedia/include/pjmedia/transport.h b/pjmedia/include/pjmedia/transport.h index 4b2450bc..1e7d67db 100644 --- a/pjmedia/include/pjmedia/transport.h +++ b/pjmedia/include/pjmedia/transport.h @@ -178,6 +178,11 @@ typedef struct pjmedia_transport pjmedia_transport; */ struct pjmedia_transport_op { + /** + * Get media socket info from the specified transport. + * + * Application should call #pjmedia_transport_get_info() instead + */ pj_status_t (*get_info)(pjmedia_transport *tp, pjmedia_sock_info *info); @@ -254,6 +259,20 @@ struct pjmedia_transport_op typedef struct pjmedia_transport_op pjmedia_transport_op; +/** + * Media transport type. + */ +typedef enum pjmedia_transport_type +{ + /** Media transport using standard UDP */ + PJMEDIA_TRANSPORT_TYPE_UDP, + + /** Media transport using ICE */ + PJMEDIA_TRANSPORT_TYPE_ICE + +} pjmedia_transport_type; + + /** * This structure declares stream transport. A stream transport is called * by the stream to transmit a packet, and will notify stream when @@ -262,13 +281,27 @@ typedef struct pjmedia_transport_op pjmedia_transport_op; struct pjmedia_transport { /** Transport name (for logging purpose). */ - char name[PJ_MAX_OBJ_NAME]; + char name[PJ_MAX_OBJ_NAME]; + + /** Transport type. */ + pjmedia_transport_type type; /** Transport's "virtual" function table. */ - pjmedia_transport_op *op; + pjmedia_transport_op *op; }; +/** + * Get media socket info from the specified transport. The socket info + * contains information about the local address of this transport, and + * would be needed for example to fill in the "c=" and "m=" line of local + * SDP. + * + * @param tp The transport. + * @param info Media socket info to be initialized. + * + * @return PJ_SUCCESS on success. + */ PJ_INLINE(pj_status_t) pjmedia_transport_get_info(pjmedia_transport *tp, pjmedia_sock_info *info) { diff --git a/pjmedia/include/pjmedia/transport_ice.h b/pjmedia/include/pjmedia/transport_ice.h index fa4deebb..e676730a 100644 --- a/pjmedia/include/pjmedia/transport_ice.h +++ b/pjmedia/include/pjmedia/transport_ice.h @@ -41,21 +41,21 @@ PJ_BEGIN_DECL PJ_DECL(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt, const char *name, + unsigned comp_cnt, pj_stun_config *stun_cfg, - pj_dns_resolver *resolver, - pj_bool_t enable_relay, - const pj_str_t *stun_name, pjmedia_transport **p_tp); PJ_DECL(pj_status_t) pjmedia_ice_destroy(pjmedia_transport *tp); +PJ_DECL(pj_ice_st*) pjmedia_ice_get_ice_st(pjmedia_transport *tp); + PJ_DECL(pj_status_t) pjmedia_ice_init_ice(pjmedia_transport *tp, pj_ice_role role, const pj_str_t *local_ufrag, const pj_str_t *local_passwd); -PJ_DECL(pj_status_t) pjmedia_ice_build_sdp(pjmedia_transport *tp, - pj_pool_t *pool, - pjmedia_sdp_session *sdp); +PJ_DECL(pj_status_t) pjmedia_ice_modify_sdp(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp); PJ_DECL(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, pj_pool_t *pool, pjmedia_sdp_session *rem_sdp); diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c index 98e89fbc..2023a1dc 100644 --- a/pjmedia/src/pjmedia/transport_ice.c +++ b/pjmedia/src/pjmedia/transport_ice.c @@ -25,7 +25,7 @@ struct transport_ice pjmedia_transport base; pj_ice_st *ice_st; - void *user_data; + void *stream; void (*rtp_cb)(void*, const void*, pj_ssize_t); @@ -41,7 +41,7 @@ struct transport_ice static pj_status_t tp_get_info(pjmedia_transport *tp, pjmedia_sock_info *info); static pj_status_t tp_attach( pjmedia_transport *tp, - void *user_data, + void *stream, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, @@ -92,15 +92,14 @@ static pjmedia_transport_op tp_ice_op = PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt, const char *name, + unsigned comp_cnt, pj_stun_config *stun_cfg, - pj_dns_resolver *resolver, - pj_bool_t enable_relay, - const pj_str_t *stun_name, - pjmedia_transport **p_tp) + pjmedia_transport **p_tp) { pj_ice_st *ice_st; pj_ice_st_cb ice_st_cb; struct transport_ice *tp_ice; + unsigned i; pj_status_t status; PJ_UNUSED_ARG(endpt); @@ -117,24 +116,11 @@ PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt, if (status != PJ_SUCCESS) return status; - /* Add component 1 (RTP) */ - status = pj_ice_st_add_comp(ice_st, 1); - if (status != PJ_SUCCESS) - goto on_error; - - /* Add host candidates. */ - status = pj_ice_st_add_all_host_interfaces(ice_st, 1, 0, PJ_FALSE, NULL); - if (status != PJ_SUCCESS) - goto on_error; - - /* Configure STUN server for ICE */ - if (stun_name) { - status = pj_ice_st_set_stun(ice_st, resolver, enable_relay, stun_name); + /* Add components */ + for (i=0; i<comp_cnt; ++i) { + status = pj_ice_st_add_comp(ice_st, i+1); if (status != PJ_SUCCESS) goto on_error; - - /* Add STUN candidates */ - status = pj_ice_st_add_stun_interface(ice_st, 1, 0, PJ_FALSE, NULL); } /* Create transport instance and attach to ICE */ @@ -142,6 +128,7 @@ PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt, tp_ice->ice_st = ice_st; pj_ansi_strcpy(tp_ice->base.name, ice_st->obj_name); tp_ice->base.op = &tp_ice_op; + tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE; ice_st->user_data = (void*)tp_ice; @@ -157,19 +144,28 @@ on_error: } -PJ_DEF(pj_status_t) pjmedia_ice_close(pjmedia_transport *tp) +PJ_DEF(pj_status_t) pjmedia_ice_destroy(pjmedia_transport *tp) { struct transport_ice *tp_ice = (struct transport_ice*)tp; if (tp_ice->ice_st) { pj_ice_st_destroy(tp_ice->ice_st); - tp_ice->ice_st = NULL; + //Must not touch tp_ice after ice_st is destroyed! + //(it has the pool) + //tp_ice->ice_st = NULL; } return PJ_SUCCESS; } +PJ_DECL(pj_ice_st*) pjmedia_ice_get_ice_st(pjmedia_transport *tp) +{ + struct transport_ice *tp_ice = (struct transport_ice*)tp; + return tp_ice->ice_st; +} + + PJ_DEF(pj_status_t) pjmedia_ice_init_ice(pjmedia_transport *tp, pj_ice_role role, const pj_str_t *local_ufrag, @@ -180,9 +176,9 @@ PJ_DEF(pj_status_t) pjmedia_ice_init_ice(pjmedia_transport *tp, } -PJ_DEF(pj_status_t) pjmedia_ice_build_sdp(pjmedia_transport *tp, - pj_pool_t *pool, - pjmedia_sdp_session *sdp) +PJ_DEF(pj_status_t) pjmedia_ice_modify_sdp(pjmedia_transport *tp, + pj_pool_t *pool, + pjmedia_sdp_session *sdp) { struct transport_ice *tp_ice = (struct transport_ice*)tp; enum { MAXLEN = 256 }; @@ -202,7 +198,7 @@ PJ_DEF(pj_status_t) pjmedia_ice_build_sdp(pjmedia_transport *tp, &tp_ice->ice_st->ice->rx_pass); sdp->attr[sdp->attr_count++] = attr; - /* Add all candidates */ + /* Add all candidates (to media level) */ cand_cnt = pj_ice_get_cand_cnt(tp_ice->ice_st->ice); for (i=0; i<cand_cnt; ++i) { pj_ice_cand *cand; @@ -255,7 +251,7 @@ PJ_DEF(pj_status_t) pjmedia_ice_build_sdp(pjmedia_transport *tp, value = pj_str(buffer); attr = pjmedia_sdp_attr_create(pool, "candidate", &value); - sdp->attr[sdp->attr_count++] = attr; + sdp->media[0]->attr[sdp->media[0]->attr_count++] = attr; } /* Done */ @@ -377,10 +373,10 @@ PJ_DEF(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp, /* Get all candidates */ cand_cnt = 0; - for (i=0; i<rem_sdp->attr_count; ++i) { + for (i=0; i<rem_sdp->media[0]->attr_count; ++i) { pjmedia_sdp_attr *attr; - attr = rem_sdp->attr[i]; + attr = rem_sdp->media[0]->attr[i]; if (pj_strcmp2(&attr->name, "candidate")!=0) continue; @@ -441,7 +437,7 @@ static pj_status_t tp_get_info(pjmedia_transport *tp, static pj_status_t tp_attach( pjmedia_transport *tp, - void *user_data, + void *stream, const pj_sockaddr_t *rem_addr, const pj_sockaddr_t *rem_rtcp, unsigned addr_len, @@ -454,7 +450,7 @@ static pj_status_t tp_attach( pjmedia_transport *tp, { struct transport_ice *tp_ice = (struct transport_ice*)tp; - tp_ice->user_data = user_data; + tp_ice->stream = stream; tp_ice->rtp_cb = rtp_cb; tp_ice->rtcp_cb = rtcp_cb; @@ -473,7 +469,7 @@ static void tp_detach(pjmedia_transport *tp, tp_ice->rtp_cb = NULL; tp_ice->rtcp_cb = NULL; - tp_ice->user_data = NULL; + tp_ice->stream = NULL; PJ_UNUSED_ARG(strm); } @@ -514,9 +510,9 @@ static void ice_on_rx_data(pj_ice_st *ice_st, struct transport_ice *tp_ice = (struct transport_ice*) ice_st->user_data; if (comp_id==1 && tp_ice->rtp_cb) - (*tp_ice->rtp_cb)(tp_ice->user_data, pkt, size); + (*tp_ice->rtp_cb)(tp_ice->stream, pkt, size); else if (comp_id==2 && tp_ice->rtcp_cb) - (*tp_ice->rtcp_cb)(tp_ice->user_data, pkt, size); + (*tp_ice->rtcp_cb)(tp_ice->stream, pkt, size); PJ_UNUSED_ARG(cand_id); PJ_UNUSED_ARG(src_addr); diff --git a/pjmedia/src/pjmedia/transport_udp.c b/pjmedia/src/pjmedia/transport_udp.c index 3325be0e..2bf570e5 100644 --- a/pjmedia/src/pjmedia/transport_udp.c +++ b/pjmedia/src/pjmedia/transport_udp.c @@ -239,6 +239,7 @@ PJ_DEF(pj_status_t) pjmedia_transport_udp_attach( pjmedia_endpt *endpt, tp->options = options; pj_ansi_strcpy(tp->base.name, name); tp->base.op = &transport_udp_op; + tp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP; /* Copy socket infos */ tp->rtp_sock = si->rtp_sock; diff --git a/pjnath/include/pjnath/ice_stream_transport.h b/pjnath/include/pjnath/ice_stream_transport.h index c85aff98..37ff5686 100644 --- a/pjnath/include/pjnath/ice_stream_transport.h +++ b/pjnath/include/pjnath/ice_stream_transport.h @@ -71,6 +71,7 @@ typedef struct pj_ice_st_interface { pj_ice_st *ice_st; pj_ice_cand_type type; + pj_status_t status; unsigned comp_id; int cand_id; pj_str_t foundation; @@ -150,7 +151,7 @@ PJ_DECL(pj_status_t) pj_ice_st_add_relay_interface(pj_ice_st *ice_st, unsigned local_port, pj_bool_t notify, void *notify_data); - +PJ_DECL(pj_status_t) pj_ice_st_get_interfaces_status(pj_ice_st *ice_st); PJ_DECL(pj_status_t) pj_ice_st_init_ice(pj_ice_st *ice_st, pj_ice_role role, diff --git a/pjnath/src/pjnath/ice.c b/pjnath/src/pjnath/ice.c index e21e81f9..d2d2474d 100644 --- a/pjnath/src/pjnath/ice.c +++ b/pjnath/src/pjnath/ice.c @@ -888,12 +888,15 @@ static void prune_checklist(pj_ice *ice, pj_ice_checklist *clist) static void on_ice_complete(pj_ice *ice, pj_status_t status) { if (!ice->is_complete) { + char errmsg[PJ_ERR_MSG_SIZE]; ice->is_complete = PJ_TRUE; ice->ice_status = status; /* Log message */ - LOG((ice->obj_name, "ICE process complete")); + LOG((ice->obj_name, "ICE process complete, status=%s", + pj_strerror(status, errmsg, sizeof(errmsg)).ptr)); + dump_checklist("Dumping checklist", ice, &ice->clist); dump_valid_list("Dumping valid list", ice); @@ -1136,7 +1139,7 @@ PJ_DEF(pj_status_t) pj_ice_create_check_list(pj_ice *ice, /* Log checklist */ dump_checklist("Checklist created:", ice, clist); - pj_mutex_lock(ice->mutex); + pj_mutex_unlock(ice->mutex); return PJ_SUCCESS; } diff --git a/pjnath/src/pjnath/ice_stream_transport.c b/pjnath/src/pjnath/ice_stream_transport.c index 475aa35d..e654fcf7 100644 --- a/pjnath/src/pjnath/ice_stream_transport.c +++ b/pjnath/src/pjnath/ice_stream_transport.c @@ -151,7 +151,6 @@ on_error: return status; } - /* * This is callback called by ioqueue on incoming packet */ @@ -191,7 +190,6 @@ static void on_read_complete(pj_ioqueue_key_t *key, } } - /* * Destroy an interface */ @@ -207,7 +205,6 @@ static void destroy_ice_interface(pj_ice_st_interface *is) } } - /* * Create ICE stream transport */ @@ -241,7 +238,6 @@ PJ_DECL(pj_status_t) pj_ice_st_create(pj_stun_config *stun_cfg, return PJ_SUCCESS; } - static void destroy_ice_st(pj_ice_st *ice_st, pj_status_t reason) { unsigned i; @@ -273,7 +269,6 @@ static void destroy_ice_st(pj_ice_st *ice_st, pj_status_t reason) } } - /* * Destroy ICE stream transport. */ @@ -283,7 +278,6 @@ PJ_DEF(pj_status_t) pj_ice_st_destroy(pj_ice_st *ice_st) return PJ_SUCCESS; } - /* * Resolve STUN server */ @@ -300,7 +294,6 @@ PJ_DEF(pj_status_t) pj_ice_st_set_stun( pj_ice_st *ice_st, return -1; } - /* * Set STUN server address. */ @@ -319,7 +312,6 @@ PJ_DEF(pj_status_t) pj_ice_st_set_stun_addr( pj_ice_st *ice_st, return PJ_SUCCESS; } - /* * Add new component. */ @@ -350,7 +342,6 @@ PJ_DEF(pj_status_t) pj_ice_st_add_comp(pj_ice_st *ice_st, return PJ_SUCCESS; } - /* Add interface */ static void add_interface(pj_ice_st *ice_st, pj_ice_st_interface *is, unsigned *p_itf_id, pj_bool_t notify, @@ -405,10 +396,12 @@ PJ_DEF(pj_status_t) pj_ice_st_add_host_interface(pj_ice_st *ice_st, /* Store this interface */ add_interface(ice_st, is, p_itf_id, notify, notify_data); + /* Set interface status to SUCCESS */ + is->status = PJ_SUCCESS; + return PJ_SUCCESS; } - /* * Enumerate and add all host interfaces. */ @@ -434,7 +427,6 @@ PJ_DEF(pj_status_t) pj_ice_st_add_all_host_interfaces(pj_ice_st *ice_st, NULL, notify, notify_data); } - /* * Add STUN mapping interface. */ @@ -453,7 +445,6 @@ PJ_DEF(pj_status_t) pj_ice_st_add_stun_interface(pj_ice_st *ice_st, return -1; } - /* * Add TURN mapping interface. */ @@ -472,6 +463,25 @@ PJ_DEF(pj_status_t) pj_ice_st_add_relay_interface(pj_ice_st *ice_st, return -1; } +PJ_DEF(pj_status_t) pj_ice_st_get_interfaces_status(pj_ice_st *ice_st) +{ + unsigned i; + pj_status_t worst = PJ_SUCCESS; + + for (i=0; i<ice_st->itf_cnt; ++i) { + pj_ice_st_interface *itf = ice_st->itfs[i]; + + if (itf->status == PJ_SUCCESS) { + /* okay */ + } else if (itf->status == PJ_EPENDING && worst==PJ_SUCCESS) { + worst = itf->status; + } else { + worst = itf->status; + } + } + + return worst; +} /* * Create ICE! @@ -537,7 +547,6 @@ on_error: return status; } - /* * Enum candidates */ @@ -568,7 +577,6 @@ PJ_DEF(pj_status_t) pj_ice_st_enum_cands(pj_ice_st *ice_st, return PJ_SUCCESS; } - /* * Start ICE processing ! */ @@ -588,7 +596,6 @@ PJ_DEF(pj_status_t) pj_ice_st_start_ice( pj_ice_st *ice_st, return pj_ice_start_check(ice_st->ice); } - /* * Stop ICE! */ @@ -602,7 +609,6 @@ PJ_DECL(pj_status_t) pj_ice_st_stop_ice(pj_ice_st *ice_st) return PJ_SUCCESS; } - /* * Send data to peer agent. */ @@ -617,8 +623,6 @@ PJ_DEF(pj_status_t) pj_ice_st_send_data( pj_ice_st *ice_st, return pj_ice_send_data(ice_st->ice, comp_id, data, data_len); } - - /* * Callback called by ICE session when ICE processing is complete, either * successfully or with failure. @@ -631,7 +635,6 @@ static void on_ice_complete(pj_ice *ice, pj_status_t status) } } - /* * Callback called by ICE session when it wants to send outgoing packet. */ @@ -667,7 +670,6 @@ static pj_status_t on_tx_pkt(pj_ice *ice, return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; } - /* * Callback called by ICE session when it receives application data. */ diff --git a/pjsip-apps/build/pjsua.dsp b/pjsip-apps/build/pjsua.dsp index 89392633..5ab3d3d0 100644 --- a/pjsip-apps/build/pjsua.dsp +++ b/pjsip-apps/build/pjsua.dsp @@ -42,7 +42,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
+# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /I "../../pjnath/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
@@ -68,7 +68,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /I "../../pjnath/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index 50efe7fb..ed4017dd 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -144,8 +144,7 @@ static void usage(void) puts (" This option can be specified multiple times."); puts (" --outbound=url Set the URL of global outbound proxy server"); puts (" May be specified multiple times"); - puts (" --use-stun1=FORMAT where FORMAT=host[:port]"); - puts (" --use-stun2=FORMAT Resolve local IP with the specified STUN servers"); + puts (" --stun-srv=name Set STUN server host or domain"); puts (""); puts ("TLS Options:"); puts (" --use-tls Enable TLS transport (default=no)"); @@ -159,6 +158,7 @@ static void usage(void) puts (""); puts ("Media Options:"); + puts (" --use-ice Enable ICE (default:no)"); puts (" --add-codec=name Manually add codec (default is to enable all)"); puts (" --clock-rate=N Override sound device clock rate"); puts (" --null-audio Use NULL audio device"); @@ -325,10 +325,10 @@ static pj_status_t parse_args(int argc, char *argv[], OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT, OPT_REALM, OPT_USERNAME, OPT_PASSWORD, - OPT_NAMESERVER, OPT_USE_STUN1, OPT_USE_STUN2, + OPT_NAMESERVER, OPT_STUN_SRV, OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE, OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP, - OPT_AUTO_CONF, OPT_CLOCK_RATE, + OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_USE_ICE, OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC, OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC, OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD, @@ -366,8 +366,7 @@ static pj_status_t parse_args(int argc, char *argv[], { "username", 1, 0, OPT_USERNAME}, { "password", 1, 0, OPT_PASSWORD}, { "nameserver", 1, 0, OPT_NAMESERVER}, - { "use-stun1", 1, 0, OPT_USE_STUN1}, - { "use-stun2", 1, 0, OPT_USE_STUN2}, + { "stun-srv", 1, 0, OPT_STUN_SRV}, { "add-buddy", 1, 0, OPT_ADD_BUDDY}, { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG}, { "no-presence", 0, 0, OPT_NO_PRESENCE}, @@ -381,6 +380,7 @@ static pj_status_t parse_args(int argc, char *argv[], { "play-tone", 1, 0, OPT_PLAY_TONE}, { "rec-file", 1, 0, OPT_REC_FILE}, { "rtp-port", 1, 0, OPT_RTP_PORT}, + { "use-ice", 0, 0, OPT_USE_ICE}, { "add-codec", 1, 0, OPT_ADD_CODEC}, { "complexity", 1, 0, OPT_COMPLEXITY}, { "quality", 1, 0, OPT_QUALITY}, @@ -441,7 +441,6 @@ static pj_status_t parse_args(int argc, char *argv[], */ pj_optind = 0; while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) { - char *p; pj_str_t tmp; long lval; @@ -632,41 +631,8 @@ static pj_status_t parse_args(int argc, char *argv[], } break; - case OPT_USE_STUN1: /* STUN server 1 */ - p = pj_ansi_strchr(pj_optarg, ':'); - if (p) { - *p = '\0'; - cfg->udp_cfg.stun_config.stun_srv1 = pj_str(pj_optarg); - cfg->udp_cfg.stun_config.stun_port1 = pj_strtoul(pj_cstr(&tmp, p+1)); - if (cfg->udp_cfg.stun_config.stun_port1 < 1 || cfg->udp_cfg.stun_config.stun_port1 > 65535) { - PJ_LOG(1,(THIS_FILE, - "Error: expecting port number with " - "option --use-stun1")); - return PJ_EINVAL; - } - } else { - cfg->udp_cfg.stun_config.stun_port1 = 3478; - cfg->udp_cfg.stun_config.stun_srv1 = pj_str(pj_optarg); - } - cfg->udp_cfg.use_stun = PJ_TRUE; - break; - - case OPT_USE_STUN2: /* STUN server 2 */ - p = pj_ansi_strchr(pj_optarg, ':'); - if (p) { - *p = '\0'; - cfg->udp_cfg.stun_config.stun_srv2 = pj_str(pj_optarg); - cfg->udp_cfg.stun_config.stun_port2 = pj_strtoul(pj_cstr(&tmp,p+1)); - if (cfg->udp_cfg.stun_config.stun_port2 < 1 || cfg->udp_cfg.stun_config.stun_port2 > 65535) { - PJ_LOG(1,(THIS_FILE, - "Error: expecting port number with " - "option --use-stun2")); - return PJ_EINVAL; - } - } else { - cfg->udp_cfg.stun_config.stun_port2 = 3478; - cfg->udp_cfg.stun_config.stun_srv2 = pj_str(pj_optarg); - } + case OPT_STUN_SRV: /* STUN server */ + cfg->cfg.stun_srv = pj_str(pj_optarg); break; case OPT_ADD_BUDDY: /* Add to buddy list. */ @@ -728,6 +694,10 @@ static pj_status_t parse_args(int argc, char *argv[], cfg->rec_file = pj_str(pj_optarg); break; + case OPT_USE_ICE: + cfg->media_cfg.enable_ice = PJ_TRUE; + break; + case OPT_RTP_PORT: cfg->rtp_cfg.port = my_atoi(pj_optarg); if (cfg->rtp_cfg.port < 1 || cfg->rtp_cfg.port > 65535) { @@ -1113,21 +1083,13 @@ static int write_settings(const struct app_config *config, } /* STUN */ - if (config->udp_cfg.stun_config.stun_port1) { - pj_ansi_sprintf(line, "--use-stun1 %.*s:%d\n", - (int)config->udp_cfg.stun_config.stun_srv1.slen, - config->udp_cfg.stun_config.stun_srv1.ptr, - config->udp_cfg.stun_config.stun_port1); + if (config->cfg.stun_srv.slen) { + pj_ansi_sprintf(line, "--stun-srv %.*s\n", + (int)config->cfg.stun_srv.slen, + config->cfg.stun_srv.ptr); pj_strcat2(&cfg, line); } - if (config->udp_cfg.stun_config.stun_port2) { - pj_ansi_sprintf(line, "--use-stun2 %.*s:%d\n", - (int)config->udp_cfg.stun_config.stun_srv2.slen, - config->udp_cfg.stun_config.stun_srv2.ptr, - config->udp_cfg.stun_config.stun_port2); - pj_strcat2(&cfg, line); - } /* TLS */ if (config->use_tls) @@ -1174,6 +1136,8 @@ static int write_settings(const struct app_config *config, /* Media */ + if (config->media_cfg.enable_ice) + pj_strcat2(&cfg, "--use-ice\n"); if (config->null_audio) pj_strcat2(&cfg, "--null-audio\n"); if (config->auto_play) @@ -2962,11 +2926,6 @@ pj_status_t app_init(int argc, char *argv[]) if (status != PJ_SUCCESS) return status; - /* Copy udp_cfg STUN config to rtp_cfg */ - app_config.rtp_cfg.use_stun = app_config.udp_cfg.use_stun; - app_config.rtp_cfg.stun_config = app_config.udp_cfg.stun_config; - - /* Initialize application callbacks */ app_config.cfg.cb.on_call_state = &on_call_state; app_config.cfg.cb.on_call_media_state = &on_call_media_state; diff --git a/pjsip/build/pjsua_lib.dsp b/pjsip/build/pjsua_lib.dsp index b15a7f43..da7c163f 100644 --- a/pjsip/build/pjsua_lib.dsp +++ b/pjsip/build/pjsua_lib.dsp @@ -41,7 +41,7 @@ RSC=rc.exe # PROP Intermediate_Dir ".\output\pjsua-lib-i386-win32-vc6-release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
-# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../include" /I "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /c
+# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../include" /I "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /I "../../pjnath/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
@@ -65,7 +65,7 @@ LIB32=link.exe -lib # PROP Intermediate_Dir ".\output\pjsua-lib-i386-win32-vc6-debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /I "../../pjnath/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
diff --git a/pjsip/include/pjsip/sip_endpoint.h b/pjsip/include/pjsip/sip_endpoint.h index 08c67931..f285ef8f 100644 --- a/pjsip/include/pjsip/sip_endpoint.h +++ b/pjsip/include/pjsip/sip_endpoint.h @@ -156,6 +156,15 @@ PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, pj_timer_entry *entry ); +/** + * Get the timer heap instance of the SIP endpoint. + * + * @param endpt The endpoint. + * + * @return The timer heap instance. + */ +PJ_DECL(pj_timer_heap_t*) pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt); + /** * Register new module to the endpoint. diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 55cf6264..fcd0549a 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -902,6 +902,14 @@ typedef struct pjsua_config */ pj_str_t outbound_proxy[4]; + /** + * Specify STUN server. This server will be first resolved with DNS SRV + * to get the actual server address. If DNS SRV resolution failed, or + * when nameserver is not configured, the server will be resolved using + * DNS A resolution (i.e. gethostbyname()). + */ + pj_str_t stun_srv; + /** * Number of credentials in the credential array. */ @@ -1001,6 +1009,7 @@ PJ_INLINE(void) pjsua_config_dup(pj_pool_t *pool, } pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent); + pj_strdup_with_null(pool, &dst->stun_srv, &src->stun_srv); } @@ -1334,60 +1343,6 @@ typedef int pjsua_transport_id; /** - * This structure describes STUN configuration for SIP and media transport, - * and is embedded inside pjsua_transport_config structure. - */ -typedef struct pjsua_stun_config -{ - /** - * The first STUN server IP address or hostname. - */ - pj_str_t stun_srv1; - - /** - * Port number of the first STUN server. - * If zero, default STUN port will be used. - */ - unsigned stun_port1; - - /** - * Optional second STUN server IP address or hostname, for which the - * result of the mapping request will be compared to. If the value - * is empty, only one STUN server will be used. - */ - pj_str_t stun_srv2; - - /** - * Port number of the second STUN server. - * If zero, default STUN port will be used. - */ - unsigned stun_port2; - -} pjsua_stun_config; - - - -/** - * Call this function to initialize STUN config with default values. - * STUN config is normally embedded inside pjsua_transport_config, so - * normally there is no need to call this function and rather just - * call pjsua_transport_config_default() instead. - * - * @param cfg The STUN config to be initialized. - * - * \par Python: - * The corresponding Python function creates and initialize the config: - * \code - stun_cfg = py_pjsua.stun_config_default() - * \endcode - */ -PJ_INLINE(void) pjsua_stun_config_default(pjsua_stun_config *cfg) -{ - pj_bzero(cfg, sizeof(*cfg)); -} - - -/** * Transport configuration for creating transports for both SIP * and media. Before setting some values to this structure, application * MUST call #pjsua_transport_config_default() to initialize its @@ -1435,16 +1390,6 @@ typedef struct pjsua_transport_config pj_str_t bound_addr; /** - * Flag to indicate whether STUN should be used. - */ - pj_bool_t use_stun; - - /** - * STUN configuration, must be specified when STUN is used. - */ - pjsua_stun_config stun_config; - - /** * This specifies TLS settings for TLS transport. It is only be used * when this transport config is being used to create a SIP TLS * transport. @@ -1468,46 +1413,11 @@ typedef struct pjsua_transport_config PJ_INLINE(void) pjsua_transport_config_default(pjsua_transport_config *cfg) { pj_bzero(cfg, sizeof(*cfg)); - pjsua_stun_config_default(&cfg->stun_config); pjsip_tls_setting_default(&cfg->tls_setting); } /** - * This is a utility function to normalize STUN config. It's only - * used internally by the library. - * - * @param cfg The STUN config to be initialized. - * - * \par Python: - * \code - py_pjsua.normalize_stun_config(cfg) - * \code - */ -PJ_INLINE(void) pjsua_normalize_stun_config( pjsua_stun_config *cfg ) -{ - if (cfg->stun_srv1.slen) { - - if (cfg->stun_port1 == 0) - cfg->stun_port1 = 3478; - - if (cfg->stun_srv2.slen == 0) { - cfg->stun_srv2 = cfg->stun_srv1; - cfg->stun_port2 = cfg->stun_port1; - } else { - if (cfg->stun_port2 == 0) - cfg->stun_port2 = 3478; - } - - } else { - cfg->stun_port1 = 0; - cfg->stun_srv2.slen = 0; - cfg->stun_port2 = 0; - } -} - - -/** * Duplicate transport config. * * @param pool The pool. @@ -1522,19 +1432,8 @@ PJ_INLINE(void) pjsua_transport_config_dup(pj_pool_t *pool, pjsua_transport_config *dst, const pjsua_transport_config *src) { + PJ_UNUSED_ARG(pool); pj_memcpy(dst, src, sizeof(*src)); - - if (src->stun_config.stun_srv1.slen) { - pj_strdup_with_null(pool, &dst->stun_config.stun_srv1, - &src->stun_config.stun_srv1); - } - - if (src->stun_config.stun_srv2.slen) { - pj_strdup_with_null(pool, &dst->stun_config.stun_srv2, - &src->stun_config.stun_srv2); - } - - pjsua_normalize_stun_config(&dst->stun_config); } @@ -3530,6 +3429,15 @@ struct pjsua_media_config */ int jb_max; + /** + * Enable ICE + */ + pj_bool_t enable_ice; + + /** + * Enable ICE media relay. + */ + pj_bool_t enable_relay; }; diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index 4f6a49fd..5365ff12 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -186,6 +186,12 @@ struct pjsua_data pj_bool_t thread_quit_flag; /**< Thread quit flag. */ pj_thread_t *thread[4]; /**< Array of threads. */ + /* STUN and resolver */ + pj_stun_config stun_cfg; /**< Global STUN settings. */ + pj_sockaddr stun_srv; /**< Resolved STUN server address */ + pj_status_t stun_status; /**< STUN server status. */ + pj_dns_resolver *resolver; /**< DNS resolver. */ + /* Account: */ unsigned acc_cnt; /**< Number of accounts. */ pjsua_acc_id default_acc; /**< Default account ID */ @@ -270,12 +276,30 @@ PJ_INLINE(pjsua_im_data*) pjsua_im_data_dup(pj_pool_t *pool, #endif +/** + * Resolve STUN server. + */ +pj_status_t pjsua_resolve_stun_server(pj_bool_t wait); /** * Handle incoming invite request. */ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata); +/* + * Media channel. + */ +pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, + pjsip_role_e role); +pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, + pj_pool_t *pool, + pjmedia_sdp_session **p_sdp); +pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, + pjmedia_sdp_session *local_sdp, + pjmedia_sdp_session *remote_sdp); +pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id); + + /** * Init presence. */ diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c index b587ac1a..1a509aab 100644 --- a/pjsip/src/pjsip/sip_endpoint.c +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -758,6 +758,14 @@ PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, } /* + * Get the timer heap instance of the SIP endpoint. + */ +PJ_DEF(pj_timer_heap_t*) pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt) +{ + return endpt->timer_heap; +} + +/* * This is the callback that is called by the transport manager when it * receives a message from the network. */ diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 68eba778..2441f8ed 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -60,8 +60,6 @@ static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_event *e); -/* Destroy the call's media */ -static pj_status_t call_destroy_media(int call_id); /* Create inactive SDP for call hold. */ static pj_status_t create_inactive_sdp(pjsua_call *call, @@ -302,10 +300,15 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id, return status; } - /* Get media capability from media endpoint: */ + /* Init media channel */ + status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error initializing media channel", status); + goto on_error; + } - status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, dlg->pool, 1, - &call->skinfo, &offer); + /* Create SDP offer */ + status = pjsua_media_channel_create_sdp(call->index, dlg->pool, &offer); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status); goto on_error; @@ -403,6 +406,7 @@ on_error: if (call_id != -1) { reset_call(call_id); + pjsua_media_channel_deinit(call_id); } PJSUA_UNLOCK(); @@ -526,13 +530,22 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) } + /* Init media channel */ + status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS); + if (status != PJ_SUCCESS) { + pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, + NULL, NULL); + PJSUA_UNLOCK(); + return PJ_TRUE; + } + + /* Get media capability from media endpoint: */ - status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, - rdata->tp_info.pool, 1, - &call->skinfo, &answer ); + status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool, &answer); if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, NULL, NULL); + pjsua_media_channel_deinit(call->index); PJSUA_UNLOCK(); return PJ_TRUE; } @@ -561,6 +574,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) NULL, NULL); } + pjsua_media_channel_deinit(call->index); PJSUA_UNLOCK(); return PJ_TRUE; } @@ -580,6 +594,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, NULL, NULL); + pjsua_media_channel_deinit(call->index); PJSUA_UNLOCK(); return PJ_TRUE; } @@ -590,6 +605,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) if (status != PJ_SUCCESS) { pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL, NULL, NULL); + pjsua_media_channel_deinit(call->index); PJSUA_UNLOCK(); return PJ_TRUE; } @@ -618,6 +634,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) /* Can't terminate dialog because transaction is in progress. pjsip_dlg_terminate(dlg); */ + pjsua_media_channel_deinit(call->index); PJSUA_UNLOCK(); return PJ_TRUE; } @@ -649,13 +666,15 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL); pjsip_inv_terminate(inv, 500, PJ_FALSE); + pjsua_media_channel_deinit(call->index); PJSUA_UNLOCK(); return PJ_TRUE; } else { status = pjsip_inv_send_msg(inv, response); - if (status != PJ_SUCCESS) + if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send 100 response", status); + } } ++pjsua_var.call_cnt; @@ -1191,8 +1210,7 @@ PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id, /* Create SDP */ PJ_UNUSED_ARG(unhold); PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg); - status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool, - 1, &call->skinfo, &sdp); + status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &sdp); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", status); @@ -1885,34 +1903,6 @@ PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id, /* - * Destroy the call's media - */ -static pj_status_t call_destroy_media(int call_id) -{ - pjsua_call *call = &pjsua_var.calls[call_id]; - - if (call->conf_slot != PJSUA_INVALID_ID) { - pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot); - call->conf_slot = PJSUA_INVALID_ID; - } - - if (call->session) { - /* Destroy session (this will also close RTP/RTCP sockets). */ - pjmedia_session_destroy(call->session); - call->session = NULL; - - PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed", - call_id)); - - } - - call->media_st = PJSUA_CALL_MEDIA_NONE; - - return PJ_SUCCESS; -} - - -/* * This callback receives notification from invite session when the * session state has changed. */ @@ -2034,7 +2024,7 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv, pj_assert(call != NULL); if (call) - call_destroy_media(call->index); + pjsua_media_channel_deinit(call->index); /* Free call */ call->inv = NULL; @@ -2077,22 +2067,6 @@ static void call_disconnect( pjsip_inv_session *inv, } /* - * DTMF callback from the stream. - */ -static void dtmf_callback(pjmedia_stream *strm, void *user_data, - int digit) -{ - PJ_UNUSED_ARG(strm); - - if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { - pjsua_call_id call_id; - - call_id = (pjsua_call_id)user_data; - pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit); - } -} - -/* * Callback to be called when SDP offer/answer negotiation has just completed * in the session. This function will start/update media if negotiation * has succeeded. @@ -2100,14 +2074,9 @@ static void dtmf_callback(pjmedia_stream *strm, void *user_data, static void pjsua_call_on_media_update(pjsip_inv_session *inv, pj_status_t status) { - int prev_media_st = 0; pjsua_call *call; - pjmedia_session_info sess_info; - const pjmedia_sdp_session *local_sdp; - const pjmedia_sdp_session *remote_sdp; - pjmedia_port *media_port; - pj_str_t port_name; - char tmp[PJSIP_MAX_URL_SIZE]; + pjmedia_sdp_session *local_sdp; + pjmedia_sdp_session *remote_sdp; PJSUA_LOCK(); @@ -2118,7 +2087,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv, pjsua_perror(THIS_FILE, "SDP negotiation has failed", status); /* Stop/destroy media, if any */ - call_destroy_media(call->index); + pjsua_media_channel_deinit(call->index); /* Disconnect call if we're not in the middle of initializing an * UAS dialog and if this is not a re-INVITE @@ -2133,15 +2102,8 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv, return; } - /* Destroy existing media session, if any. */ - - if (call) { - prev_media_st = call->media_st; - call_destroy_media(call->index); - } /* Get local and remote SDP */ - status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, @@ -2163,155 +2125,16 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv, return; } - /* Create media session info based on SDP parameters. - * We only support one stream per session at the moment - */ - status = pjmedia_session_info_from_sdp( call->inv->dlg->pool, - pjsua_var.med_endpt, - 1,&sess_info, - local_sdp, remote_sdp); + status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create media session", status); call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); + pjsua_media_channel_deinit(call->index); PJSUA_UNLOCK(); return; } - /* Check if media is put on-hold */ - if (sess_info.stream_cnt == 0 || - sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE) - { - - /* Determine who puts the call on-hold */ - if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) { - if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) { - /* It was local who offer hold */ - call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD; - } else { - call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD; - } - } - - call->media_dir = PJMEDIA_DIR_NONE; - - } else { - - /* Override ptime, if this option is specified. */ - if (pjsua_var.media_cfg.ptime != 0) { - sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t) - (pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime); - if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0) - sess_info.stream_info[0].param->setting.frm_per_pkt = 1; - } - - /* Disable VAD, if this option is specified. */ - if (pjsua_var.media_cfg.no_vad) { - sess_info.stream_info[0].param->setting.vad = 0; - } - - - /* Optionally, application may modify other stream settings here - * (such as jitter buffer parameters, codec ptime, etc.) - */ - sess_info.stream_info[0].jb_init = pjsua_var.media_cfg.jb_init; - sess_info.stream_info[0].jb_min_pre = pjsua_var.media_cfg.jb_min_pre; - sess_info.stream_info[0].jb_max_pre = pjsua_var.media_cfg.jb_max_pre; - sess_info.stream_info[0].jb_max = pjsua_var.media_cfg.jb_max; - - /* Create session based on session info. */ - status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info, - &call->med_tp, - call, &call->session ); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create media session", - status); - //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); - PJSUA_UNLOCK(); - return; - } - - /* If DTMF callback is installed by application, install our - * callback to the session. - */ - if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { - pjmedia_session_set_dtmf_callback(call->session, 0, - &dtmf_callback, - (void*)(call->index)); - } - - /* Get the port interface of the first stream in the session. - * We need the port interface to add to the conference bridge. - */ - pjmedia_session_get_port(call->session, 0, &media_port); - - - /* - * Add the call to conference bridge. - */ - port_name.ptr = tmp; - port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, - call->inv->dlg->remote.info->uri, - tmp, sizeof(tmp)); - if (port_name.slen < 1) { - port_name = pj_str("call"); - } - status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool, - media_port, - &port_name, - (unsigned*)&call->conf_slot); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create conference slot", - status); - call_destroy_media(call->index); - //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR); - PJSUA_UNLOCK(); - return; - } - - /* Call's media state is active */ - call->media_st = PJSUA_CALL_MEDIA_ACTIVE; - call->media_dir = sess_info.stream_info[0].dir; - } - - /* Print info. */ - { - char info[80]; - int info_len = 0; - unsigned i; - - for (i=0; i<sess_info.stream_cnt; ++i) { - int len; - const char *dir; - pjmedia_stream_info *strm_info = &sess_info.stream_info[i]; - - switch (strm_info->dir) { - case PJMEDIA_DIR_NONE: - dir = "inactive"; - break; - case PJMEDIA_DIR_ENCODING: - dir = "sendonly"; - break; - case PJMEDIA_DIR_DECODING: - dir = "recvonly"; - break; - case PJMEDIA_DIR_ENCODING_DECODING: - dir = "sendrecv"; - break; - default: - dir = "unknown"; - break; - } - len = pj_ansi_sprintf( info+info_len, - ", stream #%d: %.*s (%s)", i, - (int)strm_info->fmt.encoding_name.slen, - strm_info->fmt.encoding_name.ptr, - dir); - if (len > 0) - info_len += len; - } - PJ_LOG(4,(THIS_FILE,"Media updates%s", info)); - } /* Call application callback, if any */ if (pjsua_var.ua_cfg.cb.on_call_media_state) @@ -2413,9 +2236,7 @@ static void pjsua_call_on_rx_offer(pjsip_inv_session *inv, } else { PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer", call->index)); - status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, - call->inv->pool, 1, - &call->skinfo, &answer); + status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &answer); } if (status != PJ_SUCCESS) { diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 46b8e0e5..116a51f5 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -42,11 +42,15 @@ static void init_data() { unsigned i; + pj_bzero(&pjsua_var, sizeof(pjsua_var)); + for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) pjsua_var.acc[i].index = i; for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i) pjsua_var.tpdata[i].index = i; + + pjsua_var.stun_status = PJ_EUNKNOWN; } @@ -466,15 +470,16 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, */ if (ua_cfg->nameserver_count) { #if PJSIP_HAS_RESOLVER - pj_dns_resolver *resv; unsigned i; /* Create DNS resolver */ - status = pjsip_endpt_create_resolver(pjsua_var.endpt, &resv); + status = pjsip_endpt_create_resolver(pjsua_var.endpt, + &pjsua_var.resolver); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Configure nameserver for the DNS resolver */ - status = pj_dns_resolver_set_ns(resv, ua_cfg->nameserver_count, + status = pj_dns_resolver_set_ns(pjsua_var.resolver, + ua_cfg->nameserver_count, ua_cfg->nameserver, NULL); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error setting nameserver", status); @@ -482,7 +487,7 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, } /* Set this DNS resolver to be used by the SIP resolver */ - status = pjsip_endpt_set_resolver(pjsua_var.endpt, resv); + status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error setting DNS resolver", status); return status; @@ -550,6 +555,13 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg, goto on_error; + /* Start resolving STUN server */ + status = pjsua_resolve_stun_server(PJ_FALSE); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + pjsua_perror(THIS_FILE, "Error resolving STUN server", status); + return status; + } + /* Initialize PJSUA media subsystem */ status = pjsua_media_subsys_init(media_cfg); if (status != PJ_SUCCESS) @@ -638,6 +650,49 @@ static void busy_sleep(unsigned msec) } /* + * Resolve STUN server. + */ +pj_status_t pjsua_resolve_stun_server(pj_bool_t wait) +{ + if (pjsua_var.stun_status == PJ_EUNKNOWN) { + /* Initialize STUN configuration */ + pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0, + pjsip_endpt_get_ioqueue(pjsua_var.endpt), + pjsip_endpt_get_timer_heap(pjsua_var.endpt)); + + /* Start STUN server resolution */ + /* For now just do DNS A resolution */ + + if (pjsua_var.ua_cfg.stun_srv.slen == 0) { + pjsua_var.stun_status = PJ_SUCCESS; + } else { + pj_hostent he; + + pjsua_var.stun_status = + pj_gethostbyname(&pjsua_var.ua_cfg.stun_srv, &he); + + if (pjsua_var.stun_status == PJ_SUCCESS) { + pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0); + pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr; + pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)3478); + } + } + return pjsua_var.stun_status; + + } else if (pjsua_var.stun_status == PJ_EPENDING) { + /* STUN server resolution has been started, wait for the + * result. + */ + pj_assert(!"Should not happen"); + return PJ_EBUG; + + } else { + /* STUN server has been resolved, return the status */ + return pjsua_var.stun_status; + } +} + +/* * Destroy pjsua. */ PJ_DEF(pj_status_t) pjsua_destroy(void) @@ -819,15 +874,21 @@ PJ_DEF(pj_pool_factory*) pjsua_get_pool_factory(void) */ static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr, int port, - pj_bool_t use_stun, - const pjsua_stun_config *stun_param, pj_sock_t *p_sock, pj_sockaddr_in *p_pub_addr) { - pjsua_stun_config stun; + char ip_addr[32]; + pj_str_t stun_srv; pj_sock_t sock; pj_status_t status; + /* Make sure STUN server resolution has completed */ + status = pjsua_resolve_stun_server(PJ_TRUE); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error resolving STUN server", status); + return status; + } + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "socket() error", status); @@ -856,27 +917,24 @@ static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr, port = pj_ntohs(bound_addr.sin_port); } - /* Copy and normalize STUN param */ - if (use_stun) { - pj_memcpy(&stun, stun_param, sizeof(*stun_param)); - pjsua_normalize_stun_config(&stun); + if (pjsua_var.stun_srv.addr.sa_family != 0) { + pj_ansi_strcpy(ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr)); + stun_srv = pj_str(ip_addr); } else { - pj_bzero(&stun, sizeof(pjsua_stun_config)); + stun_srv.slen = 0; } /* Get the published address, either by STUN or by resolving * the name of local host. */ - if (stun.stun_srv1.slen) { + if (stun_srv.slen) { /* * STUN is specified, resolve the address with STUN. */ status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock, - &stun.stun_srv1, - stun.stun_port1, - &stun.stun_srv2, - stun.stun_port2, - p_pub_addr); + &stun_srv, 3478, + &stun_srv, 3478, + p_pub_addr); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error resolving with STUN", status); pj_sock_close(sock); @@ -986,7 +1044,6 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, * (only when public address is not specified). */ status = create_sip_udp_sock(bound_addr.sin_addr, cfg->port, - cfg->use_stun, &cfg->stun_config, &sock, &pub_addr); if (status != PJ_SUCCESS) goto on_return; diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 668d3b92..6c81583d 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -215,6 +215,14 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, pj_status_t status = PJ_SUCCESS; pj_sock_t sock[2]; + /* Make sure STUN server resolution has completed */ + status = pjsua_resolve_stun_server(PJ_TRUE); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error resolving STUN server", status); + return status; + } + + if (next_rtp_port == 0) next_rtp_port = (pj_uint16_t)cfg->port; @@ -272,12 +280,17 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, * If we're configured to use STUN, then find out the mapped address, * and make sure that the mapped RTCP port is adjacent with the RTP. */ - if (cfg->stun_config.stun_srv1.slen) { + if (pjsua_var.stun_srv.addr.sa_family != 0) { + char ip_addr[32]; + pj_str_t stun_srv; + + pj_ansi_strcpy(ip_addr, + pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr)); + stun_srv = pj_str(ip_addr); + status=pjstun_get_mapped_addr(&pjsua_var.cp.factory, 2, sock, - &cfg->stun_config.stun_srv1, - cfg->stun_config.stun_port1, - &cfg->stun_config.stun_srv2, - cfg->stun_config.stun_port2, + &stun_srv, 3478, + &stun_srv, 3478, mapped_addr); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "STUN resolve error", status); @@ -488,39 +501,16 @@ pj_status_t pjsua_media_subsys_destroy(void) } -/* - * Create UDP media transports for all the calls. This function creates - * one UDP media transport for each call. - */ -PJ_DEF(pj_status_t) -pjsua_media_transports_create(const pjsua_transport_config *app_cfg) +/* Create normal UDP media transports */ +static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg) { - pjsua_transport_config cfg; unsigned i; pj_status_t status; - - /* Make sure pjsua_init() has been called */ - PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP); - - PJSUA_LOCK(); - - /* Delete existing media transports */ - for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { - if (pjsua_var.calls[i].med_tp != NULL) { - pjsua_var.calls[i].med_tp->op->destroy(pjsua_var.calls[i].med_tp); - pjsua_var.calls[i].med_tp = NULL; - } - } - - /* Copy config */ - pj_memcpy(&cfg, app_cfg, sizeof(*app_cfg)); - pjsua_normalize_stun_config(&cfg.stun_config); - /* Create each media transport */ for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { - status = create_rtp_rtcp_sock(&cfg, &pjsua_var.calls[i].skinfo); + status = create_rtp_rtcp_sock(cfg, &pjsua_var.calls[i].skinfo); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create RTP/RTCP socket", status); @@ -545,24 +535,428 @@ pjsua_media_transports_create(const pjsua_transport_config *app_cfg) } - PJSUA_UNLOCK(); + return PJ_SUCCESS; + +on_error: + for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { + if (pjsua_var.calls[i].med_tp != NULL) { + pjmedia_transport_close(pjsua_var.calls[i].med_tp); + pjsua_var.calls[i].med_tp = NULL; + } + } + + return status; +} + + +/* Create ICE media transports (when ice is enabled) */ +static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg) +{ + unsigned i; + pj_status_t status; + + /* Create each media transport */ + for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { + pj_ice_st *ice_st; + + status = pjmedia_ice_create(pjsua_var.med_endpt, NULL, 1, + &pjsua_var.stun_cfg, + &pjsua_var.calls[i].med_tp); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create ICE media transport", + status); + goto on_error; + } + + ice_st = pjmedia_ice_get_ice_st(pjsua_var.calls[i].med_tp); + + /* Add host candidates for RTP */ + status = pj_ice_st_add_all_host_interfaces(ice_st, 1, 0, + PJ_FALSE, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error adding ICE host candidates", + status); + goto on_error; + } + + /* Configure STUN server */ + if (pjsua_var.stun_srv.addr.sa_family != 0) { + + status = pj_ice_st_set_stun_addr(ice_st, + pjsua_var.media_cfg.enable_relay, + &pjsua_var.stun_srv.ipv4); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error setting ICE's STUN server", + status); + goto on_error; + } + + /* Add STUN server reflexive candidate for RTP */ + status = pj_ice_st_add_stun_interface(ice_st, 1, 0, + PJ_FALSE, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error adding ICE address", + status); + goto on_error; + } + } + } + + /* Wait until all ICE transports are ready */ + for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { + pj_ice_st *ice_st; + + ice_st = pjmedia_ice_get_ice_st(pjsua_var.calls[i].med_tp); + + /* Wait until interface status is PJ_SUCCESS */ + for (;;) { + status = pj_ice_st_get_interfaces_status(ice_st); + if (status == PJ_EPENDING) + pjsua_handle_events(100); + else + break; + } + + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, + "Error adding STUN address to ICE media transport", + status); + goto on_error; + } + + /* Get transport info */ + pjmedia_transport_get_info(pjsua_var.calls[i].med_tp, + &pjsua_var.calls[i].skinfo); + + } return PJ_SUCCESS; on_error: for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { if (pjsua_var.calls[i].med_tp != NULL) { - pjsua_var.calls[i].med_tp->op->destroy(pjsua_var.calls[i].med_tp); + pjmedia_transport_close(pjsua_var.calls[i].med_tp); pjsua_var.calls[i].med_tp = NULL; } } + return status; +} + + +/* + * Create UDP media transports for all the calls. This function creates + * one UDP media transport for each call. + */ +PJ_DEF(pj_status_t) +pjsua_media_transports_create(const pjsua_transport_config *app_cfg) +{ + pjsua_transport_config cfg; + unsigned i; + pj_status_t status; + + + /* Make sure pjsua_init() has been called */ + PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP); + + PJSUA_LOCK(); + + /* Delete existing media transports */ + for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { + if (pjsua_var.calls[i].med_tp != NULL) { + pjmedia_transport_close(pjsua_var.calls[i].med_tp); + pjsua_var.calls[i].med_tp = NULL; + } + } + + /* Copy config */ + pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg); + + if (pjsua_var.media_cfg.enable_ice) { + status = create_ice_media_transports(&cfg); + } else { + status = create_udp_media_transports(&cfg); + } + + PJSUA_UNLOCK(); return status; } +pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, + pjsip_role_e role) +{ + pjsua_call *call = &pjsua_var.calls[call_id]; + + if (pjsua_var.media_cfg.enable_ice) { + pj_ice_role ice_role; + pj_status_t status; + + ice_role = (role==PJSIP_ROLE_UAC ? PJ_ICE_ROLE_CONTROLLING : + PJ_ICE_ROLE_CONTROLLED); + + /* Restart ICE */ + pjmedia_ice_stop_ice(call->med_tp); + + status = pjmedia_ice_init_ice(call->med_tp, ice_role, NULL, NULL); + if (status != PJ_SUCCESS) + return status; + } + + return PJ_SUCCESS; +} + +pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, + pj_pool_t *pool, + pjmedia_sdp_session **p_sdp) +{ + pjmedia_sdp_session *sdp; + pjsua_call *call = &pjsua_var.calls[call_id]; + pj_status_t status; + + status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pool, 1, + &call->skinfo, &sdp); + if (status != PJ_SUCCESS) + goto on_error; + + if (pjsua_var.media_cfg.enable_ice) { + status = pjmedia_ice_modify_sdp(call->med_tp, pool, sdp); + if (status != PJ_SUCCESS) + goto on_error; + } + + *p_sdp = sdp; + return PJ_SUCCESS; + +on_error: + pjsua_media_channel_deinit(call_id); + return status; + +} + + +static void stop_media_session(pjsua_call_id call_id) +{ + pjsua_call *call = &pjsua_var.calls[call_id]; + + if (call->conf_slot != PJSUA_INVALID_ID) { + pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot); + call->conf_slot = PJSUA_INVALID_ID; + } + + if (call->session) { + pjmedia_session_destroy(call->session); + call->session = NULL; + + PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed", + call_id)); + + } + + call->media_st = PJSUA_CALL_MEDIA_NONE; +} + +pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) +{ + pjsua_call *call = &pjsua_var.calls[call_id]; + + stop_media_session(call_id); + + if (pjsua_var.media_cfg.enable_ice) { + pjmedia_ice_stop_ice(call->med_tp); + } + + return PJ_SUCCESS; +} + + +/* + * DTMF callback from the stream. + */ +static void dtmf_callback(pjmedia_stream *strm, void *user_data, + int digit) +{ + PJ_UNUSED_ARG(strm); + + if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { + pjsua_call_id call_id; + + call_id = (pjsua_call_id)user_data; + pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit); + } +} + + +pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, + pjmedia_sdp_session *local_sdp, + pjmedia_sdp_session *remote_sdp) +{ + int prev_media_st = 0; + pjsua_call *call = &pjsua_var.calls[call_id]; + pjmedia_session_info sess_info; + pjmedia_port *media_port; + pj_str_t port_name; + char tmp[PJSIP_MAX_URL_SIZE]; + pj_status_t status; + + /* Destroy existing media session, if any. */ + prev_media_st = call->media_st; + stop_media_session(call->index); + + /* Create media session info based on SDP parameters. + * We only support one stream per session at the moment + */ + status = pjmedia_session_info_from_sdp( call->inv->dlg->pool, + pjsua_var.med_endpt, + 1,&sess_info, + local_sdp, remote_sdp); + if (status != PJ_SUCCESS) + return status; + + + /* Check if media is put on-hold */ + if (sess_info.stream_cnt == 0 || + sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE) + { + + /* Determine who puts the call on-hold */ + if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) { + if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) { + /* It was local who offer hold */ + call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD; + } else { + call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD; + } + } + + call->media_dir = PJMEDIA_DIR_NONE; + + /* Shutdown transport */ + /* No need because we need keepalive? */ + + } else { + + /* Start ICE */ + if (pjsua_var.media_cfg.enable_ice) { + status = pjmedia_ice_start_ice(call->med_tp, call->inv->pool, + remote_sdp); + if (status != PJ_SUCCESS) + return status; + } + + /* Override ptime, if this option is specified. */ + if (pjsua_var.media_cfg.ptime != 0) { + sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t) + (pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime); + if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0) + sess_info.stream_info[0].param->setting.frm_per_pkt = 1; + } + + /* Disable VAD, if this option is specified. */ + if (pjsua_var.media_cfg.no_vad) { + sess_info.stream_info[0].param->setting.vad = 0; + } + + + /* Optionally, application may modify other stream settings here + * (such as jitter buffer parameters, codec ptime, etc.) + */ + sess_info.stream_info[0].jb_init = pjsua_var.media_cfg.jb_init; + sess_info.stream_info[0].jb_min_pre = pjsua_var.media_cfg.jb_min_pre; + sess_info.stream_info[0].jb_max_pre = pjsua_var.media_cfg.jb_max_pre; + sess_info.stream_info[0].jb_max = pjsua_var.media_cfg.jb_max; + + /* Create session based on session info. */ + status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info, + &call->med_tp, + call, &call->session ); + if (status != PJ_SUCCESS) { + return status; + } + + /* If DTMF callback is installed by application, install our + * callback to the session. + */ + if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { + pjmedia_session_set_dtmf_callback(call->session, 0, + &dtmf_callback, + (void*)(call->index)); + } + + /* Get the port interface of the first stream in the session. + * We need the port interface to add to the conference bridge. + */ + pjmedia_session_get_port(call->session, 0, &media_port); + + + /* + * Add the call to conference bridge. + */ + port_name.ptr = tmp; + port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, + call->inv->dlg->remote.info->uri, + tmp, sizeof(tmp)); + if (port_name.slen < 1) { + port_name = pj_str("call"); + } + status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool, + media_port, + &port_name, + (unsigned*)&call->conf_slot); + if (status != PJ_SUCCESS) { + return status; + } + + /* Call's media state is active */ + call->media_st = PJSUA_CALL_MEDIA_ACTIVE; + call->media_dir = sess_info.stream_info[0].dir; + } + + /* Print info. */ + { + char info[80]; + int info_len = 0; + unsigned i; + + for (i=0; i<sess_info.stream_cnt; ++i) { + int len; + const char *dir; + pjmedia_stream_info *strm_info = &sess_info.stream_info[i]; + + switch (strm_info->dir) { + case PJMEDIA_DIR_NONE: + dir = "inactive"; + break; + case PJMEDIA_DIR_ENCODING: + dir = "sendonly"; + break; + case PJMEDIA_DIR_DECODING: + dir = "recvonly"; + break; + case PJMEDIA_DIR_ENCODING_DECODING: + dir = "sendrecv"; + break; + default: + dir = "unknown"; + break; + } + len = pj_ansi_sprintf( info+info_len, + ", stream #%d: %.*s (%s)", i, + (int)strm_info->fmt.encoding_name.slen, + strm_info->fmt.encoding_name.ptr, + dir); + if (len > 0) + info_len += len; + } + PJ_LOG(4,(THIS_FILE,"Media updates%s", info)); + } + + return PJ_SUCCESS; +} + + /* * Get maxinum number of conference ports. */ |