summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-06-06 14:51:48 +0000
committerBenny Prijono <bennylp@teluu.com>2008-06-06 14:51:48 +0000
commit93c46290dcf20a704b36e66208543ff2bc36e23b (patch)
treee9257512192783ec3163602dfd5c502d7bf85342
parent5ac557ce4cbeeca1747e14fbf2769d77d23c332d (diff)
More ticket #485: added TURN support in PJSUA-LIB API
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1990 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c87
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h37
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h1
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c3
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c194
5 files changed, 237 insertions, 85 deletions
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index 2e63c73a..3c92b988 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -170,7 +170,6 @@ 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 (" --dis-codec=name Disable codec (can be specified multiple times)");
puts (" --clock-rate=N Override conference bridge clock rate");
@@ -188,17 +187,26 @@ static void usage(void)
puts (" --auto-conf Automatically put calls in conference with others");
puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");
puts (" --auto-rec Automatically record conversation");
- puts (" --rtp-port=N Base port to try for RTP (default=4000)");
puts (" --quality=N Specify media quality (0-10, default=6)");
puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)");
puts (" --no-vad Disable VAD/silence detector (default=vad enabled)");
puts (" --ec-tail=MSEC Set echo canceller tail length (default=256)");
puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is 20)");
- puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
- puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
puts (" --capture-dev=id Audio capture device ID (default=-1)");
puts (" --playback-dev=id Audio playback device ID (default=-1)");
+ puts ("");
+ puts ("Media Transport Options:");
+ puts (" --use-ice Enable ICE (default:no)");
+ puts (" --ice-no-host Disable ICE host candidates");
+ puts (" --rtp-port=N Base port to try for RTP (default=4000)");
+ puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
+ puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
+ puts (" --use-turn Enable TURN relay with ICE (default:no)");
+ puts (" --turn-srv Domain or host name of TURN server (\"NAME:PORT\" format)");
+ puts (" --turn-tcp Use TCP connection to TURN server (default no)");
+ puts (" --turn-user TURN username");
+ puts (" --turn-passwd TURN password");
puts ("");
puts ("Buddy List (can be more than one):");
@@ -392,6 +400,8 @@ static pj_status_t parse_args(int argc, char *argv[],
OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
OPT_USE_ICE, OPT_USE_SRTP, OPT_SRTP_SECURE,
+ OPT_USE_TURN,OPT_ICE_NO_HOST, OPT_TURN_SRV, OPT_TURN_TCP,
+ OPT_TURN_USER, OPT_TURN_PASSWD,
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,
@@ -451,7 +461,15 @@ 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},
+ { "use-turn", 0, 0, OPT_USE_TURN},
+ { "ice-no-host",0, 0, OPT_ICE_NO_HOST},
+ { "turn-srv", 1, 0, OPT_TURN_SRV},
+ { "turn-tcp", 0, 0, OPT_TURN_TCP},
+ { "turn-user", 1, 0, OPT_TURN_USER},
+ { "turn-passwd",1, 0, OPT_TURN_PASSWD},
+
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
{ "use-srtp", 1, 0, OPT_USE_SRTP},
{ "srtp-secure",1, 0, OPT_SRTP_SECURE},
@@ -825,6 +843,33 @@ static pj_status_t parse_args(int argc, char *argv[],
cfg->media_cfg.enable_ice = PJ_TRUE;
break;
+ case OPT_USE_TURN:
+ cfg->media_cfg.enable_turn = PJ_TRUE;
+ break;
+
+ case OPT_ICE_NO_HOST:
+ cfg->media_cfg.ice_no_host_cands = PJ_TRUE;
+ break;
+
+ case OPT_TURN_SRV:
+ cfg->media_cfg.turn_server = pj_str(pj_optarg);
+ break;
+
+ case OPT_TURN_TCP:
+ cfg->media_cfg.turn_conn_type = PJ_TURN_TP_TCP;
+ break;
+
+ case OPT_TURN_USER:
+ cfg->media_cfg.turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
+ cfg->media_cfg.turn_auth_cred.data.static_cred.realm = pj_str("*");
+ cfg->media_cfg.turn_auth_cred.data.static_cred.username = pj_str(pj_optarg);
+ break;
+
+ case OPT_TURN_PASSWD:
+ cfg->media_cfg.turn_auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
+ cfg->media_cfg.turn_auth_cred.data.static_cred.data = pj_str(pj_optarg);
+ break;
+
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
case OPT_USE_SRTP:
app_config.cfg.use_srtp = my_atoi(pj_optarg);
@@ -1359,9 +1404,41 @@ static int write_settings(const struct app_config *config,
}
#endif
- /* Media */
+ /* Media Transport*/
if (config->media_cfg.enable_ice)
pj_strcat2(&cfg, "--use-ice\n");
+
+ if (config->media_cfg.enable_turn)
+ pj_strcat2(&cfg, "--use-turn\n");
+
+ if (config->media_cfg.ice_no_host_cands)
+ pj_strcat2(&cfg, "--ice-no-host\n");
+
+ if (config->media_cfg.turn_server.slen) {
+ pj_ansi_sprintf(line, "--turn-srv %.*s\n",
+ (int)config->media_cfg.turn_server.slen,
+ config->media_cfg.turn_server.ptr);
+ pj_strcat2(&cfg, line);
+ }
+
+ if (config->media_cfg.turn_conn_type == PJ_TURN_TP_TCP)
+ pj_strcat2(&cfg, "--turn-tcp\n");
+
+ if (config->media_cfg.turn_auth_cred.data.static_cred.username.slen) {
+ pj_ansi_sprintf(line, "--turn-user %.*s\n",
+ (int)config->media_cfg.turn_auth_cred.data.static_cred.username.slen,
+ config->media_cfg.turn_auth_cred.data.static_cred.username.ptr);
+ pj_strcat2(&cfg, line);
+ }
+
+ if (config->media_cfg.turn_auth_cred.data.static_cred.data.slen) {
+ pj_ansi_sprintf(line, "--turn-passwd %.*s\n",
+ (int)config->media_cfg.turn_auth_cred.data.static_cred.data.slen,
+ config->media_cfg.turn_auth_cred.data.static_cred.data.ptr);
+ pj_strcat2(&cfg, line);
+ }
+
+ /* Media */
if (config->null_audio)
pj_strcat2(&cfg, "--null-audio\n");
if (config->auto_play)
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index 9f42b9ce..9e9ee2a2 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -1028,8 +1028,8 @@ typedef struct pjsua_config
/**
* Specify domain name to be resolved with DNS SRV resolution to get the
- * address of the STUN servers. Alternatively application may specify
- * \a stun_host and \a stun_relay_host instead.
+ * address of the STUN server. Alternatively application may specify
+ * \a stun_host instead.
*
* If DNS SRV resolution failed for this domain, then DNS A resolution
* will be performed only if \a stun_host is specified.
@@ -1043,11 +1043,6 @@ typedef struct pjsua_config
pj_str_t stun_host;
/**
- * Specify STUN relay server to be used.
- */
- pj_str_t stun_relay_host;
-
- /**
* Support for adding and parsing NAT type in the SDP to assist
* troubleshooting. The valid values are:
* - 0: no information will be added in SDP, and parsing is disabled.
@@ -3886,9 +3881,33 @@ struct pjsua_media_config
pj_bool_t enable_ice;
/**
- * Enable ICE media relay.
+ * Disable ICE host candidates.
+ */
+ pj_bool_t ice_no_host_cands;
+
+ /**
+ * Enable TURN relay candidate in ICE.
+ */
+ pj_bool_t enable_turn;
+
+ /**
+ * Specify TURN domain name or host name, in in "DOMAIN:PORT" or
+ * "HOST:PORT" format.
+ */
+ pj_str_t turn_server;
+
+ /**
+ * Specify the connection type to be used to the TURN server. Valid
+ * values are PJ_TURN_TP_UDP or PJ_TURN_TP_TCP.
+ *
+ * Default: PJ_TURN_TP_UDP
+ */
+ pj_turn_tp_type turn_conn_type;
+
+ /**
+ * Specify the credential to authenticate with the TURN server.
*/
- pj_bool_t enable_relay;
+ pj_stun_auth_cred turn_auth_cred;
};
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index df981ed8..dfb9f84a 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -52,6 +52,7 @@ typedef struct pjsua_call
pjsip_evsub *xfer_sub; /**< Xfer server subscription, if this
call was triggered by xfer. */
pjmedia_transport *med_tp; /**< Current media transport. */
+ pj_status_t med_tp_st; /**< Media transport status. */
pjmedia_transport *med_orig; /**< Original media transport */
pj_timer_entry refresh_tm;/**< Timer to send re-INVITE. */
pj_timer_entry hangup_tm; /**< Timer to hangup call. */
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 82dc4d65..a50ff433 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -113,7 +113,6 @@ PJ_DEF(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_domain, &src->stun_domain);
pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
- pj_strdup_with_null(pool, &dst->stun_relay_host, &src->stun_relay_host);
}
PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
@@ -172,6 +171,8 @@ PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg)
cfg->ilbc_mode = PJSUA_DEFAULT_ILBC_MODE;
cfg->ec_tail_len = PJSUA_DEFAULT_EC_TAIL_LEN;
cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
+
+ cfg->turn_conn_type = PJ_TURN_TP_UDP;
}
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index 90b39460..a43ce4cb 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -36,6 +36,15 @@ static pj_uint16_t next_rtp_port;
static void close_snd_dev(void);
+static void pjsua_media_config_dup(pj_pool_t *pool,
+ pjsua_media_config *dst,
+ const pjsua_media_config *src)
+{
+ pj_memcpy(dst, src, sizeof(*src));
+ pj_strdup(pool, &dst->turn_server, &src->turn_server);
+ pj_stun_auth_cred_dup(pool, &dst->turn_auth_cred, &src->turn_auth_cred);
+}
+
/**
* Init media subsystems.
*/
@@ -49,7 +58,7 @@ pj_status_t pjsua_media_subsys_init(const pjsua_media_config *cfg)
PJ_UNUSED_ARG(codec_id);
/* Copy configuration */
- pj_memcpy(&pjsua_var.media_cfg, cfg, sizeof(*cfg));
+ pjsua_media_config_dup(pjsua_var.pool, &pjsua_var.media_cfg, cfg);
/* Normalize configuration */
if (pjsua_var.media_cfg.snd_clock_rate == 0) {
@@ -581,46 +590,81 @@ on_error:
/* This callback is called when ICE negotiation completes */
-static void on_ice_complete(pjmedia_transport *tp, pj_status_t result)
+static void on_ice_complete(pjmedia_transport *tp,
+ pj_ice_strans_op op,
+ pj_status_t result)
{
- unsigned id, c;
+ unsigned id;
pj_bool_t found = PJ_FALSE;
- /* We're only interested with failure case */
- if (result == PJ_SUCCESS)
- return;
-
/* Find call which has this media transport */
PJSUA_LOCK();
- for (id=0, c=0; id<PJSUA_MAX_CALLS && c<pjsua_var.call_cnt; ++id) {
- pjsua_call *call = &pjsua_var.calls[id];
- if (call->inv) {
- ++c;
-
- if (call->med_tp == tp) {
- call->media_st = PJSUA_CALL_MEDIA_ERROR;
- call->media_dir = PJMEDIA_DIR_NONE;
- found = PJ_TRUE;
- break;
- }
+ for (id=0; id<pjsua_var.ua_cfg.max_calls; ++id) {
+ if (pjsua_var.calls[id].med_tp == tp ||
+ pjsua_var.calls[id].med_orig == tp)
+ {
+ found = PJ_TRUE;
+ break;
}
}
PJSUA_UNLOCK();
- if (found && pjsua_var.ua_cfg.cb.on_call_media_state) {
- pjsua_var.ua_cfg.cb.on_call_media_state(id);
+ if (!found)
+ return;
+
+ switch (op) {
+ case PJ_ICE_STRANS_OP_INIT:
+ pjsua_var.calls[id].med_tp_st = result;
+ break;
+ case PJ_ICE_STRANS_OP_NEGOTIATION:
+ if (result != PJ_SUCCESS) {
+ pjsua_var.calls[id].media_st = PJSUA_CALL_MEDIA_ERROR;
+ pjsua_var.calls[id].media_dir = PJMEDIA_DIR_NONE;
+
+ if (pjsua_var.ua_cfg.cb.on_call_media_state) {
+ pjsua_var.ua_cfg.cb.on_call_media_state(id);
+ }
+ }
+ break;
}
}
+/* Parse "HOST:PORT" format */
+static pj_status_t parse_host_port(const pj_str_t *host_port,
+ pj_str_t *host, pj_uint16_t *port)
+{
+ pj_str_t str_port;
+
+ str_port.ptr = pj_strchr(host_port, ':');
+ if (str_port.ptr != NULL) {
+ int iport;
+
+ host->ptr = host_port->ptr;
+ host->slen = (str_port.ptr - host->ptr);
+ str_port.ptr++;
+ str_port.slen = host_port->slen - host->slen - 1;
+ iport = (int)pj_strtoul(&str_port);
+ if (iport < 1 || iport > 65535)
+ return PJ_EINVAL;
+ *port = (pj_uint16_t)iport;
+ } else {
+ *host = *host_port;
+ *port = 0;
+ }
+
+ return PJ_SUCCESS;
+}
+
/* Create ICE media transports (when ice is enabled) */
-static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
+static pj_status_t create_ice_media_transports(void)
{
+ char stunip[PJ_INET6_ADDRSTRLEN];
+ pj_ice_strans_cfg ice_cfg;
unsigned i;
- pj_sockaddr_in addr;
pj_status_t status;
/* Make sure STUN server resolution has completed */
@@ -630,27 +674,57 @@ static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
return status;
}
- pj_sockaddr_in_init(&addr, 0, (pj_uint16_t)cfg->port);
+ /* Create ICE stream transport configuration */
+ pj_ice_strans_cfg_default(&ice_cfg);
+ pj_stun_config_init(&ice_cfg.stun_cfg, &pjsua_var.cp.factory, 0,
+ pjsip_endpt_get_ioqueue(pjsua_var.endpt),
+ pjsip_endpt_get_timer_heap(pjsua_var.endpt));
+
+ ice_cfg.af = pj_AF_INET();
+ ice_cfg.resolver = pjsua_var.resolver;
+
+ /* Configure STUN settings */
+ if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
+ pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
+ ice_cfg.stun.server = pj_str(stunip);
+ ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
+ }
+ ice_cfg.stun.no_host_cands = pjsua_var.media_cfg.ice_no_host_cands;
+
+ /* Configure TURN settings */
+ if (pjsua_var.media_cfg.enable_turn) {
+ status = parse_host_port(&pjsua_var.media_cfg.turn_server,
+ &ice_cfg.turn.server,
+ &ice_cfg.turn.port);
+ if (status != PJ_SUCCESS || ice_cfg.turn.server.slen == 0) {
+ PJ_LOG(1,(THIS_FILE, "Invalid TURN server setting"));
+ return PJ_EINVAL;
+ }
+ if (ice_cfg.turn.port == 0)
+ ice_cfg.turn.port = 3479;
+ ice_cfg.turn.conn_type = pjsua_var.media_cfg.turn_conn_type;
+ pj_memcpy(&ice_cfg.turn.auth_cred,
+ &pjsua_var.media_cfg.turn_auth_cred,
+ sizeof(ice_cfg.turn.auth_cred));
+ }
/* Create each media transport */
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
- pj_ice_strans_comp comp;
pjmedia_ice_cb ice_cb;
- int next_port;
char name[32];
-#if PJMEDIA_ADVERTISE_RTCP
- enum { COMP_CNT=2 };
-#else
- enum { COMP_CNT=1 };
-#endif
+ unsigned comp_cnt;
pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
ice_cb.on_ice_complete = &on_ice_complete;
-
pj_ansi_snprintf(name, sizeof(name), "icetp%02d", i);
-
- status = pjmedia_ice_create(pjsua_var.med_endpt, name, COMP_CNT,
- &pjsua_var.stun_cfg, &ice_cb,
+ pjsua_var.calls[i].med_tp_st = PJ_EPENDING;
+
+ comp_cnt = 1;
+ if (PJMEDIA_ADVERTISE_RTCP)
+ ++comp_cnt;
+
+ status = pjmedia_ice_create(pjsua_var.med_endpt, name, comp_cnt,
+ &ice_cfg, &ice_cb,
&pjsua_var.calls[i].med_tp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
@@ -658,6 +732,19 @@ static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
goto on_error;
}
+ /* Wait until transport is initialized, or time out */
+ PJSUA_UNLOCK();
+ while (pjsua_var.calls[i].med_tp_st == PJ_EPENDING) {
+ pjsua_handle_events(100);
+ }
+ PJSUA_LOCK();
+ if (pjsua_var.calls[i].med_tp_st != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error initializing ICE media transport",
+ pjsua_var.calls[i].med_tp_st);
+ status = pjsua_var.calls[i].med_tp_st;
+ goto on_error;
+ }
+
pjmedia_transport_simulate_lost(pjsua_var.calls[i].med_tp,
PJMEDIA_DIR_ENCODING,
pjsua_var.media_cfg.tx_drop_pct);
@@ -665,40 +752,6 @@ static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
pjmedia_transport_simulate_lost(pjsua_var.calls[i].med_tp,
PJMEDIA_DIR_DECODING,
pjsua_var.media_cfg.rx_drop_pct);
-
- status = pjmedia_ice_start_init(pjsua_var.calls[i].med_tp, 0, &addr,
- &pjsua_var.stun_srv.ipv4, NULL);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error starting ICE transport",
- status);
- goto on_error;
- }
-
- pjmedia_ice_get_comp(pjsua_var.calls[i].med_tp, 1, &comp);
- next_port = pj_ntohs(comp.local_addr.ipv4.sin_port);
- next_port += 2;
- addr.sin_port = pj_htons((pj_uint16_t)next_port);
- }
-
- /* Wait until all ICE transports are ready */
- for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
-
- /* Wait until interface status is PJ_SUCCESS */
- for (;;) {
- status = pjmedia_ice_get_init_status(pjsua_var.calls[i].med_tp);
- 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;
- }
-
}
return PJ_SUCCESS;
@@ -744,7 +797,7 @@ PJ_DEF(pj_status_t) pjsua_media_transports_create(
pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
if (pjsua_var.media_cfg.enable_ice) {
- status = create_ice_media_transports(&cfg);
+ status = create_ice_media_transports();
} else {
status = create_udp_media_transports(&cfg);
}
@@ -896,7 +949,8 @@ static void stop_media_session(pjsua_call_id call_id)
{
pjsua_call *call = &pjsua_var.calls[call_id];
- pjmedia_transport_media_stop(call->med_tp);
+ //see ticket #525
+ //pjmedia_transport_media_stop(call->med_tp);
if (call->conf_slot != PJSUA_INVALID_ID) {
pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);