blob: 81a6e5cbf01179ff9df3ffdf3fcfc0fdd65bc1bb [file] [log] [blame]
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsua-lib/pjsua.h>
20
21
22#define THIS_FILE "pjsua.c"
Benny Prijono804ff0a2006-09-14 11:17:48 +000023#define NO_LIMIT (int)0x7FFFFFFF
Benny Prijonoeebe9af2006-06-13 22:57:13 +000024
Benny Prijonoe909eac2006-07-27 22:04:56 +000025//#define STEREO_DEMO
Benny Prijonoeebe9af2006-06-13 22:57:13 +000026
Benny Prijono804ff0a2006-09-14 11:17:48 +000027/* Call specific data */
28struct call_data
29{
30 pj_timer_entry timer;
31};
32
Benny Prijonoeebe9af2006-06-13 22:57:13 +000033
34/* Pjsua application data */
35static struct app_config
36{
37 pjsua_config cfg;
38 pjsua_logging_config log_cfg;
39 pjsua_media_config media_cfg;
Benny Prijono4ddad2c2006-10-18 17:16:34 +000040 pj_bool_t no_refersub;
Benny Prijonoe93e2872006-06-28 16:46:49 +000041 pj_bool_t no_tcp;
42 pj_bool_t no_udp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000043 pjsua_transport_config udp_cfg;
44 pjsua_transport_config rtp_cfg;
45
46 unsigned acc_cnt;
47 pjsua_acc_config acc_cfg[PJSUA_MAX_ACC];
48
49 unsigned buddy_cnt;
50 pjsua_buddy_config buddy_cfg[PJSUA_MAX_BUDDIES];
51
Benny Prijono804ff0a2006-09-14 11:17:48 +000052 struct call_data call_data[PJSUA_MAX_CALLS];
53
Benny Prijonoeebe9af2006-06-13 22:57:13 +000054 pj_pool_t *pool;
55 /* Compatibility with older pjsua */
56
57 unsigned codec_cnt;
58 pj_str_t codec_arg[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +000059 pj_bool_t null_audio;
60 pj_str_t wav_file;
61 pjsua_player_id wav_id;
62 pjsua_conf_port_id wav_port;
63 pj_bool_t auto_play;
64 pj_bool_t auto_loop;
Benny Prijono7ca96da2006-08-07 12:11:40 +000065 pj_bool_t auto_conf;
Benny Prijono1ebd6142006-10-19 15:48:02 +000066 pj_str_t rec_file;
67 pj_bool_t auto_rec;
68 pjsua_recorder_id rec_id;
69 pjsua_conf_port_id rec_port;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000070 unsigned ptime;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000071 unsigned auto_answer;
72 unsigned duration;
Benny Prijonoe909eac2006-07-27 22:04:56 +000073
74#ifdef STEREO_DEMO
75 pjmedia_snd_port *snd;
76#endif
77
Benny Prijonoeebe9af2006-06-13 22:57:13 +000078} app_config;
79
80
Benny Prijono21b9ad92006-08-15 13:11:22 +000081//static pjsua_acc_id current_acc;
82#define current_acc pjsua_acc_get_default()
Benny Prijonof7b1c392006-11-11 16:46:34 +000083static pjsua_call_id current_call = PJSUA_INVALID_ID;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000084static pj_str_t uri_arg;
85
Benny Prijono594e4c52006-09-14 18:51:01 +000086#ifdef STEREO_DEMO
Benny Prijonoe909eac2006-07-27 22:04:56 +000087static void stereo_demo();
Benny Prijono594e4c52006-09-14 18:51:01 +000088#endif
Benny Prijonoe909eac2006-07-27 22:04:56 +000089
Benny Prijonoeebe9af2006-06-13 22:57:13 +000090/*****************************************************************************
91 * Configuration manipulation
92 */
93
94/* Show usage */
95static void usage(void)
96{
97 puts ("Usage:");
Benny Prijono0a5cad82006-09-26 13:21:02 +000098 puts (" pjsua [options] [SIP URL to call]");
Benny Prijonoeebe9af2006-06-13 22:57:13 +000099 puts ("");
100 puts ("General options:");
Benny Prijono804ff0a2006-09-14 11:17:48 +0000101 puts (" --config-file=file Read the config/arguments from file.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000102 puts (" --help Display this help screen");
103 puts (" --version Display version info");
104 puts ("");
105 puts ("Logging options:");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000106 puts (" --log-file=fname Log to filename (default stderr)");
107 puts (" --log-level=N Set log max level to N (0(none) to 6(trace)) (default=5)");
108 puts (" --app-log-level=N Set log max level for stdout display (default=4)");
109 puts ("");
110 puts ("SIP Account options:");
111 puts (" --registrar=url Set the URL of registrar server");
112 puts (" --id=url Set the URL of local ID (used in From header)");
113 puts (" --contact=url Optionally override the Contact information");
114 puts (" --proxy=url Optional URL of proxy server to visit");
115 puts (" May be specified multiple times");
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000116 puts (" --reg-timeout=SEC Optional registration interval (default 55)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000117 puts (" --realm=string Set realm");
118 puts (" --username=string Set authentication username");
119 puts (" --password=string Set authentication password");
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000120 puts (" --publish Send presence PUBLISH for this account");
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000121 puts (" --next-cred Add another credentials");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000122 puts ("");
123 puts ("SIP Account Control:");
124 puts (" --next-account Add more account");
125 puts ("");
126 puts ("Transport Options:");
Benny Prijonoe93e2872006-06-28 16:46:49 +0000127 puts (" --local-port=port Set TCP/UDP port. This implicitly enables both ");
128 puts (" TCP and UDP transports on the specified port, unless");
129 puts (" if TCP or UDP is disabled.");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000130 puts (" --ip-addr=IP Use the specifed address as SIP and RTP addresses.");
131 puts (" (Hint: the IP may be the public IP of the NAT/router)");
Benny Prijonoe93e2872006-06-28 16:46:49 +0000132 puts (" --no-tcp Disable TCP transport.");
133 puts (" --no-udp Disable UDP transport.");
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000134 puts (" --nameserver=NS Add the specified nameserver to enable SRV resolution");
135 puts (" This option can be specified multiple times.");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000136 puts (" --outbound=url Set the URL of global outbound proxy server");
137 puts (" May be specified multiple times");
138 puts (" --use-stun1=host[:port]");
139 puts (" --use-stun2=host[:port] Resolve local IP with the specified STUN servers");
140 puts ("");
141 puts ("Media Options:");
142 puts (" --add-codec=name Manually add codec (default is to enable all)");
143 puts (" --clock-rate=N Override sound device clock rate");
144 puts (" --null-audio Use NULL audio device");
Benny Prijono1ebd6142006-10-19 15:48:02 +0000145 puts (" --play-file=file Register WAV file in conference bridge");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000146 puts (" --auto-play Automatically play the file (to incoming calls only)");
147 puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
Benny Prijono7ca96da2006-08-07 12:11:40 +0000148 puts (" --auto-conf Automatically put calls in conference with others");
Benny Prijono1ebd6142006-10-19 15:48:02 +0000149 puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");
150 puts (" --auto-rec Automatically record conversation");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000151 puts (" --rtp-port=N Base port to try for RTP (default=4000)");
Benny Prijono00cae612006-07-31 15:19:36 +0000152 puts (" --quality=N Specify media quality (0-10, default=6)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000153 puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)");
Benny Prijono0a12f002006-07-26 17:05:39 +0000154 puts (" --no-vad Disable VAD/silence detector (default=vad enabled)");
Benny Prijonod79f25c2006-08-02 19:41:37 +0000155 puts (" --ec-tail=MSEC Set echo canceller tail length (default=256)");
Benny Prijono00cae612006-07-31 15:19:36 +0000156 puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is 20)");
157 puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
158 puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
159
Benny Prijono0a12f002006-07-26 17:05:39 +0000160
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000161 puts ("");
162 puts ("Buddy List (can be more than one):");
163 puts (" --add-buddy url Add the specified URL to the buddy list.");
164 puts ("");
165 puts ("User Agent options:");
166 puts (" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)");
167 puts (" --max-calls=N Maximum number of concurrent calls (default:4, max:255)");
Benny Prijonof521eb02006-08-06 23:07:25 +0000168 puts (" --thread-cnt=N Number of worker threads (default:1)");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000169 puts (" --duration=SEC Set maximum call duration (default:no limit)");
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000170 puts (" --norefersub Suppress event subscription when transfering calls");
Benny Prijono804ff0a2006-09-14 11:17:48 +0000171
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000172 puts ("");
Benny Prijono0a5cad82006-09-26 13:21:02 +0000173 puts ("When URL is specified, pjsua will immediately initiate call to that URL");
174 puts ("");
175
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000176 fflush(stdout);
177}
178
179
180/* Set default config. */
181static void default_config(struct app_config *cfg)
182{
Benny Prijono56315612006-07-18 14:39:40 +0000183 char tmp[80];
184
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000185 pjsua_config_default(&cfg->cfg);
Benny Prijono56315612006-07-18 14:39:40 +0000186 pj_ansi_sprintf(tmp, "PJSUA v%s/%s", PJ_VERSION, PJ_OS_NAME);
187 pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp);
188
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000189 pjsua_logging_config_default(&cfg->log_cfg);
190 pjsua_media_config_default(&cfg->media_cfg);
191 pjsua_transport_config_default(&cfg->udp_cfg);
192 cfg->udp_cfg.port = 5060;
193 pjsua_transport_config_default(&cfg->rtp_cfg);
194 cfg->rtp_cfg.port = 4000;
Benny Prijono804ff0a2006-09-14 11:17:48 +0000195 cfg->duration = NO_LIMIT;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196 cfg->wav_id = PJSUA_INVALID_ID;
Benny Prijono1ebd6142006-10-19 15:48:02 +0000197 cfg->rec_id = PJSUA_INVALID_ID;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000198 cfg->wav_port = PJSUA_INVALID_ID;
Benny Prijono1ebd6142006-10-19 15:48:02 +0000199 cfg->rec_port = PJSUA_INVALID_ID;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200}
201
202
203/*
204 * Read command arguments from config file.
205 */
206static int read_config_file(pj_pool_t *pool, const char *filename,
207 int *app_argc, char ***app_argv)
208{
209 int i;
210 FILE *fhnd;
211 char line[200];
212 int argc = 0;
213 char **argv;
214 enum { MAX_ARGS = 64 };
215
216 /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
217 argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
218 argv[argc++] = *app_argv[0];
219
220 /* Open config file. */
221 fhnd = fopen(filename, "rt");
222 if (!fhnd) {
223 PJ_LOG(1,(THIS_FILE, "Unable to open config file %s", filename));
224 fflush(stdout);
225 return -1;
226 }
227
228 /* Scan tokens in the file. */
229 while (argc < MAX_ARGS && !feof(fhnd)) {
230 char *token, *p = line;
231
232 if (fgets(line, sizeof(line), fhnd) == NULL) break;
233
234 for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS;
235 token = strtok(NULL, " \t\r\n"))
236 {
237 int token_len;
238
239 if (!token) break;
240 if (*token == '#') break;
241
242 token_len = strlen(token);
243 if (!token_len)
244 continue;
245 argv[argc] = pj_pool_alloc(pool, token_len+1);
246 pj_memcpy(argv[argc], token, token_len+1);
247 ++argc;
248 }
249 }
250
251 /* Copy arguments from command line */
252 for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
253 argv[argc++] = (*app_argv)[i];
254
255 if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
256 PJ_LOG(1,(THIS_FILE,
257 "Too many arguments specified in cmd line/config file"));
258 fflush(stdout);
259 fclose(fhnd);
260 return -1;
261 }
262
263 fclose(fhnd);
264
265 /* Assign the new command line back to the original command line. */
266 *app_argc = argc;
267 *app_argv = argv;
268 return 0;
269
270}
271
272static int my_atoi(const char *cs)
273{
274 pj_str_t s;
275 return pj_strtoul(pj_cstr(&s, cs));
276}
277
278
279/* Parse arguments. */
280static pj_status_t parse_args(int argc, char *argv[],
281 struct app_config *cfg,
282 pj_str_t *uri_to_call)
283{
284 int c;
285 int option_index;
286 enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
287 OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
Benny Prijono0a5cad82006-09-26 13:21:02 +0000288 OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY,
289 OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000290 OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000291 OPT_NAMESERVER, OPT_USE_STUN1, OPT_USE_STUN2,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000292 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
293 OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
294 OPT_AUTO_CONF, OPT_CLOCK_RATE,
Benny Prijono00cae612006-07-31 15:19:36 +0000295 OPT_PLAY_FILE, OPT_RTP_PORT, OPT_ADD_CODEC, OPT_ILBC_MODE,
Benny Prijono1ebd6142006-10-19 15:48:02 +0000296 OPT_REC_FILE, OPT_AUTO_REC,
Benny Prijono0a12f002006-07-26 17:05:39 +0000297 OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
Benny Prijonod79f25c2006-08-02 19:41:37 +0000298 OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL,
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000299 OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
Benny Prijonof521eb02006-08-06 23:07:25 +0000300 OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000301 OPT_NOREFERSUB,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000302 };
303 struct pj_getopt_option long_options[] = {
304 { "config-file",1, 0, OPT_CONFIG_FILE},
305 { "log-file", 1, 0, OPT_LOG_FILE},
306 { "log-level", 1, 0, OPT_LOG_LEVEL},
307 { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
308 { "help", 0, 0, OPT_HELP},
309 { "version", 0, 0, OPT_VERSION},
310 { "clock-rate", 1, 0, OPT_CLOCK_RATE},
311 { "null-audio", 0, 0, OPT_NULL_AUDIO},
312 { "local-port", 1, 0, OPT_LOCAL_PORT},
Benny Prijono0a5cad82006-09-26 13:21:02 +0000313 { "ip-addr", 1, 0, OPT_IP_ADDR},
Benny Prijonoe93e2872006-06-28 16:46:49 +0000314 { "no-tcp", 0, 0, OPT_NO_TCP},
315 { "no-udp", 0, 0, OPT_NO_UDP},
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000316 { "norefersub", 0, 0, OPT_NOREFERSUB},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000317 { "proxy", 1, 0, OPT_PROXY},
318 { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
319 { "registrar", 1, 0, OPT_REGISTRAR},
320 { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000321 { "publish", 0, 0, OPT_PUBLISH},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000322 { "id", 1, 0, OPT_ID},
323 { "contact", 1, 0, OPT_CONTACT},
324 { "realm", 1, 0, OPT_REALM},
325 { "username", 1, 0, OPT_USERNAME},
326 { "password", 1, 0, OPT_PASSWORD},
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000327 { "nameserver", 1, 0, OPT_NAMESERVER},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000328 { "use-stun1", 1, 0, OPT_USE_STUN1},
329 { "use-stun2", 1, 0, OPT_USE_STUN2},
330 { "add-buddy", 1, 0, OPT_ADD_BUDDY},
331 { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
332 { "no-presence", 0, 0, OPT_NO_PRESENCE},
333 { "auto-answer",1, 0, OPT_AUTO_ANSWER},
334 { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
335 { "auto-play", 0, 0, OPT_AUTO_PLAY},
Benny Prijono1ebd6142006-10-19 15:48:02 +0000336 { "auto-rec", 0, 0, OPT_AUTO_REC},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000337 { "auto-loop", 0, 0, OPT_AUTO_LOOP},
338 { "auto-conf", 0, 0, OPT_AUTO_CONF},
339 { "play-file", 1, 0, OPT_PLAY_FILE},
Benny Prijono1ebd6142006-10-19 15:48:02 +0000340 { "rec-file", 1, 0, OPT_REC_FILE},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000341 { "rtp-port", 1, 0, OPT_RTP_PORT},
342 { "add-codec", 1, 0, OPT_ADD_CODEC},
343 { "complexity", 1, 0, OPT_COMPLEXITY},
344 { "quality", 1, 0, OPT_QUALITY},
345 { "ptime", 1, 0, OPT_PTIME},
Benny Prijono0a12f002006-07-26 17:05:39 +0000346 { "no-vad", 0, 0, OPT_NO_VAD},
Benny Prijonod79f25c2006-08-02 19:41:37 +0000347 { "ec-tail", 1, 0, OPT_EC_TAIL},
Benny Prijono00cae612006-07-31 15:19:36 +0000348 { "ilbc-mode", 1, 0, OPT_ILBC_MODE},
349 { "rx-drop-pct",1, 0, OPT_RX_DROP_PCT},
350 { "tx-drop-pct",1, 0, OPT_TX_DROP_PCT},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000351 { "next-account",0,0, OPT_NEXT_ACCOUNT},
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000352 { "next-cred", 0, 0, OPT_NEXT_CRED},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000353 { "max-calls", 1, 0, OPT_MAX_CALLS},
Benny Prijonof521eb02006-08-06 23:07:25 +0000354 { "duration", 1, 0, OPT_DURATION},
355 { "thread-cnt", 1, 0, OPT_THREAD_CNT},
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000356 { NULL, 0, 0, 0}
357 };
358 pj_status_t status;
359 pjsua_acc_config *cur_acc;
360 char *config_file = NULL;
361 unsigned i;
362
363 /* Run pj_getopt once to see if user specifies config file to read. */
Benny Prijonof762ee72006-12-01 11:14:37 +0000364 pj_optind = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000365 while ((c=pj_getopt_long(argc, argv, "", long_options,
366 &option_index)) != -1)
367 {
368 switch (c) {
369 case OPT_CONFIG_FILE:
370 config_file = pj_optarg;
371 break;
372 }
373 if (config_file)
374 break;
375 }
376
377 if (config_file) {
378 status = read_config_file(app_config.pool, config_file, &argc, &argv);
379 if (status != 0)
380 return status;
381 }
382
383 cfg->acc_cnt = 0;
384 cur_acc = &cfg->acc_cfg[0];
385
386
387 /* Reinitialize and re-run pj_getopt again, possibly with new arguments
388 * read from config file.
389 */
390 pj_optind = 0;
391 while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
392 char *p;
393 pj_str_t tmp;
394 long lval;
395
396 switch (c) {
397
Benny Prijono6f137482006-06-15 11:31:36 +0000398 case OPT_CONFIG_FILE:
399 /* Ignore as this has been processed before */
400 break;
401
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000402 case OPT_LOG_FILE:
403 cfg->log_cfg.log_filename = pj_str(pj_optarg);
404 break;
405
406 case OPT_LOG_LEVEL:
407 c = pj_strtoul(pj_cstr(&tmp, pj_optarg));
408 if (c < 0 || c > 6) {
409 PJ_LOG(1,(THIS_FILE,
410 "Error: expecting integer value 0-6 "
411 "for --log-level"));
412 return PJ_EINVAL;
413 }
414 cfg->log_cfg.level = c;
415 pj_log_set_level( c );
416 break;
417
418 case OPT_APP_LOG_LEVEL:
419 cfg->log_cfg.console_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
420 if (cfg->log_cfg.console_level < 0 || cfg->log_cfg.console_level > 6) {
421 PJ_LOG(1,(THIS_FILE,
422 "Error: expecting integer value 0-6 "
423 "for --app-log-level"));
424 return PJ_EINVAL;
425 }
426 break;
427
428 case OPT_HELP:
429 usage();
430 return PJ_EINVAL;
431
432 case OPT_VERSION: /* version */
433 pj_dump_config();
434 return PJ_EINVAL;
435
436 case OPT_NULL_AUDIO:
437 cfg->null_audio = PJ_TRUE;
438 break;
439
440 case OPT_CLOCK_RATE:
441 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
442 if (lval < 8000 || lval > 48000) {
443 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
444 "8000-48000 for clock rate"));
445 return PJ_EINVAL;
446 }
447 cfg->media_cfg.clock_rate = lval;
448 break;
449
450 case OPT_LOCAL_PORT: /* local-port */
451 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
452 if (lval < 1 || lval > 65535) {
453 PJ_LOG(1,(THIS_FILE,
454 "Error: expecting integer value for "
455 "--local-port"));
456 return PJ_EINVAL;
457 }
458 cfg->udp_cfg.port = (pj_uint16_t)lval;
459 break;
460
Benny Prijono0a5cad82006-09-26 13:21:02 +0000461 case OPT_IP_ADDR: /* ip-addr */
462 cfg->udp_cfg.public_addr = pj_str(pj_optarg);
463 cfg->rtp_cfg.public_addr = pj_str(pj_optarg);
464 break;
465
Benny Prijonoe93e2872006-06-28 16:46:49 +0000466 case OPT_NO_UDP: /* no-udp */
467 if (cfg->no_tcp) {
468 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
469 return PJ_EINVAL;
470 }
471
472 cfg->no_udp = PJ_TRUE;
473 break;
474
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000475 case OPT_NOREFERSUB: /* norefersub */
476 cfg->no_refersub = PJ_TRUE;
477 break;
478
Benny Prijonoe93e2872006-06-28 16:46:49 +0000479 case OPT_NO_TCP: /* no-tcp */
480 if (cfg->no_udp) {
481 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
482 return PJ_EINVAL;
483 }
484
485 cfg->no_tcp = PJ_TRUE;
486 break;
487
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000488 case OPT_PROXY: /* proxy */
489 if (pjsua_verify_sip_url(pj_optarg) != 0) {
490 PJ_LOG(1,(THIS_FILE,
491 "Error: invalid SIP URL '%s' "
492 "in proxy argument", pj_optarg));
493 return PJ_EINVAL;
494 }
495 cur_acc->proxy[cur_acc->proxy_cnt++] = pj_str(pj_optarg);
496 break;
497
498 case OPT_OUTBOUND_PROXY: /* outbound proxy */
499 if (pjsua_verify_sip_url(pj_optarg) != 0) {
500 PJ_LOG(1,(THIS_FILE,
501 "Error: invalid SIP URL '%s' "
502 "in outbound proxy argument", pj_optarg));
503 return PJ_EINVAL;
504 }
505 cfg->cfg.outbound_proxy[cfg->cfg.outbound_proxy_cnt++] = pj_str(pj_optarg);
506 break;
507
508 case OPT_REGISTRAR: /* registrar */
509 if (pjsua_verify_sip_url(pj_optarg) != 0) {
510 PJ_LOG(1,(THIS_FILE,
511 "Error: invalid SIP URL '%s' in "
512 "registrar argument", pj_optarg));
513 return PJ_EINVAL;
514 }
515 cur_acc->reg_uri = pj_str(pj_optarg);
516 break;
517
518 case OPT_REG_TIMEOUT: /* reg-timeout */
519 cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,pj_optarg));
520 if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
521 PJ_LOG(1,(THIS_FILE,
522 "Error: invalid value for --reg-timeout "
523 "(expecting 1-3600)"));
524 return PJ_EINVAL;
525 }
526 break;
527
Benny Prijono3a5e1ab2006-08-15 20:26:34 +0000528 case OPT_PUBLISH: /* publish */
529 cur_acc->publish_enabled = PJ_TRUE;
530 break;
531
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000532 case OPT_ID: /* id */
533 if (pjsua_verify_sip_url(pj_optarg) != 0) {
534 PJ_LOG(1,(THIS_FILE,
535 "Error: invalid SIP URL '%s' "
536 "in local id argument", pj_optarg));
537 return PJ_EINVAL;
538 }
539 cur_acc->id = pj_str(pj_optarg);
540 break;
541
542 case OPT_CONTACT: /* contact */
543 if (pjsua_verify_sip_url(pj_optarg) != 0) {
544 PJ_LOG(1,(THIS_FILE,
545 "Error: invalid SIP URL '%s' "
546 "in contact argument", pj_optarg));
547 return PJ_EINVAL;
548 }
Benny Prijonob4a17c92006-07-10 14:40:21 +0000549 cur_acc->force_contact = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000550 break;
551
552 case OPT_NEXT_ACCOUNT: /* Add more account. */
553 cfg->acc_cnt++;
Benny Prijono56315612006-07-18 14:39:40 +0000554 cur_acc = &cfg->acc_cfg[cfg->acc_cnt];
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000555 break;
556
557 case OPT_USERNAME: /* Default authentication user */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000558 cur_acc->cred_info[cur_acc->cred_count].username = pj_str(pj_optarg);
559 cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("digest");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000560 break;
561
562 case OPT_REALM: /* Default authentication realm. */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000563 cur_acc->cred_info[cur_acc->cred_count].realm = pj_str(pj_optarg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000564 break;
565
566 case OPT_PASSWORD: /* authentication password */
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000567 cur_acc->cred_info[cur_acc->cred_count].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
568 cur_acc->cred_info[cur_acc->cred_count].data = pj_str(pj_optarg);
569 break;
570
571 case OPT_NEXT_CRED: /* next credential */
572 cur_acc->cred_count++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000573 break;
574
Benny Prijonofa9e5b12006-10-08 12:39:34 +0000575 case OPT_NAMESERVER: /* nameserver */
576 cfg->cfg.nameserver[cfg->cfg.nameserver_count++] = pj_str(pj_optarg);
577 if (cfg->cfg.nameserver_count > PJ_ARRAY_SIZE(cfg->cfg.nameserver)) {
578 PJ_LOG(1,(THIS_FILE, "Error: too many nameservers"));
579 return PJ_ETOOMANY;
580 }
581 break;
582
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000583 case OPT_USE_STUN1: /* STUN server 1 */
584 p = pj_ansi_strchr(pj_optarg, ':');
585 if (p) {
586 *p = '\0';
587 cfg->udp_cfg.stun_config.stun_srv1 = pj_str(pj_optarg);
588 cfg->udp_cfg.stun_config.stun_port1 = pj_strtoul(pj_cstr(&tmp, p+1));
589 if (cfg->udp_cfg.stun_config.stun_port1 < 1 || cfg->udp_cfg.stun_config.stun_port1 > 65535) {
590 PJ_LOG(1,(THIS_FILE,
591 "Error: expecting port number with "
592 "option --use-stun1"));
593 return PJ_EINVAL;
594 }
595 } else {
596 cfg->udp_cfg.stun_config.stun_port1 = 3478;
597 cfg->udp_cfg.stun_config.stun_srv1 = pj_str(pj_optarg);
598 }
599 cfg->udp_cfg.use_stun = PJ_TRUE;
600 break;
601
602 case OPT_USE_STUN2: /* STUN server 2 */
603 p = pj_ansi_strchr(pj_optarg, ':');
604 if (p) {
605 *p = '\0';
606 cfg->udp_cfg.stun_config.stun_srv2 = pj_str(pj_optarg);
607 cfg->udp_cfg.stun_config.stun_port2 = pj_strtoul(pj_cstr(&tmp,p+1));
608 if (cfg->udp_cfg.stun_config.stun_port2 < 1 || cfg->udp_cfg.stun_config.stun_port2 > 65535) {
609 PJ_LOG(1,(THIS_FILE,
610 "Error: expecting port number with "
611 "option --use-stun2"));
612 return PJ_EINVAL;
613 }
614 } else {
615 cfg->udp_cfg.stun_config.stun_port2 = 3478;
616 cfg->udp_cfg.stun_config.stun_srv2 = pj_str(pj_optarg);
617 }
618 break;
619
620 case OPT_ADD_BUDDY: /* Add to buddy list. */
621 if (pjsua_verify_sip_url(pj_optarg) != 0) {
622 PJ_LOG(1,(THIS_FILE,
623 "Error: invalid URL '%s' in "
624 "--add-buddy option", pj_optarg));
625 return -1;
626 }
627 if (cfg->buddy_cnt == PJ_ARRAY_SIZE(cfg->buddy_cfg)) {
628 PJ_LOG(1,(THIS_FILE,
629 "Error: too many buddies in buddy list."));
630 return -1;
631 }
632 cfg->buddy_cfg[cfg->buddy_cnt].uri = pj_str(pj_optarg);
633 cfg->buddy_cnt++;
634 break;
635
636 case OPT_AUTO_PLAY:
637 cfg->auto_play = 1;
638 break;
639
Benny Prijono1ebd6142006-10-19 15:48:02 +0000640 case OPT_AUTO_REC:
641 cfg->auto_rec = 1;
642 break;
643
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000644 case OPT_AUTO_LOOP:
645 cfg->auto_loop = 1;
646 break;
647
Benny Prijono7ca96da2006-08-07 12:11:40 +0000648 case OPT_AUTO_CONF:
649 cfg->auto_conf = 1;
650 break;
651
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000652 case OPT_PLAY_FILE:
653 cfg->wav_file = pj_str(pj_optarg);
654 break;
655
Benny Prijono1ebd6142006-10-19 15:48:02 +0000656 case OPT_REC_FILE:
657 cfg->rec_file = pj_str(pj_optarg);
658 break;
659
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000660 case OPT_RTP_PORT:
661 cfg->rtp_cfg.port = my_atoi(pj_optarg);
662 if (cfg->rtp_cfg.port < 1 || cfg->rtp_cfg.port > 65535) {
663 PJ_LOG(1,(THIS_FILE,
664 "Error: rtp-port argument value "
665 "(expecting 1-65535"));
666 return -1;
667 }
668 break;
669
670 case OPT_ADD_CODEC:
671 cfg->codec_arg[cfg->codec_cnt++] = pj_str(pj_optarg);
672 break;
673
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000674 /* These options were no longer valid after new pjsua */
675 /*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000676 case OPT_COMPLEXITY:
677 cfg->complexity = my_atoi(pj_optarg);
678 if (cfg->complexity < 0 || cfg->complexity > 10) {
679 PJ_LOG(1,(THIS_FILE,
680 "Error: invalid --complexity (expecting 0-10"));
681 return -1;
682 }
683 break;
Benny Prijono804ff0a2006-09-14 11:17:48 +0000684 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000685
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000686 case OPT_DURATION:
687 cfg->duration = my_atoi(pj_optarg);
688 break;
689
Benny Prijonof521eb02006-08-06 23:07:25 +0000690 case OPT_THREAD_CNT:
691 cfg->cfg.thread_cnt = my_atoi(pj_optarg);
692 if (cfg->cfg.thread_cnt > 128) {
693 PJ_LOG(1,(THIS_FILE,
694 "Error: invalid --thread-cnt option"));
695 return -1;
696 }
697 break;
698
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000699 case OPT_PTIME:
Benny Prijono0a12f002006-07-26 17:05:39 +0000700 cfg->media_cfg.ptime = my_atoi(pj_optarg);
701 if (cfg->media_cfg.ptime < 10 || cfg->media_cfg.ptime > 1000) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000702 PJ_LOG(1,(THIS_FILE,
703 "Error: invalid --ptime option"));
704 return -1;
705 }
706 break;
707
Benny Prijono0a12f002006-07-26 17:05:39 +0000708 case OPT_NO_VAD:
709 cfg->media_cfg.no_vad = PJ_TRUE;
710 break;
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000711
Benny Prijonod79f25c2006-08-02 19:41:37 +0000712 case OPT_EC_TAIL:
713 cfg->media_cfg.ec_tail_len = my_atoi(pj_optarg);
714 if (cfg->media_cfg.ec_tail_len > 1000) {
715 PJ_LOG(1,(THIS_FILE, "I think the ec-tail length setting "
716 "is too big"));
717 return -1;
718 }
719 break;
720
Benny Prijono0498d902006-06-19 14:49:14 +0000721 case OPT_QUALITY:
722 cfg->media_cfg.quality = my_atoi(pj_optarg);
723 if (cfg->media_cfg.quality < 0 || cfg->media_cfg.quality > 10) {
724 PJ_LOG(1,(THIS_FILE,
725 "Error: invalid --quality (expecting 0-10"));
726 return -1;
727 }
728 break;
729
Benny Prijono00cae612006-07-31 15:19:36 +0000730 case OPT_ILBC_MODE:
731 cfg->media_cfg.ilbc_mode = my_atoi(pj_optarg);
732 if (cfg->media_cfg.ilbc_mode!=20 && cfg->media_cfg.ilbc_mode!=30) {
733 PJ_LOG(1,(THIS_FILE,
734 "Error: invalid --ilbc-mode (expecting 20 or 30"));
735 return -1;
736 }
737 break;
738
739 case OPT_RX_DROP_PCT:
740 cfg->media_cfg.rx_drop_pct = my_atoi(pj_optarg);
741 if (cfg->media_cfg.rx_drop_pct > 100) {
742 PJ_LOG(1,(THIS_FILE,
743 "Error: invalid --rx-drop-pct (expecting <= 100"));
744 return -1;
745 }
746 break;
747
748 case OPT_TX_DROP_PCT:
749 cfg->media_cfg.tx_drop_pct = my_atoi(pj_optarg);
750 if (cfg->media_cfg.tx_drop_pct > 100) {
751 PJ_LOG(1,(THIS_FILE,
752 "Error: invalid --tx-drop-pct (expecting <= 100"));
753 return -1;
754 }
755 break;
756
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000757 case OPT_AUTO_ANSWER:
758 cfg->auto_answer = my_atoi(pj_optarg);
759 if (cfg->auto_answer < 100 || cfg->auto_answer > 699) {
760 PJ_LOG(1,(THIS_FILE,
761 "Error: invalid code in --auto-answer "
762 "(expecting 100-699"));
763 return -1;
764 }
765 break;
766
767 case OPT_MAX_CALLS:
768 cfg->cfg.max_calls = my_atoi(pj_optarg);
Benny Prijono48af79c2006-07-22 12:49:17 +0000769 if (cfg->cfg.max_calls < 1 || cfg->cfg.max_calls > PJSUA_MAX_CALLS) {
Benny Prijono804ff0a2006-09-14 11:17:48 +0000770 PJ_LOG(1,(THIS_FILE,"Error: maximum call setting exceeds "
771 "compile time limit (PJSUA_MAX_CALLS=%d)",
Benny Prijono48af79c2006-07-22 12:49:17 +0000772 PJSUA_MAX_CALLS));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000773 return -1;
774 }
775 break;
776
Benny Prijono22a300a2006-06-14 20:04:55 +0000777 default:
Benny Prijono787b8692006-06-19 12:40:03 +0000778 PJ_LOG(1,(THIS_FILE,
Benny Prijonod6388ac2006-09-09 13:23:09 +0000779 "Argument \"%s\" is not valid. Use --help to see help",
780 argv[pj_optind-1]));
Benny Prijono22a300a2006-06-14 20:04:55 +0000781 return -1;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000782 }
783 }
784
785 if (pj_optind != argc) {
786 pj_str_t uri_arg;
787
788 if (pjsua_verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
789 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
790 return -1;
791 }
792 uri_arg = pj_str(argv[pj_optind]);
793 if (uri_to_call)
794 *uri_to_call = uri_arg;
795 pj_optind++;
796
797 /* Add URI to call to buddy list if it's not already there */
798 for (i=0; i<cfg->buddy_cnt; ++i) {
799 if (pj_stricmp(&cfg->buddy_cfg[i].uri, &uri_arg)==0)
800 break;
801 }
802 if (i == cfg->buddy_cnt && cfg->buddy_cnt < PJSUA_MAX_BUDDIES) {
803 cfg->buddy_cfg[cfg->buddy_cnt++].uri = uri_arg;
804 }
805
806 } else {
807 if (uri_to_call)
808 uri_to_call->slen = 0;
809 }
810
811 if (pj_optind != argc) {
812 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
813 return PJ_EINVAL;
814 }
815
Benny Prijono56315612006-07-18 14:39:40 +0000816 if (cfg->acc_cfg[cfg->acc_cnt].id.slen)
817 cfg->acc_cnt++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000818
819 for (i=0; i<cfg->acc_cnt; ++i) {
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000820 if (cfg->acc_cfg[i].cred_info[cfg->acc_cfg[i].cred_count].username.slen)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000821 {
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000822 cfg->acc_cfg[i].cred_count++;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000823 }
824 }
825
826
827 return PJ_SUCCESS;
828}
829
830
831/*
832 * Save account settings
833 */
834static void write_account_settings(int acc_index, pj_str_t *result)
835{
836 unsigned i;
837 char line[128];
838 pjsua_acc_config *acc_cfg = &app_config.acc_cfg[acc_index];
839
840
841 pj_ansi_sprintf(line, "\n#\n# Account %d:\n#\n", acc_index);
842 pj_strcat2(result, line);
843
844
845 /* Identity */
846 if (acc_cfg->id.slen) {
847 pj_ansi_sprintf(line, "--id %.*s\n",
848 (int)acc_cfg->id.slen,
849 acc_cfg->id.ptr);
850 pj_strcat2(result, line);
851 }
852
853 /* Registrar server */
854 if (acc_cfg->reg_uri.slen) {
855 pj_ansi_sprintf(line, "--registrar %.*s\n",
856 (int)acc_cfg->reg_uri.slen,
857 acc_cfg->reg_uri.ptr);
858 pj_strcat2(result, line);
859
860 pj_ansi_sprintf(line, "--reg-timeout %u\n",
861 acc_cfg->reg_timeout);
862 pj_strcat2(result, line);
863 }
864
865 /* Contact */
Benny Prijonob4a17c92006-07-10 14:40:21 +0000866 if (acc_cfg->force_contact.slen) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000867 pj_ansi_sprintf(line, "--contact %.*s\n",
Benny Prijonob4a17c92006-07-10 14:40:21 +0000868 (int)acc_cfg->force_contact.slen,
869 acc_cfg->force_contact.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000870 pj_strcat2(result, line);
871 }
872
873 /* Proxy */
874 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
875 pj_ansi_sprintf(line, "--proxy %.*s\n",
876 (int)acc_cfg->proxy[i].slen,
877 acc_cfg->proxy[i].ptr);
878 pj_strcat2(result, line);
879 }
880
881 /* Credentials */
882 for (i=0; i<acc_cfg->cred_count; ++i) {
883 if (acc_cfg->cred_info[i].realm.slen) {
884 pj_ansi_sprintf(line, "--realm %.*s\n",
885 (int)acc_cfg->cred_info[i].realm.slen,
886 acc_cfg->cred_info[i].realm.ptr);
887 pj_strcat2(result, line);
888 }
889
890 if (acc_cfg->cred_info[i].username.slen) {
891 pj_ansi_sprintf(line, "--username %.*s\n",
892 (int)acc_cfg->cred_info[i].username.slen,
893 acc_cfg->cred_info[i].username.ptr);
894 pj_strcat2(result, line);
895 }
896
897 if (acc_cfg->cred_info[i].data.slen) {
898 pj_ansi_sprintf(line, "--password %.*s\n",
899 (int)acc_cfg->cred_info[i].data.slen,
900 acc_cfg->cred_info[i].data.ptr);
901 pj_strcat2(result, line);
902 }
Benny Prijonoeef4f8c2006-06-19 12:07:44 +0000903
904 if (i != acc_cfg->cred_count - 1)
905 pj_strcat2(result, "--next-cred\n");
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000906 }
907
908}
909
910
911/*
912 * Write settings.
913 */
914static int write_settings(const struct app_config *config,
915 char *buf, pj_size_t max)
916{
917 unsigned acc_index;
918 unsigned i;
919 pj_str_t cfg;
920 char line[128];
921
922 PJ_UNUSED_ARG(max);
923
924 cfg.ptr = buf;
925 cfg.slen = 0;
926
927 /* Logging. */
928 pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
929 pj_ansi_sprintf(line, "--log-level %d\n",
930 config->log_cfg.level);
931 pj_strcat2(&cfg, line);
932
933 pj_ansi_sprintf(line, "--app-log-level %d\n",
934 config->log_cfg.console_level);
935 pj_strcat2(&cfg, line);
936
937 if (config->log_cfg.log_filename.slen) {
938 pj_ansi_sprintf(line, "--log-file %.*s\n",
939 (int)config->log_cfg.log_filename.slen,
940 config->log_cfg.log_filename.ptr);
941 pj_strcat2(&cfg, line);
942 }
943
944
945 /* Save account settings. */
946 for (acc_index=0; acc_index < config->acc_cnt; ++acc_index) {
947
948 write_account_settings(acc_index, &cfg);
949
950 if (acc_index < config->acc_cnt-1)
951 pj_strcat2(&cfg, "--next-account\n");
952 }
953
954
955 pj_strcat2(&cfg, "\n#\n# Network settings:\n#\n");
956
957 /* Outbound proxy */
958 for (i=0; i<config->cfg.outbound_proxy_cnt; ++i) {
959 pj_ansi_sprintf(line, "--outbound %.*s\n",
960 (int)config->cfg.outbound_proxy[i].slen,
961 config->cfg.outbound_proxy[i].ptr);
962 pj_strcat2(&cfg, line);
963 }
964
965
966 /* UDP Transport. */
967 pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port);
968 pj_strcat2(&cfg, line);
969
Benny Prijono0a5cad82006-09-26 13:21:02 +0000970 /* IP address, if any. */
971 if (config->udp_cfg.public_addr.slen) {
972 pj_ansi_sprintf(line, "--ip-addr %.*s\n",
973 (int)config->udp_cfg.public_addr.slen,
974 config->udp_cfg.public_addr.ptr);
975 pj_strcat2(&cfg, line);
976 }
977
Benny Prijono4ddad2c2006-10-18 17:16:34 +0000978 /* No TCP ? */
979 if (config->no_tcp) {
980 pj_strcat2(&cfg, "--no-tcp\n");
981 }
982
983 /* No UDP ? */
984 if (config->no_udp) {
985 pj_strcat2(&cfg, "--no-udp\n");
986 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000987
988 /* STUN */
989 if (config->udp_cfg.stun_config.stun_port1) {
990 pj_ansi_sprintf(line, "--use-stun1 %.*s:%d\n",
991 (int)config->udp_cfg.stun_config.stun_srv1.slen,
992 config->udp_cfg.stun_config.stun_srv1.ptr,
993 config->udp_cfg.stun_config.stun_port1);
994 pj_strcat2(&cfg, line);
995 }
996
997 if (config->udp_cfg.stun_config.stun_port2) {
998 pj_ansi_sprintf(line, "--use-stun2 %.*s:%d\n",
999 (int)config->udp_cfg.stun_config.stun_srv2.slen,
1000 config->udp_cfg.stun_config.stun_srv2.ptr,
1001 config->udp_cfg.stun_config.stun_port2);
1002 pj_strcat2(&cfg, line);
1003 }
1004
1005
1006 pj_strcat2(&cfg, "\n#\n# Media settings:\n#\n");
1007
1008
1009 /* Media */
1010 if (config->null_audio)
1011 pj_strcat2(&cfg, "--null-audio\n");
1012 if (config->auto_play)
1013 pj_strcat2(&cfg, "--auto-play\n");
1014 if (config->auto_loop)
1015 pj_strcat2(&cfg, "--auto-loop\n");
Benny Prijono7ca96da2006-08-07 12:11:40 +00001016 if (config->auto_conf)
1017 pj_strcat2(&cfg, "--auto-conf\n");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001018 if (config->wav_file.slen) {
1019 pj_ansi_sprintf(line, "--play-file %s\n",
1020 config->wav_file.ptr);
1021 pj_strcat2(&cfg, line);
1022 }
Benny Prijono1ebd6142006-10-19 15:48:02 +00001023 if (config->rec_file.slen) {
1024 pj_ansi_sprintf(line, "--rec-file %s\n",
1025 config->rec_file.ptr);
1026 pj_strcat2(&cfg, line);
1027 }
1028 if (config->auto_rec)
1029 pj_strcat2(&cfg, "--auto-rec\n");
1030
1031
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001032 /* Media clock rate. */
Benny Prijono70972992006-08-05 11:13:58 +00001033 if (config->media_cfg.clock_rate != PJSUA_DEFAULT_CLOCK_RATE) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001034 pj_ansi_sprintf(line, "--clock-rate %d\n",
Benny Prijono0498d902006-06-19 14:49:14 +00001035 config->media_cfg.clock_rate);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001036 pj_strcat2(&cfg, line);
Benny Prijono70972992006-08-05 11:13:58 +00001037 } else {
1038 pj_ansi_sprintf(line, "#using default --clock-rate %d\n",
1039 config->media_cfg.clock_rate);
1040 pj_strcat2(&cfg, line);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001041 }
Benny Prijono70972992006-08-05 11:13:58 +00001042
1043 /* quality */
1044 if (config->media_cfg.quality != PJSUA_DEFAULT_CODEC_QUALITY) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001045 pj_ansi_sprintf(line, "--quality %d\n",
Benny Prijono0498d902006-06-19 14:49:14 +00001046 config->media_cfg.quality);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001047 pj_strcat2(&cfg, line);
Benny Prijono70972992006-08-05 11:13:58 +00001048 } else {
1049 pj_ansi_sprintf(line, "#using default --quality %d\n",
1050 config->media_cfg.quality);
1051 pj_strcat2(&cfg, line);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001052 }
Benny Prijono0498d902006-06-19 14:49:14 +00001053
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001054
1055 /* ptime */
1056 if (config->ptime) {
1057 pj_ansi_sprintf(line, "--ptime %d\n",
1058 config->ptime);
1059 pj_strcat2(&cfg, line);
1060 }
1061
Benny Prijono70972992006-08-05 11:13:58 +00001062 /* no-vad */
1063 if (config->media_cfg.no_vad) {
1064 pj_strcat2(&cfg, "--no-vad\n");
1065 }
1066
1067 /* ec-tail */
1068 if (config->media_cfg.ec_tail_len != PJSUA_DEFAULT_EC_TAIL_LEN) {
1069 pj_ansi_sprintf(line, "--ec-tail %d\n",
1070 config->media_cfg.ec_tail_len);
1071 pj_strcat2(&cfg, line);
1072 } else {
1073 pj_ansi_sprintf(line, "#using default --ec-tail %d\n",
1074 config->media_cfg.ec_tail_len);
1075 pj_strcat2(&cfg, line);
1076 }
1077
1078
1079 /* ilbc-mode */
1080 if (config->media_cfg.ilbc_mode != PJSUA_DEFAULT_ILBC_MODE) {
1081 pj_ansi_sprintf(line, "--ilbc-mode %d\n",
1082 config->media_cfg.ilbc_mode);
1083 pj_strcat2(&cfg, line);
1084 } else {
1085 pj_ansi_sprintf(line, "#using default --ilbc-mode %d\n",
1086 config->media_cfg.ilbc_mode);
1087 pj_strcat2(&cfg, line);
1088 }
1089
1090 /* RTP drop */
1091 if (config->media_cfg.tx_drop_pct) {
1092 pj_ansi_sprintf(line, "--tx-drop-pct %d\n",
1093 config->media_cfg.tx_drop_pct);
1094 pj_strcat2(&cfg, line);
1095
1096 }
1097 if (config->media_cfg.rx_drop_pct) {
1098 pj_ansi_sprintf(line, "--rx-drop-pct %d\n",
1099 config->media_cfg.rx_drop_pct);
1100 pj_strcat2(&cfg, line);
1101
1102 }
1103
1104
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001105 /* Start RTP port. */
1106 pj_ansi_sprintf(line, "--rtp-port %d\n",
1107 config->rtp_cfg.port);
1108 pj_strcat2(&cfg, line);
1109
1110 /* Add codec. */
1111 for (i=0; i<config->codec_cnt; ++i) {
1112 pj_ansi_sprintf(line, "--add-codec %s\n",
1113 config->codec_arg[i].ptr);
1114 pj_strcat2(&cfg, line);
1115 }
1116
1117 pj_strcat2(&cfg, "\n#\n# User agent:\n#\n");
1118
1119 /* Auto-answer. */
1120 if (config->auto_answer != 0) {
1121 pj_ansi_sprintf(line, "--auto-answer %d\n",
1122 config->auto_answer);
1123 pj_strcat2(&cfg, line);
1124 }
1125
1126 /* Max calls. */
1127 pj_ansi_sprintf(line, "--max-calls %d\n",
1128 config->cfg.max_calls);
1129 pj_strcat2(&cfg, line);
1130
1131 /* Uas-duration. */
Benny Prijono804ff0a2006-09-14 11:17:48 +00001132 if (config->duration != NO_LIMIT) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001133 pj_ansi_sprintf(line, "--duration %d\n",
1134 config->duration);
1135 pj_strcat2(&cfg, line);
1136 }
1137
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001138 /* norefersub ? */
1139 if (config->no_refersub) {
1140 pj_strcat2(&cfg, "--norefersub\n");
1141 }
1142
1143
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001144 pj_strcat2(&cfg, "\n#\n# Buddies:\n#\n");
1145
1146 /* Add buddies. */
1147 for (i=0; i<config->buddy_cnt; ++i) {
1148 pj_ansi_sprintf(line, "--add-buddy %.*s\n",
1149 (int)config->buddy_cfg[i].uri.slen,
1150 config->buddy_cfg[i].uri.ptr);
1151 pj_strcat2(&cfg, line);
1152 }
1153
1154
1155 *(cfg.ptr + cfg.slen) = '\0';
1156 return cfg.slen;
1157}
1158
1159
1160/*
1161 * Dump application states.
1162 */
1163static void app_dump(pj_bool_t detail)
1164{
1165 unsigned old_decor;
1166 char buf[1024];
1167
1168 PJ_LOG(3,(THIS_FILE, "Start dumping application states:"));
1169
1170 old_decor = pj_log_get_decor();
1171 pj_log_set_decor(old_decor & (PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
1172
1173 if (detail)
1174 pj_dump_config();
1175
1176 pjsip_endpt_dump(pjsua_get_pjsip_endpt(), detail);
1177 pjmedia_endpt_dump(pjsua_get_pjmedia_endpt());
1178 pjsip_tsx_layer_dump(detail);
1179 pjsip_ua_dump(detail);
1180
1181
1182 /* Dump all invite sessions: */
1183 PJ_LOG(3,(THIS_FILE, "Dumping invite sessions:"));
1184
1185 if (pjsua_call_get_count() == 0) {
1186
1187 PJ_LOG(3,(THIS_FILE, " - no sessions -"));
1188
1189 } else {
1190 unsigned i;
1191
1192 for (i=0; i<app_config.cfg.max_calls; ++i) {
1193 if (pjsua_call_is_active(i)) {
1194 pjsua_call_dump(i, detail, buf, sizeof(buf), " ");
1195 PJ_LOG(3,(THIS_FILE, "%s", buf));
1196 }
1197 }
1198 }
1199
1200 /* Dump presence status */
1201 pjsua_pres_dump(detail);
1202
1203 pj_log_set_decor(old_decor);
1204 PJ_LOG(3,(THIS_FILE, "Dump complete"));
1205}
1206
1207
1208/*****************************************************************************
1209 * Console application
1210 */
1211
1212/*
1213 * Find next call when current call is disconnected or when user
1214 * press ']'
1215 */
1216static pj_bool_t find_next_call(void)
1217{
1218 int i, max;
1219
1220 max = pjsua_call_get_max_count();
1221 for (i=current_call+1; i<max; ++i) {
1222 if (pjsua_call_is_active(i)) {
1223 current_call = i;
1224 return PJ_TRUE;
1225 }
1226 }
1227
1228 for (i=0; i<current_call; ++i) {
1229 if (pjsua_call_is_active(i)) {
1230 current_call = i;
1231 return PJ_TRUE;
1232 }
1233 }
1234
1235 current_call = PJSUA_INVALID_ID;
1236 return PJ_FALSE;
1237}
1238
1239
1240/*
1241 * Find previous call when user press '['
1242 */
1243static pj_bool_t find_prev_call(void)
1244{
1245 int i, max;
1246
1247 max = pjsua_call_get_max_count();
1248 for (i=current_call-1; i>=0; --i) {
1249 if (pjsua_call_is_active(i)) {
1250 current_call = i;
1251 return PJ_TRUE;
1252 }
1253 }
1254
1255 for (i=max-1; i>current_call; --i) {
1256 if (pjsua_call_is_active(i)) {
1257 current_call = i;
1258 return PJ_TRUE;
1259 }
1260 }
1261
1262 current_call = PJSUA_INVALID_ID;
1263 return PJ_FALSE;
1264}
1265
1266
Benny Prijono804ff0a2006-09-14 11:17:48 +00001267/* Callback from timer when the maximum call duration has been
1268 * exceeded.
1269 */
1270static void call_timeout_callback(pj_timer_heap_t *timer_heap,
1271 struct pj_timer_entry *entry)
1272{
1273 pjsua_call_id call_id = entry->id;
1274 pjsua_msg_data msg_data;
1275 pjsip_generic_string_hdr warn;
1276 pj_str_t hname = pj_str("Warning");
1277 pj_str_t hvalue = pj_str("399 pjsua \"Call duration exceeded\"");
1278
1279 PJ_UNUSED_ARG(timer_heap);
1280
Benny Prijono148c9dd2006-09-19 13:37:53 +00001281 if (call_id == PJSUA_INVALID_ID) {
1282 PJ_LOG(1,(THIS_FILE, "Invalid call ID in timer callback"));
1283 return;
1284 }
1285
Benny Prijono804ff0a2006-09-14 11:17:48 +00001286 /* Add warning header */
1287 pjsua_msg_data_init(&msg_data);
1288 pjsip_generic_string_hdr_init2(&warn, &hname, &hvalue);
1289 pj_list_push_back(&msg_data.hdr_list, &warn);
1290
1291 /* Call duration has been exceeded; disconnect the call */
1292 PJ_LOG(3,(THIS_FILE, "Duration (%d seconds) has been exceeded "
1293 "for call %d, disconnecting the call",
1294 app_config.duration, call_id));
1295 entry->id = PJSUA_INVALID_ID;
1296 pjsua_call_hangup(call_id, 200, NULL, &msg_data);
1297}
1298
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001299
1300/*
1301 * Handler when invite state has changed.
1302 */
1303static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
1304{
1305 pjsua_call_info call_info;
1306
1307 PJ_UNUSED_ARG(e);
1308
1309 pjsua_call_get_info(call_id, &call_info);
1310
1311 if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
1312
Benny Prijono804ff0a2006-09-14 11:17:48 +00001313 /* Cancel duration timer, if any */
1314 if (app_config.call_data[call_id].timer.id != PJSUA_INVALID_ID) {
1315 struct call_data *cd = &app_config.call_data[call_id];
1316 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
1317
1318 cd->timer.id = PJSUA_INVALID_ID;
1319 pjsip_endpt_cancel_timer(endpt, &cd->timer);
1320 }
1321
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001322 PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]",
1323 call_id,
1324 call_info.last_status,
1325 call_info.last_status_text.ptr));
1326
1327 if (call_id == current_call) {
1328 find_next_call();
1329 }
1330
Benny Prijono4be63b52006-11-25 14:50:25 +00001331 /* Dump media state upon disconnected */
1332 if (1) {
1333 char buf[1024];
1334 pjsua_call_dump(call_id, PJ_TRUE, buf,
1335 sizeof(buf), " ");
1336 PJ_LOG(5,(THIS_FILE,
1337 "Call %d disconnected, dumping media stats\n%s",
1338 call_id, buf));
1339 }
1340
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001341 } else {
1342
Benny Prijono804ff0a2006-09-14 11:17:48 +00001343 if (app_config.duration!=NO_LIMIT &&
1344 call_info.state == PJSIP_INV_STATE_CONFIRMED)
1345 {
1346 /* Schedule timer to hangup call after the specified duration */
1347 struct call_data *cd = &app_config.call_data[call_id];
1348 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
1349 pj_time_val delay;
1350
1351 cd->timer.id = call_id;
1352 delay.sec = app_config.duration;
1353 delay.msec = 0;
1354 pjsip_endpt_schedule_timer(endpt, &cd->timer, &delay);
1355 }
1356
Benny Prijono4be63b52006-11-25 14:50:25 +00001357 if (call_info.state == PJSIP_INV_STATE_EARLY) {
1358 int code;
1359 pj_str_t reason;
1360 pjsip_msg *msg;
1361
1362 /* This can only occur because of TX or RX message */
1363 pj_assert(e->type == PJSIP_EVENT_TSX_STATE);
1364
1365 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
1366 msg = e->body.tsx_state.src.rdata->msg_info.msg;
1367 } else {
1368 msg = e->body.tsx_state.src.tdata->msg;
1369 }
1370
1371 code = msg->line.status.code;
1372 reason = msg->line.status.reason;
1373
1374 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s (%d %.*s)",
1375 call_id, call_info.state_text.ptr,
1376 code, (int)reason.slen, reason.ptr));
1377 } else {
1378 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
1379 call_id,
1380 call_info.state_text.ptr));
1381 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001382
1383 if (current_call==PJSUA_INVALID_ID)
1384 current_call = call_id;
1385
1386 }
1387}
1388
1389
1390/**
1391 * Handler when there is incoming call.
1392 */
1393static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
1394 pjsip_rx_data *rdata)
1395{
1396 pjsua_call_info call_info;
1397
1398 PJ_UNUSED_ARG(acc_id);
1399 PJ_UNUSED_ARG(rdata);
1400
1401 pjsua_call_get_info(call_id, &call_info);
1402
1403 if (app_config.auto_answer > 0) {
1404 pjsua_call_answer(call_id, app_config.auto_answer, NULL, NULL);
1405 }
1406
1407 if (app_config.auto_answer < 200) {
1408 PJ_LOG(3,(THIS_FILE,
1409 "Incoming call for account %d!\n"
1410 "From: %s\n"
1411 "To: %s\n"
1412 "Press a to answer or h to reject call",
1413 acc_id,
1414 call_info.remote_info.ptr,
1415 call_info.local_info.ptr));
1416 }
1417}
1418
1419
1420/*
1421 * Callback on media state changed event.
1422 * The action may connect the call to sound device, to file, or
1423 * to loop the call.
1424 */
1425static void on_call_media_state(pjsua_call_id call_id)
1426{
1427 pjsua_call_info call_info;
1428
1429 pjsua_call_get_info(call_id, &call_info);
1430
1431 if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
1432 pj_bool_t connect_sound = PJ_TRUE;
1433
1434 /* Loopback sound, if desired */
1435 if (app_config.auto_loop) {
1436 pjsua_conf_connect(call_info.conf_slot, call_info.conf_slot);
1437 connect_sound = PJ_FALSE;
Benny Prijono1ebd6142006-10-19 15:48:02 +00001438
1439 /* Automatically record conversation, if desired */
1440 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1441 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
1442 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001443 }
1444
1445 /* Stream a file, if desired */
1446 if (app_config.auto_play && app_config.wav_port != PJSUA_INVALID_ID) {
1447 pjsua_conf_connect(app_config.wav_port, call_info.conf_slot);
1448 connect_sound = PJ_FALSE;
1449 }
1450
Benny Prijono7ca96da2006-08-07 12:11:40 +00001451 /* Put call in conference with other calls, if desired */
1452 if (app_config.auto_conf) {
1453 pjsua_call_id call_ids[PJSUA_MAX_CALLS];
Benny Prijono10861bc2006-08-07 13:22:43 +00001454 unsigned call_cnt=PJ_ARRAY_SIZE(call_ids);
Benny Prijono7ca96da2006-08-07 12:11:40 +00001455 unsigned i;
1456
1457 /* Get all calls, and establish media connection between
1458 * this call and other calls.
1459 */
1460 pjsua_enum_calls(call_ids, &call_cnt);
Benny Prijono10861bc2006-08-07 13:22:43 +00001461
Benny Prijono7ca96da2006-08-07 12:11:40 +00001462 for (i=0; i<call_cnt; ++i) {
1463 if (call_ids[i] == call_id)
1464 continue;
1465
1466 if (!pjsua_call_has_media(call_ids[i]))
1467 continue;
1468
1469 pjsua_conf_connect(call_info.conf_slot,
1470 pjsua_call_get_conf_port(call_ids[i]));
1471 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
1472 call_info.conf_slot);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001473
1474 /* Automatically record conversation, if desired */
1475 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1476 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
1477 app_config.rec_port);
1478 }
1479
Benny Prijono7ca96da2006-08-07 12:11:40 +00001480 }
1481
1482 /* Also connect call to local sound device */
1483 connect_sound = PJ_TRUE;
1484 }
1485
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001486 /* Otherwise connect to sound device */
1487 if (connect_sound) {
1488 pjsua_conf_connect(call_info.conf_slot, 0);
1489 pjsua_conf_connect(0, call_info.conf_slot);
Benny Prijono1ebd6142006-10-19 15:48:02 +00001490
1491 /* Automatically record conversation, if desired */
1492 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
1493 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
1494 pjsua_conf_connect(0, app_config.rec_port);
1495 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001496 }
1497
1498 PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id));
1499
1500 } else if (call_info.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
1501 PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local",
1502 call_id));
1503 } else if (call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD) {
1504 PJ_LOG(3,(THIS_FILE,
1505 "Media for call %d is suspended (hold) by remote",
1506 call_id));
1507 } else {
1508 PJ_LOG(3,(THIS_FILE,
1509 "Media for call %d is inactive",
1510 call_id));
1511 }
1512}
1513
1514
1515/*
1516 * Handler registration status has changed.
1517 */
1518static void on_reg_state(pjsua_acc_id acc_id)
1519{
1520 PJ_UNUSED_ARG(acc_id);
1521
1522 // Log already written.
1523}
1524
1525
1526/*
1527 * Handler on buddy state changed.
1528 */
1529static void on_buddy_state(pjsua_buddy_id buddy_id)
1530{
1531 pjsua_buddy_info info;
1532 pjsua_buddy_get_info(buddy_id, &info);
1533
1534 PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s",
1535 (int)info.uri.slen,
1536 info.uri.ptr,
1537 (int)info.status_text.slen,
1538 info.status_text.ptr));
1539}
1540
1541
1542/**
1543 * Incoming IM message (i.e. MESSAGE request)!
1544 */
1545static void on_pager(pjsua_call_id call_id, const pj_str_t *from,
1546 const pj_str_t *to, const pj_str_t *contact,
1547 const pj_str_t *mime_type, const pj_str_t *text)
1548{
1549 /* Note: call index may be -1 */
1550 PJ_UNUSED_ARG(call_id);
1551 PJ_UNUSED_ARG(to);
1552 PJ_UNUSED_ARG(contact);
1553 PJ_UNUSED_ARG(mime_type);
1554
1555 PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s",
1556 (int)from->slen, from->ptr,
1557 (int)text->slen, text->ptr));
1558}
1559
1560
1561/**
1562 * Received typing indication
1563 */
1564static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
1565 const pj_str_t *to, const pj_str_t *contact,
1566 pj_bool_t is_typing)
1567{
1568 PJ_UNUSED_ARG(call_id);
1569 PJ_UNUSED_ARG(to);
1570 PJ_UNUSED_ARG(contact);
1571
1572 PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
1573 (int)from->slen, from->ptr,
1574 (is_typing?"is typing..":"has stopped typing")));
1575}
1576
1577
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001578/**
1579 * Call transfer request status.
1580 */
1581static void on_call_transfer_status(pjsua_call_id call_id,
1582 int status_code,
1583 const pj_str_t *status_text,
1584 pj_bool_t final,
1585 pj_bool_t *p_cont)
1586{
1587 PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
1588 call_id, status_code,
1589 (int)status_text->slen, status_text->ptr,
1590 (final ? "[final]" : "")));
1591
1592 if (status_code/100 == 2) {
1593 PJ_LOG(3,(THIS_FILE,
1594 "Call %d: call transfered successfully, disconnecting call",
1595 call_id));
1596 pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
1597 *p_cont = PJ_FALSE;
1598 }
1599}
1600
1601
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001602/*
Benny Prijonof7b1c392006-11-11 16:46:34 +00001603 * Notification that call is being replaced.
1604 */
1605static void on_call_replaced(pjsua_call_id old_call_id,
1606 pjsua_call_id new_call_id)
1607{
1608 pjsua_call_info old_ci, new_ci;
1609
1610 pjsua_call_get_info(old_call_id, &old_ci);
1611 pjsua_call_get_info(new_call_id, &new_ci);
1612
1613 PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by "
1614 "call %d with %.*s",
1615 old_call_id,
1616 (int)old_ci.remote_info.slen, old_ci.remote_info.ptr,
1617 new_call_id,
1618 (int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
1619}
1620
1621
1622/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001623 * Print buddy list.
1624 */
1625static void print_buddy_list(void)
1626{
1627 pjsua_buddy_id ids[64];
1628 int i;
1629 unsigned count = PJ_ARRAY_SIZE(ids);
1630
1631 puts("Buddy list:");
1632
1633 pjsua_enum_buddies(ids, &count);
1634
1635 if (count == 0)
1636 puts(" -none-");
1637 else {
1638 for (i=0; i<(int)count; ++i) {
1639 pjsua_buddy_info info;
1640
1641 if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
1642 continue;
1643
1644 printf(" [%2d] <%7s> %.*s\n",
1645 ids[i]+1, info.status_text.ptr,
1646 (int)info.uri.slen,
1647 info.uri.ptr);
1648 }
1649 }
1650 puts("");
1651}
1652
1653
1654/*
1655 * Print account status.
1656 */
1657static void print_acc_status(int acc_id)
1658{
1659 char buf[80];
1660 pjsua_acc_info info;
1661
1662 pjsua_acc_get_info(acc_id, &info);
1663
1664 if (!info.has_registration) {
1665 pj_ansi_snprintf(buf, sizeof(buf), "%.*s",
1666 (int)info.status_text.slen,
1667 info.status_text.ptr);
1668
1669 } else {
1670 pj_ansi_snprintf(buf, sizeof(buf),
1671 "%d/%.*s (expires=%d)",
1672 info.status,
1673 (int)info.status_text.slen,
1674 info.status_text.ptr,
1675 info.expires);
1676
1677 }
1678
1679 printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
1680 acc_id, (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
1681 printf(" Online status: %s\n",
1682 (info.online_status ? "Online" : "Invisible"));
1683}
1684
1685
1686/*
1687 * Show a bit of help.
1688 */
1689static void keystroke_help(void)
1690{
1691 pjsua_acc_id acc_ids[16];
1692 unsigned count = PJ_ARRAY_SIZE(acc_ids);
1693 int i;
1694
1695 printf(">>>>\n");
1696
1697 pjsua_enum_accs(acc_ids, &count);
1698
1699 printf("Account list:\n");
1700 for (i=0; i<(int)count; ++i)
1701 print_acc_status(acc_ids[i]);
1702
1703 print_buddy_list();
1704
1705 //puts("Commands:");
1706 puts("+=============================================================================+");
1707 puts("| Call Commands: | Buddy, IM & Presence: | Account: |");
1708 puts("| | | |");
1709 puts("| m Make new call | +b Add new buddy .| +a Add new accnt |");
1710 puts("| M Make multiple calls | -b Delete buddy | -a Delete accnt. |");
1711 puts("| a Answer call | !b Modify buddy | !a Modify accnt. |");
1712 puts("| h Hangup call (ha=all) | i Send IM | rr (Re-)register |");
1713 puts("| H Hold call | s Subscribe presence | ru Unregister |");
1714 puts("| v re-inVite (release hold) | u Unsubscribe presence | > Cycle next ac.|");
1715 puts("| ] Select next dialog | t ToGgle Online status | < Cycle prev ac.|");
1716 puts("| [ Select previous dialog +--------------------------+-------------------+");
1717 puts("| x Xfer call | Media Commands: | Status & Config: |");
Benny Prijonof7b1c392006-11-11 16:46:34 +00001718 puts("| X Xfer with Replaces | | |");
1719 puts("| # Send DTMF string | cl List ports | d Dump status |");
1720 puts("| dq Dump curr. call quality | cc Connect port | dd Dump detailed |");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001721 puts("| | cd Disconnect port | dc Dump config |");
Benny Prijono56315612006-07-18 14:39:40 +00001722 puts("| S Send arbitrary REQUEST | | f Save config |");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001723 puts("+------------------------------+--------------------------+-------------------+");
1724 puts("| q QUIT |");
1725 puts("+=============================================================================+");
Benny Prijono48af79c2006-07-22 12:49:17 +00001726
1727 i = pjsua_call_get_count();
1728 printf("You have %d active call%s\n", i, (i>1?"s":""));
Benny Prijonof7b1c392006-11-11 16:46:34 +00001729
1730 if (current_call != PJSUA_INVALID_ID) {
1731 pjsua_call_info ci;
1732 if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
1733 printf("Current call id=%d to %.*s [%.*s]\n", current_call,
1734 (int)ci.remote_info.slen, ci.remote_info.ptr,
1735 (int)ci.state_text.slen, ci.state_text.ptr);
1736 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001737}
1738
1739
1740/*
1741 * Input simple string
1742 */
1743static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
1744{
1745 char *p;
1746
1747 printf("%s (empty to cancel): ", title); fflush(stdout);
1748 fgets(buf, len, stdin);
1749
1750 /* Remove trailing newlines. */
1751 for (p=buf; ; ++p) {
1752 if (*p=='\r' || *p=='\n') *p='\0';
1753 else if (!*p) break;
1754 }
1755
1756 if (!*buf)
1757 return PJ_FALSE;
1758
1759 return PJ_TRUE;
1760}
1761
1762
1763#define NO_NB -2
1764struct input_result
1765{
1766 int nb_result;
1767 char *uri_result;
1768};
1769
1770
1771/*
1772 * Input URL.
1773 */
1774static void ui_input_url(const char *title, char *buf, int len,
1775 struct input_result *result)
1776{
1777 result->nb_result = NO_NB;
1778 result->uri_result = NULL;
1779
1780 print_buddy_list();
1781
1782 printf("Choices:\n"
1783 " 0 For current dialog.\n"
1784 " -1 All %d buddies in buddy list\n"
1785 " [1 -%2d] Select from buddy list\n"
1786 " URL An URL\n"
1787 " <Enter> Empty input (or 'q') to cancel\n"
1788 , pjsua_get_buddy_count(), pjsua_get_buddy_count());
1789 printf("%s: ", title);
1790
1791 fflush(stdout);
1792 fgets(buf, len, stdin);
1793 len = strlen(buf);
1794
1795 /* Left trim */
1796 while (pj_isspace(*buf)) {
1797 ++buf;
1798 --len;
1799 }
1800
1801 /* Remove trailing newlines */
1802 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
1803 buf[--len] = '\0';
1804
1805 if (len == 0 || buf[0]=='q')
1806 return;
1807
1808 if (pj_isdigit(*buf) || *buf=='-') {
1809
1810 int i;
1811
1812 if (*buf=='-')
1813 i = 1;
1814 else
1815 i = 0;
1816
1817 for (; i<len; ++i) {
1818 if (!pj_isdigit(buf[i])) {
1819 puts("Invalid input");
1820 return;
1821 }
1822 }
1823
1824 result->nb_result = my_atoi(buf);
1825
1826 if (result->nb_result >= 0 &&
1827 result->nb_result <= (int)pjsua_get_buddy_count())
1828 {
1829 return;
1830 }
1831 if (result->nb_result == -1)
1832 return;
1833
1834 puts("Invalid input");
1835 result->nb_result = NO_NB;
1836 return;
1837
1838 } else {
1839 pj_status_t status;
1840
1841 if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
1842 pjsua_perror(THIS_FILE, "Invalid URL", status);
1843 return;
1844 }
1845
1846 result->uri_result = buf;
1847 }
1848}
1849
1850/*
1851 * List the ports in conference bridge
1852 */
1853static void conf_list(void)
1854{
1855 unsigned i, count;
1856 pjsua_conf_port_id id[PJSUA_MAX_CALLS];
1857
1858 printf("Conference ports:\n");
1859
1860 count = PJ_ARRAY_SIZE(id);
1861 pjsua_enum_conf_ports(id, &count);
1862
1863 for (i=0; i<count; ++i) {
1864 char txlist[PJSUA_MAX_CALLS*4+10];
1865 unsigned j;
1866 pjsua_conf_port_info info;
1867
1868 pjsua_conf_get_port_info(id[i], &info);
1869
1870 txlist[0] = '\0';
1871 for (j=0; j<info.listener_cnt; ++j) {
1872 char s[10];
1873 pj_ansi_sprintf(s, "#%d ", info.listeners[j]);
1874 pj_ansi_strcat(txlist, s);
1875 }
1876 printf("Port #%02d[%2dKHz/%dms] %20.*s transmitting to: %s\n",
1877 info.slot_id,
1878 info.clock_rate/1000,
1879 info.samples_per_frame * 1000 / info.clock_rate,
1880 (int)info.name.slen,
1881 info.name.ptr,
1882 txlist);
1883
1884 }
1885 puts("");
1886}
1887
1888
1889/*
Benny Prijono56315612006-07-18 14:39:40 +00001890 * Send arbitrary request to remote host
1891 */
1892static void send_request(char *cstr_method, const pj_str_t *dst_uri)
1893{
1894 pj_str_t str_method;
1895 pjsip_method method;
1896 pjsip_tx_data *tdata;
1897 pjsua_acc_info acc_info;
1898 pjsip_endpoint *endpt;
1899 pj_status_t status;
1900
1901 endpt = pjsua_get_pjsip_endpt();
1902
1903 str_method = pj_str(cstr_method);
1904 pjsip_method_init_np(&method, &str_method);
1905
1906 pjsua_acc_get_info(current_acc, &acc_info);
1907
1908 status = pjsip_endpt_create_request(endpt, &method, dst_uri,
1909 &acc_info.acc_uri, dst_uri,
1910 NULL, NULL, -1, NULL, &tdata);
1911 if (status != PJ_SUCCESS) {
1912 pjsua_perror(THIS_FILE, "Unable to create request", status);
1913 return;
1914 }
1915
1916 status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL);
1917 if (status != PJ_SUCCESS) {
1918 pjsua_perror(THIS_FILE, "Unable to send request", status);
Benny Prijono56315612006-07-18 14:39:40 +00001919 return;
1920 }
1921}
1922
1923
1924/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001925 * Main "user interface" loop.
1926 */
1927void console_app_main(const pj_str_t *uri_to_call)
1928{
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00001929 char menuin[32];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001930 char buf[128];
1931 char text[128];
1932 int i, count;
1933 char *uri;
1934 pj_str_t tmp;
1935 struct input_result result;
1936 pjsua_call_info call_info;
1937 pjsua_acc_info acc_info;
1938
1939
1940 /* If user specifies URI to call, then call the URI */
1941 if (uri_to_call->slen) {
1942 pjsua_call_make_call( current_acc, uri_to_call, 0, NULL, NULL, NULL);
1943 }
1944
1945 keystroke_help();
1946
1947 for (;;) {
1948
1949 printf(">>> ");
1950 fflush(stdout);
1951
1952 fgets(menuin, sizeof(menuin), stdin);
1953
1954 switch (menuin[0]) {
1955
1956 case 'm':
1957 /* Make call! : */
1958 printf("(You currently have %d calls)\n",
1959 pjsua_call_get_count());
1960
1961 uri = NULL;
1962 ui_input_url("Make call", buf, sizeof(buf), &result);
1963 if (result.nb_result != NO_NB) {
1964
1965 if (result.nb_result == -1 || result.nb_result == 0) {
1966 puts("You can't do that with make call!");
1967 continue;
1968 } else {
1969 pjsua_buddy_info binfo;
1970 pjsua_buddy_get_info(result.nb_result-1, &binfo);
1971 uri = binfo.uri.ptr;
1972 }
1973
1974 } else if (result.uri_result) {
1975 uri = result.uri_result;
1976 }
1977
1978 tmp = pj_str(uri);
1979 pjsua_call_make_call( current_acc, &tmp, 0, NULL, NULL, NULL);
1980 break;
1981
1982 case 'M':
1983 /* Make multiple calls! : */
1984 printf("(You currently have %d calls)\n",
1985 pjsua_call_get_count());
1986
1987 if (!simple_input("Number of calls", menuin, sizeof(menuin)))
1988 continue;
1989
1990 count = my_atoi(menuin);
1991 if (count < 1)
1992 continue;
1993
1994 ui_input_url("Make call", buf, sizeof(buf), &result);
1995 if (result.nb_result != NO_NB) {
1996 pjsua_buddy_info binfo;
1997 if (result.nb_result == -1 || result.nb_result == 0) {
1998 puts("You can't do that with make call!");
1999 continue;
2000 }
2001 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2002 uri = binfo.uri.ptr;
2003 } else {
2004 uri = result.uri_result;
2005 }
2006
2007 for (i=0; i<my_atoi(menuin); ++i) {
2008 pj_status_t status;
2009
2010 tmp = pj_str(uri);
2011 status = pjsua_call_make_call(current_acc, &tmp, 0, NULL,
2012 NULL, NULL);
2013 if (status != PJ_SUCCESS)
2014 break;
2015 }
2016 break;
2017
2018 case 'i':
2019 /* Send instant messaeg */
2020
2021 /* i is for call index to send message, if any */
2022 i = -1;
2023
2024 /* Make compiler happy. */
2025 uri = NULL;
2026
2027 /* Input destination. */
2028 ui_input_url("Send IM to", buf, sizeof(buf), &result);
2029 if (result.nb_result != NO_NB) {
2030
2031 if (result.nb_result == -1) {
2032 puts("You can't send broadcast IM like that!");
2033 continue;
2034
2035 } else if (result.nb_result == 0) {
2036
2037 i = current_call;
2038
2039 } else {
2040 pjsua_buddy_info binfo;
2041 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2042 uri = binfo.uri.ptr;
2043 }
2044
2045 } else if (result.uri_result) {
2046 uri = result.uri_result;
2047 }
2048
2049
2050 /* Send typing indication. */
2051 if (i != -1)
2052 pjsua_call_send_typing_ind(i, PJ_TRUE, NULL);
2053 else {
2054 pj_str_t tmp_uri = pj_str(uri);
2055 pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL);
2056 }
2057
2058 /* Input the IM . */
2059 if (!simple_input("Message", text, sizeof(text))) {
2060 /*
2061 * Cancelled.
2062 * Send typing notification too, saying we're not typing.
2063 */
2064 if (i != -1)
2065 pjsua_call_send_typing_ind(i, PJ_FALSE, NULL);
2066 else {
2067 pj_str_t tmp_uri = pj_str(uri);
2068 pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL);
2069 }
2070 continue;
2071 }
2072
2073 tmp = pj_str(text);
2074
2075 /* Send the IM */
2076 if (i != -1)
2077 pjsua_call_send_im(i, NULL, &tmp, NULL, NULL);
2078 else {
2079 pj_str_t tmp_uri = pj_str(uri);
2080 pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL);
2081 }
2082
2083 break;
2084
2085 case 'a':
2086
2087 if (current_call != -1) {
2088 pjsua_call_get_info(current_call, &call_info);
2089 } else {
2090 /* Make compiler happy */
2091 call_info.role = PJSIP_ROLE_UAC;
2092 call_info.state = PJSIP_INV_STATE_DISCONNECTED;
2093 }
2094
2095 if (current_call == -1 ||
2096 call_info.role != PJSIP_ROLE_UAS ||
2097 call_info.state >= PJSIP_INV_STATE_CONNECTING)
2098 {
2099 puts("No pending incoming call");
2100 fflush(stdout);
2101 continue;
2102
2103 } else {
2104 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
2105 continue;
2106
2107 if (my_atoi(buf) < 100)
2108 continue;
2109
2110 /*
2111 * Must check again!
2112 * Call may have been disconnected while we're waiting for
2113 * keyboard input.
2114 */
2115 if (current_call == -1) {
2116 puts("Call has been disconnected");
2117 fflush(stdout);
2118 continue;
2119 }
2120
2121 pjsua_call_answer(current_call, my_atoi(buf), NULL, NULL);
2122 }
2123
2124 break;
2125
2126
2127 case 'h':
2128
2129 if (current_call == -1) {
2130 puts("No current call");
2131 fflush(stdout);
2132 continue;
2133
2134 } else if (menuin[1] == 'a') {
2135
2136 /* Hangup all calls */
2137 pjsua_call_hangup_all();
2138
2139 } else {
2140
2141 /* Hangup current calls */
2142 pjsua_call_hangup(current_call, 0, NULL, NULL);
2143 }
2144 break;
2145
2146 case ']':
2147 case '[':
2148 /*
2149 * Cycle next/prev dialog.
2150 */
2151 if (menuin[0] == ']') {
2152 find_next_call();
2153
2154 } else {
2155 find_prev_call();
2156 }
2157
2158 if (current_call != -1) {
2159
2160 pjsua_call_get_info(current_call, &call_info);
2161 PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s",
2162 (int)call_info.remote_info.slen,
2163 call_info.remote_info.ptr));
2164
2165 } else {
2166 PJ_LOG(3,(THIS_FILE,"No current dialog"));
2167 }
2168 break;
2169
2170
2171 case '>':
2172 case '<':
2173 if (!simple_input("Enter account ID to select", buf, sizeof(buf)))
2174 break;
2175
2176 i = my_atoi(buf);
2177 if (pjsua_acc_is_valid(i)) {
Benny Prijono21b9ad92006-08-15 13:11:22 +00002178 pjsua_acc_set_default(i);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002179 PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i));
2180 } else {
2181 PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i));
2182 }
2183 break;
2184
2185
2186 case '+':
2187 if (menuin[1] == 'b') {
2188
2189 pjsua_buddy_config buddy_cfg;
2190 pjsua_buddy_id buddy_id;
2191 pj_status_t status;
2192
2193 if (!simple_input("Enter buddy's URI:", buf, sizeof(buf)))
2194 break;
2195
2196 if (pjsua_verify_sip_url(buf) != PJ_SUCCESS) {
2197 printf("Invalid SIP URI '%s'\n", buf);
2198 break;
2199 }
2200
Benny Prijonoac623b32006-07-03 15:19:31 +00002201 pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002202
2203 buddy_cfg.uri = pj_str(buf);
2204 buddy_cfg.subscribe = PJ_TRUE;
2205
2206 status = pjsua_buddy_add(&buddy_cfg, &buddy_id);
2207 if (status == PJ_SUCCESS) {
2208 printf("New buddy '%s' added at index %d\n",
2209 buf, buddy_id+1);
2210 }
2211
2212 } else if (menuin[1] == 'a') {
2213
2214 printf("Sorry, this command is not supported yet\n");
2215
2216 } else {
2217 printf("Invalid input %s\n", menuin);
2218 }
2219 break;
2220
2221 case '-':
2222 if (menuin[1] == 'b') {
2223 if (!simple_input("Enter buddy ID to delete",buf,sizeof(buf)))
2224 break;
2225
2226 i = my_atoi(buf) - 1;
2227
2228 if (!pjsua_buddy_is_valid(i)) {
2229 printf("Invalid buddy id %d\n", i);
2230 } else {
2231 pjsua_buddy_del(i);
2232 printf("Buddy %d deleted\n", i);
2233 }
2234
2235 } else if (menuin[1] == 'a') {
2236
2237 if (!simple_input("Enter account ID to delete",buf,sizeof(buf)))
2238 break;
2239
2240 i = my_atoi(buf);
2241
2242 if (!pjsua_acc_is_valid(i)) {
2243 printf("Invalid account id %d\n", i);
2244 } else {
2245 pjsua_acc_del(i);
2246 printf("Account %d deleted\n", i);
2247 }
2248
2249 } else {
2250 printf("Invalid input %s\n", menuin);
2251 }
2252 break;
2253
2254 case 'H':
2255 /*
2256 * Hold call.
2257 */
2258 if (current_call != -1) {
2259
2260 pjsua_call_set_hold(current_call, NULL);
2261
2262 } else {
2263 PJ_LOG(3,(THIS_FILE, "No current call"));
2264 }
2265 break;
2266
2267 case 'v':
2268 /*
2269 * Send re-INVITE (to release hold, etc).
2270 */
2271 if (current_call != -1) {
2272
2273 pjsua_call_reinvite(current_call, PJ_TRUE, NULL);
2274
2275 } else {
2276 PJ_LOG(3,(THIS_FILE, "No current call"));
2277 }
2278 break;
2279
2280 case 'x':
2281 /*
2282 * Transfer call.
2283 */
2284 if (current_call == -1) {
2285
2286 PJ_LOG(3,(THIS_FILE, "No current call"));
2287
2288 } else {
2289 int call = current_call;
Benny Prijonod524e822006-09-22 12:48:18 +00002290 pjsua_msg_data msg_data;
2291 pjsip_generic_string_hdr refer_sub;
2292 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
2293 pj_str_t STR_FALSE = { "false", 5 };
Benny Prijonof7b1c392006-11-11 16:46:34 +00002294 pjsua_call_info ci;
2295
2296 pjsua_call_get_info(current_call, &ci);
2297 printf("Transfering current call [%d] %.*s\n",
2298 current_call,
2299 (int)ci.remote_info.slen, ci.remote_info.ptr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002300
2301 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
2302
2303 /* Check if call is still there. */
2304
2305 if (call != current_call) {
2306 puts("Call has been disconnected");
2307 continue;
2308 }
2309
Benny Prijonod524e822006-09-22 12:48:18 +00002310 pjsua_msg_data_init(&msg_data);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002311 if (app_config.no_refersub) {
2312 /* Add Refer-Sub: false in outgoing REFER request */
2313 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
2314 &STR_FALSE);
2315 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
2316 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002317 if (result.nb_result != NO_NB) {
2318 if (result.nb_result == -1 || result.nb_result == 0)
2319 puts("You can't do that with transfer call!");
2320 else {
2321 pjsua_buddy_info binfo;
2322 pjsua_buddy_get_info(result.nb_result-1, &binfo);
Benny Prijonod524e822006-09-22 12:48:18 +00002323 pjsua_call_xfer( current_call, &binfo.uri, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002324 }
2325
2326 } else if (result.uri_result) {
2327 pj_str_t tmp;
2328 tmp = pj_str(result.uri_result);
Benny Prijonod524e822006-09-22 12:48:18 +00002329 pjsua_call_xfer( current_call, &tmp, &msg_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002330 }
2331 }
2332 break;
2333
Benny Prijonof7b1c392006-11-11 16:46:34 +00002334 case 'X':
2335 /*
2336 * Transfer call with replaces.
2337 */
2338 if (current_call == -1) {
2339
2340 PJ_LOG(3,(THIS_FILE, "No current call"));
2341
2342 } else {
2343 int call = current_call;
2344 int dst_call;
2345 pjsua_msg_data msg_data;
2346 pjsip_generic_string_hdr refer_sub;
2347 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
2348 pj_str_t STR_FALSE = { "false", 5 };
2349 pjsua_call_id ids[PJSUA_MAX_CALLS];
2350 pjsua_call_info ci;
2351 unsigned i, count;
2352
2353 count = PJ_ARRAY_SIZE(ids);
2354 pjsua_enum_calls(ids, &count);
2355
2356 if (count <= 1) {
2357 puts("There are no other calls");
2358 continue;
2359 }
2360
2361 pjsua_call_get_info(current_call, &ci);
2362 printf("Transfer call [%d] %.*s to one of the following:\n",
2363 current_call,
2364 (int)ci.remote_info.slen, ci.remote_info.ptr);
2365
2366 for (i=0; i<count; ++i) {
2367 pjsua_call_info call_info;
2368
2369 if (ids[i] == call)
2370 continue;
2371
2372 pjsua_call_get_info(ids[i], &call_info);
2373 printf("%d %.*s [%.*s]\n",
2374 ids[i],
2375 (int)call_info.remote_info.slen,
2376 call_info.remote_info.ptr,
2377 (int)call_info.state_text.slen,
2378 call_info.state_text.ptr);
2379 }
2380
2381 if (!simple_input("Enter call number to be replaced",
2382 buf, sizeof(buf)))
2383 continue;
2384
2385 dst_call = my_atoi(buf);
2386
2387 /* Check if call is still there. */
2388
2389 if (call != current_call) {
2390 puts("Call has been disconnected");
2391 continue;
2392 }
2393
2394 /* Check that destination call is valid. */
2395 if (dst_call == call) {
2396 puts("Destination call number must not be the same "
2397 "as the call being transfered");
2398 continue;
2399 }
2400 if (dst_call >= PJSUA_MAX_CALLS) {
2401 puts("Invalid destination call number");
2402 continue;
2403 }
2404 if (!pjsua_call_is_active(dst_call)) {
2405 puts("Invalid destination call number");
2406 continue;
2407 }
2408
2409 pjsua_msg_data_init(&msg_data);
2410 if (app_config.no_refersub) {
2411 /* Add Refer-Sub: false in outgoing REFER request */
2412 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
2413 &STR_FALSE);
2414 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
2415 }
2416
2417 pjsua_call_xfer_replaces(call, dst_call, 0, &msg_data);
2418 }
2419 break;
2420
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002421 case '#':
2422 /*
2423 * Send DTMF strings.
2424 */
2425 if (current_call == -1) {
2426
2427 PJ_LOG(3,(THIS_FILE, "No current call"));
2428
2429 } else if (!pjsua_call_has_media(current_call)) {
2430
2431 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
2432
2433 } else {
2434 pj_str_t digits;
2435 int call = current_call;
2436 pj_status_t status;
2437
2438 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
2439 sizeof(buf)))
2440 {
2441 break;
2442 }
2443
2444 if (call != current_call) {
2445 puts("Call has been disconnected");
2446 continue;
2447 }
2448
2449 digits = pj_str(buf);
2450 status = pjsua_call_dial_dtmf(current_call, &digits);
2451 if (status != PJ_SUCCESS) {
2452 pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
2453 } else {
2454 puts("DTMF digits enqueued for transmission");
2455 }
2456 }
2457 break;
2458
Benny Prijono56315612006-07-18 14:39:40 +00002459 case 'S':
2460 /*
2461 * Send arbitrary request
2462 */
2463 if (pjsua_acc_get_count() == 0) {
2464 puts("Sorry, need at least one account configured");
2465 break;
2466 }
2467
2468 puts("Send arbitrary request to remote host");
2469
2470 /* Input METHOD */
2471 if (!simple_input("Request method:",text,sizeof(text)))
2472 break;
2473
2474 /* Input destination URI */
2475 uri = NULL;
2476 ui_input_url("Destination URI", buf, sizeof(buf), &result);
2477 if (result.nb_result != NO_NB) {
2478
2479 if (result.nb_result == -1 || result.nb_result == 0) {
2480 puts("Sorry you can't do that!");
2481 continue;
2482 } else {
2483 pjsua_buddy_info binfo;
2484 pjsua_buddy_get_info(result.nb_result-1, &binfo);
2485 uri = binfo.uri.ptr;
2486 }
2487
2488 } else if (result.uri_result) {
2489 uri = result.uri_result;
2490 }
2491
2492 tmp = pj_str(uri);
2493
2494 send_request(text, &tmp);
2495 break;
2496
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002497 case 's':
2498 case 'u':
2499 /*
2500 * Subscribe/unsubscribe presence.
2501 */
2502 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
2503 if (result.nb_result != NO_NB) {
2504 if (result.nb_result == -1) {
2505 int i, count;
2506 count = pjsua_get_buddy_count();
2507 for (i=0; i<count; ++i)
2508 pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
2509 } else if (result.nb_result == 0) {
2510 puts("Sorry, can only subscribe to buddy's presence, "
2511 "not from existing call");
2512 } else {
2513 pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
2514 }
2515
2516 } else if (result.uri_result) {
2517 puts("Sorry, can only subscribe to buddy's presence, "
2518 "not arbitrary URL (for now)");
2519 }
2520
2521 break;
2522
2523 case 'r':
2524 switch (menuin[1]) {
2525 case 'r':
2526 /*
2527 * Re-Register.
2528 */
2529 pjsua_acc_set_registration(current_acc, PJ_TRUE);
2530 break;
2531 case 'u':
2532 /*
2533 * Unregister
2534 */
2535 pjsua_acc_set_registration(current_acc, PJ_FALSE);
2536 break;
2537 }
2538 break;
2539
2540 case 't':
2541 pjsua_acc_get_info(current_acc, &acc_info);
2542 acc_info.online_status = !acc_info.online_status;
2543 pjsua_acc_set_online_status(current_acc, acc_info.online_status);
2544 printf("Setting %s online status to %s\n",
2545 acc_info.acc_uri.ptr,
2546 (acc_info.online_status?"online":"offline"));
2547 break;
2548
2549 case 'c':
2550 switch (menuin[1]) {
2551 case 'l':
2552 conf_list();
2553 break;
2554 case 'c':
2555 case 'd':
2556 {
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002557 char tmp[10], src_port[10], dst_port[10];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002558 pj_status_t status;
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002559 int cnt;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002560 const char *src_title, *dst_title;
2561
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002562 cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002563
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002564 if (cnt != 3) {
2565 conf_list();
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002566
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002567 src_title = (menuin[1]=='c'?
2568 "Connect src port #":
2569 "Disconnect src port #");
2570 dst_title = (menuin[1]=='c'?
2571 "To dst port #":
2572 "From dst port #");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002573
Benny Prijono2b6ec1a2006-11-26 10:49:58 +00002574 if (!simple_input(src_title, src_port, sizeof(src_port)))
2575 break;
2576
2577 if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
2578 break;
2579 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002580
2581 if (menuin[1]=='c') {
2582 status = pjsua_conf_connect(my_atoi(src_port),
2583 my_atoi(dst_port));
2584 } else {
2585 status = pjsua_conf_disconnect(my_atoi(src_port),
2586 my_atoi(dst_port));
2587 }
2588 if (status == PJ_SUCCESS) {
2589 puts("Success");
2590 } else {
2591 puts("ERROR!!");
2592 }
2593 }
2594 break;
2595 }
2596 break;
2597
2598 case 'd':
2599 if (menuin[1] == 'c') {
2600 char settings[2000];
2601 int len;
2602
2603 len = write_settings(&app_config, settings, sizeof(settings));
2604 if (len < 1)
2605 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
2606 else
2607 PJ_LOG(3,(THIS_FILE,
2608 "Dumping configuration (%d bytes):\n%s\n",
2609 len, settings));
Benny Prijono819506c2006-06-22 22:29:51 +00002610
2611 } else if (menuin[1] == 'q') {
2612
2613 if (current_call != PJSUA_INVALID_ID) {
2614 char buf[1024];
2615 pjsua_call_dump(current_call, PJ_TRUE, buf,
2616 sizeof(buf), " ");
2617 PJ_LOG(3,(THIS_FILE, "\n%s", buf));
2618 } else {
2619 PJ_LOG(3,(THIS_FILE, "No current call"));
2620 }
2621
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002622 } else {
2623 app_dump(menuin[1]=='d');
2624 }
2625 break;
2626
2627
2628 case 'f':
2629 if (simple_input("Enter output filename", buf, sizeof(buf))) {
2630 char settings[2000];
2631 int len;
2632
2633 len = write_settings(&app_config, settings, sizeof(settings));
2634 if (len < 1)
2635 PJ_LOG(3,(THIS_FILE, "Error: not enough buffer"));
2636 else {
2637 pj_oshandle_t fd;
2638 pj_status_t status;
2639
2640 status = pj_file_open(app_config.pool, buf,
2641 PJ_O_WRONLY, &fd);
2642 if (status != PJ_SUCCESS) {
2643 pjsua_perror(THIS_FILE, "Unable to open file", status);
2644 } else {
2645 pj_ssize_t size = len;
2646 pj_file_write(fd, settings, &size);
2647 pj_file_close(fd);
2648
2649 printf("Settings successfully written to '%s'\n", buf);
2650 }
2651 }
2652
2653 }
2654 break;
2655
2656
2657 case 'q':
2658 goto on_exit;
2659
2660
2661 default:
2662 if (menuin[0] != '\n' && menuin[0] != '\r') {
2663 printf("Invalid input %s", menuin);
2664 }
2665 keystroke_help();
2666 break;
2667 }
2668 }
2669
2670on_exit:
2671 ;
2672}
2673
2674
2675/*****************************************************************************
2676 * Public API
2677 */
2678
2679pj_status_t app_init(int argc, char *argv[])
2680{
Benny Prijonoe93e2872006-06-28 16:46:49 +00002681 pjsua_transport_id transport_id = -1;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002682 unsigned i;
2683 pj_status_t status;
2684
2685 /* Create pjsua */
2686 status = pjsua_create();
2687 if (status != PJ_SUCCESS)
2688 return status;
2689
2690 /* Create pool for application */
2691 app_config.pool = pjsua_pool_create("pjsua", 4000, 4000);
2692
2693 /* Initialize default config */
2694 default_config(&app_config);
2695
2696 /* Parse the arguments */
2697 status = parse_args(argc, argv, &app_config, &uri_arg);
2698 if (status != PJ_SUCCESS)
2699 return status;
2700
2701 /* Copy udp_cfg STUN config to rtp_cfg */
2702 app_config.rtp_cfg.use_stun = app_config.udp_cfg.use_stun;
2703 app_config.rtp_cfg.stun_config = app_config.udp_cfg.stun_config;
2704
2705
2706 /* Initialize application callbacks */
2707 app_config.cfg.cb.on_call_state = &on_call_state;
2708 app_config.cfg.cb.on_call_media_state = &on_call_media_state;
2709 app_config.cfg.cb.on_incoming_call = &on_incoming_call;
2710 app_config.cfg.cb.on_reg_state = &on_reg_state;
2711 app_config.cfg.cb.on_buddy_state = &on_buddy_state;
2712 app_config.cfg.cb.on_pager = &on_pager;
2713 app_config.cfg.cb.on_typing = &on_typing;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00002714 app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
Benny Prijonof7b1c392006-11-11 16:46:34 +00002715 app_config.cfg.cb.on_call_replaced = &on_call_replaced;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002716
2717 /* Initialize pjsua */
2718 status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
2719 &app_config.media_cfg);
2720 if (status != PJ_SUCCESS)
2721 return status;
2722
Benny Prijonoe909eac2006-07-27 22:04:56 +00002723#ifdef STEREO_DEMO
2724 stereo_demo();
2725#endif
2726
Benny Prijono804ff0a2006-09-14 11:17:48 +00002727 /* Initialize calls data */
2728 for (i=0; i<PJ_ARRAY_SIZE(app_config.call_data); ++i) {
2729 app_config.call_data[i].timer.id = PJSUA_INVALID_ID;
2730 app_config.call_data[i].timer.cb = &call_timeout_callback;
2731 }
2732
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002733 /* Optionally registers WAV file */
2734 if (app_config.wav_file.slen) {
Benny Prijono6fd4b8f2006-06-22 18:51:50 +00002735 status = pjsua_player_create(&app_config.wav_file, 0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002736 &app_config.wav_id);
2737 if (status != PJ_SUCCESS)
2738 goto on_error;
Benny Prijono22a300a2006-06-14 20:04:55 +00002739
2740 app_config.wav_port = pjsua_player_get_conf_port(app_config.wav_id);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002741 }
2742
Benny Prijono1ebd6142006-10-19 15:48:02 +00002743 /* Optionally create recorder file, if any. */
2744 if (app_config.rec_file.slen) {
2745 status = pjsua_recorder_create(&app_config.rec_file, 0, NULL, 0, 0,
2746 &app_config.rec_id);
2747 if (status != PJ_SUCCESS)
2748 goto on_error;
2749
2750 app_config.rec_port = pjsua_recorder_get_conf_port(app_config.rec_id);
2751 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002752
Benny Prijonoe93e2872006-06-28 16:46:49 +00002753 /* Add TCP transport unless it's disabled */
2754 if (!app_config.no_tcp) {
2755 status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
2756 &app_config.udp_cfg,
2757 &transport_id);
2758 if (status != PJ_SUCCESS)
2759 goto on_error;
2760
2761 /* Add local account */
Benny Prijono21b9ad92006-08-15 13:11:22 +00002762 pjsua_acc_add_local(transport_id, PJ_TRUE, NULL);
Benny Prijonoe93e2872006-06-28 16:46:49 +00002763 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
2764
2765 }
2766
2767
2768 /* Add UDP transport unless it's disabled. */
2769 if (!app_config.no_udp) {
2770 status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
2771 &app_config.udp_cfg,
2772 &transport_id);
2773 if (status != PJ_SUCCESS)
2774 goto on_error;
2775
2776 /* Add local account */
Benny Prijono21b9ad92006-08-15 13:11:22 +00002777 pjsua_acc_add_local(transport_id, PJ_TRUE, NULL);
Benny Prijonoe93e2872006-06-28 16:46:49 +00002778 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
2779 }
2780
2781 if (transport_id == -1) {
2782 PJ_LOG(3,(THIS_FILE, "Error: no transport is configured"));
2783 status = -1;
2784 goto on_error;
2785 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002786
2787
2788 /* Add accounts */
2789 for (i=0; i<app_config.acc_cnt; ++i) {
Benny Prijono21b9ad92006-08-15 13:11:22 +00002790 status = pjsua_acc_add(&app_config.acc_cfg[i], PJ_TRUE, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002791 if (status != PJ_SUCCESS)
2792 goto on_error;
2793 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
2794 }
2795
2796 /* Add buddies */
2797 for (i=0; i<app_config.buddy_cnt; ++i) {
2798 status = pjsua_buddy_add(&app_config.buddy_cfg[i], NULL);
2799 if (status != PJ_SUCCESS)
2800 goto on_error;
2801 }
2802
2803 /* Optionally set codec orders */
2804 for (i=0; i<app_config.codec_cnt; ++i) {
2805 pjsua_codec_set_priority(&app_config.codec_arg[i],
2806 (pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9));
2807 }
2808
2809 /* Add RTP transports */
2810 status = pjsua_media_transports_create(&app_config.rtp_cfg);
2811 if (status != PJ_SUCCESS)
2812 goto on_error;
2813
Benny Prijono22a300a2006-06-14 20:04:55 +00002814 /* Use null sound device? */
Benny Prijonoe909eac2006-07-27 22:04:56 +00002815#ifndef STEREO_DEMO
Benny Prijono22a300a2006-06-14 20:04:55 +00002816 if (app_config.null_audio) {
2817 status = pjsua_set_null_snd_dev();
2818 if (status != PJ_SUCCESS)
2819 return status;
2820 }
Benny Prijonoe909eac2006-07-27 22:04:56 +00002821#endif
Benny Prijono22a300a2006-06-14 20:04:55 +00002822
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002823 return PJ_SUCCESS;
2824
2825on_error:
2826 pjsua_destroy();
2827 return status;
2828}
2829
2830
2831pj_status_t app_main(void)
2832{
2833 pj_status_t status;
2834
2835 /* Start pjsua */
2836 status = pjsua_start();
2837 if (status != PJ_SUCCESS) {
2838 pjsua_destroy();
2839 return status;
2840 }
2841
2842 console_app_main(&uri_arg);
2843
2844 return PJ_SUCCESS;
2845}
2846
2847pj_status_t app_destroy(void)
2848{
Benny Prijonof762ee72006-12-01 11:14:37 +00002849 pj_status_t status;
2850
Benny Prijonoe909eac2006-07-27 22:04:56 +00002851#ifdef STEREO_DEMO
2852 if (app_config.snd) {
2853 pjmedia_snd_port_destroy(app_config.snd);
2854 app_config.snd = NULL;
2855 }
2856#endif
2857
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002858 if (app_config.pool) {
2859 pj_pool_release(app_config.pool);
2860 app_config.pool = NULL;
2861 }
2862
Benny Prijonof762ee72006-12-01 11:14:37 +00002863 status = pjsua_destroy();
2864
2865 pj_bzero(&app_config, sizeof(app_config));
2866
2867 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002868}
Benny Prijonoe909eac2006-07-27 22:04:56 +00002869
2870
2871#ifdef STEREO_DEMO
2872static void stereo_demo()
2873{
2874 pjmedia_port *conf, *splitter, *ch1;
2875 unsigned clock;
2876 pj_status_t status;
2877
2878 /* Disable existing sound device */
2879 conf = pjsua_set_no_snd_dev();
2880
2881 clock = app_config.media_cfg.clock_rate;
2882
2883 /* Create stereo-mono splitter/combiner */
2884 status = pjmedia_splitcomb_create(app_config.pool,
2885 clock /* clock rate */,
2886 2 /* stereo */,
2887 clock*2*10/1000/* 10ms samples * 2ch */,
2888 16 /* bits */,
2889 0 /* options */,
2890 &splitter);
2891 pj_assert(status == PJ_SUCCESS);
2892
2893 /* Connect channel0 (left channel?) to conference port slot0 */
2894 status = pjmedia_splitcomb_set_channel(splitter, 0 /* ch0 */,
2895 0 /*options*/,
2896 conf);
2897 pj_assert(status == PJ_SUCCESS);
2898
2899 /* Create reverse channel for channel1 (right channel?)... */
2900 status = pjmedia_splitcomb_create_rev_channel(app_config.pool,
2901 splitter,
2902 1 /* ch1 */,
2903 0 /* options */,
2904 &ch1);
2905 pj_assert(status == PJ_SUCCESS);
2906
2907 /* .. and register it to conference bridge (it would be slot1
2908 * if there's no other devices connected to the bridge)
2909 */
2910 status = pjsua_conf_add_port(app_config.pool, ch1, NULL);
2911 pj_assert(status == PJ_SUCCESS);
2912
2913 /* Create sound device */
2914 status = pjmedia_snd_port_create(app_config.pool, -1, -1,
2915 clock /* clock rate */,
2916 2 /* stereo */,
2917 clock*2*10/1000 /* 10 ms samples * 2ch */,
2918 16 /* bits */,
2919 0, &app_config.snd);
2920 pj_assert(status == PJ_SUCCESS);
2921
2922
2923 /* Connect the splitter to the sound device */
2924 status = pjmedia_snd_port_connect(app_config.snd, splitter);
2925 pj_assert(status == PJ_SUCCESS);
2926
2927}
2928#endif
2929