diff options
-rw-r--r-- | pjsip-apps/src/pjsua/pjsua_app.c | 87 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua.h | 37 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua_internal.h | 1 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_core.c | 3 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_media.c | 194 |
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); |