summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip')
-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
8 files changed, 601 insertions, 380 deletions
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.
*/