blob: d8539a7a8b3927c689e2bdf9412cf7cc6df8caa0 [file] [log] [blame]
Benny Prijono8df5b022006-03-01 19:31:18 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsua-lib/pjsua.h>
20#include <stdlib.h> /* atoi */
21
22
23#define THIS_FILE "main.c"
24
25/* Current dialog */
26static int current_acc;
27static int current_call = -1;
28
29
30/*
31 * Find next call.
32 */
33static pj_bool_t find_next_call(void)
34{
35 int i;
36
37 for (i=current_call+1; i<(int)pjsua.max_calls; ++i) {
38 if (pjsua.calls[i].inv != NULL) {
39 current_call = i;
40 return PJ_TRUE;
41 }
42 }
43
44 for (i=0; i<current_call; ++i) {
45 if (pjsua.calls[i].inv != NULL) {
46 current_call = i;
47 return PJ_TRUE;
48 }
49 }
50
51 current_call = -1;
52 return PJ_FALSE;
53}
54
55
56/*
57 * Find previous call.
58 */
59static pj_bool_t find_prev_call(void)
60{
61 int i;
62
63 for (i=current_call-1; i>=0; --i) {
64 if (pjsua.calls[i].inv != NULL) {
65 current_call = i;
66 return PJ_TRUE;
67 }
68 }
69
70 for (i=pjsua.max_calls-1; i>current_call; --i) {
71 if (pjsua.calls[i].inv != NULL) {
72 current_call = i;
73 return PJ_TRUE;
74 }
75 }
76
77 current_call = -1;
78 return PJ_FALSE;
79}
80
81
82
83/*
84 * Notify UI when invite state has changed.
85 */
86void pjsua_ui_inv_on_state_changed(int call_index, pjsip_event *e)
87{
88 pjsua_call *call = &pjsua.calls[call_index];
89
90 PJ_UNUSED_ARG(e);
91
92 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
93 call_index,
94 pjsua_inv_state_names[call->inv->state]));
95
96 if (call->inv->state == PJSIP_INV_STATE_DISCONNECTED) {
97 call->inv = NULL;
98 if ((int)call->index == current_call) {
99 find_next_call();
100 }
101
102 } else {
103
104 if (call && current_call==-1)
105 current_call = call->index;
106
107 }
108}
109
110/**
111 * Notify UI when registration status has changed.
112 */
113void pjsua_ui_regc_on_state_changed(int code)
114{
115 PJ_UNUSED_ARG(code);
116
117 // Log already written.
118}
119
120
121/*
122 * Print buddy list.
123 */
124static void print_buddy_list(void)
125{
126 int i;
127
128 puts("Buddy list:");
129
130 if (pjsua.buddy_cnt == 0)
131 puts(" -none-");
132 else {
133 for (i=0; i<pjsua.buddy_cnt; ++i) {
134 const char *status;
135
136 if (pjsua.buddies[i].sub == NULL ||
137 pjsua.buddies[i].status.info_cnt==0)
138 {
139 status = " ? ";
140 }
141 else if (pjsua.buddies[i].status.info[0].basic_open)
142 status = " Online";
143 else
144 status = "Offline";
145
146 printf(" [%2d] <%s> %s\n",
147 i+1, status, pjsua.buddies[i].uri.ptr);
148 }
149 }
150 puts("");
151}
152
153
154/*
155 * Print account status.
156 */
157static void print_acc_status(int acc_index)
158{
159 char reg_status[128];
160
161 if (pjsua.acc[acc_index].regc == NULL) {
162 pj_ansi_strcpy(reg_status, " -not registered to server-");
163
164 } else if (pjsua.acc[acc_index].reg_last_err != PJ_SUCCESS) {
165 pj_strerror(pjsua.acc[acc_index].reg_last_err, reg_status, sizeof(reg_status));
166
167 } else if (pjsua.acc[acc_index].reg_last_code>=200 &&
168 pjsua.acc[acc_index].reg_last_code<=699) {
169
170 pjsip_regc_info info;
171
172 pjsip_regc_get_info(pjsua.acc[acc_index].regc, &info);
173
174 pj_snprintf(reg_status, sizeof(reg_status),
175 "%s (%.*s;expires=%d)",
176 pjsip_get_status_text(pjsua.acc[acc_index].reg_last_code)->ptr,
177 (int)info.client_uri.slen,
178 info.client_uri.ptr,
179 info.next_reg);
180
181 } else {
182 pj_sprintf(reg_status, "in progress (%d)",
183 pjsua.acc[acc_index].reg_last_code);
184 }
185
186 printf("[%2d] Registration status: %s\n", acc_index, reg_status);
187 printf(" Online status: %s\n",
188 (pjsua.acc[acc_index].online_status ? "Online" : "Invisible"));
189}
190
191/*
192 * Show a bit of help.
193 */
194static void keystroke_help(void)
195{
196 int i;
197
198 printf(">>>>\n");
199
200 for (i=0; i<pjsua.acc_cnt; ++i)
201 print_acc_status(i);
202
203 print_buddy_list();
204
205 //puts("Commands:");
206 puts("+=============================================================================+");
207 puts("| Call Commands: | IM & Presence: | Misc: |");
208 puts("| | | |");
209 puts("| m Make new call | i Send IM | o Send OPTIONS |");
210 puts("| M Make multiple calls | s Subscribe presence | rr (Re-)register |");
211 puts("| a Answer call | u Unsubscribe presence | ru Unregister |");
Benny Prijonof090a832006-03-01 20:47:16 +0000212 puts("| h Hangup call (ha=all) | t ToGgle Online status | |");
213 puts("| H Hold call | | |");
Benny Prijono8df5b022006-03-01 19:31:18 +0000214 puts("| v re-inVite (release hold) +--------------------------+-------------------+");
215 puts("| ] Select next dialog | Conference Command | |");
Benny Prijonof090a832006-03-01 20:47:16 +0000216 puts("| [ Select previous dialog | cl List ports | d Dump status |");
217 puts("| x Xfer call | cc Connect port | dd Dump detailed |");
218 puts("| # Send DTMF string | cd Disconnect port | dc Dump config |");
Benny Prijono8df5b022006-03-01 19:31:18 +0000219 puts("+------------------------------+--------------------------+-------------------+");
220 puts("| q QUIT |");
221 puts("+=============================================================================+");
222}
223
224
225/*
226 * Input simple string
227 */
228static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
229{
230 char *p;
231
232 printf("%s (empty to cancel): ", title); fflush(stdout);
233 fgets(buf, len, stdin);
234
235 /* Remove trailing newlines. */
236 for (p=buf; ; ++p) {
237 if (*p=='\r' || *p=='\n') *p='\0';
238 else if (!*p) break;
239 }
240
241 if (!*buf)
242 return PJ_FALSE;
243
244 return PJ_TRUE;
245}
246
247
248#define NO_NB -2
249struct input_result
250{
251 int nb_result;
252 char *uri_result;
253};
254
255
256/*
257 * Input URL.
258 */
259static void ui_input_url(const char *title, char *buf, int len,
260 struct input_result *result)
261{
262 result->nb_result = NO_NB;
263 result->uri_result = NULL;
264
265 print_buddy_list();
266
267 printf("Choices:\n"
268 " 0 For current dialog.\n"
269 " -1 All %d buddies in buddy list\n"
270 " [1 -%2d] Select from buddy list\n"
271 " URL An URL\n"
272 " <Enter> Empty input (or 'q') to cancel\n"
273 , pjsua.buddy_cnt, pjsua.buddy_cnt);
274 printf("%s: ", title);
275
276 fflush(stdout);
277 fgets(buf, len, stdin);
278 len = strlen(buf);
279
280 /* Left trim */
281 while (isspace(*buf)) {
282 ++buf;
283 --len;
284 }
285
286 /* Remove trailing newlines */
287 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
288 buf[--len] = '\0';
289
290 if (len == 0 || buf[0]=='q')
291 return;
292
293 if (isdigit(*buf) || *buf=='-') {
294
295 int i;
296
297 if (*buf=='-')
298 i = 1;
299 else
300 i = 0;
301
302 for (; i<len; ++i) {
303 if (!isdigit(buf[i])) {
304 puts("Invalid input");
305 return;
306 }
307 }
308
309 result->nb_result = atoi(buf);
310
311 if (result->nb_result > 0 && result->nb_result <= (int)pjsua.buddy_cnt) {
312 --result->nb_result;
313 return;
314 }
315 if (result->nb_result == -1)
316 return;
317
318 puts("Invalid input");
319 result->nb_result = NO_NB;
320 return;
321
322 } else {
323 pj_status_t status;
324
325 if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
326 pjsua_perror(THIS_FILE, "Invalid URL", status);
327 return;
328 }
329
330 result->uri_result = buf;
331 }
332}
333
334static void conf_list(void)
335{
336 unsigned i, count;
337 pjmedia_conf_port_info info[PJSUA_MAX_CALLS];
338
339 printf("Conference ports:\n");
340
341 count = PJ_ARRAY_SIZE(info);
342 pjmedia_conf_get_ports_info(pjsua.mconf, &count, info);
343 for (i=0; i<count; ++i) {
344 char txlist[PJSUA_MAX_CALLS*4+10];
345 int j;
346 pjmedia_conf_port_info *port_info = &info[i];
347
348 txlist[0] = '\0';
349 for (j=0; j<pjsua.max_calls+PJSUA_CONF_MORE_PORTS; ++j) {
350 char s[10];
351 if (port_info->listener[j]) {
352 pj_sprintf(s, "#%d ", j);
353 pj_ansi_strcat(txlist, s);
354 }
355 }
356 printf("Port #%02d %20.*s transmitting to: %s\n",
357 port_info->slot,
358 (int)port_info->name.slen,
359 port_info->name.ptr,
360 txlist);
361
362 }
363 puts("");
364}
365
366
367static void ui_console_main(void)
368{
369 char menuin[10];
370 char buf[128];
371 int i, count;
372 char *uri;
373 struct input_result result;
374
375 keystroke_help();
376
377 for (;;) {
378
379 printf(">>> ");
380 fflush(stdout);
381
382 fgets(menuin, sizeof(menuin), stdin);
383
384 switch (menuin[0]) {
385
386 case 'm':
387 /* Make call! : */
388 printf("(You currently have %d calls)\n", pjsua.call_cnt);
389
390 ui_input_url("Make call", buf, sizeof(buf), &result);
391 if (result.nb_result != NO_NB) {
392 if (result.nb_result == -1)
393 puts("You can't do that with make call!");
394 else
395 pjsua_make_call( current_acc,
396 pjsua.buddies[result.nb_result].uri.ptr,
397 NULL);
398 } else if (result.uri_result)
399 pjsua_make_call( current_acc, result.uri_result, NULL);
400
401 break;
402
403 case 'M':
404 /* Make multiple calls! : */
405 printf("(You currently have %d calls)\n", pjsua.call_cnt);
406
Benny Prijonof090a832006-03-01 20:47:16 +0000407 if (!simple_input("Number of calls", menuin, sizeof(menuin)))
408 continue;
409
410 count = atoi(menuin);
411 if (count < 1)
412 continue;
413
Benny Prijono8df5b022006-03-01 19:31:18 +0000414 ui_input_url("Make call", buf, sizeof(buf), &result);
415 if (result.nb_result != NO_NB) {
416 if (result.nb_result == -1) {
417 puts("You can't do that with make call!");
418 continue;
419 }
420 uri = pjsua.buddies[result.nb_result].uri.ptr;
421 } else {
422 uri = result.uri_result;
423 }
424
Benny Prijono8df5b022006-03-01 19:31:18 +0000425 for (i=0; i<atoi(menuin); ++i) {
426 pj_status_t status;
427
428 status = pjsua_make_call(current_acc, uri, NULL);
429 if (status != PJ_SUCCESS)
430 break;
431 }
432 break;
433
434 case 'a':
435
436 if (current_call == -1 ||
437 pjsua.calls[current_call].inv->role != PJSIP_ROLE_UAS ||
438 pjsua.calls[current_call].inv->state >= PJSIP_INV_STATE_CONNECTING)
439 {
440 puts("No pending incoming call");
441 fflush(stdout);
442 continue;
443
444 } else {
445 pj_status_t status;
446 pjsip_tx_data *tdata;
447
448 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
449 continue;
450
451 if (atoi(buf) < 100)
452 continue;
453
454 /*
455 * Must check again!
456 * Call may have been disconnected while we're waiting for
457 * keyboard input.
458 */
459 if (current_call == -1) {
460 puts("Call has been disconnected");
461 fflush(stdout);
462 continue;
463 }
464
465 status = pjsip_inv_answer(pjsua.calls[current_call].inv,
466 atoi(buf),
467 NULL, NULL, &tdata);
468 if (status == PJ_SUCCESS)
469 status = pjsip_inv_send_msg(pjsua.calls[current_call].inv,
470 tdata, NULL);
471
472 if (status != PJ_SUCCESS)
473 pjsua_perror(THIS_FILE, "Unable to create/send response",
474 status);
475 }
476
477 break;
478
479
480 case 'h':
481
482 if (current_call == -1) {
483 puts("No current call");
484 fflush(stdout);
485 continue;
486
Benny Prijonof090a832006-03-01 20:47:16 +0000487 } else if (menuin[1] == 'a') {
488
489 /* Hangup all calls */
490 pjsua_call_hangup_all();
491
Benny Prijono8df5b022006-03-01 19:31:18 +0000492 } else {
Benny Prijonof090a832006-03-01 20:47:16 +0000493
494 /* Hangup current calls */
Benny Prijono8df5b022006-03-01 19:31:18 +0000495 pjsua_call_hangup(current_call, PJSIP_SC_DECLINE);
496 }
497 break;
498
499 case ']':
500 case '[':
501 /*
502 * Cycle next/prev dialog.
503 */
504 if (menuin[0] == ']') {
505 find_next_call();
506
507 } else {
508 find_prev_call();
509 }
510
511 if (current_call != -1) {
512 char url[PJSIP_MAX_URL_SIZE];
513 int len;
514 const pjsip_uri *u;
515
516 u = pjsua.calls[current_call].inv->dlg->remote.info->uri;
517 len = pjsip_uri_print(0, u, url, sizeof(url)-1);
518 if (len < 1) {
519 pj_ansi_strcpy(url, "<uri is too long>");
520 } else {
521 url[len] = '\0';
522 }
523
524 PJ_LOG(3,(THIS_FILE,"Current dialog: %s", url));
525
526 } else {
527 PJ_LOG(3,(THIS_FILE,"No current dialog"));
528 }
529 break;
530
531 case 'H':
532 /*
533 * Hold call.
534 */
535 if (current_call != -1) {
536
537 pjsua_call_set_hold(current_call);
538
539 } else {
540 PJ_LOG(3,(THIS_FILE, "No current call"));
541 }
542 break;
543
544 case 'v':
545 /*
546 * Send re-INVITE (to release hold, etc).
547 */
548 if (current_call != -1) {
549
550 pjsua_call_reinvite(current_call);
551
552 } else {
553 PJ_LOG(3,(THIS_FILE, "No current call"));
554 }
555 break;
556
557 case 'x':
558 /*
559 * Transfer call.
560 */
561 if (current_call == -1) {
562
563 PJ_LOG(3,(THIS_FILE, "No current call"));
564
565 } else {
566 int call = current_call;
567
568 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
569
570 /* Check if call is still there. */
571
572 if (call != current_call) {
573 puts("Call has been disconnected");
574 continue;
575 }
576
577 if (result.nb_result != NO_NB) {
578 if (result.nb_result == -1)
579 puts("You can't do that with transfer call!");
580 else
581 pjsua_call_xfer( current_call,
582 pjsua.buddies[result.nb_result].uri.ptr);
583
584 } else if (result.uri_result) {
585 pjsua_call_xfer( current_call, result.uri_result);
586 }
587 }
588 break;
589
590 case '#':
591 /*
592 * Send DTMF strings.
593 */
594 if (current_call == -1) {
595
596 PJ_LOG(3,(THIS_FILE, "No current call"));
597
598 } else if (pjsua.calls[current_call].session == NULL) {
599
600 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
601
602 } else {
603 pj_str_t digits;
604 int call = current_call;
605 pj_status_t status;
606
607 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
608 sizeof(buf)))
609 {
610 break;
611 }
612
613 if (call != current_call) {
614 puts("Call has been disconnected");
615 continue;
616 }
617
618 digits = pj_str(buf);
619 status = pjmedia_session_dial_dtmf(pjsua.calls[current_call].session, 0,
620 &digits);
621 if (status != PJ_SUCCESS) {
622 pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
623 } else {
624 puts("DTMF digits enqueued for transmission");
625 }
626 }
627 break;
628
629 case 's':
630 case 'u':
631 /*
632 * Subscribe/unsubscribe presence.
633 */
634 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
635 if (result.nb_result != NO_NB) {
636 if (result.nb_result == -1) {
637 int i;
638 for (i=0; i<pjsua.buddy_cnt; ++i)
639 pjsua.buddies[i].monitor = (menuin[0]=='s');
640 } else {
641 pjsua.buddies[result.nb_result].monitor = (menuin[0]=='s');
642 }
643
644 pjsua_pres_refresh(current_acc);
645
646 } else if (result.uri_result) {
647 puts("Sorry, can only subscribe to buddy's presence, "
648 "not arbitrary URL (for now)");
649 }
650
651 break;
652
653 case 'r':
654 switch (menuin[1]) {
655 case 'r':
656 /*
657 * Re-Register.
658 */
659 pjsua_regc_update(current_acc, PJ_TRUE);
660 break;
661 case 'u':
662 /*
663 * Unregister
664 */
665 pjsua_regc_update(current_acc, PJ_FALSE);
666 break;
667 }
668 break;
669
670 case 't':
671 pjsua.acc[current_acc].online_status =
672 !pjsua.acc[current_acc].online_status;
673 pjsua_pres_refresh(current_acc);
674 break;
675
676 case 'c':
677 switch (menuin[1]) {
678 case 'l':
679 conf_list();
680 break;
681 case 'c':
682 case 'd':
683 {
684 char src_port[10], dst_port[10];
685 pj_status_t status;
686 const char *src_title, *dst_title;
687
688 conf_list();
689
690 src_title = (menuin[1]=='c'?
691 "Connect src port #":
692 "Disconnect src port #");
693 dst_title = (menuin[1]=='c'?
694 "To dst port #":
695 "From dst port #");
696
697 if (!simple_input(src_title, src_port, sizeof(src_port)))
698 break;
699
700 if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
701 break;
702
703 if (menuin[1]=='c') {
704 status = pjmedia_conf_connect_port(pjsua.mconf,
705 atoi(src_port),
706 atoi(dst_port));
707 } else {
708 status = pjmedia_conf_disconnect_port(pjsua.mconf,
709 atoi(src_port),
710 atoi(dst_port));
711 }
712 if (status == PJ_SUCCESS) {
713 puts("Success");
714 } else {
715 puts("ERROR!!");
716 }
717 }
718 break;
719 }
720 break;
721
722 case 'd':
723 if (menuin[1] == 'c') {
724 char settings[2000];
725 int len;
726
727 len = pjsua_dump_settings(settings, sizeof(settings));
728 if (len < 1)
729 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
730 else
731 PJ_LOG(3,(THIS_FILE,
732 "Dumping configuration (%d bytes):\n%s\n",
733 len, settings));
734 } else {
Benny Prijonof090a832006-03-01 20:47:16 +0000735 pjsua_dump(menuin[1]=='d');
Benny Prijono8df5b022006-03-01 19:31:18 +0000736 }
737 break;
738
739 case 'q':
740 goto on_exit;
741
742 default:
743 keystroke_help();
744 break;
745 }
746 }
747
748on_exit:
749 ;
750}
751
752
753/*****************************************************************************
754 * This is a very simple PJSIP module, whose sole purpose is to display
755 * incoming and outgoing messages to log. This module will have priority
756 * higher than transport layer, which means:
757 *
758 * - incoming messages will come to this module first before reaching
759 * transaction layer.
760 *
761 * - outgoing messages will come to this module last, after the message
762 * has been 'printed' to contiguous buffer by transport layer and
763 * appropriate transport instance has been decided for this message.
764 *
765 */
766
767/* Notification on incoming messages */
768static pj_bool_t console_on_rx_msg(pjsip_rx_data *rdata)
769{
770 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n"
771 "%s\n"
772 "--end msg--",
773 rdata->msg_info.len,
774 pjsip_rx_data_get_info(rdata),
775 rdata->pkt_info.src_name,
776 rdata->pkt_info.src_port,
777 rdata->msg_info.msg_buf));
778
779 /* Always return false, otherwise messages will not get processed! */
780 return PJ_FALSE;
781}
782
783/* Notification on outgoing messages */
784static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata)
785{
786
787 /* Important note:
788 * tp_info field is only valid after outgoing messages has passed
789 * transport layer. So don't try to access tp_info when the module
790 * has lower priority than transport layer.
791 */
792
793 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
794 "%s\n"
795 "--end msg--",
796 (tdata->buf.cur - tdata->buf.start),
797 pjsip_tx_data_get_info(tdata),
798 tdata->tp_info.dst_name,
799 tdata->tp_info.dst_port,
800 tdata->buf.start));
801
802 /* Always return success, otherwise message will not get sent! */
803 return PJ_SUCCESS;
804}
805
806/* The module instance. */
807static pjsip_module console_msg_logger =
808{
809 NULL, NULL, /* prev, next. */
810 { "mod-pjsua-log", 13 }, /* Name. */
811 -1, /* Id */
812 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
813 NULL, /* load() */
814 NULL, /* start() */
815 NULL, /* stop() */
816 NULL, /* unload() */
817 &console_on_rx_msg, /* on_rx_request() */
818 &console_on_rx_msg, /* on_rx_response() */
819 &console_on_tx_msg, /* on_tx_request. */
820 &console_on_tx_msg, /* on_tx_response() */
821 NULL, /* on_tsx_state() */
822
823};
824
825
826
827/*****************************************************************************
828 * Console application custom logging:
829 */
830
831
832static FILE *log_file;
833
834
835static void app_log_writer(int level, const char *buffer, int len)
836{
837 /* Write to both stdout and file. */
838
839 if (level <= pjsua.app_log_level)
840 pj_log_write(level, buffer, len);
841
842 if (log_file) {
843 fwrite(buffer, len, 1, log_file);
844 fflush(log_file);
845 }
846}
847
848
849void app_logging_init(void)
850{
851 /* Redirect log function to ours */
852
853 pj_log_set_log_func( &app_log_writer );
854
855 /* If output log file is desired, create the file: */
856
857 if (pjsua.log_filename)
858 log_file = fopen(pjsua.log_filename, "wt");
859}
860
861
862void app_logging_shutdown(void)
863{
864 /* Close logging file, if any: */
865
866 if (log_file) {
867 fclose(log_file);
868 log_file = NULL;
869 }
870}
871
872/*****************************************************************************
873 * Error display:
874 */
875
876/*
877 * Display error message for the specified error code.
878 */
879void pjsua_perror(const char *sender, const char *title,
880 pj_status_t status)
881{
882 char errmsg[PJ_ERR_MSG_SIZE];
883
884 pj_strerror(status, errmsg, sizeof(errmsg));
885
886 PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
887}
888
889
890
891
892/*****************************************************************************
893 * main():
894 */
895int main(int argc, char *argv[])
896{
897
898 /* Init default settings. */
899 pjsua_default();
900
901
902 /* Initialize pjsua (to create pool etc).
903 */
904 if (pjsua_init() != PJ_SUCCESS)
905 return 1;
906
907
908 /* Parse command line arguments: */
909 if (pjsua_parse_args(argc, argv) != PJ_SUCCESS)
910 return 1;
911
912
913 /* Init logging: */
914 app_logging_init();
915
916
917 /* Register message logger to print incoming and outgoing
918 * messages.
919 */
920 pjsip_endpt_register_module(pjsua.endpt, &console_msg_logger);
921
922
923 /* Start pjsua! */
924 if (pjsua_start() != PJ_SUCCESS) {
925
926 pjsua_destroy();
927 return 1;
928 }
929
930
931 /* Sleep for a while, let any messages get printed to console: */
932 pj_thread_sleep(500);
933
934
935 /* Start UI console main loop: */
936 ui_console_main();
937
938
939 /* Destroy pjsua: */
940 pjsua_destroy();
941
942
943 /* Close logging: */
944 app_logging_shutdown();
945
946
947 /* Exit... */
948
949 return 0;
950}
951