summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2007-03-23 16:34:20 +0000
committerBenny Prijono <bennylp@teluu.com>2007-03-23 16:34:20 +0000
commite3fd604ea862f68ad3ece248ca7d853899cbf48f (patch)
tree88fb4659ab449d79b25dc8e0dfe3b64f145b9dd3
parent05e7998ba4cbd7fb0b02b7f82c5b328cf203fbc9 (diff)
ICE (work in progress): integration with PJSUA
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1098 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/include/pjmedia.h1
-rw-r--r--pjmedia/include/pjmedia/transport.h37
-rw-r--r--pjmedia/include/pjmedia/transport_ice.h12
-rw-r--r--pjmedia/src/pjmedia/transport_ice.c68
-rw-r--r--pjmedia/src/pjmedia/transport_udp.c1
-rw-r--r--pjnath/include/pjnath/ice_stream_transport.h3
-rw-r--r--pjnath/src/pjnath/ice.c7
-rw-r--r--pjnath/src/pjnath/ice_stream_transport.c42
-rw-r--r--pjsip-apps/build/pjsua.dsp4
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c77
-rw-r--r--pjsip/build/pjsua_lib.dsp4
-rw-r--r--pjsip/include/pjsip/sip_endpoint.h9
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h130
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h24
-rw-r--r--pjsip/src/pjsip/sip_endpoint.c8
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c251
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c95
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c460
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.
*/