📄 pjsua_core.c
字号:
/* $Id: pjsua_core.c 1158 2007-04-06 10:25:23Z bennylp $ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjsua-lib/pjsua.h>
#include <pjsua-lib/pjsua_internal.h>
#define THIS_FILE "pjsua_core.c"
/* PJSUA application instance. */
struct pjsua_data pjsua_var;
/* 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));
}
static void init_data()
{
unsigned i;
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;
}
/*****************************************************************************
* 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.
*
*/
/* Notification on incoming messages */
static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
{
PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
"%.*s\n"
"--end msg--",
rdata->msg_info.len,
pjsip_rx_data_get_info(rdata),
rdata->tp_info.transport->type_name,
rdata->pkt_info.src_name,
rdata->pkt_info.src_port,
(int)rdata->msg_info.len,
rdata->msg_info.msg_buf));
/* Always return false, otherwise messages will not get processed! */
return PJ_FALSE;
}
/* 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 %s:%d:\n"
"%.*s\n"
"--end msg--",
(tdata->buf.cur - tdata->buf.start),
pjsip_tx_data_get_info(tdata),
tdata->tp_info.transport->type_name,
tdata->tp_info.dst_name,
tdata->tp_info.dst_port,
(int)(tdata->buf.cur - tdata->buf.start),
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() */
};
/*****************************************************************************
* Another simple module to handle incoming OPTIONS request
*/
/* Notification on incoming request */
static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata)
{
pjsip_tx_data *tdata;
pjsip_response_addr res_addr;
pjmedia_sdp_session *sdp;
const pjsip_hdr *cap_hdr;
pj_status_t status;
/* Only want to handle OPTIONS requests */
if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
&pjsip_options_method) != 0)
{
return PJ_FALSE;
}
/* Create basic response. */
status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL,
&tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status);
return PJ_TRUE;
}
/* Add Allow header */
cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL);
if (cap_hdr) {
pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool, cap_hdr));
}
/* Add Accept header */
cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL);
if (cap_hdr) {
pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool, cap_hdr));
}
/* Add Supported header */
cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL);
if (cap_hdr) {
pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool, cap_hdr));
}
/* Add Allow-Events header from the evsub module */
cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL);
if (cap_hdr) {
pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool, cap_hdr));
}
/* Add User-Agent header */
if (pjsua_var.ua_cfg.user_agent.slen) {
const pj_str_t USER_AGENT = { "User-Agent", 10};
pjsip_hdr *h;
h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
&USER_AGENT,
&pjsua_var.ua_cfg.user_agent);
pjsip_msg_add_hdr(tdata->msg, h);
}
/* Add SDP body, using call0's RTP address */
status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1,
&pjsua_var.calls[0].skinfo, &sdp);
if (status == PJ_SUCCESS) {
pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body);
}
/* Send response statelessly */
pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL);
if (status != PJ_SUCCESS)
pjsip_tx_data_dec_ref(tdata);
return PJ_TRUE;
}
/* The module instance. */
static pjsip_module pjsua_options_handler =
{
NULL, NULL, /* prev, next. */
{ "mod-pjsua-options", 17 }, /* Name. */
-1, /* Id */
PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
NULL, /* load() */
NULL, /* start() */
NULL, /* stop() */
NULL, /* unload() */
&options_on_rx_request, /* on_rx_request() */
NULL, /* on_rx_response() */
NULL, /* on_tx_request. */
NULL, /* 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.
*
* This handler serves multiple purposes:
* - it receives requests outside dialogs.
* - it receives requests inside dialogs, when the requests are
* unhandled by other dialog usages. Example of these
* requests are: MESSAGE.
*/
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) {
processed = pjsua_call_on_incoming(rdata);
}
PJSUA_UNLOCK();
return processed;
}
/*
* Handler for receiving incoming responses.
*
* This handler serves multiple purposes:
* - it receives strayed responses (i.e. outside any dialog and
* outside any transactions).
* - it receives responses coming to a transaction, when pjsua
* module is set as transaction user for the transaction.
* - it receives responses inside a dialog, when these responses
* are unhandled by other dialog usages.
*/
static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
{
PJ_UNUSED_ARG(rdata);
return PJ_FALSE;
}
/*****************************************************************************
* Logging.
*/
/* Log callback */
static void log_writer(int level, const char *buffer, int len)
{
/* Write to file, stdout or application callback. */
if (pjsua_var.log_file) {
pj_ssize_t size = len;
pj_file_write(pjsua_var.log_file, buffer, &size);
/* This will slow things down considerably! Don't do it!
pj_file_flush(pjsua_var.log_file);
*/
}
if (level <= (int)pjsua_var.log_cfg.console_level) {
if (pjsua_var.log_cfg.cb)
(*pjsua_var.log_cfg.cb)(level, buffer, len);
else
pj_log_write(level, buffer, len);
}
}
/*
* Application can call this function at any time (after pjsua_create(), of
* course) to change logging settings.
*/
PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg)
{
pj_status_t status;
/* Save config. */
pjsua_logging_config_dup(pjsua_var.pool, &pjsua_var.log_cfg, cfg);
/* Redirect log function to ours */
pj_log_set_log_func( &log_writer );
/* Close existing file, if any */
if (pjsua_var.log_file) {
pj_file_close(pjsua_var.log_file);
pjsua_var.log_file = NULL;
}
/* If output log file is desired, create the file: */
if (pjsua_var.log_cfg.log_filename.slen) {
status = pj_file_open(pjsua_var.pool,
pjsua_var.log_cfg.log_filename.ptr,
PJ_O_WRONLY,
&pjsua_var.log_file);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error creating log file", status);
return status;
}
}
/* 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);
return PJ_SUCCESS;
}
/*****************************************************************************
* PJSUA Base API.
*/
/* Worker thread function. */
static int worker_thread(void *arg)
{
enum { TIMEOUT = 10 };
PJ_UNUSED_ARG(arg);
while (!pjsua_var.thread_quit_flag) {
int count;
count = pjsua_handle_events(TIMEOUT);
if (count < 0)
pj_thread_sleep(TIMEOUT);
}
return 0;
}
/*
* Instantiate pjsua application.
*/
PJ_DEF(pj_status_t) pjsua_create(void)
{
pj_status_t status;
/* Init pjsua data */
init_data();
/* Set default logging settings */
pjsua_logging_config_default(&pjsua_var.log_cfg);
/* Init PJLIB: */
status = pj_init();
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Init PJLIB-UTIL: */
status = pjlib_util_init();
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Set default sound device ID */
pjsua_var.cap_dev = pjsua_var.play_dev = -1;
/* Init caching pool. */
pj_caching_pool_init(&pjsua_var.cp, &pj_pool_factory_default_policy, 0);
/* Create memory pool for application. */
pjsua_var.pool = pjsua_pool_create("pjsua", 4000, 4000);
PJ_ASSERT_RETURN(pjsua_var.pool, PJ_ENOMEM);
/* Create mutex */
status = pj_mutex_create_recursive(pjsua_var.pool, "pjsua",
&pjsua_var.mutex);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create mutex", status);
return status;
}
/* 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);
return PJ_SUCCESS;
}
/*
* 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_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
const pjsua_logging_config *log_cfg,
const pjsua_media_config *media_cfg)
{
pjsua_config default_cfg;
pjsua_media_config default_media_cfg;
const pj_str_t STR_OPTIONS = { "OPTIONS", 7 };
pj_status_t status;
/* 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
pj_dns_resolver *resv;
unsigned i;
/* Create DNS resolver */
status = pjsip_endpt_create_resolver(pjsua_var.endpt, &resv);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Configure nameserver for the DNS resolver */
status = pj_dns_resolver_set_ns(resv, 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, resv);
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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -