blob: 13f1dbd2e5fee467f3c0ad947f9e35accd329e4d [file] [log] [blame]
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004 *
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
21
22#define THIS_FILE "pjsua.c"
Benny Prijono804ff0a2006-09-14 11:17:48 +000023#define NO_LIMIT (int)0x7FFFFFFF
Benny Prijonoeebe9af2006-06-13 22:57:13 +000024
Benny Prijonoe909eac2006-07-27 22:04:56 +000025//#define STEREO_DEMO
Benny Prijonoeebe9af2006-06-13 22:57:13 +000026
Benny Prijono804ff0a2006-09-14 11:17:48 +000027/* Call specific data */
28struct call_data
29{
30 pj_timer_entry timer;
31};
32
Benny Prijonoeebe9af2006-06-13 22:57:13 +000033
34/* Pjsua application data */
35static struct app_config
36{
37 pjsua_config cfg;
38 pjsua_logging_config log_cfg;
39 pjsua_media_config media_cfg;
Benny Prijono4ddad2c2006-10-18 17:16:34 +000040 pj_bool_t no_refersub;
Benny Prijonoe93e2872006-06-28 16:46:49 +000041 pj_bool_t no_tcp;
42 pj_bool_t no_udp;
Benny Prijono6e0e54b2006-12-08 21:58:31 +000043 pj_bool_t use_tls;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000044 pjsua_transport_config udp_cfg;
45 pjsua_transport_config rtp_cfg;
46
47 unsigned acc_cnt;
48 pjsua_acc_config acc_cfg[PJSUA_MAX_ACC];
49
50 unsigned buddy_cnt;
51 pjsua_buddy_config buddy_cfg[PJSUA_MAX_BUDDIES];
52
Benny Prijono804ff0a2006-09-14 11:17:48 +000053 struct call_data call_data[PJSUA_MAX_CALLS];
54
Benny Prijonoeebe9af2006-06-13 22:57:13 +000055 pj_pool_t *pool;
56 /* Compatibility with older pjsua */
57
58 unsigned codec_cnt;
59 pj_str_t codec_arg[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +000060 pj_bool_t null_audio;
Benny Prijono32e4f492007-01-24 00:44:26 +000061 unsigned wav_count;
62 pj_str_t wav_files[32];
Benny Prijono4af234b2007-01-24 02:02:09 +000063 unsigned tone_count;
64 pjmedia_tone_desc tones[32];
65 pjsua_conf_port_id tone_slots[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +000066 pjsua_player_id wav_id;
67 pjsua_conf_port_id wav_port;
68 pj_bool_t auto_play;
69 pj_bool_t auto_loop;
Benny Prijono7ca96da2006-08-07 12:11:40 +000070 pj_bool_t auto_conf;
Benny Prijono1ebd6142006-10-19 15:48:02 +000071 pj_str_t rec_file;
72 pj_bool_t auto_rec;
73 pjsua_recorder_id rec_id;
74 pjsua_conf_port_id rec_port;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000075 unsigned auto_answer;
76 unsigned duration;
Benny Prijonoe909eac2006-07-27 22:04:56 +000077
78#ifdef STEREO_DEMO
79 pjmedia_snd_port *snd;
80#endif
81
Benny Prijono6dd967c2006-12-26 02:27:14 +000082 float mic_level,
83 speaker_level;
84
Benny Prijono4e5d5512007-03-06 18:11:30 +000085 int capture_dev, playback_dev;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000086} app_config;
87
88
Benny Prijono21b9ad92006-08-15 13:11:22 +000089//static pjsua_acc_id current_acc;
90#define current_acc pjsua_acc_get_default()
Benny Prijonof7b1c392006-11-11 16:46:34 +000091static pjsua_call_id current_call = PJSUA_INVALID_ID;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000092static pj_str_t uri_arg;
93
Benny Prijono594e4c52006-09-14 18:51:01 +000094#ifdef STEREO_DEMO
Benny Prijonoe909eac2006-07-27 22:04:56 +000095static void stereo_demo();
Benny Prijono594e4c52006-09-14 18:51:01 +000096#endif
Benny Prijonoad2e0ca2007-04-29 12:31:51 +000097pj_status_t app_destroy(void);
Benny Prijonoe909eac2006-07-27 22:04:56 +000098
Benny Prijonoeebe9af2006-06-13 22:57:13 +000099/*****************************************************************************
100 * Configuration manipulation
101 */
102
103/* Show usage */
104static void usage(void)
105{
106 puts ("Usage:");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000107 puts (" pjsua [options] [SIP URL to call]");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000108 puts ("");
109 puts ("General options:");
Benny Prijono804ff0a2006-09-14 11:17:48 +0000110 puts (" --config-file=file Read the config/arguments from file.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000111 puts (" --help Display this help screen");
112 puts (" --version Display version info");
113 puts ("");
114 puts ("Logging options:");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000115 puts (" --log-file=fname Log to filename (default stderr)");
116 puts (" --log-level=N Set log max level to N (0(none) to 6(trace)) (default=5)");
117 puts (" --app-log-level=N Set log max level for stdout display (default=4)");
118 puts ("");
119 puts ("SIP Account options:");
120 puts (" --registrar=url Set the URL of registrar server");
121 puts (" --id=url Set the URL of local ID (used in From header)");
122 puts (" --contact=url Optionally override the Contact information");
123 puts (" --proxy=url Optional URL of proxy server to visit");
124 puts (" May be specified multiple times");
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000125 puts (" --reg-timeout=SEC Optional registration interval (default 55)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000126 puts (" --realm=string Set realm");
127 puts (" --username=string Set authentication username");
128 puts (" --password=string Set authentication password");
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000129 puts (" --publish Send presence PUBLISH for this account");
Benny Prijonob67eaac2007-02-18 20:56:00 +0000130 puts (" --next-cred Add another credentials");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000131 puts ("");
132 puts ("SIP Account Control:");
133 puts (" --next-account Add more account");
134 puts ("");
135 puts ("Transport Options:");
Benny Prijonoe93e2872006-06-28 16:46:49 +0000136 puts (" --local-port=port Set TCP/UDP port. This implicitly enables both ");
137 puts (" TCP and UDP transports on the specified port, unless");
138 puts (" if TCP or UDP is disabled.");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000139 puts (" --ip-addr=IP Use the specifed address as SIP and RTP addresses.");
140 puts (" (Hint: the IP may be the public IP of the NAT/router)");
Benny Prijonoe93e2872006-06-28 16:46:49 +0000141 puts (" --no-tcp Disable TCP transport.");
142 puts (" --no-udp Disable UDP transport.");
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000143 puts (" --nameserver=NS Add the specified nameserver to enable SRV resolution");
144 puts (" This option can be specified multiple times.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000145 puts (" --outbound=url Set the URL of global outbound proxy server");
146 puts (" May be specified multiple times");
Benny Prijonoc97608e2007-03-23 16:34:20 +0000147 puts (" --stun-srv=name Set STUN server host or domain");
Benny Prijonof3bbc132006-12-25 06:43:59 +0000148 puts ("");
149 puts ("TLS Options:");
Benny Prijonob67eaac2007-02-18 20:56:00 +0000150 puts (" --use-tls Enable TLS transport (default=no)");
Benny Prijonof3bbc132006-12-25 06:43:59 +0000151 puts (" --tls-ca-file Specify TLS CA file (default=none)");
152 puts (" --tls-cert-file Specify TLS certificate file (default=none)");
153 puts (" --tls-privkey-file Specify TLS private key file (default=none)");
154 puts (" --tls-password Specify TLS password to private key file (default=none)");
155 puts (" --tls-verify-server Verify server's certificate (default=no)");
156 puts (" --tls-verify-client Verify client's certificate (default=no)");
157 puts (" --tls-neg-timeout Specify TLS negotiation timeout (default=no)");
158
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000159 puts ("");
160 puts ("Media Options:");
Benny Prijonoc97608e2007-03-23 16:34:20 +0000161 puts (" --use-ice Enable ICE (default:no)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162 puts (" --add-codec=name Manually add codec (default is to enable all)");
163 puts (" --clock-rate=N Override sound device clock rate");
164 puts (" --null-audio Use NULL audio device");
Benny Prijono4af234b2007-01-24 02:02:09 +0000165 puts (" --play-file=file Register WAV file in conference bridge.");
166 puts (" This can be specified multiple times.");
Benny Prijonob67eaac2007-02-18 20:56:00 +0000167 puts (" --play-tone=FORMAT Register tone to the conference bridge.");
168 puts (" FORMAT is 'F1,F2,ON,OFF', where F1,F2 are");
169 puts (" frequencies, and ON,OFF=on/off duration in msec.");
Benny Prijono4af234b2007-01-24 02:02:09 +0000170 puts (" This can be specified multiple times.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000171 puts (" --auto-play Automatically play the file (to incoming calls only)");
172 puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
Benny Prijono7ca96da2006-08-07 12:11:40 +0000173 puts (" --auto-conf Automatically put calls in conference with others");
Benny Prijono1ebd6142006-10-19 15:48:02 +0000174 puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");
175 puts (" --auto-rec Automatically record conversation");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000176 puts (" --rtp-port=N Base port to try for RTP (default=4000)");
Benny Prijono00cae612006-07-31 15:19:36 +0000177 puts (" --quality=N Specify media quality (0-10, default=6)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000178 puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)");
Benny Prijono0a12f002006-07-26 17:05:39 +0000179 puts (" --no-vad Disable VAD/silence detector (default=vad enabled)");
Benny Prijonod79f25c2006-08-02 19:41:37 +0000180 puts (" --ec-tail=MSEC Set echo canceller tail length (default=256)");
Benny Prijono00cae612006-07-31 15:19:36 +0000181 puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is 20)");
182 puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
183 puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
Benny Prijono4e5d5512007-03-06 18:11:30 +0000184 puts (" --capture-dev=id Audio capture device ID (default=-1)");
185 puts (" --playback-dev=id Audio playback device ID (default=-1)");
Benny Prijono00cae612006-07-31 15:19:36 +0000186
Benny Prijono0a12f002006-07-26 17:05:39 +0000187
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000188 puts ("");
189 puts ("Buddy List (can be more than one):");
190 puts (" --add-buddy url Add the specified URL to the buddy list.");
191 puts ("");
192 puts ("User Agent options:");
193 puts (" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)");
194 puts (" --max-calls=N Maximum number of concurrent calls (default:4, max:255)");
Benny Prijonof521eb02006-08-06 23:07:25 +0000195 puts (" --thread-cnt=N Number of worker threads (default:1)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196 puts (" --duration=SEC Set maximum call duration (default:no limit)");
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000197 puts (" --norefersub Suppress event subscription when transfering calls");
Benny Prijono804ff0a2006-09-14 11:17:48 +0000198
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000199 puts ("");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000200 puts ("When URL is specified, pjsua will immediately initiate call to that URL");
201 puts ("");
202
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000203 fflush(stdout);
204}
205
206
207/* Set default config. */
208static void default_config(struct app_config *cfg)
209{
Benny Prijono56315612006-07-18 14:39:40 +0000210 char tmp[80];
Benny Prijono1febfdf2007-02-18 01:35:04 +0000211 unsigned i;
Benny Prijono56315612006-07-18 14:39:40 +0000212
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213 pjsua_config_default(&cfg->cfg);
Benny Prijono106f5b72007-08-30 13:49:33 +0000214 pj_ansi_sprintf(tmp, "PJSUA v%s/%s", pj_get_version(), PJ_OS_NAME);
Benny Prijono56315612006-07-18 14:39:40 +0000215 pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp);
216
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000217 pjsua_logging_config_default(&cfg->log_cfg);
218 pjsua_media_config_default(&cfg->media_cfg);
219 pjsua_transport_config_default(&cfg->udp_cfg);
220 cfg->udp_cfg.port = 5060;
221 pjsua_transport_config_default(&cfg->rtp_cfg);
222 cfg->rtp_cfg.port = 4000;
Benny Prijono804ff0a2006-09-14 11:17:48 +0000223 cfg->duration = NO_LIMIT;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000224 cfg->wav_id = PJSUA_INVALID_ID;
Benny Prijono1ebd6142006-10-19 15:48:02 +0000225 cfg->rec_id = PJSUA_INVALID_ID;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000226 cfg->wav_port = PJSUA_INVALID_ID;
Benny Prijono1ebd6142006-10-19 15:48:02 +0000227 cfg->rec_port = PJSUA_INVALID_ID;
Benny Prijono6dd967c2006-12-26 02:27:14 +0000228 cfg->mic_level = cfg->speaker_level = 1.0;
Benny Prijono4e5d5512007-03-06 18:11:30 +0000229 cfg->capture_dev = PJSUA_INVALID_ID;
230 cfg->playback_dev = PJSUA_INVALID_ID;
Benny Prijono1febfdf2007-02-18 01:35:04 +0000231
232 for (i=0; i<PJ_ARRAY_SIZE(cfg->acc_cfg); ++i)
233 pjsua_acc_config_default(&cfg->acc_cfg[i]);
234
235 for (i=0; i<PJ_ARRAY_SIZE(cfg->buddy_cfg); ++i)
236 pjsua_buddy_config_default(&cfg->buddy_cfg[i]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237}
238
239
240/*
241 * Read command arguments from config file.
242 */
243static int read_config_file(pj_pool_t *pool, const char *filename,
244 int *app_argc, char ***app_argv)
245{
246 int i;
247 FILE *fhnd;
248 char line[200];
249 int argc = 0;
250 char **argv;
251 enum { MAX_ARGS = 64 };
252
253 /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
254 argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
255 argv[argc++] = *app_argv[0];
256
257 /* Open config file. */
258 fhnd = fopen(filename, "rt");
259 if (!fhnd) {
260 PJ_LOG(1,(THIS_FILE, "Unable to open config file %s", filename));
261 fflush(stdout);
262 return -1;
263 }
264
265 /* Scan tokens in the file. */
266 while (argc < MAX_ARGS && !feof(fhnd)) {
Benny Prijonobf5b4692007-06-28 03:20:17 +0000267 char *token;
268 char *p;
269 const char *whitespace = " \t\r\n";
270 char cDelimiter;
271 int len, token_len;
272
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000273 if (fgets(line, sizeof(line), fhnd) == NULL) break;
Benny Prijonobf5b4692007-06-28 03:20:17 +0000274
275 // Trim ending newlines
276 len = strlen(line);
277 if (line[len-1]=='\n')
278 line[--len] = '\0';
279 if (line[len-1]=='\r')
280 line[--len] = '\0';
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000281
Benny Prijonobf5b4692007-06-28 03:20:17 +0000282 if (len==0) continue;
283
284 for (p = line; *p != '\0' && argc < MAX_ARGS; p++) {
285 // first, scan whitespaces
286 while (*p != '\0' && strchr(whitespace, *p) != NULL) p++;
287
288 if (*p == '\0') // are we done yet?
289 break;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000290
Benny Prijonobf5b4692007-06-28 03:20:17 +0000291 if (*p == '"' || *p == '\'') { // is token a quoted string
292 cDelimiter = *p++; // save quote delimiter
293 token = p;
294
295 while (*p != '\0' && *p != cDelimiter) p++;
296
297 if (*p == '\0') // found end of the line, but,
298 cDelimiter = '\0'; // didn't find a matching quote
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000299
Benny Prijonobf5b4692007-06-28 03:20:17 +0000300 } else { // token's not a quoted string
301 token = p;
302
303 while (*p != '\0' && strchr(whitespace, *p) == NULL) p++;
304
305 cDelimiter = *p;
306 }
307
308 *p = '\0';
309 token_len = p-token;
310
311 if (token_len > 0) {
312 if (*token == '#')
313 break; // ignore remainder of line
314
315 argv[argc] = pj_pool_alloc(pool, token_len + 1);
316 pj_memcpy(argv[argc], token, token_len + 1);
317 ++argc;
318 }
319
320 *p = cDelimiter;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000321 }
322 }
323
324 /* Copy arguments from command line */
325 for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
326 argv[argc++] = (*app_argv)[i];
327
328 if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
329 PJ_LOG(1,(THIS_FILE,
330 "Too many arguments specified in cmd line/config file"));
331 fflush(stdout);
332 fclose(fhnd);
333 return -1;
334 }
335
336 fclose(fhnd);
337
338 /* Assign the new command line back to the original command line. */
339 *app_argc = argc;
340 *app_argv = argv;
341 return 0;
342
343}
344
345static int my_atoi(const char *cs)
346{
347 pj_str_t s;
Benny Prijono1e2dbe62007-06-15 04:15:16 +0000348
349 pj_cstr(&s, cs);
350 if (cs[0] == '-') {
351 s.ptr++, s.slen--;
352 return 0 - (int)pj_strtoul(&s);
353 } else if (cs[0] == '+') {
354 s.ptr++, s.slen--;
355 return pj_strtoul(&s);
356 } else {
357 return pj_strtoul(&s);
358 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000359}
360
361
362/* Parse arguments. */
363static pj_status_t parse_args(int argc, char *argv[],
364 struct app_config *cfg,
365 pj_str_t *uri_to_call)
366{
367 int c;
368 int option_index;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000369 enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000370 OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
Benny Prijono0a5cad82006-09-26 13:21:02 +0000371 OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY,
372 OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000373 OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
Benny Prijonoebbf6892007-03-24 17:37:25 +0000374 OPT_NAMESERVER, OPT_STUN_DOMAIN, OPT_STUN_SRV,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000375 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
376 OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
Benny Prijonoc97608e2007-03-23 16:34:20 +0000377 OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_USE_ICE,
Benny Prijono4af234b2007-01-24 02:02:09 +0000378 OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC,
379 OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
Benny Prijono0a12f002006-07-26 17:05:39 +0000380 OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
Benny Prijonod79f25c2006-08-02 19:41:37 +0000381 OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL,
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000382 OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
Benny Prijonof521eb02006-08-06 23:07:25 +0000383 OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000384 OPT_NOREFERSUB,
Benny Prijonof3bbc132006-12-25 06:43:59 +0000385 OPT_USE_TLS, OPT_TLS_CA_FILE, OPT_TLS_CERT_FILE, OPT_TLS_PRIV_FILE,
386 OPT_TLS_PASSWORD, OPT_TLS_VERIFY_SERVER, OPT_TLS_VERIFY_CLIENT,
387 OPT_TLS_NEG_TIMEOUT,
Benny Prijono4e5d5512007-03-06 18:11:30 +0000388 OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000389 };
390 struct pj_getopt_option long_options[] = {
391 { "config-file",1, 0, OPT_CONFIG_FILE},
392 { "log-file", 1, 0, OPT_LOG_FILE},
393 { "log-level", 1, 0, OPT_LOG_LEVEL},
394 { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
395 { "help", 0, 0, OPT_HELP},
396 { "version", 0, 0, OPT_VERSION},
397 { "clock-rate", 1, 0, OPT_CLOCK_RATE},
398 { "null-audio", 0, 0, OPT_NULL_AUDIO},
399 { "local-port", 1, 0, OPT_LOCAL_PORT},
Benny Prijono0a5cad82006-09-26 13:21:02 +0000400 { "ip-addr", 1, 0, OPT_IP_ADDR},
Benny Prijonoe93e2872006-06-28 16:46:49 +0000401 { "no-tcp", 0, 0, OPT_NO_TCP},
402 { "no-udp", 0, 0, OPT_NO_UDP},
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000403 { "norefersub", 0, 0, OPT_NOREFERSUB},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000404 { "proxy", 1, 0, OPT_PROXY},
405 { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
406 { "registrar", 1, 0, OPT_REGISTRAR},
407 { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000408 { "publish", 0, 0, OPT_PUBLISH},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000409 { "id", 1, 0, OPT_ID},
410 { "contact", 1, 0, OPT_CONTACT},
411 { "realm", 1, 0, OPT_REALM},
412 { "username", 1, 0, OPT_USERNAME},
413 { "password", 1, 0, OPT_PASSWORD},
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000414 { "nameserver", 1, 0, OPT_NAMESERVER},
Benny Prijonoebbf6892007-03-24 17:37:25 +0000415 { "stun-domain",1, 0, OPT_STUN_DOMAIN},
Benny Prijonoc97608e2007-03-23 16:34:20 +0000416 { "stun-srv", 1, 0, OPT_STUN_SRV},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000417 { "add-buddy", 1, 0, OPT_ADD_BUDDY},
418 { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
419 { "no-presence", 0, 0, OPT_NO_PRESENCE},
420 { "auto-answer",1, 0, OPT_AUTO_ANSWER},
421 { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
422 { "auto-play", 0, 0, OPT_AUTO_PLAY},
Benny Prijono1ebd6142006-10-19 15:48:02 +0000423 { "auto-rec", 0, 0, OPT_AUTO_REC},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000424 { "auto-loop", 0, 0, OPT_AUTO_LOOP},
425 { "auto-conf", 0, 0, OPT_AUTO_CONF},
426 { "play-file", 1, 0, OPT_PLAY_FILE},
Benny Prijono4af234b2007-01-24 02:02:09 +0000427 { "play-tone", 1, 0, OPT_PLAY_TONE},
Benny Prijono1ebd6142006-10-19 15:48:02 +0000428 { "rec-file", 1, 0, OPT_REC_FILE},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000429 { "rtp-port", 1, 0, OPT_RTP_PORT},
Benny Prijonoc97608e2007-03-23 16:34:20 +0000430 { "use-ice", 0, 0, OPT_USE_ICE},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000431 { "add-codec", 1, 0, OPT_ADD_CODEC},
432 { "complexity", 1, 0, OPT_COMPLEXITY},
433 { "quality", 1, 0, OPT_QUALITY},
434 { "ptime", 1, 0, OPT_PTIME},
Benny Prijono0a12f002006-07-26 17:05:39 +0000435 { "no-vad", 0, 0, OPT_NO_VAD},
Benny Prijonod79f25c2006-08-02 19:41:37 +0000436 { "ec-tail", 1, 0, OPT_EC_TAIL},
Benny Prijono00cae612006-07-31 15:19:36 +0000437 { "ilbc-mode", 1, 0, OPT_ILBC_MODE},
438 { "rx-drop-pct",1, 0, OPT_RX_DROP_PCT},
439 { "tx-drop-pct",1, 0, OPT_TX_DROP_PCT},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000440 { "next-account",0,0, OPT_NEXT_ACCOUNT},
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000441 { "next-cred", 0, 0, OPT_NEXT_CRED},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000442 { "max-calls", 1, 0, OPT_MAX_CALLS},
Benny Prijonof521eb02006-08-06 23:07:25 +0000443 { "duration", 1, 0, OPT_DURATION},
444 { "thread-cnt", 1, 0, OPT_THREAD_CNT},
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000445 { "use-tls", 0, 0, OPT_USE_TLS},
446 { "tls-ca-file",1, 0, OPT_TLS_CA_FILE},
Benny Prijonof3bbc132006-12-25 06:43:59 +0000447 { "tls-cert-file",1,0, OPT_TLS_CERT_FILE},
448 { "tls-privkey-file",1,0, OPT_TLS_PRIV_FILE},
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000449 { "tls-password",1,0, OPT_TLS_PASSWORD},
Benny Prijonof3bbc132006-12-25 06:43:59 +0000450 { "tls-verify-server", 0, 0, OPT_TLS_VERIFY_SERVER},
451 { "tls-verify-client", 0, 0, OPT_TLS_VERIFY_CLIENT},
452 { "tls-neg-timeout", 1, 0, OPT_TLS_NEG_TIMEOUT},
Benny Prijono4e5d5512007-03-06 18:11:30 +0000453 { "capture-dev", 1, 0, OPT_CAPTURE_DEV},
454 { "playback-dev", 1, 0, OPT_PLAYBACK_DEV},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000455 { NULL, 0, 0, 0}
456 };
457 pj_status_t status;
458 pjsua_acc_config *cur_acc;
459 char *config_file = NULL;
460 unsigned i;
461
462 /* Run pj_getopt once to see if user specifies config file to read. */
Benny Prijonof762ee72006-12-01 11:14:37 +0000463 pj_optind = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000464 while ((c=pj_getopt_long(argc, argv, "", long_options,
465 &option_index)) != -1)
466 {
467 switch (c) {
468 case OPT_CONFIG_FILE:
469 config_file = pj_optarg;
470 break;
471 }
472 if (config_file)
473 break;
474 }
475
476 if (config_file) {
477 status = read_config_file(app_config.pool, config_file, &argc, &argv);
478 if (status != 0)
479 return status;
480 }
481
482 cfg->acc_cnt = 0;
483 cur_acc = &cfg->acc_cfg[0];
484
485
486 /* Reinitialize and re-run pj_getopt again, possibly with new arguments
487 * read from config file.
488 */
489 pj_optind = 0;
490 while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000491 pj_str_t tmp;
492 long lval;
493
494 switch (c) {
495
Benny Prijono6f137482006-06-15 11:31:36 +0000496 case OPT_CONFIG_FILE:
497 /* Ignore as this has been processed before */
498 break;
499
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000500 case OPT_LOG_FILE:
501 cfg->log_cfg.log_filename = pj_str(pj_optarg);
502 break;
503
504 case OPT_LOG_LEVEL:
505 c = pj_strtoul(pj_cstr(&tmp, pj_optarg));
506 if (c < 0 || c > 6) {
507 PJ_LOG(1,(THIS_FILE,
508 "Error: expecting integer value 0-6 "
509 "for --log-level"));
510 return PJ_EINVAL;
511 }
512 cfg->log_cfg.level = c;
513 pj_log_set_level( c );
514 break;
515
516 case OPT_APP_LOG_LEVEL:
517 cfg->log_cfg.console_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
518 if (cfg->log_cfg.console_level < 0 || cfg->log_cfg.console_level > 6) {
519 PJ_LOG(1,(THIS_FILE,
520 "Error: expecting integer value 0-6 "
521 "for --app-log-level"));
522 return PJ_EINVAL;
523 }
524 break;
525
526 case OPT_HELP:
527 usage();
528 return PJ_EINVAL;
529
530 case OPT_VERSION: /* version */
531 pj_dump_config();
532 return PJ_EINVAL;
533
534 case OPT_NULL_AUDIO:
535 cfg->null_audio = PJ_TRUE;
536 break;
537
538 case OPT_CLOCK_RATE:
539 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
540 if (lval < 8000 || lval > 48000) {
541 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
542 "8000-48000 for clock rate"));
543 return PJ_EINVAL;
544 }
545 cfg->media_cfg.clock_rate = lval;
546 break;
547
548 case OPT_LOCAL_PORT: /* local-port */
549 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
Benny Prijonoe347cb02007-02-14 14:36:13 +0000550 if (lval < 0 || lval > 65535) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000551 PJ_LOG(1,(THIS_FILE,
552 "Error: expecting integer value for "
553 "--local-port"));
554 return PJ_EINVAL;
555 }
556 cfg->udp_cfg.port = (pj_uint16_t)lval;
557 break;
558
Benny Prijono0a5cad82006-09-26 13:21:02 +0000559 case OPT_IP_ADDR: /* ip-addr */
560 cfg->udp_cfg.public_addr = pj_str(pj_optarg);
561 cfg->rtp_cfg.public_addr = pj_str(pj_optarg);
562 break;
563
Benny Prijonoe93e2872006-06-28 16:46:49 +0000564 case OPT_NO_UDP: /* no-udp */
565 if (cfg->no_tcp) {
566 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
567 return PJ_EINVAL;
568 }
569
570 cfg->no_udp = PJ_TRUE;
571 break;
572
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000573 case OPT_NOREFERSUB: /* norefersub */
574 cfg->no_refersub = PJ_TRUE;
575 break;
576
Benny Prijonoe93e2872006-06-28 16:46:49 +0000577 case OPT_NO_TCP: /* no-tcp */
578 if (cfg->no_udp) {
579 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
580 return PJ_EINVAL;
581 }
582
583 cfg->no_tcp = PJ_TRUE;
584 break;
585
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586 case OPT_PROXY: /* proxy */
587 if (pjsua_verify_sip_url(pj_optarg) != 0) {
588 PJ_LOG(1,(THIS_FILE,
589 "Error: invalid SIP URL '%s' "
590 "in proxy argument", pj_optarg));
591 return PJ_EINVAL;
592 }
593 cur_acc->proxy[cur_acc->proxy_cnt++] = pj_str(pj_optarg);
594 break;
595
596 case OPT_OUTBOUND_PROXY: /* outbound proxy */
597 if (pjsua_verify_sip_url(pj_optarg) != 0) {
598 PJ_LOG(1,(THIS_FILE,
599 "Error: invalid SIP URL '%s' "
600 "in outbound proxy argument", pj_optarg));
601 return PJ_EINVAL;
602 }
603 cfg->cfg.outbound_proxy[cfg->cfg.outbound_proxy_cnt++] = pj_str(pj_optarg);
604 break;
605
606 case OPT_REGISTRAR: /* registrar */
607 if (pjsua_verify_sip_url(pj_optarg) != 0) {
608 PJ_LOG(1,(THIS_FILE,
609 "Error: invalid SIP URL '%s' in "
610 "registrar argument", pj_optarg));
611 return PJ_EINVAL;
612 }
613 cur_acc->reg_uri = pj_str(pj_optarg);
614 break;
615
616 case OPT_REG_TIMEOUT: /* reg-timeout */
617 cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,pj_optarg));
618 if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
619 PJ_LOG(1,(THIS_FILE,
620 "Error: invalid value for --reg-timeout "
621 "(expecting 1-3600)"));
622 return PJ_EINVAL;
623 }
624 break;
625
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000626 case OPT_PUBLISH: /* publish */
627 cur_acc->publish_enabled = PJ_TRUE;
628 break;
629
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000630 case OPT_ID: /* id */
631 if (pjsua_verify_sip_url(pj_optarg) != 0) {
632 PJ_LOG(1,(THIS_FILE,
633 "Error: invalid SIP URL '%s' "
634 "in local id argument", pj_optarg));
635 return PJ_EINVAL;
636 }
637 cur_acc->id = pj_str(pj_optarg);
638 break;
639
640 case OPT_CONTACT: /* contact */
641 if (pjsua_verify_sip_url(pj_optarg) != 0) {
642 PJ_LOG(1,(THIS_FILE,
643 "Error: invalid SIP URL '%s' "
644 "in contact argument", pj_optarg));
645 return PJ_EINVAL;
646 }
Benny Prijonob4a17c92006-07-10 14:40:21 +0000647 cur_acc->force_contact = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000648 break;
649
650 case OPT_NEXT_ACCOUNT: /* Add more account. */
651 cfg->acc_cnt++;
Benny Prijono56315612006-07-18 14:39:40 +0000652 cur_acc = &cfg->acc_cfg[cfg->acc_cnt];
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000653 break;
654
655 case OPT_USERNAME: /* Default authentication user */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000656 cur_acc->cred_info[cur_acc->cred_count].username = pj_str(pj_optarg);
657 cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("digest");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000658 break;
659
660 case OPT_REALM: /* Default authentication realm. */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000661 cur_acc->cred_info[cur_acc->cred_count].realm = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000662 break;
663
664 case OPT_PASSWORD: /* authentication password */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000665 cur_acc->cred_info[cur_acc->cred_count].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
666 cur_acc->cred_info[cur_acc->cred_count].data = pj_str(pj_optarg);
667 break;
668
669 case OPT_NEXT_CRED: /* next credential */
670 cur_acc->cred_count++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000671 break;
672
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000673 case OPT_NAMESERVER: /* nameserver */
674 cfg->cfg.nameserver[cfg->cfg.nameserver_count++] = pj_str(pj_optarg);
675 if (cfg->cfg.nameserver_count > PJ_ARRAY_SIZE(cfg->cfg.nameserver)) {
676 PJ_LOG(1,(THIS_FILE, "Error: too many nameservers"));
677 return PJ_ETOOMANY;
678 }
679 break;
680
Benny Prijonoebbf6892007-03-24 17:37:25 +0000681 case OPT_STUN_DOMAIN: /* STUN domain */
682 cfg->cfg.stun_domain = pj_str(pj_optarg);
683 break;
684
Benny Prijonoc97608e2007-03-23 16:34:20 +0000685 case OPT_STUN_SRV: /* STUN server */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000686 cfg->cfg.stun_host = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000687 break;
688
689 case OPT_ADD_BUDDY: /* Add to buddy list. */
690 if (pjsua_verify_sip_url(pj_optarg) != 0) {
691 PJ_LOG(1,(THIS_FILE,
692 "Error: invalid URL '%s' in "
693 "--add-buddy option", pj_optarg));
694 return -1;
695 }
696 if (cfg->buddy_cnt == PJ_ARRAY_SIZE(cfg->buddy_cfg)) {
697 PJ_LOG(1,(THIS_FILE,
698 "Error: too many buddies in buddy list."));
699 return -1;
700 }
701 cfg->buddy_cfg[cfg->buddy_cnt].uri = pj_str(pj_optarg);
702 cfg->buddy_cnt++;
703 break;
704
705 case OPT_AUTO_PLAY:
706 cfg->auto_play = 1;
707 break;
708
Benny Prijono1ebd6142006-10-19 15:48:02 +0000709 case OPT_AUTO_REC:
710 cfg->auto_rec = 1;
711 break;
712
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000713 case OPT_AUTO_LOOP:
714 cfg->auto_loop = 1;
715 break;
716
Benny Prijono7ca96da2006-08-07 12:11:40 +0000717 case OPT_AUTO_CONF:
718 cfg->auto_conf = 1;
719 break;
720
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000721 case OPT_PLAY_FILE:
Benny Prijono32e4f492007-01-24 00:44:26 +0000722 cfg->wav_files[cfg->wav_count++] = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000723 break;
724
Benny Prijono4af234b2007-01-24 02:02:09 +0000725 case OPT_PLAY_TONE:
726 {
727 int f1, f2, on, off;
728 int n;
729
730 n = sscanf(pj_optarg, "%d,%d,%d,%d", &f1, &f2, &on, &off);
731 if (n != 4) {
732 puts("Expecting f1,f2,on,off in --play-tone");
733 return -1;
734 }
735
736 cfg->tones[cfg->tone_count].freq1 = (short)f1;
737 cfg->tones[cfg->tone_count].freq2 = (short)f2;
738 cfg->tones[cfg->tone_count].on_msec = (short)on;
739 cfg->tones[cfg->tone_count].off_msec = (short)off;
740 ++cfg->tone_count;
741 }
742 break;
743
Benny Prijono1ebd6142006-10-19 15:48:02 +0000744 case OPT_REC_FILE:
745 cfg->rec_file = pj_str(pj_optarg);
746 break;
747
Benny Prijonoc97608e2007-03-23 16:34:20 +0000748 case OPT_USE_ICE:
749 cfg->media_cfg.enable_ice = PJ_TRUE;
750 break;
751
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000752 case OPT_RTP_PORT:
753 cfg->rtp_cfg.port = my_atoi(pj_optarg);
Benny Prijono5583a802007-06-26 12:20:37 +0000754 if (cfg->rtp_cfg.port == 0) {
755 enum { START_PORT=4000 };
756 unsigned range;
757
758 range = (65535-START_PORT-PJSUA_MAX_CALLS*2);
759 cfg->rtp_cfg.port = START_PORT +
760 ((pj_rand() % range) & 0xFFFE);
761 }
762
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000763 if (cfg->rtp_cfg.port < 1 || cfg->rtp_cfg.port > 65535) {
764 PJ_LOG(1,(THIS_FILE,
765 "Error: rtp-port argument value "
766 "(expecting 1-65535"));
767 return -1;
768 }
769 break;
770
771 case OPT_ADD_CODEC:
772 cfg->codec_arg[cfg->codec_cnt++] = pj_str(pj_optarg);
773 break;
774
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000775 /* These options were no longer valid after new pjsua */
776 /*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000777 case OPT_COMPLEXITY:
778 cfg->complexity = my_atoi(pj_optarg);
779 if (cfg->complexity < 0 || cfg->complexity > 10) {
780 PJ_LOG(1,(THIS_FILE,
781 "Error: invalid --complexity (expecting 0-10"));
782 return -1;
783 }
784 break;
Benny Prijono804ff0a2006-09-14 11:17:48 +0000785 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000786
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000787 case OPT_DURATION:
788 cfg->duration = my_atoi(pj_optarg);
789 break;
790
Benny Prijonof521eb02006-08-06 23:07:25 +0000791 case OPT_THREAD_CNT:
792 cfg->cfg.thread_cnt = my_atoi(pj_optarg);
793 if (cfg->cfg.thread_cnt > 128) {
794 PJ_LOG(1,(THIS_FILE,
795 "Error: invalid --thread-cnt option"));
796 return -1;
797 }
798 break;
799
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000800 case OPT_PTIME:
Benny Prijono0a12f002006-07-26 17:05:39 +0000801 cfg->media_cfg.ptime = my_atoi(pj_optarg);
802 if (cfg->media_cfg.ptime < 10 || cfg->media_cfg.ptime > 1000) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000803 PJ_LOG(1,(THIS_FILE,
804 "Error: invalid --ptime option"));
805 return -1;
806 }
807 break;
808
Benny Prijono0a12f002006-07-26 17:05:39 +0000809 case OPT_NO_VAD:
810 cfg->media_cfg.no_vad = PJ_TRUE;
811 break;
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000812
Benny Prijonod79f25c2006-08-02 19:41:37 +0000813 case OPT_EC_TAIL:
814 cfg->media_cfg.ec_tail_len = my_atoi(pj_optarg);
815 if (cfg->media_cfg.ec_tail_len > 1000) {
816 PJ_LOG(1,(THIS_FILE, "I think the ec-tail length setting "
817 "is too big"));
818 return -1;
819 }
820 break;
821
Benny Prijono0498d902006-06-19 14:49:14 +0000822 case OPT_QUALITY:
823 cfg->media_cfg.quality = my_atoi(pj_optarg);
824 if (cfg->media_cfg.quality < 0 || cfg->media_cfg.quality > 10) {
825 PJ_LOG(1,(THIS_FILE,
826 "Error: invalid --quality (expecting 0-10"));
827 return -1;
828 }
829 break;
830
Benny Prijono00cae612006-07-31 15:19:36 +0000831 case OPT_ILBC_MODE:
832 cfg->media_cfg.ilbc_mode = my_atoi(pj_optarg);
833 if (cfg->media_cfg.ilbc_mode!=20 && cfg->media_cfg.ilbc_mode!=30) {
834 PJ_LOG(1,(THIS_FILE,
835 "Error: invalid --ilbc-mode (expecting 20 or 30"));
836 return -1;
837 }
838 break;
839
840 case OPT_RX_DROP_PCT:
841 cfg->media_cfg.rx_drop_pct = my_atoi(pj_optarg);
842 if (cfg->media_cfg.rx_drop_pct > 100) {
843 PJ_LOG(1,(THIS_FILE,
844 "Error: invalid --rx-drop-pct (expecting <= 100"));
845 return -1;
846 }
847 break;
848
849 case OPT_TX_DROP_PCT:
850 cfg->media_cfg.tx_drop_pct = my_atoi(pj_optarg);
851 if (cfg->media_cfg.tx_drop_pct > 100) {
852 PJ_LOG(1,(THIS_FILE,
853 "Error: invalid --tx-drop-pct (expecting <= 100"));
854 return -1;
855 }
856 break;
857
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000858 case OPT_AUTO_ANSWER:
859 cfg->auto_answer = my_atoi(pj_optarg);
860 if (cfg->auto_answer < 100 || cfg->auto_answer > 699) {
861 PJ_LOG(1,(THIS_FILE,
862 "Error: invalid code in --auto-answer "
863 "(expecting 100-699"));
864 return -1;
865 }
866 break;
867
868 case OPT_MAX_CALLS:
869 cfg->cfg.max_calls = my_atoi(pj_optarg);
Benny Prijono48af79c2006-07-22 12:49:17 +0000870 if (cfg->cfg.max_calls < 1 || cfg->cfg.max_calls > PJSUA_MAX_CALLS) {
Benny Prijono804ff0a2006-09-14 11:17:48 +0000871 PJ_LOG(1,(THIS_FILE,"Error: maximum call setting exceeds "
872 "compile time limit (PJSUA_MAX_CALLS=%d)",
Benny Prijono48af79c2006-07-22 12:49:17 +0000873 PJSUA_MAX_CALLS));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000874 return -1;
875 }
876 break;
877
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000878 case OPT_USE_TLS:
879 cfg->use_tls = PJ_TRUE;
Benny Prijonof3bbc132006-12-25 06:43:59 +0000880#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
881 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
882 return -1;
883#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000884 break;
885
886 case OPT_TLS_CA_FILE:
Benny Prijonof3bbc132006-12-25 06:43:59 +0000887 cfg->udp_cfg.tls_setting.ca_list_file = pj_str(pj_optarg);
888#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
889 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
890 return -1;
891#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000892 break;
893
Benny Prijonof3bbc132006-12-25 06:43:59 +0000894 case OPT_TLS_CERT_FILE:
895 cfg->udp_cfg.tls_setting.cert_file = pj_str(pj_optarg);
896#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
897 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
898 return -1;
899#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000900 break;
901
Benny Prijonof3bbc132006-12-25 06:43:59 +0000902 case OPT_TLS_PRIV_FILE:
903 cfg->udp_cfg.tls_setting.privkey_file = pj_str(pj_optarg);
904 break;
905
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000906 case OPT_TLS_PASSWORD:
Benny Prijonof3bbc132006-12-25 06:43:59 +0000907 cfg->udp_cfg.tls_setting.password = pj_str(pj_optarg);
908#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
909 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
910 return -1;
911#endif
912 break;
913
914 case OPT_TLS_VERIFY_SERVER:
915 cfg->udp_cfg.tls_setting.verify_server = PJ_TRUE;
916 break;
917
918 case OPT_TLS_VERIFY_CLIENT:
919 cfg->udp_cfg.tls_setting.verify_client = PJ_TRUE;
920 break;
921
922 case OPT_TLS_NEG_TIMEOUT:
923 cfg->udp_cfg.tls_setting.timeout.sec = atoi(pj_optarg);
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000924 break;
925
Benny Prijono4e5d5512007-03-06 18:11:30 +0000926 case OPT_CAPTURE_DEV:
927 cfg->capture_dev = atoi(pj_optarg);
928 break;
929
930 case OPT_PLAYBACK_DEV:
931 cfg->playback_dev = atoi(pj_optarg);
932 break;
933
Benny Prijono22a300a2006-06-14 20:04:55 +0000934 default:
Benny Prijono787b8692006-06-19 12:40:03 +0000935 PJ_LOG(1,(THIS_FILE,
Benny Prijonod6388ac2006-09-09 13:23:09 +0000936 "Argument \"%s\" is not valid. Use --help to see help",
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000937 argv[pj_optind-1]));
Benny Prijono22a300a2006-06-14 20:04:55 +0000938 return -1;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000939 }
940 }
941
942 if (pj_optind != argc) {
943 pj_str_t uri_arg;
944
945 if (pjsua_verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
946 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
947 return -1;
948 }
949 uri_arg = pj_str(argv[pj_optind]);
950 if (uri_to_call)
951 *uri_to_call = uri_arg;
952 pj_optind++;
953
954 /* Add URI to call to buddy list if it's not already there */
955 for (i=0; i<cfg->buddy_cnt; ++i) {
956 if (pj_stricmp(&cfg->buddy_cfg[i].uri, &uri_arg)==0)
957 break;
958 }
959 if (i == cfg->buddy_cnt && cfg->buddy_cnt < PJSUA_MAX_BUDDIES) {
960 cfg->buddy_cfg[cfg->buddy_cnt++].uri = uri_arg;
961 }
962
963 } else {
964 if (uri_to_call)
965 uri_to_call->slen = 0;
966 }
967
968 if (pj_optind != argc) {
969 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
970 return PJ_EINVAL;
971 }
972
Benny Prijono56315612006-07-18 14:39:40 +0000973 if (cfg->acc_cfg[cfg->acc_cnt].id.slen)
974 cfg->acc_cnt++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000975
976 for (i=0; i<cfg->acc_cnt; ++i) {
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000977 if (cfg->acc_cfg[i].cred_info[cfg->acc_cfg[i].cred_count].username.slen)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000978 {
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000979 cfg->acc_cfg[i].cred_count++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000980 }
981 }
982
983
984 return PJ_SUCCESS;
985}
986
987
988/*
989 * Save account settings
990 */
991static void write_account_settings(int acc_index, pj_str_t *result)
992{
993 unsigned i;
994 char line[128];
995 pjsua_acc_config *acc_cfg = &app_config.acc_cfg[acc_index];
996
997
998 pj_ansi_sprintf(line, "\n#\n# Account %d:\n#\n", acc_index);
999 pj_strcat2(result, line);
1000
1001
1002 /* Identity */
1003 if (acc_cfg->id.slen) {
1004 pj_ansi_sprintf(line, "--id %.*s\n",
1005 (int)acc_cfg->id.slen,
1006 acc_cfg->id.ptr);
1007 pj_strcat2(result, line);
1008 }
1009
1010 /* Registrar server */
1011 if (acc_cfg->reg_uri.slen) {
1012 pj_ansi_sprintf(line, "--registrar %.*s\n",
1013 (int)acc_cfg->reg_uri.slen,
1014 acc_cfg->reg_uri.ptr);
1015 pj_strcat2(result, line);
1016
1017 pj_ansi_sprintf(line, "--reg-timeout %u\n",
1018 acc_cfg->reg_timeout);
1019 pj_strcat2(result, line);
1020 }
1021
1022 /* Contact */
Benny Prijonob4a17c92006-07-10 14:40:21 +00001023 if (acc_cfg->force_contact.slen) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001024 pj_ansi_sprintf(line, "--contact %.*s\n",
Benny Prijonob4a17c92006-07-10 14:40:21 +00001025 (int)acc_cfg->force_contact.slen,
1026 acc_cfg->force_contact.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001027 pj_strcat2(result, line);
1028 }
1029
1030 /* Proxy */
1031 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
1032 pj_ansi_sprintf(line, "--proxy %.*s\n",
1033 (int)acc_cfg->proxy[i].slen,
1034 acc_cfg->proxy[i].ptr);
1035 pj_strcat2(result, line);
1036 }
1037
1038 /* Credentials */
1039 for (i=0; i<acc_cfg->cred_count; ++i) {
1040 if (acc_cfg->cred_info[i].realm.slen) {
1041 pj_ansi_sprintf(line, "--realm %.*s\n",
1042 (int)acc_cfg->cred_info[i].realm.slen,
1043 acc_cfg->cred_info[i].realm.ptr);
1044 pj_strcat2(result, line);
1045 }
1046
1047 if (acc_cfg->cred_info[i].username.slen) {
1048 pj_ansi_sprintf(line, "--username %.*s\n",
1049 (int)acc_cfg->cred_info[i].username.slen,
1050 acc_cfg->cred_info[i].username.ptr);
1051 pj_strcat2(result, line);
1052 }
1053
1054 if (acc_cfg->cred_info[i].data.slen) {
1055 pj_ansi_sprintf(line, "--password %.*s\n",
1056 (int)acc_cfg->cred_info[i].data.slen,
1057 acc_cfg->cred_info[i].data.ptr);
1058 pj_strcat2(result, line);
1059 }
Benny Prijonoeef4f8c2006-06-19 12:07:44 +00001060
1061 if (i != acc_cfg->cred_count - 1)
1062 pj_strcat2(result, "--next-cred\n");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001063 }
1064
1065}
1066
1067
1068/*
1069 * Write settings.
1070 */
1071static int write_settings(const struct app_config *config,
1072 char *buf, pj_size_t max)
1073{
1074 unsigned acc_index;
1075 unsigned i;
1076 pj_str_t cfg;
1077 char line[128];
1078
1079 PJ_UNUSED_ARG(max);
1080
1081 cfg.ptr = buf;
1082 cfg.slen = 0;
1083
1084 /* Logging. */
1085 pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
1086 pj_ansi_sprintf(line, "--log-level %d\n",
1087 config->log_cfg.level);
1088 pj_strcat2(&cfg, line);
1089
1090 pj_ansi_sprintf(line, "--app-log-level %d\n",
1091 config->log_cfg.console_level);
1092 pj_strcat2(&cfg, line);
1093
1094 if (config->log_cfg.log_filename.slen) {
1095 pj_ansi_sprintf(line, "--log-file %.*s\n",
1096 (int)config->log_cfg.log_filename.slen,
1097 config->log_cfg.log_filename.ptr);
1098 pj_strcat2(&cfg, line);
1099 }
1100
1101
1102 /* Save account settings. */
1103 for (acc_index=0; acc_index < config->acc_cnt; ++acc_index) {
1104
1105 write_account_settings(acc_index, &cfg);
1106
1107 if (acc_index < config->acc_cnt-1)
1108 pj_strcat2(&cfg, "--next-account\n");
1109 }
1110
1111
1112 pj_strcat2(&cfg, "\n#\n# Network settings:\n#\n");
1113
1114 /* Outbound proxy */
1115 for (i=0; i<config->cfg.outbound_proxy_cnt; ++i) {
1116 pj_ansi_sprintf(line, "--outbound %.*s\n",
1117 (int)config->cfg.outbound_proxy[i].slen,
1118 config->cfg.outbound_proxy[i].ptr);
1119 pj_strcat2(&cfg, line);
1120 }
1121
1122
1123 /* UDP Transport. */
1124 pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port);
1125 pj_strcat2(&cfg, line);
1126
Benny Prijono0a5cad82006-09-26 13:21:02 +00001127 /* IP address, if any. */
1128 if (config->udp_cfg.public_addr.slen) {
1129 pj_ansi_sprintf(line, "--ip-addr %.*s\n",
1130 (int)config->udp_cfg.public_addr.slen,
1131 config->udp_cfg.public_addr.ptr);
1132 pj_strcat2(&cfg, line);
1133 }
1134
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001135 /* No TCP ? */
1136 if (config->no_tcp) {
1137 pj_strcat2(&cfg, "--no-tcp\n");
1138 }
1139
1140 /* No UDP ? */
1141 if (config->no_udp) {
1142 pj_strcat2(&cfg, "--no-udp\n");
1143 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001144
1145 /* STUN */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001146 if (config->cfg.stun_domain.slen) {
1147 pj_ansi_sprintf(line, "--stun-domain %.*s\n",
1148 (int)config->cfg.stun_domain.slen,
1149 config->cfg.stun_domain.ptr);
1150 pj_strcat2(&cfg, line);
1151 }
1152 if (config->cfg.stun_host.slen) {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001153 pj_ansi_sprintf(line, "--stun-srv %.*s\n",
Benny Prijonoebbf6892007-03-24 17:37:25 +00001154 (int)config->cfg.stun_host.slen,
1155 config->cfg.stun_host.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001156 pj_strcat2(&cfg, line);
1157 }
1158
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001159
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001160 /* TLS */
1161 if (config->use_tls)
1162 pj_strcat2(&cfg, "--use-tls\n");
Benny Prijonof3bbc132006-12-25 06:43:59 +00001163 if (config->udp_cfg.tls_setting.ca_list_file.slen) {
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001164 pj_ansi_sprintf(line, "--tls-ca-file %.*s\n",
Benny Prijonof3bbc132006-12-25 06:43:59 +00001165 (int)config->udp_cfg.tls_setting.ca_list_file.slen,
1166 config->udp_cfg.tls_setting.ca_list_file.ptr);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001167 pj_strcat2(&cfg, line);
1168 }
Benny Prijonof3bbc132006-12-25 06:43:59 +00001169 if (config->udp_cfg.tls_setting.cert_file.slen) {
1170 pj_ansi_sprintf(line, "--tls-cert-file %.*s\n",
1171 (int)config->udp_cfg.tls_setting.cert_file.slen,
1172 config->udp_cfg.tls_setting.cert_file.ptr);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001173 pj_strcat2(&cfg, line);
1174 }
Benny Prijonof3bbc132006-12-25 06:43:59 +00001175 if (config->udp_cfg.tls_setting.privkey_file.slen) {
1176 pj_ansi_sprintf(line, "--tls-privkey-file %.*s\n",
1177 (int)config->udp_cfg.tls_setting.privkey_file.slen,
1178 config->udp_cfg.tls_setting.privkey_file.ptr);
1179 pj_strcat2(&cfg, line);
1180 }
1181
1182 if (config->udp_cfg.tls_setting.password.slen) {
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001183 pj_ansi_sprintf(line, "--tls-password %.*s\n",
Benny Prijonof3bbc132006-12-25 06:43:59 +00001184 (int)config->udp_cfg.tls_setting.password.slen,
1185 config->udp_cfg.tls_setting.password.ptr);
1186 pj_strcat2(&cfg, line);
1187 }
1188
1189 if (config->udp_cfg.tls_setting.verify_server)
1190 pj_strcat2(&cfg, "--tls-verify-server\n");
1191
1192 if (config->udp_cfg.tls_setting.verify_client)
1193 pj_strcat2(&cfg, "--tls-verify-client\n");
1194
1195 if (config->udp_cfg.tls_setting.timeout.sec) {
1196 pj_ansi_sprintf(line, "--tls-neg-timeout %d\n",
Benny Prijonoe960bb52007-01-21 17:53:39 +00001197 (int)config->udp_cfg.tls_setting.timeout.sec);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001198 pj_strcat2(&cfg, line);
1199 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001200
1201 pj_strcat2(&cfg, "\n#\n# Media settings:\n#\n");
1202
1203
1204 /* Media */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001205 if (config->media_cfg.enable_ice)
1206 pj_strcat2(&cfg, "--use-ice\n");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001207 if (config->null_audio)
1208 pj_strcat2(&cfg, "--null-audio\n");
1209 if (config->auto_play)
1210 pj_strcat2(&cfg, "--auto-play\n");
1211 if (config->auto_loop)
1212 pj_strcat2(&cfg, "--auto-loop\n");
Benny Prijono7ca96da2006-08-07 12:11:40 +00001213 if (config->auto_conf)
1214 pj_strcat2(&cfg, "--auto-conf\n");
Benny Prijono32e4f492007-01-24 00:44:26 +00001215 for (i=0; i<config->wav_count; ++i) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001216 pj_ansi_sprintf(line, "--play-file %s\n",
Benny Prijono32e4f492007-01-24 00:44:26 +00001217 config->wav_files[i].ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001218 pj_strcat2(&cfg, line);
1219 }
Benny Prijono4af234b2007-01-24 02:02:09 +00001220 for (i=0; i<config->tone_count; ++i) {
1221 pj_ansi_sprintf(line, "--play-tone %d,%d,%d,%d\n",
1222 config->tones[i].freq1, config->tones[i].freq2,
1223 config->tones[i].on_msec, config->tones[i].off_msec);
1224 pj_strcat2(&cfg, line);
1225 }
Benny Prijono1ebd6142006-10-19 15:48:02 +00001226 if (config->rec_file.slen) {
1227 pj_ansi_sprintf(line, "--rec-file %s\n",
1228 config->rec_file.ptr);
1229 pj_strcat2(&cfg, line);
1230 }
1231 if (config->auto_rec)
1232 pj_strcat2(&cfg, "--auto-rec\n");
Benny Prijono4e5d5512007-03-06 18:11:30 +00001233 if (config->capture_dev != PJSUA_INVALID_ID) {
1234 pj_ansi_sprintf(line, "--capture-dev %d\n", config->capture_dev);
1235 pj_strcat2(&cfg, line);
1236 }
1237 if (config->playback_dev != PJSUA_INVALID_ID) {
1238 pj_ansi_sprintf(line, "--playback-dev %d\n", config->playback_dev);
1239 pj_strcat2(&cfg, line);
1240 }
Benny Prijono1ebd6142006-10-19 15:48:02 +00001241
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001242 /* Media clock rate. */
Benny Prijono70972992006-08-05 11:13:58 +00001243 if (config->media_cfg.clock_rate != PJSUA_DEFAULT_CLOCK_RATE) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001244 pj_ansi_sprintf(line, "--clock-rate %d\n",
Benny Prijono0498d902006-06-19 14:49:14 +00001245 config->media_cfg.clock_rate);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001246 pj_strcat2(&cfg, line);
Benny Prijono70972992006-08-05 11:13:58 +00001247 } else {
1248 pj_ansi_sprintf(line, "#using default --clock-rate %d\n",
1249 config->media_cfg.clock_rate);
1250 pj_strcat2(&cfg, line);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001251 }
Benny Prijono70972992006-08-05 11:13:58 +00001252
1253 /* quality */
1254 if (config->media_cfg.quality != PJSUA_DEFAULT_CODEC_QUALITY) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001255 pj_ansi_sprintf(line, "--quality %d\n",
Benny Prijono0498d902006-06-19 14:49:14 +00001256 config->media_cfg.quality);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001257 pj_strcat2(&cfg, line);
Benny Prijono70972992006-08-05 11:13:58 +00001258 } else {
1259 pj_ansi_sprintf(line, "#using default --quality %d\n",
1260 config->media_cfg.quality);
1261 pj_strcat2(&cfg, line);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001262 }
Benny Prijono0498d902006-06-19 14:49:14 +00001263
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001264
1265 /* ptime */
Benny Prijono2adfe292007-05-11 10:36:08 +00001266 if (config->media_cfg.ptime) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001267 pj_ansi_sprintf(line, "--ptime %d\n",
Benny Prijono2adfe292007-05-11 10:36:08 +00001268 config->media_cfg.ptime);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001269 pj_strcat2(&cfg, line);
1270 }
1271
Benny Prijono70972992006-08-05 11:13:58 +00001272 /* no-vad */
1273 if (config->media_cfg.no_vad) {
1274 pj_strcat2(&cfg, "--no-vad\n");
1275 }
1276
1277 /* ec-tail */
1278 if (config->media_cfg.ec_tail_len != PJSUA_DEFAULT_EC_TAIL_LEN) {
1279 pj_ansi_sprintf(line, "--ec-tail %d\n",
1280 config->media_cfg.ec_tail_len);
1281 pj_strcat2(&cfg, line);
1282 } else {
1283 pj_ansi_sprintf(line, "#using default --ec-tail %d\n",
1284 config->media_cfg.ec_tail_len);
1285 pj_strcat2(&cfg, line);
1286 }
1287
1288
1289 /* ilbc-mode */
1290 if (config->media_cfg.ilbc_mode != PJSUA_DEFAULT_ILBC_MODE) {
1291 pj_ansi_sprintf(line, "--ilbc-mode %d\n",
1292 config->media_cfg.ilbc_mode);
1293 pj_strcat2(&cfg, line);
1294 } else {
1295 pj_ansi_sprintf(line, "#using default --ilbc-mode %d\n",
1296 config->media_cfg.ilbc_mode);
1297 pj_strcat2(&cfg, line);
1298 }
1299
1300 /* RTP drop */
1301 if (config->media_cfg.tx_drop_pct) {
1302 pj_ansi_sprintf(line, "--tx-drop-pct %d\n",
1303 config->media_cfg.tx_drop_pct);
1304 pj_strcat2(&cfg, line);
1305
1306 }
1307 if (config->media_cfg.rx_drop_pct) {
1308 pj_ansi_sprintf(line, "--rx-drop-pct %d\n",
1309 config->media_cfg.rx_drop_pct);
1310 pj_strcat2(&cfg, line);
1311
1312 }
1313
1314
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001315 /* Start RTP port. */
1316 pj_ansi_sprintf(line, "--rtp-port %d\n",
1317 config->rtp_cfg.port);
1318 pj_strcat2(&cfg, line);
1319
1320 /* Add codec. */
1321 for (i=0; i<config->codec_cnt; ++i) {
1322 pj_ansi_sprintf(line, "--add-codec %s\n",
1323 config->codec_arg[i].ptr);
1324 pj_strcat2(&cfg, line);
1325 }
1326
1327 pj_strcat2(&cfg, "\n#\n# User agent:\n#\n");
1328
1329 /* Auto-answer. */
1330 if (config->auto_answer != 0) {
1331 pj_ansi_sprintf(line, "--auto-answer %d\n",
1332 config->auto_answer);
1333 pj_strcat2(&cfg, line);
1334 }
1335
1336 /* Max calls. */
1337 pj_ansi_sprintf(line, "--max-calls %d\n",
1338 config->cfg.max_calls);
1339 pj_strcat2(&cfg, line);
1340
1341 /* Uas-duration. */
Benny Prijono804ff0a2006-09-14 11:17:48 +00001342 if (config->duration != NO_LIMIT) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001343 pj_ansi_sprintf(line, "--duration %d\n",
1344 config->duration);
1345 pj_strcat2(&cfg, line);
1346 }
1347
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001348 /* norefersub ? */
1349 if (config->no_refersub) {
1350 pj_strcat2(&cfg, "--norefersub\n");
1351 }
1352
1353
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001354 pj_strcat2(&cfg, "\n#\n# Buddies:\n#\n");
1355
1356 /* Add buddies. */
1357 for (i=0; i<config->buddy_cnt; ++i) {
1358 pj_ansi_sprintf(line, "--add-buddy %.*s\n",
1359 (int)config->buddy_cfg[i].uri.slen,
1360 config->buddy_cfg[i].uri.ptr);
1361 pj_strcat2(&cfg, line);
1362 }
1363
1364
1365 *(cfg.ptr + cfg.slen) = '\0';
1366 return cfg.slen;
1367}
1368
1369
1370/*
1371 * Dump application states.
1372 */
1373static void app_dump(pj_bool_t detail)
1374{
Benny Prijonoda9785b2007-04-02 20:43:06 +00001375 pjsua_dump(detail);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001376}
1377
1378
1379/*****************************************************************************
1380 * Console application
1381 */
1382
1383/*
1384 * Find next call when current call is disconnected or when user
1385 * press ']'
1386 */
1387static pj_bool_t find_next_call(void)
1388{
1389 int i, max;
1390
1391 max = pjsua_call_get_max_count();
1392 for (i=current_call+1; i<max; ++i) {
1393 if (pjsua_call_is_active(i)) {
1394 current_call = i;
1395 return PJ_TRUE;
1396 }
1397 }
1398
1399 for (i=0; i<current_call; ++i) {
1400 if (pjsua_call_is_active(i)) {
1401 current_call = i;
1402 return PJ_TRUE;
1403 }
1404 }
1405
1406 current_call = PJSUA_INVALID_ID;
1407 return PJ_FALSE;
1408}
1409
1410
1411/*
1412 * Find previous call when user press '['
1413 */
1414static pj_bool_t find_prev_call(void)
1415{
1416 int i, max;
1417
1418 max = pjsua_call_get_max_count();
1419 for (i=current_call-1; i>=0; --i) {
1420 if (pjsua_call_is_active(i)) {
1421 current_call = i;
1422 return PJ_TRUE;
1423 }
1424 }
1425
1426 for (i=max-1; i>current_call; --i) {
1427 if (pjsua_call_is_active(i)) {
1428 current_call = i;
1429 return PJ_TRUE;
1430 }
1431 }
1432
1433 current_call = PJSUA_INVALID_ID;
1434 return PJ_FALSE;
1435}
1436
1437
Benny Prijono804ff0a2006-09-14 11:17:48 +00001438/* Callback from timer when the maximum call duration has been
1439 * exceeded.
1440 */
1441static void call_timeout_callback(pj_timer_heap_t *timer_heap,
1442 struct pj_timer_entry *entry)
1443{
1444 pjsua_call_id call_id = entry->id;
1445 pjsua_msg_data msg_data;
1446 pjsip_generic_string_hdr warn;
1447 pj_str_t hname = pj_str("Warning");
1448 pj_str_t hvalue = pj_str("399 pjsua \"Call duration exceeded\"");
1449
1450 PJ_UNUSED_ARG(timer_heap);
1451
Benny Prijono148c9dd2006-09-19 13:37:53 +00001452 if (call_id == PJSUA_INVALID_ID) {
1453 PJ_LOG(1,(THIS_FILE, "Invalid call ID in timer callback"));
1454 return;
1455 }
1456
Benny Prijono804ff0a2006-09-14 11:17:48 +00001457 /* Add warning header */
1458 pjsua_msg_data_init(&msg_data);
1459 pjsip_generic_string_hdr_init2(&warn, &hname, &hvalue);
1460 pj_list_push_back(&msg_data.hdr_list, &warn);
1461
1462 /* Call duration has been exceeded; disconnect the call */
1463 PJ_LOG(3,(THIS_FILE, "Duration (%d seconds) has been exceeded "
1464 "for call %d, disconnecting the call",
1465 app_config.duration, call_id));
1466 entry->id = PJSUA_INVALID_ID;
1467 pjsua_call_hangup(call_id, 200, NULL, &msg_data);
1468}
1469
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001470
1471/*
1472 * Handler when invite state has changed.
1473 */
1474static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
1475{
1476 pjsua_call_info call_info;
1477
1478 PJ_UNUSED_ARG(e);
1479
1480 pjsua_call_get_info(call_id, &call_info);
1481
1482 if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
1483
Benny Prijono804ff0a2006-09-14 11:17:48 +00001484 /* Cancel duration timer, if any */
1485 if (app_config.call_data[call_id].timer.id != PJSUA_INVALID_ID) {
1486 struct call_data *cd = &app_config.call_data[call_id];
1487 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
1488
1489 cd->timer.id = PJSUA_INVALID_ID;
1490 pjsip_endpt_cancel_timer(endpt, &cd->timer);
1491 }
1492
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001493 PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]",
1494 call_id,
1495 call_info.last_status,
1496 call_info.last_status_text.ptr));
1497
1498 if (call_id == current_call) {
1499 find_next_call();
1500 }
1501
Benny Prijono4be63b52006-11-25 14:50:25 +00001502 /* Dump media state upon disconnected */
1503 if (1) {
1504 char buf[1024];
1505 pjsua_call_dump(call_id, PJ_TRUE, buf,
1506 sizeof(buf), " ");
1507 PJ_LOG(5,(THIS_FILE,
1508 "Call %d disconnected, dumping media stats\n%s",
1509 call_id, buf));
1510 }
1511
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001512 } else {
1513
Benny Prijono804ff0a2006-09-14 11:17:48 +00001514 if (app_config.duration!=NO_LIMIT &&
1515 call_info.state == PJSIP_INV_STATE_CONFIRMED)
1516 {
1517 /* Schedule timer to hangup call after the specified duration */
1518 struct call_data *cd = &app_config.call_data[call_id];
1519 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
1520 pj_time_val delay;
1521
1522 cd->timer.id = call_id;
1523 delay.sec = app_config.duration;
1524 delay.msec = 0;
1525 pjsip_endpt_schedule_timer(endpt, &cd->timer, &delay);
1526 }
1527
Benny Prijono4be63b52006-11-25 14:50:25 +00001528 if (call_info.state == PJSIP_INV_STATE_EARLY) {
1529 int code;
1530 pj_str_t reason;
1531 pjsip_msg *msg;
1532
1533 /* This can only occur because of TX or RX message */
1534 pj_assert(e->type == PJSIP_EVENT_TSX_STATE);
1535
1536 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
1537 msg = e->body.tsx_state.src.rdata->msg_info.msg;
1538 } else {
1539 msg = e->body.tsx_state.src.tdata->msg;
1540 }
1541
1542 code = msg->line.status.code;
1543 reason = msg->line.status.reason;
1544
1545 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s (%d %.*s)",
1546 call_id, call_info.state_text.ptr,
1547 code, (int)reason.slen, reason.ptr));
1548 } else {
1549 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
1550 call_id,
1551 call_info.state_text.ptr));
1552 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001553
1554 if (current_call==PJSUA_INVALID_ID)
1555 current_call = call_id;
1556
1557 }
1558}
1559
1560
1561/**
1562 * Handler when there is incoming call.
1563 */
1564static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
1565 pjsip_rx_data *rdata)
1566{
1567 pjsua_call_info call_info;
1568
1569 PJ_UNUSED_ARG(acc_id);
1570 PJ_UNUSED_ARG(rdata);
1571
1572 pjsua_call_get_info(call_id, &call_info);
1573
1574 if (app_config.auto_answer > 0) {
1575 pjsua_call_answer(call_id, app_config.auto_answer, NULL, NULL);
1576 }
1577
1578 if (app_config.auto_answer < 200) {
1579 PJ_LOG(3,(THIS_FILE,
1580 "Incoming call for account %d!\n"
1581 "From: %s\n"
1582 "To: %s\n"
1583 "Press a to answer or h to reject call",
1584 acc_id,
1585 call_info.remote_info.ptr,
1586 call_info.local_info.ptr));
1587 }
1588}
1589
1590
1591/*
1592 * Callback on media state changed event.
1593 * The action may connect the call to sound device, to file, or
1594 * to loop the call.
1595 */
1596static void on_call_media_state(pjsua_call_id call_id)
1597{
1598 pjsua_call_info call_info;
1599
1600 pjsua_call_get_info(call_id, &call_info);
1601
1602 if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
1603 pj_bool_t connect_sound = PJ_TRUE;
1604
1605 /* Loopback sound, if desired */
1606 if (app_config.auto_loop) {
1607 pjsua_conf_connect(call_info.conf_slot, call_info.conf_slot);
1608 connect_sound = PJ_FALSE;
Benny Prijono1ebd6142006-10-19 15:48:02 +00001609
1610 /* Automatically record conversation, if desired */
1611 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1612 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
1613 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001614 }
1615
1616 /* Stream a file, if desired */
1617 if (app_config.auto_play && app_config.wav_port != PJSUA_INVALID_ID) {
1618 pjsua_conf_connect(app_config.wav_port, call_info.conf_slot);
1619 connect_sound = PJ_FALSE;
1620 }
1621
Benny Prijono7ca96da2006-08-07 12:11:40 +00001622 /* Put call in conference with other calls, if desired */
1623 if (app_config.auto_conf) {
1624 pjsua_call_id call_ids[PJSUA_MAX_CALLS];
Benny Prijono10861bc2006-08-07 13:22:43 +00001625 unsigned call_cnt=PJ_ARRAY_SIZE(call_ids);
Benny Prijono7ca96da2006-08-07 12:11:40 +00001626 unsigned i;
1627
1628 /* Get all calls, and establish media connection between
1629 * this call and other calls.
1630 */
1631 pjsua_enum_calls(call_ids, &call_cnt);
Benny Prijono10861bc2006-08-07 13:22:43 +00001632
Benny Prijono7ca96da2006-08-07 12:11:40 +00001633 for (i=0; i<call_cnt; ++i) {
1634 if (call_ids[i] == call_id)
1635 continue;
1636
1637 if (!pjsua_call_has_media(call_ids[i]))
1638 continue;
1639
1640 pjsua_conf_connect(call_info.conf_slot,
1641 pjsua_call_get_conf_port(call_ids[i]));
1642 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
1643 call_info.conf_slot);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001644
1645 /* Automatically record conversation, if desired */
1646 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1647 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
1648 app_config.rec_port);
1649 }
1650
Benny Prijono7ca96da2006-08-07 12:11:40 +00001651 }
1652
1653 /* Also connect call to local sound device */
1654 connect_sound = PJ_TRUE;
1655 }
1656
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001657 /* Otherwise connect to sound device */
1658 if (connect_sound) {
1659 pjsua_conf_connect(call_info.conf_slot, 0);
1660 pjsua_conf_connect(0, call_info.conf_slot);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001661
1662 /* Automatically record conversation, if desired */
1663 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1664 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
1665 pjsua_conf_connect(0, app_config.rec_port);
1666 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001667 }
1668
1669 PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id));
1670
1671 } else if (call_info.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
1672 PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local",
1673 call_id));
1674 } else if (call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD) {
1675 PJ_LOG(3,(THIS_FILE,
1676 "Media for call %d is suspended (hold) by remote",
1677 call_id));
Benny Prijono096c56c2007-09-15 08:30:16 +00001678 } else if (call_info.media_status == PJSUA_CALL_MEDIA_ERROR) {
1679 pj_str_t reason = pj_str("ICE negotiation failed");
1680
1681 PJ_LOG(1,(THIS_FILE,
1682 "Media has reported error, disconnecting call"));
1683
1684 pjsua_call_hangup(call_id, 500, &reason, NULL);
1685
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001686 } else {
1687 PJ_LOG(3,(THIS_FILE,
1688 "Media for call %d is inactive",
1689 call_id));
1690 }
1691}
1692
Benny Prijono0875ae82006-12-26 00:11:48 +00001693/*
1694 * DTMF callback.
1695 */
1696static void call_on_dtmf_callback(pjsua_call_id call_id, int dtmf)
1697{
1698 PJ_LOG(3,(THIS_FILE, "Incoming DTMF on call %d: %c", call_id, dtmf));
1699}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001700
1701/*
1702 * Handler registration status has changed.
1703 */
1704static void on_reg_state(pjsua_acc_id acc_id)
1705{
1706 PJ_UNUSED_ARG(acc_id);
1707
1708 // Log already written.
1709}
1710
1711
1712/*
1713 * Handler on buddy state changed.
1714 */
1715static void on_buddy_state(pjsua_buddy_id buddy_id)
1716{
1717 pjsua_buddy_info info;
1718 pjsua_buddy_get_info(buddy_id, &info);
1719
1720 PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s",
1721 (int)info.uri.slen,
1722 info.uri.ptr,
1723 (int)info.status_text.slen,
1724 info.status_text.ptr));
1725}
1726
1727
1728/**
1729 * Incoming IM message (i.e. MESSAGE request)!
1730 */
1731static void on_pager(pjsua_call_id call_id, const pj_str_t *from,
1732 const pj_str_t *to, const pj_str_t *contact,
1733 const pj_str_t *mime_type, const pj_str_t *text)
1734{
1735 /* Note: call index may be -1 */
1736 PJ_UNUSED_ARG(call_id);
1737 PJ_UNUSED_ARG(to);
1738 PJ_UNUSED_ARG(contact);
1739 PJ_UNUSED_ARG(mime_type);
1740
Benny Prijonof4b538d2007-05-14 16:45:20 +00001741 PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s (%.*s)",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001742 (int)from->slen, from->ptr,
Benny Prijonof4b538d2007-05-14 16:45:20 +00001743 (int)text->slen, text->ptr,
1744 (int)mime_type->slen, mime_type->ptr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001745}
1746
1747
1748/**
1749 * Received typing indication
1750 */
1751static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
1752 const pj_str_t *to, const pj_str_t *contact,
1753 pj_bool_t is_typing)
1754{
1755 PJ_UNUSED_ARG(call_id);
1756 PJ_UNUSED_ARG(to);
1757 PJ_UNUSED_ARG(contact);
1758
1759 PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
1760 (int)from->slen, from->ptr,
1761 (is_typing?"is typing..":"has stopped typing")));
1762}
1763
1764
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001765/**
1766 * Call transfer request status.
1767 */
1768static void on_call_transfer_status(pjsua_call_id call_id,
1769 int status_code,
1770 const pj_str_t *status_text,
1771 pj_bool_t final,
1772 pj_bool_t *p_cont)
1773{
1774 PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
1775 call_id, status_code,
1776 (int)status_text->slen, status_text->ptr,
1777 (final ? "[final]" : "")));
1778
1779 if (status_code/100 == 2) {
1780 PJ_LOG(3,(THIS_FILE,
1781 "Call %d: call transfered successfully, disconnecting call",
1782 call_id));
1783 pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
1784 *p_cont = PJ_FALSE;
1785 }
1786}
1787
1788
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001789/*
Benny Prijonof7b1c392006-11-11 16:46:34 +00001790 * Notification that call is being replaced.
1791 */
1792static void on_call_replaced(pjsua_call_id old_call_id,
1793 pjsua_call_id new_call_id)
1794{
1795 pjsua_call_info old_ci, new_ci;
1796
1797 pjsua_call_get_info(old_call_id, &old_ci);
1798 pjsua_call_get_info(new_call_id, &new_ci);
1799
1800 PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by "
1801 "call %d with %.*s",
1802 old_call_id,
1803 (int)old_ci.remote_info.slen, old_ci.remote_info.ptr,
1804 new_call_id,
1805 (int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
1806}
1807
1808
1809/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001810 * Print buddy list.
1811 */
1812static void print_buddy_list(void)
1813{
1814 pjsua_buddy_id ids[64];
1815 int i;
1816 unsigned count = PJ_ARRAY_SIZE(ids);
1817
1818 puts("Buddy list:");
1819
1820 pjsua_enum_buddies(ids, &count);
1821
1822 if (count == 0)
1823 puts(" -none-");
1824 else {
1825 for (i=0; i<(int)count; ++i) {
1826 pjsua_buddy_info info;
1827
1828 if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
1829 continue;
1830
Benny Prijono4461c7d2007-08-25 13:36:15 +00001831 printf(" [%2d] <%.*s> %.*s\n",
1832 ids[i]+1,
1833 (int)info.status_text.slen,
1834 info.status_text.ptr,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001835 (int)info.uri.slen,
1836 info.uri.ptr);
1837 }
1838 }
1839 puts("");
1840}
1841
1842
1843/*
1844 * Print account status.
1845 */
1846static void print_acc_status(int acc_id)
1847{
1848 char buf[80];
1849 pjsua_acc_info info;
1850
1851 pjsua_acc_get_info(acc_id, &info);
1852
1853 if (!info.has_registration) {
1854 pj_ansi_snprintf(buf, sizeof(buf), "%.*s",
1855 (int)info.status_text.slen,
1856 info.status_text.ptr);
1857
1858 } else {
1859 pj_ansi_snprintf(buf, sizeof(buf),
1860 "%d/%.*s (expires=%d)",
1861 info.status,
1862 (int)info.status_text.slen,
1863 info.status_text.ptr,
1864 info.expires);
1865
1866 }
1867
1868 printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
1869 acc_id, (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
Benny Prijono4461c7d2007-08-25 13:36:15 +00001870 printf(" Online status: %.*s\n",
1871 (int)info.online_status_text.slen,
1872 info.online_status_text.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001873}
1874
1875
1876/*
1877 * Show a bit of help.
1878 */
1879static void keystroke_help(void)
1880{
1881 pjsua_acc_id acc_ids[16];
1882 unsigned count = PJ_ARRAY_SIZE(acc_ids);
1883 int i;
1884
1885 printf(">>>>\n");
1886
1887 pjsua_enum_accs(acc_ids, &count);
1888
1889 printf("Account list:\n");
1890 for (i=0; i<(int)count; ++i)
1891 print_acc_status(acc_ids[i]);
1892
1893 print_buddy_list();
1894
1895 //puts("Commands:");
1896 puts("+=============================================================================+");
1897 puts("| Call Commands: | Buddy, IM & Presence: | Account: |");
1898 puts("| | | |");
1899 puts("| m Make new call | +b Add new buddy .| +a Add new accnt |");
1900 puts("| M Make multiple calls | -b Delete buddy | -a Delete accnt. |");
Benny Prijono4461c7d2007-08-25 13:36:15 +00001901 puts("| a Answer call | i Send IM | !a Modify accnt. |");
1902 puts("| h Hangup call (ha=all) | s Subscribe presence | rr (Re-)register |");
1903 puts("| H Hold call | u Unsubscribe presence | ru Unregister |");
1904 puts("| v re-inVite (release hold) | t ToGgle Online status | > Cycle next ac.|");
1905 puts("| ] Select next dialog | T Set online status | < Cycle prev ac.|");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001906 puts("| [ Select previous dialog +--------------------------+-------------------+");
1907 puts("| x Xfer call | Media Commands: | Status & Config: |");
Benny Prijonof7b1c392006-11-11 16:46:34 +00001908 puts("| X Xfer with Replaces | | |");
1909 puts("| # Send DTMF string | cl List ports | d Dump status |");
1910 puts("| dq Dump curr. call quality | cc Connect port | dd Dump detailed |");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001911 puts("| | cd Disconnect port | dc Dump config |");
Benny Prijono6dd967c2006-12-26 02:27:14 +00001912 puts("| S Send arbitrary REQUEST | V Adjust audio Volume | f Save config |");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001913 puts("+------------------------------+--------------------------+-------------------+");
Benny Prijono990042e2007-01-21 19:36:00 +00001914 puts("| q QUIT sleep N: console sleep for N ms |");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001915 puts("+=============================================================================+");
Benny Prijono48af79c2006-07-22 12:49:17 +00001916
1917 i = pjsua_call_get_count();
1918 printf("You have %d active call%s\n", i, (i>1?"s":""));
Benny Prijonof7b1c392006-11-11 16:46:34 +00001919
1920 if (current_call != PJSUA_INVALID_ID) {
1921 pjsua_call_info ci;
1922 if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
1923 printf("Current call id=%d to %.*s [%.*s]\n", current_call,
1924 (int)ci.remote_info.slen, ci.remote_info.ptr,
1925 (int)ci.state_text.slen, ci.state_text.ptr);
1926 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001927}
1928
1929
1930/*
1931 * Input simple string
1932 */
1933static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
1934{
1935 char *p;
1936
1937 printf("%s (empty to cancel): ", title); fflush(stdout);
1938 fgets(buf, len, stdin);
1939
1940 /* Remove trailing newlines. */
1941 for (p=buf; ; ++p) {
1942 if (*p=='\r' || *p=='\n') *p='\0';
1943 else if (!*p) break;
1944 }
1945
1946 if (!*buf)
1947 return PJ_FALSE;
1948
1949 return PJ_TRUE;
1950}
1951
1952
1953#define NO_NB -2
1954struct input_result
1955{
1956 int nb_result;
1957 char *uri_result;
1958};
1959
1960
1961/*
1962 * Input URL.
1963 */
1964static void ui_input_url(const char *title, char *buf, int len,
1965 struct input_result *result)
1966{
1967 result->nb_result = NO_NB;
1968 result->uri_result = NULL;
1969
1970 print_buddy_list();
1971
1972 printf("Choices:\n"
1973 " 0 For current dialog.\n"
1974 " -1 All %d buddies in buddy list\n"
1975 " [1 -%2d] Select from buddy list\n"
1976 " URL An URL\n"
1977 " <Enter> Empty input (or 'q') to cancel\n"
1978 , pjsua_get_buddy_count(), pjsua_get_buddy_count());
1979 printf("%s: ", title);
1980
1981 fflush(stdout);
1982 fgets(buf, len, stdin);
1983 len = strlen(buf);
1984
1985 /* Left trim */
1986 while (pj_isspace(*buf)) {
1987 ++buf;
1988 --len;
1989 }
1990
1991 /* Remove trailing newlines */
1992 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
1993 buf[--len] = '\0';
1994
1995 if (len == 0 || buf[0]=='q')
1996 return;
1997
1998 if (pj_isdigit(*buf) || *buf=='-') {
1999
2000 int i;
2001
2002 if (*buf=='-')
2003 i = 1;
2004 else
2005 i = 0;
2006
2007 for (; i<len; ++i) {
2008 if (!pj_isdigit(buf[i])) {
2009 puts("Invalid input");
2010 return;
2011 }
2012 }
2013
2014 result->nb_result = my_atoi(buf);
2015
2016 if (result->nb_result >= 0 &&
2017 result->nb_result <= (int)pjsua_get_buddy_count())
2018 {
2019 return;
2020 }
2021 if (result->nb_result == -1)
2022 return;
2023
2024 puts("Invalid input");
2025 result->nb_result = NO_NB;
2026 return;
2027
2028 } else {
2029 pj_status_t status;
2030
2031 if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
2032 pjsua_perror(THIS_FILE, "Invalid URL", status);
2033 return;
2034 }
2035
2036 result->uri_result = buf;
2037 }
2038}
2039
2040/*
2041 * List the ports in conference bridge
2042 */
2043static void conf_list(void)
2044{
2045 unsigned i, count;
2046 pjsua_conf_port_id id[PJSUA_MAX_CALLS];
2047
2048 printf("Conference ports:\n");
2049
2050 count = PJ_ARRAY_SIZE(id);
2051 pjsua_enum_conf_ports(id, &count);
2052
2053 for (i=0; i<count; ++i) {
2054 char txlist[PJSUA_MAX_CALLS*4+10];
2055 unsigned j;
2056 pjsua_conf_port_info info;
2057
2058 pjsua_conf_get_port_info(id[i], &info);
2059
2060 txlist[0] = '\0';
2061 for (j=0; j<info.listener_cnt; ++j) {
2062 char s[10];
2063 pj_ansi_sprintf(s, "#%d ", info.listeners[j]);
2064 pj_ansi_strcat(txlist, s);
2065 }
2066 printf("Port #%02d[%2dKHz/%dms] %20.*s transmitting to: %s\n",
2067 info.slot_id,
2068 info.clock_rate/1000,
2069 info.samples_per_frame * 1000 / info.clock_rate,
2070 (int)info.name.slen,
2071 info.name.ptr,
2072 txlist);
2073
2074 }
2075 puts("");
2076}
2077
2078
2079/*
Benny Prijono56315612006-07-18 14:39:40 +00002080 * Send arbitrary request to remote host
2081 */
2082static void send_request(char *cstr_method, const pj_str_t *dst_uri)
2083{
2084 pj_str_t str_method;
2085 pjsip_method method;
2086 pjsip_tx_data *tdata;
Benny Prijono56315612006-07-18 14:39:40 +00002087 pjsip_endpoint *endpt;
2088 pj_status_t status;
2089
2090 endpt = pjsua_get_pjsip_endpt();
2091
2092 str_method = pj_str(cstr_method);
2093 pjsip_method_init_np(&method, &str_method);
2094
Benny Prijonofff245c2007-04-02 11:44:47 +00002095 status = pjsua_acc_create_request(current_acc, &method, dst_uri, &tdata);
Benny Prijono56315612006-07-18 14:39:40 +00002096
2097 status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL);
2098 if (status != PJ_SUCCESS) {
2099 pjsua_perror(THIS_FILE, "Unable to send request", status);
Benny Prijono56315612006-07-18 14:39:40 +00002100 return;
2101 }
2102}
2103
2104
2105/*
Benny Prijono4461c7d2007-08-25 13:36:15 +00002106 * Change extended online status.
2107 */
2108static void change_online_status(void)
2109{
2110 char menuin[32];
2111 pj_bool_t online_status;
2112 pjrpid_element elem;
2113 int i, choice;
2114
2115 enum {
2116 AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX
2117 };
2118
2119 struct opt {
2120 int id;
2121 char *name;
2122 } opts[] = {
2123 { AVAILABLE, "Available" },
2124 { BUSY, "Busy"},
2125 { OTP, "On the phone"},
2126 { IDLE, "Idle"},
2127 { AWAY, "Away"},
2128 { BRB, "Be right back"},
2129 { OFFLINE, "Offline"}
2130 };
2131
2132 printf("\n"
2133 "Choices:\n");
2134 for (i=0; i<PJ_ARRAY_SIZE(opts); ++i) {
2135 printf(" %d %s\n", opts[i].id+1, opts[i].name);
2136 }
2137
2138 if (!simple_input("Select status", menuin, sizeof(menuin)))
2139 return;
2140
2141 choice = atoi(menuin) - 1;
2142 if (choice < 0 || choice >= OPT_MAX) {
2143 puts("Invalid selection");
2144 return;
2145 }
2146
2147 pj_bzero(&elem, sizeof(elem));
2148 elem.type = PJRPID_ELEMENT_TYPE_PERSON;
2149
2150 online_status = PJ_TRUE;
2151
2152 switch (choice) {
2153 case AVAILABLE:
2154 break;
2155 case BUSY:
2156 elem.activity = PJRPID_ACTIVITY_BUSY;
2157 elem.note = pj_str("Busy");
2158 break;
2159 case OTP:
2160 elem.activity = PJRPID_ACTIVITY_BUSY;
2161 elem.note = pj_str("On the phone");
2162 break;
2163 case IDLE:
2164 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
2165 elem.note = pj_str("Idle");
2166 break;
2167 case AWAY:
2168 elem.activity = PJRPID_ACTIVITY_AWAY;
2169 elem.note = pj_str("Away");
2170 break;
2171 case BRB:
2172 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
2173 elem.note = pj_str("Be right back");
2174 break;
2175 case OFFLINE:
2176 online_status = PJ_FALSE;
2177 break;
2178 }
2179
2180 pjsua_acc_set_online_status2(current_acc, online_status, &elem);
2181}
2182
2183
2184/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002185 * Main "user interface" loop.
2186 */
2187void console_app_main(const pj_str_t *uri_to_call)
2188{
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002189 char menuin[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002190 char buf[128];
2191 char text[128];
2192 int i, count;
2193 char *uri;
2194 pj_str_t tmp;
2195 struct input_result result;
2196 pjsua_call_info call_info;
2197 pjsua_acc_info acc_info;
2198
2199
2200 /* If user specifies URI to call, then call the URI */
2201 if (uri_to_call->slen) {
2202 pjsua_call_make_call( current_acc, uri_to_call, 0, NULL, NULL, NULL);
2203 }
2204
2205 keystroke_help();
2206
2207 for (;;) {
2208
2209 printf(">>> ");
2210 fflush(stdout);
2211
Benny Prijono990042e2007-01-21 19:36:00 +00002212 if (fgets(menuin, sizeof(menuin), stdin) == NULL) {
2213 /*
2214 * Be friendly to users who redirect commands into
2215 * program, when file ends, resume with kbd.
2216 * If exit is desired end script with q for quit
2217 */
2218 /* Reopen stdin/stdout/stderr to /dev/console */
2219#if defined(PJ_WIN32) && PJ_WIN32!=0
2220 if (freopen ("CONIN$", "r", stdin) == NULL) {
2221#else
2222 if (1) {
2223#endif
2224 puts("Cannot switch back to console from file redirection");
2225 menuin[0] = 'q';
2226 menuin[1] = '\0';
2227 } else {
2228 puts("Switched back to console from file redirection");
2229 continue;
2230 }
2231 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002232
2233 switch (menuin[0]) {
2234
2235 case 'm':
2236 /* Make call! : */
2237 printf("(You currently have %d calls)\n",
2238 pjsua_call_get_count());
2239
2240 uri = NULL;
2241 ui_input_url("Make call", buf, sizeof(buf), &result);
2242 if (result.nb_result != NO_NB) {
2243
2244 if (result.nb_result == -1 || result.nb_result == 0) {
2245 puts("You can't do that with make call!");
2246 continue;
2247 } else {
2248 pjsua_buddy_info binfo;
2249 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2250 uri = binfo.uri.ptr;
2251 }
2252
2253 } else if (result.uri_result) {
2254 uri = result.uri_result;
2255 }
2256
2257 tmp = pj_str(uri);
2258 pjsua_call_make_call( current_acc, &tmp, 0, NULL, NULL, NULL);
2259 break;
2260
2261 case 'M':
2262 /* Make multiple calls! : */
2263 printf("(You currently have %d calls)\n",
2264 pjsua_call_get_count());
2265
2266 if (!simple_input("Number of calls", menuin, sizeof(menuin)))
2267 continue;
2268
2269 count = my_atoi(menuin);
2270 if (count < 1)
2271 continue;
2272
2273 ui_input_url("Make call", buf, sizeof(buf), &result);
2274 if (result.nb_result != NO_NB) {
2275 pjsua_buddy_info binfo;
2276 if (result.nb_result == -1 || result.nb_result == 0) {
2277 puts("You can't do that with make call!");
2278 continue;
2279 }
2280 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2281 uri = binfo.uri.ptr;
2282 } else {
2283 uri = result.uri_result;
2284 }
2285
2286 for (i=0; i<my_atoi(menuin); ++i) {
2287 pj_status_t status;
2288
2289 tmp = pj_str(uri);
2290 status = pjsua_call_make_call(current_acc, &tmp, 0, NULL,
2291 NULL, NULL);
2292 if (status != PJ_SUCCESS)
2293 break;
2294 }
2295 break;
2296
2297 case 'i':
2298 /* Send instant messaeg */
2299
2300 /* i is for call index to send message, if any */
2301 i = -1;
2302
2303 /* Make compiler happy. */
2304 uri = NULL;
2305
2306 /* Input destination. */
2307 ui_input_url("Send IM to", buf, sizeof(buf), &result);
2308 if (result.nb_result != NO_NB) {
2309
2310 if (result.nb_result == -1) {
2311 puts("You can't send broadcast IM like that!");
2312 continue;
2313
2314 } else if (result.nb_result == 0) {
2315
2316 i = current_call;
2317
2318 } else {
2319 pjsua_buddy_info binfo;
2320 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2321 uri = binfo.uri.ptr;
2322 }
2323
2324 } else if (result.uri_result) {
2325 uri = result.uri_result;
2326 }
2327
2328
2329 /* Send typing indication. */
2330 if (i != -1)
2331 pjsua_call_send_typing_ind(i, PJ_TRUE, NULL);
2332 else {
2333 pj_str_t tmp_uri = pj_str(uri);
2334 pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL);
2335 }
2336
2337 /* Input the IM . */
2338 if (!simple_input("Message", text, sizeof(text))) {
2339 /*
2340 * Cancelled.
2341 * Send typing notification too, saying we're not typing.
2342 */
2343 if (i != -1)
2344 pjsua_call_send_typing_ind(i, PJ_FALSE, NULL);
2345 else {
2346 pj_str_t tmp_uri = pj_str(uri);
2347 pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL);
2348 }
2349 continue;
2350 }
2351
2352 tmp = pj_str(text);
2353
2354 /* Send the IM */
2355 if (i != -1)
2356 pjsua_call_send_im(i, NULL, &tmp, NULL, NULL);
2357 else {
2358 pj_str_t tmp_uri = pj_str(uri);
2359 pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL);
2360 }
2361
2362 break;
2363
2364 case 'a':
2365
2366 if (current_call != -1) {
2367 pjsua_call_get_info(current_call, &call_info);
2368 } else {
2369 /* Make compiler happy */
2370 call_info.role = PJSIP_ROLE_UAC;
2371 call_info.state = PJSIP_INV_STATE_DISCONNECTED;
2372 }
2373
2374 if (current_call == -1 ||
2375 call_info.role != PJSIP_ROLE_UAS ||
2376 call_info.state >= PJSIP_INV_STATE_CONNECTING)
2377 {
2378 puts("No pending incoming call");
2379 fflush(stdout);
2380 continue;
2381
2382 } else {
Benny Prijono20d36722007-02-22 14:52:24 +00002383 int st_code;
2384 char contact[120];
2385 pj_str_t hname = { "Contact", 7 };
2386 pj_str_t hvalue;
2387 pjsip_generic_string_hdr hcontact;
2388 pjsua_msg_data msg_data;
2389
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002390 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
2391 continue;
2392
Benny Prijono20d36722007-02-22 14:52:24 +00002393 st_code = my_atoi(buf);
2394 if (st_code < 100)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002395 continue;
2396
Benny Prijono20d36722007-02-22 14:52:24 +00002397 pjsua_msg_data_init(&msg_data);
2398
2399 if (st_code/100 == 3) {
2400 if (!simple_input("Enter URL to be put in Contact",
2401 contact, sizeof(contact)))
2402 continue;
2403 hvalue = pj_str(contact);
2404 pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue);
2405
2406 pj_list_push_back(&msg_data.hdr_list, &hcontact);
2407 }
2408
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002409 /*
2410 * Must check again!
2411 * Call may have been disconnected while we're waiting for
2412 * keyboard input.
2413 */
2414 if (current_call == -1) {
2415 puts("Call has been disconnected");
2416 fflush(stdout);
2417 continue;
2418 }
2419
Benny Prijono20d36722007-02-22 14:52:24 +00002420 pjsua_call_answer(current_call, st_code, NULL, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002421 }
2422
2423 break;
2424
2425
2426 case 'h':
2427
2428 if (current_call == -1) {
2429 puts("No current call");
2430 fflush(stdout);
2431 continue;
2432
2433 } else if (menuin[1] == 'a') {
2434
2435 /* Hangup all calls */
2436 pjsua_call_hangup_all();
2437
2438 } else {
2439
2440 /* Hangup current calls */
2441 pjsua_call_hangup(current_call, 0, NULL, NULL);
2442 }
2443 break;
2444
2445 case ']':
2446 case '[':
2447 /*
2448 * Cycle next/prev dialog.
2449 */
2450 if (menuin[0] == ']') {
2451 find_next_call();
2452
2453 } else {
2454 find_prev_call();
2455 }
2456
2457 if (current_call != -1) {
2458
2459 pjsua_call_get_info(current_call, &call_info);
2460 PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s",
2461 (int)call_info.remote_info.slen,
2462 call_info.remote_info.ptr));
2463
2464 } else {
2465 PJ_LOG(3,(THIS_FILE,"No current dialog"));
2466 }
2467 break;
2468
2469
2470 case '>':
2471 case '<':
2472 if (!simple_input("Enter account ID to select", buf, sizeof(buf)))
2473 break;
2474
2475 i = my_atoi(buf);
2476 if (pjsua_acc_is_valid(i)) {
Benny Prijono21b9ad92006-08-15 13:11:22 +00002477 pjsua_acc_set_default(i);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002478 PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i));
2479 } else {
2480 PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i));
2481 }
2482 break;
2483
2484
2485 case '+':
2486 if (menuin[1] == 'b') {
2487
2488 pjsua_buddy_config buddy_cfg;
2489 pjsua_buddy_id buddy_id;
2490 pj_status_t status;
2491
2492 if (!simple_input("Enter buddy's URI:", buf, sizeof(buf)))
2493 break;
2494
2495 if (pjsua_verify_sip_url(buf) != PJ_SUCCESS) {
2496 printf("Invalid SIP URI '%s'\n", buf);
2497 break;
2498 }
2499
Benny Prijonoac623b32006-07-03 15:19:31 +00002500 pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002501
2502 buddy_cfg.uri = pj_str(buf);
2503 buddy_cfg.subscribe = PJ_TRUE;
2504
2505 status = pjsua_buddy_add(&buddy_cfg, &buddy_id);
2506 if (status == PJ_SUCCESS) {
2507 printf("New buddy '%s' added at index %d\n",
2508 buf, buddy_id+1);
2509 }
2510
2511 } else if (menuin[1] == 'a') {
2512
Benny Prijonofb2b3652007-06-28 07:15:03 +00002513 char id[80], registrar[80], realm[80], uname[80], passwd[30];
2514 pjsua_acc_config acc_cfg;
2515 pj_status_t status;
2516
2517 if (!simple_input("Your SIP URL:", id, sizeof(id)))
2518 break;
2519 if (!simple_input("URL of the registrar:", registrar, sizeof(registrar)))
2520 break;
2521 if (!simple_input("Auth Realm:", realm, sizeof(realm)))
2522 break;
2523 if (!simple_input("Auth Username:", uname, sizeof(uname)))
2524 break;
2525 if (!simple_input("Auth Password:", passwd, sizeof(passwd)))
2526 break;
2527
2528 pjsua_acc_config_default(&acc_cfg);
2529 acc_cfg.id = pj_str(id);
2530 acc_cfg.reg_uri = pj_str(registrar);
2531 acc_cfg.cred_count = 1;
2532 acc_cfg.cred_info[0].scheme = pj_str("digest");
2533 acc_cfg.cred_info[0].realm = pj_str(realm);
2534 acc_cfg.cred_info[0].username = pj_str(uname);
2535 acc_cfg.cred_info[0].data_type = 0;
2536 acc_cfg.cred_info[0].data = pj_str(passwd);
2537
2538 status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
2539 if (status != PJ_SUCCESS) {
2540 pjsua_perror(THIS_FILE, "Error adding new account", status);
2541 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002542
2543 } else {
2544 printf("Invalid input %s\n", menuin);
2545 }
2546 break;
2547
2548 case '-':
2549 if (menuin[1] == 'b') {
2550 if (!simple_input("Enter buddy ID to delete",buf,sizeof(buf)))
2551 break;
2552
2553 i = my_atoi(buf) - 1;
2554
2555 if (!pjsua_buddy_is_valid(i)) {
2556 printf("Invalid buddy id %d\n", i);
2557 } else {
2558 pjsua_buddy_del(i);
2559 printf("Buddy %d deleted\n", i);
2560 }
2561
2562 } else if (menuin[1] == 'a') {
2563
2564 if (!simple_input("Enter account ID to delete",buf,sizeof(buf)))
2565 break;
2566
2567 i = my_atoi(buf);
2568
2569 if (!pjsua_acc_is_valid(i)) {
2570 printf("Invalid account id %d\n", i);
2571 } else {
2572 pjsua_acc_del(i);
2573 printf("Account %d deleted\n", i);
2574 }
2575
2576 } else {
2577 printf("Invalid input %s\n", menuin);
2578 }
2579 break;
2580
2581 case 'H':
2582 /*
2583 * Hold call.
2584 */
2585 if (current_call != -1) {
2586
2587 pjsua_call_set_hold(current_call, NULL);
2588
2589 } else {
2590 PJ_LOG(3,(THIS_FILE, "No current call"));
2591 }
2592 break;
2593
2594 case 'v':
2595 /*
2596 * Send re-INVITE (to release hold, etc).
2597 */
2598 if (current_call != -1) {
2599
2600 pjsua_call_reinvite(current_call, PJ_TRUE, NULL);
2601
2602 } else {
2603 PJ_LOG(3,(THIS_FILE, "No current call"));
2604 }
2605 break;
2606
2607 case 'x':
2608 /*
2609 * Transfer call.
2610 */
2611 if (current_call == -1) {
2612
2613 PJ_LOG(3,(THIS_FILE, "No current call"));
2614
2615 } else {
2616 int call = current_call;
Benny Prijonod524e822006-09-22 12:48:18 +00002617 pjsua_msg_data msg_data;
2618 pjsip_generic_string_hdr refer_sub;
2619 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
2620 pj_str_t STR_FALSE = { "false", 5 };
Benny Prijonof7b1c392006-11-11 16:46:34 +00002621 pjsua_call_info ci;
2622
2623 pjsua_call_get_info(current_call, &ci);
2624 printf("Transfering current call [%d] %.*s\n",
2625 current_call,
2626 (int)ci.remote_info.slen, ci.remote_info.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002627
2628 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
2629
2630 /* Check if call is still there. */
2631
2632 if (call != current_call) {
2633 puts("Call has been disconnected");
2634 continue;
2635 }
2636
Benny Prijonod524e822006-09-22 12:48:18 +00002637 pjsua_msg_data_init(&msg_data);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002638 if (app_config.no_refersub) {
2639 /* Add Refer-Sub: false in outgoing REFER request */
2640 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
2641 &STR_FALSE);
2642 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
2643 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002644 if (result.nb_result != NO_NB) {
2645 if (result.nb_result == -1 || result.nb_result == 0)
2646 puts("You can't do that with transfer call!");
2647 else {
2648 pjsua_buddy_info binfo;
2649 pjsua_buddy_get_info(result.nb_result-1, &binfo);
Benny Prijonod524e822006-09-22 12:48:18 +00002650 pjsua_call_xfer( current_call, &binfo.uri, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002651 }
2652
2653 } else if (result.uri_result) {
2654 pj_str_t tmp;
2655 tmp = pj_str(result.uri_result);
Benny Prijonod524e822006-09-22 12:48:18 +00002656 pjsua_call_xfer( current_call, &tmp, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002657 }
2658 }
2659 break;
2660
Benny Prijonof7b1c392006-11-11 16:46:34 +00002661 case 'X':
2662 /*
2663 * Transfer call with replaces.
2664 */
2665 if (current_call == -1) {
2666
2667 PJ_LOG(3,(THIS_FILE, "No current call"));
2668
2669 } else {
2670 int call = current_call;
2671 int dst_call;
2672 pjsua_msg_data msg_data;
2673 pjsip_generic_string_hdr refer_sub;
2674 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
2675 pj_str_t STR_FALSE = { "false", 5 };
2676 pjsua_call_id ids[PJSUA_MAX_CALLS];
2677 pjsua_call_info ci;
2678 unsigned i, count;
2679
2680 count = PJ_ARRAY_SIZE(ids);
2681 pjsua_enum_calls(ids, &count);
2682
2683 if (count <= 1) {
2684 puts("There are no other calls");
2685 continue;
2686 }
2687
2688 pjsua_call_get_info(current_call, &ci);
2689 printf("Transfer call [%d] %.*s to one of the following:\n",
2690 current_call,
2691 (int)ci.remote_info.slen, ci.remote_info.ptr);
2692
2693 for (i=0; i<count; ++i) {
2694 pjsua_call_info call_info;
2695
2696 if (ids[i] == call)
2697 continue;
2698
2699 pjsua_call_get_info(ids[i], &call_info);
2700 printf("%d %.*s [%.*s]\n",
2701 ids[i],
2702 (int)call_info.remote_info.slen,
2703 call_info.remote_info.ptr,
2704 (int)call_info.state_text.slen,
2705 call_info.state_text.ptr);
2706 }
2707
2708 if (!simple_input("Enter call number to be replaced",
2709 buf, sizeof(buf)))
2710 continue;
2711
2712 dst_call = my_atoi(buf);
2713
2714 /* Check if call is still there. */
2715
2716 if (call != current_call) {
2717 puts("Call has been disconnected");
2718 continue;
2719 }
2720
2721 /* Check that destination call is valid. */
2722 if (dst_call == call) {
2723 puts("Destination call number must not be the same "
2724 "as the call being transfered");
2725 continue;
2726 }
2727 if (dst_call >= PJSUA_MAX_CALLS) {
2728 puts("Invalid destination call number");
2729 continue;
2730 }
2731 if (!pjsua_call_is_active(dst_call)) {
2732 puts("Invalid destination call number");
2733 continue;
2734 }
2735
2736 pjsua_msg_data_init(&msg_data);
2737 if (app_config.no_refersub) {
2738 /* Add Refer-Sub: false in outgoing REFER request */
2739 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
2740 &STR_FALSE);
2741 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
2742 }
2743
2744 pjsua_call_xfer_replaces(call, dst_call, 0, &msg_data);
2745 }
2746 break;
2747
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002748 case '#':
2749 /*
2750 * Send DTMF strings.
2751 */
2752 if (current_call == -1) {
2753
2754 PJ_LOG(3,(THIS_FILE, "No current call"));
2755
2756 } else if (!pjsua_call_has_media(current_call)) {
2757
2758 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
2759
2760 } else {
2761 pj_str_t digits;
2762 int call = current_call;
2763 pj_status_t status;
2764
2765 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
2766 sizeof(buf)))
2767 {
2768 break;
2769 }
2770
2771 if (call != current_call) {
2772 puts("Call has been disconnected");
2773 continue;
2774 }
2775
2776 digits = pj_str(buf);
2777 status = pjsua_call_dial_dtmf(current_call, &digits);
2778 if (status != PJ_SUCCESS) {
2779 pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
2780 } else {
2781 puts("DTMF digits enqueued for transmission");
2782 }
2783 }
2784 break;
2785
Benny Prijono56315612006-07-18 14:39:40 +00002786 case 'S':
2787 /*
2788 * Send arbitrary request
2789 */
2790 if (pjsua_acc_get_count() == 0) {
2791 puts("Sorry, need at least one account configured");
2792 break;
2793 }
2794
2795 puts("Send arbitrary request to remote host");
2796
2797 /* Input METHOD */
2798 if (!simple_input("Request method:",text,sizeof(text)))
2799 break;
2800
2801 /* Input destination URI */
2802 uri = NULL;
2803 ui_input_url("Destination URI", buf, sizeof(buf), &result);
2804 if (result.nb_result != NO_NB) {
2805
2806 if (result.nb_result == -1 || result.nb_result == 0) {
2807 puts("Sorry you can't do that!");
2808 continue;
2809 } else {
2810 pjsua_buddy_info binfo;
2811 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2812 uri = binfo.uri.ptr;
2813 }
2814
2815 } else if (result.uri_result) {
2816 uri = result.uri_result;
2817 }
2818
2819 tmp = pj_str(uri);
2820
2821 send_request(text, &tmp);
2822 break;
2823
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002824 case 's':
Benny Prijono990042e2007-01-21 19:36:00 +00002825 if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
2826 pj_str_t tmp;
2827 int delay;
2828
2829 tmp.ptr = menuin+6;
2830 tmp.slen = pj_ansi_strlen(menuin)-7;
2831
2832 if (tmp.slen < 1) {
2833 puts("Usage: sleep MSEC");
2834 break;
2835 }
2836
2837 delay = pj_strtoul(&tmp);
2838 if (delay < 0) delay = 0;
2839 pj_thread_sleep(delay);
2840 break;
2841 }
2842 /* Continue below */
2843
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002844 case 'u':
2845 /*
2846 * Subscribe/unsubscribe presence.
2847 */
2848 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
2849 if (result.nb_result != NO_NB) {
2850 if (result.nb_result == -1) {
2851 int i, count;
2852 count = pjsua_get_buddy_count();
2853 for (i=0; i<count; ++i)
2854 pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
2855 } else if (result.nb_result == 0) {
2856 puts("Sorry, can only subscribe to buddy's presence, "
2857 "not from existing call");
2858 } else {
2859 pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
2860 }
2861
2862 } else if (result.uri_result) {
2863 puts("Sorry, can only subscribe to buddy's presence, "
2864 "not arbitrary URL (for now)");
2865 }
2866
2867 break;
2868
2869 case 'r':
2870 switch (menuin[1]) {
2871 case 'r':
2872 /*
2873 * Re-Register.
2874 */
2875 pjsua_acc_set_registration(current_acc, PJ_TRUE);
2876 break;
2877 case 'u':
2878 /*
2879 * Unregister
2880 */
2881 pjsua_acc_set_registration(current_acc, PJ_FALSE);
2882 break;
2883 }
2884 break;
2885
2886 case 't':
2887 pjsua_acc_get_info(current_acc, &acc_info);
2888 acc_info.online_status = !acc_info.online_status;
2889 pjsua_acc_set_online_status(current_acc, acc_info.online_status);
2890 printf("Setting %s online status to %s\n",
2891 acc_info.acc_uri.ptr,
2892 (acc_info.online_status?"online":"offline"));
2893 break;
2894
Benny Prijono4461c7d2007-08-25 13:36:15 +00002895 case 'T':
2896 change_online_status();
2897 break;
2898
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002899 case 'c':
2900 switch (menuin[1]) {
2901 case 'l':
2902 conf_list();
2903 break;
2904 case 'c':
2905 case 'd':
2906 {
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002907 char tmp[10], src_port[10], dst_port[10];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002908 pj_status_t status;
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002909 int cnt;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002910 const char *src_title, *dst_title;
2911
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002912 cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002913
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002914 if (cnt != 3) {
2915 conf_list();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002916
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002917 src_title = (menuin[1]=='c'?
2918 "Connect src port #":
2919 "Disconnect src port #");
2920 dst_title = (menuin[1]=='c'?
2921 "To dst port #":
2922 "From dst port #");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002923
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002924 if (!simple_input(src_title, src_port, sizeof(src_port)))
2925 break;
2926
2927 if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
2928 break;
2929 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002930
2931 if (menuin[1]=='c') {
2932 status = pjsua_conf_connect(my_atoi(src_port),
2933 my_atoi(dst_port));
2934 } else {
2935 status = pjsua_conf_disconnect(my_atoi(src_port),
2936 my_atoi(dst_port));
2937 }
2938 if (status == PJ_SUCCESS) {
2939 puts("Success");
2940 } else {
2941 puts("ERROR!!");
2942 }
2943 }
2944 break;
2945 }
2946 break;
2947
Benny Prijono6dd967c2006-12-26 02:27:14 +00002948 case 'V':
2949 /* Adjust audio volume */
2950 sprintf(buf, "Adjust mic level: [%4.1fx] ", app_config.mic_level);
2951 if (simple_input(buf,text,sizeof(text))) {
2952 char *err;
2953 app_config.mic_level = (float)strtod(text, &err);
2954 pjsua_conf_adjust_rx_level(0, app_config.mic_level);
2955 }
2956 sprintf(buf, "Adjust speaker level: [%4.1fx] ",
2957 app_config.speaker_level);
2958 if (simple_input(buf,text,sizeof(text))) {
2959 char *err;
2960 app_config.speaker_level = (float)strtod(text, &err);
2961 pjsua_conf_adjust_tx_level(0, app_config.speaker_level);
2962 }
2963
2964 break;
2965
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002966 case 'd':
2967 if (menuin[1] == 'c') {
2968 char settings[2000];
2969 int len;
2970
2971 len = write_settings(&app_config, settings, sizeof(settings));
2972 if (len < 1)
2973 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
2974 else
2975 PJ_LOG(3,(THIS_FILE,
2976 "Dumping configuration (%d bytes):\n%s\n",
2977 len, settings));
Benny Prijono819506c2006-06-22 22:29:51 +00002978
2979 } else if (menuin[1] == 'q') {
2980
2981 if (current_call != PJSUA_INVALID_ID) {
2982 char buf[1024];
2983 pjsua_call_dump(current_call, PJ_TRUE, buf,
2984 sizeof(buf), " ");
2985 PJ_LOG(3,(THIS_FILE, "\n%s", buf));
2986 } else {
2987 PJ_LOG(3,(THIS_FILE, "No current call"));
2988 }
2989
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002990 } else {
2991 app_dump(menuin[1]=='d');
2992 }
2993 break;
2994
2995
2996 case 'f':
2997 if (simple_input("Enter output filename", buf, sizeof(buf))) {
2998 char settings[2000];
2999 int len;
3000
3001 len = write_settings(&app_config, settings, sizeof(settings));
3002 if (len < 1)
3003 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
3004 else {
3005 pj_oshandle_t fd;
3006 pj_status_t status;
3007
3008 status = pj_file_open(app_config.pool, buf,
3009 PJ_O_WRONLY, &fd);
3010 if (status != PJ_SUCCESS) {
3011 pjsua_perror(THIS_FILE, "Unable to open file", status);
3012 } else {
3013 pj_ssize_t size = len;
3014 pj_file_write(fd, settings, &size);
3015 pj_file_close(fd);
3016
3017 printf("Settings successfully written to '%s'\n", buf);
3018 }
3019 }
3020
3021 }
3022 break;
3023
3024
3025 case 'q':
3026 goto on_exit;
3027
3028
3029 default:
3030 if (menuin[0] != '\n' && menuin[0] != '\r') {
3031 printf("Invalid input %s", menuin);
3032 }
3033 keystroke_help();
3034 break;
3035 }
3036 }
3037
3038on_exit:
3039 ;
3040}
3041
3042
3043/*****************************************************************************
3044 * Public API
3045 */
3046
3047pj_status_t app_init(int argc, char *argv[])
3048{
Benny Prijonoe93e2872006-06-28 16:46:49 +00003049 pjsua_transport_id transport_id = -1;
Benny Prijonoe347cb02007-02-14 14:36:13 +00003050 pjsua_transport_config tcp_cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003051 unsigned i;
3052 pj_status_t status;
3053
3054 /* Create pjsua */
3055 status = pjsua_create();
3056 if (status != PJ_SUCCESS)
3057 return status;
3058
3059 /* Create pool for application */
Benny Prijonod5696da2007-07-17 16:25:45 +00003060 app_config.pool = pjsua_pool_create("pjsua", 1000, 1000);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003061
3062 /* Initialize default config */
3063 default_config(&app_config);
3064
3065 /* Parse the arguments */
3066 status = parse_args(argc, argv, &app_config, &uri_arg);
3067 if (status != PJ_SUCCESS)
3068 return status;
3069
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003070 /* Initialize application callbacks */
3071 app_config.cfg.cb.on_call_state = &on_call_state;
3072 app_config.cfg.cb.on_call_media_state = &on_call_media_state;
3073 app_config.cfg.cb.on_incoming_call = &on_incoming_call;
Benny Prijono0875ae82006-12-26 00:11:48 +00003074 app_config.cfg.cb.on_dtmf_digit = &call_on_dtmf_callback;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003075 app_config.cfg.cb.on_reg_state = &on_reg_state;
3076 app_config.cfg.cb.on_buddy_state = &on_buddy_state;
3077 app_config.cfg.cb.on_pager = &on_pager;
3078 app_config.cfg.cb.on_typing = &on_typing;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003079 app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
Benny Prijonof7b1c392006-11-11 16:46:34 +00003080 app_config.cfg.cb.on_call_replaced = &on_call_replaced;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003081
3082 /* Initialize pjsua */
3083 status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
3084 &app_config.media_cfg);
3085 if (status != PJ_SUCCESS)
3086 return status;
3087
Benny Prijonoe909eac2006-07-27 22:04:56 +00003088#ifdef STEREO_DEMO
3089 stereo_demo();
3090#endif
3091
Benny Prijono804ff0a2006-09-14 11:17:48 +00003092 /* Initialize calls data */
3093 for (i=0; i<PJ_ARRAY_SIZE(app_config.call_data); ++i) {
3094 app_config.call_data[i].timer.id = PJSUA_INVALID_ID;
3095 app_config.call_data[i].timer.cb = &call_timeout_callback;
3096 }
3097
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003098 /* Optionally registers WAV file */
Benny Prijono32e4f492007-01-24 00:44:26 +00003099 for (i=0; i<app_config.wav_count; ++i) {
3100 pjsua_player_id wav_id;
3101
3102 status = pjsua_player_create(&app_config.wav_files[i], 0,
3103 &wav_id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003104 if (status != PJ_SUCCESS)
3105 goto on_error;
Benny Prijono22a300a2006-06-14 20:04:55 +00003106
Benny Prijono72c04d22007-02-17 22:20:58 +00003107 if (app_config.wav_id == PJSUA_INVALID_ID) {
Benny Prijono32e4f492007-01-24 00:44:26 +00003108 app_config.wav_id = wav_id;
3109 app_config.wav_port = pjsua_player_get_conf_port(app_config.wav_id);
3110 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003111 }
3112
Benny Prijono4af234b2007-01-24 02:02:09 +00003113 /* Optionally registers tone players */
3114 for (i=0; i<app_config.tone_count; ++i) {
3115 pjmedia_port *tport;
3116 char name[80];
3117 pj_str_t label;
3118 pj_status_t status;
3119
3120 pj_ansi_snprintf(name, sizeof(name), "tone-%d,%d",
3121 app_config.tones[i].freq1,
3122 app_config.tones[i].freq2);
3123 label = pj_str(name);
3124 status = pjmedia_tonegen_create2(app_config.pool, &label,
3125 8000, 1, 160, 16,
3126 PJMEDIA_TONEGEN_LOOP, &tport);
3127 if (status != PJ_SUCCESS) {
3128 pjsua_perror(THIS_FILE, "Unable to create tone generator", status);
3129 goto on_error;
3130 }
3131
3132 status = pjsua_conf_add_port(app_config.pool, tport,
3133 &app_config.tone_slots[i]);
3134 pj_assert(status == PJ_SUCCESS);
3135
3136 status = pjmedia_tonegen_play(tport, 1, &app_config.tones[i], 0);
3137 pj_assert(status == PJ_SUCCESS);
3138 }
3139
Benny Prijono1ebd6142006-10-19 15:48:02 +00003140 /* Optionally create recorder file, if any. */
3141 if (app_config.rec_file.slen) {
3142 status = pjsua_recorder_create(&app_config.rec_file, 0, NULL, 0, 0,
3143 &app_config.rec_id);
3144 if (status != PJ_SUCCESS)
3145 goto on_error;
3146
3147 app_config.rec_port = pjsua_recorder_get_conf_port(app_config.rec_id);
3148 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003149
Benny Prijonoe347cb02007-02-14 14:36:13 +00003150 pj_memcpy(&tcp_cfg, &app_config.udp_cfg, sizeof(tcp_cfg));
3151
Benny Prijono87ef89a2007-01-14 00:39:45 +00003152 /* Add UDP transport unless it's disabled. */
3153 if (!app_config.no_udp) {
3154 pjsua_acc_id aid;
3155
3156 status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
3157 &app_config.udp_cfg,
3158 &transport_id);
3159 if (status != PJ_SUCCESS)
3160 goto on_error;
3161
3162 /* Add local account */
3163 pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
3164 //pjsua_acc_set_transport(aid, transport_id);
3165 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
Benny Prijonoe347cb02007-02-14 14:36:13 +00003166
3167 if (app_config.udp_cfg.port == 0) {
3168 pjsua_transport_info ti;
3169 pj_sockaddr_in *a;
3170
3171 pjsua_transport_get_info(transport_id, &ti);
3172 a = (pj_sockaddr_in*)&ti.local_addr;
3173
3174 tcp_cfg.port = pj_ntohs(a->sin_port);
3175 }
Benny Prijono87ef89a2007-01-14 00:39:45 +00003176 }
3177
Benny Prijonoe93e2872006-06-28 16:46:49 +00003178 /* Add TCP transport unless it's disabled */
3179 if (!app_config.no_tcp) {
3180 status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
Benny Prijonoe347cb02007-02-14 14:36:13 +00003181 &tcp_cfg,
Benny Prijonoe93e2872006-06-28 16:46:49 +00003182 &transport_id);
3183 if (status != PJ_SUCCESS)
3184 goto on_error;
3185
3186 /* Add local account */
Benny Prijono21b9ad92006-08-15 13:11:22 +00003187 pjsua_acc_add_local(transport_id, PJ_TRUE, NULL);
Benny Prijonoe93e2872006-06-28 16:46:49 +00003188 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
3189
3190 }
3191
Benny Prijonoe93e2872006-06-28 16:46:49 +00003192
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003193#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
3194 /* Add TLS transport when application wants one */
3195 if (app_config.use_tls) {
Benny Prijonof3bbc132006-12-25 06:43:59 +00003196
3197 pjsua_acc_id acc_id;
3198
3199 /* Set TLS port as TCP port+1 */
Benny Prijonoafc47be2007-02-14 14:44:55 +00003200 tcp_cfg.port++;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003201 status = pjsua_transport_create(PJSIP_TRANSPORT_TLS,
Benny Prijonoafc47be2007-02-14 14:44:55 +00003202 &tcp_cfg,
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003203 &transport_id);
Benny Prijonoafc47be2007-02-14 14:44:55 +00003204 tcp_cfg.port--;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003205 if (status != PJ_SUCCESS)
3206 goto on_error;
Benny Prijonof3bbc132006-12-25 06:43:59 +00003207
3208 /* Add local account */
3209 pjsua_acc_add_local(transport_id, PJ_FALSE, &acc_id);
3210 pjsua_acc_set_online_status(acc_id, PJ_TRUE);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003211 }
3212#endif
3213
Benny Prijonoe93e2872006-06-28 16:46:49 +00003214 if (transport_id == -1) {
3215 PJ_LOG(3,(THIS_FILE, "Error: no transport is configured"));
3216 status = -1;
3217 goto on_error;
3218 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003219
3220
3221 /* Add accounts */
3222 for (i=0; i<app_config.acc_cnt; ++i) {
Benny Prijono21b9ad92006-08-15 13:11:22 +00003223 status = pjsua_acc_add(&app_config.acc_cfg[i], PJ_TRUE, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003224 if (status != PJ_SUCCESS)
3225 goto on_error;
3226 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
3227 }
3228
3229 /* Add buddies */
3230 for (i=0; i<app_config.buddy_cnt; ++i) {
3231 status = pjsua_buddy_add(&app_config.buddy_cfg[i], NULL);
3232 if (status != PJ_SUCCESS)
3233 goto on_error;
3234 }
3235
3236 /* Optionally set codec orders */
3237 for (i=0; i<app_config.codec_cnt; ++i) {
3238 pjsua_codec_set_priority(&app_config.codec_arg[i],
3239 (pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9));
3240 }
3241
3242 /* Add RTP transports */
3243 status = pjsua_media_transports_create(&app_config.rtp_cfg);
3244 if (status != PJ_SUCCESS)
3245 goto on_error;
3246
Benny Prijono22a300a2006-06-14 20:04:55 +00003247 /* Use null sound device? */
Benny Prijonoe909eac2006-07-27 22:04:56 +00003248#ifndef STEREO_DEMO
Benny Prijono22a300a2006-06-14 20:04:55 +00003249 if (app_config.null_audio) {
3250 status = pjsua_set_null_snd_dev();
3251 if (status != PJ_SUCCESS)
3252 return status;
3253 }
Benny Prijonoe909eac2006-07-27 22:04:56 +00003254#endif
Benny Prijono22a300a2006-06-14 20:04:55 +00003255
Benny Prijono4e5d5512007-03-06 18:11:30 +00003256 if (app_config.capture_dev != PJSUA_INVALID_ID
3257 || app_config.playback_dev != PJSUA_INVALID_ID) {
3258 status
3259 = pjsua_set_snd_dev(app_config.capture_dev, app_config.playback_dev);
3260 if (status != PJ_SUCCESS)
3261 goto on_error;
3262 }
3263
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003264 return PJ_SUCCESS;
3265
3266on_error:
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00003267 app_destroy();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003268 return status;
3269}
3270
3271
3272pj_status_t app_main(void)
3273{
3274 pj_status_t status;
3275
3276 /* Start pjsua */
3277 status = pjsua_start();
3278 if (status != PJ_SUCCESS) {
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00003279 app_destroy();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003280 return status;
3281 }
3282
3283 console_app_main(&uri_arg);
3284
3285 return PJ_SUCCESS;
3286}
3287
3288pj_status_t app_destroy(void)
3289{
Benny Prijonof762ee72006-12-01 11:14:37 +00003290 pj_status_t status;
Benny Prijono4af234b2007-01-24 02:02:09 +00003291 unsigned i;
Benny Prijonof762ee72006-12-01 11:14:37 +00003292
Benny Prijonoe909eac2006-07-27 22:04:56 +00003293#ifdef STEREO_DEMO
3294 if (app_config.snd) {
3295 pjmedia_snd_port_destroy(app_config.snd);
3296 app_config.snd = NULL;
3297 }
3298#endif
3299
Benny Prijono4af234b2007-01-24 02:02:09 +00003300 /* Close tone generators */
3301 for (i=0; i<app_config.tone_count; ++i) {
3302 pjsua_conf_remove_port(app_config.tone_slots[i]);
3303 }
3304
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003305 if (app_config.pool) {
3306 pj_pool_release(app_config.pool);
3307 app_config.pool = NULL;
3308 }
3309
Benny Prijonof762ee72006-12-01 11:14:37 +00003310 status = pjsua_destroy();
3311
3312 pj_bzero(&app_config, sizeof(app_config));
3313
3314 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003315}
Benny Prijonoe909eac2006-07-27 22:04:56 +00003316
3317
3318#ifdef STEREO_DEMO
3319static void stereo_demo()
3320{
3321 pjmedia_port *conf, *splitter, *ch1;
Benny Prijonoe909eac2006-07-27 22:04:56 +00003322 pj_status_t status;
3323
3324 /* Disable existing sound device */
3325 conf = pjsua_set_no_snd_dev();
3326
Benny Prijonoe909eac2006-07-27 22:04:56 +00003327 /* Create stereo-mono splitter/combiner */
3328 status = pjmedia_splitcomb_create(app_config.pool,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003329 conf->info.clock_rate /* clock rate */,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003330 2 /* stereo */,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003331 2 * conf->info.samples_per_frame,
3332 conf->info.bits_per_sample,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003333 0 /* options */,
3334 &splitter);
3335 pj_assert(status == PJ_SUCCESS);
3336
3337 /* Connect channel0 (left channel?) to conference port slot0 */
3338 status = pjmedia_splitcomb_set_channel(splitter, 0 /* ch0 */,
3339 0 /*options*/,
3340 conf);
3341 pj_assert(status == PJ_SUCCESS);
3342
3343 /* Create reverse channel for channel1 (right channel?)... */
3344 status = pjmedia_splitcomb_create_rev_channel(app_config.pool,
3345 splitter,
3346 1 /* ch1 */,
3347 0 /* options */,
3348 &ch1);
3349 pj_assert(status == PJ_SUCCESS);
3350
3351 /* .. and register it to conference bridge (it would be slot1
3352 * if there's no other devices connected to the bridge)
3353 */
3354 status = pjsua_conf_add_port(app_config.pool, ch1, NULL);
3355 pj_assert(status == PJ_SUCCESS);
3356
3357 /* Create sound device */
3358 status = pjmedia_snd_port_create(app_config.pool, -1, -1,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003359 conf->info.clock_rate,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003360 2 /* stereo */,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003361 2 * conf->info.samples_per_frame,
3362 conf->info.bits_per_sample,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003363 0, &app_config.snd);
3364 pj_assert(status == PJ_SUCCESS);
3365
3366
3367 /* Connect the splitter to the sound device */
3368 status = pjmedia_snd_port_connect(app_config.snd, splitter);
3369 pj_assert(status == PJ_SUCCESS);
3370
3371}
3372#endif
3373