summaryrefslogtreecommitdiff
path: root/pjsip/src/pjsua-lib/pjsua_core.c
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-06-13 22:57:13 +0000
committerBenny Prijono <bennylp@teluu.com>2006-06-13 22:57:13 +0000
commit5263415f8300e09213e4dd3b684d3c16b8263f9f (patch)
tree619e3c631a84a5791e4fa9fdf56601afcc0830a9 /pjsip/src/pjsua-lib/pjsua_core.c
parent94e741d055535156504bfbb182c8b63412299fb9 (diff)
-- REWRITE OF PJSUA API --
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@503 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip/src/pjsua-lib/pjsua_core.c')
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c2113
1 files changed, 651 insertions, 1462 deletions
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 01b6c8c4..e269fa07 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -17,178 +17,118 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjsua-lib/pjsua.h>
-#include "pjsua_imp.h"
+#include <pjsua-lib/pjsua_internal.h>
-/*
- * pjsua_core.c
- *
- * Core application functionalities.
- */
#define THIS_FILE "pjsua_core.c"
-/*
- * Global variable.
- */
-struct pjsua pjsua;
+/* PJSUA application instance. */
+struct pjsua_data pjsua_var;
-/*
- * Default local URI, if none is specified in cmd-line
- */
-#define PJSUA_LOCAL_URI "<sip:user@127.0.0.1>"
+/* Display error */
+PJ_DEF(void) pjsua_perror( const char *sender, const char *title,
+ pj_status_t status)
+{
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ PJ_LOG(3,(sender, "%s: %s [status=%d]", title, errmsg, status));
+}
-/*
- * Init default application parameters.
- */
-PJ_DEF(void) pjsua_default_config(pjsua_config *cfg)
+static void init_data()
{
unsigned i;
- pj_memset(cfg, 0, sizeof(pjsua_config));
-
- cfg->thread_cnt = 1;
- cfg->media_has_ioqueue = 1;
- cfg->media_thread_cnt = 1;
- cfg->udp_port = 5060;
- cfg->start_rtp_port = 4000;
- cfg->msg_logging = PJ_TRUE;
- cfg->max_calls = 4;
- cfg->conf_ports = 0;
-
-#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
- pjsua.clock_rate = 44100;
-#endif
-
- cfg->complexity = 10;
- cfg->quality = 10;
+ for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i)
+ pjsua_var.acc[i].index = i;
- cfg->auto_answer = 100;
- cfg->uas_duration = 3600;
-
- /* Default logging settings: */
- cfg->log_level = 5;
- cfg->app_log_level = 4;
- cfg->log_decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
- PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
-
-
- /* Also init logging settings in pjsua.config, because log
- * may be written before pjsua_init() is called.
- */
- pjsua.config.log_level = 5;
- pjsua.config.app_log_level = 4;
-
-
- /* Init accounts: */
- for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
- cfg->acc_config[i].reg_timeout = 55;
- }
-
+ for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i)
+ pjsua_var.tpdata[i].index = i;
}
-#define strncpy_with_null(dst,src,len) \
-do { \
- strncpy(dst, src, len); \
- dst[len-1] = '\0'; \
-} while (0)
-
+/*****************************************************************************
+ * This is a very simple PJSIP module, whose sole purpose is to display
+ * incoming and outgoing messages to log. This module will have priority
+ * higher than transport layer, which means:
+ *
+ * - incoming messages will come to this module first before reaching
+ * transaction layer.
+ *
+ * - outgoing messages will come to this module last, after the message
+ * has been 'printed' to contiguous buffer by transport layer and
+ * appropriate transport instance has been decided for this message.
+ *
+ */
-PJ_DEF(pj_status_t) pjsua_test_config( const pjsua_config *cfg,
- char *errmsg,
- int len)
+/* Notification on incoming messages */
+static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
{
- unsigned i;
-
- /* If UDP port is zero, then sip_host and sip_port must be specified */
- if (cfg->udp_port == 0) {
- if (cfg->sip_host.slen==0 || cfg->sip_port==0) {
- strncpy_with_null(errmsg,
- "sip_host and sip_port must be specified",
- len);
- return -1;
- }
- }
-
- if (cfg->max_calls < 1) {
- strncpy_with_null(errmsg,
- "max_calls needs to be at least 1",
- len);
- return -1;
- }
-
- /* STUN */
- if (cfg->stun_srv1.slen || cfg->stun_port1 || cfg->stun_port2 ||
- cfg->stun_srv2.slen)
- {
- if (cfg->stun_port1 == 0) {
- strncpy_with_null(errmsg, "stun_port1 required", len);
- return -1;
- }
- if (cfg->stun_srv1.slen == 0) {
- strncpy_with_null(errmsg, "stun_srv1 required", len);
- return -1;
- }
- if (cfg->stun_port2 == 0) {
- strncpy_with_null(errmsg, "stun_port2 required", len);
- return -1;
- }
- if (cfg->stun_srv2.slen == 0) {
- strncpy_with_null(errmsg, "stun_srv2 required", len);
- return -1;
- }
- }
-
- /* Verify accounts */
- for (i=0; i<cfg->acc_cnt; ++i) {
- const pjsua_acc_config *acc_cfg = &cfg->acc_config[i];
- unsigned j;
-
- if (acc_cfg->id.slen == 0) {
- strncpy_with_null(errmsg, "missing account ID", len);
- return -1;
- }
-
- if (acc_cfg->id.slen == 0) {
- strncpy_with_null(errmsg, "missing registrar URI", len);
- return -1;
- }
-
- if (acc_cfg->reg_timeout == 0) {
- strncpy_with_null(errmsg, "missing registration timeout", len);
- return -1;
- }
-
-
- for (j=0; j<acc_cfg->cred_count; ++j) {
-
- if (acc_cfg->cred_info[j].scheme.slen == 0) {
- strncpy_with_null(errmsg, "missing auth scheme in account",
- len);
- return -1;
- }
-
- if (acc_cfg->cred_info[j].realm.slen == 0) {
- strncpy_with_null(errmsg, "missing realm in account", len);
- return -1;
- }
+ PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n"
+ "%s\n"
+ "--end msg--",
+ rdata->msg_info.len,
+ pjsip_rx_data_get_info(rdata),
+ rdata->pkt_info.src_name,
+ rdata->pkt_info.src_port,
+ rdata->msg_info.msg_buf));
+
+ /* Always return false, otherwise messages will not get processed! */
+ return PJ_FALSE;
+}
- if (acc_cfg->cred_info[j].username.slen == 0) {
- strncpy_with_null(errmsg, "missing username in account", len);
- return -1;
- }
+/* Notification on outgoing messages */
+static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
+{
+
+ /* Important note:
+ * tp_info field is only valid after outgoing messages has passed
+ * transport layer. So don't try to access tp_info when the module
+ * has lower priority than transport layer.
+ */
- }
- }
+ PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
+ "%s\n"
+ "--end msg--",
+ (tdata->buf.cur - tdata->buf.start),
+ pjsip_tx_data_get_info(tdata),
+ tdata->tp_info.dst_name,
+ tdata->tp_info.dst_port,
+ tdata->buf.start));
+ /* Always return success, otherwise message will not get sent! */
return PJ_SUCCESS;
}
+/* The module instance. */
+static pjsip_module pjsua_msg_logger =
+{
+ NULL, NULL, /* prev, next. */
+ { "mod-pjsua-log", 13 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &logging_on_rx_msg, /* on_rx_request() */
+ &logging_on_rx_msg, /* on_rx_response() */
+ &logging_on_tx_msg, /* on_tx_request. */
+ &logging_on_tx_msg, /* on_tx_response() */
+ NULL, /* on_tsx_state() */
+
+};
+
+
+/*****************************************************************************
+ * These two functions are the main callbacks registered to PJSIP stack
+ * to receive SIP request and response messages that are outside any
+ * dialogs and any transactions.
+ */
/*
* Handler for receiving incoming requests.
@@ -201,13 +141,18 @@ PJ_DEF(pj_status_t) pjsua_test_config( const pjsua_config *cfg,
*/
static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
{
+ pj_bool_t processed = PJ_FALSE;
+
+ PJSUA_LOCK();
if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
- return pjsua_call_on_incoming(rdata);
+ processed = pjsua_call_on_incoming(rdata);
}
- return PJ_FALSE;
+ PJSUA_UNLOCK();
+
+ return processed;
}
@@ -229,742 +174,199 @@ static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
}
-static int PJ_THREAD_FUNC pjsua_poll(void *arg)
-{
- pj_status_t last_err = 0;
-
- PJ_UNUSED_ARG(arg);
-
- do {
- pj_time_val timeout = { 0, 10 };
- pj_status_t status;
-
- status = pjsip_endpt_handle_events (pjsua.endpt, &timeout);
- if (status != PJ_SUCCESS && status != last_err) {
- last_err = status;
- pjsua_perror(THIS_FILE, "handle_events() returned error", status);
- }
- } while (!pjsua.quit_flag);
-
- return 0;
-}
-
-/**
- * Poll pjsua.
+/*****************************************************************************
+ * Logging.
*/
-PJ_DECL(int) pjsua_handle_events(unsigned msec_timeout)
+
+/* Log callback */
+static void log_writer(int level, const char *buffer, int len)
{
- unsigned count = 0;
- pj_time_val tv;
- pj_status_t status;
+ /* Write to stdout, file, and application callback. */
- tv.sec = 0;
- tv.msec = msec_timeout;
- pj_time_val_normalize(&tv);
+ if (level <= (int)pjsua_var.log_cfg.console_level)
+ pj_log_write(level, buffer, len);
- status = pjsip_endpt_handle_events2(pjsua.endpt, &tv, &count);
- if (status != PJ_SUCCESS)
- return -status;
+ if (pjsua_var.log_file) {
+ pj_ssize_t size = len;
+ pj_file_write(pjsua_var.log_file, buffer, &size);
+ }
- return count;
+ if (pjsua_var.log_cfg.cb)
+ (*pjsua_var.log_cfg.cb)(level, buffer, len);
}
-#define pjsua_has_stun() (pjsua.config.stun_port1 && \
- pjsua.config.stun_port2)
-
-
/*
- * Create and initialize SIP socket (and possibly resolve public
- * address via STUN, depending on config).
+ * Application can call this function at any time (after pjsua_create(), of
+ * course) to change logging settings.
*/
-static pj_status_t create_sip_udp_sock(int port,
- pj_sock_t *p_sock,
- pj_sockaddr_in *p_pub_addr)
+PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
{
- pj_sock_t sock;
pj_status_t status;
- status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "socket() error", status);
- return status;
- }
+ /* Save config. */
+ pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
- status = pj_sock_bind_in(sock, 0, (pj_uint16_t)port);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "bind() error", status);
- pj_sock_close(sock);
- return status;
- }
-
- if (pjsua_has_stun()) {
- status = pj_stun_get_mapped_addr(&pjsua.cp.factory, 1, &sock,
- &pjsua.config.stun_srv1,
- pjsua.config.stun_port1,
- &pjsua.config.stun_srv2,
- pjsua.config.stun_port2,
- p_pub_addr);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "STUN resolve error", status);
- pj_sock_close(sock);
- return status;
- }
-
- } else {
-
- const pj_str_t *hostname = pj_gethostname();
- struct pj_hostent he;
-
- status = pj_gethostbyname(hostname, &he);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to resolve local host", status);
- pj_sock_close(sock);
- return status;
- }
+ /* Redirect log function to ours */
+ pj_log_set_log_func( &log_writer );
- pj_memset(p_pub_addr, 0, sizeof(pj_sockaddr_in));
- p_pub_addr->sin_family = PJ_AF_INET;
- p_pub_addr->sin_port = pj_htons((pj_uint16_t)port);
- p_pub_addr->sin_addr = *(pj_in_addr*)he.h_addr;
+ /* Close existing file, if any */
+ if (pjsua_var.log_file) {
+ pj_file_close(pjsua_var.log_file);
+ pjsua_var.log_file = NULL;
}
- *p_sock = sock;
- return PJ_SUCCESS;
-}
-
-
-/*
- * Create RTP and RTCP socket pair, and possibly resolve their public
- * address via STUN.
- */
-static pj_status_t create_rtp_rtcp_sock(pjmedia_sock_info *skinfo)
-{
- enum {
- RTP_RETRY = 100
- };
- int i;
- static pj_uint16_t rtp_port;
- pj_sockaddr_in mapped_addr[2];
- pj_status_t status = PJ_SUCCESS;
- pj_sock_t sock[2];
-
- if (rtp_port == 0)
- rtp_port = (pj_uint16_t)pjsua.config.start_rtp_port;
-
- for (i=0; i<2; ++i)
- sock[i] = PJ_INVALID_SOCKET;
-
-
- /* Loop retry to bind RTP and RTCP sockets. */
- for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) {
-
- /* Create and bind RTP socket. */
- status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[0]);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "socket() error", status);
- return status;
- }
+ /* If output log file is desired, create the file: */
+ if (pjsua_var.log_cfg.log_filename.slen) {
- status = pj_sock_bind_in(sock[0], 0, rtp_port);
- if (status != PJ_SUCCESS) {
- pj_sock_close(sock[0]);
- sock[0] = PJ_INVALID_SOCKET;
- continue;
- }
+ status = pj_file_open(pjsua_var.pool,
+ pjsua_var.log_cfg.log_filename.ptr,
+ PJ_O_WRONLY,
+ &pjsua_var.log_file);
- /* Create and bind RTCP socket. */
- status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[1]);
if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "socket() error", status);
- pj_sock_close(sock[0]);
+ pjsua_perror(THIS_FILE, "Error creating log file", status);
return status;
}
-
- status = pj_sock_bind_in(sock[1], 0, (pj_uint16_t)(rtp_port+1));
- if (status != PJ_SUCCESS) {
- pj_sock_close(sock[0]);
- sock[0] = PJ_INVALID_SOCKET;
-
- pj_sock_close(sock[1]);
- sock[1] = PJ_INVALID_SOCKET;
- continue;
- }
-
- /*
- * 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 (pjsua_has_stun()) {
- status=pj_stun_get_mapped_addr(&pjsua.cp.factory, 2, sock,
- &pjsua.config.stun_srv1,
- pjsua.config.stun_port1,
- &pjsua.config.stun_srv2,
- pjsua.config.stun_port2,
- mapped_addr);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "STUN resolve error", status);
- goto on_error;
- }
-
- if (pj_ntohs(mapped_addr[1].sin_port) ==
- pj_ntohs(mapped_addr[0].sin_port)+1)
- {
- /* Success! */
- break;
- }
-
- pj_sock_close(sock[0]);
- sock[0] = PJ_INVALID_SOCKET;
-
- pj_sock_close(sock[1]);
- sock[1] = PJ_INVALID_SOCKET;
-
- } else {
- const pj_str_t *hostname;
- pj_sockaddr_in addr;
-
- /* Get local IP address. */
- hostname = pj_gethostname();
-
- pj_memset( &addr, 0, sizeof(addr));
- addr.sin_family = PJ_AF_INET;
- status = pj_sockaddr_in_set_str_addr( &addr, hostname);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unresolvable local hostname",
- status);
- goto on_error;
- }
-
- for (i=0; i<2; ++i)
- pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
-
- mapped_addr[0].sin_port=pj_htons((pj_uint16_t)rtp_port);
- mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(rtp_port+1));
- break;
- }
}
- if (sock[0] == PJ_INVALID_SOCKET) {
- PJ_LOG(1,(THIS_FILE,
- "Unable to find appropriate RTP/RTCP ports combination"));
- goto on_error;
+ /* Unregister msg logging if it's previously registered */
+ if (pjsua_msg_logger.id >= 0) {
+ pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger);
+ pjsua_msg_logger.id = -1;
}
+ /* Enable SIP message logging */
+ if (pjsua_var.log_cfg.msg_logging)
+ pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger);
- skinfo->rtp_sock = sock[0];
- pj_memcpy(&skinfo->rtp_addr_name,
- &mapped_addr[0], sizeof(pj_sockaddr_in));
-
- skinfo->rtcp_sock = sock[1];
- pj_memcpy(&skinfo->rtcp_addr_name,
- &mapped_addr[1], sizeof(pj_sockaddr_in));
-
- PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d",
- pj_inet_ntoa(skinfo->rtp_addr_name.sin_addr),
- pj_ntohs(skinfo->rtp_addr_name.sin_port)));
- PJ_LOG(4,(THIS_FILE, "RTCP socket reachable at %s:%d",
- pj_inet_ntoa(skinfo->rtcp_addr_name.sin_addr),
- pj_ntohs(skinfo->rtcp_addr_name.sin_port)));
- rtp_port += 2;
return PJ_SUCCESS;
-
-on_error:
- for (i=0; i<2; ++i) {
- if (sock[i] != PJ_INVALID_SOCKET)
- pj_sock_close(sock[i]);
- }
- return status;
}
-
-/**
- * Create pjsua application.
- * This initializes pjlib/pjlib-util, and creates memory pool factory to
- * be used by application.
+/*****************************************************************************
+ * PJSUA Base API.
*/
-PJ_DEF(pj_status_t) pjsua_create(void)
-{
- pj_status_t status;
- /* Init PJLIB: */
-
- status = pj_init();
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "pj_init() error", status);
- return status;
- }
-
- /* Init PJLIB-UTIL: */
-
- status = pjlib_util_init();
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "pjlib_util_init() error", status);
- return status;
- }
-
- /* Init memory pool: */
-
- /* Init caching pool. */
- pj_caching_pool_init(&pjsua.cp, &pj_pool_factory_default_policy, 0);
-
- /* Create memory pool for application. */
- pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL);
+/* Worker thread function. */
+static int worker_thread(void *arg)
+{
+ enum { TIMEOUT = 10 };
- /* Must create endpoint to initialize SIP parser. */
- /* Create global endpoint: */
+ PJ_UNUSED_ARG(arg);
- status = pjsip_endpt_create(&pjsua.cp.factory,
- pj_gethostname()->ptr,
- &pjsua.endpt);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to create SIP endpoint", status);
- return status;
- }
+ while (!pjsua_var.thread_quit_flag) {
+ int count;
- /* Must create media endpoint too */
- status = pjmedia_endpt_create(&pjsua.cp.factory,
- pjsua.config.media_has_ioqueue? NULL :
- pjsip_endpt_get_ioqueue(pjsua.endpt),
- pjsua.config.media_thread_cnt,
- &pjsua.med_endpt);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE,
- "Media stack initialization has returned error",
- status);
- return status;
+ count = pjsua_handle_events(TIMEOUT);
+ if (count < 0)
+ pj_thread_sleep(TIMEOUT);
}
-
- return PJ_SUCCESS;
+ return 0;
}
-
/*
- * Init media.
+ * Instantiate pjsua application.
*/
-static pj_status_t init_media(void)
+PJ_DEF(pj_status_t) pjsua_create(void)
{
- int i;
- unsigned options;
- pj_str_t codec_id;
pj_status_t status;
- /* Register all codecs */
-#if PJMEDIA_HAS_SPEEX_CODEC
- /* Register speex. */
- status = pjmedia_codec_speex_init(pjsua.med_endpt,
- PJMEDIA_SPEEX_NO_UWB,
- pjsua.config.quality,
- pjsua.config.complexity );
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error initializing Speex codec",
- status);
- return status;
- }
-
- /* Set "speex/16000/1" to have highest priority */
- codec_id = pj_str("speex/16000/1");
- pjmedia_codec_mgr_set_codec_priority(
- pjmedia_endpt_get_codec_mgr(pjsua.med_endpt),
- &codec_id,
- PJMEDIA_CODEC_PRIO_HIGHEST);
-
-#endif /* PJMEDIA_HAS_SPEEX_CODEC */
-
-#if PJMEDIA_HAS_GSM_CODEC
- /* Register GSM */
- status = pjmedia_codec_gsm_init(pjsua.med_endpt);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error initializing GSM codec",
- status);
- return status;
- }
-#endif /* PJMEDIA_HAS_GSM_CODEC */
-
-#if PJMEDIA_HAS_G711_CODEC
- /* Register PCMA and PCMU */
- status = pjmedia_codec_g711_init(pjsua.med_endpt);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error initializing G711 codec",
- status);
- return status;
- }
-#endif /* PJMEDIA_HAS_G711_CODEC */
-
-#if PJMEDIA_HAS_L16_CODEC
- /* Register L16 family codecs, but disable all */
- status = pjmedia_codec_l16_init(pjsua.med_endpt, 0);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error initializing L16 codecs",
- status);
- return status;
- }
+ /* Init pjsua data */
+ init_data();
- /* Disable ALL L16 codecs */
- codec_id = pj_str("L16");
- pjmedia_codec_mgr_set_codec_priority(
- pjmedia_endpt_get_codec_mgr(pjsua.med_endpt),
- &codec_id,
- PJMEDIA_CODEC_PRIO_DISABLED);
+ /* Set default logging settings */
+ pjsua_logging_config_default(&pjsua_var.log_cfg);
-#endif /* PJMEDIA_HAS_L16_CODEC */
+ /* Init PJLIB: */
+ status = pj_init();
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- /* Enable those codecs that user put with "--add-codec", and move
- * the priority to top
- */
- for (i=0; i<(int)pjsua.config.codec_cnt; ++i) {
- pjmedia_codec_mgr_set_codec_priority(
- pjmedia_endpt_get_codec_mgr(pjsua.med_endpt),
- &pjsua.config.codec_arg[i],
- PJMEDIA_CODEC_PRIO_HIGHEST);
- }
+ /* Init PJLIB-UTIL: */
+ status = pjlib_util_init();
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- /* Init options for conference bridge. */
- options = PJMEDIA_CONF_NO_DEVICE;
+ /* Init caching pool. */
+ pj_caching_pool_init(&pjsua_var.cp, &pj_pool_factory_default_policy, 0);
- /* Calculate maximum number of ports, if it's not specified */
- if (pjsua.config.conf_ports == 0) {
- pjsua.config.conf_ports = 3 * pjsua.config.max_calls;
- }
+ /* Create memory pool for application. */
+ pjsua_var.pool = pjsua_pool_create("pjsua", 4000, 4000);
+
+ PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
- /* Init conference bridge. */
- pjsua.clock_rate = pjsua.config.clock_rate ? pjsua.config.clock_rate : 16000;
- pjsua.samples_per_frame = pjsua.clock_rate * 10 / 1000;
- status = pjmedia_conf_create(pjsua.pool,
- pjsua.config.conf_ports,
- pjsua.clock_rate,
- 1, /* mono */
- pjsua.samples_per_frame,
- 16,
- options,
- &pjsua.mconf);
+ /* Create mutex */
+ status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
+ &pjsua_var.mutex);
if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE,
- "Media stack initialization has returned error",
- status);
+ pjsua_perror(THIS_FILE, "Unable to create mutex", status);
return status;
}
- if (pjsua.config.null_audio == PJ_FALSE) {
- pjmedia_port *conf_port;
-
- /* Create sound device port */
- status = pjmedia_snd_port_create(pjsua.pool,
- pjsua.config.snd_capture_id,
- pjsua.config.snd_player_id,
- pjsua.clock_rate, 1 /* mono */,
- pjsua.samples_per_frame, 16,
- 0, &pjsua.snd_port);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to create sound device", status);
- return status;
- }
-
- /* Get the port interface of the conference bridge */
- conf_port = pjmedia_conf_get_master_port(pjsua.mconf);
-
- /* Connect conference port interface to sound port */
- pjmedia_snd_port_connect( pjsua.snd_port, conf_port);
-
- } else {
- pjmedia_port *null_port, *conf_port;
-
- /* Create NULL port */
- status = pjmedia_null_port_create(pjsua.pool, pjsua.clock_rate,
- 1, pjsua.samples_per_frame, 16,
- &null_port);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to create NULL port", status);
- return status;
- }
-
- /* Get the port interface of the conference bridge */
- conf_port = pjmedia_conf_get_master_port(pjsua.mconf);
-
- /* Create master port to control conference bridge's clock */
- status = pjmedia_master_port_create(pjsua.pool, null_port, conf_port,
- 0, &pjsua.master_port);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to create master port", status);
- return status;
- }
- }
-
- /* Create WAV file player if required: */
-
- if (pjsua.config.wav_file.slen) {
-
- status = pjsua_player_create(&pjsua.config.wav_file, NULL);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to create file player",
- status);
- return status;
- }
- }
-
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Copy account configuration.
- */
-static void copy_acc_config(pj_pool_t *pool,
- pjsua_acc_config *dst_acc,
- const pjsua_acc_config *src_acc)
-{
- unsigned j;
-
- pj_memcpy(dst_acc, src_acc, sizeof(pjsua_acc_config));
-
- pj_strdup_with_null(pool, &dst_acc->id, &src_acc->id);
- pj_strdup_with_null(pool, &dst_acc->reg_uri, &src_acc->reg_uri);
- pj_strdup_with_null(pool, &dst_acc->contact, &src_acc->contact);
- pj_strdup_with_null(pool, &dst_acc->proxy, &src_acc->proxy);
-
- for (j=0; j<src_acc->cred_count; ++j) {
- pj_strdup_with_null(pool, &dst_acc->cred_info[j].realm,
- &src_acc->cred_info[j].realm);
- pj_strdup_with_null(pool, &dst_acc->cred_info[j].scheme,
- &src_acc->cred_info[j].scheme);
- pj_strdup_with_null(pool, &dst_acc->cred_info[j].username,
- &src_acc->cred_info[j].username);
- pj_strdup_with_null(pool, &dst_acc->cred_info[j].data,
- &src_acc->cred_info[j].data);
- }
-}
-
-
-/*
- * Copy configuration.
- */
-void pjsua_copy_config( pj_pool_t *pool, pjsua_config *dst,
- const pjsua_config *src)
-{
- unsigned i;
-
- /* Plain memcpy */
- pj_memcpy(dst, src, sizeof(pjsua_config));
-
- /* Duplicate strings */
- pj_strdup_with_null(pool, &dst->sip_host, &src->sip_host);
- pj_strdup_with_null(pool, &dst->stun_srv1, &src->stun_srv1);
- pj_strdup_with_null(pool, &dst->stun_srv2, &src->stun_srv2);
- pj_strdup_with_null(pool, &dst->wav_file, &src->wav_file);
-
- for (i=0; i<src->codec_cnt; ++i) {
- pj_strdup_with_null(pool, &dst->codec_arg[i], &src->codec_arg[i]);
- }
-
- pj_strdup_with_null(pool, &dst->outbound_proxy, &src->outbound_proxy);
- //pj_strdup_with_null(pool, &dst->uri_to_call, &src->uri_to_call);
-
- for (i=0; i<src->acc_cnt; ++i) {
- pjsua_acc_config *dst_acc = &dst->acc_config[i];
- const pjsua_acc_config *src_acc = &src->acc_config[i];
- copy_acc_config(pool, dst_acc, src_acc);
- }
-
- pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename);
-
- for (i=0; i<src->buddy_cnt; ++i) {
- pj_strdup_with_null(pool, &dst->buddy_uri[i], &src->buddy_uri[i]);
- }
-}
-
-
-/*****************************************************************************
- * Console application custom logging:
- */
-
-
-static void log_writer(int level, const char *buffer, int len)
-{
- /* Write to both stdout and file. */
-
- if (level <= (int)pjsua.config.app_log_level)
- pj_log_write(level, buffer, len);
-
- if (pjsua.log_file) {
- fwrite(buffer, len, 1, pjsua.log_file);
- fflush(pjsua.log_file);
- }
-}
-
-
-static pj_status_t logging_init()
-{
- /* Redirect log function to ours */
-
- pj_log_set_log_func( &log_writer );
-
- /* If output log file is desired, create the file: */
-
- if (pjsua.config.log_filename.slen) {
- pjsua.log_file = fopen(pjsua.config.log_filename.ptr, "wt");
- if (pjsua.log_file == NULL) {
- PJ_LOG(1,(THIS_FILE, "Unable to open log file %s",
- pjsua.config.log_filename.ptr));
- return -1;
- }
- }
+ /* Must create SIP endpoint to initialize SIP parser. The parser
+ * is needed for example when application needs to call pjsua_verify_url().
+ */
+ status = pjsip_endpt_create(&pjsua_var.cp.factory,
+ pj_gethostname()->ptr,
+ &pjsua_var.endpt);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- /* Enable SIP message logging */
- if (pjsua.config.msg_logging)
- pjsip_endpt_register_module(pjsua.endpt, &pjsua_msg_logger);
return PJ_SUCCESS;
}
-static void logging_shutdown(void)
-{
- /* Close logging file, if any: */
-
- if (pjsua.log_file) {
- fclose(pjsua.log_file);
- pjsua.log_file = NULL;
- }
-}
-
-
/*
- * Initialize pjsua application.
- * This will initialize all libraries, create endpoint instance, and register
- * pjsip modules.
+ * Initialize pjsua with the specified settings. All the settings are
+ * optional, and the default values will be used when the config is not
+ * specified.
*/
-PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg,
- const pjsua_callback *cb)
+PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
+ const pjsua_logging_config *log_cfg,
+ const pjsua_media_config *media_cfg)
{
- char errmsg[80];
- unsigned i;
+ pjsua_config default_cfg;
+ pjsua_media_config default_media_cfg;
pj_status_t status;
- /* Init accounts: */
- for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
- pjsua.acc[i].index = i;
- pjsua.acc[i].online_status = PJ_TRUE;
- pj_list_init(&pjsua.acc[i].route_set);
- pj_list_init(&pjsua.acc[i].pres_srv_list);
- }
-
- /* Init call array: */
- for (i=0; i<PJ_ARRAY_SIZE(pjsua.calls); ++i) {
- pjsua.calls[i].index = i;
- pjsua.calls[i].refresh_tm._timer_id = -1;
- pjsua.calls[i].hangup_tm._timer_id = -1;
- pjsua.calls[i].conf_slot = 0;
- }
-
- /* Init buddies array */
- for (i=0; i<PJ_ARRAY_SIZE(pjsua.buddies); ++i) {
- pjsua.buddies[i].index = i;
- }
-
- /* Copy configuration */
- pjsua_copy_config(pjsua.pool, &pjsua.config, cfg);
-
- /* Copy callback */
- pj_memcpy(&pjsua.cb, cb, sizeof(pjsua_callback));
+ /* Create default configurations when the config is not supplied */
- /* Test configuration */
- if (pjsua_test_config(&pjsua.config, errmsg, sizeof(errmsg))) {
- PJ_LOG(1,(THIS_FILE, "Error in configuration: %s", errmsg));
- status = -1;
- goto on_error;
+ if (ua_cfg == NULL) {
+ pjsua_config_default(&default_cfg);
+ ua_cfg = &default_cfg;
}
-
- /* Init PJLIB logging: */
-
- pj_log_set_level(pjsua.config.log_level);
- pj_log_set_decor(pjsua.config.log_decor);
-
- status = logging_init();
- if (status != PJ_SUCCESS)
- goto on_error;
-
-
- /* Create SIP UDP socket */
- if (pjsua.config.udp_port) {
-
- status = create_sip_udp_sock( pjsua.config.udp_port,
- &pjsua.sip_sock,
- &pjsua.sip_sock_name);
- if (status != PJ_SUCCESS)
- goto on_error;
-
- pj_strdup2_with_null(pjsua.pool, &pjsua.config.sip_host,
- pj_inet_ntoa(pjsua.sip_sock_name.sin_addr));
- pjsua.config.sip_port = pj_ntohs(pjsua.sip_sock_name.sin_port);
-
- } else {
-
- /* Check that SIP host and port is configured */
- if (cfg->sip_host.slen == 0 || cfg->sip_port == 0) {
- PJ_LOG(1,(THIS_FILE,
- "Error: sip_host and sip_port must be specified"));
- status = PJ_EINVAL;
- goto on_error;
- }
-
- pjsua.sip_sock = PJ_INVALID_SOCKET;
+ if (media_cfg == NULL) {
+ pjsua_media_config_default(&default_media_cfg);
+ media_cfg = &default_media_cfg;
}
-
- /* Init media endpoint */
- status = init_media();
- if (status != PJ_SUCCESS)
- goto on_error;
-
-
- /* Init RTP sockets, only when UDP transport is enabled */
- for (i=0; pjsua.config.start_rtp_port && i<pjsua.config.max_calls; ++i) {
- status = create_rtp_rtcp_sock(&pjsua.calls[i].skinfo);
- if (status != PJ_SUCCESS) {
- unsigned j;
- for (j=0; j<i; ++j) {
- if (pjsua.calls[i].med_tp)
- pjsua.calls[i].med_tp->op->destroy(pjsua.calls[i].med_tp);
- }
- goto on_error;
- }
- status = pjmedia_transport_udp_attach(pjsua.med_endpt, NULL,
- &pjsua.calls[i].skinfo, 0,
- &pjsua.calls[i].med_tp);
+ /* Initialize logging first so that info/errors can be captured */
+ if (log_cfg) {
+ status = pjsua_reconfigure_logging(log_cfg);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
}
- /* Init PJSIP : */
+ /* Init SIP UA: */
/* Initialize transaction layer: */
+ status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- status = pjsip_tsx_layer_init_module(pjsua.endpt);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Transaction layer initialization error",
- status);
- goto on_error;
- }
/* Initialize UA layer module: */
+ status = pjsip_ua_init_module( pjsua_var.endpt, NULL );
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- status = pjsip_ua_init_module( pjsua.endpt, NULL );
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "UA layer initialization error", status);
- goto on_error;
- }
-
- /* Initialize and register pjsua's application module: */
+ /* Initialize and register PJSUA application module. */
{
- pjsip_module my_mod =
+ const pjsip_module mod_initializer =
{
NULL, NULL, /* prev, next. */
{ "mod-pjsua", 9 }, /* Name. */
@@ -981,47 +383,70 @@ PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg,
NULL, /* on_tsx_state() */
};
- pjsua.mod = my_mod;
+ pjsua_var.mod = mod_initializer;
- status = pjsip_endpt_register_module(pjsua.endpt, &pjsua.mod);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to register pjsua module",
- status);
- goto on_error;
- }
+ status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
}
- /* Initialize invite session module: */
+
- status = pjsua_call_init();
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Invite usage initialization error",
- status);
+ /* Initialize PJSUA call subsystem: */
+ status = pjsua_call_subsys_init(ua_cfg);
+ if (status != PJ_SUCCESS)
goto on_error;
- }
+
+
+ /* Initialize PJSUA media subsystem */
+ status = pjsua_media_subsys_init(media_cfg);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
/* Init core SIMPLE module : */
+ status = pjsip_evsub_init_module(pjsua_var.endpt);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pjsip_evsub_init_module(pjsua.endpt);
/* Init presence module: */
+ status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance());
/* Init xfer/REFER module */
-
- pjsip_xfer_init_module( pjsua.endpt );
+ status = pjsip_xfer_init_module( pjsua_var.endpt );
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Init pjsua presence handler: */
-
- pjsua_pres_init();
+ status = pjsua_pres_init();
+ if (status != PJ_SUCCESS)
+ goto on_error;
/* Init out-of-dialog MESSAGE request handler. */
+ status = pjsua_im_init();
+ if (status != PJ_SUCCESS)
+ goto on_error;
- pjsua_im_init();
+ /* Start worker thread if needed. */
+ if (pjsua_var.ua_cfg.thread_cnt) {
+ unsigned i;
+ if (pjsua_var.ua_cfg.thread_cnt > PJ_ARRAY_SIZE(pjsua_var.thread))
+ pjsua_var.ua_cfg.thread_cnt = PJ_ARRAY_SIZE(pjsua_var.thread);
+
+ for (i=0; i<pjsua_var.ua_cfg.thread_cnt; ++i) {
+ status = pj_thread_create(pjsua_var.pool, "pjsua", &worker_thread,
+ NULL, 0, 0, &pjsua_var.thread[i]);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+ }
+ }
+
+ /* Done! */
+
+ PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
+ PJ_VERSION, PJ_OS_NAME));
- /* Done. */
return PJ_SUCCESS;
on_error:
@@ -1030,836 +455,600 @@ on_error:
}
-/*
- * Find account for incoming request.
- */
-PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata)
+/* Sleep with polling */
+static void busy_sleep(unsigned msec)
{
- pjsip_uri *uri;
- pjsip_sip_uri *sip_uri;
- unsigned acc_index;
-
- uri = rdata->msg_info.to->uri;
-
- /* Just return default account if To URI is not SIP: */
- if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
- !PJSIP_URI_SCHEME_IS_SIPS(uri))
- {
- return pjsua.default_acc;
- }
-
-
- sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
-
- /* Find account which has matching username and domain. */
- for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) {
-
- pjsua_acc *acc = &pjsua.acc[acc_index];
-
- if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
- pj_stricmp(&acc->host_part, &sip_uri->host)==0)
- {
- /* Match ! */
- return acc_index;
- }
- }
-
- /* No matching, try match domain part only. */
- for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) {
-
- pjsua_acc *acc = &pjsua.acc[acc_index];
+ pj_time_val timeout, now;
- if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) {
- /* Match ! */
- return acc_index;
- }
- }
+ pj_gettimeofday(&timeout);
+ timeout.msec += msec;
+ pj_time_val_normalize(&timeout);
- /* Still no match, use default account */
- return pjsua.default_acc;
+ do {
+ while (pjsua_handle_events(10) > 0)
+ ;
+ pj_gettimeofday(&now);
+ } while (PJ_TIME_VAL_LT(now, timeout));
}
-
/*
- * Find account for outgoing request.
+ * Destroy pjsua.
*/
-PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_outgoing(const pj_str_t *str_url)
+PJ_DEF(pj_status_t) pjsua_destroy(void)
{
- pj_str_t tmp;
- pjsip_uri *uri;
- pjsip_sip_uri *sip_uri;
- unsigned i;
-
- pj_strdup_with_null(pjsua.pool, &tmp, str_url);
+ int i; /* Must be signed */
- uri = pjsip_parse_uri(pjsua.pool, tmp.ptr, tmp.slen, 0);
- if (!uri)
- return pjsua.config.acc_cnt-1;
+ /* Signal threads to quit: */
+ pjsua_var.thread_quit_flag = 1;
- if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
- !PJSIP_URI_SCHEME_IS_SIPS(uri))
- {
- /* Return the first account with proxy */
- for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
- if (!pjsua.acc[i].valid)
- continue;
- if (pjsua.config.acc_config[i].proxy.slen)
- break;
+ /* Wait worker threads to quit: */
+ for (i=0; i<(int)pjsua_var.ua_cfg.thread_cnt; ++i) {
+ if (pjsua_var.thread[i]) {
+ pj_thread_join(pjsua_var.thread[i]);
+ pj_thread_destroy(pjsua_var.thread[i]);
+ pjsua_var.thread[i] = NULL;
}
-
- if (i != PJ_ARRAY_SIZE(pjsua.acc))
- return i;
-
- /* Not found, use default account */
- return pjsua.default_acc;
- }
-
- sip_uri = pjsip_uri_get_uri(uri);
-
- /* Find matching domain */
- for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
- if (!pjsua.acc[i].valid)
- continue;
- if (pj_stricmp(&pjsua.acc[i].host_part, &sip_uri->host)==0)
- break;
}
+
+ if (pjsua_var.endpt) {
+ /* Terminate all calls. */
+ pjsua_call_hangup_all();
- if (i != PJ_ARRAY_SIZE(pjsua.acc))
- return i;
+ /* Terminate all presence subscriptions. */
+ pjsua_pres_shutdown();
- /* Just use default account */
- return pjsua.default_acc;
-}
+ /* Unregister, if required: */
+ for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
+ if (!pjsua_var.acc[i].valid)
+ continue;
+ if (pjsua_var.acc[i].regc) {
+ pjsua_acc_set_registration(i, PJ_FALSE);
+ }
+ }
-/*
- * Init account
- */
-static pj_status_t init_acc(unsigned acc_index)
-{
- pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index];
- pjsua_acc *acc = &pjsua.acc[acc_index];
- pjsip_uri *uri;
- pjsip_sip_uri *sip_uri;
-
- /* Need to parse local_uri to get the elements: */
-
- uri = pjsip_parse_uri(pjsua.pool, acc_cfg->id.ptr,
- acc_cfg->id.slen, 0);
- if (uri == NULL) {
- pjsua_perror(THIS_FILE, "Invalid local URI",
- PJSIP_EINVALIDURI);
- return PJSIP_EINVALIDURI;
+ /* Wait for some time to allow unregistration to complete: */
+ PJ_LOG(4,(THIS_FILE, "Shutting down..."));
+ busy_sleep(1000);
}
- /* Local URI MUST be a SIP or SIPS: */
+ /* Destroy media */
+ pjsua_media_subsys_destroy();
- if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
- !PJSIP_URI_SCHEME_IS_SIPS(uri))
- {
- pjsua_perror(THIS_FILE, "Invalid local URI",
- PJSIP_EINVALIDSCHEME);
- return PJSIP_EINVALIDSCHEME;
+ /* Destroy endpoint. */
+ if (pjsua_var.endpt) {
+ pjsip_endpt_destroy(pjsua_var.endpt);
+ pjsua_var.endpt = NULL;
}
-
- /* Get the SIP URI object: */
-
- sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
-
- acc->user_part = sip_uri->user;
- acc->host_part = sip_uri->host;
-
- /* Build Contact header */
-
- if (acc_cfg->contact.slen == 0) {
- char contact[128];
- const char *addr;
- int port;
- int len;
-
- addr = pjsua.config.sip_host.ptr;
- port = pjsua.config.sip_port;
-
- /* The local Contact is the username@ip-addr, where
- * - username is taken from the local URI,
- * - ip-addr in UDP transport's address name (which may have been
- * resolved from STUN.
- */
-
- /* Build temporary contact string. */
-
- if (sip_uri->user.slen) {
-
- /* With the user part. */
- len = pj_ansi_snprintf(contact, sizeof(contact),
- "<sip:%.*s@%s:%d>",
- (int)sip_uri->user.slen,
- sip_uri->user.ptr,
- addr, port);
- } else {
-
- /* Without user part */
-
- len = pj_ansi_snprintf(contact, sizeof(contact),
- "<sip:%s:%d>",
- addr, port);
- }
-
- if (len < 1 || len >= sizeof(contact)) {
- pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG);
- return PJSIP_EURITOOLONG;
- }
-
- /* Duplicate Contact uri. */
-
- pj_strdup2(pjsua.pool, &acc_cfg->contact, contact);
-
+ /* Destroy mutex */
+ if (pjsua_var.mutex) {
+ pj_mutex_destroy(pjsua_var.mutex);
+ pjsua_var.mutex = NULL;
}
-
- /* Build route-set for this account */
- if (pjsua.config.outbound_proxy.slen) {
- pj_str_t hname = { "Route", 5};
- pjsip_route_hdr *r;
- pj_str_t tmp;
-
- pj_strdup_with_null(pjsua.pool, &tmp, &pjsua.config.outbound_proxy);
- r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
- pj_list_push_back(&acc->route_set, r);
+ /* Destroy pool and pool factory. */
+ if (pjsua_var.pool) {
+ pj_pool_release(pjsua_var.pool);
+ pjsua_var.pool = NULL;
+ pj_caching_pool_destroy(&pjsua_var.cp);
}
- if (acc_cfg->proxy.slen) {
- pj_str_t hname = { "Route", 5};
- pjsip_route_hdr *r;
- pj_str_t tmp;
- pj_strdup_with_null(pjsua.pool, &tmp, &acc_cfg->proxy);
- r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
- pj_list_push_back(&acc->route_set, r);
- }
-
- /* Mark account as valid */
- pjsua.acc[acc_index].valid = PJ_TRUE;
+ PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
+ /* End logging */
+ if (pjsua_var.log_file) {
+ pj_file_close(pjsua_var.log_file);
+ pjsua_var.log_file = NULL;
+ }
+ /* Done. */
return PJ_SUCCESS;
}
-/*
- * Add a new account.
+
+/**
+ * Application is recommended to call this function after all initialization
+ * is done, so that the library can do additional checking set up
+ * additional
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
*/
-PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
- pjsua_acc_id *acc_index)
+PJ_DEF(pj_status_t) pjsua_start(void)
{
- unsigned index;
pj_status_t status;
- PJ_ASSERT_RETURN(pjsua.config.acc_cnt <
- PJ_ARRAY_SIZE(pjsua.config.acc_config),
- PJ_ETOOMANY);
-
- /* Find empty account index. */
- for (index=0; index < PJ_ARRAY_SIZE(pjsua.acc); ++index) {
- if (pjsua.acc[index].valid == PJ_FALSE)
- break;
- }
-
- /* Expect to find a slot */
- PJ_ASSERT_RETURN(index < PJ_ARRAY_SIZE(pjsua.acc), PJ_EBUG);
-
- copy_acc_config(pjsua.pool, &pjsua.config.acc_config[index], cfg);
-
- status = init_acc(index);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error adding account", status);
+ status = pjsua_call_subsys_start();
+ if (status != PJ_SUCCESS)
return status;
- }
- if (acc_index)
- *acc_index = index;
+ status = pjsua_media_subsys_start();
+ if (status != PJ_SUCCESS)
+ return status;
- pjsua.config.acc_cnt++;
+ status = pjsua_pres_start();
+ if (status != PJ_SUCCESS)
+ return status;
return PJ_SUCCESS;
}
-/*
- * Delete account.
+/**
+ * Poll pjsua for events, and if necessary block the caller thread for
+ * the specified maximum interval (in miliseconds).
*/
-PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_index)
+PJ_DEF(int) pjsua_handle_events(unsigned msec_timeout)
{
- PJ_ASSERT_RETURN(acc_index < (int)pjsua.config.acc_cnt,
- PJ_EINVAL);
- PJ_ASSERT_RETURN(pjsua.acc[acc_index].valid, PJ_EINVALIDOP);
+ unsigned count = 0;
+ pj_time_val tv;
+ pj_status_t status;
+
+ tv.sec = 0;
+ tv.msec = msec_timeout;
+ pj_time_val_normalize(&tv);
- /* Delete registration */
- if (pjsua.acc[acc_index].regc != NULL)
- pjsua_acc_set_registration(acc_index, PJ_FALSE);
+ status = pjsip_endpt_handle_events2(pjsua_var.endpt, &tv, &count);
- /* Invalidate */
- pjsua.acc[acc_index].valid = PJ_FALSE;
+ if (status != PJ_SUCCESS)
+ return -status;
- return PJ_SUCCESS;
+ return count;
}
/*
- * Start pjsua stack.
- * This will start the registration process, if registration is configured.
+ * Create memory pool.
*/
-PJ_DEF(pj_status_t) pjsua_start(void)
+PJ_DEF(pj_pool_t*) pjsua_pool_create( const char *name, pj_size_t init_size,
+ pj_size_t increment)
{
- int i; /* Must be signed */
- unsigned count;
- pj_status_t status = PJ_SUCCESS;
-
-
- /* Add UDP transport: */
- if (pjsua.sip_sock > 0) {
+ /* Pool factory is thread safe, no need to lock */
+ return pj_pool_create(&pjsua_var.cp.factory, name, init_size, increment,
+ NULL);
+}
- /* Init the published name for the transport.
- * Depending whether STUN is used, this may be the STUN mapped
- * address, or socket's bound address.
- */
- pjsip_host_port addr_name;
- addr_name.host = pjsua.config.sip_host;
- addr_name.port = pjsua.config.sip_port;
+/*
+ * Internal function to get SIP endpoint instance of pjsua, which is
+ * needed for example to register module, create transports, etc.
+ * Probably is only valid after #pjsua_init() is called.
+ */
+PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
+{
+ return pjsua_var.endpt;
+}
- /* Create UDP transport from previously created UDP socket: */
+/*
+ * Internal function to get media endpoint instance.
+ * Only valid after #pjsua_init() is called.
+ */
+PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
+{
+ return pjsua_var.med_endpt;
+}
- status = pjsip_udp_transport_attach( pjsua.endpt, pjsua.sip_sock,
- &addr_name, 1,
- NULL);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to start UDP transport",
- status);
- goto on_error;
- }
- }
- /* Initialize all unused accounts with default id and contact.
- */
- {
- char buf[80];
- pj_str_t tmp, id;
+/*****************************************************************************
+ * PJSUA SIP Transport API.
+ */
- tmp.ptr = buf;
- tmp.slen = pj_ansi_sprintf(tmp.ptr, "Local <sip:%s:%d>",
- pjsua.config.sip_host.ptr,
- pjsua.config.sip_port);
- pj_strdup_with_null( pjsua.pool, &id, &tmp);
+/*
+ * Create and initialize SIP socket (and possibly resolve public
+ * address via STUN, depending on config).
+ */
+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;
+ pj_sock_t sock;
+ pj_status_t status;
- for (i=pjsua.config.acc_cnt; i<PJ_ARRAY_SIZE(pjsua.config.acc_config);
- ++i)
- {
- pjsua_acc_config *acc_cfg =
- &pjsua.config.acc_config[pjsua.config.acc_cnt];
+ PJSUA_LOCK();
- acc_cfg->id = id;
- acc_cfg->contact = id;
- }
+ status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "socket() error", status);
+ goto on_return;
}
-
- /* Add another account as the last one.
- * This account corresponds to local endpoint, and is user-less.
- * This is also the default account.
- */
- if (pjsua.config.acc_cnt < PJ_ARRAY_SIZE(pjsua.config.acc_config)) {
- pjsua.default_acc = pjsua.config.acc_cnt;
- pjsua.acc[pjsua.default_acc].auto_gen = 1;
- pjsua.config.acc_cnt++;
+ status = pj_sock_bind_in(sock, bound_addr.s_addr, (pj_uint16_t)port);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "bind() error", status);
+ pj_sock_close(sock);
+ goto on_return;
}
-
- /* Initialize accounts: */
- for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
- status = init_acc(i);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error initializing account", status);
- goto on_error;
- }
+ /* Copy and normalize STUN param */
+ if (use_stun) {
+ pj_memcpy(&stun, stun_param, sizeof(*stun_param));
+ pjsua_normalize_stun_config(&stun);
+ } else {
+ pj_memset(&stun, 0, sizeof(pjsua_stun_config));
}
-
- /* Create worker thread(s), if required: */
-
- for (i=0; i<(int)pjsua.config.thread_cnt; ++i) {
- status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_poll,
- NULL, 0, 0, &pjsua.threads[i]);
+ /* Get the published address, either by STUN or by resolving
+ * the name of local host.
+ */
+ if (stun.stun_srv1.slen) {
+ status = pj_stun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
+ &stun.stun_srv1,
+ stun.stun_port1,
+ &stun.stun_srv2,
+ stun.stun_port2,
+ p_pub_addr);
if (status != PJ_SUCCESS) {
- pjsua.quit_flag = 1;
- for (--i; i>=0; --i) {
- pj_thread_join(pjsua.threads[i]);
- pj_thread_destroy(pjsua.threads[i]);
- }
- goto on_error;
+ pjsua_perror(THIS_FILE, "Error resolving with STUN", status);
+ pj_sock_close(sock);
+ goto on_return;
}
- }
- /* Start registration: */
+ } else {
- /* Create client registration session: */
- for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
- status = pjsua_regc_init(i);
- if (status != PJ_SUCCESS)
- goto on_error;
+ const pj_str_t *hostname = pj_gethostname();
+ struct pj_hostent he;
- /* Perform registration, if required. */
- if (pjsua.acc[i].regc) {
- pjsua_acc_set_registration(i, PJ_TRUE);
+ status = pj_gethostbyname(hostname, &he);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to resolve local host", status);
+ pj_sock_close(sock);
+ goto on_return;
}
- }
-
- /* Re-init buddies */
- count = pjsua.config.buddy_cnt;
- pjsua.config.buddy_cnt = 0;
- for (i=0; i<(int)count; ++i) {
- pj_str_t uri = pjsua.config.buddy_uri[i];
- pjsua_buddy_add(&uri, NULL);
+ pj_memset(p_pub_addr, 0, sizeof(pj_sockaddr_in));
+ p_pub_addr->sin_family = PJ_AF_INET;
+ p_pub_addr->sin_port = pj_htons((pj_uint16_t)port);
+ p_pub_addr->sin_addr = *(pj_in_addr*)he.h_addr;
}
+ *p_sock = sock;
- /* Refresh presence */
- pjsua_pres_refresh();
+on_return:
+ PJSUA_UNLOCK();
- PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION));
- return PJ_SUCCESS;
+ PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
+ pj_inet_ntoa(p_pub_addr->sin_addr),
+ (int)pj_ntohs(p_pub_addr->sin_port)));
-on_error:
- pjsua_destroy();
return status;
}
-/* Sleep with polling */
-static void busy_sleep(unsigned msec)
+/*
+ * Create SIP transport.
+ */
+PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type,
+ const pjsua_transport_config *cfg,
+ pjsua_transport_id *p_id)
{
- pj_time_val timeout, now;
+ pjsip_transport *tp;
+ unsigned id;
+ pj_status_t status;
- pj_gettimeofday(&timeout);
- timeout.msec += msec;
- pj_time_val_normalize(&timeout);
+ PJSUA_LOCK();
- do {
- pjsua_poll(NULL);
- pj_gettimeofday(&now);
- } while (PJ_TIME_VAL_LT(now, timeout));
-}
-
-/**
- * Get maxinum number of conference ports.
- */
-PJ_DEF(unsigned) pjsua_conf_max_ports(void)
-{
- return pjsua.config.conf_ports;
-}
+ /* Find empty transport slot */
+ for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
+ if (pjsua_var.tpdata[id].tp == NULL)
+ break;
+ }
+ if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
+ status = PJ_ETOOMANY;
+ pjsua_perror(THIS_FILE, "Error creating transport", status);
+ goto on_return;
+ }
-/**
- * Enum all conference port ID.
- */
-PJ_DEF(pj_status_t) pjsua_conf_enum_port_ids( pjsua_conf_port_id id[],
- unsigned *count)
-{
- return pjmedia_conf_enum_ports( pjsua.mconf, (unsigned*)id, count);
-}
+ /* Create the transport */
+ if (type == PJSIP_TRANSPORT_UDP) {
+ pjsua_transport_config config;
+ pj_sock_t sock;
+ pj_sockaddr_in pub_addr;
+ pjsip_host_port addr_name;
+ /* Supply default config if it's not specified */
+ if (cfg == NULL) {
+ pjsua_transport_config_default(&config);
+ cfg = &config;
+ }
-/**
- * Get information about the specified conference port
- */
-PJ_DEF(pj_status_t) pjsua_conf_get_port_info( pjsua_conf_port_id id,
- pjsua_conf_port_info *info)
-{
- pjmedia_conf_port_info cinfo;
- unsigned i, count;
- pj_status_t status;
+ /* Create the socket and possibly resolve the address with STUN */
+ status = create_sip_udp_sock(cfg->ip_addr, cfg->port, cfg->use_stun,
+ &cfg->stun_config, &sock, &pub_addr);
+ if (status != PJ_SUCCESS)
+ goto on_return;
- status = pjmedia_conf_get_port_info( pjsua.mconf, id, &cinfo);
- if (status != PJ_SUCCESS)
- return status;
+ addr_name.host = pj_str(pj_inet_ntoa(pub_addr.sin_addr));
+ addr_name.port = pj_ntohs(pub_addr.sin_port);
- pj_memset(info, 0, sizeof(*info));
- info->slot_id = id;
- info->name = cinfo.name;
- info->clock_rate = cinfo.clock_rate;
- info->channel_count = cinfo.channel_count;
- info->samples_per_frame = cinfo.samples_per_frame;
- info->bits_per_sample = cinfo.bits_per_sample;
-
- /* Build array of listeners */
- count = pjsua.config.conf_ports;
- for (i=0; i<count; ++i) {
- if (cinfo.listener[i]) {
- info->listeners[info->listener_cnt++] = i;
+ /* Create UDP transport */
+ status = pjsip_udp_transport_attach( pjsua_var.endpt, sock,
+ &addr_name, 1,
+ &tp);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error creating SIP UDP transport",
+ status);
+ pj_sock_close(sock);
+ goto on_return;
}
+
+ } else {
+ status = PJSIP_EUNSUPTRANSPORT;
+ pjsua_perror(THIS_FILE, "Error creating transport", status);
+ goto on_return;
}
- return PJ_SUCCESS;
-}
+ /* Save the transport */
+ pjsua_var.tpdata[id].tp = tp;
+ /* Return the ID */
+ if (p_id) *p_id = id;
-/**
- * Connect conference port.
- */
-PJ_DEF(pj_status_t) pjsua_conf_connect( pjsua_conf_port_id src_port,
- pjsua_conf_port_id dst_port)
-{
- return pjmedia_conf_connect_port(pjsua.mconf, src_port, dst_port, 0);
-}
+ status = PJ_SUCCESS;
+on_return:
-/**
- * Connect conference port connection.
- */
-PJ_DEF(pj_status_t) pjsua_conf_disconnect( pjsua_conf_port_id src_port,
- pjsua_conf_port_id dst_port)
-{
- return pjmedia_conf_disconnect_port(pjsua.mconf, src_port, dst_port);
-}
+ PJSUA_UNLOCK();
+ return PJ_SUCCESS;
+}
-/**
- * Create a file player.
+/*
+ * Register transport that has been created by application.
*/
-PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename,
- pjsua_player_id *id)
+PJ_DEF(pj_status_t) pjsua_transport_register( pjsip_transport *tp,
+ pjsua_transport_id *p_id)
{
- unsigned slot;
- char path[128];
- pjmedia_port *port;
- pj_status_t status;
+ unsigned id;
- if (pjsua.player_cnt >= PJ_ARRAY_SIZE(pjsua.player))
- return PJ_ETOOMANY;
+ PJSUA_LOCK();
- pj_memcpy(path, filename->ptr, filename->slen);
- path[filename->slen] = '\0';
- status = pjmedia_wav_player_port_create(pjsua.pool, path,
- pjsua.samples_per_frame *
- 1000 / pjsua.clock_rate,
- 0, 0, NULL,
- &port);
- if (status != PJ_SUCCESS)
- return status;
+ /* Find empty transport slot */
+ for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.tpdata); ++id) {
+ if (pjsua_var.tpdata[id].tp == NULL)
+ break;
+ }
- status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool,
- port, filename, &slot);
- if (status != PJ_SUCCESS) {
- pjmedia_port_destroy(port);
- return status;
+ if (id == PJ_ARRAY_SIZE(pjsua_var.tpdata)) {
+ pjsua_perror(THIS_FILE, "Error creating transport", PJ_ETOOMANY);
+ PJSUA_UNLOCK();
+ return PJ_ETOOMANY;
}
- pjsua.player[pjsua.player_cnt].port = port;
- pjsua.player[pjsua.player_cnt].slot = slot;
+ /* Save the transport */
+ pjsua_var.tpdata[id].tp = tp;
- if (id)
- *id = pjsua.player_cnt;
+ /* Return the ID */
+ if (p_id) *p_id = id;
- ++pjsua.player_cnt;
+ PJSUA_UNLOCK();
return PJ_SUCCESS;
}
-/**
- * Get conference port associated with player.
+/*
+ * Enumerate all transports currently created in the system.
*/
-PJ_DEF(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id)
+PJ_DEF(pj_status_t) pjsua_enum_transports( pjsua_transport_id id[],
+ unsigned *p_count )
{
- PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);
- return pjsua.player[id].slot;
-}
-
+ unsigned i, count;
-/**
- * Re-wind playback.
- */
-PJ_DEF(pj_status_t) pjsua_player_set_pos(pjsua_player_id id,
- pj_uint32_t samples)
-{
- PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);
- PJ_ASSERT_RETURN(pjsua.player[id].port != NULL, PJ_EINVALIDOP);
+ PJSUA_LOCK();
- return pjmedia_wav_player_port_set_pos(pjsua.player[id].port, samples);
-}
+ for (i=0, count=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata) && count<*p_count;
+ ++i)
+ {
+ if (!pjsua_var.tpdata[i].tp)
+ continue;
+ id[count++] = i;
+ }
-/**
- * Get conference port associated with player.
- */
-PJ_DEF(pj_status_t) pjsua_player_destroy(pjsua_player_id id)
-{
- PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);
+ *p_count = count;
- if (pjsua.player[id].port) {
- pjmedia_port_destroy(pjsua.player[id].port);
- pjsua.player[id].port = NULL;
- pjsua.player[id].slot = 0xFFFF;
- pjsua.player_cnt--;
- }
+ PJSUA_UNLOCK();
return PJ_SUCCESS;
}
-/**
- * Create a file recorder.
+/*
+ * Get information about transports.
*/
-PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename,
- pjsua_recorder_id *id)
+PJ_DEF(pj_status_t) pjsua_transport_get_info( pjsua_transport_id id,
+ pjsua_transport_info *info)
{
- unsigned slot;
- char path[128];
- pjmedia_port *port;
- pj_status_t status;
+ pjsip_transport *tp;
- if (pjsua.recorder_cnt >= PJ_ARRAY_SIZE(pjsua.recorder))
- return PJ_ETOOMANY;
-
- pj_memcpy(path, filename->ptr, filename->slen);
- path[filename->slen] = '\0';
- status = pjmedia_wav_writer_port_create(pjsua.pool, path,
- pjsua.clock_rate, 1,
- pjsua.samples_per_frame,
- 16, 0, 0, NULL,
- &port);
- if (status != PJ_SUCCESS)
- return status;
-
- status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool,
- port, filename, &slot);
- if (status != PJ_SUCCESS) {
- pjmedia_port_destroy(port);
- return status;
- }
+ pj_memset(info, 0, sizeof(*info));
- pjsua.recorder[pjsua.recorder_cnt].port = port;
- pjsua.recorder[pjsua.recorder_cnt].slot = slot;
+ /* Make sure id is in range. */
+ PJ_ASSERT_RETURN(id>=0 && id<PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL);
- if (*id)
- *id = pjsua.recorder_cnt;
+ /* Make sure that transport exists */
+ PJ_ASSERT_RETURN(pjsua_var.tpdata[id].tp != NULL, PJ_EINVAL);
- ++pjsua.recorder_cnt;
+ PJSUA_LOCK();
- return PJ_SUCCESS;
+ tp = pjsua_var.tpdata[id].tp;
+ if (tp == NULL) {
+ PJSUA_UNLOCK();
+ return PJ_EINVALIDOP;
+ }
+
+ info->id = id;
+ info->type = tp->key.type;
+ info->type_name = pj_str(tp->type_name);
+ info->info = pj_str(tp->info);
+ info->flag = tp->flag;
+ info->addr_len = tp->addr_len;
+ info->local_addr = tp->local_addr;
+ info->local_name = tp->local_name;
+ info->usage_count = pj_atomic_get(tp->ref_cnt);
+
+ PJSUA_UNLOCK();
+
+ return PJ_EINVALIDOP;
}
-/**
- * Get conference port associated with recorder.
+/*
+ * Disable a transport or re-enable it.
*/
-PJ_DEF(pjsua_conf_port_id) pjsua_recorder_get_conf_port(pjsua_recorder_id id)
+PJ_DEF(pj_status_t) pjsua_transport_set_enable( pjsua_transport_id id,
+ pj_bool_t enabled)
{
- PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.recorder), PJ_EINVAL);
- return pjsua.recorder[id].slot;
-}
+ /* Make sure id is in range. */
+ PJ_ASSERT_RETURN(id>=0 && id<PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL);
+ /* Make sure that transport exists */
+ PJ_ASSERT_RETURN(pjsua_var.tpdata[id].tp != NULL, PJ_EINVAL);
-/**
- * Destroy recorder (will complete recording).
- */
-PJ_DEF(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id)
-{
- PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.recorder), PJ_EINVAL);
- if (pjsua.recorder[id].port) {
- pjmedia_port_destroy(pjsua.recorder[id].port);
- pjsua.recorder[id].port = NULL;
- pjsua.recorder[id].slot = 0xFFFF;
- pjsua.recorder_cnt--;
- }
+ /* To be done!! */
+ PJ_TODO(pjsua_transport_set_enable);
- return PJ_SUCCESS;
+ return PJ_EINVALIDOP;
}
-/**
- * Enum sound devices.
+
+/*
+ * Close the transport.
*/
-PJ_DEF(pj_status_t) pjsua_enum_snd_devices( unsigned *count,
- pjmedia_snd_dev_info info[])
+PJ_DEF(pj_status_t) pjsua_transport_close( pjsua_transport_id id,
+ pj_bool_t force )
{
- int i, dev_count;
+ /* Make sure id is in range. */
+ PJ_ASSERT_RETURN(id>=0 && id<PJ_ARRAY_SIZE(pjsua_var.tpdata), PJ_EINVAL);
- dev_count = pjmedia_snd_get_dev_count();
- if (dev_count > (int)*count)
- dev_count = *count;
+ /* Make sure that transport exists */
+ PJ_ASSERT_RETURN(pjsua_var.tpdata[id].tp != NULL, PJ_EINVAL);
- for (i=0; i<dev_count; ++i) {
- const pjmedia_snd_dev_info *dev_info;
- dev_info = pjmedia_snd_get_dev_info(i);
- pj_memcpy(&info[i], dev_info, sizeof(pjmedia_snd_dev_info));
- }
- *count = dev_count;
- return PJ_SUCCESS;
-}
+ /* To be done!! */
-/**
- * Select or change sound device.
- */
-PJ_DEF(pj_status_t) pjsua_set_snd_dev( int snd_capture_id,
- int snd_player_id)
-{
- pjsua.config.snd_capture_id = snd_capture_id;
- pjsua.config.snd_player_id = snd_player_id;
- return PJ_SUCCESS;
+ PJ_TODO(pjsua_transport_close);
+
+ return PJ_EINVALIDOP;
}
/*
- * Destroy pjsua.
+ * Add additional headers etc in msg_data specified by application
+ * when sending requests.
*/
-PJ_DEF(pj_status_t) pjsua_destroy(void)
+void pjsua_process_msg_data(pjsip_tx_data *tdata,
+ const pjsua_msg_data *msg_data)
{
- int i; /* Must be signed */
-
- /* Signal threads to quit: */
- pjsua.quit_flag = 1;
-
- /* Wait worker threads to quit: */
- for (i=0; i<(int)pjsua.config.thread_cnt; ++i) {
-
- if (pjsua.threads[i]) {
- pj_thread_join(pjsua.threads[i]);
- pj_thread_destroy(pjsua.threads[i]);
- pjsua.threads[i] = NULL;
- }
- }
-
-
- if (pjsua.endpt) {
- /* Terminate all calls. */
- pjsua_call_hangup_all();
+ pj_bool_t allow_body;
+ const pjsip_hdr *hdr;
- /* Terminate all presence subscriptions. */
- pjsua_pres_shutdown();
+ if (!msg_data)
+ return;
- /* Unregister, if required: */
- for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
- if (pjsua.acc[i].regc) {
- pjsua_acc_set_registration(i, PJ_FALSE);
- }
- }
+ hdr = msg_data->hdr_list.next;
+ while (hdr && hdr != &msg_data->hdr_list) {
+ pjsip_hdr *new_hdr;
- /* Wait for some time to allow unregistration to complete: */
- PJ_LOG(4,(THIS_FILE, "Shutting down..."));
- busy_sleep(1000);
- }
+ new_hdr = pjsip_hdr_clone(tdata->pool, hdr);
+ pjsip_msg_add_hdr(tdata->msg, new_hdr);
- /* If we have master port, destroying master port will recursively
- * destroy conference bridge, otherwise must destroy it manually.
- */
- if (pjsua.master_port) {
- pjmedia_master_port_destroy(pjsua.master_port);
- pjsua.master_port = NULL;
- } else {
- if (pjsua.snd_port) {
- pjmedia_snd_port_destroy(pjsua.snd_port);
- pjsua.snd_port = NULL;
- }
- if (pjsua.mconf) {
- pjmedia_conf_destroy(pjsua.mconf);
- pjsua.mconf = NULL;
- }
+ hdr = hdr->next;
}
- /* Destroy file players */
- for (i=0; i<PJ_ARRAY_SIZE(pjsua.player); ++i) {
- if (pjsua.player[i].port) {
- pjmedia_port_destroy(pjsua.player[i].port);
- pjsua.player[i].port = NULL;
- }
- }
+ allow_body = (tdata->msg->body == NULL);
+ if (allow_body && msg_data->content_type.slen && msg_data->msg_body.slen) {
+ pjsip_media_type ctype;
+ pjsip_msg_body *body;
- /* Destroy file recorders */
- for (i=0; i<PJ_ARRAY_SIZE(pjsua.recorder); ++i) {
- if (pjsua.recorder[i].port) {
- pjmedia_port_destroy(pjsua.recorder[i].port);
- pjsua.recorder[i].port = NULL;
- }
+ pjsua_parse_media_type(tdata->pool, &msg_data->content_type, &ctype);
+ body = pjsip_msg_body_create(tdata->pool, &ctype.type, &ctype.subtype,
+ &msg_data->msg_body);
+ tdata->msg->body = body;
}
+}
- /* Close transports */
- for (i=0; i<(int)pjsua.config.max_calls; ++i) {
- if (pjsua.calls[i].med_tp) {
- (*pjsua.calls[i].med_tp->op->destroy)(pjsua.calls[i].med_tp);
- pjsua.calls[i].med_tp = NULL;
- }
- }
+/*
+ * Add route_set to outgoing requests
+ */
+void pjsua_set_msg_route_set( pjsip_tx_data *tdata,
+ const pjsip_route_hdr *route_set )
+{
+ const pjsip_route_hdr *r;
- /* Destroy media endpoint. */
- if (pjsua.med_endpt) {
+ r = route_set->next;
+ while (r != route_set) {
+ pjsip_route_hdr *new_r;
- /* Shutdown all codecs: */
-# if PJMEDIA_HAS_SPEEX_CODEC
- pjmedia_codec_speex_deinit();
-# endif /* PJMEDIA_HAS_SPEEX_CODEC */
+ new_r = pjsip_hdr_clone(tdata->pool, r);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)new_r);
-# if PJMEDIA_HAS_GSM_CODEC
- pjmedia_codec_gsm_deinit();
-# endif /* PJMEDIA_HAS_GSM_CODEC */
+ r = r->next;
+ }
+}
-# if PJMEDIA_HAS_G711_CODEC
- pjmedia_codec_g711_deinit();
-# endif /* PJMEDIA_HAS_G711_CODEC */
-# if PJMEDIA_HAS_L16_CODEC
- pjmedia_codec_l16_deinit();
-# endif /* PJMEDIA_HAS_L16_CODEC */
+/*
+ * Simple version of MIME type parsing (it doesn't support parameters)
+ */
+void pjsua_parse_media_type( pj_pool_t *pool,
+ const pj_str_t *mime,
+ pjsip_media_type *media_type)
+{
+ pj_str_t tmp;
+ char *pos;
+ pj_memset(media_type, 0, sizeof(*media_type));
- pjmedia_endpt_destroy(pjsua.med_endpt);
- pjsua.med_endpt = NULL;
- }
+ pj_strdup_with_null(pool, &tmp, mime);
- /* Destroy endpoint. */
- if (pjsua.endpt) {
- pjsip_endpt_destroy(pjsua.endpt);
- pjsua.endpt = NULL;
+ pos = pj_strchr(&tmp, '/');
+ if (pos) {
+ media_type->type.ptr = tmp.ptr;
+ media_type->type.slen = (pos-tmp.ptr);
+ media_type->subtype.ptr = pos+1;
+ media_type->subtype.slen = tmp.ptr+tmp.slen-pos-1;
+ } else {
+ media_type->type = tmp;
}
+}
- /* Destroy caching pool. */
- pj_caching_pool_destroy(&pjsua.cp);
-
-
- PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
- /* End logging */
- logging_shutdown();
+/*
+ * Verify that valid SIP url is given.
+ */
+PJ_DEF(pj_status_t) pjsua_verify_sip_url(const char *c_url)
+{
+ pjsip_uri *p;
+ pj_pool_t *pool;
+ char *url;
+ int len = (c_url ? pj_ansi_strlen(c_url) : 0);
- /* Done. */
+ if (!len) return -1;
- return PJ_SUCCESS;
-}
+ pool = pj_pool_create(&pjsua_var.cp.factory, "check%p", 1024, 0, NULL);
+ if (!pool) return -1;
+ url = pj_pool_alloc(pool, len+1);
+ pj_ansi_strcpy(url, c_url);
-/**
- * Get SIP endpoint instance.
- * Only valid after pjsua_init().
- */
-PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
-{
- return pjsua.endpt;
-}
+ p = pjsip_parse_uri(pool, url, len, 0);
+ if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
+ p = NULL;
-/**
- * Get media endpoint instance.
- * Only valid after pjsua_init().
- */
-PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
-{
- return pjsua.med_endpt;
+ pj_pool_release(pool);
+ return p ? 0 : -1;
}
-