blob: 39560f11e360cdcb7dbb2a60ea8d4cb7decbc180 [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 Prijonoeebe9af2006-06-13 22:57:13 +0000174 puts (" --clock-rate=N Override sound device clock rate");
175 puts (" --null-audio Use NULL audio device");
Benny Prijono4af234b2007-01-24 02:02:09 +0000176 puts (" --play-file=file Register WAV file in conference bridge.");
177 puts (" This can be specified multiple times.");
Benny Prijonob67eaac2007-02-18 20:56:00 +0000178 puts (" --play-tone=FORMAT Register tone to the conference bridge.");
179 puts (" FORMAT is 'F1,F2,ON,OFF', where F1,F2 are");
180 puts (" frequencies, and ON,OFF=on/off duration in msec.");
Benny Prijono4af234b2007-01-24 02:02:09 +0000181 puts (" This can be specified multiple times.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000182 puts (" --auto-play Automatically play the file (to incoming calls only)");
183 puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
Benny Prijono7ca96da2006-08-07 12:11:40 +0000184 puts (" --auto-conf Automatically put calls in conference with others");
Benny Prijono1ebd6142006-10-19 15:48:02 +0000185 puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");
186 puts (" --auto-rec Automatically record conversation");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000187 puts (" --rtp-port=N Base port to try for RTP (default=4000)");
Benny Prijono00cae612006-07-31 15:19:36 +0000188 puts (" --quality=N Specify media quality (0-10, default=6)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000189 puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)");
Benny Prijono0a12f002006-07-26 17:05:39 +0000190 puts (" --no-vad Disable VAD/silence detector (default=vad enabled)");
Benny Prijonod79f25c2006-08-02 19:41:37 +0000191 puts (" --ec-tail=MSEC Set echo canceller tail length (default=256)");
Benny Prijono00cae612006-07-31 15:19:36 +0000192 puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is 20)");
193 puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
194 puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
Benny Prijono4e5d5512007-03-06 18:11:30 +0000195 puts (" --capture-dev=id Audio capture device ID (default=-1)");
196 puts (" --playback-dev=id Audio playback device ID (default=-1)");
Benny Prijono00cae612006-07-31 15:19:36 +0000197
Benny Prijono0a12f002006-07-26 17:05:39 +0000198
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000199 puts ("");
200 puts ("Buddy List (can be more than one):");
201 puts (" --add-buddy url Add the specified URL to the buddy list.");
202 puts ("");
203 puts ("User Agent options:");
204 puts (" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)");
205 puts (" --max-calls=N Maximum number of concurrent calls (default:4, max:255)");
Benny Prijonof521eb02006-08-06 23:07:25 +0000206 puts (" --thread-cnt=N Number of worker threads (default:1)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000207 puts (" --duration=SEC Set maximum call duration (default:no limit)");
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000208 puts (" --norefersub Suppress event subscription when transfering calls");
Benny Prijonofce28542007-12-09 15:41:10 +0000209 puts (" --use-compact-form Minimize SIP message size");
Benny Prijono804ff0a2006-09-14 11:17:48 +0000210
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000211 puts ("");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000212 puts ("When URL is specified, pjsua will immediately initiate call to that URL");
213 puts ("");
214
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000215 fflush(stdout);
216}
217
218
219/* Set default config. */
220static void default_config(struct app_config *cfg)
221{
Benny Prijono56315612006-07-18 14:39:40 +0000222 char tmp[80];
Benny Prijono1febfdf2007-02-18 01:35:04 +0000223 unsigned i;
Benny Prijono56315612006-07-18 14:39:40 +0000224
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000225 pjsua_config_default(&cfg->cfg);
Benny Prijono106f5b72007-08-30 13:49:33 +0000226 pj_ansi_sprintf(tmp, "PJSUA v%s/%s", pj_get_version(), PJ_OS_NAME);
Benny Prijono56315612006-07-18 14:39:40 +0000227 pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp);
228
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000229 pjsua_logging_config_default(&cfg->log_cfg);
230 pjsua_media_config_default(&cfg->media_cfg);
231 pjsua_transport_config_default(&cfg->udp_cfg);
232 cfg->udp_cfg.port = 5060;
233 pjsua_transport_config_default(&cfg->rtp_cfg);
234 cfg->rtp_cfg.port = 4000;
Benny Prijono804ff0a2006-09-14 11:17:48 +0000235 cfg->duration = NO_LIMIT;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000236 cfg->wav_id = PJSUA_INVALID_ID;
Benny Prijono1ebd6142006-10-19 15:48:02 +0000237 cfg->rec_id = PJSUA_INVALID_ID;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000238 cfg->wav_port = PJSUA_INVALID_ID;
Benny Prijono1ebd6142006-10-19 15:48:02 +0000239 cfg->rec_port = PJSUA_INVALID_ID;
Benny Prijono6dd967c2006-12-26 02:27:14 +0000240 cfg->mic_level = cfg->speaker_level = 1.0;
Benny Prijono4e5d5512007-03-06 18:11:30 +0000241 cfg->capture_dev = PJSUA_INVALID_ID;
242 cfg->playback_dev = PJSUA_INVALID_ID;
Benny Prijono1febfdf2007-02-18 01:35:04 +0000243
244 for (i=0; i<PJ_ARRAY_SIZE(cfg->acc_cfg); ++i)
245 pjsua_acc_config_default(&cfg->acc_cfg[i]);
246
247 for (i=0; i<PJ_ARRAY_SIZE(cfg->buddy_cfg); ++i)
248 pjsua_buddy_config_default(&cfg->buddy_cfg[i]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000249}
250
251
252/*
253 * Read command arguments from config file.
254 */
255static int read_config_file(pj_pool_t *pool, const char *filename,
256 int *app_argc, char ***app_argv)
257{
258 int i;
259 FILE *fhnd;
260 char line[200];
261 int argc = 0;
262 char **argv;
263 enum { MAX_ARGS = 64 };
264
265 /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
266 argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
267 argv[argc++] = *app_argv[0];
268
269 /* Open config file. */
270 fhnd = fopen(filename, "rt");
271 if (!fhnd) {
272 PJ_LOG(1,(THIS_FILE, "Unable to open config file %s", filename));
273 fflush(stdout);
274 return -1;
275 }
276
277 /* Scan tokens in the file. */
278 while (argc < MAX_ARGS && !feof(fhnd)) {
Benny Prijonobf5b4692007-06-28 03:20:17 +0000279 char *token;
280 char *p;
281 const char *whitespace = " \t\r\n";
282 char cDelimiter;
283 int len, token_len;
284
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000285 if (fgets(line, sizeof(line), fhnd) == NULL) break;
Benny Prijonobf5b4692007-06-28 03:20:17 +0000286
287 // Trim ending newlines
288 len = strlen(line);
289 if (line[len-1]=='\n')
290 line[--len] = '\0';
291 if (line[len-1]=='\r')
292 line[--len] = '\0';
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000293
Benny Prijonobf5b4692007-06-28 03:20:17 +0000294 if (len==0) continue;
295
296 for (p = line; *p != '\0' && argc < MAX_ARGS; p++) {
297 // first, scan whitespaces
298 while (*p != '\0' && strchr(whitespace, *p) != NULL) p++;
299
300 if (*p == '\0') // are we done yet?
301 break;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000302
Benny Prijonobf5b4692007-06-28 03:20:17 +0000303 if (*p == '"' || *p == '\'') { // is token a quoted string
304 cDelimiter = *p++; // save quote delimiter
305 token = p;
306
307 while (*p != '\0' && *p != cDelimiter) p++;
308
309 if (*p == '\0') // found end of the line, but,
310 cDelimiter = '\0'; // didn't find a matching quote
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000311
Benny Prijonobf5b4692007-06-28 03:20:17 +0000312 } else { // token's not a quoted string
313 token = p;
314
315 while (*p != '\0' && strchr(whitespace, *p) == NULL) p++;
316
317 cDelimiter = *p;
318 }
319
320 *p = '\0';
321 token_len = p-token;
322
323 if (token_len > 0) {
324 if (*token == '#')
325 break; // ignore remainder of line
326
327 argv[argc] = pj_pool_alloc(pool, token_len + 1);
328 pj_memcpy(argv[argc], token, token_len + 1);
329 ++argc;
330 }
331
332 *p = cDelimiter;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000333 }
334 }
335
336 /* Copy arguments from command line */
337 for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
338 argv[argc++] = (*app_argv)[i];
339
340 if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
341 PJ_LOG(1,(THIS_FILE,
342 "Too many arguments specified in cmd line/config file"));
343 fflush(stdout);
344 fclose(fhnd);
345 return -1;
346 }
347
348 fclose(fhnd);
349
350 /* Assign the new command line back to the original command line. */
351 *app_argc = argc;
352 *app_argv = argv;
353 return 0;
354
355}
356
357static int my_atoi(const char *cs)
358{
359 pj_str_t s;
Benny Prijono1e2dbe62007-06-15 04:15:16 +0000360
361 pj_cstr(&s, cs);
362 if (cs[0] == '-') {
363 s.ptr++, s.slen--;
364 return 0 - (int)pj_strtoul(&s);
365 } else if (cs[0] == '+') {
366 s.ptr++, s.slen--;
367 return pj_strtoul(&s);
368 } else {
369 return pj_strtoul(&s);
370 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000371}
372
373
374/* Parse arguments. */
375static pj_status_t parse_args(int argc, char *argv[],
376 struct app_config *cfg,
377 pj_str_t *uri_to_call)
378{
379 int c;
380 int option_index;
Benny Prijonoaf09dc32007-04-22 12:48:30 +0000381 enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000382 OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
Benny Prijono0a5cad82006-09-26 13:21:02 +0000383 OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY,
384 OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
Benny Prijono48ab2b72007-11-08 09:24:30 +0000385 OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
Benny Prijonoebbf6892007-03-24 17:37:25 +0000386 OPT_NAMESERVER, OPT_STUN_DOMAIN, OPT_STUN_SRV,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000387 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
388 OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
Benny Prijonof6508982008-01-25 09:02:33 +0000389 OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_USE_ICE, OPT_USE_SRTP,
390 OPT_SRTP_SECURE,
Benny Prijono4af234b2007-01-24 02:02:09 +0000391 OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC,
392 OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
Benny Prijono0a12f002006-07-26 17:05:39 +0000393 OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
Benny Prijonod79f25c2006-08-02 19:41:37 +0000394 OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL,
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000395 OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
Benny Prijonof521eb02006-08-06 23:07:25 +0000396 OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000397 OPT_NOREFERSUB,
Benny Prijonof3bbc132006-12-25 06:43:59 +0000398 OPT_USE_TLS, OPT_TLS_CA_FILE, OPT_TLS_CERT_FILE, OPT_TLS_PRIV_FILE,
399 OPT_TLS_PASSWORD, OPT_TLS_VERIFY_SERVER, OPT_TLS_VERIFY_CLIENT,
400 OPT_TLS_NEG_TIMEOUT,
Benny Prijono4e5d5512007-03-06 18:11:30 +0000401 OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV,
Benny Prijonofce28542007-12-09 15:41:10 +0000402 OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000403 };
404 struct pj_getopt_option long_options[] = {
405 { "config-file",1, 0, OPT_CONFIG_FILE},
406 { "log-file", 1, 0, OPT_LOG_FILE},
407 { "log-level", 1, 0, OPT_LOG_LEVEL},
408 { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
409 { "help", 0, 0, OPT_HELP},
410 { "version", 0, 0, OPT_VERSION},
411 { "clock-rate", 1, 0, OPT_CLOCK_RATE},
412 { "null-audio", 0, 0, OPT_NULL_AUDIO},
413 { "local-port", 1, 0, OPT_LOCAL_PORT},
Benny Prijono0a5cad82006-09-26 13:21:02 +0000414 { "ip-addr", 1, 0, OPT_IP_ADDR},
Benny Prijonoe93e2872006-06-28 16:46:49 +0000415 { "no-tcp", 0, 0, OPT_NO_TCP},
416 { "no-udp", 0, 0, OPT_NO_UDP},
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000417 { "norefersub", 0, 0, OPT_NOREFERSUB},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000418 { "proxy", 1, 0, OPT_PROXY},
419 { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
420 { "registrar", 1, 0, OPT_REGISTRAR},
421 { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000422 { "publish", 0, 0, OPT_PUBLISH},
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000423 { "use-100rel", 0, 0, OPT_100REL},
Benny Prijono48ab2b72007-11-08 09:24:30 +0000424 { "use-ims", 0, 0, OPT_USE_IMS},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000425 { "id", 1, 0, OPT_ID},
426 { "contact", 1, 0, OPT_CONTACT},
Benny Prijonofce28542007-12-09 15:41:10 +0000427 { "auto-update-nat", 1, 0, OPT_AUTO_UPDATE_NAT},
428 { "use-compact-form", 0, 0, OPT_USE_COMPACT_FORM},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000429 { "realm", 1, 0, OPT_REALM},
430 { "username", 1, 0, OPT_USERNAME},
431 { "password", 1, 0, OPT_PASSWORD},
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000432 { "nameserver", 1, 0, OPT_NAMESERVER},
Benny Prijonoebbf6892007-03-24 17:37:25 +0000433 { "stun-domain",1, 0, OPT_STUN_DOMAIN},
Benny Prijonoc97608e2007-03-23 16:34:20 +0000434 { "stun-srv", 1, 0, OPT_STUN_SRV},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000435 { "add-buddy", 1, 0, OPT_ADD_BUDDY},
436 { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
437 { "no-presence", 0, 0, OPT_NO_PRESENCE},
438 { "auto-answer",1, 0, OPT_AUTO_ANSWER},
439 { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
440 { "auto-play", 0, 0, OPT_AUTO_PLAY},
Benny Prijono1ebd6142006-10-19 15:48:02 +0000441 { "auto-rec", 0, 0, OPT_AUTO_REC},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000442 { "auto-loop", 0, 0, OPT_AUTO_LOOP},
443 { "auto-conf", 0, 0, OPT_AUTO_CONF},
444 { "play-file", 1, 0, OPT_PLAY_FILE},
Benny Prijono4af234b2007-01-24 02:02:09 +0000445 { "play-tone", 1, 0, OPT_PLAY_TONE},
Benny Prijono1ebd6142006-10-19 15:48:02 +0000446 { "rec-file", 1, 0, OPT_REC_FILE},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000447 { "rtp-port", 1, 0, OPT_RTP_PORT},
Benny Prijonoc97608e2007-03-23 16:34:20 +0000448 { "use-ice", 0, 0, OPT_USE_ICE},
Benny Prijonod8179652008-01-23 20:39:07 +0000449#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
450 { "use-srtp", 1, 0, OPT_USE_SRTP},
Benny Prijonof6508982008-01-25 09:02:33 +0000451 { "srtp-secure",1, 0, OPT_SRTP_SECURE},
Benny Prijonod8179652008-01-23 20:39:07 +0000452#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000453 { "add-codec", 1, 0, OPT_ADD_CODEC},
Benny Prijonofce28542007-12-09 15:41:10 +0000454 { "dis-codec", 1, 0, OPT_DIS_CODEC},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000455 { "complexity", 1, 0, OPT_COMPLEXITY},
456 { "quality", 1, 0, OPT_QUALITY},
457 { "ptime", 1, 0, OPT_PTIME},
Benny Prijono0a12f002006-07-26 17:05:39 +0000458 { "no-vad", 0, 0, OPT_NO_VAD},
Benny Prijonod79f25c2006-08-02 19:41:37 +0000459 { "ec-tail", 1, 0, OPT_EC_TAIL},
Benny Prijono00cae612006-07-31 15:19:36 +0000460 { "ilbc-mode", 1, 0, OPT_ILBC_MODE},
461 { "rx-drop-pct",1, 0, OPT_RX_DROP_PCT},
462 { "tx-drop-pct",1, 0, OPT_TX_DROP_PCT},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000463 { "next-account",0,0, OPT_NEXT_ACCOUNT},
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000464 { "next-cred", 0, 0, OPT_NEXT_CRED},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000465 { "max-calls", 1, 0, OPT_MAX_CALLS},
Benny Prijonof521eb02006-08-06 23:07:25 +0000466 { "duration", 1, 0, OPT_DURATION},
467 { "thread-cnt", 1, 0, OPT_THREAD_CNT},
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000468 { "use-tls", 0, 0, OPT_USE_TLS},
469 { "tls-ca-file",1, 0, OPT_TLS_CA_FILE},
Benny Prijonof3bbc132006-12-25 06:43:59 +0000470 { "tls-cert-file",1,0, OPT_TLS_CERT_FILE},
471 { "tls-privkey-file",1,0, OPT_TLS_PRIV_FILE},
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000472 { "tls-password",1,0, OPT_TLS_PASSWORD},
Benny Prijonof3bbc132006-12-25 06:43:59 +0000473 { "tls-verify-server", 0, 0, OPT_TLS_VERIFY_SERVER},
474 { "tls-verify-client", 0, 0, OPT_TLS_VERIFY_CLIENT},
475 { "tls-neg-timeout", 1, 0, OPT_TLS_NEG_TIMEOUT},
Benny Prijono4e5d5512007-03-06 18:11:30 +0000476 { "capture-dev", 1, 0, OPT_CAPTURE_DEV},
477 { "playback-dev", 1, 0, OPT_PLAYBACK_DEV},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000478 { NULL, 0, 0, 0}
479 };
480 pj_status_t status;
481 pjsua_acc_config *cur_acc;
482 char *config_file = NULL;
483 unsigned i;
484
485 /* Run pj_getopt once to see if user specifies config file to read. */
Benny Prijonof762ee72006-12-01 11:14:37 +0000486 pj_optind = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000487 while ((c=pj_getopt_long(argc, argv, "", long_options,
488 &option_index)) != -1)
489 {
490 switch (c) {
491 case OPT_CONFIG_FILE:
492 config_file = pj_optarg;
493 break;
494 }
495 if (config_file)
496 break;
497 }
498
499 if (config_file) {
500 status = read_config_file(app_config.pool, config_file, &argc, &argv);
501 if (status != 0)
502 return status;
503 }
504
505 cfg->acc_cnt = 0;
506 cur_acc = &cfg->acc_cfg[0];
507
508
509 /* Reinitialize and re-run pj_getopt again, possibly with new arguments
510 * read from config file.
511 */
512 pj_optind = 0;
513 while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000514 pj_str_t tmp;
515 long lval;
516
517 switch (c) {
518
Benny Prijono6f137482006-06-15 11:31:36 +0000519 case OPT_CONFIG_FILE:
520 /* Ignore as this has been processed before */
521 break;
522
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000523 case OPT_LOG_FILE:
524 cfg->log_cfg.log_filename = pj_str(pj_optarg);
525 break;
526
527 case OPT_LOG_LEVEL:
528 c = pj_strtoul(pj_cstr(&tmp, pj_optarg));
529 if (c < 0 || c > 6) {
530 PJ_LOG(1,(THIS_FILE,
531 "Error: expecting integer value 0-6 "
532 "for --log-level"));
533 return PJ_EINVAL;
534 }
535 cfg->log_cfg.level = c;
536 pj_log_set_level( c );
537 break;
538
539 case OPT_APP_LOG_LEVEL:
540 cfg->log_cfg.console_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
541 if (cfg->log_cfg.console_level < 0 || cfg->log_cfg.console_level > 6) {
542 PJ_LOG(1,(THIS_FILE,
543 "Error: expecting integer value 0-6 "
544 "for --app-log-level"));
545 return PJ_EINVAL;
546 }
547 break;
548
549 case OPT_HELP:
550 usage();
551 return PJ_EINVAL;
552
553 case OPT_VERSION: /* version */
554 pj_dump_config();
555 return PJ_EINVAL;
556
557 case OPT_NULL_AUDIO:
558 cfg->null_audio = PJ_TRUE;
559 break;
560
561 case OPT_CLOCK_RATE:
562 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
563 if (lval < 8000 || lval > 48000) {
564 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
565 "8000-48000 for clock rate"));
566 return PJ_EINVAL;
567 }
568 cfg->media_cfg.clock_rate = lval;
569 break;
570
571 case OPT_LOCAL_PORT: /* local-port */
572 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
Benny Prijonoe347cb02007-02-14 14:36:13 +0000573 if (lval < 0 || lval > 65535) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000574 PJ_LOG(1,(THIS_FILE,
575 "Error: expecting integer value for "
576 "--local-port"));
577 return PJ_EINVAL;
578 }
579 cfg->udp_cfg.port = (pj_uint16_t)lval;
580 break;
581
Benny Prijono0a5cad82006-09-26 13:21:02 +0000582 case OPT_IP_ADDR: /* ip-addr */
583 cfg->udp_cfg.public_addr = pj_str(pj_optarg);
584 cfg->rtp_cfg.public_addr = pj_str(pj_optarg);
585 break;
586
Benny Prijonoe93e2872006-06-28 16:46:49 +0000587 case OPT_NO_UDP: /* no-udp */
588 if (cfg->no_tcp) {
Benny Prijonob988d762007-12-05 04:30:04 +0000589 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
590 return PJ_EINVAL;
Benny Prijonoe93e2872006-06-28 16:46:49 +0000591 }
592
593 cfg->no_udp = PJ_TRUE;
594 break;
595
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000596 case OPT_NOREFERSUB: /* norefersub */
597 cfg->no_refersub = PJ_TRUE;
598 break;
599
Benny Prijonoe93e2872006-06-28 16:46:49 +0000600 case OPT_NO_TCP: /* no-tcp */
601 if (cfg->no_udp) {
Benny Prijonob988d762007-12-05 04:30:04 +0000602 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
603 return PJ_EINVAL;
Benny Prijonoe93e2872006-06-28 16:46:49 +0000604 }
605
606 cfg->no_tcp = PJ_TRUE;
607 break;
608
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000609 case OPT_PROXY: /* proxy */
610 if (pjsua_verify_sip_url(pj_optarg) != 0) {
611 PJ_LOG(1,(THIS_FILE,
612 "Error: invalid SIP URL '%s' "
613 "in proxy argument", pj_optarg));
614 return PJ_EINVAL;
615 }
616 cur_acc->proxy[cur_acc->proxy_cnt++] = pj_str(pj_optarg);
617 break;
618
619 case OPT_OUTBOUND_PROXY: /* outbound proxy */
620 if (pjsua_verify_sip_url(pj_optarg) != 0) {
621 PJ_LOG(1,(THIS_FILE,
622 "Error: invalid SIP URL '%s' "
623 "in outbound proxy argument", pj_optarg));
624 return PJ_EINVAL;
625 }
626 cfg->cfg.outbound_proxy[cfg->cfg.outbound_proxy_cnt++] = pj_str(pj_optarg);
627 break;
628
629 case OPT_REGISTRAR: /* registrar */
630 if (pjsua_verify_sip_url(pj_optarg) != 0) {
631 PJ_LOG(1,(THIS_FILE,
632 "Error: invalid SIP URL '%s' in "
633 "registrar argument", pj_optarg));
634 return PJ_EINVAL;
635 }
636 cur_acc->reg_uri = pj_str(pj_optarg);
637 break;
638
639 case OPT_REG_TIMEOUT: /* reg-timeout */
640 cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,pj_optarg));
641 if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
642 PJ_LOG(1,(THIS_FILE,
643 "Error: invalid value for --reg-timeout "
644 "(expecting 1-3600)"));
645 return PJ_EINVAL;
646 }
647 break;
648
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000649 case OPT_PUBLISH: /* publish */
650 cur_acc->publish_enabled = PJ_TRUE;
651 break;
652
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000653 case OPT_100REL: /** 100rel */
654 cur_acc->require_100rel = PJ_TRUE;
655 cfg->cfg.require_100rel = PJ_TRUE;
656 break;
657
Benny Prijono48ab2b72007-11-08 09:24:30 +0000658 case OPT_USE_IMS: /* Activate IMS settings */
659 cur_acc->auth_pref.initial_auth = PJ_TRUE;
Benny Prijono2a67ea42007-10-25 02:51:33 +0000660 break;
661
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000662 case OPT_ID: /* id */
663 if (pjsua_verify_sip_url(pj_optarg) != 0) {
664 PJ_LOG(1,(THIS_FILE,
665 "Error: invalid SIP URL '%s' "
666 "in local id argument", pj_optarg));
667 return PJ_EINVAL;
668 }
669 cur_acc->id = pj_str(pj_optarg);
670 break;
671
672 case OPT_CONTACT: /* contact */
673 if (pjsua_verify_sip_url(pj_optarg) != 0) {
674 PJ_LOG(1,(THIS_FILE,
675 "Error: invalid SIP URL '%s' "
676 "in contact argument", pj_optarg));
677 return PJ_EINVAL;
678 }
Benny Prijonob4a17c92006-07-10 14:40:21 +0000679 cur_acc->force_contact = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000680 break;
681
Benny Prijonofce28542007-12-09 15:41:10 +0000682 case OPT_AUTO_UPDATE_NAT: /* OPT_AUTO_UPDATE_NAT */
683 cur_acc->auto_update_nat = pj_strtoul(pj_cstr(&tmp, pj_optarg));
684 break;
685
686 case OPT_USE_COMPACT_FORM:
687 /* enable compact form - from Ticket #342 */
688 {
689 extern pj_bool_t pjsip_use_compact_form;
690 extern pj_bool_t pjsip_include_allow_hdr_in_dlg;
691 extern pj_bool_t pjmedia_add_rtpmap_for_static_pt;
692
693 pjsip_use_compact_form = PJ_TRUE;
694 /* do not transmit Allow header */
695 pjsip_include_allow_hdr_in_dlg = PJ_FALSE;
696 /* Do not include rtpmap for static payload types (<96) */
697 pjmedia_add_rtpmap_for_static_pt = PJ_FALSE;
698 }
699 break;
700
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000701 case OPT_NEXT_ACCOUNT: /* Add more account. */
702 cfg->acc_cnt++;
Benny Prijono56315612006-07-18 14:39:40 +0000703 cur_acc = &cfg->acc_cfg[cfg->acc_cnt];
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000704 break;
705
706 case OPT_USERNAME: /* Default authentication user */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000707 cur_acc->cred_info[cur_acc->cred_count].username = pj_str(pj_optarg);
Benny Prijono48ab2b72007-11-08 09:24:30 +0000708 cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("Digest");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000709 break;
710
711 case OPT_REALM: /* Default authentication realm. */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000712 cur_acc->cred_info[cur_acc->cred_count].realm = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000713 break;
714
715 case OPT_PASSWORD: /* authentication password */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000716 cur_acc->cred_info[cur_acc->cred_count].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
717 cur_acc->cred_info[cur_acc->cred_count].data = pj_str(pj_optarg);
Benny Prijono28f673a2007-10-15 07:04:59 +0000718#if PJSIP_HAS_DIGEST_AKA_AUTH
719 cur_acc->cred_info[cur_acc->cred_count].data_type |= PJSIP_CRED_DATA_EXT_AKA;
720 cur_acc->cred_info[cur_acc->cred_count].ext.aka.k = pj_str(pj_optarg);
721 cur_acc->cred_info[cur_acc->cred_count].ext.aka.cb = &pjsip_auth_create_aka_response;
722#endif
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000723 break;
724
725 case OPT_NEXT_CRED: /* next credential */
726 cur_acc->cred_count++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000727 break;
728
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000729 case OPT_NAMESERVER: /* nameserver */
730 cfg->cfg.nameserver[cfg->cfg.nameserver_count++] = pj_str(pj_optarg);
731 if (cfg->cfg.nameserver_count > PJ_ARRAY_SIZE(cfg->cfg.nameserver)) {
732 PJ_LOG(1,(THIS_FILE, "Error: too many nameservers"));
733 return PJ_ETOOMANY;
734 }
735 break;
736
Benny Prijonoebbf6892007-03-24 17:37:25 +0000737 case OPT_STUN_DOMAIN: /* STUN domain */
738 cfg->cfg.stun_domain = pj_str(pj_optarg);
739 break;
740
Benny Prijonoc97608e2007-03-23 16:34:20 +0000741 case OPT_STUN_SRV: /* STUN server */
Benny Prijonoebbf6892007-03-24 17:37:25 +0000742 cfg->cfg.stun_host = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000743 break;
744
745 case OPT_ADD_BUDDY: /* Add to buddy list. */
746 if (pjsua_verify_sip_url(pj_optarg) != 0) {
747 PJ_LOG(1,(THIS_FILE,
748 "Error: invalid URL '%s' in "
749 "--add-buddy option", pj_optarg));
750 return -1;
751 }
752 if (cfg->buddy_cnt == PJ_ARRAY_SIZE(cfg->buddy_cfg)) {
753 PJ_LOG(1,(THIS_FILE,
754 "Error: too many buddies in buddy list."));
755 return -1;
756 }
757 cfg->buddy_cfg[cfg->buddy_cnt].uri = pj_str(pj_optarg);
758 cfg->buddy_cnt++;
759 break;
760
761 case OPT_AUTO_PLAY:
762 cfg->auto_play = 1;
763 break;
764
Benny Prijono1ebd6142006-10-19 15:48:02 +0000765 case OPT_AUTO_REC:
766 cfg->auto_rec = 1;
767 break;
768
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000769 case OPT_AUTO_LOOP:
770 cfg->auto_loop = 1;
771 break;
772
Benny Prijono7ca96da2006-08-07 12:11:40 +0000773 case OPT_AUTO_CONF:
774 cfg->auto_conf = 1;
775 break;
776
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000777 case OPT_PLAY_FILE:
Benny Prijono32e4f492007-01-24 00:44:26 +0000778 cfg->wav_files[cfg->wav_count++] = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000779 break;
780
Benny Prijono4af234b2007-01-24 02:02:09 +0000781 case OPT_PLAY_TONE:
782 {
783 int f1, f2, on, off;
784 int n;
785
786 n = sscanf(pj_optarg, "%d,%d,%d,%d", &f1, &f2, &on, &off);
787 if (n != 4) {
788 puts("Expecting f1,f2,on,off in --play-tone");
789 return -1;
790 }
791
792 cfg->tones[cfg->tone_count].freq1 = (short)f1;
793 cfg->tones[cfg->tone_count].freq2 = (short)f2;
794 cfg->tones[cfg->tone_count].on_msec = (short)on;
795 cfg->tones[cfg->tone_count].off_msec = (short)off;
796 ++cfg->tone_count;
797 }
798 break;
799
Benny Prijono1ebd6142006-10-19 15:48:02 +0000800 case OPT_REC_FILE:
801 cfg->rec_file = pj_str(pj_optarg);
802 break;
803
Benny Prijonoc97608e2007-03-23 16:34:20 +0000804 case OPT_USE_ICE:
805 cfg->media_cfg.enable_ice = PJ_TRUE;
806 break;
807
Benny Prijonod8179652008-01-23 20:39:07 +0000808#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
809 case OPT_USE_SRTP:
810 app_config.cfg.use_srtp = my_atoi(pj_optarg);
811 if (!pj_isdigit(*pj_optarg) || app_config.cfg.use_srtp > 2) {
812 PJ_LOG(1,(THIS_FILE, "Invalid value for --use-srtp option"));
813 return -1;
814 }
Benny Prijono3ec13c72008-01-29 11:52:58 +0000815 cur_acc->use_srtp = app_config.cfg.use_srtp;
Benny Prijonod8179652008-01-23 20:39:07 +0000816 break;
Benny Prijonof6508982008-01-25 09:02:33 +0000817 case OPT_SRTP_SECURE:
818 app_config.cfg.srtp_secure_signaling = my_atoi(pj_optarg);
819 if (!pj_isdigit(*pj_optarg) ||
820 app_config.cfg.srtp_secure_signaling > 2)
821 {
822 PJ_LOG(1,(THIS_FILE, "Invalid value for --srtp-secure option"));
823 return -1;
824 }
Benny Prijono3ec13c72008-01-29 11:52:58 +0000825 cur_acc->srtp_secure_signaling = app_config.cfg.srtp_secure_signaling;
Benny Prijonof6508982008-01-25 09:02:33 +0000826 break;
Benny Prijonod8179652008-01-23 20:39:07 +0000827#endif
828
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000829 case OPT_RTP_PORT:
830 cfg->rtp_cfg.port = my_atoi(pj_optarg);
Benny Prijono5583a802007-06-26 12:20:37 +0000831 if (cfg->rtp_cfg.port == 0) {
832 enum { START_PORT=4000 };
833 unsigned range;
834
835 range = (65535-START_PORT-PJSUA_MAX_CALLS*2);
836 cfg->rtp_cfg.port = START_PORT +
837 ((pj_rand() % range) & 0xFFFE);
838 }
839
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000840 if (cfg->rtp_cfg.port < 1 || cfg->rtp_cfg.port > 65535) {
841 PJ_LOG(1,(THIS_FILE,
842 "Error: rtp-port argument value "
843 "(expecting 1-65535"));
844 return -1;
845 }
846 break;
847
Benny Prijonofce28542007-12-09 15:41:10 +0000848 case OPT_DIS_CODEC:
849 cfg->codec_dis[cfg->codec_dis_cnt++] = pj_str(pj_optarg);
850 break;
851
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000852 case OPT_ADD_CODEC:
853 cfg->codec_arg[cfg->codec_cnt++] = pj_str(pj_optarg);
854 break;
855
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000856 /* These options were no longer valid after new pjsua */
857 /*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000858 case OPT_COMPLEXITY:
859 cfg->complexity = my_atoi(pj_optarg);
860 if (cfg->complexity < 0 || cfg->complexity > 10) {
861 PJ_LOG(1,(THIS_FILE,
862 "Error: invalid --complexity (expecting 0-10"));
863 return -1;
864 }
865 break;
Benny Prijono804ff0a2006-09-14 11:17:48 +0000866 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000867
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000868 case OPT_DURATION:
869 cfg->duration = my_atoi(pj_optarg);
870 break;
871
Benny Prijonof521eb02006-08-06 23:07:25 +0000872 case OPT_THREAD_CNT:
873 cfg->cfg.thread_cnt = my_atoi(pj_optarg);
874 if (cfg->cfg.thread_cnt > 128) {
875 PJ_LOG(1,(THIS_FILE,
876 "Error: invalid --thread-cnt option"));
877 return -1;
878 }
879 break;
880
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000881 case OPT_PTIME:
Benny Prijono0a12f002006-07-26 17:05:39 +0000882 cfg->media_cfg.ptime = my_atoi(pj_optarg);
883 if (cfg->media_cfg.ptime < 10 || cfg->media_cfg.ptime > 1000) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000884 PJ_LOG(1,(THIS_FILE,
885 "Error: invalid --ptime option"));
886 return -1;
887 }
888 break;
889
Benny Prijono0a12f002006-07-26 17:05:39 +0000890 case OPT_NO_VAD:
891 cfg->media_cfg.no_vad = PJ_TRUE;
892 break;
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000893
Benny Prijonod79f25c2006-08-02 19:41:37 +0000894 case OPT_EC_TAIL:
895 cfg->media_cfg.ec_tail_len = my_atoi(pj_optarg);
896 if (cfg->media_cfg.ec_tail_len > 1000) {
897 PJ_LOG(1,(THIS_FILE, "I think the ec-tail length setting "
898 "is too big"));
899 return -1;
900 }
901 break;
902
Benny Prijono0498d902006-06-19 14:49:14 +0000903 case OPT_QUALITY:
904 cfg->media_cfg.quality = my_atoi(pj_optarg);
905 if (cfg->media_cfg.quality < 0 || cfg->media_cfg.quality > 10) {
906 PJ_LOG(1,(THIS_FILE,
907 "Error: invalid --quality (expecting 0-10"));
908 return -1;
909 }
910 break;
911
Benny Prijono00cae612006-07-31 15:19:36 +0000912 case OPT_ILBC_MODE:
913 cfg->media_cfg.ilbc_mode = my_atoi(pj_optarg);
914 if (cfg->media_cfg.ilbc_mode!=20 && cfg->media_cfg.ilbc_mode!=30) {
915 PJ_LOG(1,(THIS_FILE,
916 "Error: invalid --ilbc-mode (expecting 20 or 30"));
917 return -1;
918 }
919 break;
920
921 case OPT_RX_DROP_PCT:
922 cfg->media_cfg.rx_drop_pct = my_atoi(pj_optarg);
923 if (cfg->media_cfg.rx_drop_pct > 100) {
924 PJ_LOG(1,(THIS_FILE,
925 "Error: invalid --rx-drop-pct (expecting <= 100"));
926 return -1;
927 }
928 break;
929
930 case OPT_TX_DROP_PCT:
931 cfg->media_cfg.tx_drop_pct = my_atoi(pj_optarg);
932 if (cfg->media_cfg.tx_drop_pct > 100) {
933 PJ_LOG(1,(THIS_FILE,
934 "Error: invalid --tx-drop-pct (expecting <= 100"));
935 return -1;
936 }
937 break;
938
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000939 case OPT_AUTO_ANSWER:
940 cfg->auto_answer = my_atoi(pj_optarg);
941 if (cfg->auto_answer < 100 || cfg->auto_answer > 699) {
942 PJ_LOG(1,(THIS_FILE,
943 "Error: invalid code in --auto-answer "
944 "(expecting 100-699"));
945 return -1;
946 }
947 break;
948
949 case OPT_MAX_CALLS:
950 cfg->cfg.max_calls = my_atoi(pj_optarg);
Benny Prijono48af79c2006-07-22 12:49:17 +0000951 if (cfg->cfg.max_calls < 1 || cfg->cfg.max_calls > PJSUA_MAX_CALLS) {
Benny Prijono804ff0a2006-09-14 11:17:48 +0000952 PJ_LOG(1,(THIS_FILE,"Error: maximum call setting exceeds "
953 "compile time limit (PJSUA_MAX_CALLS=%d)",
Benny Prijono48af79c2006-07-22 12:49:17 +0000954 PJSUA_MAX_CALLS));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000955 return -1;
956 }
957 break;
958
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000959 case OPT_USE_TLS:
960 cfg->use_tls = PJ_TRUE;
Benny Prijonof3bbc132006-12-25 06:43:59 +0000961#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
962 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
963 return -1;
964#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000965 break;
966
967 case OPT_TLS_CA_FILE:
Benny Prijonof3bbc132006-12-25 06:43:59 +0000968 cfg->udp_cfg.tls_setting.ca_list_file = pj_str(pj_optarg);
969#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
970 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
971 return -1;
972#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000973 break;
974
Benny Prijonof3bbc132006-12-25 06:43:59 +0000975 case OPT_TLS_CERT_FILE:
976 cfg->udp_cfg.tls_setting.cert_file = pj_str(pj_optarg);
977#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
978 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
979 return -1;
980#endif
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000981 break;
982
Benny Prijonof3bbc132006-12-25 06:43:59 +0000983 case OPT_TLS_PRIV_FILE:
984 cfg->udp_cfg.tls_setting.privkey_file = pj_str(pj_optarg);
985 break;
986
Benny Prijono6e0e54b2006-12-08 21:58:31 +0000987 case OPT_TLS_PASSWORD:
Benny Prijonof3bbc132006-12-25 06:43:59 +0000988 cfg->udp_cfg.tls_setting.password = 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
993 break;
994
995 case OPT_TLS_VERIFY_SERVER:
996 cfg->udp_cfg.tls_setting.verify_server = PJ_TRUE;
997 break;
998
999 case OPT_TLS_VERIFY_CLIENT:
1000 cfg->udp_cfg.tls_setting.verify_client = PJ_TRUE;
1001 break;
1002
1003 case OPT_TLS_NEG_TIMEOUT:
1004 cfg->udp_cfg.tls_setting.timeout.sec = atoi(pj_optarg);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001005 break;
1006
Benny Prijono4e5d5512007-03-06 18:11:30 +00001007 case OPT_CAPTURE_DEV:
1008 cfg->capture_dev = atoi(pj_optarg);
1009 break;
1010
1011 case OPT_PLAYBACK_DEV:
1012 cfg->playback_dev = atoi(pj_optarg);
1013 break;
1014
Benny Prijono22a300a2006-06-14 20:04:55 +00001015 default:
Benny Prijono787b8692006-06-19 12:40:03 +00001016 PJ_LOG(1,(THIS_FILE,
Benny Prijonod6388ac2006-09-09 13:23:09 +00001017 "Argument \"%s\" is not valid. Use --help to see help",
Benny Prijonoaf09dc32007-04-22 12:48:30 +00001018 argv[pj_optind-1]));
Benny Prijono22a300a2006-06-14 20:04:55 +00001019 return -1;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001020 }
1021 }
1022
1023 if (pj_optind != argc) {
1024 pj_str_t uri_arg;
1025
1026 if (pjsua_verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
1027 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
1028 return -1;
1029 }
1030 uri_arg = pj_str(argv[pj_optind]);
1031 if (uri_to_call)
1032 *uri_to_call = uri_arg;
1033 pj_optind++;
1034
1035 /* Add URI to call to buddy list if it's not already there */
1036 for (i=0; i<cfg->buddy_cnt; ++i) {
1037 if (pj_stricmp(&cfg->buddy_cfg[i].uri, &uri_arg)==0)
1038 break;
1039 }
1040 if (i == cfg->buddy_cnt && cfg->buddy_cnt < PJSUA_MAX_BUDDIES) {
1041 cfg->buddy_cfg[cfg->buddy_cnt++].uri = uri_arg;
1042 }
1043
1044 } else {
1045 if (uri_to_call)
1046 uri_to_call->slen = 0;
1047 }
1048
1049 if (pj_optind != argc) {
1050 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
1051 return PJ_EINVAL;
1052 }
1053
Benny Prijono56315612006-07-18 14:39:40 +00001054 if (cfg->acc_cfg[cfg->acc_cnt].id.slen)
1055 cfg->acc_cnt++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001056
1057 for (i=0; i<cfg->acc_cnt; ++i) {
Benny Prijono48ab2b72007-11-08 09:24:30 +00001058 pjsua_acc_config *acfg = &cfg->acc_cfg[i];
1059
1060 if (acfg->cred_info[acfg->cred_count].username.slen)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001061 {
Benny Prijono48ab2b72007-11-08 09:24:30 +00001062 acfg->cred_count++;
1063 }
1064
1065 /* When IMS mode is enabled for the account, verify that settings
1066 * are okay.
1067 */
1068 /* For now we check if IMS mode is activated by looking if
1069 * initial_auth is set.
1070 */
1071 if (acfg->auth_pref.initial_auth && acfg->cred_count) {
1072 /* Realm must point to the real domain */
1073 if (*acfg->cred_info[0].realm.ptr=='*') {
1074 PJ_LOG(1,(THIS_FILE,
1075 "Error: cannot use '*' as realm with IMS"));
1076 return PJ_EINVAL;
1077 }
1078
1079 /* Username for authentication must be in a@b format */
1080 if (strchr(acfg->cred_info[0].username.ptr, '@')==0) {
1081 PJ_LOG(1,(THIS_FILE,
1082 "Error: Username for authentication must "
1083 "be in user@domain format with IMS"));
1084 return PJ_EINVAL;
1085 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001086 }
1087 }
1088
1089
1090 return PJ_SUCCESS;
1091}
1092
1093
1094/*
1095 * Save account settings
1096 */
1097static void write_account_settings(int acc_index, pj_str_t *result)
1098{
1099 unsigned i;
1100 char line[128];
1101 pjsua_acc_config *acc_cfg = &app_config.acc_cfg[acc_index];
1102
1103
1104 pj_ansi_sprintf(line, "\n#\n# Account %d:\n#\n", acc_index);
1105 pj_strcat2(result, line);
1106
1107
1108 /* Identity */
1109 if (acc_cfg->id.slen) {
1110 pj_ansi_sprintf(line, "--id %.*s\n",
1111 (int)acc_cfg->id.slen,
1112 acc_cfg->id.ptr);
1113 pj_strcat2(result, line);
1114 }
1115
1116 /* Registrar server */
1117 if (acc_cfg->reg_uri.slen) {
1118 pj_ansi_sprintf(line, "--registrar %.*s\n",
1119 (int)acc_cfg->reg_uri.slen,
1120 acc_cfg->reg_uri.ptr);
1121 pj_strcat2(result, line);
1122
1123 pj_ansi_sprintf(line, "--reg-timeout %u\n",
1124 acc_cfg->reg_timeout);
1125 pj_strcat2(result, line);
1126 }
1127
1128 /* Contact */
Benny Prijonob4a17c92006-07-10 14:40:21 +00001129 if (acc_cfg->force_contact.slen) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001130 pj_ansi_sprintf(line, "--contact %.*s\n",
Benny Prijonob4a17c92006-07-10 14:40:21 +00001131 (int)acc_cfg->force_contact.slen,
1132 acc_cfg->force_contact.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001133 pj_strcat2(result, line);
1134 }
1135
Benny Prijonofce28542007-12-09 15:41:10 +00001136 /* */
1137 //if (acc_cfg->auto_update_nat)
1138 {
1139 pj_ansi_sprintf(line, "--auto-update-nat %i\n",
1140 (int)acc_cfg->auto_update_nat);
1141 pj_strcat2(result, line);
1142 }
1143
Benny Prijonod8179652008-01-23 20:39:07 +00001144#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
1145 /* SRTP */
1146 if (acc_cfg->use_srtp) {
1147 pj_ansi_sprintf(line, "--use-srtp %i\n",
1148 (int)acc_cfg->use_srtp);
1149 pj_strcat2(result, line);
1150 }
1151#endif
1152
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001153 /* Proxy */
1154 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
1155 pj_ansi_sprintf(line, "--proxy %.*s\n",
1156 (int)acc_cfg->proxy[i].slen,
1157 acc_cfg->proxy[i].ptr);
1158 pj_strcat2(result, line);
1159 }
1160
1161 /* Credentials */
1162 for (i=0; i<acc_cfg->cred_count; ++i) {
1163 if (acc_cfg->cred_info[i].realm.slen) {
1164 pj_ansi_sprintf(line, "--realm %.*s\n",
1165 (int)acc_cfg->cred_info[i].realm.slen,
1166 acc_cfg->cred_info[i].realm.ptr);
1167 pj_strcat2(result, line);
1168 }
1169
1170 if (acc_cfg->cred_info[i].username.slen) {
1171 pj_ansi_sprintf(line, "--username %.*s\n",
1172 (int)acc_cfg->cred_info[i].username.slen,
1173 acc_cfg->cred_info[i].username.ptr);
1174 pj_strcat2(result, line);
1175 }
1176
1177 if (acc_cfg->cred_info[i].data.slen) {
1178 pj_ansi_sprintf(line, "--password %.*s\n",
1179 (int)acc_cfg->cred_info[i].data.slen,
1180 acc_cfg->cred_info[i].data.ptr);
1181 pj_strcat2(result, line);
1182 }
Benny Prijonoeef4f8c2006-06-19 12:07:44 +00001183
1184 if (i != acc_cfg->cred_count - 1)
1185 pj_strcat2(result, "--next-cred\n");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001186 }
1187
1188}
1189
1190
1191/*
1192 * Write settings.
1193 */
1194static int write_settings(const struct app_config *config,
1195 char *buf, pj_size_t max)
1196{
1197 unsigned acc_index;
1198 unsigned i;
1199 pj_str_t cfg;
1200 char line[128];
Benny Prijonofce28542007-12-09 15:41:10 +00001201 extern pj_bool_t pjsip_use_compact_form;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001202
1203 PJ_UNUSED_ARG(max);
1204
1205 cfg.ptr = buf;
1206 cfg.slen = 0;
1207
1208 /* Logging. */
1209 pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
1210 pj_ansi_sprintf(line, "--log-level %d\n",
1211 config->log_cfg.level);
1212 pj_strcat2(&cfg, line);
1213
1214 pj_ansi_sprintf(line, "--app-log-level %d\n",
1215 config->log_cfg.console_level);
1216 pj_strcat2(&cfg, line);
1217
1218 if (config->log_cfg.log_filename.slen) {
1219 pj_ansi_sprintf(line, "--log-file %.*s\n",
1220 (int)config->log_cfg.log_filename.slen,
1221 config->log_cfg.log_filename.ptr);
1222 pj_strcat2(&cfg, line);
1223 }
1224
1225
1226 /* Save account settings. */
1227 for (acc_index=0; acc_index < config->acc_cnt; ++acc_index) {
1228
1229 write_account_settings(acc_index, &cfg);
1230
1231 if (acc_index < config->acc_cnt-1)
1232 pj_strcat2(&cfg, "--next-account\n");
1233 }
1234
1235
1236 pj_strcat2(&cfg, "\n#\n# Network settings:\n#\n");
1237
1238 /* Outbound proxy */
1239 for (i=0; i<config->cfg.outbound_proxy_cnt; ++i) {
1240 pj_ansi_sprintf(line, "--outbound %.*s\n",
1241 (int)config->cfg.outbound_proxy[i].slen,
1242 config->cfg.outbound_proxy[i].ptr);
1243 pj_strcat2(&cfg, line);
1244 }
1245
1246
1247 /* UDP Transport. */
1248 pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port);
1249 pj_strcat2(&cfg, line);
1250
Benny Prijono0a5cad82006-09-26 13:21:02 +00001251 /* IP address, if any. */
1252 if (config->udp_cfg.public_addr.slen) {
1253 pj_ansi_sprintf(line, "--ip-addr %.*s\n",
1254 (int)config->udp_cfg.public_addr.slen,
1255 config->udp_cfg.public_addr.ptr);
1256 pj_strcat2(&cfg, line);
1257 }
1258
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001259 /* No TCP ? */
1260 if (config->no_tcp) {
1261 pj_strcat2(&cfg, "--no-tcp\n");
1262 }
1263
1264 /* No UDP ? */
1265 if (config->no_udp) {
1266 pj_strcat2(&cfg, "--no-udp\n");
1267 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001268
1269 /* STUN */
Benny Prijonoebbf6892007-03-24 17:37:25 +00001270 if (config->cfg.stun_domain.slen) {
1271 pj_ansi_sprintf(line, "--stun-domain %.*s\n",
1272 (int)config->cfg.stun_domain.slen,
1273 config->cfg.stun_domain.ptr);
1274 pj_strcat2(&cfg, line);
1275 }
1276 if (config->cfg.stun_host.slen) {
Benny Prijonoc97608e2007-03-23 16:34:20 +00001277 pj_ansi_sprintf(line, "--stun-srv %.*s\n",
Benny Prijonoebbf6892007-03-24 17:37:25 +00001278 (int)config->cfg.stun_host.slen,
1279 config->cfg.stun_host.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001280 pj_strcat2(&cfg, line);
1281 }
1282
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001283 /* TLS */
1284 if (config->use_tls)
1285 pj_strcat2(&cfg, "--use-tls\n");
Benny Prijonof3bbc132006-12-25 06:43:59 +00001286 if (config->udp_cfg.tls_setting.ca_list_file.slen) {
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001287 pj_ansi_sprintf(line, "--tls-ca-file %.*s\n",
Benny Prijonof3bbc132006-12-25 06:43:59 +00001288 (int)config->udp_cfg.tls_setting.ca_list_file.slen,
1289 config->udp_cfg.tls_setting.ca_list_file.ptr);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001290 pj_strcat2(&cfg, line);
1291 }
Benny Prijonof3bbc132006-12-25 06:43:59 +00001292 if (config->udp_cfg.tls_setting.cert_file.slen) {
1293 pj_ansi_sprintf(line, "--tls-cert-file %.*s\n",
1294 (int)config->udp_cfg.tls_setting.cert_file.slen,
1295 config->udp_cfg.tls_setting.cert_file.ptr);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001296 pj_strcat2(&cfg, line);
1297 }
Benny Prijonof3bbc132006-12-25 06:43:59 +00001298 if (config->udp_cfg.tls_setting.privkey_file.slen) {
1299 pj_ansi_sprintf(line, "--tls-privkey-file %.*s\n",
1300 (int)config->udp_cfg.tls_setting.privkey_file.slen,
1301 config->udp_cfg.tls_setting.privkey_file.ptr);
1302 pj_strcat2(&cfg, line);
1303 }
1304
1305 if (config->udp_cfg.tls_setting.password.slen) {
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001306 pj_ansi_sprintf(line, "--tls-password %.*s\n",
Benny Prijonof3bbc132006-12-25 06:43:59 +00001307 (int)config->udp_cfg.tls_setting.password.slen,
1308 config->udp_cfg.tls_setting.password.ptr);
1309 pj_strcat2(&cfg, line);
1310 }
1311
1312 if (config->udp_cfg.tls_setting.verify_server)
1313 pj_strcat2(&cfg, "--tls-verify-server\n");
1314
1315 if (config->udp_cfg.tls_setting.verify_client)
1316 pj_strcat2(&cfg, "--tls-verify-client\n");
1317
1318 if (config->udp_cfg.tls_setting.timeout.sec) {
1319 pj_ansi_sprintf(line, "--tls-neg-timeout %d\n",
Benny Prijonoe960bb52007-01-21 17:53:39 +00001320 (int)config->udp_cfg.tls_setting.timeout.sec);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00001321 pj_strcat2(&cfg, line);
1322 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001323
1324 pj_strcat2(&cfg, "\n#\n# Media settings:\n#\n");
1325
Benny Prijonof6508982008-01-25 09:02:33 +00001326 /* SRTP */
Benny Prijonofe5a6942008-02-18 12:16:23 +00001327#if PJMEDIA_HAS_SRTP
Benny Prijonof6508982008-01-25 09:02:33 +00001328 if (app_config.cfg.use_srtp != PJSUA_DEFAULT_USE_SRTP) {
1329 pj_ansi_sprintf(line, "--use-srtp %d\n",
1330 app_config.cfg.use_srtp);
1331 pj_strcat2(&cfg, line);
1332 }
1333 if (app_config.cfg.srtp_secure_signaling !=
1334 PJSUA_DEFAULT_SRTP_SECURE_SIGNALING)
1335 {
1336 pj_ansi_sprintf(line, "--srtp-secure %d\n",
1337 app_config.cfg.srtp_secure_signaling);
1338 pj_strcat2(&cfg, line);
1339 }
Benny Prijonofe5a6942008-02-18 12:16:23 +00001340#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001341
1342 /* Media */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001343 if (config->media_cfg.enable_ice)
1344 pj_strcat2(&cfg, "--use-ice\n");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001345 if (config->null_audio)
1346 pj_strcat2(&cfg, "--null-audio\n");
1347 if (config->auto_play)
1348 pj_strcat2(&cfg, "--auto-play\n");
1349 if (config->auto_loop)
1350 pj_strcat2(&cfg, "--auto-loop\n");
Benny Prijono7ca96da2006-08-07 12:11:40 +00001351 if (config->auto_conf)
1352 pj_strcat2(&cfg, "--auto-conf\n");
Benny Prijono32e4f492007-01-24 00:44:26 +00001353 for (i=0; i<config->wav_count; ++i) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001354 pj_ansi_sprintf(line, "--play-file %s\n",
Benny Prijono32e4f492007-01-24 00:44:26 +00001355 config->wav_files[i].ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001356 pj_strcat2(&cfg, line);
1357 }
Benny Prijono4af234b2007-01-24 02:02:09 +00001358 for (i=0; i<config->tone_count; ++i) {
1359 pj_ansi_sprintf(line, "--play-tone %d,%d,%d,%d\n",
1360 config->tones[i].freq1, config->tones[i].freq2,
1361 config->tones[i].on_msec, config->tones[i].off_msec);
1362 pj_strcat2(&cfg, line);
1363 }
Benny Prijono1ebd6142006-10-19 15:48:02 +00001364 if (config->rec_file.slen) {
1365 pj_ansi_sprintf(line, "--rec-file %s\n",
1366 config->rec_file.ptr);
1367 pj_strcat2(&cfg, line);
1368 }
1369 if (config->auto_rec)
1370 pj_strcat2(&cfg, "--auto-rec\n");
Benny Prijono4e5d5512007-03-06 18:11:30 +00001371 if (config->capture_dev != PJSUA_INVALID_ID) {
1372 pj_ansi_sprintf(line, "--capture-dev %d\n", config->capture_dev);
1373 pj_strcat2(&cfg, line);
1374 }
1375 if (config->playback_dev != PJSUA_INVALID_ID) {
1376 pj_ansi_sprintf(line, "--playback-dev %d\n", config->playback_dev);
1377 pj_strcat2(&cfg, line);
1378 }
Benny Prijono1ebd6142006-10-19 15:48:02 +00001379
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001380 /* Media clock rate. */
Benny Prijono70972992006-08-05 11:13:58 +00001381 if (config->media_cfg.clock_rate != PJSUA_DEFAULT_CLOCK_RATE) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001382 pj_ansi_sprintf(line, "--clock-rate %d\n",
Benny Prijono0498d902006-06-19 14:49:14 +00001383 config->media_cfg.clock_rate);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001384 pj_strcat2(&cfg, line);
Benny Prijono70972992006-08-05 11:13:58 +00001385 } else {
1386 pj_ansi_sprintf(line, "#using default --clock-rate %d\n",
1387 config->media_cfg.clock_rate);
1388 pj_strcat2(&cfg, line);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001389 }
Benny Prijono70972992006-08-05 11:13:58 +00001390
1391 /* quality */
1392 if (config->media_cfg.quality != PJSUA_DEFAULT_CODEC_QUALITY) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001393 pj_ansi_sprintf(line, "--quality %d\n",
Benny Prijono0498d902006-06-19 14:49:14 +00001394 config->media_cfg.quality);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001395 pj_strcat2(&cfg, line);
Benny Prijono70972992006-08-05 11:13:58 +00001396 } else {
1397 pj_ansi_sprintf(line, "#using default --quality %d\n",
1398 config->media_cfg.quality);
1399 pj_strcat2(&cfg, line);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001400 }
Benny Prijono0498d902006-06-19 14:49:14 +00001401
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001402
1403 /* ptime */
Benny Prijono2adfe292007-05-11 10:36:08 +00001404 if (config->media_cfg.ptime) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001405 pj_ansi_sprintf(line, "--ptime %d\n",
Benny Prijono2adfe292007-05-11 10:36:08 +00001406 config->media_cfg.ptime);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001407 pj_strcat2(&cfg, line);
1408 }
1409
Benny Prijono70972992006-08-05 11:13:58 +00001410 /* no-vad */
1411 if (config->media_cfg.no_vad) {
1412 pj_strcat2(&cfg, "--no-vad\n");
1413 }
1414
1415 /* ec-tail */
1416 if (config->media_cfg.ec_tail_len != PJSUA_DEFAULT_EC_TAIL_LEN) {
1417 pj_ansi_sprintf(line, "--ec-tail %d\n",
1418 config->media_cfg.ec_tail_len);
1419 pj_strcat2(&cfg, line);
1420 } else {
1421 pj_ansi_sprintf(line, "#using default --ec-tail %d\n",
1422 config->media_cfg.ec_tail_len);
1423 pj_strcat2(&cfg, line);
1424 }
1425
1426
1427 /* ilbc-mode */
1428 if (config->media_cfg.ilbc_mode != PJSUA_DEFAULT_ILBC_MODE) {
1429 pj_ansi_sprintf(line, "--ilbc-mode %d\n",
1430 config->media_cfg.ilbc_mode);
1431 pj_strcat2(&cfg, line);
1432 } else {
1433 pj_ansi_sprintf(line, "#using default --ilbc-mode %d\n",
1434 config->media_cfg.ilbc_mode);
1435 pj_strcat2(&cfg, line);
1436 }
1437
1438 /* RTP drop */
1439 if (config->media_cfg.tx_drop_pct) {
1440 pj_ansi_sprintf(line, "--tx-drop-pct %d\n",
1441 config->media_cfg.tx_drop_pct);
1442 pj_strcat2(&cfg, line);
1443
1444 }
1445 if (config->media_cfg.rx_drop_pct) {
1446 pj_ansi_sprintf(line, "--rx-drop-pct %d\n",
1447 config->media_cfg.rx_drop_pct);
1448 pj_strcat2(&cfg, line);
1449
1450 }
1451
1452
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001453 /* Start RTP port. */
1454 pj_ansi_sprintf(line, "--rtp-port %d\n",
1455 config->rtp_cfg.port);
1456 pj_strcat2(&cfg, line);
1457
1458 /* Add codec. */
1459 for (i=0; i<config->codec_cnt; ++i) {
1460 pj_ansi_sprintf(line, "--add-codec %s\n",
1461 config->codec_arg[i].ptr);
1462 pj_strcat2(&cfg, line);
1463 }
Benny Prijonofce28542007-12-09 15:41:10 +00001464 /* Disable codec */
1465 for (i=0; i<config->codec_dis_cnt; ++i) {
1466 pj_ansi_sprintf(line, "--dis-codec %s\n",
1467 config->codec_dis[i].ptr);
1468 pj_strcat2(&cfg, line);
1469 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001470
1471 pj_strcat2(&cfg, "\n#\n# User agent:\n#\n");
1472
1473 /* Auto-answer. */
1474 if (config->auto_answer != 0) {
1475 pj_ansi_sprintf(line, "--auto-answer %d\n",
1476 config->auto_answer);
1477 pj_strcat2(&cfg, line);
1478 }
1479
1480 /* Max calls. */
1481 pj_ansi_sprintf(line, "--max-calls %d\n",
1482 config->cfg.max_calls);
1483 pj_strcat2(&cfg, line);
1484
1485 /* Uas-duration. */
Benny Prijono804ff0a2006-09-14 11:17:48 +00001486 if (config->duration != NO_LIMIT) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001487 pj_ansi_sprintf(line, "--duration %d\n",
1488 config->duration);
1489 pj_strcat2(&cfg, line);
1490 }
1491
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001492 /* norefersub ? */
1493 if (config->no_refersub) {
1494 pj_strcat2(&cfg, "--norefersub\n");
1495 }
1496
Benny Prijonofce28542007-12-09 15:41:10 +00001497 if (pjsip_use_compact_form)
1498 {
1499 pj_strcat2(&cfg, "--use-compact-form\n");
1500 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001501
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001502 pj_strcat2(&cfg, "\n#\n# Buddies:\n#\n");
1503
1504 /* Add buddies. */
1505 for (i=0; i<config->buddy_cnt; ++i) {
1506 pj_ansi_sprintf(line, "--add-buddy %.*s\n",
1507 (int)config->buddy_cfg[i].uri.slen,
1508 config->buddy_cfg[i].uri.ptr);
1509 pj_strcat2(&cfg, line);
1510 }
1511
1512
1513 *(cfg.ptr + cfg.slen) = '\0';
1514 return cfg.slen;
1515}
1516
1517
1518/*
1519 * Dump application states.
1520 */
1521static void app_dump(pj_bool_t detail)
1522{
Benny Prijonoda9785b2007-04-02 20:43:06 +00001523 pjsua_dump(detail);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001524}
1525
1526
1527/*****************************************************************************
1528 * Console application
1529 */
1530
1531/*
1532 * Find next call when current call is disconnected or when user
1533 * press ']'
1534 */
1535static pj_bool_t find_next_call(void)
1536{
1537 int i, max;
1538
1539 max = pjsua_call_get_max_count();
1540 for (i=current_call+1; i<max; ++i) {
1541 if (pjsua_call_is_active(i)) {
1542 current_call = i;
1543 return PJ_TRUE;
1544 }
1545 }
1546
1547 for (i=0; i<current_call; ++i) {
1548 if (pjsua_call_is_active(i)) {
1549 current_call = i;
1550 return PJ_TRUE;
1551 }
1552 }
1553
1554 current_call = PJSUA_INVALID_ID;
1555 return PJ_FALSE;
1556}
1557
1558
1559/*
1560 * Find previous call when user press '['
1561 */
1562static pj_bool_t find_prev_call(void)
1563{
1564 int i, max;
1565
1566 max = pjsua_call_get_max_count();
1567 for (i=current_call-1; i>=0; --i) {
1568 if (pjsua_call_is_active(i)) {
1569 current_call = i;
1570 return PJ_TRUE;
1571 }
1572 }
1573
1574 for (i=max-1; i>current_call; --i) {
1575 if (pjsua_call_is_active(i)) {
1576 current_call = i;
1577 return PJ_TRUE;
1578 }
1579 }
1580
1581 current_call = PJSUA_INVALID_ID;
1582 return PJ_FALSE;
1583}
1584
1585
Benny Prijono804ff0a2006-09-14 11:17:48 +00001586/* Callback from timer when the maximum call duration has been
1587 * exceeded.
1588 */
1589static void call_timeout_callback(pj_timer_heap_t *timer_heap,
1590 struct pj_timer_entry *entry)
1591{
1592 pjsua_call_id call_id = entry->id;
1593 pjsua_msg_data msg_data;
1594 pjsip_generic_string_hdr warn;
1595 pj_str_t hname = pj_str("Warning");
1596 pj_str_t hvalue = pj_str("399 pjsua \"Call duration exceeded\"");
1597
1598 PJ_UNUSED_ARG(timer_heap);
1599
Benny Prijono148c9dd2006-09-19 13:37:53 +00001600 if (call_id == PJSUA_INVALID_ID) {
1601 PJ_LOG(1,(THIS_FILE, "Invalid call ID in timer callback"));
1602 return;
1603 }
1604
Benny Prijono804ff0a2006-09-14 11:17:48 +00001605 /* Add warning header */
1606 pjsua_msg_data_init(&msg_data);
1607 pjsip_generic_string_hdr_init2(&warn, &hname, &hvalue);
1608 pj_list_push_back(&msg_data.hdr_list, &warn);
1609
1610 /* Call duration has been exceeded; disconnect the call */
1611 PJ_LOG(3,(THIS_FILE, "Duration (%d seconds) has been exceeded "
1612 "for call %d, disconnecting the call",
1613 app_config.duration, call_id));
1614 entry->id = PJSUA_INVALID_ID;
1615 pjsua_call_hangup(call_id, 200, NULL, &msg_data);
1616}
1617
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001618
1619/*
1620 * Handler when invite state has changed.
1621 */
1622static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
1623{
1624 pjsua_call_info call_info;
1625
1626 PJ_UNUSED_ARG(e);
1627
1628 pjsua_call_get_info(call_id, &call_info);
1629
1630 if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
1631
Benny Prijono804ff0a2006-09-14 11:17:48 +00001632 /* Cancel duration timer, if any */
1633 if (app_config.call_data[call_id].timer.id != PJSUA_INVALID_ID) {
1634 struct call_data *cd = &app_config.call_data[call_id];
1635 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
1636
1637 cd->timer.id = PJSUA_INVALID_ID;
1638 pjsip_endpt_cancel_timer(endpt, &cd->timer);
1639 }
1640
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001641 PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]",
1642 call_id,
1643 call_info.last_status,
1644 call_info.last_status_text.ptr));
1645
1646 if (call_id == current_call) {
1647 find_next_call();
1648 }
1649
Benny Prijono4be63b52006-11-25 14:50:25 +00001650 /* Dump media state upon disconnected */
1651 if (1) {
1652 char buf[1024];
1653 pjsua_call_dump(call_id, PJ_TRUE, buf,
1654 sizeof(buf), " ");
1655 PJ_LOG(5,(THIS_FILE,
1656 "Call %d disconnected, dumping media stats\n%s",
1657 call_id, buf));
1658 }
1659
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001660 } else {
1661
Benny Prijono804ff0a2006-09-14 11:17:48 +00001662 if (app_config.duration!=NO_LIMIT &&
1663 call_info.state == PJSIP_INV_STATE_CONFIRMED)
1664 {
1665 /* Schedule timer to hangup call after the specified duration */
1666 struct call_data *cd = &app_config.call_data[call_id];
1667 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
1668 pj_time_val delay;
1669
1670 cd->timer.id = call_id;
1671 delay.sec = app_config.duration;
1672 delay.msec = 0;
1673 pjsip_endpt_schedule_timer(endpt, &cd->timer, &delay);
1674 }
1675
Benny Prijono4be63b52006-11-25 14:50:25 +00001676 if (call_info.state == PJSIP_INV_STATE_EARLY) {
1677 int code;
1678 pj_str_t reason;
1679 pjsip_msg *msg;
1680
1681 /* This can only occur because of TX or RX message */
1682 pj_assert(e->type == PJSIP_EVENT_TSX_STATE);
1683
1684 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
1685 msg = e->body.tsx_state.src.rdata->msg_info.msg;
1686 } else {
1687 msg = e->body.tsx_state.src.tdata->msg;
1688 }
1689
1690 code = msg->line.status.code;
1691 reason = msg->line.status.reason;
1692
1693 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s (%d %.*s)",
1694 call_id, call_info.state_text.ptr,
1695 code, (int)reason.slen, reason.ptr));
1696 } else {
1697 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
1698 call_id,
1699 call_info.state_text.ptr));
1700 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001701
1702 if (current_call==PJSUA_INVALID_ID)
1703 current_call = call_id;
1704
1705 }
1706}
1707
1708
1709/**
1710 * Handler when there is incoming call.
1711 */
1712static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
1713 pjsip_rx_data *rdata)
1714{
1715 pjsua_call_info call_info;
1716
1717 PJ_UNUSED_ARG(acc_id);
1718 PJ_UNUSED_ARG(rdata);
1719
1720 pjsua_call_get_info(call_id, &call_info);
1721
1722 if (app_config.auto_answer > 0) {
1723 pjsua_call_answer(call_id, app_config.auto_answer, NULL, NULL);
1724 }
1725
1726 if (app_config.auto_answer < 200) {
1727 PJ_LOG(3,(THIS_FILE,
1728 "Incoming call for account %d!\n"
1729 "From: %s\n"
1730 "To: %s\n"
1731 "Press a to answer or h to reject call",
1732 acc_id,
1733 call_info.remote_info.ptr,
1734 call_info.local_info.ptr));
1735 }
1736}
1737
1738
1739/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00001740 * Handler when a transaction within a call has changed state.
1741 */
1742static void on_call_tsx_state(pjsua_call_id call_id,
1743 pjsip_transaction *tsx,
1744 pjsip_event *e)
1745{
1746 const pjsip_method info_method =
1747 {
1748 PJSIP_OTHER_METHOD,
1749 { "INFO", 4 }
1750 };
1751
1752 if (pjsip_method_cmp(&tsx->method, &info_method)==0) {
1753 /*
1754 * Handle INFO method.
1755 */
1756 if (tsx->role == PJSIP_ROLE_UAC &&
1757 (tsx->state == PJSIP_TSX_STATE_COMPLETED ||
Benny Prijonob071a782007-10-10 13:12:37 +00001758 (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
1759 e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED)))
Benny Prijonofeb69f42007-10-05 09:12:26 +00001760 {
1761 /* Status of outgoing INFO request */
1762 if (tsx->status_code >= 200 && tsx->status_code < 300) {
1763 PJ_LOG(4,(THIS_FILE,
1764 "Call %d: DTMF sent successfully with INFO",
1765 call_id));
1766 } else if (tsx->status_code >= 300) {
1767 PJ_LOG(4,(THIS_FILE,
1768 "Call %d: Failed to send DTMF with INFO: %d/%.*s",
1769 call_id,
1770 tsx->status_code,
1771 (int)tsx->status_text.slen,
1772 tsx->status_text.ptr));
1773 }
1774 } else if (tsx->role == PJSIP_ROLE_UAS &&
1775 tsx->state == PJSIP_TSX_STATE_TRYING)
1776 {
1777 /* Answer incoming INFO with 200/OK */
1778 pjsip_rx_data *rdata;
1779 pjsip_tx_data *tdata;
1780 pj_status_t status;
1781
1782 rdata = e->body.tsx_state.src.rdata;
1783
1784 if (rdata->msg_info.msg->body) {
1785 status = pjsip_endpt_create_response(tsx->endpt, rdata,
1786 200, NULL, &tdata);
1787 if (status == PJ_SUCCESS)
1788 status = pjsip_tsx_send_msg(tsx, tdata);
1789
1790 PJ_LOG(3,(THIS_FILE, "Call %d: incoming INFO:\n%.*s",
1791 call_id,
1792 (int)rdata->msg_info.msg->body->len,
1793 rdata->msg_info.msg->body->data));
1794 } else {
1795 status = pjsip_endpt_create_response(tsx->endpt, rdata,
1796 400, NULL, &tdata);
1797 if (status == PJ_SUCCESS)
1798 status = pjsip_tsx_send_msg(tsx, tdata);
1799 }
1800 }
1801 }
1802}
1803
1804
1805/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001806 * Callback on media state changed event.
1807 * The action may connect the call to sound device, to file, or
1808 * to loop the call.
1809 */
1810static void on_call_media_state(pjsua_call_id call_id)
1811{
1812 pjsua_call_info call_info;
1813
1814 pjsua_call_get_info(call_id, &call_info);
1815
1816 if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
1817 pj_bool_t connect_sound = PJ_TRUE;
1818
1819 /* Loopback sound, if desired */
1820 if (app_config.auto_loop) {
1821 pjsua_conf_connect(call_info.conf_slot, call_info.conf_slot);
1822 connect_sound = PJ_FALSE;
Benny Prijono1ebd6142006-10-19 15:48:02 +00001823
1824 /* Automatically record conversation, if desired */
1825 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1826 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
1827 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001828 }
1829
1830 /* Stream a file, if desired */
1831 if (app_config.auto_play && app_config.wav_port != PJSUA_INVALID_ID) {
1832 pjsua_conf_connect(app_config.wav_port, call_info.conf_slot);
1833 connect_sound = PJ_FALSE;
1834 }
1835
Benny Prijono7ca96da2006-08-07 12:11:40 +00001836 /* Put call in conference with other calls, if desired */
1837 if (app_config.auto_conf) {
1838 pjsua_call_id call_ids[PJSUA_MAX_CALLS];
Benny Prijono10861bc2006-08-07 13:22:43 +00001839 unsigned call_cnt=PJ_ARRAY_SIZE(call_ids);
Benny Prijono7ca96da2006-08-07 12:11:40 +00001840 unsigned i;
1841
1842 /* Get all calls, and establish media connection between
1843 * this call and other calls.
1844 */
1845 pjsua_enum_calls(call_ids, &call_cnt);
Benny Prijono10861bc2006-08-07 13:22:43 +00001846
Benny Prijono7ca96da2006-08-07 12:11:40 +00001847 for (i=0; i<call_cnt; ++i) {
1848 if (call_ids[i] == call_id)
1849 continue;
1850
1851 if (!pjsua_call_has_media(call_ids[i]))
1852 continue;
1853
1854 pjsua_conf_connect(call_info.conf_slot,
1855 pjsua_call_get_conf_port(call_ids[i]));
1856 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
1857 call_info.conf_slot);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001858
1859 /* Automatically record conversation, if desired */
1860 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1861 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
1862 app_config.rec_port);
1863 }
1864
Benny Prijono7ca96da2006-08-07 12:11:40 +00001865 }
1866
1867 /* Also connect call to local sound device */
1868 connect_sound = PJ_TRUE;
1869 }
1870
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001871 /* Otherwise connect to sound device */
1872 if (connect_sound) {
1873 pjsua_conf_connect(call_info.conf_slot, 0);
1874 pjsua_conf_connect(0, call_info.conf_slot);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001875
1876 /* Automatically record conversation, if desired */
1877 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1878 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
1879 pjsua_conf_connect(0, app_config.rec_port);
1880 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001881 }
1882
1883 PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id));
1884
1885 } else if (call_info.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
1886 PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local",
1887 call_id));
1888 } else if (call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD) {
1889 PJ_LOG(3,(THIS_FILE,
1890 "Media for call %d is suspended (hold) by remote",
1891 call_id));
Benny Prijono096c56c2007-09-15 08:30:16 +00001892 } else if (call_info.media_status == PJSUA_CALL_MEDIA_ERROR) {
1893 pj_str_t reason = pj_str("ICE negotiation failed");
1894
1895 PJ_LOG(1,(THIS_FILE,
1896 "Media has reported error, disconnecting call"));
1897
1898 pjsua_call_hangup(call_id, 500, &reason, NULL);
1899
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001900 } else {
1901 PJ_LOG(3,(THIS_FILE,
1902 "Media for call %d is inactive",
1903 call_id));
1904 }
1905}
1906
Benny Prijono0875ae82006-12-26 00:11:48 +00001907/*
1908 * DTMF callback.
1909 */
1910static void call_on_dtmf_callback(pjsua_call_id call_id, int dtmf)
1911{
1912 PJ_LOG(3,(THIS_FILE, "Incoming DTMF on call %d: %c", call_id, dtmf));
1913}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001914
1915/*
1916 * Handler registration status has changed.
1917 */
1918static void on_reg_state(pjsua_acc_id acc_id)
1919{
1920 PJ_UNUSED_ARG(acc_id);
1921
1922 // Log already written.
1923}
1924
1925
1926/*
1927 * Handler on buddy state changed.
1928 */
1929static void on_buddy_state(pjsua_buddy_id buddy_id)
1930{
1931 pjsua_buddy_info info;
1932 pjsua_buddy_get_info(buddy_id, &info);
1933
1934 PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s",
1935 (int)info.uri.slen,
1936 info.uri.ptr,
1937 (int)info.status_text.slen,
1938 info.status_text.ptr));
1939}
1940
1941
1942/**
1943 * Incoming IM message (i.e. MESSAGE request)!
1944 */
1945static void on_pager(pjsua_call_id call_id, const pj_str_t *from,
1946 const pj_str_t *to, const pj_str_t *contact,
1947 const pj_str_t *mime_type, const pj_str_t *text)
1948{
1949 /* Note: call index may be -1 */
1950 PJ_UNUSED_ARG(call_id);
1951 PJ_UNUSED_ARG(to);
1952 PJ_UNUSED_ARG(contact);
1953 PJ_UNUSED_ARG(mime_type);
1954
Benny Prijonof4b538d2007-05-14 16:45:20 +00001955 PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s (%.*s)",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001956 (int)from->slen, from->ptr,
Benny Prijonof4b538d2007-05-14 16:45:20 +00001957 (int)text->slen, text->ptr,
1958 (int)mime_type->slen, mime_type->ptr));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001959}
1960
1961
1962/**
1963 * Received typing indication
1964 */
1965static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
1966 const pj_str_t *to, const pj_str_t *contact,
1967 pj_bool_t is_typing)
1968{
1969 PJ_UNUSED_ARG(call_id);
1970 PJ_UNUSED_ARG(to);
1971 PJ_UNUSED_ARG(contact);
1972
1973 PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
1974 (int)from->slen, from->ptr,
1975 (is_typing?"is typing..":"has stopped typing")));
1976}
1977
1978
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001979/**
1980 * Call transfer request status.
1981 */
1982static void on_call_transfer_status(pjsua_call_id call_id,
1983 int status_code,
1984 const pj_str_t *status_text,
1985 pj_bool_t final,
1986 pj_bool_t *p_cont)
1987{
1988 PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
1989 call_id, status_code,
1990 (int)status_text->slen, status_text->ptr,
1991 (final ? "[final]" : "")));
1992
1993 if (status_code/100 == 2) {
1994 PJ_LOG(3,(THIS_FILE,
1995 "Call %d: call transfered successfully, disconnecting call",
1996 call_id));
1997 pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
1998 *p_cont = PJ_FALSE;
1999 }
2000}
2001
2002
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002003/*
Benny Prijonof7b1c392006-11-11 16:46:34 +00002004 * Notification that call is being replaced.
2005 */
2006static void on_call_replaced(pjsua_call_id old_call_id,
2007 pjsua_call_id new_call_id)
2008{
2009 pjsua_call_info old_ci, new_ci;
2010
2011 pjsua_call_get_info(old_call_id, &old_ci);
2012 pjsua_call_get_info(new_call_id, &new_ci);
2013
2014 PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by "
2015 "call %d with %.*s",
2016 old_call_id,
2017 (int)old_ci.remote_info.slen, old_ci.remote_info.ptr,
2018 new_call_id,
2019 (int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
2020}
2021
2022
2023/*
Benny Prijono6ba8c542007-10-16 01:34:14 +00002024 * NAT type detection callback.
2025 */
2026static void on_nat_detect(const pj_stun_nat_detect_result *res)
2027{
2028 if (res->status != PJ_SUCCESS) {
2029 pjsua_perror(THIS_FILE, "NAT detection failed", res->status);
2030 } else {
2031 PJ_LOG(3, (THIS_FILE, "NAT detected as %s", res->nat_type_name));
2032 }
2033}
2034
2035
2036/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002037 * Print buddy list.
2038 */
2039static void print_buddy_list(void)
2040{
2041 pjsua_buddy_id ids[64];
2042 int i;
2043 unsigned count = PJ_ARRAY_SIZE(ids);
2044
2045 puts("Buddy list:");
2046
2047 pjsua_enum_buddies(ids, &count);
2048
2049 if (count == 0)
2050 puts(" -none-");
2051 else {
2052 for (i=0; i<(int)count; ++i) {
2053 pjsua_buddy_info info;
2054
2055 if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
2056 continue;
2057
Benny Prijono4461c7d2007-08-25 13:36:15 +00002058 printf(" [%2d] <%.*s> %.*s\n",
2059 ids[i]+1,
2060 (int)info.status_text.slen,
2061 info.status_text.ptr,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002062 (int)info.uri.slen,
2063 info.uri.ptr);
2064 }
2065 }
2066 puts("");
2067}
2068
2069
2070/*
2071 * Print account status.
2072 */
2073static void print_acc_status(int acc_id)
2074{
2075 char buf[80];
2076 pjsua_acc_info info;
2077
2078 pjsua_acc_get_info(acc_id, &info);
2079
2080 if (!info.has_registration) {
2081 pj_ansi_snprintf(buf, sizeof(buf), "%.*s",
2082 (int)info.status_text.slen,
2083 info.status_text.ptr);
2084
2085 } else {
2086 pj_ansi_snprintf(buf, sizeof(buf),
2087 "%d/%.*s (expires=%d)",
2088 info.status,
2089 (int)info.status_text.slen,
2090 info.status_text.ptr,
2091 info.expires);
2092
2093 }
2094
2095 printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
2096 acc_id, (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
Benny Prijono4461c7d2007-08-25 13:36:15 +00002097 printf(" Online status: %.*s\n",
2098 (int)info.online_status_text.slen,
2099 info.online_status_text.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002100}
2101
2102
2103/*
2104 * Show a bit of help.
2105 */
2106static void keystroke_help(void)
2107{
2108 pjsua_acc_id acc_ids[16];
2109 unsigned count = PJ_ARRAY_SIZE(acc_ids);
2110 int i;
2111
2112 printf(">>>>\n");
2113
2114 pjsua_enum_accs(acc_ids, &count);
2115
2116 printf("Account list:\n");
2117 for (i=0; i<(int)count; ++i)
2118 print_acc_status(acc_ids[i]);
2119
2120 print_buddy_list();
2121
2122 //puts("Commands:");
2123 puts("+=============================================================================+");
2124 puts("| Call Commands: | Buddy, IM & Presence: | Account: |");
2125 puts("| | | |");
2126 puts("| m Make new call | +b Add new buddy .| +a Add new accnt |");
2127 puts("| M Make multiple calls | -b Delete buddy | -a Delete accnt. |");
Benny Prijono4461c7d2007-08-25 13:36:15 +00002128 puts("| a Answer call | i Send IM | !a Modify accnt. |");
2129 puts("| h Hangup call (ha=all) | s Subscribe presence | rr (Re-)register |");
2130 puts("| H Hold call | u Unsubscribe presence | ru Unregister |");
2131 puts("| v re-inVite (release hold) | t ToGgle Online status | > Cycle next ac.|");
Benny Prijonoc08682e2007-10-04 06:17:58 +00002132 puts("| U send UPDATE | T Set online status | < Cycle prev ac.|");
2133 puts("| ],[ Select next/prev call +--------------------------+-------------------+");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002134 puts("| x Xfer call | Media Commands: | Status & Config: |");
Benny Prijonof7b1c392006-11-11 16:46:34 +00002135 puts("| X Xfer with Replaces | | |");
Benny Prijonoc08682e2007-10-04 06:17:58 +00002136 puts("| # Send RFC 2833 DTMF | cl List ports | d Dump status |");
2137 puts("| * Send DTMF with INFO | cc Connect port | dd Dump detailed |");
2138 puts("| dq Dump curr. call quality | cd Disconnect port | dc Dump config |");
2139 puts("| | V Adjust audio Volume | f Save config |");
2140 puts("| S Send arbitrary REQUEST | Cp Codec priorities | f Save config |");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002141 puts("+------------------------------+--------------------------+-------------------+");
Benny Prijono438e65b2007-11-03 21:42:10 +00002142 puts("| q QUIT sleep N: console sleep for N ms n: detect NAT type |");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002143 puts("+=============================================================================+");
Benny Prijono48af79c2006-07-22 12:49:17 +00002144
2145 i = pjsua_call_get_count();
2146 printf("You have %d active call%s\n", i, (i>1?"s":""));
Benny Prijonof7b1c392006-11-11 16:46:34 +00002147
2148 if (current_call != PJSUA_INVALID_ID) {
2149 pjsua_call_info ci;
2150 if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
2151 printf("Current call id=%d to %.*s [%.*s]\n", current_call,
2152 (int)ci.remote_info.slen, ci.remote_info.ptr,
2153 (int)ci.state_text.slen, ci.state_text.ptr);
2154 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002155}
2156
2157
2158/*
2159 * Input simple string
2160 */
2161static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
2162{
2163 char *p;
2164
2165 printf("%s (empty to cancel): ", title); fflush(stdout);
2166 fgets(buf, len, stdin);
2167
2168 /* Remove trailing newlines. */
2169 for (p=buf; ; ++p) {
2170 if (*p=='\r' || *p=='\n') *p='\0';
2171 else if (!*p) break;
2172 }
2173
2174 if (!*buf)
2175 return PJ_FALSE;
2176
2177 return PJ_TRUE;
2178}
2179
2180
2181#define NO_NB -2
2182struct input_result
2183{
2184 int nb_result;
2185 char *uri_result;
2186};
2187
2188
2189/*
2190 * Input URL.
2191 */
2192static void ui_input_url(const char *title, char *buf, int len,
2193 struct input_result *result)
2194{
2195 result->nb_result = NO_NB;
2196 result->uri_result = NULL;
2197
2198 print_buddy_list();
2199
2200 printf("Choices:\n"
2201 " 0 For current dialog.\n"
2202 " -1 All %d buddies in buddy list\n"
2203 " [1 -%2d] Select from buddy list\n"
2204 " URL An URL\n"
2205 " <Enter> Empty input (or 'q') to cancel\n"
2206 , pjsua_get_buddy_count(), pjsua_get_buddy_count());
2207 printf("%s: ", title);
2208
2209 fflush(stdout);
2210 fgets(buf, len, stdin);
2211 len = strlen(buf);
2212
2213 /* Left trim */
2214 while (pj_isspace(*buf)) {
2215 ++buf;
2216 --len;
2217 }
2218
2219 /* Remove trailing newlines */
2220 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
2221 buf[--len] = '\0';
2222
2223 if (len == 0 || buf[0]=='q')
2224 return;
2225
2226 if (pj_isdigit(*buf) || *buf=='-') {
2227
2228 int i;
2229
2230 if (*buf=='-')
2231 i = 1;
2232 else
2233 i = 0;
2234
2235 for (; i<len; ++i) {
2236 if (!pj_isdigit(buf[i])) {
2237 puts("Invalid input");
2238 return;
2239 }
2240 }
2241
2242 result->nb_result = my_atoi(buf);
2243
2244 if (result->nb_result >= 0 &&
2245 result->nb_result <= (int)pjsua_get_buddy_count())
2246 {
2247 return;
2248 }
2249 if (result->nb_result == -1)
2250 return;
2251
2252 puts("Invalid input");
2253 result->nb_result = NO_NB;
2254 return;
2255
2256 } else {
2257 pj_status_t status;
2258
2259 if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
2260 pjsua_perror(THIS_FILE, "Invalid URL", status);
2261 return;
2262 }
2263
2264 result->uri_result = buf;
2265 }
2266}
2267
2268/*
2269 * List the ports in conference bridge
2270 */
2271static void conf_list(void)
2272{
2273 unsigned i, count;
2274 pjsua_conf_port_id id[PJSUA_MAX_CALLS];
2275
2276 printf("Conference ports:\n");
2277
2278 count = PJ_ARRAY_SIZE(id);
2279 pjsua_enum_conf_ports(id, &count);
2280
2281 for (i=0; i<count; ++i) {
2282 char txlist[PJSUA_MAX_CALLS*4+10];
2283 unsigned j;
2284 pjsua_conf_port_info info;
2285
2286 pjsua_conf_get_port_info(id[i], &info);
2287
2288 txlist[0] = '\0';
2289 for (j=0; j<info.listener_cnt; ++j) {
2290 char s[10];
2291 pj_ansi_sprintf(s, "#%d ", info.listeners[j]);
2292 pj_ansi_strcat(txlist, s);
2293 }
2294 printf("Port #%02d[%2dKHz/%dms] %20.*s transmitting to: %s\n",
2295 info.slot_id,
2296 info.clock_rate/1000,
2297 info.samples_per_frame * 1000 / info.clock_rate,
2298 (int)info.name.slen,
2299 info.name.ptr,
2300 txlist);
2301
2302 }
2303 puts("");
2304}
2305
2306
2307/*
Benny Prijono56315612006-07-18 14:39:40 +00002308 * Send arbitrary request to remote host
2309 */
2310static void send_request(char *cstr_method, const pj_str_t *dst_uri)
2311{
2312 pj_str_t str_method;
2313 pjsip_method method;
2314 pjsip_tx_data *tdata;
Benny Prijono56315612006-07-18 14:39:40 +00002315 pjsip_endpoint *endpt;
2316 pj_status_t status;
2317
2318 endpt = pjsua_get_pjsip_endpt();
2319
2320 str_method = pj_str(cstr_method);
2321 pjsip_method_init_np(&method, &str_method);
2322
Benny Prijonofff245c2007-04-02 11:44:47 +00002323 status = pjsua_acc_create_request(current_acc, &method, dst_uri, &tdata);
Benny Prijono56315612006-07-18 14:39:40 +00002324
Benny Prijonob988d762007-12-05 04:30:04 +00002325 status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL);
Benny Prijono56315612006-07-18 14:39:40 +00002326 if (status != PJ_SUCCESS) {
Benny Prijonob988d762007-12-05 04:30:04 +00002327 pjsua_perror(THIS_FILE, "Unable to send request", status);
Benny Prijono56315612006-07-18 14:39:40 +00002328 return;
2329 }
2330}
2331
2332
2333/*
Benny Prijono4461c7d2007-08-25 13:36:15 +00002334 * Change extended online status.
2335 */
2336static void change_online_status(void)
2337{
2338 char menuin[32];
2339 pj_bool_t online_status;
2340 pjrpid_element elem;
2341 int i, choice;
2342
2343 enum {
2344 AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX
2345 };
2346
2347 struct opt {
2348 int id;
2349 char *name;
2350 } opts[] = {
2351 { AVAILABLE, "Available" },
2352 { BUSY, "Busy"},
2353 { OTP, "On the phone"},
2354 { IDLE, "Idle"},
2355 { AWAY, "Away"},
2356 { BRB, "Be right back"},
2357 { OFFLINE, "Offline"}
2358 };
2359
2360 printf("\n"
2361 "Choices:\n");
2362 for (i=0; i<PJ_ARRAY_SIZE(opts); ++i) {
2363 printf(" %d %s\n", opts[i].id+1, opts[i].name);
2364 }
2365
2366 if (!simple_input("Select status", menuin, sizeof(menuin)))
2367 return;
2368
2369 choice = atoi(menuin) - 1;
2370 if (choice < 0 || choice >= OPT_MAX) {
2371 puts("Invalid selection");
2372 return;
2373 }
2374
2375 pj_bzero(&elem, sizeof(elem));
2376 elem.type = PJRPID_ELEMENT_TYPE_PERSON;
2377
2378 online_status = PJ_TRUE;
2379
2380 switch (choice) {
2381 case AVAILABLE:
2382 break;
2383 case BUSY:
2384 elem.activity = PJRPID_ACTIVITY_BUSY;
2385 elem.note = pj_str("Busy");
2386 break;
2387 case OTP:
2388 elem.activity = PJRPID_ACTIVITY_BUSY;
2389 elem.note = pj_str("On the phone");
2390 break;
2391 case IDLE:
2392 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
2393 elem.note = pj_str("Idle");
2394 break;
2395 case AWAY:
2396 elem.activity = PJRPID_ACTIVITY_AWAY;
2397 elem.note = pj_str("Away");
2398 break;
2399 case BRB:
2400 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
2401 elem.note = pj_str("Be right back");
2402 break;
2403 case OFFLINE:
2404 online_status = PJ_FALSE;
2405 break;
2406 }
2407
2408 pjsua_acc_set_online_status2(current_acc, online_status, &elem);
2409}
2410
2411
2412/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00002413 * Change codec priorities.
2414 */
2415static void manage_codec_prio(void)
2416{
2417 pjsua_codec_info c[32];
2418 unsigned i, count = PJ_ARRAY_SIZE(c);
2419 char input[32];
2420 char *codec, *prio;
2421 pj_str_t id;
2422 int new_prio;
2423 pj_status_t status;
2424
2425 printf("List of codecs:\n");
2426
2427 pjsua_enum_codecs(c, &count);
2428 for (i=0; i<count; ++i) {
2429 printf(" %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen,
2430 c[i].codec_id.ptr);
2431 }
2432
2433 puts("");
2434 puts("Enter codec name and its new priority (e.g. \"speex/16000 200\"), empty to cancel:");
2435
2436 printf("Codec name and priority: ");
2437 fgets(input, sizeof(input), stdin);
2438 if (input[0]=='\r' || input[0]=='\n') {
2439 puts("Done");
2440 return;
2441 }
2442
2443 codec = strtok(input, " \t\r\n");
2444 prio = strtok(NULL, " \r\n");
2445
2446 if (!codec || !prio) {
2447 puts("Invalid input");
2448 return;
2449 }
2450
2451 new_prio = atoi(prio);
2452 if (new_prio < 0)
2453 new_prio = 0;
2454 else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST)
2455 new_prio = PJMEDIA_CODEC_PRIO_HIGHEST;
2456
2457 status = pjsua_codec_set_priority(pj_cstr(&id, codec),
2458 (pj_uint8_t)new_prio);
2459 if (status != PJ_SUCCESS)
2460 pjsua_perror(THIS_FILE, "Error setting codec priority", status);
2461}
2462
2463
2464/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002465 * Main "user interface" loop.
2466 */
2467void console_app_main(const pj_str_t *uri_to_call)
2468{
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002469 char menuin[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002470 char buf[128];
2471 char text[128];
2472 int i, count;
2473 char *uri;
2474 pj_str_t tmp;
2475 struct input_result result;
2476 pjsua_call_info call_info;
2477 pjsua_acc_info acc_info;
2478
2479
2480 /* If user specifies URI to call, then call the URI */
2481 if (uri_to_call->slen) {
2482 pjsua_call_make_call( current_acc, uri_to_call, 0, NULL, NULL, NULL);
2483 }
2484
2485 keystroke_help();
2486
2487 for (;;) {
2488
2489 printf(">>> ");
2490 fflush(stdout);
2491
Benny Prijono990042e2007-01-21 19:36:00 +00002492 if (fgets(menuin, sizeof(menuin), stdin) == NULL) {
2493 /*
2494 * Be friendly to users who redirect commands into
2495 * program, when file ends, resume with kbd.
2496 * If exit is desired end script with q for quit
2497 */
2498 /* Reopen stdin/stdout/stderr to /dev/console */
2499#if defined(PJ_WIN32) && PJ_WIN32!=0
2500 if (freopen ("CONIN$", "r", stdin) == NULL) {
2501#else
2502 if (1) {
2503#endif
2504 puts("Cannot switch back to console from file redirection");
2505 menuin[0] = 'q';
2506 menuin[1] = '\0';
2507 } else {
2508 puts("Switched back to console from file redirection");
2509 continue;
2510 }
2511 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002512
2513 switch (menuin[0]) {
2514
2515 case 'm':
2516 /* Make call! : */
2517 printf("(You currently have %d calls)\n",
2518 pjsua_call_get_count());
2519
2520 uri = NULL;
2521 ui_input_url("Make call", buf, sizeof(buf), &result);
2522 if (result.nb_result != NO_NB) {
2523
2524 if (result.nb_result == -1 || result.nb_result == 0) {
2525 puts("You can't do that with make call!");
2526 continue;
2527 } else {
2528 pjsua_buddy_info binfo;
2529 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2530 uri = binfo.uri.ptr;
2531 }
2532
2533 } else if (result.uri_result) {
2534 uri = result.uri_result;
2535 }
2536
2537 tmp = pj_str(uri);
2538 pjsua_call_make_call( current_acc, &tmp, 0, NULL, NULL, NULL);
2539 break;
2540
2541 case 'M':
2542 /* Make multiple calls! : */
2543 printf("(You currently have %d calls)\n",
2544 pjsua_call_get_count());
2545
2546 if (!simple_input("Number of calls", menuin, sizeof(menuin)))
2547 continue;
2548
2549 count = my_atoi(menuin);
2550 if (count < 1)
2551 continue;
2552
2553 ui_input_url("Make call", buf, sizeof(buf), &result);
2554 if (result.nb_result != NO_NB) {
2555 pjsua_buddy_info binfo;
2556 if (result.nb_result == -1 || result.nb_result == 0) {
2557 puts("You can't do that with make call!");
2558 continue;
2559 }
2560 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2561 uri = binfo.uri.ptr;
2562 } else {
2563 uri = result.uri_result;
2564 }
2565
2566 for (i=0; i<my_atoi(menuin); ++i) {
2567 pj_status_t status;
2568
2569 tmp = pj_str(uri);
2570 status = pjsua_call_make_call(current_acc, &tmp, 0, NULL,
2571 NULL, NULL);
2572 if (status != PJ_SUCCESS)
2573 break;
2574 }
2575 break;
2576
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002577 case 'n':
Benny Prijono438e65b2007-11-03 21:42:10 +00002578 i = pjsua_detect_nat_type();
2579 if (i != PJ_SUCCESS)
2580 pjsua_perror(THIS_FILE, "Error", i);
Benny Prijono4ab9fbb2007-10-12 12:14:27 +00002581 break;
2582
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002583 case 'i':
2584 /* Send instant messaeg */
2585
2586 /* i is for call index to send message, if any */
2587 i = -1;
2588
2589 /* Make compiler happy. */
2590 uri = NULL;
2591
2592 /* Input destination. */
2593 ui_input_url("Send IM to", buf, sizeof(buf), &result);
2594 if (result.nb_result != NO_NB) {
2595
2596 if (result.nb_result == -1) {
2597 puts("You can't send broadcast IM like that!");
2598 continue;
2599
2600 } else if (result.nb_result == 0) {
2601
2602 i = current_call;
2603
2604 } else {
2605 pjsua_buddy_info binfo;
2606 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2607 uri = binfo.uri.ptr;
2608 }
2609
2610 } else if (result.uri_result) {
2611 uri = result.uri_result;
2612 }
2613
2614
2615 /* Send typing indication. */
2616 if (i != -1)
2617 pjsua_call_send_typing_ind(i, PJ_TRUE, NULL);
2618 else {
2619 pj_str_t tmp_uri = pj_str(uri);
2620 pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL);
2621 }
2622
2623 /* Input the IM . */
2624 if (!simple_input("Message", text, sizeof(text))) {
2625 /*
2626 * Cancelled.
2627 * Send typing notification too, saying we're not typing.
2628 */
2629 if (i != -1)
2630 pjsua_call_send_typing_ind(i, PJ_FALSE, NULL);
2631 else {
2632 pj_str_t tmp_uri = pj_str(uri);
2633 pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL);
2634 }
2635 continue;
2636 }
2637
2638 tmp = pj_str(text);
2639
2640 /* Send the IM */
2641 if (i != -1)
2642 pjsua_call_send_im(i, NULL, &tmp, NULL, NULL);
2643 else {
2644 pj_str_t tmp_uri = pj_str(uri);
2645 pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL);
2646 }
2647
2648 break;
2649
2650 case 'a':
2651
2652 if (current_call != -1) {
2653 pjsua_call_get_info(current_call, &call_info);
2654 } else {
2655 /* Make compiler happy */
2656 call_info.role = PJSIP_ROLE_UAC;
2657 call_info.state = PJSIP_INV_STATE_DISCONNECTED;
2658 }
2659
2660 if (current_call == -1 ||
2661 call_info.role != PJSIP_ROLE_UAS ||
2662 call_info.state >= PJSIP_INV_STATE_CONNECTING)
2663 {
2664 puts("No pending incoming call");
2665 fflush(stdout);
2666 continue;
2667
2668 } else {
Benny Prijono20d36722007-02-22 14:52:24 +00002669 int st_code;
2670 char contact[120];
2671 pj_str_t hname = { "Contact", 7 };
2672 pj_str_t hvalue;
2673 pjsip_generic_string_hdr hcontact;
2674 pjsua_msg_data msg_data;
2675
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002676 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
2677 continue;
2678
Benny Prijono20d36722007-02-22 14:52:24 +00002679 st_code = my_atoi(buf);
2680 if (st_code < 100)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002681 continue;
2682
Benny Prijono20d36722007-02-22 14:52:24 +00002683 pjsua_msg_data_init(&msg_data);
2684
2685 if (st_code/100 == 3) {
2686 if (!simple_input("Enter URL to be put in Contact",
2687 contact, sizeof(contact)))
2688 continue;
2689 hvalue = pj_str(contact);
2690 pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue);
2691
2692 pj_list_push_back(&msg_data.hdr_list, &hcontact);
2693 }
2694
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002695 /*
2696 * Must check again!
2697 * Call may have been disconnected while we're waiting for
2698 * keyboard input.
2699 */
2700 if (current_call == -1) {
2701 puts("Call has been disconnected");
2702 fflush(stdout);
2703 continue;
2704 }
2705
Benny Prijono20d36722007-02-22 14:52:24 +00002706 pjsua_call_answer(current_call, st_code, NULL, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002707 }
2708
2709 break;
2710
2711
2712 case 'h':
2713
2714 if (current_call == -1) {
2715 puts("No current call");
2716 fflush(stdout);
2717 continue;
2718
2719 } else if (menuin[1] == 'a') {
2720
2721 /* Hangup all calls */
2722 pjsua_call_hangup_all();
2723
2724 } else {
2725
2726 /* Hangup current calls */
2727 pjsua_call_hangup(current_call, 0, NULL, NULL);
2728 }
2729 break;
2730
2731 case ']':
2732 case '[':
2733 /*
2734 * Cycle next/prev dialog.
2735 */
2736 if (menuin[0] == ']') {
2737 find_next_call();
2738
2739 } else {
2740 find_prev_call();
2741 }
2742
2743 if (current_call != -1) {
2744
2745 pjsua_call_get_info(current_call, &call_info);
2746 PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s",
2747 (int)call_info.remote_info.slen,
2748 call_info.remote_info.ptr));
2749
2750 } else {
2751 PJ_LOG(3,(THIS_FILE,"No current dialog"));
2752 }
2753 break;
2754
2755
2756 case '>':
2757 case '<':
2758 if (!simple_input("Enter account ID to select", buf, sizeof(buf)))
2759 break;
2760
2761 i = my_atoi(buf);
2762 if (pjsua_acc_is_valid(i)) {
Benny Prijono21b9ad92006-08-15 13:11:22 +00002763 pjsua_acc_set_default(i);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002764 PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i));
2765 } else {
2766 PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i));
2767 }
2768 break;
2769
2770
2771 case '+':
2772 if (menuin[1] == 'b') {
2773
2774 pjsua_buddy_config buddy_cfg;
2775 pjsua_buddy_id buddy_id;
2776 pj_status_t status;
2777
2778 if (!simple_input("Enter buddy's URI:", buf, sizeof(buf)))
2779 break;
2780
2781 if (pjsua_verify_sip_url(buf) != PJ_SUCCESS) {
2782 printf("Invalid SIP URI '%s'\n", buf);
2783 break;
2784 }
2785
Benny Prijonoac623b32006-07-03 15:19:31 +00002786 pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002787
2788 buddy_cfg.uri = pj_str(buf);
2789 buddy_cfg.subscribe = PJ_TRUE;
2790
2791 status = pjsua_buddy_add(&buddy_cfg, &buddy_id);
2792 if (status == PJ_SUCCESS) {
2793 printf("New buddy '%s' added at index %d\n",
2794 buf, buddy_id+1);
2795 }
2796
2797 } else if (menuin[1] == 'a') {
2798
Benny Prijonofb2b3652007-06-28 07:15:03 +00002799 char id[80], registrar[80], realm[80], uname[80], passwd[30];
2800 pjsua_acc_config acc_cfg;
2801 pj_status_t status;
2802
2803 if (!simple_input("Your SIP URL:", id, sizeof(id)))
2804 break;
2805 if (!simple_input("URL of the registrar:", registrar, sizeof(registrar)))
2806 break;
2807 if (!simple_input("Auth Realm:", realm, sizeof(realm)))
2808 break;
2809 if (!simple_input("Auth Username:", uname, sizeof(uname)))
2810 break;
2811 if (!simple_input("Auth Password:", passwd, sizeof(passwd)))
2812 break;
2813
2814 pjsua_acc_config_default(&acc_cfg);
2815 acc_cfg.id = pj_str(id);
2816 acc_cfg.reg_uri = pj_str(registrar);
2817 acc_cfg.cred_count = 1;
Benny Prijono48ab2b72007-11-08 09:24:30 +00002818 acc_cfg.cred_info[0].scheme = pj_str("Digest");
Benny Prijonofb2b3652007-06-28 07:15:03 +00002819 acc_cfg.cred_info[0].realm = pj_str(realm);
2820 acc_cfg.cred_info[0].username = pj_str(uname);
2821 acc_cfg.cred_info[0].data_type = 0;
2822 acc_cfg.cred_info[0].data = pj_str(passwd);
2823
2824 status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
2825 if (status != PJ_SUCCESS) {
2826 pjsua_perror(THIS_FILE, "Error adding new account", status);
2827 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002828
2829 } else {
2830 printf("Invalid input %s\n", menuin);
2831 }
2832 break;
2833
2834 case '-':
2835 if (menuin[1] == 'b') {
2836 if (!simple_input("Enter buddy ID to delete",buf,sizeof(buf)))
2837 break;
2838
2839 i = my_atoi(buf) - 1;
2840
2841 if (!pjsua_buddy_is_valid(i)) {
2842 printf("Invalid buddy id %d\n", i);
2843 } else {
2844 pjsua_buddy_del(i);
2845 printf("Buddy %d deleted\n", i);
2846 }
2847
2848 } else if (menuin[1] == 'a') {
2849
2850 if (!simple_input("Enter account ID to delete",buf,sizeof(buf)))
2851 break;
2852
2853 i = my_atoi(buf);
2854
2855 if (!pjsua_acc_is_valid(i)) {
2856 printf("Invalid account id %d\n", i);
2857 } else {
2858 pjsua_acc_del(i);
2859 printf("Account %d deleted\n", i);
2860 }
2861
2862 } else {
2863 printf("Invalid input %s\n", menuin);
2864 }
2865 break;
2866
2867 case 'H':
2868 /*
2869 * Hold call.
2870 */
2871 if (current_call != -1) {
2872
2873 pjsua_call_set_hold(current_call, NULL);
2874
2875 } else {
2876 PJ_LOG(3,(THIS_FILE, "No current call"));
2877 }
2878 break;
2879
2880 case 'v':
2881 /*
2882 * Send re-INVITE (to release hold, etc).
2883 */
2884 if (current_call != -1) {
2885
2886 pjsua_call_reinvite(current_call, PJ_TRUE, NULL);
2887
2888 } else {
2889 PJ_LOG(3,(THIS_FILE, "No current call"));
2890 }
2891 break;
2892
Benny Prijonoc08682e2007-10-04 06:17:58 +00002893 case 'U':
2894 /*
2895 * Send UPDATE
2896 */
2897 if (current_call != -1) {
2898
2899 pjsua_call_update(current_call, 0, NULL);
2900
2901 } else {
2902 PJ_LOG(3,(THIS_FILE, "No current call"));
2903 }
2904 break;
2905
2906 case 'C':
2907 if (menuin[1] == 'p') {
2908 manage_codec_prio();
2909 }
2910 break;
2911
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002912 case 'x':
2913 /*
2914 * Transfer call.
2915 */
2916 if (current_call == -1) {
2917
2918 PJ_LOG(3,(THIS_FILE, "No current call"));
2919
2920 } else {
2921 int call = current_call;
Benny Prijonod524e822006-09-22 12:48:18 +00002922 pjsua_msg_data msg_data;
2923 pjsip_generic_string_hdr refer_sub;
2924 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
2925 pj_str_t STR_FALSE = { "false", 5 };
Benny Prijonof7b1c392006-11-11 16:46:34 +00002926 pjsua_call_info ci;
2927
2928 pjsua_call_get_info(current_call, &ci);
2929 printf("Transfering current call [%d] %.*s\n",
2930 current_call,
2931 (int)ci.remote_info.slen, ci.remote_info.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002932
2933 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
2934
2935 /* Check if call is still there. */
2936
2937 if (call != current_call) {
2938 puts("Call has been disconnected");
2939 continue;
2940 }
2941
Benny Prijonod524e822006-09-22 12:48:18 +00002942 pjsua_msg_data_init(&msg_data);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002943 if (app_config.no_refersub) {
2944 /* Add Refer-Sub: false in outgoing REFER request */
2945 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
2946 &STR_FALSE);
2947 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
2948 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002949 if (result.nb_result != NO_NB) {
2950 if (result.nb_result == -1 || result.nb_result == 0)
2951 puts("You can't do that with transfer call!");
2952 else {
2953 pjsua_buddy_info binfo;
2954 pjsua_buddy_get_info(result.nb_result-1, &binfo);
Benny Prijonod524e822006-09-22 12:48:18 +00002955 pjsua_call_xfer( current_call, &binfo.uri, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002956 }
2957
2958 } else if (result.uri_result) {
2959 pj_str_t tmp;
2960 tmp = pj_str(result.uri_result);
Benny Prijonod524e822006-09-22 12:48:18 +00002961 pjsua_call_xfer( current_call, &tmp, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002962 }
2963 }
2964 break;
2965
Benny Prijonof7b1c392006-11-11 16:46:34 +00002966 case 'X':
2967 /*
2968 * Transfer call with replaces.
2969 */
2970 if (current_call == -1) {
2971
2972 PJ_LOG(3,(THIS_FILE, "No current call"));
2973
2974 } else {
2975 int call = current_call;
2976 int dst_call;
2977 pjsua_msg_data msg_data;
2978 pjsip_generic_string_hdr refer_sub;
2979 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
2980 pj_str_t STR_FALSE = { "false", 5 };
2981 pjsua_call_id ids[PJSUA_MAX_CALLS];
2982 pjsua_call_info ci;
2983 unsigned i, count;
2984
2985 count = PJ_ARRAY_SIZE(ids);
2986 pjsua_enum_calls(ids, &count);
2987
2988 if (count <= 1) {
2989 puts("There are no other calls");
2990 continue;
2991 }
2992
2993 pjsua_call_get_info(current_call, &ci);
2994 printf("Transfer call [%d] %.*s to one of the following:\n",
2995 current_call,
2996 (int)ci.remote_info.slen, ci.remote_info.ptr);
2997
2998 for (i=0; i<count; ++i) {
2999 pjsua_call_info call_info;
3000
3001 if (ids[i] == call)
3002 continue;
3003
3004 pjsua_call_get_info(ids[i], &call_info);
3005 printf("%d %.*s [%.*s]\n",
3006 ids[i],
3007 (int)call_info.remote_info.slen,
3008 call_info.remote_info.ptr,
3009 (int)call_info.state_text.slen,
3010 call_info.state_text.ptr);
3011 }
3012
3013 if (!simple_input("Enter call number to be replaced",
3014 buf, sizeof(buf)))
3015 continue;
3016
3017 dst_call = my_atoi(buf);
3018
3019 /* Check if call is still there. */
3020
3021 if (call != current_call) {
3022 puts("Call has been disconnected");
3023 continue;
3024 }
3025
3026 /* Check that destination call is valid. */
3027 if (dst_call == call) {
3028 puts("Destination call number must not be the same "
3029 "as the call being transfered");
3030 continue;
3031 }
3032 if (dst_call >= PJSUA_MAX_CALLS) {
3033 puts("Invalid destination call number");
3034 continue;
3035 }
3036 if (!pjsua_call_is_active(dst_call)) {
3037 puts("Invalid destination call number");
3038 continue;
3039 }
3040
3041 pjsua_msg_data_init(&msg_data);
3042 if (app_config.no_refersub) {
3043 /* Add Refer-Sub: false in outgoing REFER request */
3044 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
3045 &STR_FALSE);
3046 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
3047 }
3048
3049 pjsua_call_xfer_replaces(call, dst_call, 0, &msg_data);
3050 }
3051 break;
3052
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003053 case '#':
3054 /*
3055 * Send DTMF strings.
3056 */
3057 if (current_call == -1) {
3058
3059 PJ_LOG(3,(THIS_FILE, "No current call"));
3060
3061 } else if (!pjsua_call_has_media(current_call)) {
3062
3063 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
3064
3065 } else {
3066 pj_str_t digits;
3067 int call = current_call;
3068 pj_status_t status;
3069
3070 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
3071 sizeof(buf)))
3072 {
3073 break;
3074 }
3075
3076 if (call != current_call) {
3077 puts("Call has been disconnected");
3078 continue;
3079 }
3080
3081 digits = pj_str(buf);
3082 status = pjsua_call_dial_dtmf(current_call, &digits);
3083 if (status != PJ_SUCCESS) {
3084 pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
3085 } else {
3086 puts("DTMF digits enqueued for transmission");
3087 }
3088 }
3089 break;
3090
Benny Prijonofeb69f42007-10-05 09:12:26 +00003091 case '*':
3092 /* Send DTMF with INFO */
3093 if (current_call == -1) {
3094
3095 PJ_LOG(3,(THIS_FILE, "No current call"));
3096
3097 } else {
3098 const pj_str_t SIP_INFO = pj_str("INFO");
3099 pj_str_t digits;
3100 int call = current_call;
3101 int i;
3102 pj_status_t status;
3103
3104 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
3105 sizeof(buf)))
3106 {
3107 break;
3108 }
3109
3110 if (call != current_call) {
3111 puts("Call has been disconnected");
3112 continue;
3113 }
3114
3115 digits = pj_str(buf);
3116 for (i=0; i<digits.slen; ++i) {
3117 pjsua_msg_data msg_data;
3118 char body[80];
3119
3120 pjsua_msg_data_init(&msg_data);
3121 msg_data.content_type = pj_str("application/dtmf-relay");
3122
3123 pj_ansi_snprintf(body, sizeof(body),
3124 "Signal=%c\r\n"
3125 "Duration=160",
3126 buf[i]);
3127 msg_data.msg_body = pj_str(body);
3128
3129 status = pjsua_call_send_request(current_call, &SIP_INFO,
3130 &msg_data);
3131 if (status != PJ_SUCCESS) {
3132 break;
3133 }
3134 }
3135 }
3136 break;
3137
Benny Prijono56315612006-07-18 14:39:40 +00003138 case 'S':
3139 /*
3140 * Send arbitrary request
3141 */
3142 if (pjsua_acc_get_count() == 0) {
3143 puts("Sorry, need at least one account configured");
3144 break;
3145 }
3146
3147 puts("Send arbitrary request to remote host");
3148
3149 /* Input METHOD */
3150 if (!simple_input("Request method:",text,sizeof(text)))
3151 break;
3152
3153 /* Input destination URI */
3154 uri = NULL;
3155 ui_input_url("Destination URI", buf, sizeof(buf), &result);
3156 if (result.nb_result != NO_NB) {
3157
3158 if (result.nb_result == -1 || result.nb_result == 0) {
3159 puts("Sorry you can't do that!");
3160 continue;
3161 } else {
3162 pjsua_buddy_info binfo;
3163 pjsua_buddy_get_info(result.nb_result-1, &binfo);
3164 uri = binfo.uri.ptr;
3165 }
3166
3167 } else if (result.uri_result) {
3168 uri = result.uri_result;
3169 }
3170
3171 tmp = pj_str(uri);
3172
3173 send_request(text, &tmp);
3174 break;
3175
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003176 case 's':
Benny Prijono990042e2007-01-21 19:36:00 +00003177 if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
3178 pj_str_t tmp;
3179 int delay;
3180
3181 tmp.ptr = menuin+6;
3182 tmp.slen = pj_ansi_strlen(menuin)-7;
3183
3184 if (tmp.slen < 1) {
3185 puts("Usage: sleep MSEC");
3186 break;
3187 }
3188
3189 delay = pj_strtoul(&tmp);
3190 if (delay < 0) delay = 0;
3191 pj_thread_sleep(delay);
3192 break;
3193 }
3194 /* Continue below */
3195
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003196 case 'u':
3197 /*
3198 * Subscribe/unsubscribe presence.
3199 */
3200 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
3201 if (result.nb_result != NO_NB) {
3202 if (result.nb_result == -1) {
3203 int i, count;
3204 count = pjsua_get_buddy_count();
3205 for (i=0; i<count; ++i)
3206 pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
3207 } else if (result.nb_result == 0) {
3208 puts("Sorry, can only subscribe to buddy's presence, "
3209 "not from existing call");
3210 } else {
3211 pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
3212 }
3213
3214 } else if (result.uri_result) {
3215 puts("Sorry, can only subscribe to buddy's presence, "
3216 "not arbitrary URL (for now)");
3217 }
3218
3219 break;
3220
3221 case 'r':
3222 switch (menuin[1]) {
3223 case 'r':
3224 /*
3225 * Re-Register.
3226 */
3227 pjsua_acc_set_registration(current_acc, PJ_TRUE);
3228 break;
3229 case 'u':
3230 /*
3231 * Unregister
3232 */
3233 pjsua_acc_set_registration(current_acc, PJ_FALSE);
3234 break;
3235 }
3236 break;
3237
3238 case 't':
3239 pjsua_acc_get_info(current_acc, &acc_info);
3240 acc_info.online_status = !acc_info.online_status;
3241 pjsua_acc_set_online_status(current_acc, acc_info.online_status);
3242 printf("Setting %s online status to %s\n",
3243 acc_info.acc_uri.ptr,
3244 (acc_info.online_status?"online":"offline"));
3245 break;
3246
Benny Prijono4461c7d2007-08-25 13:36:15 +00003247 case 'T':
3248 change_online_status();
3249 break;
3250
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003251 case 'c':
3252 switch (menuin[1]) {
3253 case 'l':
3254 conf_list();
3255 break;
3256 case 'c':
3257 case 'd':
3258 {
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003259 char tmp[10], src_port[10], dst_port[10];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003260 pj_status_t status;
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003261 int cnt;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003262 const char *src_title, *dst_title;
3263
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003264 cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003265
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003266 if (cnt != 3) {
3267 conf_list();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003268
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003269 src_title = (menuin[1]=='c'?
3270 "Connect src port #":
3271 "Disconnect src port #");
3272 dst_title = (menuin[1]=='c'?
3273 "To dst port #":
3274 "From dst port #");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003275
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00003276 if (!simple_input(src_title, src_port, sizeof(src_port)))
3277 break;
3278
3279 if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
3280 break;
3281 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003282
3283 if (menuin[1]=='c') {
3284 status = pjsua_conf_connect(my_atoi(src_port),
3285 my_atoi(dst_port));
3286 } else {
3287 status = pjsua_conf_disconnect(my_atoi(src_port),
3288 my_atoi(dst_port));
3289 }
3290 if (status == PJ_SUCCESS) {
3291 puts("Success");
3292 } else {
3293 puts("ERROR!!");
3294 }
3295 }
3296 break;
3297 }
3298 break;
3299
Benny Prijono6dd967c2006-12-26 02:27:14 +00003300 case 'V':
3301 /* Adjust audio volume */
3302 sprintf(buf, "Adjust mic level: [%4.1fx] ", app_config.mic_level);
3303 if (simple_input(buf,text,sizeof(text))) {
3304 char *err;
3305 app_config.mic_level = (float)strtod(text, &err);
3306 pjsua_conf_adjust_rx_level(0, app_config.mic_level);
3307 }
3308 sprintf(buf, "Adjust speaker level: [%4.1fx] ",
3309 app_config.speaker_level);
3310 if (simple_input(buf,text,sizeof(text))) {
3311 char *err;
3312 app_config.speaker_level = (float)strtod(text, &err);
3313 pjsua_conf_adjust_tx_level(0, app_config.speaker_level);
3314 }
3315
3316 break;
3317
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003318 case 'd':
3319 if (menuin[1] == 'c') {
3320 char settings[2000];
3321 int len;
3322
3323 len = write_settings(&app_config, settings, sizeof(settings));
3324 if (len < 1)
3325 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
3326 else
3327 PJ_LOG(3,(THIS_FILE,
3328 "Dumping configuration (%d bytes):\n%s\n",
3329 len, settings));
Benny Prijono819506c2006-06-22 22:29:51 +00003330
3331 } else if (menuin[1] == 'q') {
3332
3333 if (current_call != PJSUA_INVALID_ID) {
3334 char buf[1024];
3335 pjsua_call_dump(current_call, PJ_TRUE, buf,
3336 sizeof(buf), " ");
3337 PJ_LOG(3,(THIS_FILE, "\n%s", buf));
3338 } else {
3339 PJ_LOG(3,(THIS_FILE, "No current call"));
3340 }
3341
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003342 } else {
3343 app_dump(menuin[1]=='d');
3344 }
3345 break;
3346
3347
3348 case 'f':
3349 if (simple_input("Enter output filename", buf, sizeof(buf))) {
3350 char settings[2000];
3351 int len;
3352
3353 len = write_settings(&app_config, settings, sizeof(settings));
3354 if (len < 1)
3355 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
3356 else {
3357 pj_oshandle_t fd;
3358 pj_status_t status;
3359
3360 status = pj_file_open(app_config.pool, buf,
3361 PJ_O_WRONLY, &fd);
3362 if (status != PJ_SUCCESS) {
3363 pjsua_perror(THIS_FILE, "Unable to open file", status);
3364 } else {
3365 pj_ssize_t size = len;
3366 pj_file_write(fd, settings, &size);
3367 pj_file_close(fd);
3368
3369 printf("Settings successfully written to '%s'\n", buf);
3370 }
3371 }
3372
3373 }
3374 break;
3375
3376
3377 case 'q':
3378 goto on_exit;
3379
3380
3381 default:
3382 if (menuin[0] != '\n' && menuin[0] != '\r') {
3383 printf("Invalid input %s", menuin);
3384 }
3385 keystroke_help();
3386 break;
3387 }
3388 }
3389
3390on_exit:
3391 ;
3392}
3393
3394
3395/*****************************************************************************
3396 * Public API
3397 */
3398
3399pj_status_t app_init(int argc, char *argv[])
3400{
Benny Prijonoe93e2872006-06-28 16:46:49 +00003401 pjsua_transport_id transport_id = -1;
Benny Prijonoe347cb02007-02-14 14:36:13 +00003402 pjsua_transport_config tcp_cfg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003403 unsigned i;
3404 pj_status_t status;
3405
3406 /* Create pjsua */
3407 status = pjsua_create();
3408 if (status != PJ_SUCCESS)
3409 return status;
3410
3411 /* Create pool for application */
Benny Prijonod5696da2007-07-17 16:25:45 +00003412 app_config.pool = pjsua_pool_create("pjsua", 1000, 1000);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003413
3414 /* Initialize default config */
3415 default_config(&app_config);
3416
3417 /* Parse the arguments */
3418 status = parse_args(argc, argv, &app_config, &uri_arg);
3419 if (status != PJ_SUCCESS)
3420 return status;
3421
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003422 /* Initialize application callbacks */
3423 app_config.cfg.cb.on_call_state = &on_call_state;
3424 app_config.cfg.cb.on_call_media_state = &on_call_media_state;
3425 app_config.cfg.cb.on_incoming_call = &on_incoming_call;
Benny Prijonofeb69f42007-10-05 09:12:26 +00003426 app_config.cfg.cb.on_call_tsx_state = &on_call_tsx_state;
Benny Prijono0875ae82006-12-26 00:11:48 +00003427 app_config.cfg.cb.on_dtmf_digit = &call_on_dtmf_callback;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003428 app_config.cfg.cb.on_reg_state = &on_reg_state;
3429 app_config.cfg.cb.on_buddy_state = &on_buddy_state;
3430 app_config.cfg.cb.on_pager = &on_pager;
3431 app_config.cfg.cb.on_typing = &on_typing;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003432 app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
Benny Prijonof7b1c392006-11-11 16:46:34 +00003433 app_config.cfg.cb.on_call_replaced = &on_call_replaced;
Benny Prijono6ba8c542007-10-16 01:34:14 +00003434 app_config.cfg.cb.on_nat_detect = &on_nat_detect;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003435
3436 /* Initialize pjsua */
3437 status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
3438 &app_config.media_cfg);
3439 if (status != PJ_SUCCESS)
3440 return status;
3441
Benny Prijonoe909eac2006-07-27 22:04:56 +00003442#ifdef STEREO_DEMO
3443 stereo_demo();
3444#endif
3445
Benny Prijono804ff0a2006-09-14 11:17:48 +00003446 /* Initialize calls data */
3447 for (i=0; i<PJ_ARRAY_SIZE(app_config.call_data); ++i) {
3448 app_config.call_data[i].timer.id = PJSUA_INVALID_ID;
3449 app_config.call_data[i].timer.cb = &call_timeout_callback;
3450 }
3451
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003452 /* Optionally registers WAV file */
Benny Prijono32e4f492007-01-24 00:44:26 +00003453 for (i=0; i<app_config.wav_count; ++i) {
3454 pjsua_player_id wav_id;
3455
3456 status = pjsua_player_create(&app_config.wav_files[i], 0,
3457 &wav_id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003458 if (status != PJ_SUCCESS)
3459 goto on_error;
Benny Prijono22a300a2006-06-14 20:04:55 +00003460
Benny Prijono72c04d22007-02-17 22:20:58 +00003461 if (app_config.wav_id == PJSUA_INVALID_ID) {
Benny Prijono32e4f492007-01-24 00:44:26 +00003462 app_config.wav_id = wav_id;
3463 app_config.wav_port = pjsua_player_get_conf_port(app_config.wav_id);
3464 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003465 }
3466
Benny Prijono4af234b2007-01-24 02:02:09 +00003467 /* Optionally registers tone players */
3468 for (i=0; i<app_config.tone_count; ++i) {
3469 pjmedia_port *tport;
3470 char name[80];
3471 pj_str_t label;
3472 pj_status_t status;
3473
3474 pj_ansi_snprintf(name, sizeof(name), "tone-%d,%d",
3475 app_config.tones[i].freq1,
3476 app_config.tones[i].freq2);
3477 label = pj_str(name);
3478 status = pjmedia_tonegen_create2(app_config.pool, &label,
3479 8000, 1, 160, 16,
3480 PJMEDIA_TONEGEN_LOOP, &tport);
3481 if (status != PJ_SUCCESS) {
3482 pjsua_perror(THIS_FILE, "Unable to create tone generator", status);
3483 goto on_error;
3484 }
3485
3486 status = pjsua_conf_add_port(app_config.pool, tport,
3487 &app_config.tone_slots[i]);
3488 pj_assert(status == PJ_SUCCESS);
3489
3490 status = pjmedia_tonegen_play(tport, 1, &app_config.tones[i], 0);
3491 pj_assert(status == PJ_SUCCESS);
3492 }
3493
Benny Prijono1ebd6142006-10-19 15:48:02 +00003494 /* Optionally create recorder file, if any. */
3495 if (app_config.rec_file.slen) {
3496 status = pjsua_recorder_create(&app_config.rec_file, 0, NULL, 0, 0,
3497 &app_config.rec_id);
3498 if (status != PJ_SUCCESS)
3499 goto on_error;
3500
3501 app_config.rec_port = pjsua_recorder_get_conf_port(app_config.rec_id);
3502 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003503
Benny Prijonoe347cb02007-02-14 14:36:13 +00003504 pj_memcpy(&tcp_cfg, &app_config.udp_cfg, sizeof(tcp_cfg));
3505
Benny Prijono87ef89a2007-01-14 00:39:45 +00003506 /* Add UDP transport unless it's disabled. */
3507 if (!app_config.no_udp) {
3508 pjsua_acc_id aid;
3509
3510 status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
3511 &app_config.udp_cfg,
3512 &transport_id);
3513 if (status != PJ_SUCCESS)
3514 goto on_error;
3515
3516 /* Add local account */
3517 pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
3518 //pjsua_acc_set_transport(aid, transport_id);
3519 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
Benny Prijonoe347cb02007-02-14 14:36:13 +00003520
3521 if (app_config.udp_cfg.port == 0) {
3522 pjsua_transport_info ti;
3523 pj_sockaddr_in *a;
3524
3525 pjsua_transport_get_info(transport_id, &ti);
3526 a = (pj_sockaddr_in*)&ti.local_addr;
3527
3528 tcp_cfg.port = pj_ntohs(a->sin_port);
3529 }
Benny Prijono87ef89a2007-01-14 00:39:45 +00003530 }
3531
Benny Prijonoe93e2872006-06-28 16:46:49 +00003532 /* Add TCP transport unless it's disabled */
3533 if (!app_config.no_tcp) {
3534 status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
Benny Prijonoe347cb02007-02-14 14:36:13 +00003535 &tcp_cfg,
Benny Prijonoe93e2872006-06-28 16:46:49 +00003536 &transport_id);
3537 if (status != PJ_SUCCESS)
3538 goto on_error;
3539
3540 /* Add local account */
Benny Prijono21b9ad92006-08-15 13:11:22 +00003541 pjsua_acc_add_local(transport_id, PJ_TRUE, NULL);
Benny Prijonoe93e2872006-06-28 16:46:49 +00003542 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
3543
3544 }
3545
Benny Prijonoe93e2872006-06-28 16:46:49 +00003546
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003547#if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
3548 /* Add TLS transport when application wants one */
3549 if (app_config.use_tls) {
Benny Prijonof3bbc132006-12-25 06:43:59 +00003550
3551 pjsua_acc_id acc_id;
3552
3553 /* Set TLS port as TCP port+1 */
Benny Prijonoafc47be2007-02-14 14:44:55 +00003554 tcp_cfg.port++;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003555 status = pjsua_transport_create(PJSIP_TRANSPORT_TLS,
Benny Prijonoafc47be2007-02-14 14:44:55 +00003556 &tcp_cfg,
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003557 &transport_id);
Benny Prijonoafc47be2007-02-14 14:44:55 +00003558 tcp_cfg.port--;
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003559 if (status != PJ_SUCCESS)
3560 goto on_error;
Benny Prijonof3bbc132006-12-25 06:43:59 +00003561
3562 /* Add local account */
3563 pjsua_acc_add_local(transport_id, PJ_FALSE, &acc_id);
3564 pjsua_acc_set_online_status(acc_id, PJ_TRUE);
Benny Prijono6e0e54b2006-12-08 21:58:31 +00003565 }
3566#endif
3567
Benny Prijonoe93e2872006-06-28 16:46:49 +00003568 if (transport_id == -1) {
3569 PJ_LOG(3,(THIS_FILE, "Error: no transport is configured"));
3570 status = -1;
3571 goto on_error;
3572 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003573
3574
3575 /* Add accounts */
3576 for (i=0; i<app_config.acc_cnt; ++i) {
Benny Prijono21b9ad92006-08-15 13:11:22 +00003577 status = pjsua_acc_add(&app_config.acc_cfg[i], PJ_TRUE, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003578 if (status != PJ_SUCCESS)
3579 goto on_error;
3580 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
3581 }
3582
3583 /* Add buddies */
3584 for (i=0; i<app_config.buddy_cnt; ++i) {
3585 status = pjsua_buddy_add(&app_config.buddy_cfg[i], NULL);
3586 if (status != PJ_SUCCESS)
3587 goto on_error;
3588 }
3589
3590 /* Optionally set codec orders */
3591 for (i=0; i<app_config.codec_cnt; ++i) {
3592 pjsua_codec_set_priority(&app_config.codec_arg[i],
3593 (pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9));
3594 }
3595
Benny Prijonofce28542007-12-09 15:41:10 +00003596 /* Optionally disable some codec */
3597 for (i=0; i<app_config.codec_dis_cnt; ++i) {
3598 pjsua_codec_set_priority(&app_config.codec_dis[i],PJMEDIA_CODEC_PRIO_DISABLED);
3599 }
3600
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003601 /* Add RTP transports */
3602 status = pjsua_media_transports_create(&app_config.rtp_cfg);
3603 if (status != PJ_SUCCESS)
3604 goto on_error;
3605
Benny Prijono22a300a2006-06-14 20:04:55 +00003606 /* Use null sound device? */
Benny Prijonoe909eac2006-07-27 22:04:56 +00003607#ifndef STEREO_DEMO
Benny Prijono22a300a2006-06-14 20:04:55 +00003608 if (app_config.null_audio) {
3609 status = pjsua_set_null_snd_dev();
3610 if (status != PJ_SUCCESS)
3611 return status;
3612 }
Benny Prijonoe909eac2006-07-27 22:04:56 +00003613#endif
Benny Prijono22a300a2006-06-14 20:04:55 +00003614
Benny Prijono4e5d5512007-03-06 18:11:30 +00003615 if (app_config.capture_dev != PJSUA_INVALID_ID
3616 || app_config.playback_dev != PJSUA_INVALID_ID) {
3617 status
3618 = pjsua_set_snd_dev(app_config.capture_dev, app_config.playback_dev);
3619 if (status != PJ_SUCCESS)
3620 goto on_error;
3621 }
3622
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003623 return PJ_SUCCESS;
3624
3625on_error:
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00003626 app_destroy();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003627 return status;
3628}
3629
3630
3631pj_status_t app_main(void)
3632{
3633 pj_status_t status;
3634
3635 /* Start pjsua */
3636 status = pjsua_start();
3637 if (status != PJ_SUCCESS) {
Benny Prijonoad2e0ca2007-04-29 12:31:51 +00003638 app_destroy();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003639 return status;
3640 }
3641
3642 console_app_main(&uri_arg);
3643
3644 return PJ_SUCCESS;
3645}
3646
3647pj_status_t app_destroy(void)
3648{
Benny Prijonof762ee72006-12-01 11:14:37 +00003649 pj_status_t status;
Benny Prijono4af234b2007-01-24 02:02:09 +00003650 unsigned i;
Benny Prijonof762ee72006-12-01 11:14:37 +00003651
Benny Prijonoe909eac2006-07-27 22:04:56 +00003652#ifdef STEREO_DEMO
3653 if (app_config.snd) {
3654 pjmedia_snd_port_destroy(app_config.snd);
3655 app_config.snd = NULL;
3656 }
3657#endif
3658
Benny Prijono4af234b2007-01-24 02:02:09 +00003659 /* Close tone generators */
3660 for (i=0; i<app_config.tone_count; ++i) {
3661 pjsua_conf_remove_port(app_config.tone_slots[i]);
3662 }
3663
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003664 if (app_config.pool) {
3665 pj_pool_release(app_config.pool);
3666 app_config.pool = NULL;
3667 }
3668
Benny Prijonof762ee72006-12-01 11:14:37 +00003669 status = pjsua_destroy();
3670
3671 pj_bzero(&app_config, sizeof(app_config));
3672
3673 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003674}
Benny Prijonoe909eac2006-07-27 22:04:56 +00003675
3676
3677#ifdef STEREO_DEMO
3678static void stereo_demo()
3679{
3680 pjmedia_port *conf, *splitter, *ch1;
Benny Prijonoe909eac2006-07-27 22:04:56 +00003681 pj_status_t status;
3682
3683 /* Disable existing sound device */
3684 conf = pjsua_set_no_snd_dev();
3685
Benny Prijonoe909eac2006-07-27 22:04:56 +00003686 /* Create stereo-mono splitter/combiner */
3687 status = pjmedia_splitcomb_create(app_config.pool,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003688 conf->info.clock_rate /* clock rate */,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003689 2 /* stereo */,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003690 2 * conf->info.samples_per_frame,
3691 conf->info.bits_per_sample,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003692 0 /* options */,
3693 &splitter);
3694 pj_assert(status == PJ_SUCCESS);
3695
3696 /* Connect channel0 (left channel?) to conference port slot0 */
3697 status = pjmedia_splitcomb_set_channel(splitter, 0 /* ch0 */,
3698 0 /*options*/,
3699 conf);
3700 pj_assert(status == PJ_SUCCESS);
3701
3702 /* Create reverse channel for channel1 (right channel?)... */
3703 status = pjmedia_splitcomb_create_rev_channel(app_config.pool,
3704 splitter,
3705 1 /* ch1 */,
3706 0 /* options */,
3707 &ch1);
3708 pj_assert(status == PJ_SUCCESS);
3709
3710 /* .. and register it to conference bridge (it would be slot1
3711 * if there's no other devices connected to the bridge)
3712 */
3713 status = pjsua_conf_add_port(app_config.pool, ch1, NULL);
3714 pj_assert(status == PJ_SUCCESS);
3715
3716 /* Create sound device */
3717 status = pjmedia_snd_port_create(app_config.pool, -1, -1,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003718 conf->info.clock_rate,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003719 2 /* stereo */,
Benny Prijono478cf7c2007-06-02 00:12:29 +00003720 2 * conf->info.samples_per_frame,
3721 conf->info.bits_per_sample,
Benny Prijonoe909eac2006-07-27 22:04:56 +00003722 0, &app_config.snd);
3723 pj_assert(status == PJ_SUCCESS);
3724
3725
3726 /* Connect the splitter to the sound device */
3727 status = pjmedia_snd_port_connect(app_config.snd, splitter);
3728 pj_assert(status == PJ_SUCCESS);
3729
3730}
3731#endif
3732