summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-02-26 21:23:45 +0000
committerBenny Prijono <bennylp@teluu.com>2006-02-26 21:23:45 +0000
commit1c2e7acc0844421cd4da9fa2032e36eb98d673f3 (patch)
tree171d8e21cfc15cdbf5693a82f57a8022c9a36baf
parent3105a43d2c2e4f84e56f7623ad9178ccb4c239e1 (diff)
Major redesign in pjsua: call is indexed by number, multiple accounts, configurable max-calls, more auto-xxx features, fixed bugs in save_settings(), etc.
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@236 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip/build/pjsua.dsp4
-rw-r--r--pjsip/src/pjsua/main.c251
-rw-r--r--pjsip/src/pjsua/pjsua.h222
-rw-r--r--pjsip/src/pjsua/pjsua_call.c (renamed from pjsip/src/pjsua/pjsua_inv.c)391
-rw-r--r--pjsip/src/pjsua/pjsua_core.c322
-rw-r--r--pjsip/src/pjsua/pjsua_opt.c316
-rw-r--r--pjsip/src/pjsua/pjsua_pres.c81
-rw-r--r--pjsip/src/pjsua/pjsua_reg.c75
8 files changed, 1057 insertions, 605 deletions
diff --git a/pjsip/build/pjsua.dsp b/pjsip/build/pjsua.dsp
index 604c9939..4d808e7c 100644
--- a/pjsip/build/pjsua.dsp
+++ b/pjsip/build/pjsua.dsp
@@ -98,11 +98,11 @@ SOURCE=..\src\pjsua\main.c
# End Source File
# Begin Source File
-SOURCE=..\src\pjsua\pjsua_core.c
+SOURCE=..\src\pjsua\pjsua_call.c
# End Source File
# Begin Source File
-SOURCE=..\src\pjsua\pjsua_inv.c
+SOURCE=..\src\pjsua\pjsua_core.c
# End Source File
# Begin Source File
diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c
index 7070c761..25ce6dcf 100644
--- a/pjsip/src/pjsua/main.c
+++ b/pjsip/src/pjsua/main.c
@@ -23,29 +23,86 @@
#define THIS_FILE "main.c"
/* Current dialog */
-static struct pjsua_inv_data *inv_session;
+static int current_acc;
+static int current_call = -1;
+
+
+/*
+ * Find next call.
+ */
+static pj_bool_t find_next_call(void)
+{
+ int i;
+
+ for (i=current_call+1; i<(int)pjsua.max_calls; ++i) {
+ if (pjsua.calls[i].inv != NULL) {
+ current_call = i;
+ return PJ_TRUE;
+ }
+ }
+
+ for (i=0; i<current_call; ++i) {
+ if (pjsua.calls[i].inv != NULL) {
+ current_call = i;
+ return PJ_TRUE;
+ }
+ }
+
+ current_call = -1;
+ return PJ_FALSE;
+}
+
+
+/*
+ * Find previous call.
+ */
+static pj_bool_t find_prev_call(void)
+{
+ int i;
+
+ for (i=current_call-1; i>=0; --i) {
+ if (pjsua.calls[i].inv != NULL) {
+ current_call = i;
+ return PJ_TRUE;
+ }
+ }
+
+ for (i=pjsua.max_calls-1; i>current_call; --i) {
+ if (pjsua.calls[i].inv != NULL) {
+ current_call = i;
+ return PJ_TRUE;
+ }
+ }
+
+ current_call = -1;
+ return PJ_FALSE;
+}
+
+
/*
* Notify UI when invite state has changed.
*/
-void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
+void pjsua_ui_inv_on_state_changed(int call_index, pjsip_event *e)
{
+ pjsua_call *call = &pjsua.calls[call_index];
+
PJ_UNUSED_ARG(e);
- PJ_LOG(3,(THIS_FILE, "INVITE session state changed to %s",
- pjsua_inv_state_names[inv->state]));
+ PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
+ call_index,
+ pjsua_inv_state_names[call->inv->state]));
- if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
- if (inv == inv_session->inv) {
- inv_session = inv_session->next;
- if (inv_session == &pjsua.inv_list)
- inv_session = pjsua.inv_list.next;
+ if (call->inv->state == PJSIP_INV_STATE_DISCONNECTED) {
+ call->inv = NULL;
+ if ((int)call->index == current_call) {
+ find_next_call();
}
} else {
- if (inv_session == &pjsua.inv_list || inv_session == NULL)
- inv_session = inv->mod_data[pjsua.mod.id];
+ if (call && current_call==-1)
+ current_call = call->index;
}
}
@@ -66,10 +123,10 @@ void pjsua_ui_regc_on_state_changed(int code)
*/
static void print_buddy_list(void)
{
- unsigned i;
+ int i;
puts("Buddy list:");
- //puts("-------------------------------------------------------------------------------");
+
if (pjsua.buddy_cnt == 0)
puts(" -none-");
else {
@@ -93,37 +150,56 @@ static void print_buddy_list(void)
puts("");
}
+
/*
- * Show a bit of help.
+ * Print account status.
*/
-static void keystroke_help(void)
+static void print_acc_status(int acc_index)
{
char reg_status[128];
- if (pjsua.regc == NULL) {
+ if (pjsua.acc[acc_index].regc == NULL) {
pj_ansi_strcpy(reg_status, " -not registered to server-");
- } else if (pjsua.regc_last_err != PJ_SUCCESS) {
- pj_strerror(pjsua.regc_last_err, reg_status, sizeof(reg_status));
- } else if (pjsua.regc_last_code>=200 && pjsua.regc_last_code<=699) {
+
+ } else if (pjsua.acc[acc_index].reg_last_err != PJ_SUCCESS) {
+ pj_strerror(pjsua.acc[acc_index].reg_last_err, reg_status, sizeof(reg_status));
+
+ } else if (pjsua.acc[acc_index].reg_last_code>=200 &&
+ pjsua.acc[acc_index].reg_last_code<=699) {
pjsip_regc_info info;
- pjsip_regc_get_info(pjsua.regc, &info);
+ pjsip_regc_get_info(pjsua.acc[acc_index].regc, &info);
pj_snprintf(reg_status, sizeof(reg_status),
"%s (%.*s;expires=%d)",
- pjsip_get_status_text(pjsua.regc_last_code)->ptr,
+ pjsip_get_status_text(pjsua.acc[acc_index].reg_last_code)->ptr,
(int)info.client_uri.slen,
info.client_uri.ptr,
info.next_reg);
} else {
- pj_sprintf(reg_status, "in progress (%d)", pjsua.regc_last_code);
+ pj_sprintf(reg_status, "in progress (%d)",
+ pjsua.acc[acc_index].reg_last_code);
}
- printf(">>>>\nRegistration status: %s\n", reg_status);
- printf("Online status: %s\n",
- (pjsua.online_status ? "Online" : "Invisible"));
+ printf("[%2d] Registration status: %s\n", acc_index, reg_status);
+ printf(" Online status: %s\n",
+ (pjsua.acc[acc_index].online_status ? "Online" : "Invisible"));
+}
+
+/*
+ * Show a bit of help.
+ */
+static void keystroke_help(void)
+{
+ int i;
+
+ printf(">>>>\n");
+
+ for (i=0; i<pjsua.acc_cnt; ++i)
+ print_acc_status(i);
+
print_buddy_list();
//puts("Commands:");
@@ -134,7 +210,7 @@ static void keystroke_help(void)
puts("| a Answer call | s Subscribe presence | rr (Re-)register |");
puts("| h Hangup call | u Unsubscribe presence | ru Unregister |");
puts("| ] Select next dialog | t ToGgle Online status | d Dump status |");
- puts("| [ Select previous dialog | | |");
+ puts("| [ Select previous dialog | | dc Dump config |");
puts("| +--------------------------+-------------------+");
puts("| H Hold call | Conference Command | |");
puts("| v re-inVite (release hold) | cl List ports | |");
@@ -262,19 +338,19 @@ static void ui_input_url(const char *title, char *buf, int len,
static void conf_list(void)
{
unsigned i, count;
- pjmedia_conf_port_info info[16];
+ pjmedia_conf_port_info info[PJSUA_MAX_CALLS];
printf("Conference ports:\n");
count = PJ_ARRAY_SIZE(info);
pjmedia_conf_get_ports_info(pjsua.mconf, &count, info);
for (i=0; i<count; ++i) {
- char txlist[80];
- unsigned j;
+ char txlist[PJSUA_MAX_CALLS*4+10];
+ int j;
pjmedia_conf_port_info *port_info = &info[i];
txlist[0] = '\0';
- for (j=0; j<pjsua.max_ports; ++j) {
+ for (j=0; j<pjsua.max_calls+PJSUA_CONF_MORE_PORTS; ++j) {
char s[10];
if (port_info->listener[j]) {
pj_sprintf(s, "#%d ", j);
@@ -309,26 +385,27 @@ static void ui_console_main(void)
case 'm':
/* Make call! : */
- if (pj_list_size(&pjsua.inv_list))
- printf("(You have %d calls)\n", pj_list_size(&pjsua.inv_list));
+ printf("(You currently have %d calls)\n", pjsua.call_cnt);
ui_input_url("Make call", buf, sizeof(buf), &result);
if (result.nb_result != NO_NB) {
if (result.nb_result == -1)
puts("You can't do that with make call!");
else
- pjsua_invite(pjsua.buddies[result.nb_result].uri.ptr, NULL);
+ pjsua_make_call( current_acc,
+ pjsua.buddies[result.nb_result].uri.ptr,
+ NULL);
} else if (result.uri_result)
- pjsua_invite(result.uri_result, NULL);
+ pjsua_make_call( current_acc, result.uri_result, NULL);
break;
case 'a':
- if (inv_session == &pjsua.inv_list ||
- inv_session->inv->role != PJSIP_ROLE_UAS ||
- inv_session->inv->state >= PJSIP_INV_STATE_CONNECTING)
+ if (current_call == -1 ||
+ pjsua.calls[current_call].inv->role != PJSIP_ROLE_UAS ||
+ pjsua.calls[current_call].inv->state >= PJSIP_INV_STATE_CONNECTING)
{
puts("No pending incoming call");
fflush(stdout);
@@ -349,16 +426,18 @@ static void ui_console_main(void)
* Call may have been disconnected while we're waiting for
* keyboard input.
*/
- if (inv_session == &pjsua.inv_list) {
+ if (current_call == -1) {
puts("Call has been disconnected");
fflush(stdout);
continue;
}
- status = pjsip_inv_answer(inv_session->inv, atoi(buf),
+ status = pjsip_inv_answer(pjsua.calls[current_call].inv,
+ atoi(buf),
NULL, NULL, &tdata);
if (status == PJ_SUCCESS)
- status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL);
+ status = pjsip_inv_send_msg(pjsua.calls[current_call].inv,
+ tdata, NULL);
if (status != PJ_SUCCESS)
pjsua_perror(THIS_FILE, "Unable to create/send response",
@@ -370,13 +449,13 @@ static void ui_console_main(void)
case 'h':
- if (inv_session == &pjsua.inv_list) {
+ if (current_call == -1) {
puts("No current call");
fflush(stdout);
continue;
} else {
- pjsua_inv_hangup(inv_session, PJSIP_SC_DECLINE);
+ pjsua_call_hangup(current_call, PJSIP_SC_DECLINE);
}
break;
@@ -386,22 +465,19 @@ static void ui_console_main(void)
* Cycle next/prev dialog.
*/
if (menuin[0] == ']') {
- inv_session = inv_session->next;
- if (inv_session == &pjsua.inv_list)
- inv_session = pjsua.inv_list.next;
+ find_next_call();
} else {
- inv_session = inv_session->prev;
- if (inv_session == &pjsua.inv_list)
- inv_session = pjsua.inv_list.prev;
+ find_prev_call();
}
- if (inv_session != &pjsua.inv_list) {
+ if (current_call != -1) {
char url[PJSIP_MAX_URL_SIZE];
int len;
+ const pjsip_uri *u;
- len = pjsip_uri_print(0, inv_session->inv->dlg->remote.info->uri,
- url, sizeof(url)-1);
+ u = pjsua.calls[current_call].inv->dlg->remote.info->uri;
+ len = pjsip_uri_print(0, u, url, sizeof(url)-1);
if (len < 1) {
pj_ansi_strcpy(url, "<uri is too long>");
} else {
@@ -419,9 +495,9 @@ static void ui_console_main(void)
/*
* Hold call.
*/
- if (inv_session != &pjsua.inv_list) {
+ if (current_call != -1) {
- pjsua_inv_set_hold(inv_session);
+ pjsua_call_set_hold(current_call);
} else {
PJ_LOG(3,(THIS_FILE, "No current call"));
@@ -432,9 +508,9 @@ static void ui_console_main(void)
/*
* Send re-INVITE (to release hold, etc).
*/
- if (inv_session != &pjsua.inv_list) {
+ if (current_call != -1) {
- pjsua_inv_reinvite(inv_session);
+ pjsua_call_reinvite(current_call);
} else {
PJ_LOG(3,(THIS_FILE, "No current call"));
@@ -445,18 +521,18 @@ static void ui_console_main(void)
/*
* Transfer call.
*/
- if (inv_session == &pjsua.inv_list) {
+ if (current_call == -1) {
PJ_LOG(3,(THIS_FILE, "No current call"));
} else {
- struct pjsua_inv_data *cur = inv_session;
+ int call = current_call;
ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
/* Check if call is still there. */
- if (cur != inv_session) {
+ if (call != current_call) {
puts("Call has been disconnected");
continue;
}
@@ -465,11 +541,11 @@ static void ui_console_main(void)
if (result.nb_result == -1)
puts("You can't do that with transfer call!");
else
- pjsua_inv_xfer_call( inv_session,
- pjsua.buddies[result.nb_result].uri.ptr);
+ pjsua_call_xfer( current_call,
+ pjsua.buddies[result.nb_result].uri.ptr);
} else if (result.uri_result) {
- pjsua_inv_xfer_call( inv_session, result.uri_result);
+ pjsua_call_xfer( current_call, result.uri_result);
}
}
break;
@@ -478,17 +554,17 @@ static void ui_console_main(void)
/*
* Send DTMF strings.
*/
- if (inv_session == &pjsua.inv_list) {
+ if (current_call == -1) {
PJ_LOG(3,(THIS_FILE, "No current call"));
- } else if (inv_session->session == NULL) {
+ } else if (pjsua.calls[current_call].session == NULL) {
PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
} else {
pj_str_t digits;
- struct pjsua_inv_data *cur = inv_session;
+ int call = current_call;
pj_status_t status;
if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
@@ -497,13 +573,13 @@ static void ui_console_main(void)
break;
}
- if (cur != inv_session) {
+ if (call != current_call) {
puts("Call has been disconnected");
continue;
}
digits = pj_str(buf);
- status = pjmedia_session_dial_dtmf(inv_session->session, 0,
+ status = pjmedia_session_dial_dtmf(pjsua.calls[current_call].session, 0,
&digits);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
@@ -521,14 +597,14 @@ static void ui_console_main(void)
ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
if (result.nb_result != NO_NB) {
if (result.nb_result == -1) {
- unsigned i;
+ int i;
for (i=0; i<pjsua.buddy_cnt; ++i)
pjsua.buddies[i].monitor = (menuin[0]=='s');
} else {
pjsua.buddies[result.nb_result].monitor = (menuin[0]=='s');
}
- pjsua_pres_refresh();
+ pjsua_pres_refresh(current_acc);
} else if (result.uri_result) {
puts("Sorry, can only subscribe to buddy's presence, "
@@ -543,20 +619,21 @@ static void ui_console_main(void)
/*
* Re-Register.
*/
- pjsua_regc_update(PJ_TRUE);
+ pjsua_regc_update(current_acc, PJ_TRUE);
break;
case 'u':
/*
* Unregister
*/
- pjsua_regc_update(PJ_FALSE);
+ pjsua_regc_update(current_acc, PJ_FALSE);
break;
}
break;
case 't':
- pjsua.online_status = !pjsua.online_status;
- pjsua_pres_refresh();
+ pjsua.acc[current_acc].online_status =
+ !pjsua.acc[current_acc].online_status;
+ pjsua_pres_refresh(current_acc);
break;
case 'c':
@@ -606,7 +683,20 @@ static void ui_console_main(void)
break;
case 'd':
- pjsua_dump();
+ if (menuin[1] == 'c') {
+ char settings[2000];
+ int len;
+
+ len = pjsua_dump_settings(settings, sizeof(settings));
+ if (len < 1)
+ PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
+ else
+ PJ_LOG(3,(THIS_FILE,
+ "Dumping configuration (%d bytes):\n%s\n",
+ len, settings));
+ } else {
+ pjsua_dump();
+ }
break;
case 'q':
@@ -765,37 +855,31 @@ int main(int argc, char *argv[])
{
/* Init default settings. */
-
pjsua_default();
/* Initialize pjsua (to create pool etc).
*/
-
if (pjsua_init() != PJ_SUCCESS)
return 1;
/* Parse command line arguments: */
-
if (pjsua_parse_args(argc, argv) != PJ_SUCCESS)
return 1;
/* Init logging: */
-
app_logging_init();
/* Register message logger to print incoming and outgoing
* messages.
*/
-
pjsip_endpt_register_module(pjsua.endpt, &console_msg_logger);
/* Start pjsua! */
-
if (pjsua_start() != PJ_SUCCESS) {
pjsua_destroy();
@@ -804,27 +888,18 @@ int main(int argc, char *argv[])
/* Sleep for a while, let any messages get printed to console: */
-
pj_thread_sleep(500);
- /* No current call initially: */
-
- inv_session = &pjsua.inv_list;
-
-
/* Start UI console main loop: */
-
ui_console_main();
/* Destroy pjsua: */
-
pjsua_destroy();
/* Close logging: */
-
app_logging_shutdown();
diff --git a/pjsip/src/pjsua/pjsua.h b/pjsip/src/pjsua/pjsua.h
index 2e430ce9..09874160 100644
--- a/pjsip/src/pjsua/pjsua.h
+++ b/pjsip/src/pjsua/pjsua.h
@@ -47,31 +47,63 @@ PJ_BEGIN_DECL
/**
* Max buddies in buddy list.
*/
-#define PJSUA_MAX_BUDDIES 32
+#ifndef PJSUA_MAX_BUDDIES
+# define PJSUA_MAX_BUDDIES 32
+#endif
+
/**
* Max simultaneous calls.
*/
-#define PJSUA_MAX_CALLS 8
+#ifndef PJSUA_MAX_CALLS
+# define PJSUA_MAX_CALLS 256
+#endif
+
+
+/**
+ * Aditional ports to be allocated in the conference ports for non-call
+ * streams.
+ */
+#define PJSUA_CONF_MORE_PORTS 2
+
+
+/**
+ * Maximum accounts.
+ */
+#ifndef PJSUA_MAX_ACC
+# define PJSUA_MAX_ACC 8
+#endif
+
+
+/**
+ * Maximum credentials.
+ */
+#ifndef PJSUA_MAX_CRED
+# define PJSUA_MAX_CRED PJSUA_MAX_ACC
+#endif
/**
- * Structure to be attached to all dialog.
+ * Structure to be attached to invite dialog.
* Given a dialog "dlg", application can retrieve this structure
* by accessing dlg->mod_data[pjsua.mod.id].
*/
-struct pjsua_inv_data
+struct pjsua_call
{
- PJ_DECL_LIST_MEMBER(struct pjsua_inv_data);
-
+ unsigned index; /**< Index in pjsua array. */
pjsip_inv_session *inv; /**< The invite session. */
+ int acc_index; /**< Account index being used. */
pjmedia_session *session; /**< The media session. */
unsigned conf_slot; /**< Slot # in conference bridge. */
- unsigned call_slot; /**< RTP media index in med_sock_use[] */
pjsip_evsub *xfer_sub; /**< Xfer server subscription, if this
call was triggered by xfer. */
+ pjmedia_sock_info skinfo; /**< Preallocated media sockets. */
+
+ void *app_data; /**< Application data. */
};
+typedef struct pjsua_call pjsua_call;
+
/**
* Buddy data.
@@ -79,6 +111,7 @@ struct pjsua_inv_data
struct pjsua_buddy
{
pj_str_t uri; /**< Buddy URI */
+ int acc_index; /**< Which account to use. */
pj_bool_t monitor; /**< Should we monitor? */
pjsip_evsub *sub; /**< Buddy presence subscription */
pjsip_pres_status status; /**< Buddy presence status. */
@@ -100,12 +133,41 @@ struct pjsua_srv_pres
typedef struct pjsua_srv_pres pjsua_srv_pres;
+/**
+ * Account
+ */
+struct pjsua_acc
+{
+ int index; /**< Index in accounts array. */
+ pj_str_t local_uri; /**< Uri in From: header. */
+ pj_str_t user_part; /**< User part of local URI. */
+ pj_str_t host_part; /**< Host part of local URI. */
+ pj_str_t contact_uri; /**< Uri in Contact: header. */
+
+ pj_str_t reg_uri; /**< Registrar URI. */
+ pjsip_regc *regc; /**< Client registration session. */
+ pj_int32_t reg_timeout; /**< Default timeout. */
+ pj_timer_entry reg_timer; /**< Registration timer. */
+ pj_status_t reg_last_err; /**< Last registration error. */
+ int reg_last_code; /**< Last status last register. */
+
+ pj_str_t proxy; /**< Proxy URL. */
+ pjsip_route_hdr route_set; /**< Route set. */
+
+ pj_bool_t online_status; /**< Our online status. */
+ pjsua_srv_pres pres_srv_list; /**< Server subscription list. */
+
+ void *app_data; /**< Application data. */
+};
+
+
+typedef struct pjsua_acc pjsua_acc;
+
/* PJSUA application variables. */
struct pjsua
{
/* Control: */
-
pj_caching_pool cp; /**< Global pool factory. */
pjsip_endpoint *endpt; /**< Global endpoint. */
pj_pool_t *pool; /**< pjsua's private pool. */
@@ -113,95 +175,66 @@ struct pjsua
/* Media: */
-
pjmedia_endpt *med_endpt; /**< Media endpoint. */
- unsigned max_ports; /**< Max ports in conf. */
pjmedia_conf *mconf; /**< Media conference. */
pj_bool_t null_audio; /**< Null audio flag. */
char *wav_file; /**< WAV file name to play. */
unsigned wav_slot; /**< WAV player slot in bridge */
+ pj_bool_t auto_play; /**< Auto play file for calls? */
+ pj_bool_t auto_loop; /**< Auto loop RTP stream? */
+ pj_bool_t auto_conf; /**< Auto put to conference? */
- /* User Agent behaviour: */
+ /* User Agent behaviour: */
int auto_answer; /**< Automatically answer in calls. */
-
- /* Since we support simultaneous calls, we need to have multiple
- * RTP sockets.
- */
- pjmedia_sock_info med_sock_info[PJSUA_MAX_CALLS];
- pj_bool_t med_sock_use[PJSUA_MAX_CALLS];
-
- /* User info: */
-
- pj_str_t local_uri; /**< Uri in From: header. */
- pj_str_t contact_uri; /**< Uri in Contact: header. */
-
- /* Proxy URLs: */
-
- pj_str_t proxy;
- pj_str_t outbound_proxy;
- pjsip_route_hdr route_set;
-
-
- /* Registration: */
-
- pj_str_t registrar_uri;
- pjsip_regc *regc;
- pj_int32_t reg_timeout;
- pj_timer_entry regc_timer;
- pj_status_t regc_last_err; /**< Last registration error. */
- int regc_last_code;/**< Last status last register. */
+ /* Account: */
+ int acc_cnt; /**< Number of client registrations */
+ pjsua_acc acc[PJSUA_MAX_ACC]; /** Client regs array. */
/* Authentication credentials: */
- unsigned cred_count;
- pjsip_cred_info cred_info[4];
+ int cred_count; /**< Number of credentials. */
+ pjsip_cred_info cred_info[10]; /**< Array of credentials. */
/* Threading (optional): */
-
int thread_cnt; /**< Thread count. */
pj_thread_t *threads[8]; /**< Thread instances. */
pj_bool_t quit_flag; /**< To signal thread to quit. */
/* Transport (UDP): */
-
pj_uint16_t sip_port; /**< SIP signaling port. */
pj_sock_t sip_sock; /**< SIP UDP socket. */
pj_sockaddr_in sip_sock_name; /**< Public/STUN UDP socket addr. */
+ pj_str_t outbound_proxy;/**< Outbound proxy. */
/* STUN: */
-
pj_str_t stun_srv1;
int stun_port1;
pj_str_t stun_srv2;
int stun_port2;
- /* Logging: */
-
+ /* Logging: */
int log_level; /**< Logging verbosity. */
int app_log_level; /**< stdout log verbosity. */
unsigned log_decor; /**< Log decoration. */
char *log_filename; /**< Log filename. */
- /* List of invite sessions: */
-
- struct pjsua_inv_data inv_list;
+ /* PJSUA Calls: */
+ int max_calls; /**< Max nb of calls. */
+ int call_cnt; /**< Number of calls. */
+ pjsua_call calls[PJSUA_MAX_CALLS]; /** Calls array. */
/* SIMPLE and buddy status: */
-
- pj_bool_t online_status; /**< Out online status. */
- pjsua_srv_pres pres_srv_list; /**< Server subscription list. */
-
- unsigned buddy_cnt;
- pjsua_buddy buddies[PJSUA_MAX_BUDDIES];
+ int buddy_cnt;
+ pjsua_buddy buddies[PJSUA_MAX_BUDDIES];
};
@@ -255,81 +288,69 @@ pj_status_t pjsua_start(void);
pj_status_t pjsua_destroy(void);
-/*****************************************************************************
- * PJSUA Invite session API (defined in pjsua_inv.c).
- */
-
/**
- * Make outgoing call.
+ * Find account for incoming request.
*/
-pj_status_t pjsua_invite(const char *cstr_dest_uri,
- struct pjsua_inv_data **p_inv_data);
+int pjsua_find_account_for_incoming(pjsip_rx_data *rdata);
/**
- * Handle incoming invite request.
+ * Find account for outgoing request.
*/
-pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata);
+int pjsua_find_account_for_outgoing(const pj_str_t *url);
-/**
- * Hangup call.
+/*****************************************************************************
+ * PJSUA Call API (defined in pjsua_call.c).
*/
-void pjsua_inv_hangup(struct pjsua_inv_data *inv_session, int code);
-
/**
- * Put call on-hold.
+ * Init pjsua call module.
*/
-void pjsua_inv_set_hold(struct pjsua_inv_data *inv_session);
-
+pj_status_t pjsua_call_init(void);
/**
- * Send re-INVITE (to release hold).
+ * Make outgoing call.
*/
-void pjsua_inv_reinvite(struct pjsua_inv_data *inv_session);
+pj_status_t pjsua_make_call(int acc_index,
+ const char *cstr_dest_uri,
+ int *p_call_index);
/**
- * Transfer call.
+ * Handle incoming invite request.
*/
-void pjsua_inv_xfer_call(struct pjsua_inv_data *inv_session,
- const char *dest);
+pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata);
/**
- * Callback to be called by session when invite session's state has changed.
+ * Answer call.
*/
-void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e);
-
+void pjsua_call_answer(int call_index, int code);
/**
- * Callback to be called by session when outgoing dialog has forked.
- * This function will create a forked dialog.
+ * Hangup call.
*/
-void pjsua_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e);
+void pjsua_call_hangup(int call_index, int code);
/**
- * 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.
+ * Put call on-hold.
*/
-void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status);
+void pjsua_call_set_hold(int call_index);
+
/**
- * Callback called when invite session received new offer.
+ * Send re-INVITE (to release hold).
*/
-void pjsua_inv_on_rx_offer( pjsip_inv_session *inv,
- const pjmedia_sdp_session *offer);
+void pjsua_call_reinvite(int call_index);
+
/**
- * Callback to receive transaction state inside invite session or dialog
- * (e.g. REFER, MESSAGE).
+ * Transfer call.
*/
-void pjsua_inv_on_tsx_state_changed(pjsip_inv_session *inv,
- pjsip_transaction *tsx,
- pjsip_event *e);
+void pjsua_call_xfer(int call_index, const char *dest);
+
/**
* Terminate all calls.
@@ -346,13 +367,13 @@ void pjsua_inv_shutdown(void);
*
* @param app_callback Optional callback
*/
-pj_status_t pjsua_regc_init(void);
+pj_status_t pjsua_regc_init(int acc_index);
/**
* Update registration or perform unregistration. If renew argument is zero,
* this will start unregistration process.
*/
-void pjsua_regc_update(pj_bool_t renew);
+void pjsua_regc_update(int acc_index, pj_bool_t renew);
@@ -369,7 +390,7 @@ pj_status_t pjsua_pres_init();
/**
* Refresh both presence client and server subscriptions.
*/
-void pjsua_pres_refresh(void);
+void pjsua_pres_refresh(int acc_index);
/**
* Terminate all subscriptions
@@ -392,12 +413,12 @@ void pjsua_pres_dump(void);
/**
* Notify UI when invite state has changed.
*/
-void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e);
+void pjsua_ui_inv_on_state_changed(int call_index, pjsip_event *e);
/**
* Notify UI when registration status has changed.
*/
-void pjsua_ui_regc_on_state_changed(int code);
+void pjsua_ui_regc_on_state_changed(int acc_index);
/*****************************************************************************
@@ -419,6 +440,11 @@ pj_status_t pjsua_parse_args(int argc, char *argv[]);
pj_status_t pjsua_load_settings(const char *filename);
/**
+ * Dump settings.
+ */
+int pjsua_dump_settings(char *buf, pj_size_t max);
+
+/**
* Save settings to a file.
*/
pj_status_t pjsua_save_settings(const char *filename);
diff --git a/pjsip/src/pjsua/pjsua_inv.c b/pjsip/src/pjsua/pjsua_call.c
index f2994558..e0a9a0af 100644
--- a/pjsip/src/pjsua/pjsua_inv.c
+++ b/pjsip/src/pjsua/pjsua_call.c
@@ -32,39 +32,39 @@
/**
* Make outgoing call.
*/
-pj_status_t pjsua_invite(const char *cstr_dest_uri,
- struct pjsua_inv_data **p_inv_data)
+pj_status_t pjsua_make_call(int acc_index,
+ const char *cstr_dest_uri,
+ int *p_call_index)
{
pj_str_t dest_uri;
pjsip_dialog *dlg;
pjmedia_sdp_session *offer;
pjsip_inv_session *inv;
- struct pjsua_inv_data *inv_data;
+ int call_index = -1;
pjsip_tx_data *tdata;
- int med_sk_index = 0;
pj_status_t status;
/* Convert cstr_dest_uri to dest_uri */
dest_uri = pj_str((char*)cstr_dest_uri);
- /* Find free socket. */
- for (med_sk_index=0; med_sk_index<PJSUA_MAX_CALLS; ++med_sk_index) {
- if (!pjsua.med_sock_use[med_sk_index])
+ /* Find free call slot. */
+ for (call_index=0; call_index<pjsua.max_calls; ++call_index) {
+ if (pjsua.calls[call_index].inv == NULL)
break;
}
- if (med_sk_index == PJSUA_MAX_CALLS) {
+ if (call_index == pjsua.max_calls) {
PJ_LOG(3,(THIS_FILE, "Error: too many calls!"));
return PJ_ETOOMANY;
}
- pjsua.med_sock_use[med_sk_index] = 1;
-
/* Create outgoing dialog: */
- status = pjsip_dlg_create_uac( pjsip_ua_instance(), &pjsua.local_uri,
- &pjsua.contact_uri, &dest_uri, &dest_uri,
+ status = pjsip_dlg_create_uac( pjsip_ua_instance(),
+ &pjsua.acc[acc_index].local_uri,
+ &pjsua.acc[acc_index].contact_uri,
+ &dest_uri, &dest_uri,
&dlg);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Dialog creation failed", status);
@@ -73,8 +73,8 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri,
/* Get media capability from media endpoint: */
- status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool,
- 1, &pjsua.med_sock_info[med_sk_index],
+ status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool, 1,
+ &pjsua.calls[call_index].skinfo,
&offer);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
@@ -92,17 +92,16 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri,
/* Create and associate our data in the session. */
- inv_data = pj_pool_zalloc( dlg->pool, sizeof(struct pjsua_inv_data));
- inv_data->inv = inv;
- inv_data->call_slot = med_sk_index;
- dlg->mod_data[pjsua.mod.id] = inv_data;
- inv->mod_data[pjsua.mod.id] = inv_data;
+ pjsua.calls[call_index].inv = inv;
+
+ dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
+ inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
/* Set dialog Route-Set: */
- if (!pj_list_empty(&pjsua.route_set))
- pjsip_dlg_set_route_set(dlg, &pjsua.route_set);
+ if (!pj_list_empty(&pjsua.acc[acc_index].route_set))
+ pjsip_dlg_set_route_set(dlg, &pjsua.acc[acc_index].route_set);
/* Set credentials: */
@@ -121,19 +120,10 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri,
}
- /* Add invite session to the list. */
-
- pj_list_push_back(&pjsua.inv_list, inv_data);
-
-
/* Send initial INVITE: */
status = pjsip_inv_send_msg(inv, tdata, NULL);
if (status != PJ_SUCCESS) {
- /*
- * Note:
- * inv_data will be removed from the list in the callback
- */
pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
status);
goto on_error;
@@ -141,16 +131,20 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri,
/* Done. */
- if (p_inv_data)
- *p_inv_data = inv_data;
+
+ ++pjsua.call_cnt;
+
+ if (p_call_index)
+ *p_call_index = call_index;
return PJ_SUCCESS;
on_error:
-
PJ_TODO(DESTROY_DIALOG_ON_FAIL);
- pjsua.med_sock_use[med_sk_index] = 0;
+ if (call_index != -1) {
+ pjsua.calls[call_index].inv = NULL;
+ }
return status;
}
@@ -158,7 +152,7 @@ on_error:
/**
* Handle incoming INVITE request.
*/
-pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
+pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
{
pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
@@ -166,9 +160,9 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
pjsip_tx_data *response = NULL;
unsigned options = 0;
pjsip_inv_session *inv;
- struct pjsua_inv_data *inv_data;
+ int acc_index;
+ int call_index = -1;
pjmedia_sdp_session *answer;
- int med_sk_index;
pj_status_t status;
/* Don't want to handle anything but INVITE */
@@ -214,12 +208,12 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
*/
/* Find free call slot. */
- for (med_sk_index=0; med_sk_index<PJSUA_MAX_CALLS; ++med_sk_index) {
- if (!pjsua.med_sock_use[med_sk_index])
+ for (call_index=0; call_index < pjsua.max_calls; ++call_index) {
+ if (pjsua.calls[call_index].inv == NULL)
break;
}
- if (med_sk_index == PJSUA_MAX_CALLS) {
+ if (call_index == PJSUA_MAX_CALLS) {
pjsip_endpt_respond_stateless(pjsua.endpt, rdata,
PJSIP_SC_BUSY_HERE, NULL,
NULL, NULL);
@@ -227,32 +221,35 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
}
- pjsua.med_sock_use[med_sk_index] = 1;
-
/* Get media capability from media endpoint: */
- status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool,
- 1, &pjsua.med_sock_info[med_sk_index],
+ status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool, 1,
+ &pjsua.calls[call_index].skinfo,
&answer );
if (status != PJ_SUCCESS) {
pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
NULL, NULL);
- /* Free call socket. */
- pjsua.med_sock_use[med_sk_index] = 0;
return PJ_TRUE;
}
+ /* TODO:
+ *
+ * Get which account is most likely to be associated with this incoming
+ * call. We need the account to find which contact URI to put for
+ * the call.
+ */
+ acc_index = 0;
+
/* Create dialog: */
status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
- &pjsua.contact_uri, &dlg);
+ &pjsua.acc[acc_index].contact_uri,
+ &dlg);
if (status != PJ_SUCCESS) {
pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
NULL, NULL);
- /* Free call socket. */
- pjsua.med_sock_use[med_sk_index] = 0;
return PJ_TRUE;
}
@@ -264,9 +261,6 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
pjsip_dlg_respond(dlg, rdata, 500, NULL);
- /* Free call socket. */
- pjsua.med_sock_use[med_sk_index] = 0;
-
// TODO: Need to delete dialog
return PJ_TRUE;
}
@@ -274,13 +268,10 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
/* Create and attach pjsua data to the dialog: */
- inv_data = pj_pool_zalloc(dlg->pool, sizeof(struct pjsua_inv_data));
- inv_data->inv = inv;
- inv_data->call_slot = inv_data->call_slot = med_sk_index;
- dlg->mod_data[pjsua.mod.id] = inv_data;
- inv->mod_data[pjsua.mod.id] = inv_data;
+ pjsua.calls[call_index].inv = inv;
- pj_list_push_back(&pjsua.inv_list, inv_data);
+ dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
+ inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
/* Must answer with some response to initial INVITE.
@@ -296,9 +287,6 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
pjsip_dlg_respond(dlg, rdata, 500, NULL);
- /* Free call socket. */
- pjsua.med_sock_use[med_sk_index] = 0;
-
// TODO: Need to delete dialog
} else {
@@ -328,6 +316,8 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
pjsip_get_status_text(pjsua.auto_answer)->ptr ));
}
+ ++pjsua.call_cnt;
+
/* This INVITE request has been handled. */
return PJ_TRUE;
}
@@ -337,22 +327,20 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
* This callback receives notification from invite session when the
* session state has changed.
*/
-void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
+static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
+ pjsip_event *e)
{
- struct pjsua_inv_data *inv_data;
-
- inv_data = inv->dlg->mod_data[pjsua.mod.id];
+ pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id];
/* If this is an outgoing INVITE that was created because of
* REFER/transfer, send NOTIFY to transferer.
*/
- if (inv_data && inv_data->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE)
- {
+ if (call && call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
int st_code = -1;
pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
- switch (inv->state) {
+ switch (call->inv->state) {
case PJSIP_INV_STATE_NULL:
case PJSIP_INV_STATE_CALLING:
/* Do nothing */
@@ -382,13 +370,13 @@ void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
pjsip_tx_data *tdata;
pj_status_t status;
- status = pjsip_xfer_notify( inv_data->xfer_sub,
+ status = pjsip_xfer_notify( call->xfer_sub,
ev_state, st_code,
NULL, &tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
} else {
- status = pjsip_xfer_send_request(inv_data->xfer_sub, tdata);
+ status = pjsip_xfer_send_request(call->xfer_sub, tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
}
@@ -397,28 +385,26 @@ void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
}
+ pjsua_ui_inv_on_state_changed(call->index, e);
+
+ /* call->inv may be NULL now */
+
/* Destroy media session when invite session is disconnected. */
if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
- pj_assert(inv_data != NULL);
+ pj_assert(call != NULL);
- if (inv_data && inv_data->session) {
- pjmedia_conf_remove_port(pjsua.mconf, inv_data->conf_slot);
- pjmedia_session_destroy(inv_data->session);
- pjsua.med_sock_use[inv_data->call_slot] = 0;
- inv_data->session = NULL;
+ if (call && call->session) {
+ pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot);
+ pjmedia_session_destroy(call->session);
+ call->session = NULL;
PJ_LOG(3,(THIS_FILE,"Media session is destroyed"));
}
- if (inv_data) {
-
- pj_list_erase(inv_data);
-
- }
+ call->inv = NULL;
+ --pjsua.call_cnt;
}
-
- pjsua_ui_inv_on_state_changed(inv, e);
}
@@ -436,14 +422,14 @@ static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
* clear the xfer_sub member of the inv_data.
*/
if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
- struct pjsua_inv_data *inv_data;
+ pjsua_call *call;
- inv_data = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
- if (!inv_data)
+ call = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
+ if (!call)
return;
pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
- inv_data->xfer_sub = NULL;
+ call->xfer_sub = NULL;
PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
}
@@ -458,13 +444,16 @@ static void on_call_transfered( pjsip_inv_session *inv,
{
pj_status_t status;
pjsip_tx_data *tdata;
- struct pjsua_inv_data *inv_data;
+ pjsua_call *existing_call;
+ int new_call;
const pj_str_t str_refer_to = { "Refer-To", 8};
pjsip_generic_string_hdr *refer_to;
char *uri;
struct pjsip_evsub_user xfer_cb;
pjsip_evsub *sub;
+ existing_call = inv->dlg->mod_data[pjsua.mod.id];
+
/* Find the Refer-To header */
refer_to = (pjsip_generic_string_hdr*)
pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
@@ -523,7 +512,7 @@ static void on_call_transfered( pjsip_inv_session *inv,
uri[refer_to->hvalue.slen] = '\0';
/* Now make the outgoing call. */
- status = pjsua_invite(uri, &inv_data);
+ status = pjsua_make_call(existing_call->acc_index, uri, &new_call);
if (status != PJ_SUCCESS) {
/* Notify xferer about the error */
@@ -547,10 +536,10 @@ static void on_call_transfered( pjsip_inv_session *inv,
* Subsequent state changed in pjsua_inv_on_state_changed() will be
* reported back to the server subscription.
*/
- inv_data->xfer_sub = sub;
+ pjsua.calls[new_call].xfer_sub = sub;
/* Put the invite_data in the subscription. */
- pjsip_evsub_set_mod_data(sub, pjsua.mod.id, inv_data);
+ pjsip_evsub_set_mod_data(sub, pjsua.mod.id, &pjsua.calls[new_call]);
}
@@ -558,10 +547,12 @@ static void on_call_transfered( pjsip_inv_session *inv,
* This callback is called when transaction state has changed in INVITE
* session. We use this to trap incoming REFER request.
*/
-void pjsua_inv_on_tsx_state_changed(pjsip_inv_session *inv,
- pjsip_transaction *tsx,
- pjsip_event *e)
+static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
+ pjsip_transaction *tsx,
+ pjsip_event *e)
{
+ pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id];
+
if (tsx->role==PJSIP_ROLE_UAS &&
tsx->state==PJSIP_TSX_STATE_TRYING &&
pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
@@ -569,7 +560,7 @@ void pjsua_inv_on_tsx_state_changed(pjsip_inv_session *inv,
/*
* Incoming REFER request.
*/
- on_call_transfered(inv, e->body.tsx_state.src.rdata);
+ on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
}
}
@@ -578,7 +569,8 @@ void pjsua_inv_on_tsx_state_changed(pjsip_inv_session *inv,
* This callback is called by invite session framework when UAC session
* has forked.
*/
-void pjsua_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e)
+static void pjsua_call_on_forked( pjsip_inv_session *inv,
+ pjsip_event *e)
{
PJ_UNUSED_ARG(inv);
PJ_UNUSED_ARG(e);
@@ -590,7 +582,7 @@ void pjsua_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e)
/*
* Create inactive SDP for call hold.
*/
-static pj_status_t create_inactive_sdp(struct pjsua_inv_data *inv_session,
+static pj_status_t create_inactive_sdp(pjsua_call *call,
pjmedia_sdp_session **p_answer)
{
pj_status_t status;
@@ -600,8 +592,7 @@ static pj_status_t create_inactive_sdp(struct pjsua_inv_data *inv_session,
/* Create new offer */
status = pjmedia_endpt_create_sdp(pjsua.med_endpt, pjsua.pool, 1,
- &pjsua.med_sock_info[inv_session->call_slot],
- &sdp);
+ &call->skinfo, &sdp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
return status;
@@ -633,16 +624,16 @@ static pj_status_t create_inactive_sdp(struct pjsua_inv_data *inv_session,
/*
* Called when session received new offer.
*/
-void pjsua_inv_on_rx_offer( pjsip_inv_session *inv,
- const pjmedia_sdp_session *offer)
+static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
+ const pjmedia_sdp_session *offer)
{
- struct pjsua_inv_data *inv_data;
+ pjsua_call *call;
pjmedia_sdp_conn *conn;
pjmedia_sdp_session *answer;
pj_bool_t is_remote_active;
pj_status_t status;
- inv_data = inv->dlg->mod_data[pjsua.mod.id];
+ call = inv->dlg->mod_data[pjsua.mod.id];
/*
* See if remote is offering active media (i.e. not on-hold)
@@ -669,11 +660,10 @@ void pjsua_inv_on_rx_offer( pjsip_inv_session *inv,
/* Supply candidate answer */
if (is_remote_active) {
- status = pjmedia_endpt_create_sdp( pjsua.med_endpt, inv->pool, 1,
- &pjsua.med_sock_info[inv_data->call_slot],
- &answer);
+ status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
+ &call->skinfo, &answer);
} else {
- status = create_inactive_sdp( inv_data, &answer );
+ status = create_inactive_sdp( call, &answer );
}
if (status != PJ_SUCCESS) {
@@ -681,7 +671,7 @@ void pjsua_inv_on_rx_offer( pjsip_inv_session *inv,
return;
}
- status = pjsip_inv_set_sdp_answer(inv, answer);
+ status = pjsip_inv_set_sdp_answer(call->inv, answer);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to set answer", status);
return;
@@ -695,15 +685,18 @@ void pjsua_inv_on_rx_offer( pjsip_inv_session *inv,
* in the session. This function will start/update media if negotiation
* has succeeded.
*/
-void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
+static void pjsua_call_on_media_update(pjsip_inv_session *inv,
+ pj_status_t status)
{
- struct pjsua_inv_data *inv_data;
+ pjsua_call *call;
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];
+ call = inv->dlg->mod_data[pjsua.mod.id];
+
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
@@ -713,17 +706,15 @@ void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
/* Destroy existing media session, if any. */
- inv_data = inv->dlg->mod_data[pjsua.mod.id];
- if (inv_data && inv_data->session) {
- pjmedia_conf_remove_port(pjsua.mconf, inv_data->conf_slot);
- pjmedia_session_destroy(inv_data->session);
- pjsua.med_sock_use[inv_data->call_slot] = 0;
- inv_data->session = NULL;
+ if (call && call->session) {
+ pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot);
+ pjmedia_session_destroy(call->session);
+ call->session = NULL;
}
/* Get local and remote SDP */
- status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
+ status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Unable to retrieve currently active local SDP",
@@ -732,7 +723,7 @@ void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
}
- status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
+ status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Unable to retrieve currently active remote SDP",
@@ -747,10 +738,10 @@ void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
return;
status = pjmedia_session_create( pjsua.med_endpt, 1,
- &pjsua.med_sock_info[inv_data->call_slot],
+ &call->skinfo,
local_sdp, remote_sdp,
- inv_data,
- &inv_data->session );
+ call,
+ &call->session );
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create media session",
status);
@@ -761,7 +752,7 @@ void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
/* 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(inv_data->session, 0, &media_port);
+ pjmedia_session_get_port(call->session, 0, &media_port);
/*
@@ -769,38 +760,63 @@ void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
*/
port_name.ptr = tmp;
port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
- inv_data->inv->dlg->remote.info->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.mconf, inv->pool,
+ status = pjmedia_conf_add_port( pjsua.mconf, call->inv->pool,
media_port,
&port_name,
- &inv_data->conf_slot);
+ &call->conf_slot);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create conference slot",
status);
- pjmedia_session_destroy(inv_data->session);
- inv_data->session = NULL;
+ pjmedia_session_destroy(call->session);
+ call->session = NULL;
return;
}
/* If auto-play is configured, connect the call to the file player
* port
*/
- if (pjsua.wav_file && inv->role == PJSIP_ROLE_UAS) {
+ if (pjsua.auto_play && pjsua.wav_file &&
+ call->inv->role == PJSIP_ROLE_UAS)
+ {
pjmedia_conf_connect_port( pjsua.mconf, pjsua.wav_slot,
- inv_data->conf_slot);
+ call->conf_slot);
+
+ } else if (pjsua.auto_loop && call->inv->role == PJSIP_ROLE_UAS) {
+
+ pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot,
+ call->conf_slot);
+
+ } else if (pjsua.auto_conf) {
+
+ int i;
+
+ pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot);
+ pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0);
+
+ for (i=0; i < pjsua.max_calls; ++i) {
+
+ if (!pjsua.calls[i].session)
+ continue;
+
+ pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot,
+ pjsua.calls[i].conf_slot);
+ pjmedia_conf_connect_port( pjsua.mconf, pjsua.calls[i].conf_slot,
+ call->conf_slot);
+ }
} else {
/* Connect new call to the sound device port (port zero) in the
* main conference bridge.
*/
- pjmedia_conf_connect_port( pjsua.mconf, 0, inv_data->conf_slot);
- pjmedia_conf_connect_port( pjsua.mconf, inv_data->conf_slot, 0);
+ pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot);
+ pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0);
}
@@ -811,7 +827,7 @@ void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
int info_len = 0;
unsigned i;
- pjmedia_session_get_info(inv_data->session, &sess_info);
+ pjmedia_session_get_info(call->session, &sess_info);
for (i=0; i<sess_info.stream_cnt; ++i) {
int len;
const char *dir;
@@ -850,13 +866,21 @@ void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
/*
* Hangup call.
*/
-void pjsua_inv_hangup(struct pjsua_inv_data *inv_session, int code)
+void pjsua_call_hangup(int call_index, int code)
{
+ pjsua_call *call;
pj_status_t status;
pjsip_tx_data *tdata;
- status = pjsip_inv_end_session(inv_session->inv,
- code, NULL, &tdata);
+
+ call = &pjsua.calls[call_index];
+
+ if (!call->inv) {
+ PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+ return;
+ }
+
+ status = pjsip_inv_end_session(call->inv, code, NULL, &tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Failed to create end session message",
@@ -871,7 +895,7 @@ void pjsua_inv_hangup(struct pjsua_inv_data *inv_session, int code)
if (tdata == NULL)
return;
- status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL);
+ status = pjsip_inv_send_msg(call->inv, tdata, NULL);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Failed to send end session message",
@@ -884,30 +908,37 @@ void pjsua_inv_hangup(struct pjsua_inv_data *inv_session, int code)
/*
* Put call on-Hold.
*/
-void pjsua_inv_set_hold(struct pjsua_inv_data *inv_session)
+void pjsua_call_set_hold(int call_index)
{
pjmedia_sdp_session *sdp;
- pjsip_inv_session *inv = inv_session->inv;
+ pjsua_call *call;
pjsip_tx_data *tdata;
pj_status_t status;
- if (inv->state != PJSIP_INV_STATE_CONFIRMED) {
+ call = &pjsua.calls[call_index];
+
+ if (!call->inv) {
+ PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+ return;
+ }
+
+ if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
return;
}
- status = create_inactive_sdp(inv_session, &sdp);
+ status = create_inactive_sdp(call, &sdp);
if (status != PJ_SUCCESS)
return;
/* Send re-INVITE with new offer */
- status = pjsip_inv_reinvite( inv_session->inv, NULL, sdp, &tdata);
+ status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
return;
}
- status = pjsip_inv_send_msg( inv_session->inv, tdata, NULL);
+ status = pjsip_inv_send_msg( call->inv, tdata, NULL);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
return;
@@ -918,36 +949,43 @@ void pjsua_inv_set_hold(struct pjsua_inv_data *inv_session)
/*
* re-INVITE.
*/
-void pjsua_inv_reinvite(struct pjsua_inv_data *inv_session)
+void pjsua_call_reinvite(int call_index)
{
pjmedia_sdp_session *sdp;
pjsip_tx_data *tdata;
- pjsip_inv_session *inv = inv_session->inv;
+ pjsua_call *call;
pj_status_t status;
+ call = &pjsua.calls[call_index];
+
+ if (!call->inv) {
+ PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+ return;
+ }
+
- if (inv->state != PJSIP_INV_STATE_CONFIRMED) {
+ if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
return;
}
/* Create SDP */
- status = pjmedia_endpt_create_sdp( pjsua.med_endpt, inv->pool, 1,
- &pjsua.med_sock_info[inv_session->call_slot],
- &sdp);
+ status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
+ &call->skinfo, &sdp);
if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", status);
+ pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
+ status);
return;
}
/* Send re-INVITE with new offer */
- status = pjsip_inv_reinvite( inv_session->inv, NULL, sdp, &tdata);
+ status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
return;
}
- status = pjsip_inv_send_msg( inv_session->inv, tdata, NULL);
+ status = pjsip_inv_send_msg( call->inv, tdata, NULL);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
return;
@@ -958,20 +996,27 @@ void pjsua_inv_reinvite(struct pjsua_inv_data *inv_session)
/*
* Transfer call.
*/
-void pjsua_inv_xfer_call(struct pjsua_inv_data *inv_session,
- const char *dest)
+void pjsua_call_xfer(int call_index, const char *dest)
{
pjsip_evsub *sub;
pjsip_tx_data *tdata;
+ pjsua_call *call;
pj_str_t tmp;
pj_status_t status;
-
+
+ call = &pjsua.calls[call_index];
+
+ if (!call->inv) {
+ PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+ return;
+ }
+
/* Create xfer client subscription.
* We're not interested in knowing the transfer result, so we
* put NULL as the callback.
*/
- status = pjsip_xfer_create_uac(inv_session->inv->dlg, NULL, &sub);
+ status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create xfer", status);
return;
@@ -1005,21 +1050,41 @@ void pjsua_inv_xfer_call(struct pjsua_inv_data *inv_session,
*/
void pjsua_inv_shutdown()
{
- struct pjsua_inv_data *inv_data, *next;
+ int i;
- inv_data = pjsua.inv_list.next;
- while (inv_data != &pjsua.inv_list) {
+ for (i=0; i<pjsua.max_calls; ++i) {
pjsip_tx_data *tdata;
+ pjsua_call *call;
- next = inv_data->next;
+ if (pjsua.calls[i].inv == NULL)
+ continue;
- if (pjsip_inv_end_session(inv_data->inv, 410, NULL, &tdata)==0) {
+ call = &pjsua.calls[i];
+
+ if (pjsip_inv_end_session(call->inv, 410, NULL, &tdata)==0) {
if (tdata)
- pjsip_inv_send_msg(inv_data->inv, tdata, NULL);
+ pjsip_inv_send_msg(call->inv, tdata, NULL);
}
-
- inv_data = next;
}
}
+pj_status_t pjsua_call_init(void)
+{
+ /* Initialize invite session callback. */
+ pjsip_inv_callback inv_cb;
+ pj_status_t status;
+
+ pj_memset(&inv_cb, 0, sizeof(inv_cb));
+ inv_cb.on_state_changed = &pjsua_call_on_state_changed;
+ inv_cb.on_new_session = &pjsua_call_on_forked;
+ inv_cb.on_media_update = &pjsua_call_on_media_update;
+ inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
+ inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
+
+
+ /* Initialize invite session module: */
+ status = pjsip_inv_usage_init(pjsua.endpt, &pjsua.mod, &inv_cb);
+
+ return status;
+}
diff --git a/pjsip/src/pjsua/pjsua_core.c b/pjsip/src/pjsua/pjsua_core.c
index f5c35468..974c6d53 100644
--- a/pjsip/src/pjsua/pjsua_core.c
+++ b/pjsip/src/pjsua/pjsua_core.c
@@ -45,6 +45,8 @@ struct pjsua pjsua;
*/
void pjsua_default(void)
{
+ unsigned i;
+
/* Normally need another thread for console application, because main
* thread will be blocked in fgets().
@@ -53,44 +55,38 @@ void pjsua_default(void)
/* Default transport settings: */
-
pjsua.sip_port = 5060;
/* Default logging settings: */
-
pjsua.log_level = 5;
pjsua.app_log_level = 4;
pjsua.log_decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
- /* Default: do not use STUN: */
+ /* Default: do not use STUN: */
pjsua.stun_port1 = pjsua.stun_port2 = 0;
- /* Default URIs: */
-
- pjsua.local_uri = pj_str(PJSUA_LOCAL_URI);
-
- /* Default registration timeout: */
-
- pjsua.reg_timeout = 55;
-
- /* Default maximum conference ports: */
-
- pjsua.max_ports = 8;
-
- /* Init route set list: */
-
- pj_list_init(&pjsua.route_set);
+ /* Init accounts: */
+ pjsua.acc_cnt = 1;
+ for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
+ pjsua.acc[i].index = i;
+ pjsua.acc[i].local_uri = pj_str(PJSUA_LOCAL_URI);
+ pjsua.acc[i].reg_timeout = 55;
+ pj_list_init(&pjsua.acc[i].route_set);
+ pj_list_init(&pjsua.acc[i].pres_srv_list);
+ }
- /* Init invite session list: */
+ /* Init call array: */
+ for (i=0; i<PJ_ARRAY_SIZE(pjsua.calls); ++i)
+ pjsua.calls[i].index = i;
- pj_list_init(&pjsua.inv_list);
+ /* Default max nb of calls. */
+ pjsua.max_calls = 4;
/* Init server presence subscription list: */
- pj_list_init(&pjsua.pres_srv_list);
}
@@ -110,8 +106,7 @@ static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
- return pjsua_inv_on_incoming(rdata);
-
+ return pjsua_call_on_incoming(rdata);
}
return PJ_FALSE;
@@ -145,7 +140,7 @@ static pj_status_t init_sockets(pj_bool_t sip,
enum {
RTP_START_PORT = 4000,
RTP_RANDOM_START = 2,
- RTP_RETRY = 20
+ RTP_RETRY = 100
};
enum {
SIP_SOCK,
@@ -240,37 +235,49 @@ static pj_status_t init_sockets(pj_bool_t sip,
for (i=0; i<3; ++i)
pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
- if (sip)
- mapped_addr[SIP_SOCK].sin_port = pj_htons((pj_uint16_t)pjsua.sip_port);
- mapped_addr[RTP_SOCK].sin_port = pj_htons((pj_uint16_t)rtp_port);
- mapped_addr[RTCP_SOCK].sin_port = pj_htons((pj_uint16_t)(rtp_port+1));
+ if (sip) {
+ mapped_addr[SIP_SOCK].sin_port =
+ pj_htons((pj_uint16_t)pjsua.sip_port);
+ }
+ mapped_addr[RTP_SOCK].sin_port=pj_htons((pj_uint16_t)rtp_port);
+ mapped_addr[RTCP_SOCK].sin_port=pj_htons((pj_uint16_t)(rtp_port+1));
break;
+
} else {
- status = pj_stun_get_mapped_addr( &pjsua.cp.factory, 3, sock,
- &pjsua.stun_srv1, pjsua.stun_port1,
- &pjsua.stun_srv2, pjsua.stun_port2,
- mapped_addr);
+ status=pj_stun_get_mapped_addr(&pjsua.cp.factory, 3, sock,
+ &pjsua.stun_srv1, pjsua.stun_port1,
+ &pjsua.stun_srv2, pjsua.stun_port2,
+ mapped_addr);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "STUN error", status);
goto on_error;
}
- if (pj_ntohs(mapped_addr[2].sin_port) == pj_ntohs(mapped_addr[1].sin_port)+1)
+ if (pj_ntohs(mapped_addr[2].sin_port) ==
+ pj_ntohs(mapped_addr[1].sin_port)+1)
+ {
break;
+ }
- pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
- pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
+ pj_sock_close(sock[RTP_SOCK]);
+ sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+
+ pj_sock_close(sock[RTCP_SOCK]);
+ sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
}
}
if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) {
- PJ_LOG(1,(THIS_FILE, "Unable to find appropriate RTP/RTCP ports combination"));
+ PJ_LOG(1,(THIS_FILE,
+ "Unable to find appropriate RTP/RTCP ports combination"));
goto on_error;
}
if (sip) {
pjsua.sip_sock = sock[SIP_SOCK];
- pj_memcpy(&pjsua.sip_sock_name, &mapped_addr[SIP_SOCK], sizeof(pj_sockaddr_in));
+ pj_memcpy(&pjsua.sip_sock_name,
+ &mapped_addr[SIP_SOCK],
+ sizeof(pj_sockaddr_in));
} else {
pj_sock_close(sock[0]);
}
@@ -391,30 +398,13 @@ static pj_status_t init_stack(void)
/* Initialize invite session module: */
- {
-
- /* Initialize invite session callback. */
- pjsip_inv_callback inv_cb;
-
- pj_memset(&inv_cb, 0, sizeof(inv_cb));
- inv_cb.on_state_changed = &pjsua_inv_on_state_changed;
- inv_cb.on_new_session = &pjsua_inv_on_new_session;
- inv_cb.on_media_update = &pjsua_inv_on_media_update;
- inv_cb.on_rx_offer = &pjsua_inv_on_rx_offer;
- inv_cb.on_tsx_state_changed = &pjsua_inv_on_tsx_state_changed;
-
-
- /* Initialize invite session module: */
- status = pjsip_inv_usage_init(pjsua.endpt, &pjsua.mod, &inv_cb);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Invite usage initialization error",
- status);
- goto on_error;
- }
-
+ status = pjsua_call_init();
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Invite usage initialization error",
+ status);
+ goto on_error;
}
-
/* Done */
return PJ_SUCCESS;
@@ -520,7 +510,8 @@ pj_status_t pjsua_init(void)
/* Init conference bridge. */
- status = pjmedia_conf_create(pjsua.pool, pjsua.max_ports,
+ status = pjmedia_conf_create(pjsua.pool,
+ pjsua.max_calls+PJSUA_CONF_MORE_PORTS,
8000, 160, 16, &pjsua.mconf);
if (status != PJ_SUCCESS) {
pj_caching_pool_destroy(&pjsua.cp);
@@ -547,6 +538,67 @@ pj_status_t pjsua_init(void)
}
+/*
+ * Find account for incoming request.
+ */
+int pjsua_find_account_for_incoming(pjsip_rx_data *rdata)
+{
+ pjsip_uri *uri;
+ pjsip_sip_uri *sip_uri;
+ int acc_index;
+
+ uri = rdata->msg_info.to->uri;
+
+ /* Just return account #0 if To URI is not SIP: */
+ if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
+ !PJSIP_URI_SCHEME_IS_SIPS(uri))
+ {
+ return 0;
+ }
+
+
+ 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.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.acc_cnt; ++acc_index) {
+
+ pjsua_acc *acc = &pjsua.acc[acc_index];
+
+ if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) {
+ /* Match ! */
+ return acc_index;
+ }
+ }
+
+ /* Still no match, just return account #0 */
+ return 0;
+}
+
+
+/*
+ * Find account for outgoing request.
+ */
+int pjsua_find_account_for_outgoing(const pj_str_t *url)
+{
+ PJ_UNUSED_ARG(url);
+
+ /* Just use account #0 */
+ return 0;
+}
+
/*
* Start pjsua stack.
@@ -588,11 +640,18 @@ pj_status_t pjsua_start(void)
/* Init sockets (STUN etc): */
- for (i=0; i<PJ_ARRAY_SIZE(pjsua.med_sock_info); ++i) {
- status = init_sockets(i==0, &pjsua.med_sock_info[i]);
+ for (i=0; i<(int)pjsua.max_calls; ++i) {
+ status = init_sockets(i==0, &pjsua.calls[i].skinfo);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "init_sockets() has returned error",
status);
+ --i;
+ if (i >= 0)
+ pj_sock_close(pjsua.sip_sock);
+ while (i >= 0) {
+ pj_sock_close(pjsua.calls[i].skinfo.rtp_sock);
+ pj_sock_close(pjsua.calls[i].skinfo.rtcp_sock);
+ }
return status;
}
}
@@ -623,34 +682,26 @@ pj_status_t pjsua_start(void)
}
/* Initialize Contact URI, if one is not specified: */
-
- if (pjsua.contact_uri.slen == 0 && pjsua.local_uri.slen) {
+ for (i=0; i<pjsua.acc_cnt; ++i) {
pjsip_uri *uri;
pjsip_sip_uri *sip_uri;
- char contact[128];
- int len;
- /* 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.
- */
-
/* Need to parse local_uri to get the elements: */
- uri = pjsip_parse_uri(pjsua.pool, pjsua.local_uri.ptr,
- pjsua.local_uri.slen, 0);
+ uri = pjsip_parse_uri(pjsua.pool, pjsua.acc[i].local_uri.ptr,
+ pjsua.acc[i].local_uri.slen, 0);
if (uri == NULL) {
pjsua_perror(THIS_FILE, "Invalid local URI",
PJSIP_EINVALIDURI);
return PJSIP_EINVALIDURI;
}
-
/* Local URI MUST be a SIP or SIPS: */
- if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) {
+ 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;
@@ -661,39 +712,54 @@ pj_status_t pjsua_start(void)
sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
-
- /* Build temporary contact string. */
-
- if (sip_uri->user.slen) {
-
- /* With the user part. */
- len = pj_snprintf(contact, sizeof(contact),
- "<sip:%.*s@%.*s:%d>",
- (int)sip_uri->user.slen,
- sip_uri->user.ptr,
- (int)udp_transport->local_name.host.slen,
- udp_transport->local_name.host.ptr,
- udp_transport->local_name.port);
- } else {
-
- /* Without user part */
+ pjsua.acc[i].user_part = sip_uri->user;
+ pjsua.acc[i].host_part = sip_uri->host;
- len = pj_snprintf(contact, sizeof(contact),
- "<sip:%.*s:%d>",
- (int)udp_transport->local_name.host.slen,
- udp_transport->local_name.host.ptr,
- udp_transport->local_name.port);
- }
+ if (pjsua.acc[i].contact_uri.slen == 0 &&
+ pjsua.acc[i].local_uri.slen)
+ {
+ char contact[128];
+ int len;
+
+ /* 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_snprintf(contact, sizeof(contact),
+ "<sip:%.*s@%.*s:%d>",
+ (int)sip_uri->user.slen,
+ sip_uri->user.ptr,
+ (int)udp_transport->local_name.host.slen,
+ udp_transport->local_name.host.ptr,
+ udp_transport->local_name.port);
+ } else {
+
+ /* Without user part */
+
+ len = pj_snprintf(contact, sizeof(contact),
+ "<sip:%.*s:%d>",
+ (int)udp_transport->local_name.host.slen,
+ udp_transport->local_name.host.ptr,
+ udp_transport->local_name.port);
+ }
- if (len < 1 || len >= sizeof(contact)) {
- pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG);
- return PJSIP_EURITOOLONG;
- }
+ if (len < 1 || len >= sizeof(contact)) {
+ pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG);
+ return PJSIP_EURITOOLONG;
+ }
- /* Duplicate Contact uri. */
+ /* Duplicate Contact uri. */
- pj_strdup2(pjsua.pool, &pjsua.contact_uri, contact);
+ pj_strdup2(pjsua.pool, &pjsua.acc[i].contact_uri, contact);
+ }
}
/* If outbound_proxy is specified, put it in the route_set: */
@@ -714,7 +780,9 @@ pj_status_t pjsua_start(void)
return PJSIP_EINVALIDURI;
}
- pj_list_push_back(&pjsua.route_set, route);
+ for (i=0; i<pjsua.acc_cnt; ++i) {
+ pj_list_push_front(&pjsua.acc[i].route_set, route);
+ }
}
@@ -736,14 +804,22 @@ pj_status_t pjsua_start(void)
/* Start registration: */
/* Create client registration session: */
+ for (i=0; i<pjsua.acc_cnt; ++i) {
+ status = pjsua_regc_init(i);
+ if (status != PJ_SUCCESS)
+ return status;
- status = pjsua_regc_init();
- if (status != PJ_SUCCESS)
- return status;
+ /* Perform registration, if required. */
+ if (pjsua.acc[i].regc) {
+ pjsua_regc_update(i, 1);
+ }
+ }
- /* Perform registration, if required. */
- if (pjsua.regc) {
- pjsua_regc_update(1);
+
+ /* Find account for outgoing preence subscription */
+ for (i=0; i<pjsua.buddy_cnt; ++i) {
+ pjsua.buddies[i].acc_index =
+ pjsua_find_account_for_outgoing(&pjsua.buddies[i].uri);
}
@@ -777,6 +853,19 @@ pj_status_t pjsua_destroy(void)
/* Signal threads to quit: */
pjsua.quit_flag = 1;
+ /* Terminate all calls. */
+ pjsua_inv_shutdown();
+
+ /* Terminate all presence subscriptions. */
+ pjsua_pres_shutdown();
+
+ /* Unregister, if required: */
+ for (i=0; i<pjsua.acc_cnt; ++i) {
+ if (pjsua.acc[i].regc) {
+ pjsua_regc_update(i, 0);
+ }
+ }
+
/* Wait worker threads to quit: */
for (i=0; i<pjsua.thread_cnt; ++i) {
@@ -788,17 +877,6 @@ pj_status_t pjsua_destroy(void)
}
- /* Terminate all calls. */
- pjsua_inv_shutdown();
-
- /* Terminate all presence subscriptions. */
- pjsua_pres_shutdown();
-
- /* Unregister, if required: */
- if (pjsua.regc) {
- pjsua_regc_update(0);
- }
-
/* Wait for some time to allow unregistration to complete: */
PJ_LOG(4,(THIS_FILE, "Shutting down..."));
busy_sleep(1000);
diff --git a/pjsip/src/pjsua/pjsua_opt.c b/pjsip/src/pjsua/pjsua_opt.c
index 52819265..5ec37980 100644
--- a/pjsip/src/pjsua/pjsua_opt.c
+++ b/pjsip/src/pjsua/pjsua_opt.c
@@ -43,8 +43,6 @@ static void usage(void)
puts("Usage:");
puts(" pjsua [options]");
puts("");
- puts(" [sip-url] Default URL to invite.");
- puts("");
puts("General options:");
puts(" --help Display this help screen");
puts(" --version Display version info");
@@ -55,36 +53,43 @@ static void usage(void)
puts(" --log-level=N Set log max level to N (0(none) to 6(trace))");
puts(" --app-log-level=N Set log max level for stdout display to N");
puts("");
- puts("Authentication options:");
- puts(" --realm=string Set realm");
- puts(" --username=string Set authentication username");
- puts(" --password=string Set authentication password");
- puts("");
- puts("SIP options:");
+ puts("SIP Account options:");
puts(" --id=url Set the URL of local ID (used in From header)");
puts(" --contact=url Override the Contact information");
puts(" --proxy=url Set the URL of proxy server");
- //puts(" --outbound=url Set the URL of outbound proxy server");
puts("");
- puts("Registration Options:");
+ puts("SIP Account Registration Options:");
puts(" --registrar=url Set the URL of registrar server");
puts(" --reg-timeout=secs Set registration interval to secs (default 3600)");
puts("");
+ puts("SIP Account Control:");
+ puts(" --next-account Add more account");
+ puts("");
+ puts("Authentication options:");
+ puts(" --realm=string Set realm");
+ puts(" --username=string Set authentication username");
+ puts(" --password=string Set authentication password");
+ puts(" --next-cred Add more credential");
+ puts("");
puts("Transport Options:");
- puts(" --local-port=port Set TCP/UDP port");
+ puts(" --local-port=port Set TCP/UDP port");
+ puts(" --outbound=url Set the URL of outbound proxy server");
puts(" --use-stun1=host[:port]");
- puts(" --use-stun2=host[:port] Use STUN and set host name and port of STUN servers");
+ puts(" --use-stun2=host[:port] Resolve local IP with the specified STUN servers");
puts("");
puts("Media Options:");
puts(" --null-audio Use NULL audio device");
- //puts(" --wav-file=file Play WAV file in conference bridge");
+ puts(" --play-file=file Play WAV file in conference bridge");
+ puts(" --auto-play Automatically play the file (to incoming calls only)");
+ puts(" --auto-loop Automatically loop incoming RTP to outgoing RTP");
+ puts(" --auto-conf Automatically put incoming calls to conference");
puts("");
puts("Buddy List (can be more than one):");
puts(" --add-buddy url Add the specified URL to the buddy list.");
puts("");
puts("User Agent options:");
puts(" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)");
- puts(" --auto-play=file Automatically play WAVE file to incoming calls");
+ puts(" --max-calls=N Maximum number of concurrent calls (default:4, max:255)");
puts("");
fflush(stdout);
}
@@ -199,7 +204,11 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
OPT_USE_STUN1, OPT_USE_STUN2,
OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
- OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY};
+ OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
+ OPT_AUTO_CONF,
+ OPT_PLAY_FILE,
+ OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
+ };
struct option long_options[] = {
{ "config-file",1, 0, OPT_CONFIG_FILE},
{ "log-file", 1, 0, OPT_LOG_FILE},
@@ -225,10 +234,18 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
{ "no-presence", 0, 0, OPT_NO_PRESENCE},
{ "auto-answer",1, 0, OPT_AUTO_ANSWER},
{ "auto-hangup",1, 0, OPT_AUTO_HANGUP},
- { "auto-play", 1, 0, OPT_AUTO_PLAY},
+ { "auto-play", 0, 0, OPT_AUTO_PLAY},
+ { "auto-loop", 0, 0, OPT_AUTO_LOOP},
+ { "auto-conf", 0, 0, OPT_AUTO_CONF},
+ { "play-file", 1, 0, OPT_PLAY_FILE},
+ { "next-account",0,0, OPT_NEXT_ACCOUNT},
+ { "next-cred", 0, 0, OPT_NEXT_CRED},
+ { "max-calls", 1, 0, OPT_MAX_CALLS},
{ NULL, 0, 0, 0}
};
pj_status_t status;
+ pjsua_acc *cur_acc;
+ pjsip_cred_info *cur_cred;
char *config_file = NULL;
/* Run getopt once to see if user specifies config file to read. */
@@ -249,6 +266,10 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
}
+ cur_acc = &pjsua.acc[0];
+ cur_cred = &pjsua.cred_info[0];
+
+
/* Reinitialize and re-run getopt again, possibly with new arguments
* read from config file.
*/
@@ -307,7 +328,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg);
return PJ_EINVAL;
}
- pjsua.proxy = pj_str(optarg);
+ cur_acc->proxy = pj_str(optarg);
break;
case OPT_OUTBOUND_PROXY: /* outbound proxy */
@@ -323,12 +344,12 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg);
return PJ_EINVAL;
}
- pjsua.registrar_uri = pj_str(optarg);
+ cur_acc->reg_uri = pj_str(optarg);
break;
case OPT_REG_TIMEOUT: /* reg-timeout */
- pjsua.reg_timeout = pj_strtoul(pj_cstr(&tmp,optarg));
- if (pjsua.reg_timeout < 1 || pjsua.reg_timeout > 3600) {
+ cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,optarg));
+ if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
printf("Error: invalid value for --reg-timeout (expecting 1-3600)\n");
return PJ_EINVAL;
}
@@ -339,7 +360,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
printf("Error: invalid SIP URL '%s' in local id argument\n", optarg);
return PJ_EINVAL;
}
- pjsua.local_uri = pj_str(optarg);
+ cur_acc->local_uri = pj_str(optarg);
break;
case OPT_CONTACT: /* contact */
@@ -347,23 +368,33 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
printf("Error: invalid SIP URL '%s' in contact argument\n", optarg);
return PJ_EINVAL;
}
- pjsua.contact_uri = pj_str(optarg);
+ cur_acc->contact_uri = pj_str(optarg);
+ break;
+
+ case OPT_NEXT_ACCOUNT: /* Add more account. */
+ pjsua.acc_cnt++;
+ cur_acc = &pjsua.acc[pjsua.acc_cnt - 1];
break;
case OPT_USERNAME: /* Default authentication user */
- if (!pjsua.cred_count) pjsua.cred_count = 1;
- pjsua.cred_info[0].username = pj_str(optarg);
+ if (pjsua.cred_count==0) pjsua.cred_count=1;
+ cur_cred->username = pj_str(optarg);
break;
case OPT_REALM: /* Default authentication realm. */
- if (!pjsua.cred_count) pjsua.cred_count = 1;
- pjsua.cred_info[0].realm = pj_str(optarg);
+ if (pjsua.cred_count==0) pjsua.cred_count=1;
+ cur_cred->realm = pj_str(optarg);
break;
case OPT_PASSWORD: /* authentication password */
- if (!pjsua.cred_count) pjsua.cred_count = 1;
- pjsua.cred_info[0].data_type = 0;
- pjsua.cred_info[0].data = pj_str(optarg);
+ if (pjsua.cred_count==0) pjsua.cred_count=1;
+ cur_cred->data_type = 0;
+ cur_cred->data = pj_str(optarg);
+ break;
+
+ case OPT_NEXT_CRED: /* Next credential */
+ pjsua.cred_count++;
+ cur_cred = &pjsua.cred_info[pjsua.cred_count - 1];
break;
case OPT_USE_STUN1: /* STUN server 1 */
@@ -411,6 +442,18 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
break;
case OPT_AUTO_PLAY:
+ pjsua.auto_play = 1;
+ break;
+
+ case OPT_AUTO_LOOP:
+ pjsua.auto_loop = 1;
+ break;
+
+ case OPT_AUTO_CONF:
+ pjsua.auto_conf = 1;
+ break;
+
+ case OPT_PLAY_FILE:
pjsua.wav_file = optarg;
break;
@@ -421,6 +464,14 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
return -1;
}
break;
+
+ case OPT_MAX_CALLS:
+ pjsua.max_calls = atoi(optarg);
+ if (pjsua.max_calls < 1 || pjsua.max_calls > 255) {
+ puts("Too many calls for max-calls (1-255)");
+ return -1;
+ }
+ break;
}
}
@@ -429,21 +480,17 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
return PJ_EINVAL;
}
- if (pjsua.reg_timeout == 0)
- pjsua.reg_timeout = 3600;
-
-
return PJ_SUCCESS;
}
-static void print_invite_session(const char *title,
- struct pjsua_inv_data *inv_data,
- char *buf, pj_size_t size)
+static void print_call(const char *title,
+ int call_index,
+ char *buf, pj_size_t size)
{
int len;
- pjsip_inv_session *inv = inv_data->inv;
+ pjsip_inv_session *inv = pjsua.calls[call_index].inv;
pjsip_dialog *dlg = inv->dlg;
char userinfo[128];
@@ -515,7 +562,6 @@ static void dump_media_session(pjmedia_session *session)
*/
void pjsua_dump(void)
{
- struct pjsua_inv_data *inv_data;
char buf[128];
unsigned old_decor;
@@ -533,23 +579,23 @@ void pjsua_dump(void)
/* Dump all invite sessions: */
PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
- if (pj_list_empty(&pjsua.inv_list)) {
+ if (pjsua.call_cnt == 0) {
PJ_LOG(3,(THIS_FILE, " - no sessions -"));
} else {
+ int i;
- inv_data = pjsua.inv_list.next;
+ for (i=0; i<pjsua.max_calls; ++i) {
- while (inv_data != &pjsua.inv_list) {
+ if (pjsua.calls[i].inv == NULL)
+ continue;
- print_invite_session(" ", inv_data, buf, sizeof(buf));
+ print_call(" ", i, buf, sizeof(buf));
PJ_LOG(3,(THIS_FILE, "%s", buf));
- if (inv_data->session)
- dump_media_session(inv_data->session);
-
- inv_data = inv_data->next;
+ if (pjsua.calls[i].session)
+ dump_media_session(pjsua.calls[i].session);
}
}
@@ -575,40 +621,96 @@ pj_status_t pjsua_load_settings(const char *filename)
/*
- * Save settings.
+ * Save account settings
*/
-pj_status_t pjsua_save_settings(const char *filename)
+static void save_account_settings(int acc_index, pj_str_t *result)
{
- unsigned i;
- pj_str_t cfg;
char line[128];
- pj_pool_t *pool;
- FILE *fhnd;
+ pjsua_acc *acc = &pjsua.acc[acc_index];
- /* Create pool for temporary buffer. */
- pool = pj_pool_create(&pjsua.cp.factory, "settings", 4000, 0, NULL);
- if (!pool)
- return PJ_ENOMEM;
+
+ pj_ansi_sprintf(line, "#\n# Account %d:\n#\n", acc_index);
+ pj_strcat2(result, line);
- cfg.ptr = pj_pool_alloc(pool, 3800);
- if (!cfg.ptr) {
- pj_pool_release(pool);
- return PJ_EBUG;
+ /* Identity */
+ if (acc->local_uri.slen) {
+ pj_ansi_sprintf(line, "--id %.*s\n",
+ (int)acc->local_uri.slen,
+ acc->local_uri.ptr);
+ pj_strcat2(result, line);
+ }
+
+ /* Registrar server */
+ if (acc->reg_uri.slen) {
+ pj_ansi_sprintf(line, "--registrar %.*s\n",
+ (int)acc->reg_uri.slen,
+ acc->reg_uri.ptr);
+ pj_strcat2(result, line);
+
+ pj_ansi_sprintf(line, "--reg-timeout %u\n",
+ acc->reg_timeout);
+ pj_strcat2(result, line);
}
+
+
+ /* Proxy */
+ if (acc->proxy.slen) {
+ pj_ansi_sprintf(line, "--proxy %.*s\n",
+ (int)acc->proxy.slen,
+ acc->proxy.ptr);
+ pj_strcat2(result, line);
+ }
+}
+
+
+
+/*
+ * Dump settings.
+ */
+int pjsua_dump_settings(char *buf, pj_size_t max)
+{
+ int acc_index;
+ int i;
+ pj_str_t cfg;
+ char line[128];
+
+ cfg.ptr = buf;
cfg.slen = 0;
- /* Identity */
- if (pjsua.local_uri.slen) {
- pj_ansi_sprintf(line, "--id %.*s\n",
- (int)pjsua.local_uri.slen,
- pjsua.local_uri.ptr);
+ /* Logging. */
+ pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
+ pj_ansi_sprintf(line, "--log-level %d\n",
+ pjsua.log_level);
+ pj_strcat2(&cfg, line);
+
+ pj_ansi_sprintf(line, "--app-log-level %d\n",
+ pjsua.app_log_level);
+ pj_strcat2(&cfg, line);
+
+ if (pjsua.log_filename) {
+ pj_ansi_sprintf(line, "--log-file %s\n",
+ pjsua.log_filename);
pj_strcat2(&cfg, line);
}
+
+ /* Save account settings. */
+ for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
+
+ save_account_settings(acc_index, &cfg);
+
+ if (acc_index < pjsua.acc_cnt-1)
+ pj_strcat2(&cfg, "--next-account\n");
+ }
+
/* Credentials. */
for (i=0; i<pjsua.cred_count; ++i) {
+
+ pj_ansi_sprintf(line, "#\n# Credential %d:\n#\n", i);
+ pj_strcat2(&cfg, line);
+
if (pjsua.cred_info[i].realm.slen) {
pj_ansi_sprintf(line, "--realm %.*s\n",
(int)pjsua.cred_info[i].realm.slen,
@@ -625,17 +727,14 @@ pj_status_t pjsua_save_settings(const char *filename)
(int)pjsua.cred_info[i].data.slen,
pjsua.cred_info[i].data.ptr);
pj_strcat2(&cfg, line);
- }
- /* Registrar server */
- if (pjsua.registrar_uri.slen) {
- pj_ansi_sprintf(line, "--registrar %.*s\n",
- (int)pjsua.registrar_uri.slen,
- pjsua.registrar_uri.ptr);
- pj_strcat2(&cfg, line);
+ if (i < pjsua.cred_count-1)
+ pj_strcat2(&cfg, "--next-cred\n");
}
+ pj_strcat2(&cfg, "#\n# Network settings:\n#\n");
+
/* Outbound proxy */
if (pjsua.outbound_proxy.slen) {
pj_ansi_sprintf(line, "--outbound %.*s\n",
@@ -644,10 +743,6 @@ pj_status_t pjsua_save_settings(const char *filename)
pj_strcat2(&cfg, line);
}
- /* Media */
- if (pjsua.null_audio)
- pj_strcat2(&cfg, "--null-audio\n");
-
/* Transport. */
pj_ansi_sprintf(line, "--local-port %d\n", pjsua.sip_port);
@@ -672,6 +767,42 @@ pj_status_t pjsua_save_settings(const char *filename)
}
+ pj_strcat2(&cfg, "#\n# Media settings:\n#\n");
+
+
+ /* Media */
+ if (pjsua.null_audio)
+ pj_strcat2(&cfg, "--null-audio\n");
+ if (pjsua.auto_play)
+ pj_strcat2(&cfg, "--auto-play\n");
+ if (pjsua.auto_loop)
+ pj_strcat2(&cfg, "--auto-loop\n");
+ if (pjsua.auto_conf)
+ pj_strcat2(&cfg, "--auto-conf\n");
+ if (pjsua.wav_file) {
+ pj_ansi_sprintf(line, "--play-file %s\n",
+ pjsua.wav_file);
+ pj_strcat2(&cfg, line);
+ }
+
+
+ pj_strcat2(&cfg, "#\n# User agent:\n#\n");
+
+ /* Auto-answer. */
+ if (pjsua.auto_answer != 0) {
+ pj_ansi_sprintf(line, "--auto-answer %d\n",
+ pjsua.auto_answer);
+ pj_strcat2(&cfg, line);
+ }
+
+ /* Max calls. */
+ pj_ansi_sprintf(line, "--max-calls %d\n",
+ pjsua.max_calls);
+ pj_strcat2(&cfg, line);
+
+
+ pj_strcat2(&cfg, "#\n# Buddies:\n#\n");
+
/* Add buddies. */
for (i=0; i<pjsua.buddy_cnt; ++i) {
pj_ansi_sprintf(line, "--add-buddy %.*s\n",
@@ -681,6 +812,39 @@ pj_status_t pjsua_save_settings(const char *filename)
}
+ *(cfg.ptr + cfg.slen) = '\0';
+ return cfg.slen;
+}
+
+/*
+ * Save settings.
+ */
+pj_status_t pjsua_save_settings(const char *filename)
+{
+ pj_str_t cfg;
+ pj_pool_t *pool;
+ FILE *fhnd;
+
+ /* Create pool for temporary buffer. */
+ pool = pj_pool_create(&pjsua.cp.factory, "settings", 4000, 0, NULL);
+ if (!pool)
+ return PJ_ENOMEM;
+
+
+ cfg.ptr = pj_pool_alloc(pool, 3800);
+ if (!cfg.ptr) {
+ pj_pool_release(pool);
+ return PJ_EBUG;
+ }
+
+
+ cfg.slen = pjsua_dump_settings(cfg.ptr, 3800);
+ if (cfg.slen < 1) {
+ pj_pool_release(pool);
+ return PJ_ENOMEM;
+ }
+
+
/* Write to file. */
fhnd = fopen(filename, "wt");
if (!fhnd) {
diff --git a/pjsip/src/pjsua/pjsua_pres.c b/pjsip/src/pjsua/pjsua_pres.c
index cba02b5c..b203a2ca 100644
--- a/pjsip/src/pjsua/pjsua_pres.c
+++ b/pjsip/src/pjsua/pjsua_pres.c
@@ -79,6 +79,7 @@ static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
*/
static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
{
+ int acc_index;
pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
pjsua_srv_pres *uapres;
pjsip_evsub *sub;
@@ -93,9 +94,13 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
/* Incoming SUBSCRIBE: */
+ /* Find which account for the incoming request. */
+ acc_index = pjsua_find_account_for_incoming(rdata);
+
/* Create UAS dialog: */
status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
- &pjsua.contact_uri, &dlg);
+ &pjsua.acc[acc_index].contact_uri,
+ &dlg);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Unable to create UAS dialog for subscription",
@@ -130,7 +135,7 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
pjsip_evsub_set_mod_data(sub, pjsua.mod.id, uapres);
/* Add server subscription to the list: */
- pj_list_push_back(&pjsua.pres_srv_list, uapres);
+ pj_list_push_back(&pjsua.acc[acc_index].pres_srv_list, uapres);
/* Create and send 200 (OK) to the SUBSCRIBE request: */
@@ -146,7 +151,7 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
/* Set our online status: */
pj_memset(&pres_status, 0, sizeof(pres_status));
pres_status.info_cnt = 1;
- pres_status.info[0].basic_open = pjsua.online_status;
+ pres_status.info[0].basic_open = pjsua.acc[acc_index].online_status;
//Both pjsua.local_uri and pjsua.contact_uri are enclosed in "<" and ">"
//causing XML parsing to fail.
//pres_status.info[0].contact = pjsua.local_uri;
@@ -174,20 +179,20 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
/* Refresh subscription (e.g. when our online status has changed) */
-static void refresh_server_subscription()
+static void refresh_server_subscription(int acc_index)
{
pjsua_srv_pres *uapres;
- uapres = pjsua.pres_srv_list.next;
+ uapres = pjsua.acc[acc_index].pres_srv_list.next;
- while (uapres != &pjsua.pres_srv_list) {
+ while (uapres != &pjsua.acc[acc_index].pres_srv_list) {
pjsip_pres_status pres_status;
pjsip_tx_data *tdata;
pjsip_pres_get_status(uapres->sub, &pres_status);
- if (pres_status.info[0].basic_open != pjsua.online_status) {
- pres_status.info[0].basic_open = pjsua.online_status;
+ if (pres_status.info[0].basic_open != pjsua.acc[acc_index].online_status) {
+ pres_status.info[0].basic_open = pjsua.acc[acc_index].online_status;
pjsip_pres_set_status(uapres->sub, &pres_status);
if (pjsua.quit_flag) {
@@ -298,13 +303,16 @@ static pjsip_evsub_user pres_callback =
/* It does what it says.. */
static void subscribe_buddy_presence(unsigned index)
{
+ int acc_index;
pjsip_dialog *dlg;
pjsip_tx_data *tdata;
pj_status_t status;
+ acc_index = pjsua.buddies[index].acc_index;
+
status = pjsip_dlg_create_uac( pjsip_ua_instance(),
- &pjsua.local_uri,
- &pjsua.contact_uri,
+ &pjsua.acc[acc_index].local_uri,
+ &pjsua.acc[acc_index].contact_uri,
&pjsua.buddies[index].uri,
NULL, &dlg);
if (status != PJ_SUCCESS) {
@@ -325,7 +333,7 @@ static void subscribe_buddy_presence(unsigned index)
pjsip_evsub_set_mod_data(pjsua.buddies[index].sub, pjsua.mod.id,
&pjsua.buddies[index]);
- status = pjsip_pres_initiate(pjsua.buddies[index].sub, 60, &tdata);
+ status = pjsip_pres_initiate(pjsua.buddies[index].sub, -1, &tdata);
if (status != PJ_SUCCESS) {
pjsua.buddies[index].sub = NULL;
pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE",
@@ -381,7 +389,7 @@ static void unsubscribe_buddy_presence(unsigned index)
/* It does what it says.. */
static void refresh_client_subscription(void)
{
- unsigned i;
+ int i;
for (i=0; i<pjsua.buddy_cnt; ++i) {
@@ -415,10 +423,10 @@ pj_status_t pjsua_pres_init()
/*
* Refresh presence
*/
-void pjsua_pres_refresh(void)
+void pjsua_pres_refresh(int acc_index)
{
refresh_client_subscription();
- refresh_server_subscription();
+ refresh_server_subscription(acc_index);
}
@@ -427,13 +435,20 @@ void pjsua_pres_refresh(void)
*/
void pjsua_pres_shutdown(void)
{
- unsigned i;
+ int acc_index;
+ int i;
+
+ for (acc_index=0; acc_index<pjsua.acc_cnt; ++acc_index) {
+ pjsua.acc[acc_index].online_status = 0;
+ }
- pjsua.online_status = 0;
for (i=0; i<pjsua.buddy_cnt; ++i) {
pjsua.buddies[i].monitor = 0;
}
- pjsua_pres_refresh();
+
+ for (acc_index=0; acc_index<pjsua.acc_cnt; ++acc_index) {
+ pjsua_pres_refresh(acc_index);
+ }
}
/*
@@ -441,22 +456,30 @@ void pjsua_pres_shutdown(void)
*/
void pjsua_pres_dump(void)
{
- unsigned i;
+ int acc_index;
+ int i;
PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
- if (pj_list_empty(&pjsua.pres_srv_list)) {
- PJ_LOG(3,(THIS_FILE, " - none - "));
- } else {
- struct pjsua_srv_pres *uapres;
+ for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) {
- uapres = pjsua.pres_srv_list.next;
- while (uapres != &pjsua.pres_srv_list) {
-
- PJ_LOG(3,(THIS_FILE, " %10s %s",
- pjsip_evsub_get_state_name(uapres->sub),
- uapres->remote));
+ PJ_LOG(3,(THIS_FILE, " %.*s",
+ (int)pjsua.acc[acc_index].local_uri.slen,
+ pjsua.acc[acc_index].local_uri.ptr));
+
+ if (pj_list_empty(&pjsua.acc[acc_index].pres_srv_list)) {
+ PJ_LOG(3,(THIS_FILE, " - none - "));
+ } else {
+ struct pjsua_srv_pres *uapres;
+
+ uapres = pjsua.acc[acc_index].pres_srv_list.next;
+ while (uapres != &pjsua.acc[acc_index].pres_srv_list) {
+
+ PJ_LOG(3,(THIS_FILE, " %10s %s",
+ pjsip_evsub_get_state_name(uapres->sub),
+ uapres->remote));
- uapres = uapres->next;
+ uapres = uapres->next;
+ }
}
}
diff --git a/pjsip/src/pjsua/pjsua_reg.c b/pjsip/src/pjsua/pjsua_reg.c
index f11ff3fd..006bc732 100644
--- a/pjsip/src/pjsua/pjsua_reg.c
+++ b/pjsip/src/pjsua/pjsua_reg.c
@@ -34,66 +34,81 @@
*/
static void regc_cb(struct pjsip_regc_cbparam *param)
{
+
+ pjsua_acc *acc = param->token;
+
/*
* Print registration status.
*/
if (param->status!=PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "SIP registration error",
param->status);
- pjsua.regc = NULL;
+ pjsip_regc_destroy(acc->regc);
+ acc->regc = NULL;
} else if (param->code < 0 || param->code >= 300) {
PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)",
param->code,
pjsip_get_status_text(param->code)->ptr));
- pjsua.regc = NULL;
+ pjsip_regc_destroy(acc->regc);
+ acc->regc = NULL;
} else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
- PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), "
- "will re-register in %d seconds",
- param->code,
- pjsip_get_status_text(param->code)->ptr,
- param->expiration));
+
+ if (param->expiration < 1) {
+ pjsip_regc_destroy(acc->regc);
+ acc->regc = NULL;
+ PJ_LOG(3,(THIS_FILE, "%s: unregistration success",
+ acc->local_uri.ptr));
+ } else {
+ PJ_LOG(3, (THIS_FILE,
+ "%s: registration success, status=%d (%s), "
+ "will re-register in %d seconds",
+ acc->local_uri.ptr,
+ param->code,
+ pjsip_get_status_text(param->code)->ptr,
+ param->expiration));
+ }
} else {
PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
}
- pjsua.regc_last_err = param->status;
- pjsua.regc_last_code = param->code;
+ acc->reg_last_err = param->status;
+ acc->reg_last_code = param->code;
- pjsua_ui_regc_on_state_changed(pjsua.regc_last_code);
+ pjsua_ui_regc_on_state_changed(acc->index);
}
/*
* Update registration. If renew is false, then unregistration will be performed.
*/
-void pjsua_regc_update(pj_bool_t renew)
+void pjsua_regc_update(int acc_index, pj_bool_t renew)
{
pj_status_t status;
pjsip_tx_data *tdata;
if (renew) {
- if (pjsua.regc == NULL) {
- status = pjsua_regc_init();
+ if (pjsua.acc[acc_index].regc == NULL) {
+ status = pjsua_regc_init(acc_index);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create registration",
status);
return;
}
}
- status = pjsip_regc_register(pjsua.regc, 1, &tdata);
+ status = pjsip_regc_register(pjsua.acc[acc_index].regc, 1, &tdata);
} else {
- if (pjsua.regc == NULL) {
+ if (pjsua.acc[acc_index].regc == NULL) {
PJ_LOG(3,(THIS_FILE, "Currently not registered"));
return;
}
- status = pjsip_regc_unregister(pjsua.regc, &tdata);
+ status = pjsip_regc_unregister(pjsua.acc[acc_index].regc, &tdata);
}
if (status == PJ_SUCCESS)
- status = pjsip_regc_send( pjsua.regc, tdata );
+ status = pjsip_regc_send( pjsua.acc[acc_index].regc, tdata );
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create/send REGISTER",
@@ -107,14 +122,17 @@ void pjsua_regc_update(pj_bool_t renew)
/*
* Initialize client registration.
*/
-pj_status_t pjsua_regc_init(void)
+pj_status_t pjsua_regc_init(int acc_index)
{
pj_status_t status;
/* initialize SIP registration if registrar is configured */
- if (pjsua.registrar_uri.slen) {
+ if (pjsua.acc[acc_index].reg_uri.slen) {
- status = pjsip_regc_create( pjsua.endpt, NULL, &regc_cb, &pjsua.regc);
+ status = pjsip_regc_create( pjsua.endpt,
+ &pjsua.acc[acc_index],
+ &regc_cb,
+ &pjsua.acc[acc_index].regc);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create client registration",
@@ -123,11 +141,12 @@ pj_status_t pjsua_regc_init(void)
}
- status = pjsip_regc_init( pjsua.regc, &pjsua.registrar_uri,
- &pjsua.local_uri,
- &pjsua.local_uri,
- 1, &pjsua.contact_uri,
- pjsua.reg_timeout);
+ status = pjsip_regc_init( pjsua.acc[acc_index].regc,
+ &pjsua.acc[acc_index].reg_uri,
+ &pjsua.acc[acc_index].local_uri,
+ &pjsua.acc[acc_index].local_uri,
+ 1, &pjsua.acc[acc_index].contact_uri,
+ pjsua.acc[acc_index].reg_timeout);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Client registration initialization error",
@@ -135,10 +154,12 @@ pj_status_t pjsua_regc_init(void)
return status;
}
- pjsip_regc_set_credentials( pjsua.regc, pjsua.cred_count,
+ pjsip_regc_set_credentials( pjsua.acc[acc_index].regc,
+ pjsua.cred_count,
pjsua.cred_info );
- pjsip_regc_set_route_set( pjsua.regc, &pjsua.route_set );
+ pjsip_regc_set_route_set( pjsua.acc[acc_index].regc,
+ &pjsua.acc[acc_index].route_set );
}
return PJ_SUCCESS;