📄 pjsua_core.c
字号:
/* Create default configurations when the config is not supplied */
if (ua_cfg == NULL) {
pjsua_config_default(&default_cfg);
ua_cfg = &default_cfg;
}
if (media_cfg == NULL) {
pjsua_media_config_default(&default_media_cfg);
media_cfg = &default_media_cfg;
}
/* 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);
}
/* If nameserver is configured, create DNS resolver instance and
* set it to be used by SIP resolver.
*/
if (ua_cfg->nameserver_count) {
#if PJSIP_HAS_RESOLVER
unsigned i;
/* Create DNS resolver */
status = pjsip_endpt_create_resolver(pjsua_var.endpt,
&pjsua_var.resolver);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error creating resolver", status);
return status;
}
/* Configure nameserver for the DNS resolver */
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);
return status;
}
/* Set this DNS resolver to be used by the SIP resolver */
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;
}
/* Print nameservers */
for (i=0; i<ua_cfg->nameserver_count; ++i) {
PJ_LOG(4,(THIS_FILE, "Nameserver %.*s added",
(int)ua_cfg->nameserver[i].slen,
ua_cfg->nameserver[i].ptr));
}
#else
PJ_LOG(2,(THIS_FILE,
"DNS resolver is disabled (PJSIP_HAS_RESOLVER==0)"));
#endif
}
/* Init SIP UA: */
/* Initialize transaction layer: */
status = pjsip_tsx_layer_init_module(pjsua_var.endpt);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Initialize UA layer module: */
status = pjsip_ua_init_module( pjsua_var.endpt, NULL );
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Initialize Replaces support. */
status = pjsip_replaces_init_module( pjsua_var.endpt );
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Initialize and register PJSUA application module. */
{
const pjsip_module mod_initializer =
{
NULL, NULL, /* prev, next. */
{ "mod-pjsua", 9 }, /* Name. */
-1, /* Id */
PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
NULL, /* load() */
NULL, /* start() */
NULL, /* stop() */
NULL, /* unload() */
&mod_pjsua_on_rx_request, /* on_rx_request() */
&mod_pjsua_on_rx_response, /* on_rx_response() */
NULL, /* on_tx_request. */
NULL, /* on_tx_response() */
NULL, /* on_tsx_state() */
};
pjsua_var.mod = mod_initializer;
status = pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_var.mod);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
}
/* Initialize PJSUA call subsystem: */
status = pjsua_call_subsys_init(ua_cfg);
if (status != PJ_SUCCESS)
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)
goto on_error;
/* Init core SIMPLE module : */
status = pjsip_evsub_init_module(pjsua_var.endpt);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Init presence module: */
status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Init PUBLISH module */
pjsip_publishc_init_module(pjsua_var.endpt);
/* Init xfer/REFER module */
status = pjsip_xfer_init_module( pjsua_var.endpt );
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Init pjsua presence handler: */
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;
/* Register OPTIONS handler */
pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler);
/* Add OPTIONS in Allow header */
pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_ALLOW,
NULL, 1, &STR_OPTIONS);
/* 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;
}
PJ_LOG(4,(THIS_FILE, "%d SIP worker threads created",
pjsua_var.ua_cfg.thread_cnt));
} else {
PJ_LOG(4,(THIS_FILE, "No SIP worker threads created"));
}
/* Done! */
PJ_LOG(3,(THIS_FILE, "pjsua version %s for %s initialized",
PJ_VERSION, PJ_OS_NAME));
return PJ_SUCCESS;
on_error:
pjsua_destroy();
return status;
}
/* Sleep with polling */
static void busy_sleep(unsigned msec)
{
#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
/* Ideally we shouldn't call pj_thread_sleep() and rather
* CActiveScheduler::WaitForAnyRequest() here, but that will
* drag in Symbian header and it doesn't look pretty.
*/
pj_thread_sleep(msec);
#else
pj_time_val timeout, now;
pj_gettimeofday(&timeout);
timeout.msec += msec;
pj_time_val_normalize(&timeout);
do {
while (pjsua_handle_events(10) > 0)
;
pj_gettimeofday(&now);
} while (PJ_TIME_VAL_LT(now, timeout));
#endif
}
/*
* Callback function to receive notification from the resolver
* when the resolution process completes.
*/
static void stun_dns_srv_resolver_cb(void *user_data,
pj_status_t status,
const pj_dns_srv_record *rec)
{
unsigned i;
PJ_UNUSED_ARG(user_data);
pjsua_var.stun_status = status;
if (status != PJ_SUCCESS) {
/* DNS SRV resolution failed. If stun_host is specified, resolve
* it with gethostbyname()
*/
if (pjsua_var.ua_cfg.stun_host.slen) {
pj_str_t str_host, str_port;
int port;
pj_hostent he;
str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
if (str_port.ptr != NULL) {
str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
str_host.slen = (str_port.ptr - str_host.ptr);
str_port.ptr++;
str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
str_host.slen - 1;
port = (int)pj_strtoul(&str_port);
if (port < 1 || port > 65535) {
pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
pjsua_var.stun_status = PJ_EINVAL;
return;
}
} else {
str_host = pjsua_var.ua_cfg.stun_host;
port = 3478;
}
pjsua_var.stun_status = pj_gethostbyname(&str_host, &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)port);
PJ_LOG(3,(THIS_FILE,
"STUN server %.*s resolved, address is %s:%d",
(int)pjsua_var.ua_cfg.stun_host.slen,
pjsua_var.ua_cfg.stun_host.ptr,
pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
(int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
}
} else {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(1,(THIS_FILE,
"DNS SRV resolution failed for STUN server %.*s: %s",
(int)pjsua_var.ua_cfg.stun_domain.slen,
pjsua_var.ua_cfg.stun_domain.ptr,
errmsg));
}
return;
}
pj_memcpy(&pjsua_var.stun_srv, &rec->entry[0].addr,
rec->entry[0].addr_len);
PJ_LOG(3,(THIS_FILE, "_stun._udp.%.*s resolved, found %d entry(s):",
(int)pjsua_var.ua_cfg.stun_domain.slen,
pjsua_var.ua_cfg.stun_domain.ptr,
rec->count));
for (i=0; i<rec->count; ++i) {
PJ_LOG(3,(THIS_FILE,
" %d: prio=%d, weight=%d %s:%d",
i, rec->entry[i].priority, rec->entry[i].weight,
pj_inet_ntoa(rec->entry[i].addr.ipv4.sin_addr),
(int)pj_ntohs(rec->entry[i].addr.ipv4.sin_port)));
}
}
/*
* 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 */
pjsua_var.stun_status = PJ_EPENDING;
/* If stun_domain is specified, resolve STUN servers with DNS
* SRV resolution.
*/
if (pjsua_var.ua_cfg.stun_domain.slen) {
pj_str_t res_type;
pj_status_t status;
/* Fail if resolver is not configured */
if (pjsua_var.resolver == NULL) {
PJ_LOG(1,(THIS_FILE, "Nameserver must be configured when "
"stun_domain is specified"));
pjsua_var.stun_status = PJLIB_UTIL_EDNSNONS;
return PJLIB_UTIL_EDNSNONS;
}
res_type = pj_str("_stun._udp");
status =
pj_dns_srv_resolve(&pjsua_var.ua_cfg.stun_domain, &res_type,
3478, pjsua_var.pool, pjsua_var.resolver,
0, NULL, &stun_dns_srv_resolver_cb);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error starting DNS SRV resolution",
pjsua_var.stun_status);
pjsua_var.stun_status = status;
return pjsua_var.stun_status;
} else {
pjsua_var.stun_status = PJ_EPENDING;
}
}
/* Otherwise if stun_host is specified, resolve STUN server with
* gethostbyname().
*/
else if (pjsua_var.ua_cfg.stun_host.slen) {
pj_str_t str_host, str_port;
int port;
pj_hostent he;
str_port.ptr = pj_strchr(&pjsua_var.ua_cfg.stun_host, ':');
if (str_port.ptr != NULL) {
str_host.ptr = pjsua_var.ua_cfg.stun_host.ptr;
str_host.slen = (str_port.ptr - str_host.ptr);
str_port.ptr++;
str_port.slen = pjsua_var.ua_cfg.stun_host.slen -
str_host.slen - 1;
port = (int)pj_strtoul(&str_port);
if (port < 1 || port > 65535) {
pjsua_perror(THIS_FILE, "Invalid STUN server", PJ_EINVAL);
pjsua_var.stun_status = PJ_EINVAL;
return pjsua_var.stun_status;
}
} else {
str_host = pjsua_var.ua_cfg.stun_host;
port = 3478;
}
pjsua_var.stun_status = pj_gethostbyname(&str_host, &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)port);
PJ_LOG(3,(THIS_FILE,
"STUN server %.*s resolved, address is %s:%d",
(int)pjsua_var.ua_cfg.stun_host.slen,
pjsua_var.ua_cfg.stun_host.ptr,
pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr),
(int)pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port)));
}
}
/* Otherwise disable STUN. */
else {
pjsua_var.stun_status = PJ_SUCCESS;
}
return pjsua_var.stun_status;
} else if (pjsua_var.stun_status == PJ_EPENDING) {
/* STUN server resolution has been started, wait for the
* result.
*/
if (wait) {
while (pjsua_var.stun_status == PJ_EPENDING)
pjsua_handle_events(10);
}
return pjsua_var.stun_status;
} else {
/* STUN server has been resolved, return the status */
return pjsua_var.stun_status;
}
}
/*
* Destroy pjsua.
*/
PJ_DEF(pj_status_t) pjsua_destroy(void)
{
int i; /* Must be signed */
/* Signal threads to quit: */
pjsua_var.thread_quit_flag = 1;
/* 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 (pjsua_var.endpt) {
/* Terminate all calls. */
pjsua_call_hangup_all();
/* Terminate all presence subscriptions. */
pjsua_pres_shutdown();
/* 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);
}
}
/* Wait for some time to allow unregistration to complete: */
PJ_LOG(4,(THIS_FILE, "Shutting down..."));
busy_sleep(1000);
}
/* Destroy media */
pjsua_media_subsys_destroy();
/* Destroy endpoint. */
if (pjsua_var.endpt) {
pjsip_endpt_destroy(pjsua_var.endpt);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -