blob: 880f98d86da25010d366b27375cbc0785da97bc6 [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
Benny Prijono4ab9fbb2007-10-12 12:14:27 +000022#define THIS_FILE "pjsua_app.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 Prijonofce28542007-12-09 15:41:10 +000060 unsigned codec_dis_cnt;
61 pj_str_t codec_dis[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +000062 pj_bool_t null_audio;
Benny Prijono32e4f492007-01-24 00:44:26 +000063 unsigned wav_count;
64 pj_str_t wav_files[32];
Benny Prijono4af234b2007-01-24 02:02:09 +000065 unsigned tone_count;
66 pjmedia_tone_desc tones[32];
67 pjsua_conf_port_id tone_slots[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +000068 pjsua_player_id wav_id;
69 pjsua_conf_port_id wav_port;
70 pj_bool_t auto_play;
71 pj_bool_t auto_loop;
Benny Prijono7ca96da2006-08-07 12:11:40 +000072 pj_bool_t auto_conf;
Benny Prijono1ebd6142006-10-19 15:48:02 +000073 pj_str_t rec_file;
74 pj_bool_t auto_rec;
75 pjsua_recorder_id rec_id;
76 pjsua_conf_port_id rec_port;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000077 unsigned auto_answer;
78 unsigned duration;
Benny Prijonoe909eac2006-07-27 22:04:56 +000079
80#ifdef STEREO_DEMO
81 pjmedia_snd_port *snd;
82#endif
83
Benny Prijono6dd967c2006-12-26 02:27:14 +000084 float mic_level,
85 speaker_level;
86
Benny Prijono4e5d5512007-03-06 18:11:30 +000087 int capture_dev, playback_dev;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000088} app_config;
89
90
Benny Prijono21b9ad92006-08-15 13:11:22 +000091//static pjsua_acc_id current_acc;
92#define current_acc pjsua_acc_get_default()
Benny Prijonof7b1c392006-11-11 16:46:34 +000093static pjsua_call_id current_call = PJSUA_INVALID_ID;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000094static pj_str_t uri_arg;
95
Benny Prijono594e4c52006-09-14 18:51:01 +000096#ifdef STEREO_DEMO
Benny Prijonoe909eac2006-07-27 22:04:56 +000097static void stereo_demo();
Benny Prijono594e4c52006-09-14 18:51:01 +000098#endif
Benny Prijonoad2e0ca2007-04-29 12:31:51 +000099pj_status_t app_destroy(void);
Benny Prijonoe909eac2006-07-27 22:04:56 +0000100
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000101/*****************************************************************************
102 * Configuration manipulation
103 */
104
105/* Show usage */
106static void usage(void)
107{
108 puts ("Usage:");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000109 puts (" pjsua [options] [SIP URL to call]");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000110 puts ("");
111 puts ("General options:");
Benny Prijono804ff0a2006-09-14 11:17:48 +0000112 puts (" --config-file=file Read the config/arguments from file.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000113 puts (" --help Display this help screen");
114 puts (" --version Display version info");
115 puts ("");
116 puts ("Logging options:");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000117 puts (" --log-file=fname Log to filename (default stderr)");
118 puts (" --log-level=N Set log max level to N (0(none) to 6(trace)) (default=5)");
119 puts (" --app-log-level=N Set log max level for stdout display (default=4)");
120 puts ("");
121 puts ("SIP Account options:");
Benny Prijono48ab2b72007-11-08 09:24:30 +0000122 puts (" --use-ims Enable 3GPP/IMS related settings on this account");
Benny Prijonod8179652008-01-23 20:39:07 +0000123#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
Benny Prijonof6508982008-01-25 09:02:33 +0000124 puts (" --use-srtp=N Use SRTP? 0:disabled, 1:optional, 2:mandatory (def:0)");
125 puts (" --srtp-secure=N SRTP require secure SIP? 0:no, 1:tls, 1:sips (def:1)");
Benny Prijonod8179652008-01-23 20:39:07 +0000126#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000127 puts (" --registrar=url Set the URL of registrar server");
128 puts (" --id=url Set the URL of local ID (used in From header)");
129 puts (" --contact=url Optionally override the Contact information");
130 puts (" --proxy=url Optional URL of proxy server to visit");
131 puts (" May be specified multiple times");
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000132 puts (" --reg-timeout=SEC Optional registration interval (default 55)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000133 puts (" --realm=string Set realm");
134 puts (" --username=string Set authentication username");
135 puts (" --password=string Set authentication password");
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000136 puts (" --publish Send presence PUBLISH for this account");
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000137 puts (" --use-100rel Require reliable provisional response (100rel)");
Benny Prijonofce28542007-12-09 15:41:10 +0000138 puts (" --auto-update-nat=N Where N is 0 or 1 to enable/disable SIP traversal behind");
139 puts (" symmetric NAT (default 1)");
Benny Prijonob67eaac2007-02-18 20:56:00 +0000140 puts (" --next-cred Add another credentials");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000141 puts ("");
142 puts ("SIP Account Control:");
143 puts (" --next-account Add more account");
144 puts ("");
145 puts ("Transport Options:");
Benny Prijonoe93e2872006-06-28 16:46:49 +0000146 puts (" --local-port=port Set TCP/UDP port. This implicitly enables both ");
147 puts (" TCP and UDP transports on the specified port, unless");
148 puts (" if TCP or UDP is disabled.");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000149 puts (" --ip-addr=IP Use the specifed address as SIP and RTP addresses.");
150 puts (" (Hint: the IP may be the public IP of the NAT/router)");
Benny Prijonoe93e2872006-06-28 16:46:49 +0000151 puts (" --no-tcp Disable TCP transport.");
152 puts (" --no-udp Disable UDP transport.");
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000153 puts (" --nameserver=NS Add the specified nameserver to enable SRV resolution");
154 puts (" This option can be specified multiple times.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000155 puts (" --outbound=url Set the URL of global outbound proxy server");
156 puts (" May be specified multiple times");
Benny Prijonoc97608e2007-03-23 16:34:20 +0000157 puts (" --stun-srv=name Set STUN server host or domain");
Benny Prijonof3bbc132006-12-25 06:43:59 +0000158 puts ("");
159 puts ("TLS Options:");
Benny Prijonob67eaac2007-02-18 20:56:00 +0000160 puts (" --use-tls Enable TLS transport (default=no)");
Benny Prijonof3bbc132006-12-25 06:43:59 +0000161 puts (" --tls-ca-file Specify TLS CA file (default=none)");
162 puts (" --tls-cert-file Specify TLS certificate file (default=none)");
163 puts (" --tls-privkey-file Specify TLS private key file (default=none)");
164 puts (" --tls-password Specify TLS password to private key file (default=none)");
165 puts (" --tls-verify-server Verify server's certificate (default=no)");
166 puts (" --tls-verify-client Verify client's certificate (default=no)");
167 puts (" --tls-neg-timeout Specify TLS negotiation timeout (default=no)");
168
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000169 puts ("");
170 puts ("Media Options:");
Benny Prijonoc97608e2007-03-23 16:34:20 +0000171 puts (" --use-ice Enable ICE (default:no)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000172 puts (" --add-codec=name Manually add codec (default is to enable all)");
Benny Prijonofce28542007-12-09 15:41:10 +0000173 puts (" --dis-codec=name Disable codec (can be specified multiple times)");
Benny Prijonof3758ee2008-02-26 15:32:16 +0000174 puts (" --clock-rate=N Override conference bridge clock rate");
175 puts (" --snd-clock-rate=N Override sound device clock rate");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000176 puts (" --null-audio Use NULL audio device");
Benny Prijono4af234b2007-01-24 02:02:09 +0000177 puts (" --play-file=file Register WAV file in conference bridge.");
178 puts (" This can be specified multiple times.");
Benny Prijonob67eaac2007-02-18 20:56:00 +0000179 puts (" --play-tone=FORMAT Register tone to the conference bridge.");
180 puts (" FORMAT is 'F1,F2,ON,OFF', where F1,F2 are");
181 puts (" frequencies, and ON,OFF=on/off duration in msec.");
Benny Prijono4af234b2007-01-24 02:02:09 +0000182 puts (" This can be specified multiple times.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000183 puts (" --auto-play Automatically play the file (to incoming calls only)");
184 puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
Benny Prijono7ca96da2006-08-07 12:11:40 +0000185 puts (" --auto-conf Automatically put calls in conference with others");
Benny Prijono1ebd6142006-10-19 15:48:02 +0000186 puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");
187 puts (" --auto-rec Automatically record conversation");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000188 puts (" --rtp-port=N Base port to try for RTP (default=4000)");
Benny Prijono00cae612006-07-31 15:19:36 +0000189 puts (" --quality=N Specify media quality (0-10, default=6)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190 puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)");
Benny Prijono0a12f002006-07-26 17:05:39 +0000191 puts (" --no-vad Disable VAD/silence detector (default=vad enabled)");
Benny Prijonod79f25c2006-08-02 19:41:37 +0000192 puts (" --ec-tail=MSEC Set echo canceller tail length (default=256)");
Benny Prijono00cae612006-07-31 15:19:36 +0000193 puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is 20)");
194 puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
195 puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
Benny Prijono4e5d5512007-03-06 18:11:30 +0000196 puts (" --capture-dev=id Audio capture device ID (default=-1)");
197 puts (" --playback-dev=id Audio playback device ID (default=-1)");
Benny Prijono00cae612006-07-31 15:19:36 +0000198
Benny Prijono0a12f002006-07-26 17:05:39 +0000199
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200 puts ("");
201 puts ("Buddy List (can be more than one):");
202 puts (" --add-buddy url Add the specified URL to the buddy list.");
203 puts ("");
204 puts ("User Agent options:");
205 puts (" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)");
206 puts (" --max-calls=N Maximum number of concurrent calls (default:4, max:255)");
Benny Prijonof521eb02006-08-06 23:07:25 +0000207 puts (" --thread-cnt=N Number of worker threads (default:1)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208 puts (" --duration=SEC Set maximum call duration (default:no limit)");
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000209 puts (" --norefersub Suppress event subscription when transfering calls");
Benny Prijonofce28542007-12-09 15:41:10 +0000210 puts (" --use-compact-form Minimize SIP message size");
Benny Prijono804ff0a2006-09-14 11:17:48 +0000211
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000212 puts ("");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000213 puts ("When URL is specified, pjsua will immediately initiate call to that URL");
214 puts ("");
215
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000216 fflush(stdout);
217}
218
219
220/* Set default config. */
221static void default_config(struct app_config *cfg)
222{
Benny Prijono56315612006-07-18 14:39:40 +0000223 char tmp[80];
Benny Prijono1febfdf2007-02-18 01:35:04 +0000224 unsigned i;
Benny Prijono56315612006-07-18 14:39:40 +0000225
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000226 pjsua_config_default(&cfg->cfg);
Benny Prijono106f5b72007-08-30 13:49:33 +0000227 pj_ansi_sprintf(tmp, "PJSUA v%s/%s", pj_get_version(), PJ_OS_NAME);
Benny Prijono56315612006-07-18 14:39:40 +0000228 pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp);
229
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000230 pjsua_logging_config_default(&cfg->log_cfg);
231 pjsua_media_config_default(&cfg->media_cfg);
232 pjsua_transport_config_default(&cfg->udp_cfg);
233 cfg->udp_cfg.port = 5060;
234 pjsua_transport_config_default(&cfg->rtp_cfg);
235 cfg->rtp_cfg.port = 4000;
Benny Prijono804ff0a2006-09-14 11:17:48 +0000236 cfg->duration = NO_LIMIT;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237 cfg->wav_id = PJSUA_INVALID_ID;
Benny Prijono1ebd6142006-10-19 15:48:02 +0000238 cfg->rec_id = PJSUA_INVALID_ID;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000239 cfg->wav_port = PJSUA_INVALID_ID;
Benny Prijono1ebd6142006-10-19 15:48:02 +0000240 cfg->rec_port = PJSUA_INVALID_ID;
Benny Prijono6dd967c2006-12-26 02:27:14 +0000241 cfg->mic_level = cfg->speaker_level = 1.0;
Benny Prijono4e5d5512007-03-06 18:11:30 +0000242 cfg->capture_dev = PJSUA_INVALID_ID;
243 cfg->playback_dev = PJSUA_INVALID_ID;
Benny Prijono1febfdf2007-02-18 01:35:04 +0000244
245 for (i=0; i<PJ_ARRAY_SIZE(cfg->acc_cfg); ++i)
246 pjsua_acc_config_default(&cfg->acc_cfg[i]);
247
248 for (i=0; i<PJ_ARRAY_SIZE(cfg->buddy_cfg); ++i)
249 pjsua_buddy_config_default(&cfg->buddy_cfg[i]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000250}
251
252
253/*
254 * Read command arguments from config file.
255 */
256static int read_config_file(pj_pool_t *pool, const char *filename,
257 int *app_argc, char ***app_argv)
258{
259 int i;
260 FILE *fhnd;
261 char line[200];
262 int argc = 0;
263 char **argv;
264 enum { MAX_ARGS = 64 };
265
266 /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
267 argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
268 argv[argc++] = *app_argv[0];
269
270 /* Open config file. */
271 fhnd = fopen(filename, "rt");
272 if (!fhnd) {
273 PJ_LOG(1,(THIS_FILE, "Unable to open config file %s", filename));
274 fflush(stdout);
275 return -1;
276 }
277
278 /* Scan tokens in the file. */
279 while (argc < MAX_ARGS && !feof(fhnd)) {
Benny Prijonobf5b4692007-06-28 03:20:17 +0000280 char *token;
281 char *p;
282 const char *whitespace = " \t\r\n";
283 char cDelimiter;
284 int len, token_len;
285
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000286 if (fgets(line, sizeof(line), fhnd) == NULL) break;
Benny Prijonobf5b4692007-06-28 03:20:17 +0000287
288 // Trim ending newlines
289 len = strlen(line);
290 if (line[len-1]=='\n')
291 line[--len] = '\0';
292 if (line[len-1]=='\r')
293 line[--len] = '\0';
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000294
Benny Prijonobf5b4692007-06-28 03:20:17 +0000295 if (len==0) continue;
296
297 for (p = line; *p != '\0' && argc < MAX_ARGS; p++) {
298 // first, scan whitespaces
299 while (*p != '\0' && strchr(whitespace, *p) != NULL) p++;
300
301 if (*p == '\0') // are we done yet?
302 break;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000303
Benny Prijonobf5b4692007-06-28 03:20:17 +0000304 if (*p == '"' || *p == '\'') { // is token a quoted string
305 cDelimiter = *p++; // save quote delimiter
306 token = p;
307
308 while (*p != '\0' && *p != cDelimiter) p++;
309
310 if (*p == '\0') // found end of the line, but,
311 cDelimiter = '\0'; // didn't find a matching quote
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000312
Benny Prijonobf5b4692007-06-28 03:20:17 +0000313 } else { // token's not a quoted string
314 token = p;
315
316 while (*p != '\0' && strchr(whitespace, *p) == NULL) p++;
317
318 cDelimiter = *p;
319 }
320
321 *p = '\0';
322 token_len = p-token;
323
324 if (token_len > 0) {
325 if (*token == '#')
326 break; // ignore remainder of line
327
328 argv[argc] = pj_pool_alloc(pool, token_len + 1);
329 pj_memcpy(argv[argc], token, token_len + 1);
330 ++argc;
331 }
332
333 *p = cDelimiter;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000334 }
335 }
336
337 /* Copy arguments from command line */
338 for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
339 argv[argc++] = (*app_argv)[i];
340
341 if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
342 PJ_LOG(1,(THIS_FILE,
343 "Too many arguments specified in cmd line/config file"));
344 fflush(stdout);
345 fclose(fhnd);
346 return -1;
347 }
348
349 fclose(fhnd);
350
351 /* Assign the new command line back to the original command line. */
352 *app_argc = argc;
353 *app_argv = argv;
354 return 0;
355
356}
357
358static int my_atoi(const char *cs)
359{
360 pj_str_t s;
Benny Prijono1e2dbe62007-06-15 04:15:16 +0000361
362 pj_cstr(&s, cs);
363 if (cs[0] == '-') {
364 s.ptr++, s.slen--;
365 return 0 - (int)pj_strtoul(&s);
366 } else if (cs[0] == '+') {
367 s.ptr++, s.slen--;
368 return pj_strtoul(&s);
369 } else {
370 return pj_strtoul(&s);
371 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000372}
373
374
375/* Parse arguments. */
376static pj_status_t parse_args(int argc, char *argv[],
377 struct app_config *cfg,
378 pj_str_t *uri_to_call)
379{
380 int c;
381 int option_index;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000382 enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000383 OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
Benny Prijono0a5cad82006-09-26 13:21:02 +0000384 OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY,
385 OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
Benny Prijono48ab2b72007-11-08 09:24:30 +0000386 OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
Benny Prijonoebbf6892007-03-24 17:37:25 +0000387 OPT_NAMESERVER, OPT_STUN_DOMAIN, OPT_STUN_SRV,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000388 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
389 OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
Benny Prijonof3758ee2008-02-26 15:32:16 +0000390 OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_USE_ICE,
391 OPT_USE_SRTP, OPT_SRTP_SECURE,
Benny Prijono4af234b2007-01-24 02:02:09 +0000392 OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC,
393 OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
Benny Prijono0a12f002006-07-26 17:05:39 +0000394 OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
Benny Prijonod79f25c2006-08-02 19:41:37 +0000395 OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL,
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000396 OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
Benny Prijonof521eb02006-08-06 23:07:25 +0000397 OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000398 OPT_NOREFERSUB,
Benny Prijonof3bbc132006-12-25 06:43:59 +0000399 OPT_USE_TLS, OPT_TLS_CA_FILE, OPT_TLS_CERT_FILE, OPT_TLS_PRIV_FILE,
400 OPT_TLS_PASSWORD, OPT_TLS_VERIFY_SERVER, OPT_TLS_VERIFY_CLIENT,
401 OPT_TLS_NEG_TIMEOUT,
Benny Prijono4e5d5512007-03-06 18:11:30 +0000402 OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV,
Benny Prijonofce28542007-12-09 15:41:10 +0000403 OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000404 };
405 struct pj_getopt_option long_options[] = {
406 { "config-file",1, 0, OPT_CONFIG_FILE},
407 { "log-file", 1, 0, OPT_LOG_FILE},
408 { "log-level", 1, 0, OPT_LOG_LEVEL},
409 { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
410 { "help", 0, 0, OPT_HELP},
411 { "version", 0, 0, OPT_VERSION},
412 { "clock-rate", 1, 0, OPT_CLOCK_RATE},
Benny Prijonof3758ee2008-02-26 15:32:16 +0000413 { "snd-clock-rate", 1, 0, OPT_SND_CLOCK_RATE},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000414 { "null-audio", 0, 0, OPT_NULL_AUDIO},
415 { "local-port", 1, 0, OPT_LOCAL_PORT},
Benny Prijono0a5cad82006-09-26 13:21:02 +0000416 { "ip-addr", 1, 0, OPT_IP_ADDR},
Benny Prijonoe93e2872006-06-28 16:46:49 +0000417 { "no-tcp", 0, 0, OPT_NO_TCP},
418 { "no-udp", 0, 0, OPT_NO_UDP},
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000419 { "norefersub", 0, 0, OPT_NOREFERSUB},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000420 { "proxy", 1, 0, OPT_PROXY},
421 { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
422 { "registrar", 1, 0, OPT_REGISTRAR},
423 { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000424 { "publish", 0, 0, OPT_PUBLISH},
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000425 { "use-100rel", 0, 0, OPT_100REL},
Benny Prijono48ab2b72007-11-08 09:24:30 +0000426 { "use-ims", 0, 0, OPT_USE_IMS},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000427 { "id", 1, 0, OPT_ID},
428 { "contact", 1, 0, OPT_CONTACT},
Benny Prijonofce28542007-12-09 15:41:10 +0000429 { "auto-update-nat", 1, 0, OPT_AUTO_UPDATE_NAT},
430 { "use-compact-form", 0, 0, OPT_USE_COMPACT_FORM},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000431 { "realm", 1, 0, OPT_REALM},
432 { "username", 1, 0, OPT_USERNAME},
433 { "password", 1, 0, OPT_PASSWORD},
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000434 { "nameserver", 1, 0, OPT_NAMESERVER},
Benny Prijonoebbf6892007-03-24 17:37:25 +0000435 { "stun-domain",1, 0, OPT_STUN_DOMAIN},
Benny Prijonoc97608e2007-03-23 16:34:20 +0000436 { "stun-srv", 1, 0, OPT_STUN_SRV},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000437 { "add-buddy", 1, 0, OPT_ADD_BUDDY},
438 { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
439 { "no-presence", 0, 0, OPT_NO_PRESENCE},
440 { "auto-answer",1, 0, OPT_AUTO_ANSWER},
441 { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
442 { "auto-play", 0, 0, OPT_AUTO_PLAY},
Benny Prijono1ebd6142006-10-19 15:48:02 +0000443 { "auto-rec", 0, 0, OPT_AUTO_REC},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000444 { "auto-loop", 0, 0, OPT_AUTO_LOOP},
445 { "auto-conf", 0, 0, OPT_AUTO_CONF},
446 { "play-file", 1, 0, OPT_PLAY_FILE},
Benny Prijono4af234b2007-01-24 02:02:09 +0000447 { "play-tone", 1, 0, OPT_PLAY_TONE},
Benny Prijono1ebd6142006-10-19 15:48:02 +0000448 { "rec-file", 1, 0, OPT_REC_FILE},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000449 { "rtp-port", 1, 0, OPT_RTP_PORT},
Benny Prijonoc97608e2007-03-23 16:34:20 +0000450 { "use-ice", 0, 0, OPT_USE_ICE},
Benny Prijonod8179652008-01-23 20:39:07 +0000451#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
452 { "use-srtp", 1, 0, OPT_USE_SRTP},
Benny Prijonof6508982008-01-25 09:02:33 +0000453 { "srtp-secure",1, 0, OPT_SRTP_SECURE},
Benny Prijonod8179652008-01-23 20:39:07 +0000454#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000455 { "add-codec", 1, 0, OPT_ADD_CODEC},
Benny Prijonofce28542007-12-09 15:41:10 +0000456 { "dis-codec", 1, 0, OPT_DIS_CODEC},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000457 { "complexity", 1, 0, OPT_COMPLEXITY},
458 { "quality", 1, 0, OPT_QUALITY},
459 { "ptime", 1, 0, OPT_PTIME},
Benny Prijono0a12f002006-07-26 17:05:39 +0000460 { "no-vad", 0, 0, OPT_NO_VAD},
Benny Prijonod79f25c2006-08-02 19:41:37 +0000461 { "ec-tail", 1, 0, OPT_EC_TAIL},
Benny Prijono00cae612006-07-31 15:19:36 +0000462 { "ilbc-mode", 1, 0, OPT_ILBC_MODE},
463 { "rx-drop-pct",1, 0, OPT_RX_DROP_PCT},
464 { "tx-drop-pct",1, 0, OPT_TX_DROP_PCT},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000465 { "next-account",0,0, OPT_NEXT_ACCOUNT},
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000466 { "next-cred", 0, 0, OPT_NEXT_CRED},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000467 { "max-calls", 1, 0, OPT_MAX_CALLS},
Benny Prijonof521eb02006-08-06 23:07:25 +0000468 { "duration", 1, 0, OPT_DURATION},
469 { "thread-cnt", 1, 0, OPT_THREAD_CNT},
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000470 { "use-tls", 0, 0, OPT_USE_TLS},
471 { "tls-ca-file",1, 0, OPT_TLS_CA_FILE},
Benny Prijonof3bbc132006-12-25 06:43:59 +0000472 { "tls-cert-file",1,0, OPT_TLS_CERT_FILE},
473 { "tls-privkey-file",1,0, OPT_TLS_PRIV_FILE},
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000474 { "tls-password",1,0, OPT_TLS_PASSWORD},
Benny Prijonof3bbc132006-12-25 06:43:59 +0000475 { "tls-verify-server", 0, 0, OPT_TLS_VERIFY_SERVER},
476 { "tls-verify-client", 0, 0, OPT_TLS_VERIFY_CLIENT},
477 { "tls-neg-timeout", 1, 0, OPT_TLS_NEG_TIMEOUT},
Benny Prijono4e5d5512007-03-06 18:11:30 +0000478 { "capture-dev", 1, 0, OPT_CAPTURE_DEV},
479 { "playback-dev", 1, 0, OPT_PLAYBACK_DEV},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000480 { NULL, 0, 0, 0}
481 };
482 pj_status_t status;
483 pjsua_acc_config *cur_acc;
484 char *config_file = NULL;
485 unsigned i;
486
487 /* Run pj_getopt once to see if user specifies config file to read. */
Benny Prijonof762ee72006-12-01 11:14:37 +0000488 pj_optind = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000489 while ((c=pj_getopt_long(argc, argv, "", long_options,
490 &option_index)) != -1)
491 {
492 switch (c) {
493 case OPT_CONFIG_FILE:
494 config_file = pj_optarg;
495 break;
496 }
497 if (config_file)
498 break;
499 }
500
501 if (config_file) {
502 status = read_config_file(app_config.pool, config_file, &argc, &argv);
503 if (status != 0)
504 return status;
505 }
506
507 cfg->acc_cnt = 0;
508 cur_acc = &cfg->acc_cfg[0];
509
510
511 /* Reinitialize and re-run pj_getopt again, possibly with new arguments
512 * read from config file.
513 */
514 pj_optind = 0;
515 while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000516 pj_str_t tmp;
517 long lval;
518
519 switch (c) {
520
Benny Prijono6f137482006-06-15 11:31:36 +0000521 case OPT_CONFIG_FILE:
522 /* Ignore as this has been processed before */
523 break;
524
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000525 case OPT_LOG_FILE:
526 cfg->log_cfg.log_filename = pj_str(pj_optarg);
527 break;
528
529 case OPT_LOG_LEVEL:
530 c = pj_strtoul(pj_cstr(&tmp, pj_optarg));
531 if (c < 0 || c > 6) {
532 PJ_LOG(1,(THIS_FILE,
533 "Error: expecting integer value 0-6 "
534 "for --log-level"));
535 return PJ_EINVAL;
536 }
537 cfg->log_cfg.level = c;
538 pj_log_set_level( c );
539 break;
540
541 case OPT_APP_LOG_LEVEL:
542 cfg->log_cfg.console_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
543 if (cfg->log_cfg.console_level < 0 || cfg->log_cfg.console_level > 6) {
544 PJ_LOG(1,(THIS_FILE,
545 "Error: expecting integer value 0-6 "
546 "for --app-log-level"));
547 return PJ_EINVAL;
548 }
549 break;
550
551 case OPT_HELP:
552 usage();
553 return PJ_EINVAL;
554
555 case OPT_VERSION: /* version */
556 pj_dump_config();
557 return PJ_EINVAL;
558
559 case OPT_NULL_AUDIO:
560 cfg->null_audio = PJ_TRUE;
561 break;
562
563 case OPT_CLOCK_RATE:
564 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
565 if (lval < 8000 || lval > 48000) {
566 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
Benny Prijonof3758ee2008-02-26 15:32:16 +0000567 "8000-48000 for conference clock rate"));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000568 return PJ_EINVAL;
569 }
570 cfg->media_cfg.clock_rate = lval;
571 break;
572
Benny Prijonof3758ee2008-02-26 15:32:16 +0000573 case OPT_SND_CLOCK_RATE:
574 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
575 if (lval < 8000 || lval > 48000) {
576 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
577 "8000-48000 for sound device clock rate"));
578 return PJ_EINVAL;
579 }
580 cfg->media_cfg.snd_clock_rate = lval;
581 break;
582
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000583 case OPT_LOCAL_PORT: /* local-port */
584 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
Benny Prijonoe347cb02007-02-14 14:36:13 +0000585 if (lval < 0 || lval > 65535) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586 PJ_LOG(1,(THIS_FILE,
587 "Error: expecting integer value for "
588 "--local-port"));
589 return PJ_EINVAL;
590 }
591 cfg->udp_cfg.port = (pj_uint16_t)lval;
592 break;
593
Benny Prijono0a5cad82006-09-26 13:21:02 +0000594 case OPT_IP_ADDR: /* ip-addr */
595 cfg->udp_cfg.public_addr = pj_str(pj_optarg);
596 cfg->rtp_cfg.public_addr = pj_str(pj_optarg);
597 break;
598
Benny Prijonoe93e2872006-06-28 16:46:49 +0000599 case OPT_NO_UDP: /* no-udp */
600 if (cfg->no_tcp) {
Benny Prijonob988d762007-12-05 04:30:04 +0000601 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
602 return PJ_EINVAL;
Benny Prijonoe93e2872006-06-28 16:46:49 +0000603 }
604
605 cfg->no_udp = PJ_TRUE;
606 break;
607
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000608 case OPT_NOREFERSUB: /* norefersub */
609 cfg->no_refersub = PJ_TRUE;
610 break;
611
Benny Prijonoe93e2872006-06-28 16:46:49 +0000612 case OPT_NO_TCP: /* no-tcp */
613 if (cfg->no_udp) {
Benny Prijonob988d762007-12-05 04:30:04 +0000614 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
615 return PJ_EINVAL;
Benny Prijonoe93e2872006-06-28 16:46:49 +0000616 }
617
618 cfg->no_tcp = PJ_TRUE;
619 break;
620
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000621 case OPT_PROXY: /* proxy */
622 if (pjsua_verify_sip_url(pj_optarg) != 0) {
623 PJ_LOG(1,(THIS_FILE,
624 "Error: invalid SIP URL '%s' "
625 "in proxy argument", pj_optarg));
626 return PJ_EINVAL;
627 }
628 cur_acc->proxy[cur_acc->proxy_cnt++] = pj_str(pj_optarg);
629 break;
630
631 case OPT_OUTBOUND_PROXY: /* outbound proxy */
632 if (pjsua_verify_sip_url(pj_optarg) != 0) {
633 PJ_LOG(1,(THIS_FILE,
634 "Error: invalid SIP URL '%s' "
635 "in outbound proxy argument", pj_optarg));
636 return PJ_EINVAL;
637 }
638 cfg->cfg.outbound_proxy[cfg->cfg.outbound_proxy_cnt++] = pj_str(pj_optarg);
639 break;
640
641 case OPT_REGISTRAR: /* registrar */
642 if (pjsua_verify_sip_url(pj_optarg) != 0) {
643 PJ_LOG(1,(THIS_FILE,
644 "Error: invalid SIP URL '%s' in "
645 "registrar argument", pj_optarg));
646 return PJ_EINVAL;
647 }
648 cur_acc->reg_uri = pj_str(pj_optarg);
649 break;
650
651 case OPT_REG_TIMEOUT: /* reg-timeout */
652 cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,pj_optarg));
653 if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
654 PJ_LOG(1,(THIS_FILE,
655 "Error: invalid value for --reg-timeout "
656 "(expecting 1-3600)"));
657 return PJ_EINVAL;
658 }
659 break;
660
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000661 case OPT_PUBLISH: /* publish */
662 cur_acc->publish_enabled = PJ_TRUE;
663 break;
664
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000665 case OPT_100REL: /** 100rel */
666 cur_acc->require_100rel = PJ_TRUE;
667 cfg->cfg.require_100rel = PJ_TRUE;
668 break;
669
Benny Prijono48ab2b72007-11-08 09:24:30 +0000670 case OPT_USE_IMS: /* Activate IMS settings */
671 cur_acc->auth_pref.initial_auth = PJ_TRUE;
Benny Prijono2a67ea42007-10-25 02:51:33 +0000672 break;
673
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000674 case OPT_ID: /* id */
675 if (pjsua_verify_sip_url(pj_optarg) != 0) {
676 PJ_LOG(1,(THIS_FILE,
677 "Error: invalid SIP URL '%s' "
678 "in local id argument", pj_optarg));
679 return PJ_EINVAL;
680 }
681 cur_acc->id = pj_str(pj_optarg);
682 break;
683
684 case OPT_CONTACT: /* contact */
685 if (pjsua_verify_sip_url(pj_optarg) != 0) {
686 PJ_LOG(1,(THIS_FILE,
687 "Error: invalid SIP URL '%s' "
688 "in contact argument", pj_optarg));
689 return PJ_EINVAL;
690 }
Benny Prijonob4a17c92006-07-10 14:40:21 +0000691 cur_acc->force_contact = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000692 break;
693
Benny Prijonofce28542007-12-09 15:41:10 +0000694 case OPT_AUTO_UPDATE_NAT: /* OPT_AUTO_UPDATE_NAT */
695 cur_acc->auto_update_nat = pj_strtoul(pj_cstr(&tmp, pj_optarg));
696 break;
697
698 case OPT_USE_COMPACT_FORM:
699 /* enable compact form - from Ticket #342 */
700 {
701 extern pj_bool_t pjsip_use_compact_form;
702 extern pj_bool_t pjsip_include_allow_hdr_in_dlg;
703 extern pj_bool_t pjmedia_add_rtpmap_for_static_pt;
704
705 pjsip_use_compact_form = PJ_TRUE;
706 /* do not transmit Allow header */
707 pjsip_include_allow_hdr_in_dlg = PJ_FALSE;
708 /* Do not include rtpmap for static payload types (<96) */
709 pjmedia_add_rtpmap_for_static_pt = PJ_FALSE;
710 }
711 break;
712
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000713 case OPT_NEXT_ACCOUNT: /* Add more account. */
714 cfg->acc_cnt++;
Benny Prijono56315612006-07-18 14:39:40 +0000715 cur_acc = &cfg->acc_cfg[cfg->acc_cnt];
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000716 break;
717
718 case OPT_USERNAME: /* Default authentication user */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000719 cur_acc->cred_info[cur_acc->cred_count].username = pj_str(pj_optarg);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000720 cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("Digest");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000721 break;
722
723 case OPT_REALM: /* Default authentication realm. */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000724 cur_acc->cred_info[cur_acc->cred_count].realm = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000725 break;
726
727 case OPT_PASSWORD: /* authentication password */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000728 cur_acc->cred_info[cur_acc->cred_count].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
729 cur_acc->cred_info[cur_acc->cred_count].data = pj_str(pj_optarg);
Benny Prijono28f673a2007-10-15 07:04:59 +0000730#if PJSIP_HAS_DIGEST_AKA_AUTH
731 cur_acc->cred_info[cur_acc->cred_count].data_type |= PJSIP_CRED_DATA_EXT_AKA;
732 cur_acc->cred_info[cur_acc->cred_count].ext.aka.k = pj_str(pj_optarg);
733 cur_acc->cred_info[cur_acc->cred_count].ext.aka.cb = &pjsip_auth_create_aka_response;
734#endif
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000735 break;
736
737 case OPT_NEXT_CRED: /* next credential */
738 cur_acc->cred_count++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000739 break;
740
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000741 case OPT_NAMESERVER: /* nameserver */
742 cfg->cfg.nameserver[cfg->cfg.nameserver_count++] = pj_str(pj_optarg);
743 if (cfg->cfg.nameserver_count > PJ_ARRAY_SIZE(cfg->cfg.nameserver)) {
744 PJ_LOG(1,(THIS_FILE, "Error: too many nameservers"));
745 return PJ_ETOOMANY;
746 }
747 break;
748
Benny Prijonoebbf6892007-03-24 17:37:25 +0000749 case OPT_STUN_DOMAIN: /* STUN domain */
750 cfg->cfg.stun_domain = pj_str(pj_optarg);
751 break;
752
Benny Prijonoc97608e2007-03-23 16:34:20 +0000753 case OPT_STUN_SRV: /* STUN server */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000754 cfg->cfg.stun_host = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000755 break;
756
757 case OPT_ADD_BUDDY: /* Add to buddy list. */
758 if (pjsua_verify_sip_url(pj_optarg) != 0) {
759 PJ_LOG(1,(THIS_FILE,
760 "Error: invalid URL '%s' in "
761 "--add-buddy option", pj_optarg));
762 return -1;
763 }
764 if (cfg->buddy_cnt == PJ_ARRAY_SIZE(cfg->buddy_cfg)) {
765 PJ_LOG(1,(THIS_FILE,
766 "Error: too many buddies in buddy list."));
767 return -1;
768 }
769 cfg->buddy_cfg[cfg->buddy_cnt].uri = pj_str(pj_optarg);
770 cfg->buddy_cnt++;
771 break;
772
773 case OPT_AUTO_PLAY:
774 cfg->auto_play = 1;
775 break;
776
Benny Prijono1ebd6142006-10-19 15:48:02 +0000777 case OPT_AUTO_REC:
778 cfg->auto_rec = 1;
779 break;
780
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000781 case OPT_AUTO_LOOP:
782 cfg->auto_loop = 1;
783 break;
784
Benny Prijono7ca96da2006-08-07 12:11:40 +0000785 case OPT_AUTO_CONF:
786 cfg->auto_conf = 1;
787 break;
788
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000789 case OPT_PLAY_FILE:
Benny Prijono32e4f492007-01-24 00:44:26 +0000790 cfg->wav_files[cfg->wav_count++] = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000791 break;
792
Benny Prijono4af234b2007-01-24 02:02:09 +0000793 case OPT_PLAY_TONE:
794 {
795 int f1, f2, on, off;
796 int n;
797
798 n = sscanf(pj_optarg, "%d,%d,%d,%d", &f1, &f2, &on, &off);
799 if (n != 4) {
800 puts("Expecting f1,f2,on,off in --play-tone");
801 return -1;
802 }
803
804 cfg->tones[cfg->tone_count].freq1 = (short)f1;
805 cfg->tones[cfg->tone_count].freq2 = (short)f2;
806 cfg->tones[cfg->tone_count].on_msec = (short)on;
807 cfg->tones[cfg->tone_count].off_msec = (short)off;
808 ++cfg->tone_count;
809 }
810 break;
811
Benny Prijono1ebd6142006-10-19 15:48:02 +0000812 case OPT_REC_FILE:
813 cfg->rec_file = pj_str(pj_optarg);
814 break;
815
Benny Prijonoc97608e2007-03-23 16:34:20 +0000816 case OPT_USE_ICE:
817 cfg->media_cfg.enable_ice = PJ_TRUE;
818 break;
819
Benny Prijonod8179652008-01-23 20:39:07 +0000820#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
821 case OPT_USE_SRTP:
822 app_config.cfg.use_srtp = my_atoi(pj_optarg);
823 if (!pj_isdigit(*pj_optarg) || app_config.cfg.use_srtp > 2) {
824 PJ_LOG(1,(THIS_FILE, "Invalid value for --use-srtp option"));
825 return -1;
826 }
Benny Prijono3ec13c72008-01-29 11:52:58 +0000827 cur_acc->use_srtp = app_config.cfg.use_srtp;
Benny Prijonod8179652008-01-23 20:39:07 +0000828 break;
Benny Prijonof6508982008-01-25 09:02:33 +0000829 case OPT_SRTP_SECURE:
830 app_config.cfg.srtp_secure_signaling = my_atoi(pj_optarg);
831 if (!pj_isdigit(*pj_optarg) ||
832 app_config.cfg.srtp_secure_signaling > 2)
833 {
834 PJ_LOG(1,(THIS_FILE, "Invalid value for --srtp-secure option"));
835 return -1;
836 }
Benny Prijono3ec13c72008-01-29 11:52:58 +0000837 cur_acc->srtp_secure_signaling = app_config.cfg.srtp_secure_signaling;
Benny Prijonof6508982008-01-25 09:02:33 +0000838 break;
Benny Prijonod8179652008-01-23 20:39:07 +0000839#endif
840
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000841 case OPT_RTP_PORT:
842 cfg->rtp_cfg.port = my_atoi(pj_optarg);
Benny Prijono5583a802007-06-26 12:20:37 +0000843 if (cfg->rtp_cfg.port == 0) {
844 enum { START_PORT=4000 };
845 unsigned range;
846
847 range = (65535-START_PORT-PJSUA_MAX_CALLS*2);
848 cfg->rtp_cfg.port = START_PORT +
849 ((pj_rand() % range) & 0xFFFE);
850 }
851
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000852 if (cfg->rtp_cfg.port < 1 || cfg->rtp_cfg.port > 65535) {
853 PJ_LOG(1,(THIS_FILE,
854 "Error: rtp-port argument value "
855 "(expecting 1-65535"));
856 return -1;
857 }
858 break;
859
Benny Prijonofce28542007-12-09 15:41:10 +0000860 case OPT_DIS_CODEC:
861 cfg->codec_dis[cfg->codec_dis_cnt++] = pj_str(pj_optarg);
862 break;
863
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000864 case OPT_ADD_CODEC:
865 cfg->codec_arg[cfg->codec_cnt++] = pj_str(pj_optarg);
866 break;
867
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000868 /* These options were no longer valid after new pjsua */
869 /*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000870 case OPT_COMPLEXITY:
871 cfg->complexity = my_atoi(pj_optarg);
872 if (cfg->complexity < 0 || cfg->complexity > 10) {
873 PJ_LOG(1,(THIS_FILE,
874 "Error: invalid --complexity (expecting 0-10"));
875 return -1;
876 }
877 break;
Benny Prijono804ff0a2006-09-14 11:17:48 +0000878 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000879
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000880 case OPT_DURATION:
881 cfg->duration = my_atoi(pj_optarg);
882 break;
883
Benny Prijonof521eb02006-08-06 23:07:25 +0000884 case OPT_THREAD_CNT:
885 cfg->cfg.thread_cnt = my_atoi(pj_optarg);
886 if (cfg->cfg.thread_cnt > 128) {
887 PJ_LOG(1,(THIS_FILE,
888 "Error: invalid --thread-cnt option"));
889 return -1;
890 }
891 break;
892
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000893 case OPT_PTIME:
Benny Prijono0a12f002006-07-26 17:05:39 +0000894 cfg->media_cfg.ptime = my_atoi(pj_optarg);
895 if (cfg->media_cfg.ptime < 10 || cfg->media_cfg.ptime > 1000) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000896 PJ_LOG(1,(THIS_FILE,
897 "Error: invalid --ptime option"));
898 return -1;
899 }
900 break;
901
Benny Prijono0a12f002006-07-26 17:05:39 +0000902 case OPT_NO_VAD:
903 cfg->media_cfg.no_vad = PJ_TRUE;
904 break;
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000905
Benny Prijonod79f25c2006-08-02 19:41:37 +0000906 case OPT_EC_TAIL:
907 cfg->media_cfg.ec_tail_len = my_atoi(pj_optarg);
908 if (cfg->media_cfg.ec_tail_len > 1000) {
909 PJ_LOG(1,(THIS_FILE, "I think the ec-tail length setting "
910 "is too big"));
911 return -1;
912 }
913 break;
914
Benny Prijono0498d902006-06-19 14:49:14 +0000915 case OPT_QUALITY:
916 cfg->media_cfg.quality = my_atoi(pj_optarg);
917 if (cfg->media_cfg.quality < 0 || cfg->media_cfg.quality > 10) {
918 PJ_LOG(1,(THIS_FILE,
919 "Error: invalid --quality (expecting 0-10"));
920 return -1;
921 }
922 break;
923
Benny Prijono00cae612006-07-31 15:19:36 +0000924 case OPT_ILBC_MODE:
925 cfg->media_cfg.ilbc_mode = my_atoi(pj_optarg);
926 if (cfg->media_cfg.ilbc_mode!=20 && cfg->media_cfg.ilbc_mode!=30) {
927 PJ_LOG(1,(THIS_FILE,
928 "Error: invalid --ilbc-mode (expecting 20 or 30"));
929 return -1;
930 }
931 break;
932
933 case OPT_RX_DROP_PCT:
934 cfg->media_cfg.rx_drop_pct = my_atoi(pj_optarg);
935 if (cfg->media_cfg.rx_drop_pct > 100) {
936 PJ_LOG(1,(THIS_FILE,
937 "Error: invalid --rx-drop-pct (expecting <= 100"));
938 return -1;
939 }
940 break;
941
942 case OPT_TX_DROP_PCT:
943 cfg->media_cfg.tx_drop_pct = my_atoi(pj_optarg);
944 if (cfg->media_cfg.tx_drop_pct > 100) {
945 PJ_LOG(1,(THIS_FILE,
946 "Error: invalid --tx-drop-pct (expecting <= 100"));
947 return -1;
948 }
949 break;
950
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000951 case OPT_AUTO_ANSWER:
952 cfg->auto_answer = my_atoi(pj_optarg);
953 if (cfg->auto_answer < 100 || cfg->auto_answer > 699) {
954 PJ_LOG(1,(THIS_FILE,
955 "Error: invalid code in --auto-answer "
956 "(expecting 100-699"));
957 return -1;
958 }
959 break;
960
961 case OPT_MAX_CALLS:
962 cfg->cfg.max_calls = my_atoi(pj_optarg);
Benny Prijono48af79c2006-07-22 12:49:17 +0000963 if (cfg->cfg.max_calls < 1 || cfg->cfg.max_calls > PJSUA_MAX_CALLS) {
Benny Prijono804ff0a2006-09-14 11:17:48 +0000964 PJ_LOG(1,(THIS_FILE,"Error: maximum call setting exceeds "
965 "compile time limit (PJSUA_MAX_CALLS=%d)",
Benny Prijono48af79c2006-07-22 12:49:17 +0000966 PJSUA_MAX_CALLS));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000967 return -1;
968 }
969 break;
970
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000971 case OPT_USE_TLS:
972 cfg->use_tls = PJ_TRUE;
Benny Prijonof3bbc132006-12-25 06:43:59 +0000973#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
974 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
975 return -1;
976#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000977 break;
978
979 case OPT_TLS_CA_FILE:
Benny Prijonof3bbc132006-12-25 06:43:59 +0000980 cfg->udp_cfg.tls_setting.ca_list_file = pj_str(pj_optarg);
981#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
982 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
983 return -1;
984#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000985 break;
986
Benny Prijonof3bbc132006-12-25 06:43:59 +0000987 case OPT_TLS_CERT_FILE:
988 cfg->udp_cfg.tls_setting.cert_file = pj_str(pj_optarg);
989#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
990 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
991 return -1;
992#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000993 break;
994
Benny Prijonof3bbc132006-12-25 06:43:59 +0000995 case OPT_TLS_PRIV_FILE:
996 cfg->udp_cfg.tls_setting.privkey_file = pj_str(pj_optarg);
997 break;
998
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000999 case OPT_TLS_PASSWORD:
Benny Prijonof3bbc132006-12-25 06:43:59 +00001000 cfg->udp_cfg.tls_setting.password = pj_str(pj_optarg);
1001#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
1002 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
1003 return -1;
1004#endif
1005 break;
1006
1007 case OPT_TLS_VERIFY_SERVER:
1008 cfg->udp_cfg.tls_setting.verify_server = PJ_TRUE;
1009 break;
1010
1011 case OPT_TLS_VERIFY_CLIENT:
1012 cfg->udp_cfg.tls_setting.verify_client = PJ_TRUE;
1013 break;
1014
1015 case OPT_TLS_NEG_TIMEOUT:
1016 cfg->udp_cfg.tls_setting.timeout.sec = atoi(pj_optarg);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001017 break;
1018
Benny Prijono4e5d5512007-03-06 18:11:30 +00001019 case OPT_CAPTURE_DEV:
1020 cfg->capture_dev = atoi(pj_optarg);
1021 break;
1022
1023 case OPT_PLAYBACK_DEV:
1024 cfg->playback_dev = atoi(pj_optarg);
1025 break;
1026
Benny Prijono22a300a2006-06-14 20:04:55 +00001027 default:
Benny Prijono787b8692006-06-19 12:40:03 +00001028 PJ_LOG(1,(THIS_FILE,
Benny Prijonod6388ac2006-09-09 13:23:09 +00001029 "Argument \"%s\" is not valid. Use --help to see help",
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001030 argv[pj_optind-1]));
Benny Prijono22a300a2006-06-14 20:04:55 +00001031 return -1;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001032 }
1033 }
1034
1035 if (pj_optind != argc) {
1036 pj_str_t uri_arg;
1037
1038 if (pjsua_verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
1039 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
1040 return -1;
1041 }
1042 uri_arg = pj_str(argv[pj_optind]);
1043 if (uri_to_call)
1044 *uri_to_call = uri_arg;
1045 pj_optind++;
1046
1047 /* Add URI to call to buddy list if it's not already there */
1048 for (i=0; i<cfg->buddy_cnt; ++i) {
1049 if (pj_stricmp(&cfg->buddy_cfg[i].uri, &uri_arg)==0)
1050 break;
1051 }
1052 if (i == cfg->buddy_cnt && cfg->buddy_cnt < PJSUA_MAX_BUDDIES) {
1053 cfg->buddy_cfg[cfg->buddy_cnt++].uri = uri_arg;
1054 }
1055
1056 } else {
1057 if (uri_to_call)
1058 uri_to_call->slen = 0;
1059 }
1060
1061 if (pj_optind != argc) {
1062 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
1063 return PJ_EINVAL;
1064 }
1065
Benny Prijono56315612006-07-18 14:39:40 +00001066 if (cfg->acc_cfg[cfg->acc_cnt].id.slen)
1067 cfg->acc_cnt++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001068
1069 for (i=0; i<cfg->acc_cnt; ++i) {
Benny Prijono48ab2b72007-11-08 09:24:30 +00001070 pjsua_acc_config *acfg = &cfg->acc_cfg[i];
1071
1072 if (acfg->cred_info[acfg->cred_count].username.slen)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001073 {
Benny Prijono48ab2b72007-11-08 09:24:30 +00001074 acfg->cred_count++;
1075 }
1076
1077 /* When IMS mode is enabled for the account, verify that settings
1078 * are okay.
1079 */
1080 /* For now we check if IMS mode is activated by looking if
1081 * initial_auth is set.
1082 */
1083 if (acfg->auth_pref.initial_auth && acfg->cred_count) {
1084 /* Realm must point to the real domain */
1085 if (*acfg->cred_info[0].realm.ptr=='*') {
1086 PJ_LOG(1,(THIS_FILE,
1087 "Error: cannot use '*' as realm with IMS"));
1088 return PJ_EINVAL;
1089 }
1090
1091 /* Username for authentication must be in a@b format */
1092 if (strchr(acfg->cred_info[0].username.ptr, '@')==0) {
1093 PJ_LOG(1,(THIS_FILE,
1094 "Error: Username for authentication must "
1095 "be in user@domain format with IMS"));
1096 return PJ_EINVAL;
1097 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001098 }
1099 }
1100
1101
1102 return PJ_SUCCESS;
1103}
1104
1105
1106/*
1107 * Save account settings
1108 */
1109static void write_account_settings(int acc_index, pj_str_t *result)
1110{
1111 unsigned i;
1112 char line[128];
1113 pjsua_acc_config *acc_cfg = &app_config.acc_cfg[acc_index];
1114
1115
1116 pj_ansi_sprintf(line, "\n#\n# Account %d:\n#\n", acc_index);
1117 pj_strcat2(result, line);
1118
1119
1120 /* Identity */
1121 if (acc_cfg->id.slen) {
1122 pj_ansi_sprintf(line, "--id %.*s\n",
1123 (int)acc_cfg->id.slen,
1124 acc_cfg->id.ptr);
1125 pj_strcat2(result, line);
1126 }
1127
1128 /* Registrar server */
1129 if (acc_cfg->reg_uri.slen) {
1130 pj_ansi_sprintf(line, "--registrar %.*s\n",
1131 (int)acc_cfg->reg_uri.slen,
1132 acc_cfg->reg_uri.ptr);
1133 pj_strcat2(result, line);
1134
1135 pj_ansi_sprintf(line, "--reg-timeout %u\n",
1136 acc_cfg->reg_timeout);
1137 pj_strcat2(result, line);
1138 }
1139
1140 /* Contact */
Benny Prijonob4a17c92006-07-10 14:40:21 +00001141 if (acc_cfg->force_contact.slen) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001142 pj_ansi_sprintf(line, "--contact %.*s\n",
Benny Prijonob4a17c92006-07-10 14:40:21 +00001143 (int)acc_cfg->force_contact.slen,
1144 acc_cfg->force_contact.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001145 pj_strcat2(result, line);
1146 }
1147
Benny Prijonofce28542007-12-09 15:41:10 +00001148 /* */
1149 //if (acc_cfg->auto_update_nat)
1150 {
1151 pj_ansi_sprintf(line, "--auto-update-nat %i\n",
1152 (int)acc_cfg->auto_update_nat);
1153 pj_strcat2(result, line);
1154 }
1155
Benny Prijonod8179652008-01-23 20:39:07 +00001156#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
1157 /* SRTP */
1158 if (acc_cfg->use_srtp) {
1159 pj_ansi_sprintf(line, "--use-srtp %i\n",
1160 (int)acc_cfg->use_srtp);
1161 pj_strcat2(result, line);
1162 }
1163#endif
1164
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001165 /* Proxy */
1166 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
1167 pj_ansi_sprintf(line, "--proxy %.*s\n",
1168 (int)acc_cfg->proxy[i].slen,
1169 acc_cfg->proxy[i].ptr);
1170 pj_strcat2(result, line);
1171 }
1172
1173 /* Credentials */
1174 for (i=0; i<acc_cfg->cred_count; ++i) {
1175 if (acc_cfg->cred_info[i].realm.slen) {
1176 pj_ansi_sprintf(line, "--realm %.*s\n",
1177 (int)acc_cfg->cred_info[i].realm.slen,
1178 acc_cfg->cred_info[i].realm.ptr);
1179 pj_strcat2(result, line);
1180 }
1181
1182 if (acc_cfg->cred_info[i].username.slen) {
1183 pj_ansi_sprintf(line, "--username %.*s\n",
1184 (int)acc_cfg->cred_info[i].username.slen,
1185 acc_cfg->cred_info[i].username.ptr);
1186 pj_strcat2(result, line);
1187 }
1188
1189 if (acc_cfg->cred_info[i].data.slen) {
1190 pj_ansi_sprintf(line, "--password %.*s\n",
1191 (int)acc_cfg->cred_info[i].data.slen,
1192 acc_cfg->cred_info[i].data.ptr);
1193 pj_strcat2(result, line);
1194 }
Benny Prijonoeef4f8c2006-06-19 12:07:44 +00001195
1196 if (i != acc_cfg->cred_count - 1)
1197 pj_strcat2(result, "--next-cred\n");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001198 }
1199
1200}
1201
1202
1203/*
1204 * Write settings.
1205 */
1206static int write_settings(const struct app_config *config,
1207 char *buf, pj_size_t max)
1208{
1209 unsigned acc_index;
1210 unsigned i;
1211 pj_str_t cfg;
1212 char line[128];
Benny Prijonofce28542007-12-09 15:41:10 +00001213 extern pj_bool_t pjsip_use_compact_form;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001214
1215 PJ_UNUSED_ARG(max);
1216
1217 cfg.ptr = buf;
1218 cfg.slen = 0;
1219
1220 /* Logging. */
1221 pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
1222 pj_ansi_sprintf(line, "--log-level %d\n",
1223 config->log_cfg.level);
1224 pj_strcat2(&cfg, line);
1225
1226 pj_ansi_sprintf(line, "--app-log-level %d\n",
1227 config->log_cfg.console_level);
1228 pj_strcat2(&cfg, line);
1229
1230 if (config->log_cfg.log_filename.slen) {
1231 pj_ansi_sprintf(line, "--log-file %.*s\n",
1232 (int)config->log_cfg.log_filename.slen,
1233 config->log_cfg.log_filename.ptr);
1234 pj_strcat2(&cfg, line);
1235 }
1236
1237
1238 /* Save account settings. */
1239 for (acc_index=0; acc_index < config->acc_cnt; ++acc_index) {
1240
1241 write_account_settings(acc_index, &cfg);
1242
1243 if (acc_index < config->acc_cnt-1)
1244 pj_strcat2(&cfg, "--next-account\n");
1245 }
1246
1247
1248 pj_strcat2(&cfg, "\n#\n# Network settings:\n#\n");
1249
1250 /* Outbound proxy */
1251 for (i=0; i<config->cfg.outbound_proxy_cnt; ++i) {
1252 pj_ansi_sprintf(line, "--outbound %.*s\n",
1253 (int)config->cfg.outbound_proxy[i].slen,
1254 config->cfg.outbound_proxy[i].ptr);
1255 pj_strcat2(&cfg, line);
1256 }
1257
1258
1259 /* UDP Transport. */
1260 pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port);
1261 pj_strcat2(&cfg, line);
1262
Benny Prijono0a5cad82006-09-26 13:21:02 +00001263 /* IP address, if any. */
1264 if (config->udp_cfg.public_addr.slen) {
1265 pj_ansi_sprintf(line, "--ip-addr %.*s\n",
1266 (int)config->udp_cfg.public_addr.slen,
1267 config->udp_cfg.public_addr.ptr);
1268 pj_strcat2(&cfg, line);
1269 }
1270
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001271 /* No TCP ? */
1272 if (config->no_tcp) {
1273 pj_strcat2(&cfg, "--no-tcp\n");
1274 }
1275
1276 /* No UDP ? */
1277 if (config->no_udp) {
1278 pj_strcat2(&cfg, "--no-udp\n");
1279 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001280
1281 /* STUN */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001282 if (config->cfg.stun_domain.slen) {
1283 pj_ansi_sprintf(line, "--stun-domain %.*s\n",
1284 (int)config->cfg.stun_domain.slen,
1285 config->cfg.stun_domain.ptr);
1286 pj_strcat2(&cfg, line);
1287 }
1288 if (config->cfg.stun_host.slen) {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001289 pj_ansi_sprintf(line, "--stun-srv %.*s\n",
Benny Prijonoebbf6892007-03-24 17:37:25 +00001290 (int)config->cfg.stun_host.slen,
1291 config->cfg.stun_host.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001292 pj_strcat2(&cfg, line);
1293 }
1294
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001295 /* TLS */
1296 if (config->use_tls)
1297 pj_strcat2(&cfg, "--use-tls\n");
Benny Prijonof3bbc132006-12-25 06:43:59 +00001298 if (config->udp_cfg.tls_setting.ca_list_file.slen) {
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001299 pj_ansi_sprintf(line, "--tls-ca-file %.*s\n",
Benny Prijonof3bbc132006-12-25 06:43:59 +00001300 (int)config->udp_cfg.tls_setting.ca_list_file.slen,
1301 config->udp_cfg.tls_setting.ca_list_file.ptr);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001302 pj_strcat2(&cfg, line);
1303 }
Benny Prijonof3bbc132006-12-25 06:43:59 +00001304 if (config->udp_cfg.tls_setting.cert_file.slen) {
1305 pj_ansi_sprintf(line, "--tls-cert-file %.*s\n",
1306 (int)config->udp_cfg.tls_setting.cert_file.slen,
1307 config->udp_cfg.tls_setting.cert_file.ptr);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001308 pj_strcat2(&cfg, line);
1309 }
Benny Prijonof3bbc132006-12-25 06:43:59 +00001310 if (config->udp_cfg.tls_setting.privkey_file.slen) {
1311 pj_ansi_sprintf(line, "--tls-privkey-file %.*s\n",
1312 (int)config->udp_cfg.tls_setting.privkey_file.slen,
1313 config->udp_cfg.tls_setting.privkey_file.ptr);
1314 pj_strcat2(&cfg, line);
1315 }
1316
1317 if (config->udp_cfg.tls_setting.password.slen) {
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001318 pj_ansi_sprintf(line, "--tls-password %.*s\n",
Benny Prijonof3bbc132006-12-25 06:43:59 +00001319 (int)config->udp_cfg.tls_setting.password.slen,
1320 config->udp_cfg.tls_setting.password.ptr);
1321 pj_strcat2(&cfg, line);
1322 }
1323
1324 if (config->udp_cfg.tls_setting.verify_server)
1325 pj_strcat2(&cfg, "--tls-verify-server\n");
1326
1327 if (config->udp_cfg.tls_setting.verify_client)
1328 pj_strcat2(&cfg, "--tls-verify-client\n");
1329
1330 if (config->udp_cfg.tls_setting.timeout.sec) {
1331 pj_ansi_sprintf(line, "--tls-neg-timeout %d\n",
Benny Prijonoe960bb52007-01-21 17:53:39 +00001332 (int)config->udp_cfg.tls_setting.timeout.sec);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001333 pj_strcat2(&cfg, line);
1334 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001335
1336 pj_strcat2(&cfg, "\n#\n# Media settings:\n#\n");
1337
Benny Prijonof6508982008-01-25 09:02:33 +00001338 /* SRTP */
Benny Prijonofe5a6942008-02-18 12:16:23 +00001339#if PJMEDIA_HAS_SRTP
Benny Prijonof6508982008-01-25 09:02:33 +00001340 if (app_config.cfg.use_srtp != PJSUA_DEFAULT_USE_SRTP) {
1341 pj_ansi_sprintf(line, "--use-srtp %d\n",
1342 app_config.cfg.use_srtp);
1343 pj_strcat2(&cfg, line);
1344 }
1345 if (app_config.cfg.srtp_secure_signaling !=
1346 PJSUA_DEFAULT_SRTP_SECURE_SIGNALING)
1347 {
1348 pj_ansi_sprintf(line, "--srtp-secure %d\n",
1349 app_config.cfg.srtp_secure_signaling);
1350 pj_strcat2(&cfg, line);
1351 }
Benny Prijonofe5a6942008-02-18 12:16:23 +00001352#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001353
1354 /* Media */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001355 if (config->media_cfg.enable_ice)
1356 pj_strcat2(&cfg, "--use-ice\n");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001357 if (config->null_audio)
1358 pj_strcat2(&cfg, "--null-audio\n");
1359 if (config->auto_play)
1360 pj_strcat2(&cfg, "--auto-play\n");
1361 if (config->auto_loop)
1362 pj_strcat2(&cfg, "--auto-loop\n");
Benny Prijono7ca96da2006-08-07 12:11:40 +00001363 if (config->auto_conf)
1364 pj_strcat2(&cfg, "--auto-conf\n");
Benny Prijono32e4f492007-01-24 00:44:26 +00001365 for (i=0; i<config->wav_count; ++i) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001366 pj_ansi_sprintf(line, "--play-file %s\n",
Benny Prijono32e4f492007-01-24 00:44:26 +00001367 config->wav_files[i].ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001368 pj_strcat2(&cfg, line);
1369 }
Benny Prijono4af234b2007-01-24 02:02:09 +00001370 for (i=0; i<config->tone_count; ++i) {
1371 pj_ansi_sprintf(line, "--play-tone %d,%d,%d,%d\n",
1372 config->tones[i].freq1, config->tones[i].freq2,
1373 config->tones[i].on_msec, config->tones[i].off_msec);
1374 pj_strcat2(&cfg, line);
1375 }
Benny Prijono1ebd6142006-10-19 15:48:02 +00001376 if (config->rec_file.slen) {
1377 pj_ansi_sprintf(line, "--rec-file %s\n",
1378 config->rec_file.ptr);
1379 pj_strcat2(&cfg, line);
1380 }
1381 if (config->auto_rec)
1382 pj_strcat2(&cfg, "--auto-rec\n");
Benny Prijono4e5d5512007-03-06 18:11:30 +00001383 if (config->capture_dev != PJSUA_INVALID_ID) {
1384 pj_ansi_sprintf(line, "--capture-dev %d\n", config->capture_dev);
1385 pj_strcat2(&cfg, line);
1386 }
1387 if (config->playback_dev != PJSUA_INVALID_ID) {
1388 pj_ansi_sprintf(line, "--playback-dev %d\n", config->playback_dev);
1389 pj_strcat2(&cfg, line);
1390 }
Benny Prijono1ebd6142006-10-19 15:48:02 +00001391
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001392 /* Media clock rate. */
Benny Prijono70972992006-08-05 11:13:58 +00001393 if (config->media_cfg.clock_rate != PJSUA_DEFAULT_CLOCK_RATE) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001394 pj_ansi_sprintf(line, "--clock-rate %d\n",
Benny Prijono0498d902006-06-19 14:49:14 +00001395 config->media_cfg.clock_rate);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001396 pj_strcat2(&cfg, line);
Benny Prijono70972992006-08-05 11:13:58 +00001397 } else {
1398 pj_ansi_sprintf(line, "#using default --clock-rate %d\n",
1399 config->media_cfg.clock_rate);
1400 pj_strcat2(&cfg, line);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001401 }
Benny Prijono70972992006-08-05 11:13:58 +00001402
Benny Prijonof3758ee2008-02-26 15:32:16 +00001403 if (config->media_cfg.snd_clock_rate != config->media_cfg.clock_rate) {
1404 pj_ansi_sprintf(line, "--snd-clock-rate %d\n",
1405 config->media_cfg.snd_clock_rate);
1406 pj_strcat2(&cfg, line);
1407 } else {
1408 pj_ansi_sprintf(line, "#using default --snd-clock-rate %d\n",
1409 config->media_cfg.snd_clock_rate);
1410 pj_strcat2(&cfg, line);
1411 }
1412
Benny Prijono70972992006-08-05 11:13:58 +00001413 /* quality */
1414 if (config->media_cfg.quality != PJSUA_DEFAULT_CODEC_QUALITY) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001415 pj_ansi_sprintf(line, "--quality %d\n",
Benny Prijono0498d902006-06-19 14:49:14 +00001416 config->media_cfg.quality);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001417 pj_strcat2(&cfg, line);
Benny Prijono70972992006-08-05 11:13:58 +00001418 } else {
1419 pj_ansi_sprintf(line, "#using default --quality %d\n",
1420 config->media_cfg.quality);
1421 pj_strcat2(&cfg, line);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001422 }
Benny Prijono0498d902006-06-19 14:49:14 +00001423
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001424
1425 /* ptime */
Benny Prijono2adfe292007-05-11 10:36:08 +00001426 if (config->media_cfg.ptime) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001427 pj_ansi_sprintf(line, "--ptime %d\n",
Benny Prijono2adfe292007-05-11 10:36:08 +00001428 config->media_cfg.ptime);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001429 pj_strcat2(&cfg, line);
1430 }
1431
Benny Prijono70972992006-08-05 11:13:58 +00001432 /* no-vad */
1433 if (config->media_cfg.no_vad) {
1434 pj_strcat2(&cfg, "--no-vad\n");
1435 }
1436
1437 /* ec-tail */
1438 if (config->media_cfg.ec_tail_len != PJSUA_DEFAULT_EC_TAIL_LEN) {
1439 pj_ansi_sprintf(line, "--ec-tail %d\n",
1440 config->media_cfg.ec_tail_len);
1441 pj_strcat2(&cfg, line);
1442 } else {
1443 pj_ansi_sprintf(line, "#using default --ec-tail %d\n",
1444 config->media_cfg.ec_tail_len);
1445 pj_strcat2(&cfg, line);
1446 }
1447
1448
1449 /* ilbc-mode */
1450 if (config->media_cfg.ilbc_mode != PJSUA_DEFAULT_ILBC_MODE) {
1451 pj_ansi_sprintf(line, "--ilbc-mode %d\n",
1452 config->media_cfg.ilbc_mode);
1453 pj_strcat2(&cfg, line);
1454 } else {
1455 pj_ansi_sprintf(line, "#using default --ilbc-mode %d\n",
1456 config->media_cfg.ilbc_mode);
1457 pj_strcat2(&cfg, line);
1458 }
1459
1460 /* RTP drop */
1461 if (config->media_cfg.tx_drop_pct) {
1462 pj_ansi_sprintf(line, "--tx-drop-pct %d\n",
1463 config->media_cfg.tx_drop_pct);
1464 pj_strcat2(&cfg, line);
1465
1466 }
1467 if (config->media_cfg.rx_drop_pct) {
1468 pj_ansi_sprintf(line, "--rx-drop-pct %d\n",
1469 config->media_cfg.rx_drop_pct);
1470 pj_strcat2(&cfg, line);
1471
1472 }
1473
1474
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001475 /* Start RTP port. */
1476 pj_ansi_sprintf(line, "--rtp-port %d\n",
1477 config->rtp_cfg.port);
1478 pj_strcat2(&cfg, line);
1479
1480 /* Add codec. */
1481 for (i=0; i<config->codec_cnt; ++i) {
1482 pj_ansi_sprintf(line, "--add-codec %s\n",
1483 config->codec_arg[i].ptr);
1484 pj_strcat2(&cfg, line);
1485 }
Benny Prijonofce28542007-12-09 15:41:10 +00001486 /* Disable codec */
1487 for (i=0; i<config->codec_dis_cnt; ++i) {
1488 pj_ansi_sprintf(line, "--dis-codec %s\n",
1489 config->codec_dis[i].ptr);
1490 pj_strcat2(&cfg, line);
1491 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001492
1493 pj_strcat2(&cfg, "\n#\n# User agent:\n#\n");
1494
1495 /* Auto-answer. */
1496 if (config->auto_answer != 0) {
1497 pj_ansi_sprintf(line, "--auto-answer %d\n",
1498 config->auto_answer);
1499 pj_strcat2(&cfg, line);
1500 }
1501
1502 /* Max calls. */
1503 pj_ansi_sprintf(line, "--max-calls %d\n",
1504 config->cfg.max_calls);
1505 pj_strcat2(&cfg, line);
1506
1507 /* Uas-duration. */
Benny Prijono804ff0a2006-09-14 11:17:48 +00001508 if (config->duration != NO_LIMIT) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001509 pj_ansi_sprintf(line, "--duration %d\n",
1510 config->duration);
1511 pj_strcat2(&cfg, line);
1512 }
1513
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001514 /* norefersub ? */
1515 if (config->no_refersub) {
1516 pj_strcat2(&cfg, "--norefersub\n");
1517 }
1518
Benny Prijonofce28542007-12-09 15:41:10 +00001519 if (pjsip_use_compact_form)
1520 {
1521 pj_strcat2(&cfg, "--use-compact-form\n");
1522 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001523
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001524 pj_strcat2(&cfg, "\n#\n# Buddies:\n#\n");
1525
1526 /* Add buddies. */
1527 for (i=0; i<config->buddy_cnt; ++i) {
1528 pj_ansi_sprintf(line, "--add-buddy %.*s\n",
1529 (int)config->buddy_cfg[i].uri.slen,
1530 config->buddy_cfg[i].uri.ptr);
1531 pj_strcat2(&cfg, line);
1532 }
1533
1534
1535 *(cfg.ptr + cfg.slen) = '\0';
1536 return cfg.slen;
1537}
1538
1539
1540/*
1541 * Dump application states.
1542 */
1543static void app_dump(pj_bool_t detail)
1544{
Benny Prijonoda9785b2007-04-02 20:43:06 +00001545 pjsua_dump(detail);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001546}
1547
1548
1549/*****************************************************************************
1550 * Console application
1551 */
1552
1553/*
1554 * Find next call when current call is disconnected or when user
1555 * press ']'
1556 */
1557static pj_bool_t find_next_call(void)
1558{
1559 int i, max;
1560
1561 max = pjsua_call_get_max_count();
1562 for (i=current_call+1; i<max; ++i) {
1563 if (pjsua_call_is_active(i)) {
1564 current_call = i;
1565 return PJ_TRUE;
1566 }
1567 }
1568
1569 for (i=0; i<current_call; ++i) {
1570 if (pjsua_call_is_active(i)) {
1571 current_call = i;
1572 return PJ_TRUE;
1573 }
1574 }
1575
1576 current_call = PJSUA_INVALID_ID;
1577 return PJ_FALSE;
1578}
1579
1580
1581/*
1582 * Find previous call when user press '['
1583 */
1584static pj_bool_t find_prev_call(void)
1585{
1586 int i, max;
1587
1588 max = pjsua_call_get_max_count();
1589 for (i=current_call-1; i>=0; --i) {
1590 if (pjsua_call_is_active(i)) {
1591 current_call = i;
1592 return PJ_TRUE;
1593 }
1594 }
1595
1596 for (i=max-1; i>current_call; --i) {
1597 if (pjsua_call_is_active(i)) {
1598 current_call = i;
1599 return PJ_TRUE;
1600 }
1601 }
1602
1603 current_call = PJSUA_INVALID_ID;
1604 return PJ_FALSE;
1605}
1606
1607
Benny Prijono804ff0a2006-09-14 11:17:48 +00001608/* Callback from timer when the maximum call duration has been
1609 * exceeded.
1610 */
1611static void call_timeout_callback(pj_timer_heap_t *timer_heap,
1612 struct pj_timer_entry *entry)
1613{
1614 pjsua_call_id call_id = entry->id;
1615 pjsua_msg_data msg_data;
1616 pjsip_generic_string_hdr warn;
1617 pj_str_t hname = pj_str("Warning");
1618 pj_str_t hvalue = pj_str("399 pjsua \"Call duration exceeded\"");
1619
1620 PJ_UNUSED_ARG(timer_heap);
1621
Benny Prijono148c9dd2006-09-19 13:37:53 +00001622 if (call_id == PJSUA_INVALID_ID) {
1623 PJ_LOG(1,(THIS_FILE, "Invalid call ID in timer callback"));
1624 return;
1625 }
1626
Benny Prijono804ff0a2006-09-14 11:17:48 +00001627 /* Add warning header */
1628 pjsua_msg_data_init(&msg_data);
1629 pjsip_generic_string_hdr_init2(&warn, &hname, &hvalue);
1630 pj_list_push_back(&msg_data.hdr_list, &warn);
1631
1632 /* Call duration has been exceeded; disconnect the call */
1633 PJ_LOG(3,(THIS_FILE, "Duration (%d seconds) has been exceeded "
1634 "for call %d, disconnecting the call",
1635 app_config.duration, call_id));
1636 entry->id = PJSUA_INVALID_ID;
1637 pjsua_call_hangup(call_id, 200, NULL, &msg_data);
1638}
1639
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001640
1641/*
1642 * Handler when invite state has changed.
1643 */
1644static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
1645{
1646 pjsua_call_info call_info;
1647
1648 PJ_UNUSED_ARG(e);
1649
1650 pjsua_call_get_info(call_id, &call_info);
1651
1652 if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
1653
Benny Prijono804ff0a2006-09-14 11:17:48 +00001654 /* Cancel duration timer, if any */
1655 if (app_config.call_data[call_id].timer.id != PJSUA_INVALID_ID) {
1656 struct call_data *cd = &app_config.call_data[call_id];
1657 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
1658
1659 cd->timer.id = PJSUA_INVALID_ID;
1660 pjsip_endpt_cancel_timer(endpt, &cd->timer);
1661 }
1662
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001663 PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]",
1664 call_id,
1665 call_info.last_status,
1666 call_info.last_status_text.ptr));
1667
1668 if (call_id == current_call) {
1669 find_next_call();
1670 }
1671
Benny Prijono4be63b52006-11-25 14:50:25 +00001672 /* Dump media state upon disconnected */
1673 if (1) {
1674 char buf[1024];
1675 pjsua_call_dump(call_id, PJ_TRUE, buf,
1676 sizeof(buf), " ");
1677 PJ_LOG(5,(THIS_FILE,
1678 "Call %d disconnected, dumping media stats\n%s",
1679 call_id, buf));
1680 }
1681
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001682 } else {
1683
Benny Prijono804ff0a2006-09-14 11:17:48 +00001684 if (app_config.duration!=NO_LIMIT &&
1685 call_info.state == PJSIP_INV_STATE_CONFIRMED)
1686 {
1687 /* Schedule timer to hangup call after the specified duration */
1688 struct call_data *cd = &app_config.call_data[call_id];
1689 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
1690 pj_time_val delay;
1691
1692 cd->timer.id = call_id;
1693 delay.sec = app_config.duration;
1694 delay.msec = 0;
1695 pjsip_endpt_schedule_timer(endpt, &cd->timer, &delay);
1696 }
1697
Benny Prijono4be63b52006-11-25 14:50:25 +00001698 if (call_info.state == PJSIP_INV_STATE_EARLY) {
1699 int code;
1700 pj_str_t reason;
1701 pjsip_msg *msg;
1702
1703 /* This can only occur because of TX or RX message */
1704 pj_assert(e->type == PJSIP_EVENT_TSX_STATE);
1705
1706 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
1707 msg = e->body.tsx_state.src.rdata->msg_info.msg;
1708 } else {
1709 msg = e->body.tsx_state.src.tdata->msg;
1710 }
1711
1712 code = msg->line.status.code;
1713 reason = msg->line.status.reason;
1714
1715 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s (%d %.*s)",
1716 call_id, call_info.state_text.ptr,
1717 code, (int)reason.slen, reason.ptr));
1718 } else {
1719 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
1720 call_id,
1721 call_info.state_text.ptr));
1722 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001723
1724 if (current_call==PJSUA_INVALID_ID)
1725 current_call = call_id;
1726
1727 }
1728}
1729
1730
1731/**
1732 * Handler when there is incoming call.
1733 */
1734static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
1735 pjsip_rx_data *rdata)
1736{
1737 pjsua_call_info call_info;
1738
1739 PJ_UNUSED_ARG(acc_id);
1740 PJ_UNUSED_ARG(rdata);
1741
1742 pjsua_call_get_info(call_id, &call_info);
1743
1744 if (app_config.auto_answer > 0) {
1745 pjsua_call_answer(call_id, app_config.auto_answer, NULL, NULL);
1746 }
1747
1748 if (app_config.auto_answer < 200) {
1749 PJ_LOG(3,(THIS_FILE,
1750 "Incoming call for account %d!\n"
1751 "From: %s\n"
1752 "To: %s\n"
1753 "Press a to answer or h to reject call",
1754 acc_id,
1755 call_info.remote_info.ptr,
1756 call_info.local_info.ptr));
1757 }
1758}
1759
1760
1761/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001762 * Handler when a transaction within a call has changed state.
1763 */
1764static void on_call_tsx_state(pjsua_call_id call_id,
1765 pjsip_transaction *tsx,
1766 pjsip_event *e)
1767{
1768 const pjsip_method info_method =
1769 {
1770 PJSIP_OTHER_METHOD,
1771 { "INFO", 4 }
1772 };
1773
1774 if (pjsip_method_cmp(&tsx->method, &info_method)==0) {
1775 /*
1776 * Handle INFO method.
1777 */
1778 if (tsx->role == PJSIP_ROLE_UAC &&
1779 (tsx->state == PJSIP_TSX_STATE_COMPLETED ||
Benny Prijonob071a782007-10-10 13:12:37 +00001780 (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
1781 e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED)))
Benny Prijonofeb69f42007-10-05 09:12:26 +00001782 {
1783 /* Status of outgoing INFO request */
1784 if (tsx->status_code >= 200 && tsx->status_code < 300) {
1785 PJ_LOG(4,(THIS_FILE,
1786 "Call %d: DTMF sent successfully with INFO",
1787 call_id));
1788 } else if (tsx->status_code >= 300) {
1789 PJ_LOG(4,(THIS_FILE,
1790 "Call %d: Failed to send DTMF with INFO: %d/%.*s",
1791 call_id,
1792 tsx->status_code,
1793 (int)tsx->status_text.slen,
1794 tsx->status_text.ptr));
1795 }
1796 } else if (tsx->role == PJSIP_ROLE_UAS &&
1797 tsx->state == PJSIP_TSX_STATE_TRYING)
1798 {
1799 /* Answer incoming INFO with 200/OK */
1800 pjsip_rx_data *rdata;
1801 pjsip_tx_data *tdata;
1802 pj_status_t status;
1803
1804 rdata = e->body.tsx_state.src.rdata;
1805
1806 if (rdata->msg_info.msg->body) {
1807 status = pjsip_endpt_create_response(tsx->endpt, rdata,
1808 200, NULL, &tdata);
1809 if (status == PJ_SUCCESS)
1810 status = pjsip_tsx_send_msg(tsx, tdata);
1811
1812 PJ_LOG(3,(THIS_FILE, "Call %d: incoming INFO:\n%.*s",
1813 call_id,
1814 (int)rdata->msg_info.msg->body->len,
1815 rdata->msg_info.msg->body->data));
1816 } else {
1817 status = pjsip_endpt_create_response(tsx->endpt, rdata,
1818 400, NULL, &tdata);
1819 if (status == PJ_SUCCESS)
1820 status = pjsip_tsx_send_msg(tsx, tdata);
1821 }
1822 }
1823 }
1824}
1825
1826
1827/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001828 * Callback on media state changed event.
1829 * The action may connect the call to sound device, to file, or
1830 * to loop the call.
1831 */
1832static void on_call_media_state(pjsua_call_id call_id)
1833{
1834 pjsua_call_info call_info;
1835
1836 pjsua_call_get_info(call_id, &call_info);
1837
1838 if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
1839 pj_bool_t connect_sound = PJ_TRUE;
1840
1841 /* Loopback sound, if desired */
1842 if (app_config.auto_loop) {
1843 pjsua_conf_connect(call_info.conf_slot, call_info.conf_slot);
1844 connect_sound = PJ_FALSE;
Benny Prijono1ebd6142006-10-19 15:48:02 +00001845
1846 /* Automatically record conversation, if desired */
1847 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1848 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
1849 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001850 }
1851
1852 /* Stream a file, if desired */
1853 if (app_config.auto_play && app_config.wav_port != PJSUA_INVALID_ID) {
1854 pjsua_conf_connect(app_config.wav_port, call_info.conf_slot);
1855 connect_sound = PJ_FALSE;
1856 }
1857
Benny Prijono7ca96da2006-08-07 12:11:40 +00001858 /* Put call in conference with other calls, if desired */
1859 if (app_config.auto_conf) {
1860 pjsua_call_id call_ids[PJSUA_MAX_CALLS];
Benny Prijono10861bc2006-08-07 13:22:43 +00001861 unsigned call_cnt=PJ_ARRAY_SIZE(call_ids);
Benny Prijono7ca96da2006-08-07 12:11:40 +00001862 unsigned i;
1863
1864 /* Get all calls, and establish media connection between
1865 * this call and other calls.
1866 */
1867 pjsua_enum_calls(call_ids, &call_cnt);
Benny Prijono10861bc2006-08-07 13:22:43 +00001868
Benny Prijono7ca96da2006-08-07 12:11:40 +00001869 for (i=0; i<call_cnt; ++i) {
1870 if (call_ids[i] == call_id)
1871 continue;
1872
1873 if (!pjsua_call_has_media(call_ids[i]))
1874 continue;
1875
1876 pjsua_conf_connect(call_info.conf_slot,
1877 pjsua_call_get_conf_port(call_ids[i]));
1878 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
1879 call_info.conf_slot);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001880
1881 /* Automatically record conversation, if desired */
1882 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1883 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
1884 app_config.rec_port);
1885 }
1886
Benny Prijono7ca96da2006-08-07 12:11:40 +00001887 }
1888
1889 /* Also connect call to local sound device */
1890 connect_sound = PJ_TRUE;
1891 }
1892
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001893 /* Otherwise connect to sound device */
1894 if (connect_sound) {
1895 pjsua_conf_connect(call_info.conf_slot, 0);
1896 pjsua_conf_connect(0, call_info.conf_slot);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001897
1898 /* Automatically record conversation, if desired */
1899 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1900 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
1901 pjsua_conf_connect(0, app_config.rec_port);
1902 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001903 }
1904
1905 PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id));
1906
1907 } else if (call_info.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
1908 PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local",
1909 call_id));
1910 } else if (call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD) {
1911 PJ_LOG(3,(THIS_FILE,
1912 "Media for call %d is suspended (hold) by remote",
1913 call_id));
Benny Prijono096c56c2007-09-15 08:30:16 +00001914 } else if (call_info.media_status == PJSUA_CALL_MEDIA_ERROR) {
1915 pj_str_t reason = pj_str("ICE negotiation failed");
1916
1917 PJ_LOG(1,(THIS_FILE,
1918 "Media has reported error, disconnecting call"));
1919
1920 pjsua_call_hangup(call_id, 500, &reason, NULL);
1921
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001922 } else {
1923 PJ_LOG(3,(THIS_FILE,
1924 "Media for call %d is inactive",
1925 call_id));
1926 }
1927}
1928
Benny Prijono0875ae82006-12-26 00:11:48 +00001929/*
1930 * DTMF callback.
1931 */
1932static void call_on_dtmf_callback(pjsua_call_id call_id, int dtmf)
1933{
1934 PJ_LOG(3,(THIS_FILE, "Incoming DTMF on call %d: %c", call_id, dtmf));
1935}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001936
1937/*
1938 * Handler registration status has changed.
1939 */
1940static void on_reg_state(pjsua_acc_id acc_id)
1941{
1942 PJ_UNUSED_ARG(acc_id);
1943
1944 // Log already written.
1945}
1946
1947
1948/*
1949 * Handler on buddy state changed.
1950 */
1951static void on_buddy_state(pjsua_buddy_id buddy_id)
1952{
1953 pjsua_buddy_info info;
1954 pjsua_buddy_get_info(buddy_id, &info);
1955
1956 PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s",
1957 (int)info.uri.slen,
1958 info.uri.ptr,
1959 (int)info.status_text.slen,
1960 info.status_text.ptr));
1961}
1962
1963
1964/**
1965 * Incoming IM message (i.e. MESSAGE request)!
1966 */
1967static void on_pager(pjsua_call_id call_id, const pj_str_t *from,
1968 const pj_str_t *to, const pj_str_t *contact,
1969 const pj_str_t *mime_type, const pj_str_t *text)
1970{
1971 /* Note: call index may be -1 */
1972 PJ_UNUSED_ARG(call_id);
1973 PJ_UNUSED_ARG(to);
1974 PJ_UNUSED_ARG(contact);
1975 PJ_UNUSED_ARG(mime_type);
1976
Benny Prijonof4b538d2007-05-14 16:45:20 +00001977 PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s (%.*s)",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001978 (int)from->slen, from->ptr,
Benny Prijonof4b538d2007-05-14 16:45:20 +00001979 (int)text->slen, text->ptr,
1980 (int)mime_type->slen, mime_type->ptr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001981}
1982
1983
1984/**
1985 * Received typing indication
1986 */
1987static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
1988 const pj_str_t *to, const pj_str_t *contact,
1989 pj_bool_t is_typing)
1990{
1991 PJ_UNUSED_ARG(call_id);
1992 PJ_UNUSED_ARG(to);
1993 PJ_UNUSED_ARG(contact);
1994
1995 PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
1996 (int)from->slen, from->ptr,
1997 (is_typing?"is typing..":"has stopped typing")));
1998}
1999
2000
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002001/**
2002 * Call transfer request status.
2003 */
2004static void on_call_transfer_status(pjsua_call_id call_id,
2005 int status_code,
2006 const pj_str_t *status_text,
2007 pj_bool_t final,
2008 pj_bool_t *p_cont)
2009{
2010 PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
2011 call_id, status_code,
2012 (int)status_text->slen, status_text->ptr,
2013 (final ? "[final]" : "")));
2014
2015 if (status_code/100 == 2) {
2016 PJ_LOG(3,(THIS_FILE,
2017 "Call %d: call transfered successfully, disconnecting call",
2018 call_id));
2019 pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
2020 *p_cont = PJ_FALSE;
2021 }
2022}
2023
2024
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002025/*
Benny Prijonof7b1c392006-11-11 16:46:34 +00002026 * Notification that call is being replaced.
2027 */
2028static void on_call_replaced(pjsua_call_id old_call_id,
2029 pjsua_call_id new_call_id)
2030{
2031 pjsua_call_info old_ci, new_ci;
2032
2033 pjsua_call_get_info(old_call_id, &old_ci);
2034 pjsua_call_get_info(new_call_id, &new_ci);
2035
2036 PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by "
2037 "call %d with %.*s",
2038 old_call_id,
2039 (int)old_ci.remote_info.slen, old_ci.remote_info.ptr,
2040 new_call_id,
2041 (int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
2042}
2043
2044
2045/*
Benny Prijono6ba8c542007-10-16 01:34:14 +00002046 * NAT type detection callback.
2047 */
2048static void on_nat_detect(const pj_stun_nat_detect_result *res)
2049{
2050 if (res->status != PJ_SUCCESS) {
2051 pjsua_perror(THIS_FILE, "NAT detection failed", res->status);
2052 } else {
2053 PJ_LOG(3, (THIS_FILE, "NAT detected as %s", res->nat_type_name));
2054 }
2055}
2056
2057
2058/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002059 * Print buddy list.
2060 */
2061static void print_buddy_list(void)
2062{
2063 pjsua_buddy_id ids[64];
2064 int i;
2065 unsigned count = PJ_ARRAY_SIZE(ids);
2066
2067 puts("Buddy list:");
2068
2069 pjsua_enum_buddies(ids, &count);
2070
2071 if (count == 0)
2072 puts(" -none-");
2073 else {
2074 for (i=0; i<(int)count; ++i) {
2075 pjsua_buddy_info info;
2076
2077 if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
2078 continue;
2079
Benny Prijono4461c7d2007-08-25 13:36:15 +00002080 printf(" [%2d] <%.*s> %.*s\n",
2081 ids[i]+1,
2082 (int)info.status_text.slen,
2083 info.status_text.ptr,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002084 (int)info.uri.slen,
2085 info.uri.ptr);
2086 }
2087 }
2088 puts("");
2089}
2090
2091
2092/*
2093 * Print account status.
2094 */
2095static void print_acc_status(int acc_id)
2096{
2097 char buf[80];
2098 pjsua_acc_info info;
2099
2100 pjsua_acc_get_info(acc_id, &info);
2101
2102 if (!info.has_registration) {
2103 pj_ansi_snprintf(buf, sizeof(buf), "%.*s",
2104 (int)info.status_text.slen,
2105 info.status_text.ptr);
2106
2107 } else {
2108 pj_ansi_snprintf(buf, sizeof(buf),
2109 "%d/%.*s (expires=%d)",
2110 info.status,
2111 (int)info.status_text.slen,
2112 info.status_text.ptr,
2113 info.expires);
2114
2115 }
2116
2117 printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
2118 acc_id, (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
Benny Prijono4461c7d2007-08-25 13:36:15 +00002119 printf(" Online status: %.*s\n",
2120 (int)info.online_status_text.slen,
2121 info.online_status_text.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002122}
2123
2124
2125/*
2126 * Show a bit of help.
2127 */
2128static void keystroke_help(void)
2129{
2130 pjsua_acc_id acc_ids[16];
2131 unsigned count = PJ_ARRAY_SIZE(acc_ids);
2132 int i;
2133
2134 printf(">>>>\n");
2135
2136 pjsua_enum_accs(acc_ids, &count);
2137
2138 printf("Account list:\n");
2139 for (i=0; i<(int)count; ++i)
2140 print_acc_status(acc_ids[i]);
2141
2142 print_buddy_list();
2143
2144 //puts("Commands:");
2145 puts("+=============================================================================+");
2146 puts("| Call Commands: | Buddy, IM & Presence: | Account: |");
2147 puts("| | | |");
2148 puts("| m Make new call | +b Add new buddy .| +a Add new accnt |");
2149 puts("| M Make multiple calls | -b Delete buddy | -a Delete accnt. |");
Benny Prijono4461c7d2007-08-25 13:36:15 +00002150 puts("| a Answer call | i Send IM | !a Modify accnt. |");
2151 puts("| h Hangup call (ha=all) | s Subscribe presence | rr (Re-)register |");
2152 puts("| H Hold call | u Unsubscribe presence | ru Unregister |");
2153 puts("| v re-inVite (release hold) | t ToGgle Online status | > Cycle next ac.|");
Benny Prijonoc08682e2007-10-04 06:17:58 +00002154 puts("| U send UPDATE | T Set online status | < Cycle prev ac.|");
2155 puts("| ],[ Select next/prev call +--------------------------+-------------------+");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002156 puts("| x Xfer call | Media Commands: | Status & Config: |");
Benny Prijonof7b1c392006-11-11 16:46:34 +00002157 puts("| X Xfer with Replaces | | |");
Benny Prijonoc08682e2007-10-04 06:17:58 +00002158 puts("| # Send RFC 2833 DTMF | cl List ports | d Dump status |");
2159 puts("| * Send DTMF with INFO | cc Connect port | dd Dump detailed |");
2160 puts("| dq Dump curr. call quality | cd Disconnect port | dc Dump config |");
2161 puts("| | V Adjust audio Volume | f Save config |");
2162 puts("| S Send arbitrary REQUEST | Cp Codec priorities | f Save config |");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002163 puts("+------------------------------+--------------------------+-------------------+");
Benny Prijono438e65b2007-11-03 21:42:10 +00002164 puts("| q QUIT sleep N: console sleep for N ms n: detect NAT type |");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002165 puts("+=============================================================================+");
Benny Prijono48af79c2006-07-22 12:49:17 +00002166
2167 i = pjsua_call_get_count();
2168 printf("You have %d active call%s\n", i, (i>1?"s":""));
Benny Prijonof7b1c392006-11-11 16:46:34 +00002169
2170 if (current_call != PJSUA_INVALID_ID) {
2171 pjsua_call_info ci;
2172 if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
2173 printf("Current call id=%d to %.*s [%.*s]\n", current_call,
2174 (int)ci.remote_info.slen, ci.remote_info.ptr,
2175 (int)ci.state_text.slen, ci.state_text.ptr);
2176 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002177}
2178
2179
2180/*
2181 * Input simple string
2182 */
2183static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
2184{
2185 char *p;
2186
2187 printf("%s (empty to cancel): ", title); fflush(stdout);
2188 fgets(buf, len, stdin);
2189
2190 /* Remove trailing newlines. */
2191 for (p=buf; ; ++p) {
2192 if (*p=='\r' || *p=='\n') *p='\0';
2193 else if (!*p) break;
2194 }
2195
2196 if (!*buf)
2197 return PJ_FALSE;
2198
2199 return PJ_TRUE;
2200}
2201
2202
2203#define NO_NB -2
2204struct input_result
2205{
2206 int nb_result;
2207 char *uri_result;
2208};
2209
2210
2211/*
2212 * Input URL.
2213 */
2214static void ui_input_url(const char *title, char *buf, int len,
2215 struct input_result *result)
2216{
2217 result->nb_result = NO_NB;
2218 result->uri_result = NULL;
2219
2220 print_buddy_list();
2221
2222 printf("Choices:\n"
2223 " 0 For current dialog.\n"
2224 " -1 All %d buddies in buddy list\n"
2225 " [1 -%2d] Select from buddy list\n"
2226 " URL An URL\n"
2227 " <Enter> Empty input (or 'q') to cancel\n"
2228 , pjsua_get_buddy_count(), pjsua_get_buddy_count());
2229 printf("%s: ", title);
2230
2231 fflush(stdout);
2232 fgets(buf, len, stdin);
2233 len = strlen(buf);
2234
2235 /* Left trim */
2236 while (pj_isspace(*buf)) {
2237 ++buf;
2238 --len;
2239 }
2240
2241 /* Remove trailing newlines */
2242 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
2243 buf[--len] = '\0';
2244
2245 if (len == 0 || buf[0]=='q')
2246 return;
2247
2248 if (pj_isdigit(*buf) || *buf=='-') {
2249
2250 int i;
2251
2252 if (*buf=='-')
2253 i = 1;
2254 else
2255 i = 0;
2256
2257 for (; i<len; ++i) {
2258 if (!pj_isdigit(buf[i])) {
2259 puts("Invalid input");
2260 return;
2261 }
2262 }
2263
2264 result->nb_result = my_atoi(buf);
2265
2266 if (result->nb_result >= 0 &&
2267 result->nb_result <= (int)pjsua_get_buddy_count())
2268 {
2269 return;
2270 }
2271 if (result->nb_result == -1)
2272 return;
2273
2274 puts("Invalid input");
2275 result->nb_result = NO_NB;
2276 return;
2277
2278 } else {
2279 pj_status_t status;
2280
2281 if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
2282 pjsua_perror(THIS_FILE, "Invalid URL", status);
2283 return;
2284 }
2285
2286 result->uri_result = buf;
2287 }
2288}
2289
2290/*
2291 * List the ports in conference bridge
2292 */
2293static void conf_list(void)
2294{
2295 unsigned i, count;
2296 pjsua_conf_port_id id[PJSUA_MAX_CALLS];
2297
2298 printf("Conference ports:\n");
2299
2300 count = PJ_ARRAY_SIZE(id);
2301 pjsua_enum_conf_ports(id, &count);
2302
2303 for (i=0; i<count; ++i) {
2304 char txlist[PJSUA_MAX_CALLS*4+10];
2305 unsigned j;
2306 pjsua_conf_port_info info;
2307
2308 pjsua_conf_get_port_info(id[i], &info);
2309
2310 txlist[0] = '\0';
2311 for (j=0; j<info.listener_cnt; ++j) {
2312 char s[10];
2313 pj_ansi_sprintf(s, "#%d ", info.listeners[j]);
2314 pj_ansi_strcat(txlist, s);
2315 }
2316 printf("Port #%02d[%2dKHz/%dms] %20.*s transmitting to: %s\n",
2317 info.slot_id,
2318 info.clock_rate/1000,
2319 info.samples_per_frame * 1000 / info.clock_rate,
2320 (int)info.name.slen,
2321 info.name.ptr,
2322 txlist);
2323
2324 }
2325 puts("");
2326}
2327
2328
2329/*
Benny Prijono56315612006-07-18 14:39:40 +00002330 * Send arbitrary request to remote host
2331 */
2332static void send_request(char *cstr_method, const pj_str_t *dst_uri)
2333{
2334 pj_str_t str_method;
2335 pjsip_method method;
2336 pjsip_tx_data *tdata;
Benny Prijono56315612006-07-18 14:39:40 +00002337 pjsip_endpoint *endpt;
2338 pj_status_t status;
2339
2340 endpt = pjsua_get_pjsip_endpt();
2341
2342 str_method = pj_str(cstr_method);
2343 pjsip_method_init_np(&method, &str_method);
2344
Benny Prijonofff245c2007-04-02 11:44:47 +00002345 status = pjsua_acc_create_request(current_acc, &method, dst_uri, &tdata);
Benny Prijono56315612006-07-18 14:39:40 +00002346
Benny Prijonob988d762007-12-05 04:30:04 +00002347 status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL);
Benny Prijono56315612006-07-18 14:39:40 +00002348 if (status != PJ_SUCCESS) {
Benny Prijonob988d762007-12-05 04:30:04 +00002349 pjsua_perror(THIS_FILE, "Unable to send request", status);
Benny Prijono56315612006-07-18 14:39:40 +00002350 return;
2351 }
2352}
2353
2354
2355/*
Benny Prijono4461c7d2007-08-25 13:36:15 +00002356 * Change extended online status.
2357 */
2358static void change_online_status(void)
2359{
2360 char menuin[32];
2361 pj_bool_t online_status;
2362 pjrpid_element elem;
2363 int i, choice;
2364
2365 enum {
2366 AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX
2367 };
2368
2369 struct opt {
2370 int id;
2371 char *name;
2372 } opts[] = {
2373 { AVAILABLE, "Available" },
2374 { BUSY, "Busy"},
2375 { OTP, "On the phone"},
2376 { IDLE, "Idle"},
2377 { AWAY, "Away"},
2378 { BRB, "Be right back"},
2379 { OFFLINE, "Offline"}
2380 };
2381
2382 printf("\n"
2383 "Choices:\n");
2384 for (i=0; i<PJ_ARRAY_SIZE(opts); ++i) {
2385 printf(" %d %s\n", opts[i].id+1, opts[i].name);
2386 }
2387
2388 if (!simple_input("Select status", menuin, sizeof(menuin)))
2389 return;
2390
2391 choice = atoi(menuin) - 1;
2392 if (choice < 0 || choice >= OPT_MAX) {
2393 puts("Invalid selection");
2394 return;
2395 }
2396
2397 pj_bzero(&elem, sizeof(elem));
2398 elem.type = PJRPID_ELEMENT_TYPE_PERSON;
2399
2400 online_status = PJ_TRUE;
2401
2402 switch (choice) {
2403 case AVAILABLE:
2404 break;
2405 case BUSY:
2406 elem.activity = PJRPID_ACTIVITY_BUSY;
2407 elem.note = pj_str("Busy");
2408 break;
2409 case OTP:
2410 elem.activity = PJRPID_ACTIVITY_BUSY;
2411 elem.note = pj_str("On the phone");
2412 break;
2413 case IDLE:
2414 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
2415 elem.note = pj_str("Idle");
2416 break;
2417 case AWAY:
2418 elem.activity = PJRPID_ACTIVITY_AWAY;
2419 elem.note = pj_str("Away");
2420 break;
2421 case BRB:
2422 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
2423 elem.note = pj_str("Be right back");
2424 break;
2425 case OFFLINE:
2426 online_status = PJ_FALSE;
2427 break;
2428 }
2429
2430 pjsua_acc_set_online_status2(current_acc, online_status, &elem);
2431}
2432
2433
2434/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00002435 * Change codec priorities.
2436 */
2437static void manage_codec_prio(void)
2438{
2439 pjsua_codec_info c[32];
2440 unsigned i, count = PJ_ARRAY_SIZE(c);
2441 char input[32];
2442 char *codec, *prio;
2443 pj_str_t id;
2444 int new_prio;
2445 pj_status_t status;
2446
2447 printf("List of codecs:\n");
2448
2449 pjsua_enum_codecs(c, &count);
2450 for (i=0; i<count; ++i) {
2451 printf(" %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen,
2452 c[i].codec_id.ptr);
2453 }
2454
2455 puts("");
2456 puts("Enter codec name and its new priority (e.g. \"speex/16000 200\"), empty to cancel:");
2457
2458 printf("Codec name and priority: ");
2459 fgets(input, sizeof(input), stdin);
2460 if (input[0]=='\r' || input[0]=='\n') {
2461 puts("Done");
2462 return;
2463 }
2464
2465 codec = strtok(input, " \t\r\n");
2466 prio = strtok(NULL, " \r\n");
2467
2468 if (!codec || !prio) {
2469 puts("Invalid input");
2470 return;
2471 }
2472
2473 new_prio = atoi(prio);
2474 if (new_prio < 0)
2475 new_prio = 0;
2476 else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST)
2477 new_prio = PJMEDIA_CODEC_PRIO_HIGHEST;
2478
2479 status = pjsua_codec_set_priority(pj_cstr(&id, codec),
2480 (pj_uint8_t)new_prio);
2481 if (status != PJ_SUCCESS)
2482 pjsua_perror(THIS_FILE, "Error setting codec priority", status);
2483}
2484
2485
2486/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002487 * Main "user interface" loop.
2488 */
2489void console_app_main(const pj_str_t *uri_to_call)
2490{
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002491 char menuin[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002492 char buf[128];
2493 char text[128];
2494 int i, count;
2495 char *uri;
2496 pj_str_t tmp;
2497 struct input_result result;
2498 pjsua_call_info call_info;
2499 pjsua_acc_info acc_info;
2500
2501
2502 /* If user specifies URI to call, then call the URI */
2503 if (uri_to_call->slen) {
2504 pjsua_call_make_call( current_acc, uri_to_call, 0, NULL, NULL, NULL);
2505 }
2506
2507 keystroke_help();
2508
2509 for (;;) {
2510
2511 printf(">>> ");
2512 fflush(stdout);
2513
Benny Prijono990042e2007-01-21 19:36:00 +00002514 if (fgets(menuin, sizeof(menuin), stdin) == NULL) {
2515 /*
2516 * Be friendly to users who redirect commands into
2517 * program, when file ends, resume with kbd.
2518 * If exit is desired end script with q for quit
2519 */
2520 /* Reopen stdin/stdout/stderr to /dev/console */
2521#if defined(PJ_WIN32) && PJ_WIN32!=0
2522 if (freopen ("CONIN$", "r", stdin) == NULL) {
2523#else
2524 if (1) {
2525#endif
2526 puts("Cannot switch back to console from file redirection");
2527 menuin[0] = 'q';
2528 menuin[1] = '\0';
2529 } else {
2530 puts("Switched back to console from file redirection");
2531 continue;
2532 }
2533 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002534
2535 switch (menuin[0]) {
2536
2537 case 'm':
2538 /* Make call! : */
2539 printf("(You currently have %d calls)\n",
2540 pjsua_call_get_count());
2541
2542 uri = NULL;
2543 ui_input_url("Make call", buf, sizeof(buf), &result);
2544 if (result.nb_result != NO_NB) {
2545
2546 if (result.nb_result == -1 || result.nb_result == 0) {
2547 puts("You can't do that with make call!");
2548 continue;
2549 } else {
2550 pjsua_buddy_info binfo;
2551 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2552 uri = binfo.uri.ptr;
2553 }
2554
2555 } else if (result.uri_result) {
2556 uri = result.uri_result;
2557 }
2558
2559 tmp = pj_str(uri);
2560 pjsua_call_make_call( current_acc, &tmp, 0, NULL, NULL, NULL);
2561 break;
2562
2563 case 'M':
2564 /* Make multiple calls! : */
2565 printf("(You currently have %d calls)\n",
2566 pjsua_call_get_count());
2567
2568 if (!simple_input("Number of calls", menuin, sizeof(menuin)))
2569 continue;
2570
2571 count = my_atoi(menuin);
2572 if (count < 1)
2573 continue;
2574
2575 ui_input_url("Make call", buf, sizeof(buf), &result);
2576 if (result.nb_result != NO_NB) {
2577 pjsua_buddy_info binfo;
2578 if (result.nb_result == -1 || result.nb_result == 0) {
2579 puts("You can't do that with make call!");
2580 continue;
2581 }
2582 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2583 uri = binfo.uri.ptr;
2584 } else {
2585 uri = result.uri_result;
2586 }
2587
2588 for (i=0; i<my_atoi(menuin); ++i) {
2589 pj_status_t status;
2590
2591 tmp = pj_str(uri);
2592 status = pjsua_call_make_call(current_acc, &tmp, 0, NULL,
2593 NULL, NULL);
2594 if (status != PJ_SUCCESS)
2595 break;
2596 }
2597 break;
2598
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002599 case 'n':
Benny Prijono438e65b2007-11-03 21:42:10 +00002600 i = pjsua_detect_nat_type();
2601 if (i != PJ_SUCCESS)
2602 pjsua_perror(THIS_FILE, "Error", i);
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002603 break;
2604
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002605 case 'i':
2606 /* Send instant messaeg */
2607
2608 /* i is for call index to send message, if any */
2609 i = -1;
2610
2611 /* Make compiler happy. */
2612 uri = NULL;
2613
2614 /* Input destination. */
2615 ui_input_url("Send IM to", buf, sizeof(buf), &result);
2616 if (result.nb_result != NO_NB) {
2617
2618 if (result.nb_result == -1) {
2619 puts("You can't send broadcast IM like that!");
2620 continue;
2621
2622 } else if (result.nb_result == 0) {
2623
2624 i = current_call;
2625
2626 } else {
2627 pjsua_buddy_info binfo;
2628 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2629 uri = binfo.uri.ptr;
2630 }
2631
2632 } else if (result.uri_result) {
2633 uri = result.uri_result;
2634 }
2635
2636
2637 /* Send typing indication. */
2638 if (i != -1)
2639 pjsua_call_send_typing_ind(i, PJ_TRUE, NULL);
2640 else {
2641 pj_str_t tmp_uri = pj_str(uri);
2642 pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL);
2643 }
2644
2645 /* Input the IM . */
2646 if (!simple_input("Message", text, sizeof(text))) {
2647 /*
2648 * Cancelled.
2649 * Send typing notification too, saying we're not typing.
2650 */
2651 if (i != -1)
2652 pjsua_call_send_typing_ind(i, PJ_FALSE, NULL);
2653 else {
2654 pj_str_t tmp_uri = pj_str(uri);
2655 pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL);
2656 }
2657 continue;
2658 }
2659
2660 tmp = pj_str(text);
2661
2662 /* Send the IM */
2663 if (i != -1)
2664 pjsua_call_send_im(i, NULL, &tmp, NULL, NULL);
2665 else {
2666 pj_str_t tmp_uri = pj_str(uri);
2667 pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL);
2668 }
2669
2670 break;
2671
2672 case 'a':
2673
2674 if (current_call != -1) {
2675 pjsua_call_get_info(current_call, &call_info);
2676 } else {
2677 /* Make compiler happy */
2678 call_info.role = PJSIP_ROLE_UAC;
2679 call_info.state = PJSIP_INV_STATE_DISCONNECTED;
2680 }
2681
2682 if (current_call == -1 ||
2683 call_info.role != PJSIP_ROLE_UAS ||
2684 call_info.state >= PJSIP_INV_STATE_CONNECTING)
2685 {
2686 puts("No pending incoming call");
2687 fflush(stdout);
2688 continue;
2689
2690 } else {
Benny Prijono20d36722007-02-22 14:52:24 +00002691 int st_code;
2692 char contact[120];
2693 pj_str_t hname = { "Contact", 7 };
2694 pj_str_t hvalue;
2695 pjsip_generic_string_hdr hcontact;
2696 pjsua_msg_data msg_data;
2697
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002698 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
2699 continue;
2700
Benny Prijono20d36722007-02-22 14:52:24 +00002701 st_code = my_atoi(buf);
2702 if (st_code < 100)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002703 continue;
2704
Benny Prijono20d36722007-02-22 14:52:24 +00002705 pjsua_msg_data_init(&msg_data);
2706
2707 if (st_code/100 == 3) {
2708 if (!simple_input("Enter URL to be put in Contact",
2709 contact, sizeof(contact)))
2710 continue;
2711 hvalue = pj_str(contact);
2712 pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue);
2713
2714 pj_list_push_back(&msg_data.hdr_list, &hcontact);
2715 }
2716
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002717 /*
2718 * Must check again!
2719 * Call may have been disconnected while we're waiting for
2720 * keyboard input.
2721 */
2722 if (current_call == -1) {
2723 puts("Call has been disconnected");
2724 fflush(stdout);
2725 continue;
2726 }
2727
Benny Prijono20d36722007-02-22 14:52:24 +00002728 pjsua_call_answer(current_call, st_code, NULL, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002729 }
2730
2731 break;
2732
2733
2734 case 'h':
2735
2736 if (current_call == -1) {
2737 puts("No current call");
2738 fflush(stdout);
2739 continue;
2740
2741 } else if (menuin[1] == 'a') {
2742
2743 /* Hangup all calls */
2744 pjsua_call_hangup_all();
2745
2746 } else {
2747
2748 /* Hangup current calls */
2749 pjsua_call_hangup(current_call, 0, NULL, NULL);
2750 }
2751 break;
2752
2753 case ']':
2754 case '[':
2755 /*
2756 * Cycle next/prev dialog.
2757 */
2758 if (menuin[0] == ']') {
2759 find_next_call();
2760
2761 } else {
2762 find_prev_call();
2763 }
2764
2765 if (current_call != -1) {
2766
2767 pjsua_call_get_info(current_call, &call_info);
2768 PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s",
2769 (int)call_info.remote_info.slen,
2770 call_info.remote_info.ptr));
2771
2772 } else {
2773 PJ_LOG(3,(THIS_FILE,"No current dialog"));
2774 }
2775 break;
2776
2777
2778 case '>':
2779 case '<':
2780 if (!simple_input("Enter account ID to select", buf, sizeof(buf)))
2781 break;
2782
2783 i = my_atoi(buf);
2784 if (pjsua_acc_is_valid(i)) {
Benny Prijono21b9ad92006-08-15 13:11:22 +00002785 pjsua_acc_set_default(i);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002786 PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i));
2787 } else {
2788 PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i));
2789 }
2790 break;
2791
2792
2793 case '+':
2794 if (menuin[1] == 'b') {
2795
2796 pjsua_buddy_config buddy_cfg;
2797 pjsua_buddy_id buddy_id;
2798 pj_status_t status;
2799
2800 if (!simple_input("Enter buddy's URI:", buf, sizeof(buf)))
2801 break;
2802
2803 if (pjsua_verify_sip_url(buf) != PJ_SUCCESS) {
2804 printf("Invalid SIP URI '%s'\n", buf);
2805 break;
2806 }
2807
Benny Prijonoac623b32006-07-03 15:19:31 +00002808 pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002809
2810 buddy_cfg.uri = pj_str(buf);
2811 buddy_cfg.subscribe = PJ_TRUE;
2812
2813 status = pjsua_buddy_add(&buddy_cfg, &buddy_id);
2814 if (status == PJ_SUCCESS) {
2815 printf("New buddy '%s' added at index %d\n",
2816 buf, buddy_id+1);
2817 }
2818
2819 } else if (menuin[1] == 'a') {
2820
Benny Prijonofb2b3652007-06-28 07:15:03 +00002821 char id[80], registrar[80], realm[80], uname[80], passwd[30];
2822 pjsua_acc_config acc_cfg;
2823 pj_status_t status;
2824
2825 if (!simple_input("Your SIP URL:", id, sizeof(id)))
2826 break;
2827 if (!simple_input("URL of the registrar:", registrar, sizeof(registrar)))
2828 break;
2829 if (!simple_input("Auth Realm:", realm, sizeof(realm)))
2830 break;
2831 if (!simple_input("Auth Username:", uname, sizeof(uname)))
2832 break;
2833 if (!simple_input("Auth Password:", passwd, sizeof(passwd)))
2834 break;
2835
2836 pjsua_acc_config_default(&acc_cfg);
2837 acc_cfg.id = pj_str(id);
2838 acc_cfg.reg_uri = pj_str(registrar);
2839 acc_cfg.cred_count = 1;
Benny Prijono48ab2b72007-11-08 09:24:30 +00002840 acc_cfg.cred_info[0].scheme = pj_str("Digest");
Benny Prijonofb2b3652007-06-28 07:15:03 +00002841 acc_cfg.cred_info[0].realm = pj_str(realm);
2842 acc_cfg.cred_info[0].username = pj_str(uname);
2843 acc_cfg.cred_info[0].data_type = 0;
2844 acc_cfg.cred_info[0].data = pj_str(passwd);
2845
2846 status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
2847 if (status != PJ_SUCCESS) {
2848 pjsua_perror(THIS_FILE, "Error adding new account", status);
2849 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002850
2851 } else {
2852 printf("Invalid input %s\n", menuin);
2853 }
2854 break;
2855
2856 case '-':
2857 if (menuin[1] == 'b') {
2858 if (!simple_input("Enter buddy ID to delete",buf,sizeof(buf)))
2859 break;
2860
2861 i = my_atoi(buf) - 1;
2862
2863 if (!pjsua_buddy_is_valid(i)) {
2864 printf("Invalid buddy id %d\n", i);
2865 } else {
2866 pjsua_buddy_del(i);
2867 printf("Buddy %d deleted\n", i);
2868 }
2869
2870 } else if (menuin[1] == 'a') {
2871
2872 if (!simple_input("Enter account ID to delete",buf,sizeof(buf)))
2873 break;
2874
2875 i = my_atoi(buf);
2876
2877 if (!pjsua_acc_is_valid(i)) {
2878 printf("Invalid account id %d\n", i);
2879 } else {
2880 pjsua_acc_del(i);
2881 printf("Account %d deleted\n", i);
2882 }
2883
2884 } else {
2885 printf("Invalid input %s\n", menuin);
2886 }
2887 break;
2888
2889 case 'H':
2890 /*
2891 * Hold call.
2892 */
2893 if (current_call != -1) {
2894
2895 pjsua_call_set_hold(current_call, NULL);
2896
2897 } else {
2898 PJ_LOG(3,(THIS_FILE, "No current call"));
2899 }
2900 break;
2901
2902 case 'v':
2903 /*
2904 * Send re-INVITE (to release hold, etc).
2905 */
2906 if (current_call != -1) {
2907
2908 pjsua_call_reinvite(current_call, PJ_TRUE, NULL);
2909
2910 } else {
2911 PJ_LOG(3,(THIS_FILE, "No current call"));
2912 }
2913 break;
2914
Benny Prijonoc08682e2007-10-04 06:17:58 +00002915 case 'U':
2916 /*
2917 * Send UPDATE
2918 */
2919 if (current_call != -1) {
2920
2921 pjsua_call_update(current_call, 0, NULL);
2922
2923 } else {
2924 PJ_LOG(3,(THIS_FILE, "No current call"));
2925 }
2926 break;
2927
2928 case 'C':
2929 if (menuin[1] == 'p') {
2930 manage_codec_prio();
2931 }
2932 break;
2933
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002934 case 'x':
2935 /*
2936 * Transfer call.
2937 */
2938 if (current_call == -1) {
2939
2940 PJ_LOG(3,(THIS_FILE, "No current call"));
2941
2942 } else {
2943 int call = current_call;
Benny Prijonod524e822006-09-22 12:48:18 +00002944 pjsua_msg_data msg_data;
2945 pjsip_generic_string_hdr refer_sub;
2946 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
2947 pj_str_t STR_FALSE = { "false", 5 };
Benny Prijonof7b1c392006-11-11 16:46:34 +00002948 pjsua_call_info ci;
2949
2950 pjsua_call_get_info(current_call, &ci);
2951 printf("Transfering current call [%d] %.*s\n",
2952 current_call,
2953 (int)ci.remote_info.slen, ci.remote_info.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002954
2955 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
2956
2957 /* Check if call is still there. */
2958
2959 if (call != current_call) {
2960 puts("Call has been disconnected");
2961 continue;
2962 }
2963
Benny Prijonod524e822006-09-22 12:48:18 +00002964 pjsua_msg_data_init(&msg_data);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002965 if (app_config.no_refersub) {
2966 /* Add Refer-Sub: false in outgoing REFER request */
2967 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
2968 &STR_FALSE);
2969 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
2970 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002971 if (result.nb_result != NO_NB) {
2972 if (result.nb_result == -1 || result.nb_result == 0)
2973 puts("You can't do that with transfer call!");
2974 else {
2975 pjsua_buddy_info binfo;
2976 pjsua_buddy_get_info(result.nb_result-1, &binfo);
Benny Prijonod524e822006-09-22 12:48:18 +00002977 pjsua_call_xfer( current_call, &binfo.uri, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002978 }
2979
2980 } else if (result.uri_result) {
2981 pj_str_t tmp;
2982 tmp = pj_str(result.uri_result);
Benny Prijonod524e822006-09-22 12:48:18 +00002983 pjsua_call_xfer( current_call, &tmp, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002984 }
2985 }
2986 break;
2987
Benny Prijonof7b1c392006-11-11 16:46:34 +00002988 case 'X':
2989 /*
2990 * Transfer call with replaces.
2991 */
2992 if (current_call == -1) {
2993
2994 PJ_LOG(3,(THIS_FILE, "No current call"));
2995
2996 } else {
2997 int call = current_call;
2998 int dst_call;
2999 pjsua_msg_data msg_data;
3000 pjsip_generic_string_hdr refer_sub;
3001 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
3002 pj_str_t STR_FALSE = { "false", 5 };
3003 pjsua_call_id ids[PJSUA_MAX_CALLS];
3004 pjsua_call_info ci;
3005 unsigned i, count;
3006
3007 count = PJ_ARRAY_SIZE(ids);
3008 pjsua_enum_calls(ids, &count);
3009
3010 if (count <= 1) {
3011 puts("There are no other calls");
3012 continue;
3013 }
3014
3015 pjsua_call_get_info(current_call, &ci);
3016 printf("Transfer call [%d] %.*s to one of the following:\n",
3017 current_call,
3018 (int)ci.remote_info.slen, ci.remote_info.ptr);
3019
3020 for (i=0; i<count; ++i) {
3021 pjsua_call_info call_info;
3022
3023 if (ids[i] == call)
3024 continue;
3025
3026 pjsua_call_get_info(ids[i], &call_info);
3027 printf("%d %.*s [%.*s]\n",
3028 ids[i],
3029 (int)call_info.remote_info.slen,
3030 call_info.remote_info.ptr,
3031 (int)call_info.state_text.slen,
3032 call_info.state_text.ptr);
3033 }
3034
3035 if (!simple_input("Enter call number to be replaced",
3036 buf, sizeof(buf)))
3037 continue;
3038
3039 dst_call = my_atoi(buf);
3040
3041 /* Check if call is still there. */
3042
3043 if (call != current_call) {
3044 puts("Call has been disconnected");
3045 continue;
3046 }
3047
3048 /* Check that destination call is valid. */
3049 if (dst_call == call) {
3050 puts("Destination call number must not be the same "
3051 "as the call being transfered");
3052 continue;
3053 }
3054 if (dst_call >= PJSUA_MAX_CALLS) {
3055 puts("Invalid destination call number");
3056 continue;
3057 }
3058 if (!pjsua_call_is_active(dst_call)) {
3059 puts("Invalid destination call number");
3060 continue;
3061 }
3062
3063 pjsua_msg_data_init(&msg_data);
3064 if (app_config.no_refersub) {
3065 /* Add Refer-Sub: false in outgoing REFER request */
3066 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
3067 &STR_FALSE);
3068 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
3069 }
3070
3071 pjsua_call_xfer_replaces(call, dst_call, 0, &msg_data);
3072 }
3073 break;
3074
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003075 case '#':
3076 /*
3077 * Send DTMF strings.
3078 */
3079 if (current_call == -1) {
3080
3081 PJ_LOG(3,(THIS_FILE, "No current call"));
3082
3083 } else if (!pjsua_call_has_media(current_call)) {
3084
3085 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
3086
3087 } else {
3088 pj_str_t digits;
3089 int call = current_call;
3090 pj_status_t status;
3091
3092 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
3093 sizeof(buf)))
3094 {
3095 break;
3096 }
3097
3098 if (call != current_call) {
3099 puts("Call has been disconnected");
3100 continue;
3101 }
3102
3103 digits = pj_str(buf);
3104 status = pjsua_call_dial_dtmf(current_call, &digits);
3105 if (status != PJ_SUCCESS) {
3106 pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
3107 } else {
3108 puts("DTMF digits enqueued for transmission");
3109 }
3110 }
3111 break;
3112
Benny Prijonofeb69f42007-10-05 09:12:26 +00003113 case '*':
3114 /* Send DTMF with INFO */
3115 if (current_call == -1) {
3116
3117 PJ_LOG(3,(THIS_FILE, "No current call"));
3118
3119 } else {
3120 const pj_str_t SIP_INFO = pj_str("INFO");
3121 pj_str_t digits;
3122 int call = current_call;
3123 int i;
3124 pj_status_t status;
3125
3126 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
3127 sizeof(buf)))
3128 {
3129 break;
3130 }
3131
3132 if (call != current_call) {
3133 puts("Call has been disconnected");
3134 continue;
3135 }
3136
3137 digits = pj_str(buf);
3138 for (i=0; i<digits.slen; ++i) {
3139 pjsua_msg_data msg_data;
3140 char body[80];
3141
3142 pjsua_msg_data_init(&msg_data);
3143 msg_data.content_type = pj_str("application/dtmf-relay");
3144
3145 pj_ansi_snprintf(body, sizeof(body),
3146 "Signal=%c\r\n"
3147 "Duration=160",
3148 buf[i]);
3149 msg_data.msg_body = pj_str(body);
3150
3151 status = pjsua_call_send_request(current_call, &SIP_INFO,
3152 &msg_data);
3153 if (status != PJ_SUCCESS) {
3154 break;
3155 }
3156 }
3157 }
3158 break;
3159
Benny Prijono56315612006-07-18 14:39:40 +00003160 case 'S':
3161 /*
3162 * Send arbitrary request
3163 */
3164 if (pjsua_acc_get_count() == 0) {
3165 puts("Sorry, need at least one account configured");
3166 break;
3167 }
3168
3169 puts("Send arbitrary request to remote host");
3170
3171 /* Input METHOD */
3172 if (!simple_input("Request method:",text,sizeof(text)))
3173 break;
3174
3175 /* Input destination URI */
3176 uri = NULL;
3177 ui_input_url("Destination URI", buf, sizeof(buf), &result);
3178 if (result.nb_result != NO_NB) {
3179
3180 if (result.nb_result == -1 || result.nb_result == 0) {
3181 puts("Sorry you can't do that!");
3182 continue;
3183 } else {
3184 pjsua_buddy_info binfo;
3185 pjsua_buddy_get_info(result.nb_result-1, &binfo);
3186 uri = binfo.uri.ptr;
3187 }
3188
3189 } else if (result.uri_result) {
3190 uri = result.uri_result;
3191 }
3192
3193 tmp = pj_str(uri);
3194
3195 send_request(text, &tmp);
3196 break;
3197
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003198 case 's':
Benny Prijono990042e2007-01-21 19:36:00 +00003199 if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
3200 pj_str_t tmp;
3201 int delay;
3202
3203 tmp.ptr = menuin+6;
3204 tmp.slen = pj_ansi_strlen(menuin)-7;
3205
3206 if (tmp.slen < 1) {
3207 puts("Usage: sleep MSEC");
3208 break;
3209 }
3210
3211 delay = pj_strtoul(&tmp);
3212 if (delay < 0) delay = 0;
3213 pj_thread_sleep(delay);
3214 break;
3215 }
3216 /* Continue below */
3217
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003218 case 'u':
3219 /*
3220 * Subscribe/unsubscribe presence.
3221 */
3222 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
3223 if (result.nb_result != NO_NB) {
3224 if (result.nb_result == -1) {
3225 int i, count;
3226 count = pjsua_get_buddy_count();
3227 for (i=0; i<count; ++i)
3228 pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
3229 } else if (result.nb_result == 0) {
3230 puts("Sorry, can only subscribe to buddy's presence, "
3231 "not from existing call");
3232 } else {
3233 pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
3234 }
3235
3236 } else if (result.uri_result) {
3237 puts("Sorry, can only subscribe to buddy's presence, "
3238 "not arbitrary URL (for now)");
3239 }
3240
3241 break;
3242
3243 case 'r':
3244 switch (menuin[1]) {
3245 case 'r':
3246 /*
3247 * Re-Register.
3248 */
3249 pjsua_acc_set_registration(current_acc, PJ_TRUE);
3250 break;
3251 case 'u':
3252 /*
3253 * Unregister
3254 */
3255 pjsua_acc_set_registration(current_acc, PJ_FALSE);
3256 break;
3257 }
3258 break;
3259
3260 case 't':
3261 pjsua_acc_get_info(current_acc, &acc_info);
3262 acc_info.online_status = !acc_info.online_status;
3263 pjsua_acc_set_online_status(current_acc, acc_info.online_status);
3264 printf("Setting %s online status to %s\n",
3265 acc_info.acc_uri.ptr,
3266 (acc_info.online_status?"online":"offline"));
3267 break;
3268
Benny Prijono4461c7d2007-08-25 13:36:15 +00003269 case 'T':
3270 change_online_status();
3271 break;
3272
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003273 case 'c':
3274 switch (menuin[1]) {
3275 case 'l':
3276 conf_list();
3277 break;
3278 case 'c':
3279 case 'd':
3280 {
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003281 char tmp[10], src_port[10], dst_port[10];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003282 pj_status_t status;
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003283 int cnt;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003284 const char *src_title, *dst_title;
3285
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003286 cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003287
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003288 if (cnt != 3) {
3289 conf_list();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003290
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003291 src_title = (menuin[1]=='c'?
3292 "Connect src port #":
3293 "Disconnect src port #");
3294 dst_title = (menuin[1]=='c'?
3295 "To dst port #":
3296 "From dst port #");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003297
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003298 if (!simple_input(src_title, src_port, sizeof(src_port)))
3299 break;
3300
3301 if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
3302 break;
3303 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003304
3305 if (menuin[1]=='c') {
3306 status = pjsua_conf_connect(my_atoi(src_port),
3307 my_atoi(dst_port));
3308 } else {
3309 status = pjsua_conf_disconnect(my_atoi(src_port),
3310 my_atoi(dst_port));
3311 }
3312 if (status == PJ_SUCCESS) {
3313 puts("Success");
3314 } else {
3315 puts("ERROR!!");
3316 }
3317 }
3318 break;
3319 }
3320 break;
3321
Benny Prijono6dd967c2006-12-26 02:27:14 +00003322 case 'V':
3323 /* Adjust audio volume */
3324 sprintf(buf, "Adjust mic level: [%4.1fx] ", app_config.mic_level);
3325 if (simple_input(buf,text,sizeof(text))) {
3326 char *err;
3327 app_config.mic_level = (float)strtod(text, &err);
3328 pjsua_conf_adjust_rx_level(0, app_config.mic_level);
3329 }
3330 sprintf(buf, "Adjust speaker level: [%4.1fx] ",
3331 app_config.speaker_level);
3332 if (simple_input(buf,text,sizeof(text))) {
3333 char *err;
3334 app_config.speaker_level = (float)strtod(text, &err);
3335 pjsua_conf_adjust_tx_level(0, app_config.speaker_level);
3336 }
3337
3338 break;
3339
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003340 case 'd':
3341 if (menuin[1] == 'c') {
3342 char settings[2000];
3343 int len;
3344
3345 len = write_settings(&app_config, settings, sizeof(settings));
3346 if (len < 1)
3347 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
3348 else
3349 PJ_LOG(3,(THIS_FILE,
3350 "Dumping configuration (%d bytes):\n%s\n",
3351 len, settings));
Benny Prijono819506c2006-06-22 22:29:51 +00003352
3353 } else if (menuin[1] == 'q') {
3354
3355 if (current_call != PJSUA_INVALID_ID) {
3356 char buf[1024];
3357 pjsua_call_dump(current_call, PJ_TRUE, buf,
3358 sizeof(buf), " ");
3359 PJ_LOG(3,(THIS_FILE, "\n%s", buf));
3360 } else {
3361 PJ_LOG(3,(THIS_FILE, "No current call"));
3362 }
3363
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003364 } else {
3365 app_dump(menuin[1]=='d');
3366 }
3367 break;
3368
3369
3370 case 'f':
3371 if (simple_input("Enter output filename", buf, sizeof(buf))) {
3372 char settings[2000];
3373 int len;
3374
3375 len = write_settings(&app_config, settings, sizeof(settings));
3376 if (len < 1)
3377 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
3378 else {
3379 pj_oshandle_t fd;
3380 pj_status_t status;
3381
3382 status = pj_file_open(app_config.pool, buf,
3383 PJ_O_WRONLY, &fd);
3384 if (status != PJ_SUCCESS) {
3385 pjsua_perror(THIS_FILE, "Unable to open file", status);
3386 } else {
3387 pj_ssize_t size = len;
3388 pj_file_write(fd, settings, &size);
3389 pj_file_close(fd);
3390
3391 printf("Settings successfully written to '%s'\n", buf);
3392 }
3393 }
3394
3395 }
3396 break;
3397
3398
3399 case 'q':
3400 goto on_exit;
3401
3402
3403 default:
3404 if (menuin[0] != '\n' && menuin[0] != '\r') {
3405 printf("Invalid input %s", menuin);
3406 }
3407 keystroke_help();
3408 break;
3409 }
3410 }
3411
3412on_exit:
3413 ;
3414}
3415
3416
3417/*****************************************************************************
3418 * Public API
3419 */
3420
3421pj_status_t app_init(int argc, char *argv[])
3422{
Benny Prijonoe93e2872006-06-28 16:46:49 +00003423 pjsua_transport_id transport_id = -1;
Benny Prijonoe347cb02007-02-14 14:36:13 +00003424 pjsua_transport_config tcp_cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003425 unsigned i;
3426 pj_status_t status;
3427
3428 /* Create pjsua */
3429 status = pjsua_create();
3430 if (status != PJ_SUCCESS)
3431 return status;
3432
3433 /* Create pool for application */
Benny Prijonod5696da2007-07-17 16:25:45 +00003434 app_config.pool = pjsua_pool_create("pjsua", 1000, 1000);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003435
3436 /* Initialize default config */
3437 default_config(&app_config);
3438
3439 /* Parse the arguments */
3440 status = parse_args(argc, argv, &app_config, &uri_arg);
3441 if (status != PJ_SUCCESS)
3442 return status;
3443
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003444 /* Initialize application callbacks */
3445 app_config.cfg.cb.on_call_state = &on_call_state;
3446 app_config.cfg.cb.on_call_media_state = &on_call_media_state;
3447 app_config.cfg.cb.on_incoming_call = &on_incoming_call;
Benny Prijonofeb69f42007-10-05 09:12:26 +00003448 app_config.cfg.cb.on_call_tsx_state = &on_call_tsx_state;
Benny Prijono0875ae82006-12-26 00:11:48 +00003449 app_config.cfg.cb.on_dtmf_digit = &call_on_dtmf_callback;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003450 app_config.cfg.cb.on_reg_state = &on_reg_state;
3451 app_config.cfg.cb.on_buddy_state = &on_buddy_state;
3452 app_config.cfg.cb.on_pager = &on_pager;
3453 app_config.cfg.cb.on_typing = &on_typing;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003454 app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
Benny Prijonof7b1c392006-11-11 16:46:34 +00003455 app_config.cfg.cb.on_call_replaced = &on_call_replaced;
Benny Prijono6ba8c542007-10-16 01:34:14 +00003456 app_config.cfg.cb.on_nat_detect = &on_nat_detect;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003457
3458 /* Initialize pjsua */
3459 status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
3460 &app_config.media_cfg);
3461 if (status != PJ_SUCCESS)
3462 return status;
3463
Benny Prijonoe909eac2006-07-27 22:04:56 +00003464#ifdef STEREO_DEMO
3465 stereo_demo();
3466#endif
3467
Benny Prijono804ff0a2006-09-14 11:17:48 +00003468 /* Initialize calls data */
3469 for (i=0; i<PJ_ARRAY_SIZE(app_config.call_data); ++i) {
3470 app_config.call_data[i].timer.id = PJSUA_INVALID_ID;
3471 app_config.call_data[i].timer.cb = &call_timeout_callback;
3472 }
3473
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003474 /* Optionally registers WAV file */
Benny Prijono32e4f492007-01-24 00:44:26 +00003475 for (i=0; i<app_config.wav_count; ++i) {
3476 pjsua_player_id wav_id;
3477
3478 status = pjsua_player_create(&app_config.wav_files[i], 0,
3479 &wav_id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003480 if (status != PJ_SUCCESS)
3481 goto on_error;
Benny Prijono22a300a2006-06-14 20:04:55 +00003482
Benny Prijono72c04d22007-02-17 22:20:58 +00003483 if (app_config.wav_id == PJSUA_INVALID_ID) {
Benny Prijono32e4f492007-01-24 00:44:26 +00003484 app_config.wav_id = wav_id;
3485 app_config.wav_port = pjsua_player_get_conf_port(app_config.wav_id);
3486 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003487 }
3488
Benny Prijono4af234b2007-01-24 02:02:09 +00003489 /* Optionally registers tone players */
3490 for (i=0; i<app_config.tone_count; ++i) {
3491 pjmedia_port *tport;
3492 char name[80];
3493 pj_str_t label;
3494 pj_status_t status;
3495
3496 pj_ansi_snprintf(name, sizeof(name), "tone-%d,%d",
3497 app_config.tones[i].freq1,
3498 app_config.tones[i].freq2);
3499 label = pj_str(name);
3500 status = pjmedia_tonegen_create2(app_config.pool, &label,
3501 8000, 1, 160, 16,
3502 PJMEDIA_TONEGEN_LOOP, &tport);
3503 if (status != PJ_SUCCESS) {
3504 pjsua_perror(THIS_FILE, "Unable to create tone generator", status);
3505 goto on_error;
3506 }
3507
3508 status = pjsua_conf_add_port(app_config.pool, tport,
3509 &app_config.tone_slots[i]);
3510 pj_assert(status == PJ_SUCCESS);
3511
3512 status = pjmedia_tonegen_play(tport, 1, &app_config.tones[i], 0);
3513 pj_assert(status == PJ_SUCCESS);
3514 }
3515
Benny Prijono1ebd6142006-10-19 15:48:02 +00003516 /* Optionally create recorder file, if any. */
3517 if (app_config.rec_file.slen) {
3518 status = pjsua_recorder_create(&app_config.rec_file, 0, NULL, 0, 0,
3519 &app_config.rec_id);
3520 if (status != PJ_SUCCESS)
3521 goto on_error;
3522
3523 app_config.rec_port = pjsua_recorder_get_conf_port(app_config.rec_id);
3524 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003525
Benny Prijonoe347cb02007-02-14 14:36:13 +00003526 pj_memcpy(&tcp_cfg, &app_config.udp_cfg, sizeof(tcp_cfg));
3527
Benny Prijono87ef89a2007-01-14 00:39:45 +00003528 /* Add UDP transport unless it's disabled. */
3529 if (!app_config.no_udp) {
3530 pjsua_acc_id aid;
3531
3532 status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
3533 &app_config.udp_cfg,
3534 &transport_id);
3535 if (status != PJ_SUCCESS)
3536 goto on_error;
3537
3538 /* Add local account */
3539 pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
3540 //pjsua_acc_set_transport(aid, transport_id);
3541 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
Benny Prijonoe347cb02007-02-14 14:36:13 +00003542
3543 if (app_config.udp_cfg.port == 0) {
3544 pjsua_transport_info ti;
3545 pj_sockaddr_in *a;
3546
3547 pjsua_transport_get_info(transport_id, &ti);
3548 a = (pj_sockaddr_in*)&ti.local_addr;
3549
3550 tcp_cfg.port = pj_ntohs(a->sin_port);
3551 }
Benny Prijono87ef89a2007-01-14 00:39:45 +00003552 }
3553
Benny Prijonoe93e2872006-06-28 16:46:49 +00003554 /* Add TCP transport unless it's disabled */
3555 if (!app_config.no_tcp) {
3556 status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
Benny Prijonoe347cb02007-02-14 14:36:13 +00003557 &tcp_cfg,
Benny Prijonoe93e2872006-06-28 16:46:49 +00003558 &transport_id);
3559 if (status != PJ_SUCCESS)
3560 goto on_error;
3561
3562 /* Add local account */
Benny Prijono21b9ad92006-08-15 13:11:22 +00003563 pjsua_acc_add_local(transport_id, PJ_TRUE, NULL);
Benny Prijonoe93e2872006-06-28 16:46:49 +00003564 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
3565
3566 }
3567
Benny Prijonoe93e2872006-06-28 16:46:49 +00003568
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003569#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
3570 /* Add TLS transport when application wants one */
3571 if (app_config.use_tls) {
Benny Prijonof3bbc132006-12-25 06:43:59 +00003572
3573 pjsua_acc_id acc_id;
3574
3575 /* Set TLS port as TCP port+1 */
Benny Prijonoafc47be2007-02-14 14:44:55 +00003576 tcp_cfg.port++;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003577 status = pjsua_transport_create(PJSIP_TRANSPORT_TLS,
Benny Prijonoafc47be2007-02-14 14:44:55 +00003578 &tcp_cfg,
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003579 &transport_id);
Benny Prijonoafc47be2007-02-14 14:44:55 +00003580 tcp_cfg.port--;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003581 if (status != PJ_SUCCESS)
3582 goto on_error;
Benny Prijonof3bbc132006-12-25 06:43:59 +00003583
3584 /* Add local account */
3585 pjsua_acc_add_local(transport_id, PJ_FALSE, &acc_id);
3586 pjsua_acc_set_online_status(acc_id, PJ_TRUE);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003587 }
3588#endif
3589
Benny Prijonoe93e2872006-06-28 16:46:49 +00003590 if (transport_id == -1) {
3591 PJ_LOG(3,(THIS_FILE, "Error: no transport is configured"));
3592 status = -1;
3593 goto on_error;
3594 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003595
3596
3597 /* Add accounts */
3598 for (i=0; i<app_config.acc_cnt; ++i) {
Benny Prijono21b9ad92006-08-15 13:11:22 +00003599 status = pjsua_acc_add(&app_config.acc_cfg[i], PJ_TRUE, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003600 if (status != PJ_SUCCESS)
3601 goto on_error;
3602 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
3603 }
3604
3605 /* Add buddies */
3606 for (i=0; i<app_config.buddy_cnt; ++i) {
3607 status = pjsua_buddy_add(&app_config.buddy_cfg[i], NULL);
3608 if (status != PJ_SUCCESS)
3609 goto on_error;
3610 }
3611
3612 /* Optionally set codec orders */
3613 for (i=0; i<app_config.codec_cnt; ++i) {
3614 pjsua_codec_set_priority(&app_config.codec_arg[i],
3615 (pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9));
3616 }
3617
Benny Prijonofce28542007-12-09 15:41:10 +00003618 /* Optionally disable some codec */
3619 for (i=0; i<app_config.codec_dis_cnt; ++i) {
3620 pjsua_codec_set_priority(&app_config.codec_dis[i],PJMEDIA_CODEC_PRIO_DISABLED);
3621 }
3622
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003623 /* Add RTP transports */
3624 status = pjsua_media_transports_create(&app_config.rtp_cfg);
3625 if (status != PJ_SUCCESS)
3626 goto on_error;
3627
Benny Prijono22a300a2006-06-14 20:04:55 +00003628 /* Use null sound device? */
Benny Prijonoe909eac2006-07-27 22:04:56 +00003629#ifndef STEREO_DEMO
Benny Prijono22a300a2006-06-14 20:04:55 +00003630 if (app_config.null_audio) {
3631 status = pjsua_set_null_snd_dev();
3632 if (status != PJ_SUCCESS)
3633 return status;
3634 }
Benny Prijonoe909eac2006-07-27 22:04:56 +00003635#endif
Benny Prijono22a300a2006-06-14 20:04:55 +00003636
Benny Prijono4e5d5512007-03-06 18:11:30 +00003637 if (app_config.capture_dev != PJSUA_INVALID_ID
3638 || app_config.playback_dev != PJSUA_INVALID_ID) {
3639 status
3640 = pjsua_set_snd_dev(app_config.capture_dev, app_config.playback_dev);
3641 if (status != PJ_SUCCESS)
3642 goto on_error;
3643 }
3644
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003645 return PJ_SUCCESS;
3646
3647on_error:
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00003648 app_destroy();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003649 return status;
3650}
3651
3652
3653pj_status_t app_main(void)
3654{
3655 pj_status_t status;
3656
3657 /* Start pjsua */
3658 status = pjsua_start();
3659 if (status != PJ_SUCCESS) {
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00003660 app_destroy();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003661 return status;
3662 }
3663
3664 console_app_main(&uri_arg);
3665
3666 return PJ_SUCCESS;
3667}
3668
3669pj_status_t app_destroy(void)
3670{
Benny Prijonof762ee72006-12-01 11:14:37 +00003671 pj_status_t status;
Benny Prijono4af234b2007-01-24 02:02:09 +00003672 unsigned i;
Benny Prijonof762ee72006-12-01 11:14:37 +00003673
Benny Prijonoe909eac2006-07-27 22:04:56 +00003674#ifdef STEREO_DEMO
3675 if (app_config.snd) {
3676 pjmedia_snd_port_destroy(app_config.snd);
3677 app_config.snd = NULL;
3678 }
3679#endif
3680
Benny Prijono4af234b2007-01-24 02:02:09 +00003681 /* Close tone generators */
3682 for (i=0; i<app_config.tone_count; ++i) {
3683 pjsua_conf_remove_port(app_config.tone_slots[i]);
3684 }
3685
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003686 if (app_config.pool) {
3687 pj_pool_release(app_config.pool);
3688 app_config.pool = NULL;
3689 }
3690
Benny Prijonof762ee72006-12-01 11:14:37 +00003691 status = pjsua_destroy();
3692
3693 pj_bzero(&app_config, sizeof(app_config));
3694
3695 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003696}
Benny Prijonoe909eac2006-07-27 22:04:56 +00003697
3698
3699#ifdef STEREO_DEMO
3700static void stereo_demo()
3701{
3702 pjmedia_port *conf, *splitter, *ch1;
Benny Prijonoe909eac2006-07-27 22:04:56 +00003703 pj_status_t status;
3704
3705 /* Disable existing sound device */
3706 conf = pjsua_set_no_snd_dev();
3707
Benny Prijonoe909eac2006-07-27 22:04:56 +00003708 /* Create stereo-mono splitter/combiner */
3709 status = pjmedia_splitcomb_create(app_config.pool,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003710 conf->info.clock_rate /* clock rate */,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003711 2 /* stereo */,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003712 2 * conf->info.samples_per_frame,
3713 conf->info.bits_per_sample,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003714 0 /* options */,
3715 &splitter);
3716 pj_assert(status == PJ_SUCCESS);
3717
3718 /* Connect channel0 (left channel?) to conference port slot0 */
3719 status = pjmedia_splitcomb_set_channel(splitter, 0 /* ch0 */,
3720 0 /*options*/,
3721 conf);
3722 pj_assert(status == PJ_SUCCESS);
3723
3724 /* Create reverse channel for channel1 (right channel?)... */
3725 status = pjmedia_splitcomb_create_rev_channel(app_config.pool,
3726 splitter,
3727 1 /* ch1 */,
3728 0 /* options */,
3729 &ch1);
3730 pj_assert(status == PJ_SUCCESS);
3731
3732 /* .. and register it to conference bridge (it would be slot1
3733 * if there's no other devices connected to the bridge)
3734 */
3735 status = pjsua_conf_add_port(app_config.pool, ch1, NULL);
3736 pj_assert(status == PJ_SUCCESS);
3737
3738 /* Create sound device */
3739 status = pjmedia_snd_port_create(app_config.pool, -1, -1,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003740 conf->info.clock_rate,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003741 2 /* stereo */,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003742 2 * conf->info.samples_per_frame,
3743 conf->info.bits_per_sample,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003744 0, &app_config.snd);
3745 pj_assert(status == PJ_SUCCESS);
3746
3747
3748 /* Connect the splitter to the sound device */
3749 status = pjmedia_snd_port_connect(app_config.snd, splitter);
3750 pj_assert(status == PJ_SUCCESS);
3751
3752}
3753#endif
3754