|
Home --> Documentations --> PJSIP Reference
This is the reference implementation for PJSIP and PJMEDIA. PJSUA is a console based application, designed to be simple enough to be readble, but powerful enough to demonstrate all features available in PJSIP and PJMEDIA.
This file is pjsip-apps/src/pjsua/pjsua_app.c
Screenshot on WinXP:
pjsua on WinXP
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <pjsua-lib/pjsua.h>
00021 #include "gui.h"
00022
00023
00024 #define THIS_FILE "pjsua_app.c"
00025 #define NO_LIMIT (int)0x7FFFFFFF
00026
00027
00028
00029
00030
00031
00032 #define RINGBACK_FREQ1 440
00033 #define RINGBACK_FREQ2 480
00034 #define RINGBACK_ON 2000
00035 #define RINGBACK_OFF 4000
00036 #define RINGBACK_CNT 1
00037 #define RINGBACK_INTERVAL 4000
00038
00039 #define RING_FREQ1 800
00040 #define RING_FREQ2 640
00041 #define RING_ON 200
00042 #define RING_OFF 100
00043 #define RING_CNT 3
00044 #define RING_INTERVAL 3000
00045
00046 #define MAX_AVI 4
00047
00048
00049 struct call_data
00050 {
00051 pj_timer_entry timer;
00052 pj_bool_t ringback_on;
00053 pj_bool_t ring_on;
00054 };
00055
00056
00057 struct app_vid
00058 {
00059 unsigned vid_cnt;
00060 int vcapture_dev;
00061 int vrender_dev;
00062 pj_bool_t in_auto_show;
00063 pj_bool_t out_auto_transmit;
00064 };
00065
00066
00067 static struct app_config
00068 {
00069 pjsua_config cfg;
00070 pjsua_logging_config log_cfg;
00071 pjsua_media_config media_cfg;
00072 pj_bool_t no_refersub;
00073 pj_bool_t ipv6;
00074 pj_bool_t enable_qos;
00075 pj_bool_t no_tcp;
00076 pj_bool_t no_udp;
00077 pj_bool_t use_tls;
00078 pjsua_transport_config udp_cfg;
00079 pjsua_transport_config rtp_cfg;
00080 pjsip_redirect_op redir_op;
00081
00082 unsigned acc_cnt;
00083 pjsua_acc_config acc_cfg[PJSUA_MAX_ACC];
00084
00085 unsigned buddy_cnt;
00086 pjsua_buddy_config buddy_cfg[PJSUA_MAX_BUDDIES];
00087
00088 struct call_data call_data[PJSUA_MAX_CALLS];
00089
00090 pj_pool_t *pool;
00091
00092
00093 unsigned codec_cnt;
00094 pj_str_t codec_arg[32];
00095 unsigned codec_dis_cnt;
00096 pj_str_t codec_dis[32];
00097 pj_bool_t null_audio;
00098 unsigned wav_count;
00099 pj_str_t wav_files[32];
00100 unsigned tone_count;
00101 pjmedia_tone_desc tones[32];
00102 pjsua_conf_port_id tone_slots[32];
00103 pjsua_player_id wav_id;
00104 pjsua_conf_port_id wav_port;
00105 pj_bool_t auto_play;
00106 pj_bool_t auto_play_hangup;
00107 pj_timer_entry auto_hangup_timer;
00108 pj_bool_t auto_loop;
00109 pj_bool_t auto_conf;
00110 pj_str_t rec_file;
00111 pj_bool_t auto_rec;
00112 pjsua_recorder_id rec_id;
00113 pjsua_conf_port_id rec_port;
00114 unsigned auto_answer;
00115 unsigned duration;
00116
00117 #ifdef STEREO_DEMO
00118 pjmedia_snd_port *snd;
00119 pjmedia_port *sc, *sc_ch1;
00120 pjsua_conf_port_id sc_ch1_slot;
00121 #endif
00122
00123 float mic_level,
00124 speaker_level;
00125
00126 int capture_dev, playback_dev;
00127 unsigned capture_lat, playback_lat;
00128
00129 pj_bool_t no_tones;
00130 int ringback_slot;
00131 int ringback_cnt;
00132 pjmedia_port *ringback_port;
00133 int ring_slot;
00134 int ring_cnt;
00135 pjmedia_port *ring_port;
00136
00137 struct app_vid vid;
00138 unsigned aud_cnt;
00139
00140
00141 unsigned avi_cnt;
00142 struct {
00143 pj_str_t path;
00144 pjmedia_vid_dev_index dev_id;
00145 pjsua_conf_port_id slot;
00146 } avi[MAX_AVI];
00147 pj_bool_t avi_auto_play;
00148 int avi_def_idx;
00149
00150 } app_config;
00151
00152
00153
00154 #define current_acc pjsua_acc_get_default()
00155 static pjsua_call_id current_call = PJSUA_INVALID_ID;
00156 static pj_bool_t cmd_echo;
00157 static int stdout_refresh = -1;
00158 static const char *stdout_refresh_text = "STDOUT_REFRESH";
00159 static pj_bool_t stdout_refresh_quit = PJ_FALSE;
00160 static pj_str_t uri_arg;
00161
00162 #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
00163 # define SOME_BUF_SIZE (1024 * 10)
00164 #else
00165 # define SOME_BUF_SIZE (1024 * 3)
00166 #endif
00167
00168 static char some_buf[SOME_BUF_SIZE];
00169
00170 #ifdef STEREO_DEMO
00171 static void stereo_demo();
00172 #endif
00173 static pj_status_t create_ipv6_media_transports(void);
00174 pj_status_t app_destroy(void);
00175
00176 static void ringback_start(pjsua_call_id call_id);
00177 static void ring_start(pjsua_call_id call_id);
00178 static void ring_stop(pjsua_call_id call_id);
00179
00180 pj_bool_t app_restart;
00181 pj_log_func *log_cb = NULL;
00182
00183
00184
00185
00186
00187 #if (defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
00188 PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0) || \
00189 defined(__IPHONE_4_0)
00190 void keepAliveFunction(int timeout)
00191 {
00192 int i;
00193 for (i=0; i<(int)pjsua_acc_get_count(); ++i) {
00194 if (!pjsua_acc_is_valid(i))
00195 continue;
00196
00197 if (app_config.acc_cfg[i].reg_timeout < timeout)
00198 app_config.acc_cfg[i].reg_timeout = timeout;
00199 pjsua_acc_set_registration(i, PJ_TRUE);
00200 }
00201 }
00202 #endif
00203
00204
00205 static void usage(void)
00206 {
00207 puts ("Usage:");
00208 puts (" pjsua [options] [SIP URL to call]");
00209 puts ("");
00210 puts ("General options:");
00211 puts (" --config-file=file Read the config/arguments from file.");
00212 puts (" --help Display this help screen");
00213 puts (" --version Display version info");
00214 puts ("");
00215 puts ("Logging options:");
00216 puts (" --log-file=fname Log to filename (default stderr)");
00217 puts (" --log-level=N Set log max level to N (0(none) to 6(trace)) (default=5)");
00218 puts (" --app-log-level=N Set log max level for stdout display (default=4)");
00219 puts (" --log-append Append instead of overwrite existing log file.\n");
00220 puts (" --color Use colorful logging (default yes on Win32)");
00221 puts (" --no-color Disable colorful logging");
00222 puts (" --light-bg Use dark colors for light background (default is dark bg)");
00223 puts (" --no-stderr Disable stderr");
00224
00225 puts ("");
00226 puts ("SIP Account options:");
00227 puts (" --use-ims Enable 3GPP/IMS related settings on this account");
00228 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00229 puts (" --use-srtp=N Use SRTP? 0:disabled, 1:optional, 2:mandatory,");
00230 puts (" 3:optional by duplicating media offer (def:0)");
00231 puts (" --srtp-secure=N SRTP require secure SIP? 0:no, 1:tls, 2:sips (def:1)");
00232 #endif
00233 puts (" --registrar=url Set the URL of registrar server");
00234 puts (" --id=url Set the URL of local ID (used in From header)");
00235 puts (" --contact=url Optionally override the Contact information");
00236 puts (" --contact-params=S Append the specified parameters S in Contact header");
00237 puts (" --contact-uri-params=S Append the specified parameters S in Contact URI");
00238 puts (" --proxy=url Optional URL of proxy server to visit");
00239 puts (" May be specified multiple times");
00240 printf(" --reg-timeout=SEC Optional registration interval (default %d)\n",
00241 PJSUA_REG_INTERVAL);
00242 printf(" --rereg-delay=SEC Optional auto retry registration interval (default %d)\n",
00243 PJSUA_REG_RETRY_INTERVAL);
00244 puts (" --reg-use-proxy=N Control the use of proxy settings in REGISTER.");
00245 puts (" 0=no proxy, 1=outbound only, 2=acc only, 3=all (default)");
00246 puts (" --realm=string Set realm");
00247 puts (" --username=string Set authentication username");
00248 puts (" --password=string Set authentication password");
00249 puts (" --publish Send presence PUBLISH for this account");
00250 puts (" --mwi Subscribe to message summary/waiting indication");
00251 puts (" --use-100rel Require reliable provisional response (100rel)");
00252 puts (" --use-timer=N Use SIP session timers? (default=1)");
00253 puts (" 0:inactive, 1:optional, 2:mandatory, 3:always");
00254 printf(" --timer-se=N Session timers expiration period, in secs (def:%d)\n",
00255 PJSIP_SESS_TIMER_DEF_SE);
00256 puts (" --timer-min-se=N Session timers minimum expiration period, in secs (def:90)");
00257 puts (" --outb-rid=string Set SIP outbound reg-id (default:1)");
00258 puts (" --auto-update-nat=N Where N is 0 or 1 to enable/disable SIP traversal behind");
00259 puts (" symmetric NAT (default 1)");
00260 puts (" --next-cred Add another credentials");
00261 puts ("");
00262 puts ("SIP Account Control:");
00263 puts (" --next-account Add more account");
00264 puts ("");
00265 puts ("Transport Options:");
00266 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
00267 puts (" --ipv6 Use IPv6 instead for SIP and media.");
00268 #endif
00269 puts (" --set-qos Enable QoS tagging for SIP and media.");
00270 puts (" --local-port=port Set TCP/UDP port. This implicitly enables both ");
00271 puts (" TCP and UDP transports on the specified port, unless");
00272 puts (" if TCP or UDP is disabled.");
00273 puts (" --ip-addr=IP Use the specifed address as SIP and RTP addresses.");
00274 puts (" (Hint: the IP may be the public IP of the NAT/router)");
00275 puts (" --bound-addr=IP Bind transports to this IP interface");
00276 puts (" --no-tcp Disable TCP transport.");
00277 puts (" --no-udp Disable UDP transport.");
00278 puts (" --nameserver=NS Add the specified nameserver to enable SRV resolution");
00279 puts (" This option can be specified multiple times.");
00280 puts (" --outbound=url Set the URL of global outbound proxy server");
00281 puts (" May be specified multiple times");
00282 puts (" --stun-srv=FORMAT Set STUN server host or domain. This option may be");
00283 puts (" specified more than once. FORMAT is hostdom[:PORT]");
00284
00285 #if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0)
00286 puts ("");
00287 puts ("TLS Options:");
00288 puts (" --use-tls Enable TLS transport (default=no)");
00289 puts (" --tls-ca-file Specify TLS CA file (default=none)");
00290 puts (" --tls-cert-file Specify TLS certificate file (default=none)");
00291 puts (" --tls-privkey-file Specify TLS private key file (default=none)");
00292 puts (" --tls-password Specify TLS password to private key file (default=none)");
00293 puts (" --tls-verify-server Verify server's certificate (default=no)");
00294 puts (" --tls-verify-client Verify client's certificate (default=no)");
00295 puts (" --tls-neg-timeout Specify TLS negotiation timeout (default=no)");
00296 puts (" --tls-srv-name Specify TLS server name for multihosting server");
00297 puts (" --tls-cipher Specify prefered TLS cipher (optional).");
00298 puts (" May be specified multiple times");
00299 #endif
00300
00301 puts ("");
00302 puts ("Audio Options:");
00303 puts (" --add-codec=name Manually add codec (default is to enable all)");
00304 puts (" --dis-codec=name Disable codec (can be specified multiple times)");
00305 puts (" --clock-rate=N Override conference bridge clock rate");
00306 puts (" --snd-clock-rate=N Override sound device clock rate");
00307 puts (" --stereo Audio device and conference bridge opened in stereo mode");
00308 puts (" --null-audio Use NULL audio device");
00309 puts (" --play-file=file Register WAV file in conference bridge.");
00310 puts (" This can be specified multiple times.");
00311 puts (" --play-tone=FORMAT Register tone to the conference bridge.");
00312 puts (" FORMAT is 'F1,F2,ON,OFF', where F1,F2 are");
00313 puts (" frequencies, and ON,OFF=on/off duration in msec.");
00314 puts (" This can be specified multiple times.");
00315 puts (" --auto-play Automatically play the file (to incoming calls only)");
00316 puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
00317 puts (" --auto-conf Automatically put calls in conference with others");
00318 puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");
00319 puts (" --auto-rec Automatically record conversation");
00320 puts (" --quality=N Specify media quality (0-10, default=6)");
00321 puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)");
00322 puts (" --no-vad Disable VAD/silence detector (default=vad enabled)");
00323 puts (" --ec-tail=MSEC Set echo canceller tail length (default=256)");
00324 puts (" --ec-opt=OPT Select echo canceller algorithm (0=default, ");
00325 puts (" 1=speex, 2=suppressor)");
00326 puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is 30)");
00327 puts (" --capture-dev=id Audio capture device ID (default=-1)");
00328 puts (" --playback-dev=id Audio playback device ID (default=-1)");
00329 puts (" --capture-lat=N Audio capture latency, in ms (default=100)");
00330 puts (" --playback-lat=N Audio playback latency, in ms (default=100)");
00331 puts (" --snd-auto-close=N Auto close audio device when idle for N secs (default=1)");
00332 puts (" Specify N=-1 to disable this feature.");
00333 puts (" Specify N=0 for instant close when unused.");
00334 puts (" --no-tones Disable audible tones");
00335 puts (" --jb-max-size Specify jitter buffer maximum size, in frames (default=-1)");
00336 puts (" --extra-audio Add one more audio stream");
00337
00338 #if PJSUA_HAS_VIDEO
00339 puts ("");
00340 puts ("Video Options:");
00341 puts (" --video Enable video");
00342 puts (" --vcapture-dev=id Video capture device ID (default=-1)");
00343 puts (" --vrender-dev=id Video render device ID (default=-1)");
00344 puts (" --play-avi=FILE Load this AVI as virtual capture device");
00345 puts (" --auto-play-avi Automatically play the AVI media to call");
00346 #endif
00347
00348 puts ("");
00349 puts ("Media Transport Options:");
00350 puts (" --use-ice Enable ICE (default:no)");
00351 puts (" --ice-regular Use ICE regular nomination (default: aggressive)");
00352 puts (" --ice-max-hosts=N Set maximum number of ICE host candidates");
00353 puts (" --ice-no-rtcp Disable RTCP component in ICE (default: no)");
00354 puts (" --rtp-port=N Base port to try for RTP (default=4000)");
00355 puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
00356 puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
00357 puts (" --use-turn Enable TURN relay with ICE (default:no)");
00358 puts (" --turn-srv Domain or host name of TURN server (\"NAME:PORT\" format)");
00359 puts (" --turn-tcp Use TCP connection to TURN server (default no)");
00360 puts (" --turn-user TURN username");
00361 puts (" --turn-passwd TURN password");
00362
00363 puts ("");
00364 puts ("Buddy List (can be more than one):");
00365 puts (" --add-buddy url Add the specified URL to the buddy list.");
00366 puts ("");
00367 puts ("User Agent options:");
00368 puts (" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)");
00369 puts (" --max-calls=N Maximum number of concurrent calls (default:4, max:255)");
00370 puts (" --thread-cnt=N Number of worker threads (default:1)");
00371 puts (" --duration=SEC Set maximum call duration (default:no limit)");
00372 puts (" --norefersub Suppress event subscription when transfering calls");
00373 puts (" --use-compact-form Minimize SIP message size");
00374 puts (" --no-force-lr Allow strict-route to be used (i.e. do not force lr)");
00375 puts (" --accept-redirect=N Specify how to handle call redirect (3xx) response.");
00376 puts (" 0: reject, 1: follow automatically (default), 2: ask");
00377
00378 puts ("");
00379 puts ("When URL is specified, pjsua will immediately initiate call to that URL");
00380 puts ("");
00381
00382 fflush(stdout);
00383 }
00384
00385
00386
00387 static void default_config(struct app_config *cfg)
00388 {
00389 char tmp[80];
00390 unsigned i;
00391
00392 pjsua_config_default(&cfg->cfg);
00393 pj_ansi_sprintf(tmp, "PJSUA v%s %s", pj_get_version(),
00394 pj_get_sys_info()->info.ptr);
00395 pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp);
00396
00397 pjsua_logging_config_default(&cfg->log_cfg);
00398 pjsua_media_config_default(&cfg->media_cfg);
00399 pjsua_transport_config_default(&cfg->udp_cfg);
00400 cfg->udp_cfg.port = 5060;
00401 pjsua_transport_config_default(&cfg->rtp_cfg);
00402 cfg->rtp_cfg.port = 4000;
00403 cfg->redir_op = PJSIP_REDIRECT_ACCEPT;
00404 cfg->duration = NO_LIMIT;
00405 cfg->wav_id = PJSUA_INVALID_ID;
00406 cfg->rec_id = PJSUA_INVALID_ID;
00407 cfg->wav_port = PJSUA_INVALID_ID;
00408 cfg->rec_port = PJSUA_INVALID_ID;
00409 cfg->mic_level = cfg->speaker_level = 1.0;
00410 cfg->capture_dev = PJSUA_INVALID_ID;
00411 cfg->playback_dev = PJSUA_INVALID_ID;
00412 cfg->capture_lat = PJMEDIA_SND_DEFAULT_REC_LATENCY;
00413 cfg->playback_lat = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
00414 cfg->ringback_slot = PJSUA_INVALID_ID;
00415 cfg->ring_slot = PJSUA_INVALID_ID;
00416
00417 for (i=0; i<PJ_ARRAY_SIZE(cfg->acc_cfg); ++i)
00418 pjsua_acc_config_default(&cfg->acc_cfg[i]);
00419
00420 for (i=0; i<PJ_ARRAY_SIZE(cfg->buddy_cfg); ++i)
00421 pjsua_buddy_config_default(&cfg->buddy_cfg[i]);
00422
00423 cfg->vid.vcapture_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
00424 cfg->vid.vrender_dev = PJMEDIA_VID_DEFAULT_RENDER_DEV;
00425 cfg->aud_cnt = 1;
00426
00427 cfg->avi_def_idx = PJSUA_INVALID_ID;
00428 }
00429
00430
00431
00432
00433
00434 static int read_config_file(pj_pool_t *pool, const char *filename,
00435 int *app_argc, char ***app_argv)
00436 {
00437 int i;
00438 FILE *fhnd;
00439 char line[200];
00440 int argc = 0;
00441 char **argv;
00442 enum { MAX_ARGS = 128 };
00443
00444
00445 argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
00446 argv[argc++] = *app_argv[0];
00447
00448
00449 fhnd = fopen(filename, "rt");
00450 if (!fhnd) {
00451 PJ_LOG(1,(THIS_FILE, "Unable to open config file %s", filename));
00452 fflush(stdout);
00453 return -1;
00454 }
00455
00456
00457 while (argc < MAX_ARGS && !feof(fhnd)) {
00458 char *token;
00459 char *p;
00460 const char *whitespace = " \t\r\n";
00461 char cDelimiter;
00462 int len, token_len;
00463
00464 if (fgets(line, sizeof(line), fhnd) == NULL) break;
00465
00466
00467 len = strlen(line);
00468 if (line[len-1]=='\n')
00469 line[--len] = '\0';
00470 if (line[len-1]=='\r')
00471 line[--len] = '\0';
00472
00473 if (len==0) continue;
00474
00475 for (p = line; *p != '\0' && argc < MAX_ARGS; p++) {
00476
00477 while (*p != '\0' && strchr(whitespace, *p) != NULL) p++;
00478
00479 if (*p == '\0')
00480 break;
00481
00482 if (*p == '"' || *p == '\'') {
00483 cDelimiter = *p++;
00484 token = p;
00485
00486 while (*p != '\0' && *p != cDelimiter) p++;
00487
00488 if (*p == '\0')
00489 cDelimiter = '\0';
00490
00491 } else {
00492 token = p;
00493
00494 while (*p != '\0' && strchr(whitespace, *p) == NULL) p++;
00495
00496 cDelimiter = *p;
00497 }
00498
00499 *p = '\0';
00500 token_len = p-token;
00501
00502 if (token_len > 0) {
00503 if (*token == '#')
00504 break;
00505
00506 argv[argc] = pj_pool_alloc(pool, token_len + 1);
00507 pj_memcpy(argv[argc], token, token_len + 1);
00508 ++argc;
00509 }
00510
00511 *p = cDelimiter;
00512 }
00513 }
00514
00515
00516 for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
00517 argv[argc++] = (*app_argv)[i];
00518
00519 if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
00520 PJ_LOG(1,(THIS_FILE,
00521 "Too many arguments specified in cmd line/config file"));
00522 fflush(stdout);
00523 fclose(fhnd);
00524 return -1;
00525 }
00526
00527 fclose(fhnd);
00528
00529
00530 *app_argc = argc;
00531 *app_argv = argv;
00532 return 0;
00533
00534 }
00535
00536 static int my_atoi(const char *cs)
00537 {
00538 pj_str_t s;
00539
00540 pj_cstr(&s, cs);
00541 if (cs[0] == '-') {
00542 s.ptr++, s.slen--;
00543 return 0 - (int)pj_strtoul(&s);
00544 } else if (cs[0] == '+') {
00545 s.ptr++, s.slen--;
00546 return pj_strtoul(&s);
00547 } else {
00548 return pj_strtoul(&s);
00549 }
00550 }
00551
00552
00553
00554 static pj_status_t parse_args(int argc, char *argv[],
00555 struct app_config *cfg,
00556 pj_str_t *uri_to_call)
00557 {
00558 int c;
00559 int option_index;
00560 enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
00561 OPT_LOG_APPEND, OPT_COLOR, OPT_NO_COLOR, OPT_LIGHT_BG, OPT_NO_STDERR,
00562 OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, OPT_SND_AUTO_CLOSE,
00563 OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY,
00564 OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
00565 OPT_BOUND_ADDR, OPT_CONTACT_PARAMS, OPT_CONTACT_URI_PARAMS,
00566 OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
00567 OPT_REG_RETRY_INTERVAL, OPT_REG_USE_PROXY,
00568 OPT_MWI, OPT_NAMESERVER, OPT_STUN_SRV, OPT_OUTB_RID,
00569 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
00570 OPT_AUTO_ANSWER, OPT_AUTO_PLAY, OPT_AUTO_PLAY_HANGUP, OPT_AUTO_LOOP,
00571 OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
00572 OPT_USE_ICE, OPT_ICE_REGULAR, OPT_USE_SRTP, OPT_SRTP_SECURE,
00573 OPT_USE_TURN, OPT_ICE_MAX_HOSTS, OPT_ICE_NO_RTCP, OPT_TURN_SRV,
00574 OPT_TURN_TCP, OPT_TURN_USER, OPT_TURN_PASSWD,
00575 OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC,
00576 OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
00577 OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
00578 OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL, OPT_EC_OPT,
00579 OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
00580 OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
00581 OPT_NOREFERSUB, OPT_ACCEPT_REDIRECT,
00582 OPT_USE_TLS, OPT_TLS_CA_FILE, OPT_TLS_CERT_FILE, OPT_TLS_PRIV_FILE,
00583 OPT_TLS_PASSWORD, OPT_TLS_VERIFY_SERVER, OPT_TLS_VERIFY_CLIENT,
00584 OPT_TLS_NEG_TIMEOUT, OPT_TLS_CIPHER,
00585 OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV,
00586 OPT_CAPTURE_LAT, OPT_PLAYBACK_LAT, OPT_NO_TONES, OPT_JB_MAX_SIZE,
00587 OPT_STDOUT_REFRESH, OPT_STDOUT_REFRESH_TEXT, OPT_IPV6, OPT_QOS,
00588 #ifdef _IONBF
00589 OPT_STDOUT_NO_BUF,
00590 #endif
00591 OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC,
00592 OPT_NO_FORCE_LR,
00593 OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE,
00594 OPT_VIDEO, OPT_EXTRA_AUDIO,
00595 OPT_VCAPTURE_DEV, OPT_VRENDER_DEV, OPT_PLAY_AVI, OPT_AUTO_PLAY_AVI
00596 };
00597 struct pj_getopt_option long_options[] = {
00598 { "config-file",1, 0, OPT_CONFIG_FILE},
00599 { "log-file", 1, 0, OPT_LOG_FILE},
00600 { "log-level", 1, 0, OPT_LOG_LEVEL},
00601 { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
00602 { "log-append", 0, 0, OPT_LOG_APPEND},
00603 { "color", 0, 0, OPT_COLOR},
00604 { "no-color", 0, 0, OPT_NO_COLOR},
00605 { "light-bg", 0, 0, OPT_LIGHT_BG},
00606 { "no-stderr", 0, 0, OPT_NO_STDERR},
00607 { "help", 0, 0, OPT_HELP},
00608 { "version", 0, 0, OPT_VERSION},
00609 { "clock-rate", 1, 0, OPT_CLOCK_RATE},
00610 { "snd-clock-rate", 1, 0, OPT_SND_CLOCK_RATE},
00611 { "stereo", 0, 0, OPT_STEREO},
00612 { "null-audio", 0, 0, OPT_NULL_AUDIO},
00613 { "local-port", 1, 0, OPT_LOCAL_PORT},
00614 { "ip-addr", 1, 0, OPT_IP_ADDR},
00615 { "bound-addr", 1, 0, OPT_BOUND_ADDR},
00616 { "no-tcp", 0, 0, OPT_NO_TCP},
00617 { "no-udp", 0, 0, OPT_NO_UDP},
00618 { "norefersub", 0, 0, OPT_NOREFERSUB},
00619 { "proxy", 1, 0, OPT_PROXY},
00620 { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
00621 { "registrar", 1, 0, OPT_REGISTRAR},
00622 { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
00623 { "publish", 0, 0, OPT_PUBLISH},
00624 { "mwi", 0, 0, OPT_MWI},
00625 { "use-100rel", 0, 0, OPT_100REL},
00626 { "use-ims", 0, 0, OPT_USE_IMS},
00627 { "id", 1, 0, OPT_ID},
00628 { "contact", 1, 0, OPT_CONTACT},
00629 { "contact-params",1,0, OPT_CONTACT_PARAMS},
00630 { "contact-uri-params",1,0, OPT_CONTACT_URI_PARAMS},
00631 { "auto-update-nat", 1, 0, OPT_AUTO_UPDATE_NAT},
00632 { "use-compact-form", 0, 0, OPT_USE_COMPACT_FORM},
00633 { "accept-redirect", 1, 0, OPT_ACCEPT_REDIRECT},
00634 { "no-force-lr",0, 0, OPT_NO_FORCE_LR},
00635 { "realm", 1, 0, OPT_REALM},
00636 { "username", 1, 0, OPT_USERNAME},
00637 { "password", 1, 0, OPT_PASSWORD},
00638 { "rereg-delay",1, 0, OPT_REG_RETRY_INTERVAL},
00639 { "reg-use-proxy", 1, 0, OPT_REG_USE_PROXY},
00640 { "nameserver", 1, 0, OPT_NAMESERVER},
00641 { "stun-srv", 1, 0, OPT_STUN_SRV},
00642 { "add-buddy", 1, 0, OPT_ADD_BUDDY},
00643 { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
00644 { "no-presence", 0, 0, OPT_NO_PRESENCE},
00645 { "auto-answer",1, 0, OPT_AUTO_ANSWER},
00646 { "auto-play", 0, 0, OPT_AUTO_PLAY},
00647 { "auto-play-hangup",0, 0, OPT_AUTO_PLAY_HANGUP},
00648 { "auto-rec", 0, 0, OPT_AUTO_REC},
00649 { "auto-loop", 0, 0, OPT_AUTO_LOOP},
00650 { "auto-conf", 0, 0, OPT_AUTO_CONF},
00651 { "play-file", 1, 0, OPT_PLAY_FILE},
00652 { "play-tone", 1, 0, OPT_PLAY_TONE},
00653 { "rec-file", 1, 0, OPT_REC_FILE},
00654 { "rtp-port", 1, 0, OPT_RTP_PORT},
00655
00656 { "use-ice", 0, 0, OPT_USE_ICE},
00657 { "ice-regular",0, 0, OPT_ICE_REGULAR},
00658 { "use-turn", 0, 0, OPT_USE_TURN},
00659 { "ice-max-hosts",1, 0, OPT_ICE_MAX_HOSTS},
00660 { "ice-no-rtcp",0, 0, OPT_ICE_NO_RTCP},
00661 { "turn-srv", 1, 0, OPT_TURN_SRV},
00662 { "turn-tcp", 0, 0, OPT_TURN_TCP},
00663 { "turn-user", 1, 0, OPT_TURN_USER},
00664 { "turn-passwd",1, 0, OPT_TURN_PASSWD},
00665
00666 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00667 { "use-srtp", 1, 0, OPT_USE_SRTP},
00668 { "srtp-secure",1, 0, OPT_SRTP_SECURE},
00669 #endif
00670 { "add-codec", 1, 0, OPT_ADD_CODEC},
00671 { "dis-codec", 1, 0, OPT_DIS_CODEC},
00672 { "complexity", 1, 0, OPT_COMPLEXITY},
00673 { "quality", 1, 0, OPT_QUALITY},
00674 { "ptime", 1, 0, OPT_PTIME},
00675 { "no-vad", 0, 0, OPT_NO_VAD},
00676 { "ec-tail", 1, 0, OPT_EC_TAIL},
00677 { "ec-opt", 1, 0, OPT_EC_OPT},
00678 { "ilbc-mode", 1, 0, OPT_ILBC_MODE},
00679 { "rx-drop-pct",1, 0, OPT_RX_DROP_PCT},
00680 { "tx-drop-pct",1, 0, OPT_TX_DROP_PCT},
00681 { "next-account",0,0, OPT_NEXT_ACCOUNT},
00682 { "next-cred", 0, 0, OPT_NEXT_CRED},
00683 { "max-calls", 1, 0, OPT_MAX_CALLS},
00684 { "duration", 1, 0, OPT_DURATION},
00685 { "thread-cnt", 1, 0, OPT_THREAD_CNT},
00686 #if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0)
00687 { "use-tls", 0, 0, OPT_USE_TLS},
00688 { "tls-ca-file",1, 0, OPT_TLS_CA_FILE},
00689 { "tls-cert-file",1,0, OPT_TLS_CERT_FILE},
00690 { "tls-privkey-file",1,0, OPT_TLS_PRIV_FILE},
00691 { "tls-password",1,0, OPT_TLS_PASSWORD},
00692 { "tls-verify-server", 0, 0, OPT_TLS_VERIFY_SERVER},
00693 { "tls-verify-client", 0, 0, OPT_TLS_VERIFY_CLIENT},
00694 { "tls-neg-timeout", 1, 0, OPT_TLS_NEG_TIMEOUT},
00695 { "tls-cipher", 1, 0, OPT_TLS_CIPHER},
00696 #endif
00697 { "capture-dev", 1, 0, OPT_CAPTURE_DEV},
00698 { "playback-dev", 1, 0, OPT_PLAYBACK_DEV},
00699 { "capture-lat", 1, 0, OPT_CAPTURE_LAT},
00700 { "playback-lat", 1, 0, OPT_PLAYBACK_LAT},
00701 { "stdout-refresh", 1, 0, OPT_STDOUT_REFRESH},
00702 { "stdout-refresh-text", 1, 0, OPT_STDOUT_REFRESH_TEXT},
00703 #ifdef _IONBF
00704 { "stdout-no-buf", 0, 0, OPT_STDOUT_NO_BUF },
00705 #endif
00706 { "snd-auto-close", 1, 0, OPT_SND_AUTO_CLOSE},
00707 { "no-tones", 0, 0, OPT_NO_TONES},
00708 { "jb-max-size", 1, 0, OPT_JB_MAX_SIZE},
00709 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
00710 { "ipv6", 0, 0, OPT_IPV6},
00711 #endif
00712 { "set-qos", 0, 0, OPT_QOS},
00713 { "use-timer", 1, 0, OPT_TIMER},
00714 { "timer-se", 1, 0, OPT_TIMER_SE},
00715 { "timer-min-se", 1, 0, OPT_TIMER_MIN_SE},
00716 { "outb-rid", 1, 0, OPT_OUTB_RID},
00717 { "video", 0, 0, OPT_VIDEO},
00718 { "extra-audio",0, 0, OPT_EXTRA_AUDIO},
00719 { "vcapture-dev", 1, 0, OPT_VCAPTURE_DEV},
00720 { "vrender-dev", 1, 0, OPT_VRENDER_DEV},
00721 { "play-avi", 1, 0, OPT_PLAY_AVI},
00722 { "auto-play-avi", 0, 0, OPT_AUTO_PLAY_AVI},
00723 { NULL, 0, 0, 0}
00724 };
00725 pj_status_t status;
00726 pjsua_acc_config *cur_acc;
00727 char *config_file = NULL;
00728 unsigned i;
00729
00730
00731 pj_optind = 0;
00732 while ((c=pj_getopt_long(argc, argv, "", long_options,
00733 &option_index)) != -1)
00734 {
00735 switch (c) {
00736 case OPT_CONFIG_FILE:
00737 config_file = pj_optarg;
00738 break;
00739 }
00740 if (config_file)
00741 break;
00742 }
00743
00744 if (config_file) {
00745 status = read_config_file(app_config.pool, config_file, &argc, &argv);
00746 if (status != 0)
00747 return status;
00748 }
00749
00750 cfg->acc_cnt = 0;
00751 cur_acc = &cfg->acc_cfg[0];
00752
00753
00754
00755
00756
00757 pj_optind = 0;
00758 while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
00759 pj_str_t tmp;
00760 long lval;
00761
00762 switch (c) {
00763
00764 case OPT_CONFIG_FILE:
00765
00766 break;
00767
00768 case OPT_LOG_FILE:
00769 cfg->log_cfg.log_filename = pj_str(pj_optarg);
00770 break;
00771
00772 case OPT_LOG_LEVEL:
00773 c = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00774 if (c < 0 || c > 6) {
00775 PJ_LOG(1,(THIS_FILE,
00776 "Error: expecting integer value 0-6 "
00777 "for --log-level"));
00778 return PJ_EINVAL;
00779 }
00780 cfg->log_cfg.level = c;
00781 pj_log_set_level( c );
00782 break;
00783
00784 case OPT_APP_LOG_LEVEL:
00785 cfg->log_cfg.console_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00786 if (cfg->log_cfg.console_level < 0 || cfg->log_cfg.console_level > 6) {
00787 PJ_LOG(1,(THIS_FILE,
00788 "Error: expecting integer value 0-6 "
00789 "for --app-log-level"));
00790 return PJ_EINVAL;
00791 }
00792 break;
00793
00794 case OPT_LOG_APPEND:
00795 cfg->log_cfg.log_file_flags |= PJ_O_APPEND;
00796 break;
00797
00798 case OPT_COLOR:
00799 cfg->log_cfg.decor |= PJ_LOG_HAS_COLOR;
00800 break;
00801
00802 case OPT_NO_COLOR:
00803 cfg->log_cfg.decor &= ~PJ_LOG_HAS_COLOR;
00804 break;
00805
00806 case OPT_LIGHT_BG:
00807 pj_log_set_color(1, PJ_TERM_COLOR_R);
00808 pj_log_set_color(2, PJ_TERM_COLOR_R | PJ_TERM_COLOR_G);
00809 pj_log_set_color(3, PJ_TERM_COLOR_B | PJ_TERM_COLOR_G);
00810 pj_log_set_color(4, 0);
00811 pj_log_set_color(5, 0);
00812 pj_log_set_color(77, 0);
00813 break;
00814
00815 case OPT_NO_STDERR:
00816 freopen("/dev/null", "w", stderr);
00817 break;
00818
00819 case OPT_HELP:
00820 usage();
00821 return PJ_EINVAL;
00822
00823 case OPT_VERSION:
00824 pj_dump_config();
00825 return PJ_EINVAL;
00826
00827 case OPT_NULL_AUDIO:
00828 cfg->null_audio = PJ_TRUE;
00829 break;
00830
00831 case OPT_CLOCK_RATE:
00832 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00833 if (lval < 8000 || lval > 192000) {
00834 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
00835 "8000-192000 for conference clock rate"));
00836 return PJ_EINVAL;
00837 }
00838 cfg->media_cfg.clock_rate = lval;
00839 break;
00840
00841 case OPT_SND_CLOCK_RATE:
00842 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00843 if (lval < 8000 || lval > 192000) {
00844 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
00845 "8000-192000 for sound device clock rate"));
00846 return PJ_EINVAL;
00847 }
00848 cfg->media_cfg.snd_clock_rate = lval;
00849 break;
00850
00851 case OPT_STEREO:
00852 cfg->media_cfg.channel_count = 2;
00853 break;
00854
00855 case OPT_LOCAL_PORT:
00856 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00857 if (lval < 0 || lval > 65535) {
00858 PJ_LOG(1,(THIS_FILE,
00859 "Error: expecting integer value for "
00860 "--local-port"));
00861 return PJ_EINVAL;
00862 }
00863 cfg->udp_cfg.port = (pj_uint16_t)lval;
00864 break;
00865
00866 case OPT_IP_ADDR:
00867 cfg->udp_cfg.public_addr = pj_str(pj_optarg);
00868 cfg->rtp_cfg.public_addr = pj_str(pj_optarg);
00869 break;
00870
00871 case OPT_BOUND_ADDR:
00872 cfg->udp_cfg.bound_addr = pj_str(pj_optarg);
00873 cfg->rtp_cfg.bound_addr = pj_str(pj_optarg);
00874 break;
00875
00876 case OPT_NO_UDP:
00877 if (cfg->no_tcp) {
00878 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
00879 return PJ_EINVAL;
00880 }
00881
00882 cfg->no_udp = PJ_TRUE;
00883 break;
00884
00885 case OPT_NOREFERSUB:
00886 cfg->no_refersub = PJ_TRUE;
00887 break;
00888
00889 case OPT_NO_TCP:
00890 if (cfg->no_udp) {
00891 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
00892 return PJ_EINVAL;
00893 }
00894
00895 cfg->no_tcp = PJ_TRUE;
00896 break;
00897
00898 case OPT_PROXY:
00899 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00900 PJ_LOG(1,(THIS_FILE,
00901 "Error: invalid SIP URL '%s' "
00902 "in proxy argument", pj_optarg));
00903 return PJ_EINVAL;
00904 }
00905 cur_acc->proxy[cur_acc->proxy_cnt++] = pj_str(pj_optarg);
00906 break;
00907
00908 case OPT_OUTBOUND_PROXY:
00909 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00910 PJ_LOG(1,(THIS_FILE,
00911 "Error: invalid SIP URL '%s' "
00912 "in outbound proxy argument", pj_optarg));
00913 return PJ_EINVAL;
00914 }
00915 cfg->cfg.outbound_proxy[cfg->cfg.outbound_proxy_cnt++] = pj_str(pj_optarg);
00916 break;
00917
00918 case OPT_REGISTRAR:
00919 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00920 PJ_LOG(1,(THIS_FILE,
00921 "Error: invalid SIP URL '%s' in "
00922 "registrar argument", pj_optarg));
00923 return PJ_EINVAL;
00924 }
00925 cur_acc->reg_uri = pj_str(pj_optarg);
00926 break;
00927
00928 case OPT_REG_TIMEOUT:
00929 cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,pj_optarg));
00930 if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
00931 PJ_LOG(1,(THIS_FILE,
00932 "Error: invalid value for --reg-timeout "
00933 "(expecting 1-3600)"));
00934 return PJ_EINVAL;
00935 }
00936 break;
00937
00938 case OPT_PUBLISH:
00939 cur_acc->publish_enabled = PJ_TRUE;
00940 break;
00941
00942 case OPT_MWI:
00943 cur_acc->mwi_enabled = PJ_TRUE;
00944 break;
00945
00946 case OPT_100REL:
00947 cur_acc->require_100rel = PJSUA_100REL_MANDATORY;
00948 cfg->cfg.require_100rel = PJSUA_100REL_MANDATORY;
00949 break;
00950
00951 case OPT_TIMER:
00952 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00953 if (lval < 0 || lval > 3) {
00954 PJ_LOG(1,(THIS_FILE,
00955 "Error: expecting integer value 0-3 for --use-timer"));
00956 return PJ_EINVAL;
00957 }
00958 cur_acc->use_timer = lval;
00959 cfg->cfg.use_timer = lval;
00960 break;
00961
00962 case OPT_TIMER_SE:
00963 cur_acc->timer_setting.sess_expires = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00964 if (cur_acc->timer_setting.sess_expires < 90) {
00965 PJ_LOG(1,(THIS_FILE,
00966 "Error: invalid value for --timer-se "
00967 "(expecting higher than 90)"));
00968 return PJ_EINVAL;
00969 }
00970 cfg->cfg.timer_setting.sess_expires = cur_acc->timer_setting.sess_expires;
00971 break;
00972
00973 case OPT_TIMER_MIN_SE:
00974 cur_acc->timer_setting.min_se = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00975 if (cur_acc->timer_setting.min_se < 90) {
00976 PJ_LOG(1,(THIS_FILE,
00977 "Error: invalid value for --timer-min-se "
00978 "(expecting higher than 90)"));
00979 return PJ_EINVAL;
00980 }
00981 cfg->cfg.timer_setting.min_se = cur_acc->timer_setting.min_se;
00982 break;
00983
00984 case OPT_OUTB_RID:
00985 cur_acc->rfc5626_reg_id = pj_str(pj_optarg);
00986 break;
00987
00988 case OPT_USE_IMS:
00989 cur_acc->auth_pref.initial_auth = PJ_TRUE;
00990 break;
00991
00992 case OPT_ID:
00993 if (pjsua_verify_url(pj_optarg) != 0) {
00994 PJ_LOG(1,(THIS_FILE,
00995 "Error: invalid SIP URL '%s' "
00996 "in local id argument", pj_optarg));
00997 return PJ_EINVAL;
00998 }
00999 cur_acc->id = pj_str(pj_optarg);
01000 break;
01001
01002 case OPT_CONTACT:
01003 if (pjsua_verify_sip_url(pj_optarg) != 0) {
01004 PJ_LOG(1,(THIS_FILE,
01005 "Error: invalid SIP URL '%s' "
01006 "in contact argument", pj_optarg));
01007 return PJ_EINVAL;
01008 }
01009 cur_acc->force_contact = pj_str(pj_optarg);
01010 break;
01011
01012 case OPT_CONTACT_PARAMS:
01013 cur_acc->contact_params = pj_str(pj_optarg);
01014 break;
01015
01016 case OPT_CONTACT_URI_PARAMS:
01017 cur_acc->contact_uri_params = pj_str(pj_optarg);
01018 break;
01019
01020 case OPT_AUTO_UPDATE_NAT:
01021 cur_acc->allow_contact_rewrite = pj_strtoul(pj_cstr(&tmp, pj_optarg));
01022 break;
01023
01024 case OPT_USE_COMPACT_FORM:
01025
01026 {
01027 extern pj_bool_t pjsip_use_compact_form;
01028 extern pj_bool_t pjsip_include_allow_hdr_in_dlg;
01029 extern pj_bool_t pjmedia_add_rtpmap_for_static_pt;
01030
01031 pjsip_use_compact_form = PJ_TRUE;
01032
01033 pjsip_include_allow_hdr_in_dlg = PJ_FALSE;
01034
01035 pjmedia_add_rtpmap_for_static_pt = PJ_FALSE;
01036 }
01037 break;
01038
01039 case OPT_ACCEPT_REDIRECT:
01040 cfg->redir_op = my_atoi(pj_optarg);
01041 if (cfg->redir_op<0 || cfg->redir_op>PJSIP_REDIRECT_STOP) {
01042 PJ_LOG(1,(THIS_FILE,
01043 "Error: accept-redirect value '%s' ", pj_optarg));
01044 return PJ_EINVAL;
01045 }
01046 break;
01047
01048 case OPT_NO_FORCE_LR:
01049 cfg->cfg.force_lr = PJ_FALSE;
01050 break;
01051
01052 case OPT_NEXT_ACCOUNT:
01053 cfg->acc_cnt++;
01054 cur_acc = &cfg->acc_cfg[cfg->acc_cnt];
01055 break;
01056
01057 case OPT_USERNAME:
01058 cur_acc->cred_info[cur_acc->cred_count].username = pj_str(pj_optarg);
01059 cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("Digest");
01060 break;
01061
01062 case OPT_REALM:
01063 cur_acc->cred_info[cur_acc->cred_count].realm = pj_str(pj_optarg);
01064 break;
01065
01066 case OPT_PASSWORD:
01067 cur_acc->cred_info[cur_acc->cred_count].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
01068 cur_acc->cred_info[cur_acc->cred_count].data = pj_str(pj_optarg);
01069 #if PJSIP_HAS_DIGEST_AKA_AUTH
01070 cur_acc->cred_info[cur_acc->cred_count].data_type |= PJSIP_CRED_DATA_EXT_AKA;
01071 cur_acc->cred_info[cur_acc->cred_count].ext.aka.k = pj_str(pj_optarg);
01072 cur_acc->cred_info[cur_acc->cred_count].ext.aka.cb = &pjsip_auth_create_aka_response;
01073 #endif
01074 break;
01075
01076 case OPT_REG_RETRY_INTERVAL:
01077 cur_acc->reg_retry_interval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
01078 break;
01079
01080 case OPT_REG_USE_PROXY:
01081 cur_acc->reg_use_proxy = (unsigned)pj_strtoul(pj_cstr(&tmp, pj_optarg));
01082 if (cur_acc->reg_use_proxy > 3) {
01083 PJ_LOG(1,(THIS_FILE, "Error: invalid --reg-use-proxy value '%s'",
01084 pj_optarg));
01085 return PJ_EINVAL;
01086 }
01087 break;
01088
01089 case OPT_NEXT_CRED:
01090 cur_acc->cred_count++;
01091 break;
01092
01093 case OPT_NAMESERVER:
01094 cfg->cfg.nameserver[cfg->cfg.nameserver_count++] = pj_str(pj_optarg);
01095 if (cfg->cfg.nameserver_count > PJ_ARRAY_SIZE(cfg->cfg.nameserver)) {
01096 PJ_LOG(1,(THIS_FILE, "Error: too many nameservers"));
01097 return PJ_ETOOMANY;
01098 }
01099 break;
01100
01101 case OPT_STUN_SRV:
01102 cfg->cfg.stun_host = pj_str(pj_optarg);
01103 if (cfg->cfg.stun_srv_cnt==PJ_ARRAY_SIZE(cfg->cfg.stun_srv)) {
01104 PJ_LOG(1,(THIS_FILE, "Error: too many STUN servers"));
01105 return PJ_ETOOMANY;
01106 }
01107 cfg->cfg.stun_srv[cfg->cfg.stun_srv_cnt++] = pj_str(pj_optarg);
01108 break;
01109
01110 case OPT_ADD_BUDDY:
01111 if (pjsua_verify_url(pj_optarg) != 0) {
01112 PJ_LOG(1,(THIS_FILE,
01113 "Error: invalid URL '%s' in "
01114 "--add-buddy option", pj_optarg));
01115 return -1;
01116 }
01117 if (cfg->buddy_cnt == PJ_ARRAY_SIZE(cfg->buddy_cfg)) {
01118 PJ_LOG(1,(THIS_FILE,
01119 "Error: too many buddies in buddy list."));
01120 return -1;
01121 }
01122 cfg->buddy_cfg[cfg->buddy_cnt].uri = pj_str(pj_optarg);
01123 cfg->buddy_cnt++;
01124 break;
01125
01126 case OPT_AUTO_PLAY:
01127 cfg->auto_play = 1;
01128 break;
01129
01130 case OPT_AUTO_PLAY_HANGUP:
01131 cfg->auto_play_hangup = 1;
01132 break;
01133
01134 case OPT_AUTO_REC:
01135 cfg->auto_rec = 1;
01136 break;
01137
01138 case OPT_AUTO_LOOP:
01139 cfg->auto_loop = 1;
01140 break;
01141
01142 case OPT_AUTO_CONF:
01143 cfg->auto_conf = 1;
01144 break;
01145
01146 case OPT_PLAY_FILE:
01147 cfg->wav_files[cfg->wav_count++] = pj_str(pj_optarg);
01148 break;
01149
01150 case OPT_PLAY_TONE:
01151 {
01152 int f1, f2, on, off;
01153 int n;
01154
01155 n = sscanf(pj_optarg, "%d,%d,%d,%d", &f1, &f2, &on, &off);
01156 if (n != 4) {
01157 puts("Expecting f1,f2,on,off in --play-tone");
01158 return -1;
01159 }
01160
01161 cfg->tones[cfg->tone_count].freq1 = (short)f1;
01162 cfg->tones[cfg->tone_count].freq2 = (short)f2;
01163 cfg->tones[cfg->tone_count].on_msec = (short)on;
01164 cfg->tones[cfg->tone_count].off_msec = (short)off;
01165 ++cfg->tone_count;
01166 }
01167 break;
01168
01169 case OPT_REC_FILE:
01170 cfg->rec_file = pj_str(pj_optarg);
01171 break;
01172
01173 case OPT_USE_ICE:
01174 cfg->media_cfg.enable_ice = PJ_TRUE;
01175 break;
01176
01177 case OPT_ICE_REGULAR:
01178 cfg->media_cfg.ice_opt.aggressive = PJ_FALSE;
01179 break;
01180
01181 case OPT_USE_TURN:
01182 cfg->media_cfg.enable_turn = PJ_TRUE;
01183 break;
01184
01185 case OPT_ICE_MAX_HOSTS:
01186 cfg->media_cfg.ice_max_host_cands = my_atoi(pj_optarg);
01187 break;
01188
01189 case OPT_ICE_NO_RTCP:
01190 cfg->media_cfg.ice_no_rtcp = PJ_TRUE;
01191 break;
01192
01193 case OPT_TURN_SRV:
01194 cfg->media_cfg.turn_server = pj_str(pj_optarg);
01195 break;
01196
01197 case OPT_TURN_TCP:
01198 cfg->media_cfg.turn_conn_type = PJ_TURN_TP_TCP;
01199 break;
01200
01201 case OPT_TURN_USER:
01202 cfg->media_cfg.turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
01203 cfg->media_cfg.turn_auth_cred.data.static_cred.realm = pj_str("*");
01204 cfg->media_cfg.turn_auth_cred.data.static_cred.username = pj_str(pj_optarg);
01205 break;
01206
01207 case OPT_TURN_PASSWD:
01208 cfg->media_cfg.turn_auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
01209 cfg->media_cfg.turn_auth_cred.data.static_cred.data = pj_str(pj_optarg);
01210 break;
01211
01212 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
01213 case OPT_USE_SRTP:
01214 app_config.cfg.use_srtp = my_atoi(pj_optarg);
01215 if (!pj_isdigit(*pj_optarg) || app_config.cfg.use_srtp > 3) {
01216 PJ_LOG(1,(THIS_FILE, "Invalid value for --use-srtp option"));
01217 return -1;
01218 }
01219 if ((int)app_config.cfg.use_srtp == 3) {
01220
01221 app_config.cfg.use_srtp = PJMEDIA_SRTP_OPTIONAL;
01222 app_config.cfg.srtp_optional_dup_offer = PJ_TRUE;
01223 cur_acc->srtp_optional_dup_offer = PJ_TRUE;
01224 }
01225 cur_acc->use_srtp = app_config.cfg.use_srtp;
01226 break;
01227 case OPT_SRTP_SECURE:
01228 app_config.cfg.srtp_secure_signaling = my_atoi(pj_optarg);
01229 if (!pj_isdigit(*pj_optarg) ||
01230 app_config.cfg.srtp_secure_signaling > 2)
01231 {
01232 PJ_LOG(1,(THIS_FILE, "Invalid value for --srtp-secure option"));
01233 return -1;
01234 }
01235 cur_acc->srtp_secure_signaling = app_config.cfg.srtp_secure_signaling;
01236 break;
01237 #endif
01238
01239 case OPT_RTP_PORT:
01240 cfg->rtp_cfg.port = my_atoi(pj_optarg);
01241 if (cfg->rtp_cfg.port == 0) {
01242 enum { START_PORT=4000 };
01243 unsigned range;
01244
01245 range = (65535-START_PORT-PJSUA_MAX_CALLS*2);
01246 cfg->rtp_cfg.port = START_PORT +
01247 ((pj_rand() % range) & 0xFFFE);
01248 }
01249
01250 if (cfg->rtp_cfg.port < 1 || cfg->rtp_cfg.port > 65535) {
01251 PJ_LOG(1,(THIS_FILE,
01252 "Error: rtp-port argument value "
01253 "(expecting 1-65535"));
01254 return -1;
01255 }
01256 break;
01257
01258 case OPT_DIS_CODEC:
01259 cfg->codec_dis[cfg->codec_dis_cnt++] = pj_str(pj_optarg);
01260 break;
01261
01262 case OPT_ADD_CODEC:
01263 cfg->codec_arg[cfg->codec_cnt++] = pj_str(pj_optarg);
01264 break;
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278 case OPT_DURATION:
01279 cfg->duration = my_atoi(pj_optarg);
01280 break;
01281
01282 case OPT_THREAD_CNT:
01283 cfg->cfg.thread_cnt = my_atoi(pj_optarg);
01284 if (cfg->cfg.thread_cnt > 128) {
01285 PJ_LOG(1,(THIS_FILE,
01286 "Error: invalid --thread-cnt option"));
01287 return -1;
01288 }
01289 break;
01290
01291 case OPT_PTIME:
01292 cfg->media_cfg.ptime = my_atoi(pj_optarg);
01293 if (cfg->media_cfg.ptime < 10 || cfg->media_cfg.ptime > 1000) {
01294 PJ_LOG(1,(THIS_FILE,
01295 "Error: invalid --ptime option"));
01296 return -1;
01297 }
01298 break;
01299
01300 case OPT_NO_VAD:
01301 cfg->media_cfg.no_vad = PJ_TRUE;
01302 break;
01303
01304 case OPT_EC_TAIL:
01305 cfg->media_cfg.ec_tail_len = my_atoi(pj_optarg);
01306 if (cfg->media_cfg.ec_tail_len > 1000) {
01307 PJ_LOG(1,(THIS_FILE, "I think the ec-tail length setting "
01308 "is too big"));
01309 return -1;
01310 }
01311 break;
01312
01313 case OPT_EC_OPT:
01314 cfg->media_cfg.ec_options = my_atoi(pj_optarg);
01315 break;
01316
01317 case OPT_QUALITY:
01318 cfg->media_cfg.quality = my_atoi(pj_optarg);
01319 if (cfg->media_cfg.quality < 0 || cfg->media_cfg.quality > 10) {
01320 PJ_LOG(1,(THIS_FILE,
01321 "Error: invalid --quality (expecting 0-10"));
01322 return -1;
01323 }
01324 break;
01325
01326 case OPT_ILBC_MODE:
01327 cfg->media_cfg.ilbc_mode = my_atoi(pj_optarg);
01328 if (cfg->media_cfg.ilbc_mode!=20 && cfg->media_cfg.ilbc_mode!=30) {
01329 PJ_LOG(1,(THIS_FILE,
01330 "Error: invalid --ilbc-mode (expecting 20 or 30"));
01331 return -1;
01332 }
01333 break;
01334
01335 case OPT_RX_DROP_PCT:
01336 cfg->media_cfg.rx_drop_pct = my_atoi(pj_optarg);
01337 if (cfg->media_cfg.rx_drop_pct > 100) {
01338 PJ_LOG(1,(THIS_FILE,
01339 "Error: invalid --rx-drop-pct (expecting <= 100"));
01340 return -1;
01341 }
01342 break;
01343
01344 case OPT_TX_DROP_PCT:
01345 cfg->media_cfg.tx_drop_pct = my_atoi(pj_optarg);
01346 if (cfg->media_cfg.tx_drop_pct > 100) {
01347 PJ_LOG(1,(THIS_FILE,
01348 "Error: invalid --tx-drop-pct (expecting <= 100"));
01349 return -1;
01350 }
01351 break;
01352
01353 case OPT_AUTO_ANSWER:
01354 cfg->auto_answer = my_atoi(pj_optarg);
01355 if (cfg->auto_answer < 100 || cfg->auto_answer > 699) {
01356 PJ_LOG(1,(THIS_FILE,
01357 "Error: invalid code in --auto-answer "
01358 "(expecting 100-699"));
01359 return -1;
01360 }
01361 break;
01362
01363 case OPT_MAX_CALLS:
01364 cfg->cfg.max_calls = my_atoi(pj_optarg);
01365 if (cfg->cfg.max_calls < 1 || cfg->cfg.max_calls > PJSUA_MAX_CALLS) {
01366 PJ_LOG(1,(THIS_FILE,"Error: maximum call setting exceeds "
01367 "compile time limit (PJSUA_MAX_CALLS=%d)",
01368 PJSUA_MAX_CALLS));
01369 return -1;
01370 }
01371 break;
01372
01373 #if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0)
01374 case OPT_USE_TLS:
01375 cfg->use_tls = PJ_TRUE;
01376 break;
01377
01378 case OPT_TLS_CA_FILE:
01379 cfg->udp_cfg.tls_setting.ca_list_file = pj_str(pj_optarg);
01380 break;
01381
01382 case OPT_TLS_CERT_FILE:
01383 cfg->udp_cfg.tls_setting.cert_file = pj_str(pj_optarg);
01384 break;
01385
01386 case OPT_TLS_PRIV_FILE:
01387 cfg->udp_cfg.tls_setting.privkey_file = pj_str(pj_optarg);
01388 break;
01389
01390 case OPT_TLS_PASSWORD:
01391 cfg->udp_cfg.tls_setting.password = pj_str(pj_optarg);
01392 break;
01393
01394 case OPT_TLS_VERIFY_SERVER:
01395 cfg->udp_cfg.tls_setting.verify_server = PJ_TRUE;
01396 break;
01397
01398 case OPT_TLS_VERIFY_CLIENT:
01399 cfg->udp_cfg.tls_setting.verify_client = PJ_TRUE;
01400 cfg->udp_cfg.tls_setting.require_client_cert = PJ_TRUE;
01401 break;
01402
01403 case OPT_TLS_NEG_TIMEOUT:
01404 cfg->udp_cfg.tls_setting.timeout.sec = atoi(pj_optarg);
01405 break;
01406
01407 case OPT_TLS_CIPHER:
01408 {
01409 pj_ssl_cipher cipher;
01410
01411 if (pj_ansi_strnicmp(pj_optarg, "0x", 2) == 0) {
01412 pj_str_t cipher_st = pj_str(pj_optarg + 2);
01413 cipher = pj_strtoul2(&cipher_st, NULL, 16);
01414 } else {
01415 cipher = atoi(pj_optarg);
01416 }
01417
01418 if (pj_ssl_cipher_is_supported(cipher)) {
01419 static pj_ssl_cipher tls_ciphers[128];
01420
01421 tls_ciphers[cfg->udp_cfg.tls_setting.ciphers_num++] = cipher;
01422 cfg->udp_cfg.tls_setting.ciphers = tls_ciphers;
01423 } else {
01424 pj_ssl_cipher ciphers[128];
01425 unsigned j, ciphers_cnt;
01426
01427 ciphers_cnt = PJ_ARRAY_SIZE(ciphers);
01428 pj_ssl_cipher_get_availables(ciphers, &ciphers_cnt);
01429
01430 PJ_LOG(1,(THIS_FILE, "Cipher \"%s\" is not supported by "
01431 "TLS/SSL backend.", pj_optarg));
01432 printf("Available TLS/SSL ciphers (%d):\n", ciphers_cnt);
01433 for (j=0; j<ciphers_cnt; ++j)
01434 printf("- 0x%06X: %s\n", ciphers[j], pj_ssl_cipher_name(ciphers[j]));
01435 return -1;
01436 }
01437 }
01438 break;
01439 #endif
01440
01441 case OPT_CAPTURE_DEV:
01442 cfg->capture_dev = atoi(pj_optarg);
01443 break;
01444
01445 case OPT_PLAYBACK_DEV:
01446 cfg->playback_dev = atoi(pj_optarg);
01447 break;
01448
01449 case OPT_STDOUT_REFRESH:
01450 stdout_refresh = atoi(pj_optarg);
01451 break;
01452
01453 case OPT_STDOUT_REFRESH_TEXT:
01454 stdout_refresh_text = pj_optarg;
01455 break;
01456
01457 #ifdef _IONBF
01458 case OPT_STDOUT_NO_BUF:
01459 setvbuf(stdout, NULL, _IONBF, 0);
01460 break;
01461 #endif
01462
01463 case OPT_CAPTURE_LAT:
01464 cfg->capture_lat = atoi(pj_optarg);
01465 break;
01466
01467 case OPT_PLAYBACK_LAT:
01468 cfg->playback_lat = atoi(pj_optarg);
01469 break;
01470
01471 case OPT_SND_AUTO_CLOSE:
01472 cfg->media_cfg.snd_auto_close_time = atoi(pj_optarg);
01473 break;
01474
01475 case OPT_NO_TONES:
01476 cfg->no_tones = PJ_TRUE;
01477 break;
01478
01479 case OPT_JB_MAX_SIZE:
01480 cfg->media_cfg.jb_max = atoi(pj_optarg);
01481 break;
01482
01483 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
01484 case OPT_IPV6:
01485 cfg->ipv6 = PJ_TRUE;
01486 break;
01487 #endif
01488 case OPT_QOS:
01489 cfg->enable_qos = PJ_TRUE;
01490
01491 cfg->rtp_cfg.qos_type = PJ_QOS_TYPE_VOICE;
01492
01493
01494
01495
01496 cfg->udp_cfg.qos_params.flags = PJ_QOS_PARAM_HAS_DSCP;
01497 cfg->udp_cfg.qos_params.dscp_val = 0x18;
01498 break;
01499 case OPT_VIDEO:
01500 cfg->vid.vid_cnt = 1;
01501 cfg->vid.in_auto_show = PJ_TRUE;
01502 cfg->vid.out_auto_transmit = PJ_TRUE;
01503 break;
01504 case OPT_EXTRA_AUDIO:
01505 cfg->aud_cnt++;
01506 break;
01507
01508 case OPT_VCAPTURE_DEV:
01509 cfg->vid.vcapture_dev = atoi(pj_optarg);
01510 cur_acc->vid_cap_dev = cfg->vid.vcapture_dev;
01511 break;
01512
01513 case OPT_VRENDER_DEV:
01514 cfg->vid.vrender_dev = atoi(pj_optarg);
01515 cur_acc->vid_rend_dev = cfg->vid.vrender_dev;
01516 break;
01517
01518 case OPT_PLAY_AVI:
01519 if (app_config.avi_cnt >= MAX_AVI) {
01520 PJ_LOG(1,(THIS_FILE, "Too many AVIs"));
01521 return -1;
01522 }
01523 app_config.avi[app_config.avi_cnt++].path = pj_str(pj_optarg);
01524 break;
01525
01526 case OPT_AUTO_PLAY_AVI:
01527 app_config.avi_auto_play = PJ_TRUE;
01528 break;
01529
01530 default:
01531 PJ_LOG(1,(THIS_FILE,
01532 "Argument \"%s\" is not valid. Use --help to see help",
01533 argv[pj_optind-1]));
01534 return -1;
01535 }
01536 }
01537
01538 if (pj_optind != argc) {
01539 pj_str_t uri_arg;
01540
01541 if (pjsua_verify_url(argv[pj_optind]) != PJ_SUCCESS) {
01542 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
01543 return -1;
01544 }
01545 uri_arg = pj_str(argv[pj_optind]);
01546 if (uri_to_call)
01547 *uri_to_call = uri_arg;
01548 pj_optind++;
01549
01550
01551 for (i=0; i<cfg->buddy_cnt; ++i) {
01552 if (pj_stricmp(&cfg->buddy_cfg[i].uri, &uri_arg)==0)
01553 break;
01554 }
01555 if (i == cfg->buddy_cnt && cfg->buddy_cnt < PJSUA_MAX_BUDDIES) {
01556 cfg->buddy_cfg[cfg->buddy_cnt++].uri = uri_arg;
01557 }
01558
01559 } else {
01560 if (uri_to_call)
01561 uri_to_call->slen = 0;
01562 }
01563
01564 if (pj_optind != argc) {
01565 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
01566 return PJ_EINVAL;
01567 }
01568
01569 if (cfg->acc_cfg[cfg->acc_cnt].id.slen)
01570 cfg->acc_cnt++;
01571
01572 for (i=0; i<cfg->acc_cnt; ++i) {
01573 pjsua_acc_config *acfg = &cfg->acc_cfg[i];
01574
01575 if (acfg->cred_info[acfg->cred_count].username.slen)
01576 {
01577 acfg->cred_count++;
01578 }
01579
01580
01581
01582
01583
01584
01585
01586 if (acfg->auth_pref.initial_auth && acfg->cred_count) {
01587
01588 if (*acfg->cred_info[0].realm.ptr=='*') {
01589 PJ_LOG(1,(THIS_FILE,
01590 "Error: cannot use '*' as realm with IMS"));
01591 return PJ_EINVAL;
01592 }
01593
01594
01595 if (strchr(acfg->cred_info[0].username.ptr, '@')==0) {
01596 PJ_LOG(1,(THIS_FILE,
01597 "Error: Username for authentication must "
01598 "be in user@domain format with IMS"));
01599 return PJ_EINVAL;
01600 }
01601 }
01602 }
01603
01604
01605 return PJ_SUCCESS;
01606 }
01607
01608
01609
01610
01611
01612 static void write_account_settings(int acc_index, pj_str_t *result)
01613 {
01614 unsigned i;
01615 char line[128];
01616 pjsua_acc_config *acc_cfg = &app_config.acc_cfg[acc_index];
01617
01618
01619 pj_ansi_sprintf(line, "\n#\n# Account %d:\n#\n", acc_index);
01620 pj_strcat2(result, line);
01621
01622
01623
01624 if (acc_cfg->id.slen) {
01625 pj_ansi_sprintf(line, "--id %.*s\n",
01626 (int)acc_cfg->id.slen,
01627 acc_cfg->id.ptr);
01628 pj_strcat2(result, line);
01629 }
01630
01631
01632 if (acc_cfg->reg_uri.slen) {
01633 pj_ansi_sprintf(line, "--registrar %.*s\n",
01634 (int)acc_cfg->reg_uri.slen,
01635 acc_cfg->reg_uri.ptr);
01636 pj_strcat2(result, line);
01637
01638 pj_ansi_sprintf(line, "--reg-timeout %u\n",
01639 acc_cfg->reg_timeout);
01640 pj_strcat2(result, line);
01641 }
01642
01643
01644 if (acc_cfg->force_contact.slen) {
01645 pj_ansi_sprintf(line, "--contact %.*s\n",
01646 (int)acc_cfg->force_contact.slen,
01647 acc_cfg->force_contact.ptr);
01648 pj_strcat2(result, line);
01649 }
01650
01651
01652 if (acc_cfg->contact_params.slen) {
01653 pj_ansi_sprintf(line, "--contact-params %.*s\n",
01654 (int)acc_cfg->contact_params.slen,
01655 acc_cfg->contact_params.ptr);
01656 pj_strcat2(result, line);
01657 }
01658
01659
01660 if (acc_cfg->contact_uri_params.slen) {
01661 pj_ansi_sprintf(line, "--contact-uri-params %.*s\n",
01662 (int)acc_cfg->contact_uri_params.slen,
01663 acc_cfg->contact_uri_params.ptr);
01664 pj_strcat2(result, line);
01665 }
01666
01667
01668 if (acc_cfg->allow_contact_rewrite!=1)
01669 {
01670 pj_ansi_sprintf(line, "--auto-update-nat %i\n",
01671 (int)acc_cfg->allow_contact_rewrite);
01672 pj_strcat2(result, line);
01673 }
01674
01675 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
01676
01677 if (acc_cfg->use_srtp) {
01678 int use_srtp = (int)acc_cfg->use_srtp;
01679 if (use_srtp == PJMEDIA_SRTP_OPTIONAL &&
01680 acc_cfg->srtp_optional_dup_offer)
01681 {
01682 use_srtp = 3;
01683 }
01684 pj_ansi_sprintf(line, "--use-srtp %i\n", use_srtp);
01685 pj_strcat2(result, line);
01686 }
01687 if (acc_cfg->srtp_secure_signaling !=
01688 PJSUA_DEFAULT_SRTP_SECURE_SIGNALING)
01689 {
01690 pj_ansi_sprintf(line, "--srtp-secure %d\n",
01691 acc_cfg->srtp_secure_signaling);
01692 pj_strcat2(result, line);
01693 }
01694 #endif
01695
01696
01697 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
01698 pj_ansi_sprintf(line, "--proxy %.*s\n",
01699 (int)acc_cfg->proxy[i].slen,
01700 acc_cfg->proxy[i].ptr);
01701 pj_strcat2(result, line);
01702 }
01703
01704
01705 for (i=0; i<acc_cfg->cred_count; ++i) {
01706 if (acc_cfg->cred_info[i].realm.slen) {
01707 pj_ansi_sprintf(line, "--realm %.*s\n",
01708 (int)acc_cfg->cred_info[i].realm.slen,
01709 acc_cfg->cred_info[i].realm.ptr);
01710 pj_strcat2(result, line);
01711 }
01712
01713 if (acc_cfg->cred_info[i].username.slen) {
01714 pj_ansi_sprintf(line, "--username %.*s\n",
01715 (int)acc_cfg->cred_info[i].username.slen,
01716 acc_cfg->cred_info[i].username.ptr);
01717 pj_strcat2(result, line);
01718 }
01719
01720 if (acc_cfg->cred_info[i].data.slen) {
01721 pj_ansi_sprintf(line, "--password %.*s\n",
01722 (int)acc_cfg->cred_info[i].data.slen,
01723 acc_cfg->cred_info[i].data.ptr);
01724 pj_strcat2(result, line);
01725 }
01726
01727 if (i != acc_cfg->cred_count - 1)
01728 pj_strcat2(result, "--next-cred\n");
01729 }
01730
01731
01732 if (acc_cfg->reg_use_proxy != 3) {
01733 pj_ansi_sprintf(line, "--reg-use-proxy %d\n",
01734 acc_cfg->reg_use_proxy);
01735 pj_strcat2(result, line);
01736 }
01737
01738
01739 if (acc_cfg->reg_retry_interval != PJSUA_REG_RETRY_INTERVAL) {
01740 pj_ansi_sprintf(line, "--rereg-delay %d\n",
01741 acc_cfg->reg_retry_interval);
01742 pj_strcat2(result, line);
01743 }
01744
01745
01746 if (acc_cfg->require_100rel) {
01747 pj_strcat2(result, "--use-100rel\n");
01748 }
01749
01750
01751 if (acc_cfg->use_timer) {
01752 pj_ansi_sprintf(line, "--use-timer %d\n",
01753 acc_cfg->use_timer);
01754 pj_strcat2(result, line);
01755 }
01756 if (acc_cfg->timer_setting.min_se != 90) {
01757 pj_ansi_sprintf(line, "--timer-min-se %d\n",
01758 acc_cfg->timer_setting.min_se);
01759 pj_strcat2(result, line);
01760 }
01761 if (acc_cfg->timer_setting.sess_expires != PJSIP_SESS_TIMER_DEF_SE) {
01762 pj_ansi_sprintf(line, "--timer-se %d\n",
01763 acc_cfg->timer_setting.sess_expires);
01764 pj_strcat2(result, line);
01765 }
01766
01767
01768 if (acc_cfg->publish_enabled)
01769 pj_strcat2(result, "--publish\n");
01770
01771
01772 if (acc_cfg->mwi_enabled)
01773 pj_strcat2(result, "--mwi\n");
01774 }
01775
01776
01777
01778
01779
01780 static int write_settings(const struct app_config *config,
01781 char *buf, pj_size_t max)
01782 {
01783 unsigned acc_index;
01784 unsigned i;
01785 pj_str_t cfg;
01786 char line[128];
01787 extern pj_bool_t pjsip_use_compact_form;
01788
01789 PJ_UNUSED_ARG(max);
01790
01791 cfg.ptr = buf;
01792 cfg.slen = 0;
01793
01794
01795 pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
01796 pj_ansi_sprintf(line, "--log-level %d\n",
01797 config->log_cfg.level);
01798 pj_strcat2(&cfg, line);
01799
01800 pj_ansi_sprintf(line, "--app-log-level %d\n",
01801 config->log_cfg.console_level);
01802 pj_strcat2(&cfg, line);
01803
01804 if (config->log_cfg.log_filename.slen) {
01805 pj_ansi_sprintf(line, "--log-file %.*s\n",
01806 (int)config->log_cfg.log_filename.slen,
01807 config->log_cfg.log_filename.ptr);
01808 pj_strcat2(&cfg, line);
01809 }
01810
01811 if (config->log_cfg.log_file_flags & PJ_O_APPEND) {
01812 pj_strcat2(&cfg, "--log-append\n");
01813 }
01814
01815
01816 for (acc_index=0; acc_index < config->acc_cnt; ++acc_index) {
01817
01818 write_account_settings(acc_index, &cfg);
01819
01820 if (acc_index < config->acc_cnt-1)
01821 pj_strcat2(&cfg, "--next-account\n");
01822 }
01823
01824
01825 pj_strcat2(&cfg, "\n#\n# Network settings:\n#\n");
01826
01827
01828 for (i=0; i<config->cfg.nameserver_count; ++i) {
01829 pj_ansi_sprintf(line, "--nameserver %.*s\n",
01830 (int)config->cfg.nameserver[i].slen,
01831 config->cfg.nameserver[i].ptr);
01832 pj_strcat2(&cfg, line);
01833 }
01834
01835
01836 for (i=0; i<config->cfg.outbound_proxy_cnt; ++i) {
01837 pj_ansi_sprintf(line, "--outbound %.*s\n",
01838 (int)config->cfg.outbound_proxy[i].slen,
01839 config->cfg.outbound_proxy[i].ptr);
01840 pj_strcat2(&cfg, line);
01841 }
01842
01843
01844 if (config->ipv6) {
01845 pj_strcat2(&cfg, "--ipv6\n");
01846 }
01847 if (config->enable_qos) {
01848 pj_strcat2(&cfg, "--set-qos\n");
01849 }
01850
01851
01852 pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port);
01853 pj_strcat2(&cfg, line);
01854
01855
01856 if (config->udp_cfg.public_addr.slen) {
01857 pj_ansi_sprintf(line, "--ip-addr %.*s\n",
01858 (int)config->udp_cfg.public_addr.slen,
01859 config->udp_cfg.public_addr.ptr);
01860 pj_strcat2(&cfg, line);
01861 }
01862
01863
01864 if (config->udp_cfg.bound_addr.slen) {
01865 pj_ansi_sprintf(line, "--bound-addr %.*s\n",
01866 (int)config->udp_cfg.bound_addr.slen,
01867 config->udp_cfg.bound_addr.ptr);
01868 pj_strcat2(&cfg, line);
01869 }
01870
01871
01872 if (config->no_tcp) {
01873 pj_strcat2(&cfg, "--no-tcp\n");
01874 }
01875
01876
01877 if (config->no_udp) {
01878 pj_strcat2(&cfg, "--no-udp\n");
01879 }
01880
01881
01882 for (i=0; i<config->cfg.stun_srv_cnt; ++i) {
01883 pj_ansi_sprintf(line, "--stun-srv %.*s\n",
01884 (int)config->cfg.stun_srv[i].slen,
01885 config->cfg.stun_srv[i].ptr);
01886 pj_strcat2(&cfg, line);
01887 }
01888
01889 #if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0)
01890
01891 if (config->use_tls)
01892 pj_strcat2(&cfg, "--use-tls\n");
01893 if (config->udp_cfg.tls_setting.ca_list_file.slen) {
01894 pj_ansi_sprintf(line, "--tls-ca-file %.*s\n",
01895 (int)config->udp_cfg.tls_setting.ca_list_file.slen,
01896 config->udp_cfg.tls_setting.ca_list_file.ptr);
01897 pj_strcat2(&cfg, line);
01898 }
01899 if (config->udp_cfg.tls_setting.cert_file.slen) {
01900 pj_ansi_sprintf(line, "--tls-cert-file %.*s\n",
01901 (int)config->udp_cfg.tls_setting.cert_file.slen,
01902 config->udp_cfg.tls_setting.cert_file.ptr);
01903 pj_strcat2(&cfg, line);
01904 }
01905 if (config->udp_cfg.tls_setting.privkey_file.slen) {
01906 pj_ansi_sprintf(line, "--tls-privkey-file %.*s\n",
01907 (int)config->udp_cfg.tls_setting.privkey_file.slen,
01908 config->udp_cfg.tls_setting.privkey_file.ptr);
01909 pj_strcat2(&cfg, line);
01910 }
01911
01912 if (config->udp_cfg.tls_setting.password.slen) {
01913 pj_ansi_sprintf(line, "--tls-password %.*s\n",
01914 (int)config->udp_cfg.tls_setting.password.slen,
01915 config->udp_cfg.tls_setting.password.ptr);
01916 pj_strcat2(&cfg, line);
01917 }
01918
01919 if (config->udp_cfg.tls_setting.verify_server)
01920 pj_strcat2(&cfg, "--tls-verify-server\n");
01921
01922 if (config->udp_cfg.tls_setting.verify_client)
01923 pj_strcat2(&cfg, "--tls-verify-client\n");
01924
01925 if (config->udp_cfg.tls_setting.timeout.sec) {
01926 pj_ansi_sprintf(line, "--tls-neg-timeout %d\n",
01927 (int)config->udp_cfg.tls_setting.timeout.sec);
01928 pj_strcat2(&cfg, line);
01929 }
01930
01931 for (i=0; i<config->udp_cfg.tls_setting.ciphers_num; ++i) {
01932 pj_ansi_sprintf(line, "--tls-cipher 0x%06X # %s\n",
01933 config->udp_cfg.tls_setting.ciphers[i],
01934 pj_ssl_cipher_name(config->udp_cfg.tls_setting.ciphers[i]));
01935 pj_strcat2(&cfg, line);
01936 }
01937 #endif
01938
01939 pj_strcat2(&cfg, "\n#\n# Media settings:\n#\n");
01940
01941
01942 for (i=0; i<config->vid.vid_cnt; ++i) {
01943 pj_strcat2(&cfg, "--video\n");
01944 }
01945 for (i=1; i<config->aud_cnt; ++i) {
01946 pj_strcat2(&cfg, "--extra-audio\n");
01947 }
01948
01949
01950 #if PJMEDIA_HAS_SRTP
01951 if (app_config.cfg.use_srtp != PJSUA_DEFAULT_USE_SRTP) {
01952 int use_srtp = (int)app_config.cfg.use_srtp;
01953 if (use_srtp == PJMEDIA_SRTP_OPTIONAL &&
01954 app_config.cfg.srtp_optional_dup_offer)
01955 {
01956 use_srtp = 3;
01957 }
01958 pj_ansi_sprintf(line, "--use-srtp %d\n", use_srtp);
01959 pj_strcat2(&cfg, line);
01960 }
01961 if (app_config.cfg.srtp_secure_signaling !=
01962 PJSUA_DEFAULT_SRTP_SECURE_SIGNALING)
01963 {
01964 pj_ansi_sprintf(line, "--srtp-secure %d\n",
01965 app_config.cfg.srtp_secure_signaling);
01966 pj_strcat2(&cfg, line);
01967 }
01968 #endif
01969
01970
01971 if (config->media_cfg.enable_ice)
01972 pj_strcat2(&cfg, "--use-ice\n");
01973
01974 if (config->media_cfg.ice_opt.aggressive == PJ_FALSE)
01975 pj_strcat2(&cfg, "--ice-regular\n");
01976
01977 if (config->media_cfg.enable_turn)
01978 pj_strcat2(&cfg, "--use-turn\n");
01979
01980 if (config->media_cfg.ice_max_host_cands >= 0) {
01981 pj_ansi_sprintf(line, "--ice_max_host_cands %d\n",
01982 config->media_cfg.ice_max_host_cands);
01983 pj_strcat2(&cfg, line);
01984 }
01985
01986 if (config->media_cfg.ice_no_rtcp)
01987 pj_strcat2(&cfg, "--ice-no-rtcp\n");
01988
01989 if (config->media_cfg.turn_server.slen) {
01990 pj_ansi_sprintf(line, "--turn-srv %.*s\n",
01991 (int)config->media_cfg.turn_server.slen,
01992 config->media_cfg.turn_server.ptr);
01993 pj_strcat2(&cfg, line);
01994 }
01995
01996 if (config->media_cfg.turn_conn_type == PJ_TURN_TP_TCP)
01997 pj_strcat2(&cfg, "--turn-tcp\n");
01998
01999 if (config->media_cfg.turn_auth_cred.data.static_cred.username.slen) {
02000 pj_ansi_sprintf(line, "--turn-user %.*s\n",
02001 (int)config->media_cfg.turn_auth_cred.data.static_cred.username.slen,
02002 config->media_cfg.turn_auth_cred.data.static_cred.username.ptr);
02003 pj_strcat2(&cfg, line);
02004 }
02005
02006 if (config->media_cfg.turn_auth_cred.data.static_cred.data.slen) {
02007 pj_ansi_sprintf(line, "--turn-passwd %.*s\n",
02008 (int)config->media_cfg.turn_auth_cred.data.static_cred.data.slen,
02009 config->media_cfg.turn_auth_cred.data.static_cred.data.ptr);
02010 pj_strcat2(&cfg, line);
02011 }
02012
02013
02014 if (config->null_audio)
02015 pj_strcat2(&cfg, "--null-audio\n");
02016 if (config->auto_play)
02017 pj_strcat2(&cfg, "--auto-play\n");
02018 if (config->auto_loop)
02019 pj_strcat2(&cfg, "--auto-loop\n");
02020 if (config->auto_conf)
02021 pj_strcat2(&cfg, "--auto-conf\n");
02022 for (i=0; i<config->wav_count; ++i) {
02023 pj_ansi_sprintf(line, "--play-file %s\n",
02024 config->wav_files[i].ptr);
02025 pj_strcat2(&cfg, line);
02026 }
02027 for (i=0; i<config->tone_count; ++i) {
02028 pj_ansi_sprintf(line, "--play-tone %d,%d,%d,%d\n",
02029 config->tones[i].freq1, config->tones[i].freq2,
02030 config->tones[i].on_msec, config->tones[i].off_msec);
02031 pj_strcat2(&cfg, line);
02032 }
02033 if (config->rec_file.slen) {
02034 pj_ansi_sprintf(line, "--rec-file %s\n",
02035 config->rec_file.ptr);
02036 pj_strcat2(&cfg, line);
02037 }
02038 if (config->auto_rec)
02039 pj_strcat2(&cfg, "--auto-rec\n");
02040 if (config->capture_dev != PJSUA_INVALID_ID) {
02041 pj_ansi_sprintf(line, "--capture-dev %d\n", config->capture_dev);
02042 pj_strcat2(&cfg, line);
02043 }
02044 if (config->playback_dev != PJSUA_INVALID_ID) {
02045 pj_ansi_sprintf(line, "--playback-dev %d\n", config->playback_dev);
02046 pj_strcat2(&cfg, line);
02047 }
02048 if (config->media_cfg.snd_auto_close_time != -1) {
02049 pj_ansi_sprintf(line, "--snd-auto-close %d\n",
02050 config->media_cfg.snd_auto_close_time);
02051 pj_strcat2(&cfg, line);
02052 }
02053 if (config->no_tones) {
02054 pj_strcat2(&cfg, "--no-tones\n");
02055 }
02056 if (config->media_cfg.jb_max != -1) {
02057 pj_ansi_sprintf(line, "--jb-max-size %d\n",
02058 config->media_cfg.jb_max);
02059 pj_strcat2(&cfg, line);
02060 }
02061
02062
02063 if (config->capture_lat != PJMEDIA_SND_DEFAULT_REC_LATENCY) {
02064 pj_ansi_sprintf(line, "--capture-lat %d\n", config->capture_lat);
02065 pj_strcat2(&cfg, line);
02066 }
02067 if (config->playback_lat != PJMEDIA_SND_DEFAULT_PLAY_LATENCY) {
02068 pj_ansi_sprintf(line, "--playback-lat %d\n", config->playback_lat);
02069 pj_strcat2(&cfg, line);
02070 }
02071
02072
02073 if (config->media_cfg.clock_rate != PJSUA_DEFAULT_CLOCK_RATE) {
02074 pj_ansi_sprintf(line, "--clock-rate %d\n",
02075 config->media_cfg.clock_rate);
02076 pj_strcat2(&cfg, line);
02077 } else {
02078 pj_ansi_sprintf(line, "#using default --clock-rate %d\n",
02079 config->media_cfg.clock_rate);
02080 pj_strcat2(&cfg, line);
02081 }
02082
02083 if (config->media_cfg.snd_clock_rate &&
02084 config->media_cfg.snd_clock_rate != config->media_cfg.clock_rate)
02085 {
02086 pj_ansi_sprintf(line, "--snd-clock-rate %d\n",
02087 config->media_cfg.snd_clock_rate);
02088 pj_strcat2(&cfg, line);
02089 }
02090
02091
02092 if (config->media_cfg.channel_count == 2) {
02093 pj_ansi_sprintf(line, "--stereo\n");
02094 pj_strcat2(&cfg, line);
02095 }
02096
02097
02098 if (config->media_cfg.quality != PJSUA_DEFAULT_CODEC_QUALITY) {
02099 pj_ansi_sprintf(line, "--quality %d\n",
02100 config->media_cfg.quality);
02101 pj_strcat2(&cfg, line);
02102 } else {
02103 pj_ansi_sprintf(line, "#using default --quality %d\n",
02104 config->media_cfg.quality);
02105 pj_strcat2(&cfg, line);
02106 }
02107
02108 if (config->vid.vcapture_dev != PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
02109 pj_ansi_sprintf(line, "--vcapture-dev %d\n", config->vid.vcapture_dev);
02110 pj_strcat2(&cfg, line);
02111 }
02112 if (config->vid.vrender_dev != PJMEDIA_VID_DEFAULT_RENDER_DEV) {
02113 pj_ansi_sprintf(line, "--vrender-dev %d\n", config->vid.vrender_dev);
02114 pj_strcat2(&cfg, line);
02115 }
02116 for (i=0; i<config->avi_cnt; ++i) {
02117 pj_ansi_sprintf(line, "--play-avi %s\n", config->avi[i].path.ptr);
02118 pj_strcat2(&cfg, line);
02119 }
02120 if (config->avi_auto_play) {
02121 pj_ansi_sprintf(line, "--auto-play-avi\n");
02122 pj_strcat2(&cfg, line);
02123 }
02124
02125
02126 if (config->media_cfg.ptime) {
02127 pj_ansi_sprintf(line, "--ptime %d\n",
02128 config->media_cfg.ptime);
02129 pj_strcat2(&cfg, line);
02130 }
02131
02132
02133 if (config->media_cfg.no_vad) {
02134 pj_strcat2(&cfg, "--no-vad\n");
02135 }
02136
02137
02138 if (config->media_cfg.ec_tail_len != PJSUA_DEFAULT_EC_TAIL_LEN) {
02139 pj_ansi_sprintf(line, "--ec-tail %d\n",
02140 config->media_cfg.ec_tail_len);
02141 pj_strcat2(&cfg, line);
02142 } else {
02143 pj_ansi_sprintf(line, "#using default --ec-tail %d\n",
02144 config->media_cfg.ec_tail_len);
02145 pj_strcat2(&cfg, line);
02146 }
02147
02148
02149 if (config->media_cfg.ec_options != 0) {
02150 pj_ansi_sprintf(line, "--ec-opt %d\n",
02151 config->media_cfg.ec_options);
02152 pj_strcat2(&cfg, line);
02153 }
02154
02155
02156 if (config->media_cfg.ilbc_mode != PJSUA_DEFAULT_ILBC_MODE) {
02157 pj_ansi_sprintf(line, "--ilbc-mode %d\n",
02158 config->media_cfg.ilbc_mode);
02159 pj_strcat2(&cfg, line);
02160 } else {
02161 pj_ansi_sprintf(line, "#using default --ilbc-mode %d\n",
02162 config->media_cfg.ilbc_mode);
02163 pj_strcat2(&cfg, line);
02164 }
02165
02166
02167 if (config->media_cfg.tx_drop_pct) {
02168 pj_ansi_sprintf(line, "--tx-drop-pct %d\n",
02169 config->media_cfg.tx_drop_pct);
02170 pj_strcat2(&cfg, line);
02171
02172 }
02173 if (config->media_cfg.rx_drop_pct) {
02174 pj_ansi_sprintf(line, "--rx-drop-pct %d\n",
02175 config->media_cfg.rx_drop_pct);
02176 pj_strcat2(&cfg, line);
02177
02178 }
02179
02180
02181
02182 pj_ansi_sprintf(line, "--rtp-port %d\n",
02183 config->rtp_cfg.port);
02184 pj_strcat2(&cfg, line);
02185
02186
02187 for (i=0; i<config->codec_dis_cnt; ++i) {
02188 pj_ansi_sprintf(line, "--dis-codec %s\n",
02189 config->codec_dis[i].ptr);
02190 pj_strcat2(&cfg, line);
02191 }
02192
02193 for (i=0; i<config->codec_cnt; ++i) {
02194 pj_ansi_sprintf(line, "--add-codec %s\n",
02195 config->codec_arg[i].ptr);
02196 pj_strcat2(&cfg, line);
02197 }
02198
02199 pj_strcat2(&cfg, "\n#\n# User agent:\n#\n");
02200
02201
02202 if (config->auto_answer != 0) {
02203 pj_ansi_sprintf(line, "--auto-answer %d\n",
02204 config->auto_answer);
02205 pj_strcat2(&cfg, line);
02206 }
02207
02208
02209 if (config->redir_op != PJSIP_REDIRECT_ACCEPT) {
02210 pj_ansi_sprintf(line, "--accept-redirect %d\n",
02211 config->redir_op);
02212 pj_strcat2(&cfg, line);
02213 }
02214
02215
02216 pj_ansi_sprintf(line, "--max-calls %d\n",
02217 config->cfg.max_calls);
02218 pj_strcat2(&cfg, line);
02219
02220
02221 if (config->duration != NO_LIMIT) {
02222 pj_ansi_sprintf(line, "--duration %d\n",
02223 config->duration);
02224 pj_strcat2(&cfg, line);
02225 }
02226
02227
02228 if (config->no_refersub) {
02229 pj_strcat2(&cfg, "--norefersub\n");
02230 }
02231
02232 if (pjsip_use_compact_form)
02233 {
02234 pj_strcat2(&cfg, "--use-compact-form\n");
02235 }
02236
02237 if (!config->cfg.force_lr) {
02238 pj_strcat2(&cfg, "--no-force-lr\n");
02239 }
02240
02241 pj_strcat2(&cfg, "\n#\n# Buddies:\n#\n");
02242
02243
02244 for (i=0; i<config->buddy_cnt; ++i) {
02245 pj_ansi_sprintf(line, "--add-buddy %.*s\n",
02246 (int)config->buddy_cfg[i].uri.slen,
02247 config->buddy_cfg[i].uri.ptr);
02248 pj_strcat2(&cfg, line);
02249 }
02250
02251
02252 pj_strcat2(&cfg, "\n#\n# SIP extensions:\n#\n");
02253
02254 if (config->cfg.require_100rel) {
02255 pj_strcat2(&cfg, "--use-100rel\n");
02256 }
02257
02258 if (config->cfg.use_timer) {
02259 pj_ansi_sprintf(line, "--use-timer %d\n",
02260 config->cfg.use_timer);
02261 pj_strcat2(&cfg, line);
02262 }
02263 if (config->cfg.timer_setting.min_se != 90) {
02264 pj_ansi_sprintf(line, "--timer-min-se %d\n",
02265 config->cfg.timer_setting.min_se);
02266 pj_strcat2(&cfg, line);
02267 }
02268 if (config->cfg.timer_setting.sess_expires != PJSIP_SESS_TIMER_DEF_SE) {
02269 pj_ansi_sprintf(line, "--timer-se %d\n",
02270 config->cfg.timer_setting.sess_expires);
02271 pj_strcat2(&cfg, line);
02272 }
02273
02274 *(cfg.ptr + cfg.slen) = '\0';
02275 return cfg.slen;
02276 }
02277
02278
02279
02280
02281
02282 static void app_dump(pj_bool_t detail)
02283 {
02284 pjsua_dump(detail);
02285 }
02286
02287
02288
02289
02290
02291
02292 static void log_call_dump(int call_id)
02293 {
02294 unsigned call_dump_len;
02295 unsigned part_len;
02296 unsigned part_idx;
02297 unsigned log_decor;
02298
02299 pjsua_call_dump(call_id, PJ_TRUE, some_buf,
02300 sizeof(some_buf), " ");
02301 call_dump_len = strlen(some_buf);
02302
02303 log_decor = pj_log_get_decor();
02304 pj_log_set_decor(log_decor & ~(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
02305 PJ_LOG(3,(THIS_FILE, "\n"));
02306 pj_log_set_decor(0);
02307
02308 part_idx = 0;
02309 part_len = PJ_LOG_MAX_SIZE-80;
02310 while (part_idx < call_dump_len) {
02311 char p_orig, *p;
02312
02313 p = &some_buf[part_idx];
02314 if (part_idx + part_len > call_dump_len)
02315 part_len = call_dump_len - part_idx;
02316 p_orig = p[part_len];
02317 p[part_len] = '\0';
02318 PJ_LOG(3,(THIS_FILE, "%s", p));
02319 p[part_len] = p_orig;
02320 part_idx += part_len;
02321 }
02322 pj_log_set_decor(log_decor);
02323 }
02324
02325
02326
02327
02328
02329 static void ringback_start(pjsua_call_id call_id)
02330 {
02331 if (app_config.no_tones)
02332 return;
02333
02334 if (app_config.call_data[call_id].ringback_on)
02335 return;
02336
02337 app_config.call_data[call_id].ringback_on = PJ_TRUE;
02338
02339 if (++app_config.ringback_cnt==1 &&
02340 app_config.ringback_slot!=PJSUA_INVALID_ID)
02341 {
02342 pjsua_conf_connect(app_config.ringback_slot, 0);
02343 }
02344 }
02345
02346 static void ring_stop(pjsua_call_id call_id)
02347 {
02348 if (app_config.no_tones)
02349 return;
02350
02351 if (app_config.call_data[call_id].ringback_on) {
02352 app_config.call_data[call_id].ringback_on = PJ_FALSE;
02353
02354 pj_assert(app_config.ringback_cnt>0);
02355 if (--app_config.ringback_cnt == 0 &&
02356 app_config.ringback_slot!=PJSUA_INVALID_ID)
02357 {
02358 pjsua_conf_disconnect(app_config.ringback_slot, 0);
02359 pjmedia_tonegen_rewind(app_config.ringback_port);
02360 }
02361 }
02362
02363 if (app_config.call_data[call_id].ring_on) {
02364 app_config.call_data[call_id].ring_on = PJ_FALSE;
02365
02366 pj_assert(app_config.ring_cnt>0);
02367 if (--app_config.ring_cnt == 0 &&
02368 app_config.ring_slot!=PJSUA_INVALID_ID)
02369 {
02370 pjsua_conf_disconnect(app_config.ring_slot, 0);
02371 pjmedia_tonegen_rewind(app_config.ring_port);
02372 }
02373 }
02374 }
02375
02376 static void ring_start(pjsua_call_id call_id)
02377 {
02378 if (app_config.no_tones)
02379 return;
02380
02381 if (app_config.call_data[call_id].ring_on)
02382 return;
02383
02384 app_config.call_data[call_id].ring_on = PJ_TRUE;
02385
02386 if (++app_config.ring_cnt==1 &&
02387 app_config.ring_slot!=PJSUA_INVALID_ID)
02388 {
02389 pjsua_conf_connect(app_config.ring_slot, 0);
02390 }
02391 }
02392
02393 #ifdef HAVE_MULTIPART_TEST
02394
02395
02396
02397
02398 static void add_multipart(pjsua_msg_data *msg_data)
02399 {
02400 static pjsip_multipart_part *alt_part;
02401
02402 if (!alt_part) {
02403 pj_str_t type, subtype, content;
02404
02405 alt_part = pjsip_multipart_create_part(app_config.pool);
02406
02407 type = pj_str("text");
02408 subtype = pj_str("plain");
02409 content = pj_str("Sample text body of a multipart bodies");
02410 alt_part->body = pjsip_msg_body_create(app_config.pool, &type,
02411 &subtype, &content);
02412 }
02413
02414 msg_data->multipart_ctype.type = pj_str("multipart");
02415 msg_data->multipart_ctype.subtype = pj_str("mixed");
02416 pj_list_push_back(&msg_data->multipart_parts, alt_part);
02417 }
02418 # define TEST_MULTIPART(msg_data) add_multipart(msg_data)
02419 #else
02420 # define TEST_MULTIPART(msg_data)
02421 #endif
02422
02423
02424
02425
02426
02427 static pj_bool_t find_next_call(void)
02428 {
02429 int i, max;
02430
02431 max = pjsua_call_get_max_count();
02432 for (i=current_call+1; i<max; ++i) {
02433 if (pjsua_call_is_active(i)) {
02434 current_call = i;
02435 return PJ_TRUE;
02436 }
02437 }
02438
02439 for (i=0; i<current_call; ++i) {
02440 if (pjsua_call_is_active(i)) {
02441 current_call = i;
02442 return PJ_TRUE;
02443 }
02444 }
02445
02446 current_call = PJSUA_INVALID_ID;
02447 return PJ_FALSE;
02448 }
02449
02450
02451
02452
02453
02454 static pj_bool_t find_prev_call(void)
02455 {
02456 int i, max;
02457
02458 max = pjsua_call_get_max_count();
02459 for (i=current_call-1; i>=0; --i) {
02460 if (pjsua_call_is_active(i)) {
02461 current_call = i;
02462 return PJ_TRUE;
02463 }
02464 }
02465
02466 for (i=max-1; i>current_call; --i) {
02467 if (pjsua_call_is_active(i)) {
02468 current_call = i;
02469 return PJ_TRUE;
02470 }
02471 }
02472
02473 current_call = PJSUA_INVALID_ID;
02474 return PJ_FALSE;
02475 }
02476
02477
02478
02479
02480
02481 static void call_timeout_callback(pj_timer_heap_t *timer_heap,
02482 struct pj_timer_entry *entry)
02483 {
02484 pjsua_call_id call_id = entry->id;
02485 pjsua_msg_data msg_data;
02486 pjsip_generic_string_hdr warn;
02487 pj_str_t hname = pj_str("Warning");
02488 pj_str_t hvalue = pj_str("399 pjsua \"Call duration exceeded\"");
02489
02490 PJ_UNUSED_ARG(timer_heap);
02491
02492 if (call_id == PJSUA_INVALID_ID) {
02493 PJ_LOG(1,(THIS_FILE, "Invalid call ID in timer callback"));
02494 return;
02495 }
02496
02497
02498 pjsua_msg_data_init(&msg_data);
02499 pjsip_generic_string_hdr_init2(&warn, &hname, &hvalue);
02500 pj_list_push_back(&msg_data.hdr_list, &warn);
02501
02502
02503 PJ_LOG(3,(THIS_FILE, "Duration (%d seconds) has been exceeded "
02504 "for call %d, disconnecting the call",
02505 app_config.duration, call_id));
02506 entry->id = PJSUA_INVALID_ID;
02507 pjsua_call_hangup(call_id, 200, NULL, &msg_data);
02508 }
02509
02510
02511
02512
02513
02514 static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
02515 {
02516 pjsua_call_info call_info;
02517
02518 PJ_UNUSED_ARG(e);
02519
02520 pjsua_call_get_info(call_id, &call_info);
02521
02522 if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
02523
02524
02525 ring_stop(call_id);
02526
02527
02528 if (app_config.call_data[call_id].timer.id != PJSUA_INVALID_ID) {
02529 struct call_data *cd = &app_config.call_data[call_id];
02530 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
02531
02532 cd->timer.id = PJSUA_INVALID_ID;
02533 pjsip_endpt_cancel_timer(endpt, &cd->timer);
02534 }
02535
02536
02537
02538
02539 if (app_config.auto_play_hangup)
02540 pjsua_player_set_pos(app_config.wav_id, 0);
02541
02542
02543 PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]",
02544 call_id,
02545 call_info.last_status,
02546 call_info.last_status_text.ptr));
02547
02548 if (call_id == current_call) {
02549 find_next_call();
02550 }
02551
02552
02553 if (1) {
02554 PJ_LOG(5,(THIS_FILE,
02555 "Call %d disconnected, dumping media stats..",
02556 call_id));
02557 log_call_dump(call_id);
02558 }
02559
02560 } else {
02561
02562 if (app_config.duration!=NO_LIMIT &&
02563 call_info.state == PJSIP_INV_STATE_CONFIRMED)
02564 {
02565
02566 struct call_data *cd = &app_config.call_data[call_id];
02567 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
02568 pj_time_val delay;
02569
02570 cd->timer.id = call_id;
02571 delay.sec = app_config.duration;
02572 delay.msec = 0;
02573 pjsip_endpt_schedule_timer(endpt, &cd->timer, &delay);
02574 }
02575
02576 if (call_info.state == PJSIP_INV_STATE_EARLY) {
02577 int code;
02578 pj_str_t reason;
02579 pjsip_msg *msg;
02580
02581
02582 pj_assert(e->type == PJSIP_EVENT_TSX_STATE);
02583
02584 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
02585 msg = e->body.tsx_state.src.rdata->msg_info.msg;
02586 } else {
02587 msg = e->body.tsx_state.src.tdata->msg;
02588 }
02589
02590 code = msg->line.status.code;
02591 reason = msg->line.status.reason;
02592
02593
02594 if (call_info.role==PJSIP_ROLE_UAC && code==180 &&
02595 msg->body == NULL &&
02596 call_info.media_status==PJSUA_CALL_MEDIA_NONE)
02597 {
02598 ringback_start(call_id);
02599 }
02600
02601 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s (%d %.*s)",
02602 call_id, call_info.state_text.ptr,
02603 code, (int)reason.slen, reason.ptr));
02604 } else {
02605 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
02606 call_id,
02607 call_info.state_text.ptr));
02608 }
02609
02610 if (current_call==PJSUA_INVALID_ID)
02611 current_call = call_id;
02612
02613 }
02614 }
02615
02616
02620 static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
02621 pjsip_rx_data *rdata)
02622 {
02623 pjsua_call_info call_info;
02624
02625 PJ_UNUSED_ARG(acc_id);
02626 PJ_UNUSED_ARG(rdata);
02627
02628 pjsua_call_get_info(call_id, &call_info);
02629
02630 if (current_call==PJSUA_INVALID_ID)
02631 current_call = call_id;
02632
02633 #ifdef USE_GUI
02634 if (!showNotification(call_id))
02635 return;
02636 #endif
02637
02638
02639 ring_start(call_id);
02640
02641 if (app_config.auto_answer > 0) {
02642 pjsua_call_setting call_opt;
02643
02644 pjsua_call_setting_default(&call_opt);
02645 call_opt.aud_cnt = app_config.aud_cnt;
02646 call_opt.vid_cnt = app_config.vid.vid_cnt;
02647
02648 pjsua_call_answer2(call_id, &call_opt, app_config.auto_answer, NULL, NULL);
02649 }
02650
02651 if (app_config.auto_answer < 200) {
02652 char notif_st[80] = {0};
02653
02654 #if PJSUA_HAS_VIDEO
02655 if (call_info.rem_offerer && call_info.rem_vid_cnt) {
02656 snprintf(notif_st, sizeof(notif_st),
02657 "To %s the video, type \"vid %s\" first, "
02658 "before answering the call!\n",
02659 (app_config.vid.vid_cnt? "reject":"accept"),
02660 (app_config.vid.vid_cnt? "disable":"enable"));
02661 }
02662 #endif
02663
02664 PJ_LOG(3,(THIS_FILE,
02665 "Incoming call for account %d!\n"
02666 "Media count: %d audio & %d video\n"
02667 "%s"
02668 "From: %s\n"
02669 "To: %s\n"
02670 "Press a to answer or h to reject call",
02671 acc_id,
02672 call_info.rem_aud_cnt,
02673 call_info.rem_vid_cnt,
02674 notif_st,
02675 call_info.remote_info.ptr,
02676 call_info.local_info.ptr));
02677 }
02678 }
02679
02680
02681
02682
02683
02684 static void on_call_tsx_state(pjsua_call_id call_id,
02685 pjsip_transaction *tsx,
02686 pjsip_event *e)
02687 {
02688 const pjsip_method info_method =
02689 {
02690 PJSIP_OTHER_METHOD,
02691 { "INFO", 4 }
02692 };
02693
02694 if (pjsip_method_cmp(&tsx->method, &info_method)==0) {
02695
02696
02697
02698 const pj_str_t STR_APPLICATION = { "application", 11};
02699 const pj_str_t STR_DTMF_RELAY = { "dtmf-relay", 10 };
02700 pjsip_msg_body *body = NULL;
02701 pj_bool_t dtmf_info = PJ_FALSE;
02702
02703 if (tsx->role == PJSIP_ROLE_UAC) {
02704 if (e->body.tsx_state.type == PJSIP_EVENT_TX_MSG)
02705 body = e->body.tsx_state.src.tdata->msg->body;
02706 else
02707 body = e->body.tsx_state.tsx->last_tx->msg->body;
02708 } else {
02709 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
02710 body = e->body.tsx_state.src.rdata->msg_info.msg->body;
02711 }
02712
02713
02714 if (body && body->len &&
02715 pj_stricmp(&body->content_type.type, &STR_APPLICATION)==0 &&
02716 pj_stricmp(&body->content_type.subtype, &STR_DTMF_RELAY)==0)
02717 {
02718 dtmf_info = PJ_TRUE;
02719 }
02720
02721 if (dtmf_info && tsx->role == PJSIP_ROLE_UAC &&
02722 (tsx->state == PJSIP_TSX_STATE_COMPLETED ||
02723 (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
02724 e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED)))
02725 {
02726
02727 if (tsx->status_code >= 200 && tsx->status_code < 300) {
02728 PJ_LOG(4,(THIS_FILE,
02729 "Call %d: DTMF sent successfully with INFO",
02730 call_id));
02731 } else if (tsx->status_code >= 300) {
02732 PJ_LOG(4,(THIS_FILE,
02733 "Call %d: Failed to send DTMF with INFO: %d/%.*s",
02734 call_id,
02735 tsx->status_code,
02736 (int)tsx->status_text.slen,
02737 tsx->status_text.ptr));
02738 }
02739 } else if (dtmf_info && tsx->role == PJSIP_ROLE_UAS &&
02740 tsx->state == PJSIP_TSX_STATE_TRYING)
02741 {
02742
02743 pjsip_rx_data *rdata;
02744 pjsip_tx_data *tdata;
02745 pj_status_t status;
02746
02747 rdata = e->body.tsx_state.src.rdata;
02748
02749 if (rdata->msg_info.msg->body) {
02750 status = pjsip_endpt_create_response(tsx->endpt, rdata,
02751 200, NULL, &tdata);
02752 if (status == PJ_SUCCESS)
02753 status = pjsip_tsx_send_msg(tsx, tdata);
02754
02755 PJ_LOG(3,(THIS_FILE, "Call %d: incoming INFO:\n%.*s",
02756 call_id,
02757 (int)rdata->msg_info.msg->body->len,
02758 rdata->msg_info.msg->body->data));
02759 } else {
02760 status = pjsip_endpt_create_response(tsx->endpt, rdata,
02761 400, NULL, &tdata);
02762 if (status == PJ_SUCCESS)
02763 status = pjsip_tsx_send_msg(tsx, tdata);
02764 }
02765 }
02766 }
02767 }
02768
02769
02770 static void on_call_generic_media_state(pjsua_call_info *ci, unsigned mi,
02771 pj_bool_t *has_error)
02772 {
02773 const char *status_name[] = {
02774 "None",
02775 "Active",
02776 "Local hold",
02777 "Remote hold",
02778 "Error"
02779 };
02780
02781 PJ_UNUSED_ARG(has_error);
02782
02783 pj_assert(ci->media[mi].status <= PJ_ARRAY_SIZE(status_name));
02784 pj_assert(PJSUA_CALL_MEDIA_ERROR == 4);
02785
02786 PJ_LOG(4,(THIS_FILE, "Call %d media %d [type=%s], status is %s",
02787 ci->id, mi, pjmedia_type_name(ci->media[mi].type),
02788 status_name[ci->media[mi].status]));
02789 }
02790
02791
02792 static void on_call_audio_state(pjsua_call_info *ci, unsigned mi,
02793 pj_bool_t *has_error)
02794 {
02795 PJ_UNUSED_ARG(has_error);
02796
02797
02798 ring_stop(ci->id);
02799
02800
02801
02802
02803 if (ci->media[mi].status == PJSUA_CALL_MEDIA_ACTIVE ||
02804 ci->media[mi].status == PJSUA_CALL_MEDIA_REMOTE_HOLD)
02805 {
02806 pj_bool_t connect_sound = PJ_TRUE;
02807 pj_bool_t disconnect_mic = PJ_FALSE;
02808 pjsua_conf_port_id call_conf_slot;
02809
02810 call_conf_slot = ci->media[mi].stream.aud.conf_slot;
02811
02812
02813 if (app_config.auto_loop) {
02814 pjsua_conf_connect(call_conf_slot, call_conf_slot);
02815 connect_sound = PJ_FALSE;
02816 }
02817
02818
02819 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
02820 pjsua_conf_connect(call_conf_slot, app_config.rec_port);
02821 }
02822
02823
02824 if ((app_config.auto_play || app_config.auto_play_hangup) &&
02825 app_config.wav_port != PJSUA_INVALID_ID)
02826 {
02827 pjsua_conf_connect(app_config.wav_port, call_conf_slot);
02828 connect_sound = PJ_FALSE;
02829 }
02830
02831
02832 if (app_config.avi_auto_play &&
02833 app_config.avi_def_idx != PJSUA_INVALID_ID &&
02834 app_config.avi[app_config.avi_def_idx].slot != PJSUA_INVALID_ID)
02835 {
02836 pjsua_conf_connect(app_config.avi[app_config.avi_def_idx].slot,
02837 call_conf_slot);
02838 disconnect_mic = PJ_TRUE;
02839 }
02840
02841
02842 if (app_config.auto_conf) {
02843 pjsua_call_id call_ids[PJSUA_MAX_CALLS];
02844 unsigned call_cnt=PJ_ARRAY_SIZE(call_ids);
02845 unsigned i;
02846
02847
02848
02849
02850 pjsua_enum_calls(call_ids, &call_cnt);
02851
02852 for (i=0; i<call_cnt; ++i) {
02853 if (call_ids[i] == ci->id)
02854 continue;
02855
02856 if (!pjsua_call_has_media(call_ids[i]))
02857 continue;
02858
02859 pjsua_conf_connect(call_conf_slot,
02860 pjsua_call_get_conf_port(call_ids[i]));
02861 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
02862 call_conf_slot);
02863
02864
02865 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
02866 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
02867 app_config.rec_port);
02868 }
02869
02870 }
02871
02872
02873 connect_sound = PJ_TRUE;
02874 }
02875
02876
02877 if (connect_sound) {
02878 pjsua_conf_connect(call_conf_slot, 0);
02879 if (!disconnect_mic)
02880 pjsua_conf_connect(0, call_conf_slot);
02881
02882
02883 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
02884 pjsua_conf_connect(call_conf_slot, app_config.rec_port);
02885 pjsua_conf_connect(0, app_config.rec_port);
02886 }
02887 }
02888 }
02889 }
02890
02891
02892
02893
02894
02895 static void arrange_window(pjsua_vid_win_id wid)
02896 {
02897 #if PJSUA_HAS_VIDEO
02898 pjmedia_coord pos;
02899 int i, last;
02900
02901 pos.x = 0;
02902 pos.y = 10;
02903 last = (wid == PJSUA_INVALID_ID) ? PJSUA_MAX_VID_WINS : wid;
02904
02905 for (i=0; i<last; ++i) {
02906 pjsua_vid_win_info wi;
02907 pj_status_t status;
02908
02909 status = pjsua_vid_win_get_info(i, &wi);
02910 if (status != PJ_SUCCESS)
02911 continue;
02912
02913 if (wid == PJSUA_INVALID_ID)
02914 pjsua_vid_win_set_pos(i, &pos);
02915
02916 if (wi.show)
02917 pos.y += wi.size.h;
02918 }
02919
02920 if (wid != PJSUA_INVALID_ID)
02921 pjsua_vid_win_set_pos(wid, &pos);
02922 #else
02923 PJ_UNUSED_ARG(wid);
02924 #endif
02925 }
02926
02927
02928 static void on_call_video_state(pjsua_call_info *ci, unsigned mi,
02929 pj_bool_t *has_error)
02930 {
02931 if (ci->media_status != PJSUA_CALL_MEDIA_ACTIVE)
02932 return;
02933
02934 arrange_window(ci->media[mi].stream.vid.win_in);
02935
02936 PJ_UNUSED_ARG(has_error);
02937 }
02938
02939
02940
02941
02942
02943
02944 static void on_call_media_state(pjsua_call_id call_id)
02945 {
02946 pjsua_call_info call_info;
02947 unsigned mi;
02948 pj_bool_t has_error = PJ_FALSE;
02949
02950 pjsua_call_get_info(call_id, &call_info);
02951
02952 for (mi=0; mi<call_info.media_cnt; ++mi) {
02953 on_call_generic_media_state(&call_info, mi, &has_error);
02954
02955 switch (call_info.media[mi].type) {
02956 case PJMEDIA_TYPE_AUDIO:
02957 on_call_audio_state(&call_info, mi, &has_error);
02958 break;
02959 case PJMEDIA_TYPE_VIDEO:
02960 on_call_video_state(&call_info, mi, &has_error);
02961 break;
02962 default:
02963
02964 break;
02965 }
02966 }
02967
02968 if (has_error) {
02969 pj_str_t reason = pj_str("Media failed");
02970 pjsua_call_hangup(call_id, 500, &reason, NULL);
02971 }
02972
02973 #if PJSUA_HAS_VIDEO
02974
02975 if (call_info.rem_offerer && call_info.rem_vid_cnt)
02976 {
02977 int vid_idx;
02978
02979
02980 vid_idx = pjsua_call_get_vid_stream_idx(call_id);
02981 if (vid_idx == -1 || call_info.media[vid_idx].dir == PJMEDIA_DIR_NONE) {
02982 PJ_LOG(3,(THIS_FILE,
02983 "Just rejected incoming video offer on call %d, "
02984 "use \"vid call enable %d\" or \"vid call add\" to enable video!",
02985 call_id, vid_idx));
02986 }
02987 }
02988 #endif
02989 }
02990
02991
02992
02993
02994 static void call_on_dtmf_callback(pjsua_call_id call_id, int dtmf)
02995 {
02996 PJ_LOG(3,(THIS_FILE, "Incoming DTMF on call %d: %c", call_id, dtmf));
02997 }
02998
02999
03000
03001
03002 static pjsip_redirect_op call_on_redirected(pjsua_call_id call_id,
03003 const pjsip_uri *target,
03004 const pjsip_event *e)
03005 {
03006 PJ_UNUSED_ARG(e);
03007
03008 if (app_config.redir_op == PJSIP_REDIRECT_PENDING) {
03009 char uristr[PJSIP_MAX_URL_SIZE];
03010 int len;
03011
03012 len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, target, uristr,
03013 sizeof(uristr));
03014 if (len < 1) {
03015 pj_ansi_strcpy(uristr, "--URI too long--");
03016 }
03017
03018 PJ_LOG(3,(THIS_FILE, "Call %d is being redirected to %.*s. "
03019 "Press 'Ra' to accept, 'Rr' to reject, or 'Rd' to "
03020 "disconnect.",
03021 call_id, len, uristr));
03022 }
03023
03024 return app_config.redir_op;
03025 }
03026
03027
03028
03029
03030 static void on_reg_state(pjsua_acc_id acc_id)
03031 {
03032 PJ_UNUSED_ARG(acc_id);
03033
03034
03035 }
03036
03037
03038
03039
03040
03041 static void on_incoming_subscribe(pjsua_acc_id acc_id,
03042 pjsua_srv_pres *srv_pres,
03043 pjsua_buddy_id buddy_id,
03044 const pj_str_t *from,
03045 pjsip_rx_data *rdata,
03046 pjsip_status_code *code,
03047 pj_str_t *reason,
03048 pjsua_msg_data *msg_data)
03049 {
03050
03051 PJ_UNUSED_ARG(acc_id);
03052 PJ_UNUSED_ARG(srv_pres);
03053 PJ_UNUSED_ARG(buddy_id);
03054 PJ_UNUSED_ARG(from);
03055 PJ_UNUSED_ARG(rdata);
03056 PJ_UNUSED_ARG(code);
03057 PJ_UNUSED_ARG(reason);
03058 PJ_UNUSED_ARG(msg_data);
03059 }
03060
03061
03062
03063
03064
03065 static void on_buddy_state(pjsua_buddy_id buddy_id)
03066 {
03067 pjsua_buddy_info info;
03068 pjsua_buddy_get_info(buddy_id, &info);
03069
03070 PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s, subscription state is %s "
03071 "(last termination reason code=%d %.*s)",
03072 (int)info.uri.slen,
03073 info.uri.ptr,
03074 (int)info.status_text.slen,
03075 info.status_text.ptr,
03076 info.sub_state_name,
03077 info.sub_term_code,
03078 (int)info.sub_term_reason.slen,
03079 info.sub_term_reason.ptr));
03080 }
03081
03082
03083
03084
03085
03086 static void on_buddy_evsub_state(pjsua_buddy_id buddy_id,
03087 pjsip_evsub *sub,
03088 pjsip_event *event)
03089 {
03090 char event_info[80];
03091
03092 PJ_UNUSED_ARG(sub);
03093
03094 event_info[0] = '\0';
03095
03096 if (event->type == PJSIP_EVENT_TSX_STATE &&
03097 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
03098 {
03099 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
03100 snprintf(event_info, sizeof(event_info),
03101 " (RX %s)",
03102 pjsip_rx_data_get_info(rdata));
03103 }
03104
03105 PJ_LOG(4,(THIS_FILE,
03106 "Buddy %d: subscription state: %s (event: %s%s)",
03107 buddy_id, pjsip_evsub_get_state_name(sub),
03108 pjsip_event_str(event->type),
03109 event_info));
03110
03111 }
03112
03113
03117 static void on_pager(pjsua_call_id call_id, const pj_str_t *from,
03118 const pj_str_t *to, const pj_str_t *contact,
03119 const pj_str_t *mime_type, const pj_str_t *text)
03120 {
03121
03122 PJ_UNUSED_ARG(call_id);
03123 PJ_UNUSED_ARG(to);
03124 PJ_UNUSED_ARG(contact);
03125 PJ_UNUSED_ARG(mime_type);
03126
03127 PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s (%.*s)",
03128 (int)from->slen, from->ptr,
03129 (int)text->slen, text->ptr,
03130 (int)mime_type->slen, mime_type->ptr));
03131 }
03132
03133
03137 static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
03138 const pj_str_t *to, const pj_str_t *contact,
03139 pj_bool_t is_typing)
03140 {
03141 PJ_UNUSED_ARG(call_id);
03142 PJ_UNUSED_ARG(to);
03143 PJ_UNUSED_ARG(contact);
03144
03145 PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
03146 (int)from->slen, from->ptr,
03147 (is_typing?"is typing..":"has stopped typing")));
03148 }
03149
03150
03154 static void on_call_transfer_status(pjsua_call_id call_id,
03155 int status_code,
03156 const pj_str_t *status_text,
03157 pj_bool_t final,
03158 pj_bool_t *p_cont)
03159 {
03160 PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
03161 call_id, status_code,
03162 (int)status_text->slen, status_text->ptr,
03163 (final ? "[final]" : "")));
03164
03165 if (status_code/100 == 2) {
03166 PJ_LOG(3,(THIS_FILE,
03167 "Call %d: call transfered successfully, disconnecting call",
03168 call_id));
03169 pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
03170 *p_cont = PJ_FALSE;
03171 }
03172 }
03173
03174
03175
03176
03177
03178 static void on_call_replaced(pjsua_call_id old_call_id,
03179 pjsua_call_id new_call_id)
03180 {
03181 pjsua_call_info old_ci, new_ci;
03182
03183 pjsua_call_get_info(old_call_id, &old_ci);
03184 pjsua_call_get_info(new_call_id, &new_ci);
03185
03186 PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by "
03187 "call %d with %.*s",
03188 old_call_id,
03189 (int)old_ci.remote_info.slen, old_ci.remote_info.ptr,
03190 new_call_id,
03191 (int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
03192 }
03193
03194
03195
03196
03197
03198 static void on_nat_detect(const pj_stun_nat_detect_result *res)
03199 {
03200 if (res->status != PJ_SUCCESS) {
03201 pjsua_perror(THIS_FILE, "NAT detection failed", res->status);
03202 } else {
03203 PJ_LOG(3, (THIS_FILE, "NAT detected as %s", res->nat_type_name));
03204 }
03205 }
03206
03207
03208
03209
03210
03211 static void on_mwi_info(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info)
03212 {
03213 pj_str_t body;
03214
03215 PJ_LOG(3,(THIS_FILE, "Received MWI for acc %d:", acc_id));
03216
03217 if (mwi_info->rdata->msg_info.ctype) {
03218 const pjsip_ctype_hdr *ctype = mwi_info->rdata->msg_info.ctype;
03219
03220 PJ_LOG(3,(THIS_FILE, " Content-Type: %.*s/%.*s",
03221 (int)ctype->media.type.slen,
03222 ctype->media.type.ptr,
03223 (int)ctype->media.subtype.slen,
03224 ctype->media.subtype.ptr));
03225 }
03226
03227 if (!mwi_info->rdata->msg_info.msg->body) {
03228 PJ_LOG(3,(THIS_FILE, " no message body"));
03229 return;
03230 }
03231
03232 body.ptr = mwi_info->rdata->msg_info.msg->body->data;
03233 body.slen = mwi_info->rdata->msg_info.msg->body->len;
03234
03235 PJ_LOG(3,(THIS_FILE, " Body:\n%.*s", (int)body.slen, body.ptr));
03236 }
03237
03238
03239
03240
03241
03242 static void on_transport_state(pjsip_transport *tp,
03243 pjsip_transport_state state,
03244 const pjsip_transport_state_info *info)
03245 {
03246 char host_port[128];
03247
03248 pj_ansi_snprintf(host_port, sizeof(host_port), "[%.*s:%d]",
03249 (int)tp->remote_name.host.slen,
03250 tp->remote_name.host.ptr,
03251 tp->remote_name.port);
03252
03253 switch (state) {
03254 case PJSIP_TP_STATE_CONNECTED:
03255 {
03256 PJ_LOG(3,(THIS_FILE, "SIP %s transport is connected to %s",
03257 tp->type_name, host_port));
03258 }
03259 break;
03260
03261 case PJSIP_TP_STATE_DISCONNECTED:
03262 {
03263 char buf[100];
03264
03265 snprintf(buf, sizeof(buf), "SIP %s transport is disconnected from %s",
03266 tp->type_name, host_port);
03267 pjsua_perror(THIS_FILE, buf, info->status);
03268 }
03269 break;
03270
03271 default:
03272 break;
03273 }
03274
03275 #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
03276
03277 if (!pj_ansi_stricmp(tp->type_name, "tls") && info->ext_info &&
03278 (state == PJSIP_TP_STATE_CONNECTED ||
03279 ((pjsip_tls_state_info*)info->ext_info)->
03280 ssl_sock_info->verify_status != PJ_SUCCESS))
03281 {
03282 pjsip_tls_state_info *tls_info = (pjsip_tls_state_info*)info->ext_info;
03283 pj_ssl_sock_info *ssl_sock_info = tls_info->ssl_sock_info;
03284 char buf[2048];
03285 const char *verif_msgs[32];
03286 unsigned verif_msg_cnt;
03287
03288
03289 PJ_LOG(4,(THIS_FILE, "TLS cipher used: 0x%06X/%s",
03290 ssl_sock_info->cipher,
03291 pj_ssl_cipher_name(ssl_sock_info->cipher) ));
03292
03293
03294 pj_ssl_cert_info_dump(ssl_sock_info->remote_cert_info, " ",
03295 buf, sizeof(buf));
03296 PJ_LOG(4,(THIS_FILE, "TLS cert info of %s:\n%s", host_port, buf));
03297
03298
03299 verif_msg_cnt = PJ_ARRAY_SIZE(verif_msgs);
03300 pj_ssl_cert_get_verify_status_strings(ssl_sock_info->verify_status,
03301 verif_msgs, &verif_msg_cnt);
03302 PJ_LOG(3,(THIS_FILE, "TLS cert verification result of %s : %s",
03303 host_port,
03304 (verif_msg_cnt == 1? verif_msgs[0]:"")));
03305 if (verif_msg_cnt > 1) {
03306 unsigned i;
03307 for (i = 0; i < verif_msg_cnt; ++i)
03308 PJ_LOG(3,(THIS_FILE, "- %s", verif_msgs[i]));
03309 }
03310
03311 if (ssl_sock_info->verify_status &&
03312 !app_config.udp_cfg.tls_setting.verify_server)
03313 {
03314 PJ_LOG(3,(THIS_FILE, "PJSUA is configured to ignore TLS cert "
03315 "verification errors"));
03316 }
03317 }
03318
03319 #endif
03320
03321 }
03322
03323
03324
03325
03326 static void on_ice_transport_error(int index, pj_ice_strans_op op,
03327 pj_status_t status, void *param)
03328 {
03329 PJ_UNUSED_ARG(op);
03330 PJ_UNUSED_ARG(param);
03331 PJ_PERROR(1,(THIS_FILE, status,
03332 "ICE keep alive failure for transport %d", index));
03333 }
03334
03335
03336
03337
03338 static pj_status_t on_snd_dev_operation(int operation)
03339 {
03340 PJ_LOG(3,(THIS_FILE, "Turning sound device %s", (operation? "ON":"OFF")));
03341 return PJ_SUCCESS;
03342 }
03343
03344
03345 static void on_call_media_event(pjsua_call_id call_id,
03346 unsigned med_idx,
03347 pjmedia_event *event)
03348 {
03349 char event_name[5];
03350
03351 PJ_LOG(5,(THIS_FILE, "Event %s",
03352 pjmedia_fourcc_name(event->type, event_name)));
03353
03354 #if PJSUA_HAS_VIDEO
03355 if (event->type == PJMEDIA_EVENT_FMT_CHANGED) {
03356
03357 pjsua_call_info ci;
03358 pjsua_vid_win_id wid;
03359 pjmedia_rect_size size;
03360
03361 pjsua_call_get_info(call_id, &ci);
03362
03363 if ((ci.media[med_idx].type == PJMEDIA_TYPE_VIDEO) &&
03364 (ci.media[med_idx].dir & PJMEDIA_DIR_DECODING))
03365 {
03366 wid = ci.media[med_idx].stream.vid.win_in;
03367 size = event->data.fmt_changed.new_fmt.det.vid.size;
03368 pjsua_vid_win_set_size(wid, &size);
03369 }
03370
03371
03372 arrange_window(PJSUA_INVALID_ID);
03373 }
03374 #else
03375 PJ_UNUSED_ARG(call_id);
03376 PJ_UNUSED_ARG(med_idx);
03377 PJ_UNUSED_ARG(event);
03378 #endif
03379 }
03380
03381 #ifdef TRANSPORT_ADAPTER_SAMPLE
03382
03383
03384
03385 static pjmedia_transport* on_create_media_transport(pjsua_call_id call_id,
03386 unsigned media_idx,
03387 pjmedia_transport *base_tp,
03388 unsigned flags)
03389 {
03390 pjmedia_transport *adapter;
03391 pj_status_t status;
03392
03393
03394 status = pjmedia_tp_adapter_create(pjsua_get_pjmedia_endpt(),
03395 NULL, base_tp,
03396 (flags & PJSUA_MED_TP_CLOSE_MEMBER),
03397 &adapter);
03398 if (status != PJ_SUCCESS) {
03399 PJ_PERROR(1,(THIS_FILE, status, "Error creating adapter"));
03400 return NULL;
03401 }
03402
03403 PJ_LOG(3,(THIS_FILE, "Media transport is created for call %d media %d",
03404 call_id, media_idx));
03405
03406 return adapter;
03407 }
03408 #endif
03409
03410
03411
03412
03413 static void print_buddy_list(void)
03414 {
03415 pjsua_buddy_id ids[64];
03416 int i;
03417 unsigned count = PJ_ARRAY_SIZE(ids);
03418
03419 puts("Buddy list:");
03420
03421 pjsua_enum_buddies(ids, &count);
03422
03423 if (count == 0)
03424 puts(" -none-");
03425 else {
03426 for (i=0; i<(int)count; ++i) {
03427 pjsua_buddy_info info;
03428
03429 if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
03430 continue;
03431
03432 printf(" [%2d] <%.*s> %.*s\n",
03433 ids[i]+1,
03434 (int)info.status_text.slen,
03435 info.status_text.ptr,
03436 (int)info.uri.slen,
03437 info.uri.ptr);
03438 }
03439 }
03440 puts("");
03441 }
03442
03443
03444
03445
03446
03447 static void print_acc_status(int acc_id)
03448 {
03449 char buf[80];
03450 pjsua_acc_info info;
03451
03452 pjsua_acc_get_info(acc_id, &info);
03453
03454 if (!info.has_registration) {
03455 pj_ansi_snprintf(buf, sizeof(buf), "%.*s",
03456 (int)info.status_text.slen,
03457 info.status_text.ptr);
03458
03459 } else {
03460 pj_ansi_snprintf(buf, sizeof(buf),
03461 "%d/%.*s (expires=%d)",
03462 info.status,
03463 (int)info.status_text.slen,
03464 info.status_text.ptr,
03465 info.expires);
03466
03467 }
03468
03469 printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
03470 acc_id, (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
03471 printf(" Online status: %.*s\n",
03472 (int)info.online_status_text.slen,
03473 info.online_status_text.ptr);
03474 }
03475
03476
03477 pj_status_t on_playfile_done(pjmedia_port *port, void *usr_data)
03478 {
03479 pj_time_val delay;
03480
03481 PJ_UNUSED_ARG(port);
03482 PJ_UNUSED_ARG(usr_data);
03483
03484
03485 if (pjsua_call_get_count() == 0) {
03486 pjsua_player_set_pos(app_config.wav_id, 0);
03487 return PJ_SUCCESS;
03488 }
03489
03490
03491 if (app_config.auto_hangup_timer.id == 1)
03492 return PJ_SUCCESS;
03493
03494 app_config.auto_hangup_timer.id = 1;
03495 delay.sec = 0;
03496 delay.msec = 200;
03497 pjsip_endpt_schedule_timer(pjsua_get_pjsip_endpt(),
03498 &app_config.auto_hangup_timer,
03499 &delay);
03500
03501 return PJ_SUCCESS;
03502 }
03503
03504
03505 static void hangup_timeout_callback(pj_timer_heap_t *timer_heap,
03506 struct pj_timer_entry *entry)
03507 {
03508 PJ_UNUSED_ARG(timer_heap);
03509 PJ_UNUSED_ARG(entry);
03510
03511 app_config.auto_hangup_timer.id = 0;
03512 pjsua_call_hangup_all();
03513 }
03514
03515
03516
03517
03518 static void keystroke_help(void)
03519 {
03520 pjsua_acc_id acc_ids[16];
03521 unsigned count = PJ_ARRAY_SIZE(acc_ids);
03522 int i;
03523
03524 printf(">>>>\n");
03525
03526 pjsua_enum_accs(acc_ids, &count);
03527
03528 printf("Account list:\n");
03529 for (i=0; i<(int)count; ++i)
03530 print_acc_status(acc_ids[i]);
03531
03532 print_buddy_list();
03533
03534
03535 puts("+=============================================================================+");
03536 puts("| Call Commands: | Buddy, IM & Presence: | Account: |");
03537 puts("| | | |");
03538 puts("| m Make new call | +b Add new buddy .| +a Add new accnt |");
03539 puts("| M Make multiple calls | -b Delete buddy | -a Delete accnt. |");
03540 puts("| a Answer call | i Send IM | !a Modify accnt. |");
03541 puts("| h Hangup call (ha=all) | s Subscribe presence | rr (Re-)register |");
03542 puts("| H Hold call | u Unsubscribe presence | ru Unregister |");
03543 puts("| v re-inVite (release hold) | t ToGgle Online status | > Cycle next ac.|");
03544 puts("| U send UPDATE | T Set online status | < Cycle prev ac.|");
03545 puts("| ],[ Select next/prev call +--------------------------+-------------------+");
03546 puts("| x Xfer call | Media Commands: | Status & Config: |");
03547 puts("| X Xfer with Replaces | | |");
03548 puts("| # Send RFC 2833 DTMF | cl List ports | d Dump status |");
03549 puts("| * Send DTMF with INFO | cc Connect port | dd Dump detailed |");
03550 puts("| dq Dump curr. call quality | cd Disconnect port | dc Dump config |");
03551 puts("| | V Adjust audio Volume | f Save config |");
03552 puts("| S Send arbitrary REQUEST | Cp Codec priorities | |");
03553 puts("+-----------------------------------------------------------------------------+");
03554 #if PJSUA_HAS_VIDEO
03555 puts("| Video: \"vid help\" for more info |");
03556 puts("+-----------------------------------------------------------------------------+");
03557 #endif
03558 puts("| q QUIT L ReLoad sleep MS echo [0|1|txt] n: detect NAT type |");
03559 puts("+=============================================================================+");
03560
03561 i = pjsua_call_get_count();
03562 printf("You have %d active call%s\n", i, (i>1?"s":""));
03563
03564 if (current_call != PJSUA_INVALID_ID) {
03565 pjsua_call_info ci;
03566 if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
03567 printf("Current call id=%d to %.*s [%.*s]\n", current_call,
03568 (int)ci.remote_info.slen, ci.remote_info.ptr,
03569 (int)ci.state_text.slen, ci.state_text.ptr);
03570 }
03571 }
03572
03573
03574 #if PJSUA_HAS_VIDEO
03575 static void vid_show_help(void)
03576 {
03577 pj_bool_t vid_enabled = (app_config.vid.vid_cnt > 0);
03578
03579 puts("+=============================================================================+");
03580 puts("| Video commands: |");
03581 puts("| |");
03582 puts("| vid help Show this help screen |");
03583 puts("| vid enable|disable Enable or disable video in next offer/answer |");
03584 puts("| vid acc show Show current account video settings |");
03585 puts("| vid acc autorx on|off Automatically show incoming video on/off |");
03586 puts("| vid acc autotx on|off Automatically offer video on/off |");
03587 puts("| vid acc cap ID Set default capture device for current acc |");
03588 puts("| vid acc rend ID Set default renderer device for current acc |");
03589 puts("| vid call rx on|off N Enable/disable video RX for stream N in curr call |");
03590 puts("| vid call tx on|off N Enable/disable video TX for stream N in curr call |");
03591 puts("| vid call add Add video stream for current call |");
03592 puts("| vid call enable|disable N Enable/disable stream #N in current call |");
03593 puts("| vid call cap N ID Set capture dev ID for stream #N in current call |");
03594 puts("| vid dev list List all video devices |");
03595 puts("| vid dev refresh Refresh video device list |");
03596 puts("| vid dev prev on|off ID Enable/disable preview for specified device ID |");
03597 puts("| vid codec list List video codecs |");
03598 puts("| vid codec prio ID PRIO Set codec ID priority to PRIO |");
03599 puts("| vid codec fps ID NUM DEN Set codec ID framerate to (NUM/DEN) fps |");
03600 puts("| vid codec bw ID AVG MAX Set codec ID bitrate to AVG & MAX kbps |");
03601 puts("| vid codec size ID W H Set codec ID size/resolution to W x H |");
03602 puts("| vid win list List all active video windows |");
03603 puts("| vid win arrange Auto arrange windows |");
03604 puts("| vid win show|hide ID Show/hide the specified video window ID |");
03605 puts("| vid win move ID X Y Move window ID to position X,Y |");
03606 puts("| vid win resize ID w h Resize window ID to the specified width, height |");
03607 puts("+=============================================================================+");
03608 printf("| Video will be %s in the next offer/answer %s |\n",
03609 (vid_enabled? "enabled" : "disabled"), (vid_enabled? " " : ""));
03610 puts("+=============================================================================+");
03611 }
03612 #endif
03613
03614
03615
03616
03617 static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
03618 {
03619 char *p;
03620
03621 printf("%s (empty to cancel): ", title); fflush(stdout);
03622 if (fgets(buf, len, stdin) == NULL)
03623 return PJ_FALSE;
03624
03625
03626 for (p=buf; ; ++p) {
03627 if (*p=='\r' || *p=='\n') *p='\0';
03628 else if (!*p) break;
03629 }
03630
03631 if (!*buf)
03632 return PJ_FALSE;
03633
03634 return PJ_TRUE;
03635 }
03636
03637
03638 #define NO_NB -2
03639 struct input_result
03640 {
03641 int nb_result;
03642 char *uri_result;
03643 };
03644
03645
03646
03647
03648
03649 static void ui_input_url(const char *title, char *buf, int len,
03650 struct input_result *result)
03651 {
03652 result->nb_result = NO_NB;
03653 result->uri_result = NULL;
03654
03655 print_buddy_list();
03656
03657 printf("Choices:\n"
03658 " 0 For current dialog.\n"
03659 " -1 All %d buddies in buddy list\n"
03660 " [1 -%2d] Select from buddy list\n"
03661 " URL An URL\n"
03662 " <Enter> Empty input (or 'q') to cancel\n"
03663 , pjsua_get_buddy_count(), pjsua_get_buddy_count());
03664 printf("%s: ", title);
03665
03666 fflush(stdout);
03667 if (fgets(buf, len, stdin) == NULL)
03668 return;
03669 len = strlen(buf);
03670
03671
03672 while (pj_isspace(*buf)) {
03673 ++buf;
03674 --len;
03675 }
03676
03677
03678 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
03679 buf[--len] = '\0';
03680
03681 if (len == 0 || buf[0]=='q')
03682 return;
03683
03684 if (pj_isdigit(*buf) || *buf=='-') {
03685
03686 int i;
03687
03688 if (*buf=='-')
03689 i = 1;
03690 else
03691 i = 0;
03692
03693 for (; i<len; ++i) {
03694 if (!pj_isdigit(buf[i])) {
03695 puts("Invalid input");
03696 return;
03697 }
03698 }
03699
03700 result->nb_result = my_atoi(buf);
03701
03702 if (result->nb_result >= 0 &&
03703 result->nb_result <= (int)pjsua_get_buddy_count())
03704 {
03705 return;
03706 }
03707 if (result->nb_result == -1)
03708 return;
03709
03710 puts("Invalid input");
03711 result->nb_result = NO_NB;
03712 return;
03713
03714 } else {
03715 pj_status_t status;
03716
03717 if ((status=pjsua_verify_url(buf)) != PJ_SUCCESS) {
03718 pjsua_perror(THIS_FILE, "Invalid URL", status);
03719 return;
03720 }
03721
03722 result->uri_result = buf;
03723 }
03724 }
03725
03726
03727
03728
03729 static void conf_list(void)
03730 {
03731 unsigned i, count;
03732 pjsua_conf_port_id id[PJSUA_MAX_CALLS];
03733
03734 printf("Conference ports:\n");
03735
03736 count = PJ_ARRAY_SIZE(id);
03737 pjsua_enum_conf_ports(id, &count);
03738
03739 for (i=0; i<count; ++i) {
03740 char txlist[PJSUA_MAX_CALLS*4+10];
03741 unsigned j;
03742 pjsua_conf_port_info info;
03743
03744 pjsua_conf_get_port_info(id[i], &info);
03745
03746 txlist[0] = '\0';
03747 for (j=0; j<info.listener_cnt; ++j) {
03748 char s[10];
03749 pj_ansi_sprintf(s, "#%d ", info.listeners[j]);
03750 pj_ansi_strcat(txlist, s);
03751 }
03752 printf("Port #%02d[%2dKHz/%dms/%d] %20.*s transmitting to: %s\n",
03753 info.slot_id,
03754 info.clock_rate/1000,
03755 info.samples_per_frame*1000/info.channel_count/info.clock_rate,
03756 info.channel_count,
03757 (int)info.name.slen,
03758 info.name.ptr,
03759 txlist);
03760
03761 }
03762 puts("");
03763 }
03764
03765
03766
03767
03768
03769 static void send_request(char *cstr_method, const pj_str_t *dst_uri)
03770 {
03771 pj_str_t str_method;
03772 pjsip_method method;
03773 pjsip_tx_data *tdata;
03774 pjsip_endpoint *endpt;
03775 pj_status_t status;
03776
03777 endpt = pjsua_get_pjsip_endpt();
03778
03779 str_method = pj_str(cstr_method);
03780 pjsip_method_init_np(&method, &str_method);
03781
03782 status = pjsua_acc_create_request(current_acc, &method, dst_uri, &tdata);
03783
03784 status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL);
03785 if (status != PJ_SUCCESS) {
03786 pjsua_perror(THIS_FILE, "Unable to send request", status);
03787 return;
03788 }
03789 }
03790
03791
03792
03793
03794
03795 static void change_online_status(void)
03796 {
03797 char menuin[32];
03798 pj_bool_t online_status;
03799 pjrpid_element elem;
03800 int i, choice;
03801
03802 enum {
03803 AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX
03804 };
03805
03806 struct opt {
03807 int id;
03808 char *name;
03809 } opts[] = {
03810 { AVAILABLE, "Available" },
03811 { BUSY, "Busy"},
03812 { OTP, "On the phone"},
03813 { IDLE, "Idle"},
03814 { AWAY, "Away"},
03815 { BRB, "Be right back"},
03816 { OFFLINE, "Offline"}
03817 };
03818
03819 printf("\n"
03820 "Choices:\n");
03821 for (i=0; i<PJ_ARRAY_SIZE(opts); ++i) {
03822 printf(" %d %s\n", opts[i].id+1, opts[i].name);
03823 }
03824
03825 if (!simple_input("Select status", menuin, sizeof(menuin)))
03826 return;
03827
03828 choice = atoi(menuin) - 1;
03829 if (choice < 0 || choice >= OPT_MAX) {
03830 puts("Invalid selection");
03831 return;
03832 }
03833
03834 pj_bzero(&elem, sizeof(elem));
03835 elem.type = PJRPID_ELEMENT_TYPE_PERSON;
03836
03837 online_status = PJ_TRUE;
03838
03839 switch (choice) {
03840 case AVAILABLE:
03841 break;
03842 case BUSY:
03843 elem.activity = PJRPID_ACTIVITY_BUSY;
03844 elem.note = pj_str("Busy");
03845 break;
03846 case OTP:
03847 elem.activity = PJRPID_ACTIVITY_BUSY;
03848 elem.note = pj_str("On the phone");
03849 break;
03850 case IDLE:
03851 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
03852 elem.note = pj_str("Idle");
03853 break;
03854 case AWAY:
03855 elem.activity = PJRPID_ACTIVITY_AWAY;
03856 elem.note = pj_str("Away");
03857 break;
03858 case BRB:
03859 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
03860 elem.note = pj_str("Be right back");
03861 break;
03862 case OFFLINE:
03863 online_status = PJ_FALSE;
03864 break;
03865 }
03866
03867 pjsua_acc_set_online_status2(current_acc, online_status, &elem);
03868 }
03869
03870
03871
03872
03873
03874 static void manage_codec_prio(void)
03875 {
03876 pjsua_codec_info c[32];
03877 unsigned i, count = PJ_ARRAY_SIZE(c);
03878 char input[32];
03879 char *codec, *prio;
03880 pj_str_t id;
03881 int new_prio;
03882 pj_status_t status;
03883
03884 printf("List of audio codecs:\n");
03885 pjsua_enum_codecs(c, &count);
03886 for (i=0; i<count; ++i) {
03887 printf(" %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen,
03888 c[i].codec_id.ptr);
03889 }
03890
03891 #if PJSUA_HAS_VIDEO
03892 puts("");
03893 printf("List of video codecs:\n");
03894 pjsua_vid_enum_codecs(c, &count);
03895 for (i=0; i<count; ++i) {
03896 printf(" %d\t%.*s%s%.*s\n", c[i].priority,
03897 (int)c[i].codec_id.slen,
03898 c[i].codec_id.ptr,
03899 c[i].desc.slen? " - ":"",
03900 (int)c[i].desc.slen,
03901 c[i].desc.ptr);
03902 }
03903 #endif
03904
03905 puts("");
03906 puts("Enter codec id and its new priority (e.g. \"speex/16000 200\", ""\"H263 200\"),");
03907 puts("or empty to cancel.");
03908
03909 printf("Codec name (\"*\" for all) and priority: ");
03910 if (fgets(input, sizeof(input), stdin) == NULL)
03911 return;
03912 if (input[0]=='\r' || input[0]=='\n') {
03913 puts("Done");
03914 return;
03915 }
03916
03917 codec = strtok(input, " \t\r\n");
03918 prio = strtok(NULL, " \r\n");
03919
03920 if (!codec || !prio) {
03921 puts("Invalid input");
03922 return;
03923 }
03924
03925 new_prio = atoi(prio);
03926 if (new_prio < 0)
03927 new_prio = 0;
03928 else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST)
03929 new_prio = PJMEDIA_CODEC_PRIO_HIGHEST;
03930
03931 status = pjsua_codec_set_priority(pj_cstr(&id, codec),
03932 (pj_uint8_t)new_prio);
03933 #if PJSUA_HAS_VIDEO
03934 if (status != PJ_SUCCESS) {
03935 status = pjsua_vid_codec_set_priority(pj_cstr(&id, codec),
03936 (pj_uint8_t)new_prio);
03937 }
03938 #endif
03939 if (status != PJ_SUCCESS)
03940 pjsua_perror(THIS_FILE, "Error setting codec priority", status);
03941 }
03942
03943
03944 #if PJSUA_HAS_VIDEO
03945 static void vid_print_dev(int id, const pjmedia_vid_dev_info *vdi,
03946 const char *title)
03947 {
03948 char capnames[120];
03949 char formats[120];
03950 const char *dirname;
03951 unsigned i;
03952
03953 if (vdi->dir == PJMEDIA_DIR_CAPTURE_RENDER) {
03954 dirname = "capture, render";
03955 } else if (vdi->dir == PJMEDIA_DIR_CAPTURE) {
03956 dirname = "capture";
03957 } else {
03958 dirname = "render";
03959 }
03960
03961
03962 capnames[0] = '\0';
03963 for (i=0; i<sizeof(int)*8 && (1 << i) < PJMEDIA_VID_DEV_CAP_MAX; ++i) {
03964 if (vdi->caps & (1 << i)) {
03965 const char *capname = pjmedia_vid_dev_cap_name(1 << i, NULL);
03966 if (capname) {
03967 if (*capnames)
03968 strcat(capnames, ", ");
03969 strncat(capnames, capname,
03970 sizeof(capnames)-strlen(capnames)-1);
03971 }
03972 }
03973 }
03974
03975 formats[0] = '\0';
03976 for (i=0; i<vdi->fmt_cnt; ++i) {
03977 const pjmedia_video_format_info *vfi =
03978 pjmedia_get_video_format_info(NULL, vdi->fmt[i].id);
03979 if (vfi) {
03980 if (*formats)
03981 strcat(formats, ", ");
03982 strncat(formats, vfi->name, sizeof(formats)-strlen(formats)-1);
03983 }
03984 }
03985
03986 PJ_LOG(3,(THIS_FILE, "%3d %s [%s][%s] %s", id, vdi->name, vdi->driver,
03987 dirname, title));
03988 PJ_LOG(3,(THIS_FILE, " Supported capabilities: %s", capnames));
03989 PJ_LOG(3,(THIS_FILE, " Supported formats: %s", formats));
03990 }
03991
03992 static void vid_list_devs(void)
03993 {
03994 unsigned i, count;
03995 pjmedia_vid_dev_info vdi;
03996 pj_status_t status;
03997
03998 PJ_LOG(3,(THIS_FILE, "Video device list:"));
03999 count = pjsua_vid_dev_count();
04000 if (count == 0) {
04001 PJ_LOG(3,(THIS_FILE, " - no device detected -"));
04002 return;
04003 } else {
04004 PJ_LOG(3,(THIS_FILE, "%d device(s) detected:", count));
04005 }
04006
04007 status = pjsua_vid_dev_get_info(PJMEDIA_VID_DEFAULT_RENDER_DEV, &vdi);
04008 if (status == PJ_SUCCESS)
04009 vid_print_dev(PJMEDIA_VID_DEFAULT_RENDER_DEV, &vdi,
04010 "(default renderer device)");
04011
04012 status = pjsua_vid_dev_get_info(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &vdi);
04013 if (status == PJ_SUCCESS)
04014 vid_print_dev(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &vdi,
04015 "(default capture device)");
04016
04017 for (i=0; i<count; ++i) {
04018 status = pjsua_vid_dev_get_info(i, &vdi);
04019 if (status == PJ_SUCCESS)
04020 vid_print_dev(i, &vdi, "");
04021 }
04022 }
04023
04024 static void app_config_init_video(pjsua_acc_config *acc_cfg)
04025 {
04026 acc_cfg->vid_in_auto_show = app_config.vid.in_auto_show;
04027 acc_cfg->vid_out_auto_transmit = app_config.vid.out_auto_transmit;
04028
04029
04030
04031 acc_cfg->vid_wnd_flags = PJMEDIA_VID_DEV_WND_BORDER |
04032 PJMEDIA_VID_DEV_WND_RESIZABLE;
04033 acc_cfg->vid_cap_dev = app_config.vid.vcapture_dev;
04034 acc_cfg->vid_rend_dev = app_config.vid.vrender_dev;
04035
04036 if (app_config.avi_auto_play &&
04037 app_config.avi_def_idx != PJSUA_INVALID_ID &&
04038 app_config.avi[app_config.avi_def_idx].dev_id != PJMEDIA_VID_INVALID_DEV)
04039 {
04040 acc_cfg->vid_cap_dev = app_config.avi[app_config.avi_def_idx].dev_id;
04041 }
04042 }
04043
04044 static void app_config_show_video(int acc_id, const pjsua_acc_config *acc_cfg)
04045 {
04046 PJ_LOG(3,(THIS_FILE,
04047 "Account %d:\n"
04048 " RX auto show: %d\n"
04049 " TX auto transmit: %d\n"
04050 " Capture dev: %d\n"
04051 " Render dev: %d",
04052 acc_id,
04053 acc_cfg->vid_in_auto_show,
04054 acc_cfg->vid_out_auto_transmit,
04055 acc_cfg->vid_cap_dev,
04056 acc_cfg->vid_rend_dev));
04057 }
04058
04059 static void vid_handle_menu(char *menuin)
04060 {
04061 char *argv[8];
04062 int argc = 0;
04063
04064
04065 argv[argc] = strtok(menuin, " \t\r\n");
04066 while (argv[argc] && *argv[argc]) {
04067 argc++;
04068 argv[argc] = strtok(NULL, " \t\r\n");
04069 }
04070
04071 if (argc == 1 || strcmp(argv[1], "help")==0) {
04072 vid_show_help();
04073 } else if (argc == 2 && (strcmp(argv[1], "enable")==0 ||
04074 strcmp(argv[1], "disable")==0))
04075 {
04076 pj_bool_t enabled = (strcmp(argv[1], "enable")==0);
04077 app_config.vid.vid_cnt = (enabled ? 1 : 0);
04078 PJ_LOG(3,(THIS_FILE, "Video will be %s in next offer/answer",
04079 (enabled?"enabled":"disabled")));
04080 } else if (strcmp(argv[1], "acc")==0) {
04081 pjsua_acc_config acc_cfg;
04082 pj_bool_t changed = PJ_FALSE;
04083
04084 pjsua_acc_get_config(current_acc, &acc_cfg);
04085
04086 if (argc == 3 && strcmp(argv[2], "show")==0) {
04087 app_config_show_video(current_acc, &acc_cfg);
04088 } else if (argc == 4 && strcmp(argv[2], "autorx")==0) {
04089 int on = (strcmp(argv[3], "on")==0);
04090 acc_cfg.vid_in_auto_show = on;
04091 changed = PJ_TRUE;
04092 } else if (argc == 4 && strcmp(argv[2], "autotx")==0) {
04093 int on = (strcmp(argv[3], "on")==0);
04094 acc_cfg.vid_out_auto_transmit = on;
04095 changed = PJ_TRUE;
04096 } else if (argc == 4 && strcmp(argv[2], "cap")==0) {
04097 int dev = atoi(argv[3]);
04098 acc_cfg.vid_cap_dev = dev;
04099 changed = PJ_TRUE;
04100 } else if (argc == 4 && strcmp(argv[2], "rend")==0) {
04101 int dev = atoi(argv[3]);
04102 acc_cfg.vid_rend_dev = dev;
04103 changed = PJ_TRUE;
04104 } else {
04105 goto on_error;
04106 }
04107
04108 if (changed) {
04109 pj_status_t status = pjsua_acc_modify(current_acc, &acc_cfg);
04110 if (status != PJ_SUCCESS)
04111 PJ_PERROR(1,(THIS_FILE, status, "Error modifying account %d",
04112 current_acc));
04113 }
04114
04115 } else if (strcmp(argv[1], "call")==0) {
04116 pjsua_call_vid_strm_op_param param;
04117 pj_status_t status = PJ_SUCCESS;
04118
04119 pjsua_call_vid_strm_op_param_default(¶m);
04120
04121 if (argc == 5 && strcmp(argv[2], "rx")==0) {
04122 pjsua_stream_info si;
04123 pj_bool_t on = (strcmp(argv[3], "on") == 0);
04124
04125 param.med_idx = atoi(argv[4]);
04126 if (pjsua_call_get_stream_info(current_call, param.med_idx, &si) ||
04127 si.type != PJMEDIA_TYPE_VIDEO)
04128 {
04129 PJ_PERROR(1,(THIS_FILE, PJ_EINVAL, "Invalid stream"));
04130 return;
04131 }
04132
04133 if (on) param.dir = (si.info.vid.dir | PJMEDIA_DIR_DECODING);
04134 else param.dir = (si.info.vid.dir & PJMEDIA_DIR_ENCODING);
04135
04136 status = pjsua_call_set_vid_strm(current_call,
04137 PJSUA_CALL_VID_STRM_CHANGE_DIR,
04138 ¶m);
04139 }
04140 else if (argc == 5 && strcmp(argv[2], "tx")==0) {
04141 pj_bool_t on = (strcmp(argv[3], "on") == 0);
04142 pjsua_call_vid_strm_op op = on? PJSUA_CALL_VID_STRM_START_TRANSMIT :
04143 PJSUA_CALL_VID_STRM_STOP_TRANSMIT;
04144
04145 param.med_idx = atoi(argv[4]);
04146
04147 status = pjsua_call_set_vid_strm(current_call, op, ¶m);
04148 }
04149 else if (argc == 3 && strcmp(argv[2], "add")==0) {
04150 status = pjsua_call_set_vid_strm(current_call,
04151 PJSUA_CALL_VID_STRM_ADD, NULL);
04152 }
04153 else if (argc >= 3 &&
04154 (strcmp(argv[2], "disable")==0 || strcmp(argv[2], "enable")==0))
04155 {
04156 pj_bool_t enable = (strcmp(argv[2], "enable") == 0);
04157 pjsua_call_vid_strm_op op = enable? PJSUA_CALL_VID_STRM_CHANGE_DIR :
04158 PJSUA_CALL_VID_STRM_REMOVE;
04159
04160 param.med_idx = argc >= 4? atoi(argv[3]) : -1;
04161 param.dir = PJMEDIA_DIR_ENCODING_DECODING;
04162 status = pjsua_call_set_vid_strm(current_call, op, ¶m);
04163 }
04164 else if (argc >= 3 && strcmp(argv[2], "cap")==0) {
04165 param.med_idx = argc >= 4? atoi(argv[3]) : -1;
04166 param.cap_dev = argc >= 5? atoi(argv[4]) : PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
04167 status = pjsua_call_set_vid_strm(current_call,
04168 PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV,
04169 ¶m);
04170 } else
04171 goto on_error;
04172
04173 if (status != PJ_SUCCESS) {
04174 PJ_PERROR(1,(THIS_FILE, status, "Error modifying video stream"));
04175 }
04176
04177 } else if (argc >= 3 && strcmp(argv[1], "dev")==0) {
04178 if (strcmp(argv[2], "list")==0) {
04179 vid_list_devs();
04180 } else if (strcmp(argv[2], "refresh")==0) {
04181 pjmedia_vid_dev_refresh();
04182 } else if (strcmp(argv[2], "prev")==0) {
04183 if (argc != 5) {
04184 goto on_error;
04185 } else {
04186 pj_bool_t on = (strcmp(argv[3], "on") == 0);
04187 int dev_id = atoi(argv[4]);
04188 if (on) {
04189 pjsua_vid_preview_param param;
04190
04191 pjsua_vid_preview_param_default(¶m);
04192 param.wnd_flags = PJMEDIA_VID_DEV_WND_BORDER |
04193 PJMEDIA_VID_DEV_WND_RESIZABLE;
04194 pjsua_vid_preview_start(dev_id, ¶m);
04195 arrange_window(pjsua_vid_preview_get_win(dev_id));
04196 } else {
04197 pjsua_vid_win_id wid;
04198 wid = pjsua_vid_preview_get_win(dev_id);
04199 if (wid != PJSUA_INVALID_ID) {
04200
04201
04202 pjsua_vid_win_set_show(wid, PJ_FALSE);
04203 pjsua_vid_preview_stop(dev_id);
04204 }
04205 }
04206 }
04207 } else
04208 goto on_error;
04209 } else if (strcmp(argv[1], "win")==0) {
04210 pj_status_t status = PJ_SUCCESS;
04211
04212 if (argc==3 && strcmp(argv[2], "list")==0) {
04213 pjsua_vid_win_id wids[PJSUA_MAX_VID_WINS];
04214 unsigned i, cnt = PJ_ARRAY_SIZE(wids);
04215
04216 pjsua_vid_enum_wins(wids, &cnt);
04217
04218 PJ_LOG(3,(THIS_FILE, "Found %d video windows:", cnt));
04219 PJ_LOG(3,(THIS_FILE, "WID show pos size"));
04220 PJ_LOG(3,(THIS_FILE, "------------------------------"));
04221 for (i = 0; i < cnt; ++i) {
04222 pjsua_vid_win_info wi;
04223 pjsua_vid_win_get_info(wids[i], &wi);
04224 PJ_LOG(3,(THIS_FILE, "%3d %c (%d,%d) %dx%d",
04225 wids[i], (wi.show?'Y':'N'), wi.pos.x, wi.pos.y,
04226 wi.size.w, wi.size.h));
04227 }
04228 } else if (argc==4 && (strcmp(argv[2], "show")==0 ||
04229 strcmp(argv[2], "hide")==0))
04230 {
04231 pj_bool_t show = (strcmp(argv[2], "show")==0);
04232 pjsua_vid_win_id wid = atoi(argv[3]);
04233 status = pjsua_vid_win_set_show(wid, show);
04234 } else if (argc==6 && strcmp(argv[2], "move")==0) {
04235 pjsua_vid_win_id wid = atoi(argv[3]);
04236 pjmedia_coord pos;
04237
04238 pos.x = atoi(argv[4]);
04239 pos.y = atoi(argv[5]);
04240 status = pjsua_vid_win_set_pos(wid, &pos);
04241 } else if (argc==6 && strcmp(argv[2], "resize")==0) {
04242 pjsua_vid_win_id wid = atoi(argv[3]);
04243 pjmedia_rect_size size;
04244
04245 size.w = atoi(argv[4]);
04246 size.h = atoi(argv[5]);
04247 status = pjsua_vid_win_set_size(wid, &size);
04248 } else if (argc==3 && strcmp(argv[2], "arrange")==0) {
04249 arrange_window(PJSUA_INVALID_ID);
04250 } else
04251 goto on_error;
04252
04253 if (status != PJ_SUCCESS) {
04254 PJ_PERROR(1,(THIS_FILE, status, "Window operation error"));
04255 }
04256
04257 } else if (strcmp(argv[1], "codec")==0) {
04258 pjsua_codec_info ci[PJMEDIA_CODEC_MGR_MAX_CODECS];
04259 unsigned count = PJ_ARRAY_SIZE(ci);
04260 pj_status_t status;
04261
04262 if (argc==3 && strcmp(argv[2], "list")==0) {
04263 status = pjsua_vid_enum_codecs(ci, &count);
04264 if (status != PJ_SUCCESS) {
04265 PJ_PERROR(1,(THIS_FILE, status, "Error enumerating codecs"));
04266 } else {
04267 unsigned i;
04268 PJ_LOG(3,(THIS_FILE, "Found %d video codecs:", count));
04269 PJ_LOG(3,(THIS_FILE, "codec id prio fps bw(kbps) size"));
04270 PJ_LOG(3,(THIS_FILE, "------------------------------------------"));
04271 for (i=0; i<count; ++i) {
04272 pjmedia_vid_codec_param cp;
04273 pjmedia_video_format_detail *vfd;
04274
04275 status = pjsua_vid_codec_get_param(&ci[i].codec_id, &cp);
04276 if (status != PJ_SUCCESS)
04277 continue;
04278
04279 vfd = pjmedia_format_get_video_format_detail(&cp.enc_fmt,
04280 PJ_TRUE);
04281 PJ_LOG(3,(THIS_FILE, "%.*s%.*s %3d %7.2f %4d/%4d %dx%d",
04282 (int)ci[i].codec_id.slen, ci[i].codec_id.ptr,
04283 13-(int)ci[i].codec_id.slen, " ",
04284 ci[i].priority,
04285 (vfd->fps.num*1.0/vfd->fps.denum),
04286 vfd->avg_bps/1000, vfd->max_bps/1000,
04287 vfd->size.w, vfd->size.h));
04288 }
04289 }
04290 } else if (argc==5 && strcmp(argv[2], "prio")==0) {
04291 pj_str_t cid;
04292 int prio;
04293 cid = pj_str(argv[3]);
04294 prio = atoi(argv[4]);
04295 status = pjsua_vid_codec_set_priority(&cid, (pj_uint8_t)prio);
04296 if (status != PJ_SUCCESS)
04297 PJ_PERROR(1,(THIS_FILE, status, "Set codec priority error"));
04298 } else if (argc==6 && strcmp(argv[2], "fps")==0) {
04299 pjmedia_vid_codec_param cp;
04300 pj_str_t cid;
04301 int M, N;
04302 cid = pj_str(argv[3]);
04303 M = atoi(argv[4]);
04304 N = atoi(argv[5]);
04305 status = pjsua_vid_codec_get_param(&cid, &cp);
04306 if (status == PJ_SUCCESS) {
04307 cp.enc_fmt.det.vid.fps.num = M;
04308 cp.enc_fmt.det.vid.fps.denum = N;
04309 status = pjsua_vid_codec_set_param(&cid, &cp);
04310 }
04311 if (status != PJ_SUCCESS)
04312 PJ_PERROR(1,(THIS_FILE, status, "Set codec framerate error"));
04313 } else if (argc==6 && strcmp(argv[2], "bw")==0) {
04314 pjmedia_vid_codec_param cp;
04315 pj_str_t cid;
04316 int M, N;
04317 cid = pj_str(argv[3]);
04318 M = atoi(argv[4]);
04319 N = atoi(argv[5]);
04320 status = pjsua_vid_codec_get_param(&cid, &cp);
04321 if (status == PJ_SUCCESS) {
04322 cp.enc_fmt.det.vid.avg_bps = M * 1000;
04323 cp.enc_fmt.det.vid.max_bps = N * 1000;
04324 status = pjsua_vid_codec_set_param(&cid, &cp);
04325 }
04326 if (status != PJ_SUCCESS)
04327 PJ_PERROR(1,(THIS_FILE, status, "Set codec bitrate error"));
04328 } else if (argc==6 && strcmp(argv[2], "size")==0) {
04329 pjmedia_vid_codec_param cp;
04330 pj_str_t cid;
04331 int M, N;
04332 cid = pj_str(argv[3]);
04333 M = atoi(argv[4]);
04334 N = atoi(argv[5]);
04335 status = pjsua_vid_codec_get_param(&cid, &cp);
04336 if (status == PJ_SUCCESS) {
04337 cp.enc_fmt.det.vid.size.w = M;
04338 cp.enc_fmt.det.vid.size.h = N;
04339 status = pjsua_vid_codec_set_param(&cid, &cp);
04340 }
04341 if (status != PJ_SUCCESS)
04342 PJ_PERROR(1,(THIS_FILE, status, "Set codec bitrate error"));
04343 } else
04344 goto on_error;
04345 } else
04346 goto on_error;
04347
04348 return;
04349
04350 on_error:
04351 PJ_LOG(1,(THIS_FILE, "Invalid command, use 'vid help'"));
04352 }
04353
04354 #else
04355
04356 static void app_config_init_video(pjsua_acc_config *acc_cfg)
04357 {
04358 PJ_UNUSED_ARG(acc_cfg);
04359 }
04360
04361 #endif
04362
04363
04364
04365
04366
04367 void console_app_main(const pj_str_t *uri_to_call)
04368 {
04369 char menuin[32];
04370 char buf[128];
04371 char text[128];
04372 int i, count;
04373 char *uri;
04374 pj_str_t tmp;
04375 struct input_result result;
04376 pjsua_msg_data msg_data;
04377 pjsua_call_info call_info;
04378 pjsua_acc_info acc_info;
04379 pjsua_call_setting call_opt;
04380
04381 pjsua_call_setting_default(&call_opt);
04382 call_opt.aud_cnt = app_config.aud_cnt;
04383 call_opt.vid_cnt = app_config.vid.vid_cnt;
04384
04385
04386 if (uri_to_call->slen) {
04387 pjsua_call_make_call( current_acc, uri_to_call, &call_opt, NULL, NULL, NULL);
04388 }
04389
04390 keystroke_help();
04391
04392 for (;;) {
04393
04394 printf(">>> ");
04395 fflush(stdout);
04396
04397 if (fgets(menuin, sizeof(menuin), stdin) == NULL) {
04398
04399
04400
04401
04402
04403
04404 #if defined(PJ_WIN32) && PJ_WIN32!=0
04405 if (freopen ("CONIN$", "r", stdin) == NULL) {
04406 #else
04407 if (1) {
04408 #endif
04409 puts("Cannot switch back to console from file redirection");
04410 menuin[0] = 'q';
04411 menuin[1] = '\0';
04412 } else {
04413 puts("Switched back to console from file redirection");
04414 continue;
04415 }
04416 }
04417
04418 if (cmd_echo) {
04419 printf("%s", menuin);
04420 }
04421
04422
04423 pjsua_call_setting_default(&call_opt);
04424 call_opt.aud_cnt = app_config.aud_cnt;
04425 call_opt.vid_cnt = app_config.vid.vid_cnt;
04426
04427 switch (menuin[0]) {
04428
04429 case 'm':
04430
04431 printf("(You currently have %d calls)\n",
04432 pjsua_call_get_count());
04433
04434 uri = NULL;
04435 ui_input_url("Make call", buf, sizeof(buf), &result);
04436 if (result.nb_result != NO_NB) {
04437
04438 if (result.nb_result == -1 || result.nb_result == 0) {
04439 puts("You can't do that with make call!");
04440 continue;
04441 } else {
04442 pjsua_buddy_info binfo;
04443 pjsua_buddy_get_info(result.nb_result-1, &binfo);
04444 tmp.ptr = buf;
04445 pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
04446 }
04447
04448 } else if (result.uri_result) {
04449 tmp = pj_str(result.uri_result);
04450 } else {
04451 tmp.slen = 0;
04452 }
04453
04454 pjsua_msg_data_init(&msg_data);
04455 TEST_MULTIPART(&msg_data);
04456 pjsua_call_make_call( current_acc, &tmp, &call_opt, NULL, &msg_data, NULL);
04457 break;
04458
04459 case 'M':
04460
04461 printf("(You currently have %d calls)\n",
04462 pjsua_call_get_count());
04463
04464 if (!simple_input("Number of calls", menuin, sizeof(menuin)))
04465 continue;
04466
04467 count = my_atoi(menuin);
04468 if (count < 1)
04469 continue;
04470
04471 ui_input_url("Make call", buf, sizeof(buf), &result);
04472 if (result.nb_result != NO_NB) {
04473 pjsua_buddy_info binfo;
04474 if (result.nb_result == -1 || result.nb_result == 0) {
04475 puts("You can't do that with make call!");
04476 continue;
04477 }
04478 pjsua_buddy_get_info(result.nb_result-1, &binfo);
04479 tmp.ptr = buf;
04480 pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
04481 } else {
04482 tmp = pj_str(result.uri_result);
04483 }
04484
04485 for (i=0; i<my_atoi(menuin); ++i) {
04486 pj_status_t status;
04487
04488 status = pjsua_call_make_call(current_acc, &tmp, &call_opt, NULL,
04489 NULL, NULL);
04490 if (status != PJ_SUCCESS)
04491 break;
04492 }
04493 break;
04494
04495 case 'n':
04496 i = pjsua_detect_nat_type();
04497 if (i != PJ_SUCCESS)
04498 pjsua_perror(THIS_FILE, "Error", i);
04499 break;
04500
04501 case 'i':
04502
04503
04504
04505 i = -1;
04506
04507
04508 uri = NULL;
04509
04510
04511 ui_input_url("Send IM to", buf, sizeof(buf), &result);
04512 if (result.nb_result != NO_NB) {
04513
04514 if (result.nb_result == -1) {
04515 puts("You can't send broadcast IM like that!");
04516 continue;
04517
04518 } else if (result.nb_result == 0) {
04519
04520 i = current_call;
04521
04522 } else {
04523 pjsua_buddy_info binfo;
04524 pjsua_buddy_get_info(result.nb_result-1, &binfo);
04525 tmp.ptr = buf;
04526 pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
04527 uri = buf;
04528 }
04529
04530 } else if (result.uri_result) {
04531 uri = result.uri_result;
04532 }
04533
04534
04535
04536 if (i != -1)
04537 pjsua_call_send_typing_ind(i, PJ_TRUE, NULL);
04538 else {
04539 pj_str_t tmp_uri = pj_str(uri);
04540 pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL);
04541 }
04542
04543
04544 if (!simple_input("Message", text, sizeof(text))) {
04545
04546
04547
04548
04549 if (i != -1)
04550 pjsua_call_send_typing_ind(i, PJ_FALSE, NULL);
04551 else {
04552 pj_str_t tmp_uri = pj_str(uri);
04553 pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL);
04554 }
04555 continue;
04556 }
04557
04558 tmp = pj_str(text);
04559
04560
04561 if (i != -1)
04562 pjsua_call_send_im(i, NULL, &tmp, NULL, NULL);
04563 else {
04564 pj_str_t tmp_uri = pj_str(uri);
04565 pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL);
04566 }
04567
04568 break;
04569
04570 case 'a':
04571
04572 if (current_call != -1) {
04573 pjsua_call_get_info(current_call, &call_info);
04574 } else {
04575
04576 call_info.role = PJSIP_ROLE_UAC;
04577 call_info.state = PJSIP_INV_STATE_DISCONNECTED;
04578 }
04579
04580 if (current_call == -1 ||
04581 call_info.role != PJSIP_ROLE_UAS ||
04582 call_info.state >= PJSIP_INV_STATE_CONNECTING)
04583 {
04584 puts("No pending incoming call");
04585 fflush(stdout);
04586 continue;
04587
04588 } else {
04589 int st_code;
04590 char contact[120];
04591 pj_str_t hname = { "Contact", 7 };
04592 pj_str_t hvalue;
04593 pjsip_generic_string_hdr hcontact;
04594
04595 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
04596 continue;
04597
04598 st_code = my_atoi(buf);
04599 if (st_code < 100)
04600 continue;
04601
04602 pjsua_msg_data_init(&msg_data);
04603
04604 if (st_code/100 == 3) {
04605 if (!simple_input("Enter URL to be put in Contact",
04606 contact, sizeof(contact)))
04607 continue;
04608 hvalue = pj_str(contact);
04609 pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue);
04610
04611 pj_list_push_back(&msg_data.hdr_list, &hcontact);
04612 }
04613
04614
04615
04616
04617
04618
04619 if (current_call == -1) {
04620 puts("Call has been disconnected");
04621 fflush(stdout);
04622 continue;
04623 }
04624
04625 pjsua_call_answer2(current_call, &call_opt, st_code, NULL, &msg_data);
04626 }
04627
04628 break;
04629
04630
04631 case 'h':
04632
04633 if (current_call == -1) {
04634 puts("No current call");
04635 fflush(stdout);
04636 continue;
04637
04638 } else if (menuin[1] == 'a') {
04639
04640
04641 pjsua_call_hangup_all();
04642
04643 } else {
04644
04645
04646 pjsua_call_hangup(current_call, 0, NULL, NULL);
04647 }
04648 break;
04649
04650 case ']':
04651 case '[':
04652
04653
04654
04655 if (menuin[0] == ']') {
04656 find_next_call();
04657
04658 } else {
04659 find_prev_call();
04660 }
04661
04662 if (current_call != -1) {
04663
04664 pjsua_call_get_info(current_call, &call_info);
04665 PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s",
04666 (int)call_info.remote_info.slen,
04667 call_info.remote_info.ptr));
04668
04669 } else {
04670 PJ_LOG(3,(THIS_FILE,"No current dialog"));
04671 }
04672 break;
04673
04674
04675 case '>':
04676 case '<':
04677 if (!simple_input("Enter account ID to select", buf, sizeof(buf)))
04678 break;
04679
04680 i = my_atoi(buf);
04681 if (pjsua_acc_is_valid(i)) {
04682 pjsua_acc_set_default(i);
04683 PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i));
04684 } else {
04685 PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i));
04686 }
04687 break;
04688
04689
04690 case '+':
04691 if (menuin[1] == 'b') {
04692
04693 pjsua_buddy_config buddy_cfg;
04694 pjsua_buddy_id buddy_id;
04695 pj_status_t status;
04696
04697 if (!simple_input("Enter buddy's URI:", buf, sizeof(buf)))
04698 break;
04699
04700 if (pjsua_verify_url(buf) != PJ_SUCCESS) {
04701 printf("Invalid URI '%s'\n", buf);
04702 break;
04703 }
04704
04705 pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config));
04706
04707 buddy_cfg.uri = pj_str(buf);
04708 buddy_cfg.subscribe = PJ_TRUE;
04709
04710 status = pjsua_buddy_add(&buddy_cfg, &buddy_id);
04711 if (status == PJ_SUCCESS) {
04712 printf("New buddy '%s' added at index %d\n",
04713 buf, buddy_id+1);
04714 }
04715
04716 } else if (menuin[1] == 'a') {
04717
04718 char id[80], registrar[80], realm[80], uname[80], passwd[30];
04719 pjsua_acc_config acc_cfg;
04720 pj_status_t status;
04721
04722 if (!simple_input("Your SIP URL:", id, sizeof(id)))
04723 break;
04724 if (!simple_input("URL of the registrar:", registrar, sizeof(registrar)))
04725 break;
04726 if (!simple_input("Auth Realm:", realm, sizeof(realm)))
04727 break;
04728 if (!simple_input("Auth Username:", uname, sizeof(uname)))
04729 break;
04730 if (!simple_input("Auth Password:", passwd, sizeof(passwd)))
04731 break;
04732
04733 pjsua_acc_config_default(&acc_cfg);
04734 acc_cfg.id = pj_str(id);
04735 acc_cfg.reg_uri = pj_str(registrar);
04736 acc_cfg.cred_count = 1;
04737 acc_cfg.cred_info[0].scheme = pj_str("Digest");
04738 acc_cfg.cred_info[0].realm = pj_str(realm);
04739 acc_cfg.cred_info[0].username = pj_str(uname);
04740 acc_cfg.cred_info[0].data_type = 0;
04741 acc_cfg.cred_info[0].data = pj_str(passwd);
04742
04743 acc_cfg.rtp_cfg = app_config.rtp_cfg;
04744 app_config_init_video(&acc_cfg);
04745
04746 status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
04747 if (status != PJ_SUCCESS) {
04748 pjsua_perror(THIS_FILE, "Error adding new account", status);
04749 }
04750
04751 } else {
04752 printf("Invalid input %s\n", menuin);
04753 }
04754 break;
04755
04756 case '-':
04757 if (menuin[1] == 'b') {
04758 if (!simple_input("Enter buddy ID to delete",buf,sizeof(buf)))
04759 break;
04760
04761 i = my_atoi(buf) - 1;
04762
04763 if (!pjsua_buddy_is_valid(i)) {
04764 printf("Invalid buddy id %d\n", i);
04765 } else {
04766 pjsua_buddy_del(i);
04767 printf("Buddy %d deleted\n", i);
04768 }
04769
04770 } else if (menuin[1] == 'a') {
04771
04772 if (!simple_input("Enter account ID to delete",buf,sizeof(buf)))
04773 break;
04774
04775 i = my_atoi(buf);
04776
04777 if (!pjsua_acc_is_valid(i)) {
04778 printf("Invalid account id %d\n", i);
04779 } else {
04780 pjsua_acc_del(i);
04781 printf("Account %d deleted\n", i);
04782 }
04783
04784 } else {
04785 printf("Invalid input %s\n", menuin);
04786 }
04787 break;
04788
04789 case 'H':
04790
04791
04792
04793 if (current_call != -1) {
04794
04795 pjsua_call_set_hold(current_call, NULL);
04796
04797 } else {
04798 PJ_LOG(3,(THIS_FILE, "No current call"));
04799 }
04800 break;
04801
04802 case 'v':
04803 #if PJSUA_HAS_VIDEO
04804 if (menuin[1]=='i' && menuin[2]=='d' && menuin[3]==' ') {
04805
04806 vid_handle_menu(menuin);
04807
04808 } else
04809 #endif
04810 if (current_call != -1) {
04811
04812
04813
04814 call_opt.flag |= PJSUA_CALL_UNHOLD;
04815 pjsua_call_reinvite2(current_call, &call_opt, NULL);
04816
04817 } else {
04818 PJ_LOG(3,(THIS_FILE, "No current call"));
04819 }
04820 break;
04821
04822 case 'U':
04823
04824
04825
04826 if (current_call != -1) {
04827
04828 pjsua_call_update2(current_call, &call_opt, NULL);
04829
04830 } else {
04831 PJ_LOG(3,(THIS_FILE, "No current call"));
04832 }
04833 break;
04834
04835 case 'C':
04836 if (menuin[1] == 'p') {
04837 manage_codec_prio();
04838 }
04839 break;
04840
04841 case 'x':
04842
04843
04844
04845 if (current_call == -1) {
04846
04847 PJ_LOG(3,(THIS_FILE, "No current call"));
04848
04849 } else {
04850 int call = current_call;
04851 pjsip_generic_string_hdr refer_sub;
04852 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
04853 pj_str_t STR_FALSE = { "false", 5 };
04854 pjsua_call_info ci;
04855
04856 pjsua_call_get_info(current_call, &ci);
04857 printf("Transfering current call [%d] %.*s\n",
04858 current_call,
04859 (int)ci.remote_info.slen, ci.remote_info.ptr);
04860
04861 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
04862
04863
04864
04865 if (call != current_call) {
04866 puts("Call has been disconnected");
04867 continue;
04868 }
04869
04870 pjsua_msg_data_init(&msg_data);
04871 if (app_config.no_refersub) {
04872
04873 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
04874 &STR_FALSE);
04875 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
04876 }
04877 if (result.nb_result != NO_NB) {
04878 if (result.nb_result == -1 || result.nb_result == 0)
04879 puts("You can't do that with transfer call!");
04880 else {
04881 pjsua_buddy_info binfo;
04882 pjsua_buddy_get_info(result.nb_result-1, &binfo);
04883 pjsua_call_xfer( current_call, &binfo.uri, &msg_data);
04884 }
04885
04886 } else if (result.uri_result) {
04887 pj_str_t tmp;
04888 tmp = pj_str(result.uri_result);
04889 pjsua_call_xfer( current_call, &tmp, &msg_data);
04890 }
04891 }
04892 break;
04893
04894 case 'X':
04895
04896
04897
04898 if (current_call == -1) {
04899
04900 PJ_LOG(3,(THIS_FILE, "No current call"));
04901
04902 } else {
04903 int call = current_call;
04904 int dst_call;
04905 pjsip_generic_string_hdr refer_sub;
04906 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
04907 pj_str_t STR_FALSE = { "false", 5 };
04908 pjsua_call_id ids[PJSUA_MAX_CALLS];
04909 pjsua_call_info ci;
04910 unsigned i, count;
04911
04912 count = PJ_ARRAY_SIZE(ids);
04913 pjsua_enum_calls(ids, &count);
04914
04915 if (count <= 1) {
04916 puts("There are no other calls");
04917 continue;
04918 }
04919
04920 pjsua_call_get_info(current_call, &ci);
04921 printf("Transfer call [%d] %.*s to one of the following:\n",
04922 current_call,
04923 (int)ci.remote_info.slen, ci.remote_info.ptr);
04924
04925 for (i=0; i<count; ++i) {
04926 pjsua_call_info call_info;
04927
04928 if (ids[i] == call)
04929 continue;
04930
04931 pjsua_call_get_info(ids[i], &call_info);
04932 printf("%d %.*s [%.*s]\n",
04933 ids[i],
04934 (int)call_info.remote_info.slen,
04935 call_info.remote_info.ptr,
04936 (int)call_info.state_text.slen,
04937 call_info.state_text.ptr);
04938 }
04939
04940 if (!simple_input("Enter call number to be replaced",
04941 buf, sizeof(buf)))
04942 continue;
04943
04944 dst_call = my_atoi(buf);
04945
04946
04947
04948 if (call != current_call) {
04949 puts("Call has been disconnected");
04950 continue;
04951 }
04952
04953
04954 if (dst_call == call) {
04955 puts("Destination call number must not be the same "
04956 "as the call being transfered");
04957 continue;
04958 }
04959 if (dst_call >= PJSUA_MAX_CALLS) {
04960 puts("Invalid destination call number");
04961 continue;
04962 }
04963 if (!pjsua_call_is_active(dst_call)) {
04964 puts("Invalid destination call number");
04965 continue;
04966 }
04967
04968 pjsua_msg_data_init(&msg_data);
04969 if (app_config.no_refersub) {
04970
04971 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
04972 &STR_FALSE);
04973 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
04974 }
04975
04976 pjsua_call_xfer_replaces(call, dst_call,
04977 PJSUA_XFER_NO_REQUIRE_REPLACES,
04978 &msg_data);
04979 }
04980 break;
04981
04982 case '#':
04983
04984
04985
04986 if (current_call == -1) {
04987
04988 PJ_LOG(3,(THIS_FILE, "No current call"));
04989
04990 } else if (!pjsua_call_has_media(current_call)) {
04991
04992 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
04993
04994 } else {
04995 pj_str_t digits;
04996 int call = current_call;
04997 pj_status_t status;
04998
04999 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
05000 sizeof(buf)))
05001 {
05002 break;
05003 }
05004
05005 if (call != current_call) {
05006 puts("Call has been disconnected");
05007 continue;
05008 }
05009
05010 digits = pj_str(buf);
05011 status = pjsua_call_dial_dtmf(current_call, &digits);
05012 if (status != PJ_SUCCESS) {
05013 pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
05014 } else {
05015 puts("DTMF digits enqueued for transmission");
05016 }
05017 }
05018 break;
05019
05020 case '*':
05021
05022 if (current_call == -1) {
05023
05024 PJ_LOG(3,(THIS_FILE, "No current call"));
05025
05026 } else {
05027 const pj_str_t SIP_INFO = pj_str("INFO");
05028 pj_str_t digits;
05029 int call = current_call;
05030 int i;
05031 pj_status_t status;
05032
05033 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
05034 sizeof(buf)))
05035 {
05036 break;
05037 }
05038
05039 if (call != current_call) {
05040 puts("Call has been disconnected");
05041 continue;
05042 }
05043
05044 digits = pj_str(buf);
05045 for (i=0; i<digits.slen; ++i) {
05046 char body[80];
05047
05048 pjsua_msg_data_init(&msg_data);
05049 msg_data.content_type = pj_str("application/dtmf-relay");
05050
05051 pj_ansi_snprintf(body, sizeof(body),
05052 "Signal=%c\r\n"
05053 "Duration=160",
05054 buf[i]);
05055 msg_data.msg_body = pj_str(body);
05056
05057 status = pjsua_call_send_request(current_call, &SIP_INFO,
05058 &msg_data);
05059 if (status != PJ_SUCCESS) {
05060 break;
05061 }
05062 }
05063 }
05064 break;
05065
05066 case 'S':
05067
05068
05069
05070 if (pjsua_acc_get_count() == 0) {
05071 puts("Sorry, need at least one account configured");
05072 break;
05073 }
05074
05075 puts("Send arbitrary request to remote host");
05076
05077
05078 if (!simple_input("Request method:",text,sizeof(text)))
05079 break;
05080
05081
05082 uri = NULL;
05083 ui_input_url("Destination URI", buf, sizeof(buf), &result);
05084 if (result.nb_result != NO_NB) {
05085
05086 if (result.nb_result == -1) {
05087 puts("Sorry you can't do that!");
05088 continue;
05089 } else if (result.nb_result == 0) {
05090 uri = NULL;
05091 if (current_call == PJSUA_INVALID_ID) {
05092 puts("No current call");
05093 continue;
05094 }
05095 } else {
05096 pjsua_buddy_info binfo;
05097 pjsua_buddy_get_info(result.nb_result-1, &binfo);
05098 tmp.ptr = buf;
05099 pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
05100 uri = buf;
05101 }
05102
05103 } else if (result.uri_result) {
05104 uri = result.uri_result;
05105 } else {
05106 continue;
05107 }
05108
05109 if (uri) {
05110 tmp = pj_str(uri);
05111 send_request(text, &tmp);
05112 } else {
05113
05114
05115
05116
05117
05118 pj_str_t method = pj_str(text);
05119 pjsua_call_send_request(current_call, &method, NULL);
05120 }
05121 break;
05122
05123 case 'e':
05124 if (pj_ansi_strnicmp(menuin, "echo", 4)==0) {
05125 pj_str_t tmp;
05126
05127 tmp.ptr = menuin+5;
05128 tmp.slen = pj_ansi_strlen(menuin)-6;
05129
05130 if (tmp.slen < 1) {
05131 puts("Usage: echo [0|1]");
05132 break;
05133 }
05134
05135 cmd_echo = *tmp.ptr != '0' || tmp.slen!=1;
05136 }
05137 break;
05138
05139 case 's':
05140 if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
05141 pj_str_t tmp;
05142 int delay;
05143
05144 tmp.ptr = menuin+6;
05145 tmp.slen = pj_ansi_strlen(menuin)-7;
05146
05147 if (tmp.slen < 1) {
05148 puts("Usage: sleep MSEC");
05149 break;
05150 }
05151
05152 delay = pj_strtoul(&tmp);
05153 if (delay < 0) delay = 0;
05154 pj_thread_sleep(delay);
05155 break;
05156 }
05157
05158
05159 case 'u':
05160
05161
05162
05163 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
05164 if (result.nb_result != NO_NB) {
05165 if (result.nb_result == -1) {
05166 int i, count;
05167 count = pjsua_get_buddy_count();
05168 for (i=0; i<count; ++i)
05169 pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
05170 } else if (result.nb_result == 0) {
05171 puts("Sorry, can only subscribe to buddy's presence, "
05172 "not from existing call");
05173 } else {
05174 pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
05175 }
05176
05177 } else if (result.uri_result) {
05178 puts("Sorry, can only subscribe to buddy's presence, "
05179 "not arbitrary URL (for now)");
05180 }
05181
05182 break;
05183
05184 case 'r':
05185 switch (menuin[1]) {
05186 case 'r':
05187
05188
05189
05190 pjsua_acc_set_registration(current_acc, PJ_TRUE);
05191 break;
05192 case 'u':
05193
05194
05195
05196 pjsua_acc_set_registration(current_acc, PJ_FALSE);
05197 break;
05198 }
05199 break;
05200
05201 case 't':
05202 pjsua_acc_get_info(current_acc, &acc_info);
05203 acc_info.online_status = !acc_info.online_status;
05204 pjsua_acc_set_online_status(current_acc, acc_info.online_status);
05205 printf("Setting %s online status to %s\n",
05206 acc_info.acc_uri.ptr,
05207 (acc_info.online_status?"online":"offline"));
05208 break;
05209
05210 case 'T':
05211 change_online_status();
05212 break;
05213
05214 case 'c':
05215 switch (menuin[1]) {
05216 case 'l':
05217 conf_list();
05218 break;
05219 case 'c':
05220 case 'd':
05221 {
05222 char tmp[10], src_port[10], dst_port[10];
05223 pj_status_t status;
05224 int cnt;
05225 const char *src_title, *dst_title;
05226
05227 cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port);
05228
05229 if (cnt != 3) {
05230 conf_list();
05231
05232 src_title = (menuin[1]=='c'?
05233 "Connect src port #":
05234 "Disconnect src port #");
05235 dst_title = (menuin[1]=='c'?
05236 "To dst port #":
05237 "From dst port #");
05238
05239 if (!simple_input(src_title, src_port, sizeof(src_port)))
05240 break;
05241
05242 if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
05243 break;
05244 }
05245
05246 if (menuin[1]=='c') {
05247 status = pjsua_conf_connect(my_atoi(src_port),
05248 my_atoi(dst_port));
05249 } else {
05250 status = pjsua_conf_disconnect(my_atoi(src_port),
05251 my_atoi(dst_port));
05252 }
05253 if (status == PJ_SUCCESS) {
05254 puts("Success");
05255 } else {
05256 puts("ERROR!!");
05257 }
05258 }
05259 break;
05260 }
05261 break;
05262
05263 case 'V':
05264
05265 sprintf(buf, "Adjust mic level: [%4.1fx] ", app_config.mic_level);
05266 if (simple_input(buf,text,sizeof(text))) {
05267 char *err;
05268 app_config.mic_level = (float)strtod(text, &err);
05269 pjsua_conf_adjust_rx_level(0, app_config.mic_level);
05270 }
05271 sprintf(buf, "Adjust speaker level: [%4.1fx] ",
05272 app_config.speaker_level);
05273 if (simple_input(buf,text,sizeof(text))) {
05274 char *err;
05275 app_config.speaker_level = (float)strtod(text, &err);
05276 pjsua_conf_adjust_tx_level(0, app_config.speaker_level);
05277 }
05278
05279 break;
05280
05281 case 'd':
05282 if (menuin[1] == 'c') {
05283 char settings[2000];
05284 int len;
05285
05286 len = write_settings(&app_config, settings, sizeof(settings));
05287 if (len < 1)
05288 PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
05289 else
05290 PJ_LOG(3,(THIS_FILE,
05291 "Dumping configuration (%d bytes):\n%s\n",
05292 len, settings));
05293
05294 } else if (menuin[1] == 'q') {
05295
05296 if (current_call != PJSUA_INVALID_ID) {
05297 log_call_dump(current_call);
05298 } else {
05299 PJ_LOG(3,(THIS_FILE, "No current call"));
05300 }
05301
05302 } else {
05303 app_dump(menuin[1]=='d');
05304 }
05305 break;
05306
05307
05308 case 'f':
05309 if (simple_input("Enter output filename", buf, sizeof(buf))) {
05310 char settings[2000];
05311 int len;
05312
05313 len = write_settings(&app_config, settings, sizeof(settings));
05314 if (len < 1)
05315 PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
05316 else {
05317 pj_oshandle_t fd;
05318 pj_status_t status;
05319
05320 status = pj_file_open(app_config.pool, buf,
05321 PJ_O_WRONLY, &fd);
05322 if (status != PJ_SUCCESS) {
05323 pjsua_perror(THIS_FILE, "Unable to open file", status);
05324 } else {
05325 pj_ssize_t size = len;
05326 pj_file_write(fd, settings, &size);
05327 pj_file_close(fd);
05328
05329 printf("Settings successfully written to '%s'\n", buf);
05330 }
05331 }
05332
05333 }
05334 break;
05335
05336
05337 case 'L':
05338 app_restart = PJ_TRUE;
05339
05340
05341 case 'q':
05342 goto on_exit;
05343
05344 case 'R':
05345 if (!pjsua_call_is_active(current_call)) {
05346 PJ_LOG(1,(THIS_FILE, "Call %d has gone", current_call));
05347 } else if (menuin[1] == 'a') {
05348 pjsua_call_process_redirect(current_call,
05349 PJSIP_REDIRECT_ACCEPT);
05350 } else if (menuin[1] == 'r') {
05351 pjsua_call_process_redirect(current_call,
05352 PJSIP_REDIRECT_REJECT);
05353 } else {
05354 pjsua_call_process_redirect(current_call,
05355 PJSIP_REDIRECT_STOP);
05356 }
05357 break;
05358
05359 default:
05360 if (menuin[0] != '\n' && menuin[0] != '\r') {
05361 printf("Invalid input %s", menuin);
05362 }
05363 keystroke_help();
05364 break;
05365 }
05366 }
05367
05368 on_exit:
05369 ;
05370 }
05371
05372
05373
05374
05375 static void simple_registrar(pjsip_rx_data *rdata)
05376 {
05377 pjsip_tx_data *tdata;
05378 const pjsip_expires_hdr *exp;
05379 const pjsip_hdr *h;
05380 unsigned cnt = 0;
05381 pjsip_generic_string_hdr *srv;
05382 pj_status_t status;
05383
05384 status = pjsip_endpt_create_response(pjsua_get_pjsip_endpt(),
05385 rdata, 200, NULL, &tdata);
05386 if (status != PJ_SUCCESS)
05387 return;
05388
05389 exp = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
05390
05391 h = rdata->msg_info.msg->hdr.next;
05392 while (h != &rdata->msg_info.msg->hdr) {
05393 if (h->type == PJSIP_H_CONTACT) {
05394 const pjsip_contact_hdr *c = (const pjsip_contact_hdr*)h;
05395 int e = c->expires;
05396
05397 if (e < 0) {
05398 if (exp)
05399 e = exp->ivalue;
05400 else
05401 e = 3600;
05402 }
05403
05404 if (e > 0) {
05405 pjsip_contact_hdr *nc = pjsip_hdr_clone(tdata->pool, h);
05406 nc->expires = e;
05407 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)nc);
05408 ++cnt;
05409 }
05410 }
05411 h = h->next;
05412 }
05413
05414 srv = pjsip_generic_string_hdr_create(tdata->pool, NULL, NULL);
05415 srv->name = pj_str("Server");
05416 srv->hvalue = pj_str("pjsua simple registrar");
05417 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)srv);
05418
05419 pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(),
05420 rdata, tdata, NULL, NULL);
05421 }
05422
05423
05424
05425
05426
05427
05428
05429
05430
05431 static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata)
05432 {
05433 pjsip_tx_data *tdata;
05434 pjsip_status_code status_code;
05435 pj_status_t status;
05436
05437
05438 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
05439 &pjsip_ack_method) == 0)
05440 return PJ_TRUE;
05441
05442
05443 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
05444 &pjsip_register_method) == 0)
05445 {
05446 simple_registrar(rdata);
05447 return PJ_TRUE;
05448 }
05449
05450
05451 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
05452 &pjsip_notify_method) == 0)
05453 {
05454
05455 status_code = PJSIP_SC_BAD_REQUEST;
05456 } else {
05457
05458 status_code = PJSIP_SC_METHOD_NOT_ALLOWED;
05459 }
05460 status = pjsip_endpt_create_response(pjsua_get_pjsip_endpt(),
05461 rdata, status_code,
05462 NULL, &tdata);
05463 if (status != PJ_SUCCESS) {
05464 pjsua_perror(THIS_FILE, "Unable to create response", status);
05465 return PJ_TRUE;
05466 }
05467
05468
05469 if (status_code == PJSIP_SC_METHOD_NOT_ALLOWED) {
05470 const pjsip_hdr *cap_hdr;
05471 cap_hdr = pjsip_endpt_get_capability(pjsua_get_pjsip_endpt(),
05472 PJSIP_H_ALLOW, NULL);
05473 if (cap_hdr) {
05474 pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool,
05475 cap_hdr));
05476 }
05477 }
05478
05479
05480 {
05481 pj_str_t user_agent;
05482 char tmp[80];
05483 const pj_str_t USER_AGENT = { "User-Agent", 10};
05484 pjsip_hdr *h;
05485
05486 pj_ansi_snprintf(tmp, sizeof(tmp), "PJSUA v%s/%s",
05487 pj_get_version(), PJ_OS_NAME);
05488 pj_strdup2_with_null(tdata->pool, &user_agent, tmp);
05489
05490 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
05491 &USER_AGENT,
05492 &user_agent);
05493 pjsip_msg_add_hdr(tdata->msg, h);
05494 }
05495
05496 pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(), rdata, tdata,
05497 NULL, NULL);
05498
05499 return PJ_TRUE;
05500 }
05501
05502
05503
05504 static pjsip_module mod_default_handler =
05505 {
05506 NULL, NULL,
05507 { "mod-default-handler", 19 },
05508 -1,
05509 PJSIP_MOD_PRIORITY_APPLICATION+99,
05510 NULL,
05511 NULL,
05512 NULL,
05513 NULL,
05514 &default_mod_on_rx_request,
05515 NULL,
05516 NULL,
05517 NULL,
05518 NULL,
05519
05520 };
05521
05522
05523
05524
05525
05526
05527
05528
05529 pj_status_t app_init(int argc, char *argv[])
05530 {
05531 pjsua_transport_id transport_id = -1;
05532 pjsua_transport_config tcp_cfg;
05533 unsigned i;
05534 pj_status_t status;
05535
05536 app_restart = PJ_FALSE;
05537
05538
05539 status = pjsua_create();
05540 if (status != PJ_SUCCESS)
05541 return status;
05542
05543
05544 app_config.pool = pjsua_pool_create("pjsua-app", 1000, 1000);
05545
05546
05547 default_config(&app_config);
05548
05549
05550 status = parse_args(argc, argv, &app_config, &uri_arg);
05551 if (status != PJ_SUCCESS)
05552 return status;
05553
05554
05555 app_config.cfg.cb.on_call_state = &on_call_state;
05556 app_config.cfg.cb.on_call_media_state = &on_call_media_state;
05557 app_config.cfg.cb.on_incoming_call = &on_incoming_call;
05558 app_config.cfg.cb.on_call_tsx_state = &on_call_tsx_state;
05559 app_config.cfg.cb.on_dtmf_digit = &call_on_dtmf_callback;
05560 app_config.cfg.cb.on_call_redirected = &call_on_redirected;
05561 app_config.cfg.cb.on_reg_state = &on_reg_state;
05562 app_config.cfg.cb.on_incoming_subscribe = &on_incoming_subscribe;
05563 app_config.cfg.cb.on_buddy_state = &on_buddy_state;
05564 app_config.cfg.cb.on_buddy_evsub_state = &on_buddy_evsub_state;
05565 app_config.cfg.cb.on_pager = &on_pager;
05566 app_config.cfg.cb.on_typing = &on_typing;
05567 app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
05568 app_config.cfg.cb.on_call_replaced = &on_call_replaced;
05569 app_config.cfg.cb.on_nat_detect = &on_nat_detect;
05570 app_config.cfg.cb.on_mwi_info = &on_mwi_info;
05571 app_config.cfg.cb.on_transport_state = &on_transport_state;
05572 app_config.cfg.cb.on_ice_transport_error = &on_ice_transport_error;
05573 app_config.cfg.cb.on_snd_dev_operation = &on_snd_dev_operation;
05574 app_config.cfg.cb.on_call_media_event = &on_call_media_event;
05575 #ifdef TRANSPORT_ADAPTER_SAMPLE
05576 app_config.cfg.cb.on_create_media_transport = &on_create_media_transport;
05577 #endif
05578 app_config.log_cfg.cb = log_cb;
05579
05580
05581 if (app_config.capture_lat > 0)
05582 app_config.media_cfg.snd_rec_latency = app_config.capture_lat;
05583 if (app_config.playback_lat)
05584 app_config.media_cfg.snd_play_latency = app_config.playback_lat;
05585
05586
05587 status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
05588 &app_config.media_cfg);
05589 if (status != PJ_SUCCESS)
05590 return status;
05591
05592
05593 status = pjsip_endpt_register_module(pjsua_get_pjsip_endpt(),
05594 &mod_default_handler);
05595 if (status != PJ_SUCCESS)
05596 return status;
05597
05598 #ifdef STEREO_DEMO
05599 stereo_demo();
05600 #endif
05601
05602
05603 for (i=0; i<PJ_ARRAY_SIZE(app_config.call_data); ++i) {
05604 app_config.call_data[i].timer.id = PJSUA_INVALID_ID;
05605 app_config.call_data[i].timer.cb = &call_timeout_callback;
05606 }
05607
05608
05609 for (i=0; i<app_config.wav_count; ++i) {
05610 pjsua_player_id wav_id;
05611 unsigned play_options = 0;
05612
05613 if (app_config.auto_play_hangup)
05614 play_options |= PJMEDIA_FILE_NO_LOOP;
05615
05616 status = pjsua_player_create(&app_config.wav_files[i], play_options,
05617 &wav_id);
05618 if (status != PJ_SUCCESS)
05619 goto on_error;
05620
05621 if (app_config.wav_id == PJSUA_INVALID_ID) {
05622 app_config.wav_id = wav_id;
05623 app_config.wav_port = pjsua_player_get_conf_port(app_config.wav_id);
05624 if (app_config.auto_play_hangup) {
05625 pjmedia_port *port;
05626
05627 pjsua_player_get_port(app_config.wav_id, &port);
05628 status = pjmedia_wav_player_set_eof_cb(port, NULL,
05629 &on_playfile_done);
05630 if (status != PJ_SUCCESS)
05631 goto on_error;
05632
05633 pj_timer_entry_init(&app_config.auto_hangup_timer, 0, NULL,
05634 &hangup_timeout_callback);
05635 }
05636 }
05637 }
05638
05639
05640 for (i=0; i<app_config.tone_count; ++i) {
05641 pjmedia_port *tport;
05642 char name[80];
05643 pj_str_t label;
05644 pj_status_t status;
05645
05646 pj_ansi_snprintf(name, sizeof(name), "tone-%d,%d",
05647 app_config.tones[i].freq1,
05648 app_config.tones[i].freq2);
05649 label = pj_str(name);
05650 status = pjmedia_tonegen_create2(app_config.pool, &label,
05651 8000, 1, 160, 16,
05652 PJMEDIA_TONEGEN_LOOP, &tport);
05653 if (status != PJ_SUCCESS) {
05654 pjsua_perror(THIS_FILE, "Unable to create tone generator", status);
05655 goto on_error;
05656 }
05657
05658 status = pjsua_conf_add_port(app_config.pool, tport,
05659 &app_config.tone_slots[i]);
05660 pj_assert(status == PJ_SUCCESS);
05661
05662 status = pjmedia_tonegen_play(tport, 1, &app_config.tones[i], 0);
05663 pj_assert(status == PJ_SUCCESS);
05664 }
05665
05666
05667 if (app_config.rec_file.slen) {
05668 status = pjsua_recorder_create(&app_config.rec_file, 0, NULL, 0, 0,
05669 &app_config.rec_id);
05670 if (status != PJ_SUCCESS)
05671 goto on_error;
05672
05673 app_config.rec_port = pjsua_recorder_get_conf_port(app_config.rec_id);
05674 }
05675
05676 pj_memcpy(&tcp_cfg, &app_config.udp_cfg, sizeof(tcp_cfg));
05677
05678
05679 if (app_config.no_tones == PJ_FALSE) {
05680 unsigned i, samples_per_frame;
05681 pjmedia_tone_desc tone[RING_CNT+RINGBACK_CNT];
05682 pj_str_t name;
05683
05684 samples_per_frame = app_config.media_cfg.audio_frame_ptime *
05685 app_config.media_cfg.clock_rate *
05686 app_config.media_cfg.channel_count / 1000;
05687
05688
05689 name = pj_str("ringback");
05690 status = pjmedia_tonegen_create2(app_config.pool, &name,
05691 app_config.media_cfg.clock_rate,
05692 app_config.media_cfg.channel_count,
05693 samples_per_frame,
05694 16, PJMEDIA_TONEGEN_LOOP,
05695 &app_config.ringback_port);
05696 if (status != PJ_SUCCESS)
05697 goto on_error;
05698
05699 pj_bzero(&tone, sizeof(tone));
05700 for (i=0; i<RINGBACK_CNT; ++i) {
05701 tone[i].freq1 = RINGBACK_FREQ1;
05702 tone[i].freq2 = RINGBACK_FREQ2;
05703 tone[i].on_msec = RINGBACK_ON;
05704 tone[i].off_msec = RINGBACK_OFF;
05705 }
05706 tone[RINGBACK_CNT-1].off_msec = RINGBACK_INTERVAL;
05707
05708 pjmedia_tonegen_play(app_config.ringback_port, RINGBACK_CNT, tone,
05709 PJMEDIA_TONEGEN_LOOP);
05710
05711
05712 status = pjsua_conf_add_port(app_config.pool, app_config.ringback_port,
05713 &app_config.ringback_slot);
05714 if (status != PJ_SUCCESS)
05715 goto on_error;
05716
05717
05718 name = pj_str("ring");
05719 status = pjmedia_tonegen_create2(app_config.pool, &name,
05720 app_config.media_cfg.clock_rate,
05721 app_config.media_cfg.channel_count,
05722 samples_per_frame,
05723 16, PJMEDIA_TONEGEN_LOOP,
05724 &app_config.ring_port);
05725 if (status != PJ_SUCCESS)
05726 goto on_error;
05727
05728 for (i=0; i<RING_CNT; ++i) {
05729 tone[i].freq1 = RING_FREQ1;
05730 tone[i].freq2 = RING_FREQ2;
05731 tone[i].on_msec = RING_ON;
05732 tone[i].off_msec = RING_OFF;
05733 }
05734 tone[RING_CNT-1].off_msec = RING_INTERVAL;
05735
05736 pjmedia_tonegen_play(app_config.ring_port, RING_CNT,
05737 tone, PJMEDIA_TONEGEN_LOOP);
05738
05739 status = pjsua_conf_add_port(app_config.pool, app_config.ring_port,
05740 &app_config.ring_slot);
05741 if (status != PJ_SUCCESS)
05742 goto on_error;
05743
05744 }
05745
05746
05747 if (app_config.avi_cnt) {
05748 #if PJMEDIA_HAS_VIDEO && PJMEDIA_VIDEO_DEV_HAS_AVI
05749 pjmedia_vid_dev_factory *avi_factory;
05750
05751 status = pjmedia_avi_dev_create_factory(pjsua_get_pool_factory(),
05752 app_config.avi_cnt,
05753 &avi_factory);
05754 if (status != PJ_SUCCESS) {
05755 PJ_PERROR(1,(THIS_FILE, status, "Error creating AVI factory"));
05756 goto on_error;
05757 }
05758
05759 for (i=0; i<app_config.avi_cnt; ++i) {
05760 pjmedia_avi_dev_param avdp;
05761 pjmedia_vid_dev_index avid;
05762 unsigned strm_idx, strm_cnt;
05763
05764 app_config.avi[i].dev_id = PJMEDIA_VID_INVALID_DEV;
05765 app_config.avi[i].slot = PJSUA_INVALID_ID;
05766
05767 pjmedia_avi_dev_param_default(&avdp);
05768 avdp.path = app_config.avi[i].path;
05769
05770 status = pjmedia_avi_dev_alloc(avi_factory, &avdp, &avid);
05771 if (status != PJ_SUCCESS) {
05772 PJ_PERROR(1,(THIS_FILE, status,
05773 "Error creating AVI player for %.*s",
05774 (int)avdp.path.slen, avdp.path.ptr));
05775 goto on_error;
05776 }
05777
05778 PJ_LOG(4,(THIS_FILE, "AVI player %.*s created, dev_id=%d",
05779 (int)avdp.title.slen, avdp.title.ptr, avid));
05780
05781 app_config.avi[i].dev_id = avid;
05782 if (app_config.avi_def_idx == PJSUA_INVALID_ID)
05783 app_config.avi_def_idx = i;
05784
05785 strm_cnt = pjmedia_avi_streams_get_num_streams(avdp.avi_streams);
05786 for (strm_idx=0; strm_idx<strm_cnt; ++strm_idx) {
05787 pjmedia_port *aud;
05788 pjmedia_format *fmt;
05789 pjsua_conf_port_id slot;
05790 char fmt_name[5];
05791
05792 aud = pjmedia_avi_streams_get_stream(avdp.avi_streams,
05793 strm_idx);
05794 fmt = &aud->info.fmt;
05795
05796 pjmedia_fourcc_name(fmt->id, fmt_name);
05797
05798 if (fmt->id == PJMEDIA_FORMAT_PCM) {
05799 status = pjsua_conf_add_port(app_config.pool, aud,
05800 &slot);
05801 if (status == PJ_SUCCESS) {
05802 PJ_LOG(4,(THIS_FILE,
05803 "AVI %.*s: audio added to slot %d",
05804 (int)avdp.title.slen, avdp.title.ptr,
05805 slot));
05806 app_config.avi[i].slot = slot;
05807 }
05808 } else {
05809 PJ_LOG(4,(THIS_FILE,
05810 "AVI %.*s: audio ignored, format=%s",
05811 (int)avdp.title.slen, avdp.title.ptr,
05812 fmt_name));
05813 }
05814 }
05815 }
05816 #else
05817 PJ_LOG(2,(THIS_FILE,
05818 "Warning: --play-avi is ignored because AVI is disabled"));
05819 #endif
05820 }
05821
05822
05823 if (!app_config.no_udp) {
05824 pjsua_acc_id aid;
05825 pjsip_transport_type_e type = PJSIP_TRANSPORT_UDP;
05826
05827 status = pjsua_transport_create(type,
05828 &app_config.udp_cfg,
05829 &transport_id);
05830 if (status != PJ_SUCCESS)
05831 goto on_error;
05832
05833
05834 pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
05835 if (PJMEDIA_HAS_VIDEO) {
05836 pjsua_acc_config acc_cfg;
05837 pjsua_acc_get_config(aid, &acc_cfg);
05838 app_config_init_video(&acc_cfg);
05839 pjsua_acc_modify(aid, &acc_cfg);
05840 }
05841
05842 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
05843
05844 if (app_config.udp_cfg.port == 0) {
05845 pjsua_transport_info ti;
05846 pj_sockaddr_in *a;
05847
05848 pjsua_transport_get_info(transport_id, &ti);
05849 a = (pj_sockaddr_in*)&ti.local_addr;
05850
05851 tcp_cfg.port = pj_ntohs(a->sin_port);
05852 }
05853 }
05854
05855
05856 if (!app_config.no_udp && app_config.ipv6) {
05857 pjsua_acc_id aid;
05858 pjsip_transport_type_e type = PJSIP_TRANSPORT_UDP6;
05859 pjsua_transport_config udp_cfg;
05860
05861 udp_cfg = app_config.udp_cfg;
05862 if (udp_cfg.port == 0)
05863 udp_cfg.port = 5060;
05864 else
05865 udp_cfg.port += 10;
05866 status = pjsua_transport_create(type,
05867 &udp_cfg,
05868 &transport_id);
05869 if (status != PJ_SUCCESS)
05870 goto on_error;
05871
05872
05873 pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
05874 if (PJMEDIA_HAS_VIDEO) {
05875 pjsua_acc_config acc_cfg;
05876 pjsua_acc_get_config(aid, &acc_cfg);
05877 app_config_init_video(&acc_cfg);
05878 pjsua_acc_modify(aid, &acc_cfg);
05879 }
05880
05881 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
05882
05883 if (app_config.udp_cfg.port == 0) {
05884 pjsua_transport_info ti;
05885 pj_sockaddr_in *a;
05886
05887 pjsua_transport_get_info(transport_id, &ti);
05888 a = (pj_sockaddr_in*)&ti.local_addr;
05889
05890 tcp_cfg.port = pj_ntohs(a->sin_port);
05891 }
05892 }
05893
05894
05895 if (!app_config.no_tcp) {
05896 pjsua_acc_id aid;
05897
05898 status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
05899 &tcp_cfg,
05900 &transport_id);
05901 if (status != PJ_SUCCESS)
05902 goto on_error;
05903
05904
05905 pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
05906 if (PJMEDIA_HAS_VIDEO) {
05907 pjsua_acc_config acc_cfg;
05908 pjsua_acc_get_config(aid, &acc_cfg);
05909 app_config_init_video(&acc_cfg);
05910 pjsua_acc_modify(aid, &acc_cfg);
05911 }
05912 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
05913
05914 }
05915
05916
05917 #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
05918
05919 if (app_config.use_tls) {
05920
05921 pjsua_acc_id acc_id;
05922
05923
05924 tcp_cfg.tls_setting.qos_type = tcp_cfg.qos_type;
05925 pj_memcpy(&tcp_cfg.tls_setting.qos_params, &tcp_cfg.qos_params,
05926 sizeof(tcp_cfg.qos_params));
05927
05928
05929 tcp_cfg.port++;
05930 status = pjsua_transport_create(PJSIP_TRANSPORT_TLS,
05931 &tcp_cfg,
05932 &transport_id);
05933 tcp_cfg.port--;
05934 if (status != PJ_SUCCESS)
05935 goto on_error;
05936
05937
05938 pjsua_acc_add_local(transport_id, PJ_FALSE, &acc_id);
05939 if (PJMEDIA_HAS_VIDEO) {
05940 pjsua_acc_config acc_cfg;
05941 pjsua_acc_get_config(acc_id, &acc_cfg);
05942 app_config_init_video(&acc_cfg);
05943 pjsua_acc_modify(acc_id, &acc_cfg);
05944 }
05945 pjsua_acc_set_online_status(acc_id, PJ_TRUE);
05946 }
05947 #endif
05948
05949 if (transport_id == -1) {
05950 PJ_LOG(1,(THIS_FILE, "Error: no transport is configured"));
05951 status = -1;
05952 goto on_error;
05953 }
05954
05955
05956
05957 for (i=0; i<app_config.acc_cnt; ++i) {
05958 app_config.acc_cfg[i].rtp_cfg = app_config.rtp_cfg;
05959 app_config.acc_cfg[i].reg_retry_interval = 300;
05960 app_config.acc_cfg[i].reg_first_retry_interval = 60;
05961
05962 app_config_init_video(&app_config.acc_cfg[i]);
05963
05964 status = pjsua_acc_add(&app_config.acc_cfg[i], PJ_TRUE, NULL);
05965 if (status != PJ_SUCCESS)
05966 goto on_error;
05967 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
05968 }
05969
05970
05971 for (i=0; i<app_config.buddy_cnt; ++i) {
05972 status = pjsua_buddy_add(&app_config.buddy_cfg[i], NULL);
05973 if (status != PJ_SUCCESS) {
05974 PJ_PERROR(1,(THIS_FILE, status, "Error adding buddy"));
05975 goto on_error;
05976 }
05977 }
05978
05979
05980 for (i=0; i<app_config.codec_dis_cnt; ++i) {
05981 pjsua_codec_set_priority(&app_config.codec_dis[i],PJMEDIA_CODEC_PRIO_DISABLED);
05982 #if PJSUA_HAS_VIDEO
05983 pjsua_vid_codec_set_priority(&app_config.codec_dis[i],PJMEDIA_CODEC_PRIO_DISABLED);
05984 #endif
05985 }
05986
05987
05988 for (i=0; i<app_config.codec_cnt; ++i) {
05989 pjsua_codec_set_priority(&app_config.codec_arg[i],
05990 (pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9));
05991 #if PJSUA_HAS_VIDEO
05992 pjsua_vid_codec_set_priority(&app_config.codec_arg[i],
05993 (pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9));
05994 #endif
05995 }
05996
05997
05998 if (app_config.ipv6)
05999 status = create_ipv6_media_transports();
06000 #if DISABLED_FOR_TICKET_1185
06001 else
06002 status = pjsua_media_transports_create(&app_config.rtp_cfg);
06003 #endif
06004 if (status != PJ_SUCCESS)
06005 goto on_error;
06006
06007
06008 #ifndef STEREO_DEMO
06009 if (app_config.null_audio) {
06010 status = pjsua_set_null_snd_dev();
06011 if (status != PJ_SUCCESS)
06012 return status;
06013 }
06014 #endif
06015
06016 if (app_config.capture_dev != PJSUA_INVALID_ID ||
06017 app_config.playback_dev != PJSUA_INVALID_ID)
06018 {
06019 status = pjsua_set_snd_dev(app_config.capture_dev,
06020 app_config.playback_dev);
06021 if (status != PJ_SUCCESS)
06022 goto on_error;
06023 }
06024
06025 return PJ_SUCCESS;
06026
06027 on_error:
06028 app_destroy();
06029 return status;
06030 }
06031
06032
06033 static int stdout_refresh_proc(void *arg)
06034 {
06035 PJ_UNUSED_ARG(arg);
06036
06037
06038
06039
06040 pj_thread_set_prio(pj_thread_this(),
06041 pj_thread_get_prio_min(pj_thread_this()));
06042
06043 while (!stdout_refresh_quit) {
06044 pj_thread_sleep(stdout_refresh * 1000);
06045 puts(stdout_refresh_text);
06046 fflush(stdout);
06047 }
06048
06049 return 0;
06050 }
06051
06052 pj_status_t app_main(void)
06053 {
06054 pj_thread_t *stdout_refresh_thread = NULL;
06055 pj_status_t status;
06056
06057
06058 status = pjsua_start();
06059 if (status != PJ_SUCCESS) {
06060 app_destroy();
06061 return status;
06062 }
06063
06064
06065 if (stdout_refresh > 0) {
06066 pj_thread_create(app_config.pool, "stdout", &stdout_refresh_proc,
06067 NULL, 0, 0, &stdout_refresh_thread);
06068 }
06069
06070 console_app_main(&uri_arg);
06071
06072 if (stdout_refresh_thread) {
06073 stdout_refresh_quit = PJ_TRUE;
06074 pj_thread_join(stdout_refresh_thread);
06075 pj_thread_destroy(stdout_refresh_thread);
06076 }
06077
06078 return PJ_SUCCESS;
06079 }
06080
06081 pj_status_t app_destroy(void)
06082 {
06083 pj_status_t status;
06084 unsigned i;
06085
06086 #ifdef STEREO_DEMO
06087 if (app_config.snd) {
06088 pjmedia_snd_port_destroy(app_config.snd);
06089 app_config.snd = NULL;
06090 }
06091 if (app_config.sc_ch1) {
06092 pjsua_conf_remove_port(app_config.sc_ch1_slot);
06093 app_config.sc_ch1_slot = PJSUA_INVALID_ID;
06094 pjmedia_port_destroy(app_config.sc_ch1);
06095 app_config.sc_ch1 = NULL;
06096 }
06097 if (app_config.sc) {
06098 pjmedia_port_destroy(app_config.sc);
06099 app_config.sc = NULL;
06100 }
06101 #endif
06102
06103
06104 for (i=0; i<app_config.avi_cnt; ++i) {
06105 if (app_config.avi[i].slot != PJSUA_INVALID_ID)
06106 pjsua_conf_remove_port(app_config.avi[i].slot);
06107 #if PJMEDIA_HAS_VIDEO && PJMEDIA_VIDEO_DEV_HAS_AVI
06108 if (app_config.avi[i].dev_id != PJMEDIA_VID_INVALID_DEV)
06109 pjmedia_avi_dev_free(app_config.avi[i].dev_id);
06110 #endif
06111 }
06112
06113
06114 if (app_config.ringback_port &&
06115 app_config.ringback_slot != PJSUA_INVALID_ID)
06116 {
06117 pjsua_conf_remove_port(app_config.ringback_slot);
06118 app_config.ringback_slot = PJSUA_INVALID_ID;
06119 pjmedia_port_destroy(app_config.ringback_port);
06120 app_config.ringback_port = NULL;
06121 }
06122
06123
06124 if (app_config.ring_port && app_config.ring_slot != PJSUA_INVALID_ID) {
06125 pjsua_conf_remove_port(app_config.ring_slot);
06126 app_config.ring_slot = PJSUA_INVALID_ID;
06127 pjmedia_port_destroy(app_config.ring_port);
06128 app_config.ring_port = NULL;
06129 }
06130
06131
06132 for (i=0; i<app_config.tone_count; ++i) {
06133 pjsua_conf_remove_port(app_config.tone_slots[i]);
06134 }
06135
06136 if (app_config.pool) {
06137 pj_pool_release(app_config.pool);
06138 app_config.pool = NULL;
06139 }
06140
06141 status = pjsua_destroy();
06142
06143 pj_bzero(&app_config, sizeof(app_config));
06144
06145 return status;
06146 }
06147
06148
06149 #ifdef STEREO_DEMO
06150
06151
06152
06153
06154
06155
06156
06157
06158
06159
06160
06161
06162
06163
06164
06165
06166 static void stereo_demo()
06167 {
06168 pjmedia_port *conf;
06169 pj_status_t status;
06170
06171
06172 conf = pjsua_set_no_snd_dev();
06173
06174
06175 status = pjmedia_splitcomb_create(app_config.pool,
06176 conf->info.clock_rate ,
06177 2 ,
06178 2 * conf->info.samples_per_frame,
06179 conf->info.bits_per_sample,
06180 0 ,
06181 &app_config.sc);
06182 pj_assert(status == PJ_SUCCESS);
06183
06184
06185 status = pjmedia_splitcomb_set_channel(app_config.sc, 0 ,
06186 0 ,
06187 conf);
06188 pj_assert(status == PJ_SUCCESS);
06189
06190
06191 status = pjmedia_splitcomb_create_rev_channel(app_config.pool,
06192 app_config.sc,
06193 1 ,
06194 0 ,
06195 &app_config.sc_ch1);
06196 pj_assert(status == PJ_SUCCESS);
06197
06198
06199
06200
06201 status = pjsua_conf_add_port(app_config.pool, app_config.sc_ch1,
06202 &app_config.sc_ch1_slot);
06203 pj_assert(status == PJ_SUCCESS);
06204
06205
06206 status = pjmedia_snd_port_create(app_config.pool, -1, -1,
06207 conf->info.clock_rate,
06208 2 ,
06209 2 * conf->info.samples_per_frame,
06210 conf->info.bits_per_sample,
06211 0, &app_config.snd);
06212 pj_assert(status == PJ_SUCCESS);
06213
06214
06215
06216 status = pjmedia_snd_port_connect(app_config.snd, app_config.sc);
06217 pj_assert(status == PJ_SUCCESS);
06218
06219 }
06220 #endif
06221
06222 static pj_status_t create_ipv6_media_transports(void)
06223 {
06224 pjsua_media_transport tp[PJSUA_MAX_CALLS];
06225 pj_status_t status;
06226 int port = app_config.rtp_cfg.port;
06227 unsigned i;
06228
06229 for (i=0; i<app_config.cfg.max_calls; ++i) {
06230 enum { MAX_RETRY = 10 };
06231 pj_sock_t sock[2];
06232 pjmedia_sock_info si;
06233 unsigned j;
06234
06235
06236 status = PJ_SUCCESS;
06237
06238 for (j=0; j<MAX_RETRY; ++j) {
06239 unsigned k;
06240
06241 for (k=0; k<2; ++k) {
06242 pj_sockaddr bound_addr;
06243
06244 status = pj_sock_socket(pj_AF_INET6(), pj_SOCK_DGRAM(), 0, &sock[k]);
06245 if (status != PJ_SUCCESS)
06246 break;
06247
06248 status = pj_sockaddr_init(pj_AF_INET6(), &bound_addr,
06249 &app_config.rtp_cfg.bound_addr,
06250 (unsigned short)(port+k));
06251 if (status != PJ_SUCCESS)
06252 break;
06253
06254 status = pj_sock_bind(sock[k], &bound_addr,
06255 pj_sockaddr_get_len(&bound_addr));
06256 if (status != PJ_SUCCESS)
06257 break;
06258 }
06259 if (status != PJ_SUCCESS) {
06260 if (k==1)
06261 pj_sock_close(sock[0]);
06262
06263 if (port != 0)
06264 port += 10;
06265 else
06266 break;
06267
06268 continue;
06269 }
06270
06271 pj_bzero(&si, sizeof(si));
06272 si.rtp_sock = sock[0];
06273 si.rtcp_sock = sock[1];
06274
06275 pj_sockaddr_init(pj_AF_INET6(), &si.rtp_addr_name,
06276 &app_config.rtp_cfg.public_addr,
06277 (unsigned short)(port));
06278 pj_sockaddr_init(pj_AF_INET6(), &si.rtcp_addr_name,
06279 &app_config.rtp_cfg.public_addr,
06280 (unsigned short)(port+1));
06281
06282 status = pjmedia_transport_udp_attach(pjsua_get_pjmedia_endpt(),
06283 NULL,
06284 &si,
06285 0,
06286 &tp[i].transport);
06287 if (port != 0)
06288 port += 10;
06289 else
06290 break;
06291
06292 if (status == PJ_SUCCESS)
06293 break;
06294 }
06295
06296 if (status != PJ_SUCCESS) {
06297 pjsua_perror(THIS_FILE, "Error creating IPv6 UDP media transport",
06298 status);
06299 for (j=0; j<i; ++j) {
06300 pjmedia_transport_close(tp[j].transport);
06301 }
06302 return status;
06303 }
06304 }
06305
06306 #if DISABLED_FOR_TICKET_1185
06307 return pjsua_media_transports_attach(tp, i, PJ_TRUE);
06308 #else
06309 return PJ_ENOTSUP;
06310 #endif
06311 }
06312
PJSIP Open Source, high performance, small footprint, and very very portable SIP stack
Copyright (C) 2006-2008 Teluu Inc.
|