|
HOME
SIP/media Features
High Performance SIP
Small Footprint SIP
Symbian Port
FAQ
Documentation
Licensing
Download
Development (Trac)
Projects using pjsip
Mailing List
Open Source Links
About: PJLIB, PJLIB-UTIL, PJSIP, and PJMEDIA are created by: Benny Prijono <bennylp pjsip.org>
|
|
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
00022
00023 #define THIS_FILE "pjsua_app.c"
00024 #define NO_LIMIT (int)0x7FFFFFFF
00025
00026
00027
00028
00029
00030 #define RINGBACK_FREQ1 440
00031 #define RINGBACK_FREQ2 480
00032 #define RINGBACK_ON 2000
00033 #define RINGBACK_OFF 4000
00034 #define RINGBACK_CNT 1
00035 #define RINGBACK_INTERVAL 4000
00036
00037 #define RING_FREQ1 800
00038 #define RING_FREQ2 640
00039 #define RING_ON 200
00040 #define RING_OFF 100
00041 #define RING_CNT 3
00042 #define RING_INTERVAL 3000
00043
00044
00045
00046 struct call_data
00047 {
00048 pj_timer_entry timer;
00049 pj_bool_t ringback_on;
00050 pj_bool_t ring_on;
00051 };
00052
00053
00054
00055 static struct app_config
00056 {
00057 pjsua_config cfg;
00058 pjsua_logging_config log_cfg;
00059 pjsua_media_config media_cfg;
00060 pj_bool_t no_refersub;
00061 pj_bool_t ipv6;
00062 pj_bool_t enable_qos;
00063 pj_bool_t no_tcp;
00064 pj_bool_t no_udp;
00065 pj_bool_t use_tls;
00066 pjsua_transport_config udp_cfg;
00067 pjsua_transport_config rtp_cfg;
00068 pjsip_redirect_op redir_op;
00069
00070 unsigned acc_cnt;
00071 pjsua_acc_config acc_cfg[PJSUA_MAX_ACC];
00072
00073 unsigned buddy_cnt;
00074 pjsua_buddy_config buddy_cfg[PJSUA_MAX_BUDDIES];
00075
00076 struct call_data call_data[PJSUA_MAX_CALLS];
00077
00078 pj_pool_t *pool;
00079
00080
00081 unsigned codec_cnt;
00082 pj_str_t codec_arg[32];
00083 unsigned codec_dis_cnt;
00084 pj_str_t codec_dis[32];
00085 pj_bool_t null_audio;
00086 unsigned wav_count;
00087 pj_str_t wav_files[32];
00088 unsigned tone_count;
00089 pjmedia_tone_desc tones[32];
00090 pjsua_conf_port_id tone_slots[32];
00091 pjsua_player_id wav_id;
00092 pjsua_conf_port_id wav_port;
00093 pj_bool_t auto_play;
00094 pj_bool_t auto_play_hangup;
00095 pj_timer_entry auto_hangup_timer;
00096 pj_bool_t auto_loop;
00097 pj_bool_t auto_conf;
00098 pj_str_t rec_file;
00099 pj_bool_t auto_rec;
00100 pjsua_recorder_id rec_id;
00101 pjsua_conf_port_id rec_port;
00102 unsigned auto_answer;
00103 unsigned duration;
00104
00105 #ifdef STEREO_DEMO
00106 pjmedia_snd_port *snd;
00107 pjmedia_port *sc, *sc_ch1;
00108 pjsua_conf_port_id sc_ch1_slot;
00109 #endif
00110
00111 float mic_level,
00112 speaker_level;
00113
00114 int capture_dev, playback_dev;
00115 unsigned capture_lat, playback_lat;
00116
00117 pj_bool_t no_tones;
00118 int ringback_slot;
00119 int ringback_cnt;
00120 pjmedia_port *ringback_port;
00121 int ring_slot;
00122 int ring_cnt;
00123 pjmedia_port *ring_port;
00124
00125 } app_config;
00126
00127
00128
00129 #define current_acc pjsua_acc_get_default()
00130 static pjsua_call_id current_call = PJSUA_INVALID_ID;
00131 static pj_bool_t cmd_echo;
00132 static int stdout_refresh = -1;
00133 static const char *stdout_refresh_text = "STDOUT_REFRESH";
00134 static pj_bool_t stdout_refresh_quit = PJ_FALSE;
00135 static pj_str_t uri_arg;
00136
00137 static char some_buf[1024 * 3];
00138
00139 #ifdef STEREO_DEMO
00140 static void stereo_demo();
00141 #endif
00142 #ifdef TRANSPORT_ADAPTER_SAMPLE
00143 static pj_status_t transport_adapter_sample(void);
00144 #endif
00145 static pj_status_t create_ipv6_media_transports(void);
00146 pj_status_t app_destroy(void);
00147
00148 static void ringback_start(pjsua_call_id call_id);
00149 static void ring_start(pjsua_call_id call_id);
00150 static void ring_stop(pjsua_call_id call_id);
00151
00152 pj_bool_t app_restart;
00153
00154
00155
00156
00157
00158
00159 static void usage(void)
00160 {
00161 puts ("Usage:");
00162 puts (" pjsua [options] [SIP URL to call]");
00163 puts ("");
00164 puts ("General options:");
00165 puts (" --config-file=file Read the config/arguments from file.");
00166 puts (" --help Display this help screen");
00167 puts (" --version Display version info");
00168 puts ("");
00169 puts ("Logging options:");
00170 puts (" --log-file=fname Log to filename (default stderr)");
00171 puts (" --log-level=N Set log max level to N (0(none) to 6(trace)) (default=5)");
00172 puts (" --app-log-level=N Set log max level for stdout display (default=4)");
00173 puts (" --color Use colorful logging (default yes on Win32)");
00174 puts (" --no-color Disable colorful logging");
00175 puts (" --light-bg Use dark colors for light background (default is dark bg)");
00176
00177 puts ("");
00178 puts ("SIP Account options:");
00179 puts (" --use-ims Enable 3GPP/IMS related settings on this account");
00180 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00181 puts (" --use-srtp=N Use SRTP? 0:disabled, 1:optional, 2:mandatory (def:0)");
00182 puts (" --srtp-secure=N SRTP require secure SIP? 0:no, 1:tls, 1:sips (def:1)");
00183 #endif
00184 puts (" --registrar=url Set the URL of registrar server");
00185 puts (" --id=url Set the URL of local ID (used in From header)");
00186 puts (" --contact=url Optionally override the Contact information");
00187 puts (" --contact-params=S Append the specified parameters S in Contact header");
00188 puts (" --contact-uri-params=S Append the specified parameters S in Contact URI");
00189 puts (" --proxy=url Optional URL of proxy server to visit");
00190 puts (" May be specified multiple times");
00191 puts (" --reg-timeout=SEC Optional registration interval (default 55)");
00192 puts (" --realm=string Set realm");
00193 puts (" --username=string Set authentication username");
00194 puts (" --password=string Set authentication password");
00195 puts (" --publish Send presence PUBLISH for this account");
00196 puts (" --mwi Subscribe to message summary/waiting indication");
00197 puts (" --use-100rel Require reliable provisional response (100rel)");
00198 puts (" --use-timer Require SIP session timers");
00199 puts (" --timer-se=N Session timers expiration period, in secs (def:1800)");
00200 puts (" --timer-min-se=N Session timers minimum expiration period, in secs (def:90)");
00201 puts (" --auto-update-nat=N Where N is 0 or 1 to enable/disable SIP traversal behind");
00202 puts (" symmetric NAT (default 1)");
00203 puts (" --next-cred Add another credentials");
00204 puts ("");
00205 puts ("SIP Account Control:");
00206 puts (" --next-account Add more account");
00207 puts ("");
00208 puts ("Transport Options:");
00209 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
00210 puts (" --ipv6 Use IPv6 instead for SIP and media.");
00211 #endif
00212 puts (" --set-qos Enable QoS tagging for SIP and media.");
00213 puts (" --local-port=port Set TCP/UDP port. This implicitly enables both ");
00214 puts (" TCP and UDP transports on the specified port, unless");
00215 puts (" if TCP or UDP is disabled.");
00216 puts (" --ip-addr=IP Use the specifed address as SIP and RTP addresses.");
00217 puts (" (Hint: the IP may be the public IP of the NAT/router)");
00218 puts (" --bound-addr=IP Bind transports to this IP interface");
00219 puts (" --no-tcp Disable TCP transport.");
00220 puts (" --no-udp Disable UDP transport.");
00221 puts (" --nameserver=NS Add the specified nameserver to enable SRV resolution");
00222 puts (" This option can be specified multiple times.");
00223 puts (" --outbound=url Set the URL of global outbound proxy server");
00224 puts (" May be specified multiple times");
00225 puts (" --stun-srv=FORMAT Set STUN server host or domain. This option may be");
00226 puts (" specified more than once. FORMAT is hostdom[:PORT]");
00227 puts ("");
00228 puts ("TLS Options:");
00229 puts (" --use-tls Enable TLS transport (default=no)");
00230 puts (" --tls-ca-file Specify TLS CA file (default=none)");
00231 puts (" --tls-cert-file Specify TLS certificate file (default=none)");
00232 puts (" --tls-privkey-file Specify TLS private key file (default=none)");
00233 puts (" --tls-password Specify TLS password to private key file (default=none)");
00234 puts (" --tls-verify-server Verify server's certificate (default=no)");
00235 puts (" --tls-verify-client Verify client's certificate (default=no)");
00236 puts (" --tls-neg-timeout Specify TLS negotiation timeout (default=no)");
00237 puts (" --tls-srv-name Specify TLS server name for multi-hosting server (optional)");
00238
00239 puts ("");
00240 puts ("Media Options:");
00241 puts (" --add-codec=name Manually add codec (default is to enable all)");
00242 puts (" --dis-codec=name Disable codec (can be specified multiple times)");
00243 puts (" --clock-rate=N Override conference bridge clock rate");
00244 puts (" --snd-clock-rate=N Override sound device clock rate");
00245 puts (" --stereo Audio device and conference bridge opened in stereo mode");
00246 puts (" --null-audio Use NULL audio device");
00247 puts (" --play-file=file Register WAV file in conference bridge.");
00248 puts (" This can be specified multiple times.");
00249 puts (" --play-tone=FORMAT Register tone to the conference bridge.");
00250 puts (" FORMAT is 'F1,F2,ON,OFF', where F1,F2 are");
00251 puts (" frequencies, and ON,OFF=on/off duration in msec.");
00252 puts (" This can be specified multiple times.");
00253 puts (" --auto-play Automatically play the file (to incoming calls only)");
00254 puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
00255 puts (" --auto-conf Automatically put calls in conference with others");
00256 puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");
00257 puts (" --auto-rec Automatically record conversation");
00258 puts (" --quality=N Specify media quality (0-10, default=6)");
00259 puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)");
00260 puts (" --no-vad Disable VAD/silence detector (default=vad enabled)");
00261 puts (" --ec-tail=MSEC Set echo canceller tail length (default=256)");
00262 puts (" --ec-opt=OPT Select echo canceller algorithm (0=default, ");
00263 puts (" 1=speex, 2=suppressor)");
00264 puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is 30)");
00265 puts (" --capture-dev=id Audio capture device ID (default=-1)");
00266 puts (" --playback-dev=id Audio playback device ID (default=-1)");
00267 puts (" --capture-lat=N Audio capture latency, in ms (default=100)");
00268 puts (" --playback-lat=N Audio playback latency, in ms (default=100)");
00269 puts (" --snd-auto-close=N Auto close audio device when it is idle for N seconds.");
00270 puts (" Specify N=-1 (default) to disable this feature.");
00271 puts (" Specify N=0 for instant close when unused.");
00272 puts (" --no-tones Disable audible tones");
00273 puts (" --jb-max-size Specify jitter buffer maximum size, in frames (default=-1)");
00274
00275 puts ("");
00276 puts ("Media Transport Options:");
00277 puts (" --use-ice Enable ICE (default:no)");
00278 puts (" --ice-regular Use ICE regular nomination (default: aggressive)");
00279 puts (" --ice-max-hosts=N Set maximum number of ICE host candidates");
00280 puts (" --ice-no-rtcp Disable RTCP component in ICE (default: no)");
00281 puts (" --rtp-port=N Base port to try for RTP (default=4000)");
00282 puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
00283 puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
00284 puts (" --use-turn Enable TURN relay with ICE (default:no)");
00285 puts (" --turn-srv Domain or host name of TURN server (\"NAME:PORT\" format)");
00286 puts (" --turn-tcp Use TCP connection to TURN server (default no)");
00287 puts (" --turn-user TURN username");
00288 puts (" --turn-passwd TURN password");
00289
00290 puts ("");
00291 puts ("Buddy List (can be more than one):");
00292 puts (" --add-buddy url Add the specified URL to the buddy list.");
00293 puts ("");
00294 puts ("User Agent options:");
00295 puts (" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)");
00296 puts (" --max-calls=N Maximum number of concurrent calls (default:4, max:255)");
00297 puts (" --thread-cnt=N Number of worker threads (default:1)");
00298 puts (" --duration=SEC Set maximum call duration (default:no limit)");
00299 puts (" --norefersub Suppress event subscription when transfering calls");
00300 puts (" --use-compact-form Minimize SIP message size");
00301 puts (" --no-force-lr Allow strict-route to be used (i.e. do not force lr)");
00302 puts (" --accept-redirect=N Specify how to handle call redirect (3xx) response.");
00303 puts (" 0: reject, 1: follow automatically (default), 2: ask");
00304
00305 puts ("");
00306 puts ("When URL is specified, pjsua will immediately initiate call to that URL");
00307 puts ("");
00308
00309 fflush(stdout);
00310 }
00311
00312
00313
00314 static void default_config(struct app_config *cfg)
00315 {
00316 char tmp[80];
00317 unsigned i;
00318
00319 pjsua_config_default(&cfg->cfg);
00320 pj_ansi_sprintf(tmp, "PJSUA v%s/%s", pj_get_version(), PJ_OS_NAME);
00321 pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp);
00322
00323 pjsua_logging_config_default(&cfg->log_cfg);
00324 pjsua_media_config_default(&cfg->media_cfg);
00325 pjsua_transport_config_default(&cfg->udp_cfg);
00326 cfg->udp_cfg.port = 5060;
00327 pjsua_transport_config_default(&cfg->rtp_cfg);
00328 cfg->rtp_cfg.port = 4000;
00329 cfg->redir_op = PJSIP_REDIRECT_ACCEPT;
00330 cfg->duration = NO_LIMIT;
00331 cfg->wav_id = PJSUA_INVALID_ID;
00332 cfg->rec_id = PJSUA_INVALID_ID;
00333 cfg->wav_port = PJSUA_INVALID_ID;
00334 cfg->rec_port = PJSUA_INVALID_ID;
00335 cfg->mic_level = cfg->speaker_level = 1.0;
00336 cfg->capture_dev = PJSUA_INVALID_ID;
00337 cfg->playback_dev = PJSUA_INVALID_ID;
00338 cfg->capture_lat = PJMEDIA_SND_DEFAULT_REC_LATENCY;
00339 cfg->playback_lat = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
00340 cfg->ringback_slot = PJSUA_INVALID_ID;
00341 cfg->ring_slot = PJSUA_INVALID_ID;
00342
00343 for (i=0; i<PJ_ARRAY_SIZE(cfg->acc_cfg); ++i)
00344 pjsua_acc_config_default(&cfg->acc_cfg[i]);
00345
00346 for (i=0; i<PJ_ARRAY_SIZE(cfg->buddy_cfg); ++i)
00347 pjsua_buddy_config_default(&cfg->buddy_cfg[i]);
00348 }
00349
00350
00351
00352
00353
00354 static int read_config_file(pj_pool_t *pool, const char *filename,
00355 int *app_argc, char ***app_argv)
00356 {
00357 int i;
00358 FILE *fhnd;
00359 char line[200];
00360 int argc = 0;
00361 char **argv;
00362 enum { MAX_ARGS = 64 };
00363
00364
00365 argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
00366 argv[argc++] = *app_argv[0];
00367
00368
00369 fhnd = fopen(filename, "rt");
00370 if (!fhnd) {
00371 PJ_LOG(1,(THIS_FILE, "Unable to open config file %s", filename));
00372 fflush(stdout);
00373 return -1;
00374 }
00375
00376
00377 while (argc < MAX_ARGS && !feof(fhnd)) {
00378 char *token;
00379 char *p;
00380 const char *whitespace = " \t\r\n";
00381 char cDelimiter;
00382 int len, token_len;
00383
00384 if (fgets(line, sizeof(line), fhnd) == NULL) break;
00385
00386
00387 len = strlen(line);
00388 if (line[len-1]=='\n')
00389 line[--len] = '\0';
00390 if (line[len-1]=='\r')
00391 line[--len] = '\0';
00392
00393 if (len==0) continue;
00394
00395 for (p = line; *p != '\0' && argc < MAX_ARGS; p++) {
00396
00397 while (*p != '\0' && strchr(whitespace, *p) != NULL) p++;
00398
00399 if (*p == '\0')
00400 break;
00401
00402 if (*p == '"' || *p == '\'') {
00403 cDelimiter = *p++;
00404 token = p;
00405
00406 while (*p != '\0' && *p != cDelimiter) p++;
00407
00408 if (*p == '\0')
00409 cDelimiter = '\0';
00410
00411 } else {
00412 token = p;
00413
00414 while (*p != '\0' && strchr(whitespace, *p) == NULL) p++;
00415
00416 cDelimiter = *p;
00417 }
00418
00419 *p = '\0';
00420 token_len = p-token;
00421
00422 if (token_len > 0) {
00423 if (*token == '#')
00424 break;
00425
00426 argv[argc] = pj_pool_alloc(pool, token_len + 1);
00427 pj_memcpy(argv[argc], token, token_len + 1);
00428 ++argc;
00429 }
00430
00431 *p = cDelimiter;
00432 }
00433 }
00434
00435
00436 for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
00437 argv[argc++] = (*app_argv)[i];
00438
00439 if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
00440 PJ_LOG(1,(THIS_FILE,
00441 "Too many arguments specified in cmd line/config file"));
00442 fflush(stdout);
00443 fclose(fhnd);
00444 return -1;
00445 }
00446
00447 fclose(fhnd);
00448
00449
00450 *app_argc = argc;
00451 *app_argv = argv;
00452 return 0;
00453
00454 }
00455
00456 static int my_atoi(const char *cs)
00457 {
00458 pj_str_t s;
00459
00460 pj_cstr(&s, cs);
00461 if (cs[0] == '-') {
00462 s.ptr++, s.slen--;
00463 return 0 - (int)pj_strtoul(&s);
00464 } else if (cs[0] == '+') {
00465 s.ptr++, s.slen--;
00466 return pj_strtoul(&s);
00467 } else {
00468 return pj_strtoul(&s);
00469 }
00470 }
00471
00472
00473
00474 static pj_status_t parse_args(int argc, char *argv[],
00475 struct app_config *cfg,
00476 pj_str_t *uri_to_call)
00477 {
00478 int c;
00479 int option_index;
00480 enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
00481 OPT_COLOR, OPT_NO_COLOR, OPT_LIGHT_BG,
00482 OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, OPT_SND_AUTO_CLOSE,
00483 OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY,
00484 OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
00485 OPT_BOUND_ADDR, OPT_CONTACT_PARAMS, OPT_CONTACT_URI_PARAMS,
00486 OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
00487 OPT_MWI, OPT_NAMESERVER, OPT_STUN_SRV,
00488 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
00489 OPT_AUTO_ANSWER, OPT_AUTO_PLAY, OPT_AUTO_PLAY_HANGUP, OPT_AUTO_LOOP,
00490 OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
00491 OPT_USE_ICE, OPT_ICE_REGULAR, OPT_USE_SRTP, OPT_SRTP_SECURE,
00492 OPT_USE_TURN, OPT_ICE_MAX_HOSTS, OPT_ICE_NO_RTCP, OPT_TURN_SRV,
00493 OPT_TURN_TCP, OPT_TURN_USER, OPT_TURN_PASSWD,
00494 OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC,
00495 OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
00496 OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
00497 OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL, OPT_EC_OPT,
00498 OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
00499 OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
00500 OPT_NOREFERSUB, OPT_ACCEPT_REDIRECT,
00501 OPT_USE_TLS, OPT_TLS_CA_FILE, OPT_TLS_CERT_FILE, OPT_TLS_PRIV_FILE,
00502 OPT_TLS_PASSWORD, OPT_TLS_VERIFY_SERVER, OPT_TLS_VERIFY_CLIENT,
00503 OPT_TLS_NEG_TIMEOUT, OPT_TLS_SRV_NAME,
00504 OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV,
00505 OPT_CAPTURE_LAT, OPT_PLAYBACK_LAT, OPT_NO_TONES, OPT_JB_MAX_SIZE,
00506 OPT_STDOUT_REFRESH, OPT_STDOUT_REFRESH_TEXT, OPT_IPV6, OPT_QOS,
00507 #ifdef _IONBF
00508 OPT_STDOUT_NO_BUF,
00509 #endif
00510 OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC,
00511 OPT_NO_FORCE_LR,
00512 OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE
00513 };
00514 struct pj_getopt_option long_options[] = {
00515 { "config-file",1, 0, OPT_CONFIG_FILE},
00516 { "log-file", 1, 0, OPT_LOG_FILE},
00517 { "log-level", 1, 0, OPT_LOG_LEVEL},
00518 { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
00519 { "color", 0, 0, OPT_COLOR},
00520 { "no-color", 0, 0, OPT_NO_COLOR},
00521 { "light-bg", 0, 0, OPT_LIGHT_BG},
00522 { "help", 0, 0, OPT_HELP},
00523 { "version", 0, 0, OPT_VERSION},
00524 { "clock-rate", 1, 0, OPT_CLOCK_RATE},
00525 { "snd-clock-rate", 1, 0, OPT_SND_CLOCK_RATE},
00526 { "stereo", 0, 0, OPT_STEREO},
00527 { "null-audio", 0, 0, OPT_NULL_AUDIO},
00528 { "local-port", 1, 0, OPT_LOCAL_PORT},
00529 { "ip-addr", 1, 0, OPT_IP_ADDR},
00530 { "bound-addr", 1, 0, OPT_BOUND_ADDR},
00531 { "no-tcp", 0, 0, OPT_NO_TCP},
00532 { "no-udp", 0, 0, OPT_NO_UDP},
00533 { "norefersub", 0, 0, OPT_NOREFERSUB},
00534 { "proxy", 1, 0, OPT_PROXY},
00535 { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
00536 { "registrar", 1, 0, OPT_REGISTRAR},
00537 { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
00538 { "publish", 0, 0, OPT_PUBLISH},
00539 { "mwi", 0, 0, OPT_MWI},
00540 { "use-100rel", 0, 0, OPT_100REL},
00541 { "use-ims", 0, 0, OPT_USE_IMS},
00542 { "id", 1, 0, OPT_ID},
00543 { "contact", 1, 0, OPT_CONTACT},
00544 { "contact-params",1,0, OPT_CONTACT_PARAMS},
00545 { "contact-uri-params",1,0, OPT_CONTACT_URI_PARAMS},
00546 { "auto-update-nat", 1, 0, OPT_AUTO_UPDATE_NAT},
00547 { "use-compact-form", 0, 0, OPT_USE_COMPACT_FORM},
00548 { "accept-redirect", 1, 0, OPT_ACCEPT_REDIRECT},
00549 { "no-force-lr",0, 0, OPT_NO_FORCE_LR},
00550 { "realm", 1, 0, OPT_REALM},
00551 { "username", 1, 0, OPT_USERNAME},
00552 { "password", 1, 0, OPT_PASSWORD},
00553 { "nameserver", 1, 0, OPT_NAMESERVER},
00554 { "stun-srv", 1, 0, OPT_STUN_SRV},
00555 { "add-buddy", 1, 0, OPT_ADD_BUDDY},
00556 { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
00557 { "no-presence", 0, 0, OPT_NO_PRESENCE},
00558 { "auto-answer",1, 0, OPT_AUTO_ANSWER},
00559 { "auto-play", 0, 0, OPT_AUTO_PLAY},
00560 { "auto-play-hangup",0, 0, OPT_AUTO_PLAY_HANGUP},
00561 { "auto-rec", 0, 0, OPT_AUTO_REC},
00562 { "auto-loop", 0, 0, OPT_AUTO_LOOP},
00563 { "auto-conf", 0, 0, OPT_AUTO_CONF},
00564 { "play-file", 1, 0, OPT_PLAY_FILE},
00565 { "play-tone", 1, 0, OPT_PLAY_TONE},
00566 { "rec-file", 1, 0, OPT_REC_FILE},
00567 { "rtp-port", 1, 0, OPT_RTP_PORT},
00568
00569 { "use-ice", 0, 0, OPT_USE_ICE},
00570 { "ice-regular",0, 0, OPT_ICE_REGULAR},
00571 { "use-turn", 0, 0, OPT_USE_TURN},
00572 { "ice-max-hosts",1, 0, OPT_ICE_MAX_HOSTS},
00573 { "ice-no-rtcp",0, 0, OPT_ICE_NO_RTCP},
00574 { "turn-srv", 1, 0, OPT_TURN_SRV},
00575 { "turn-tcp", 0, 0, OPT_TURN_TCP},
00576 { "turn-user", 1, 0, OPT_TURN_USER},
00577 { "turn-passwd",1, 0, OPT_TURN_PASSWD},
00578
00579 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00580 { "use-srtp", 1, 0, OPT_USE_SRTP},
00581 { "srtp-secure",1, 0, OPT_SRTP_SECURE},
00582 #endif
00583 { "add-codec", 1, 0, OPT_ADD_CODEC},
00584 { "dis-codec", 1, 0, OPT_DIS_CODEC},
00585 { "complexity", 1, 0, OPT_COMPLEXITY},
00586 { "quality", 1, 0, OPT_QUALITY},
00587 { "ptime", 1, 0, OPT_PTIME},
00588 { "no-vad", 0, 0, OPT_NO_VAD},
00589 { "ec-tail", 1, 0, OPT_EC_TAIL},
00590 { "ec-opt", 1, 0, OPT_EC_OPT},
00591 { "ilbc-mode", 1, 0, OPT_ILBC_MODE},
00592 { "rx-drop-pct",1, 0, OPT_RX_DROP_PCT},
00593 { "tx-drop-pct",1, 0, OPT_TX_DROP_PCT},
00594 { "next-account",0,0, OPT_NEXT_ACCOUNT},
00595 { "next-cred", 0, 0, OPT_NEXT_CRED},
00596 { "max-calls", 1, 0, OPT_MAX_CALLS},
00597 { "duration", 1, 0, OPT_DURATION},
00598 { "thread-cnt", 1, 0, OPT_THREAD_CNT},
00599 { "use-tls", 0, 0, OPT_USE_TLS},
00600 { "tls-ca-file",1, 0, OPT_TLS_CA_FILE},
00601 { "tls-cert-file",1,0, OPT_TLS_CERT_FILE},
00602 { "tls-privkey-file",1,0, OPT_TLS_PRIV_FILE},
00603 { "tls-password",1,0, OPT_TLS_PASSWORD},
00604 { "tls-verify-server", 0, 0, OPT_TLS_VERIFY_SERVER},
00605 { "tls-verify-client", 0, 0, OPT_TLS_VERIFY_CLIENT},
00606 { "tls-neg-timeout", 1, 0, OPT_TLS_NEG_TIMEOUT},
00607 { "tls-srv-name", 1, 0, OPT_TLS_SRV_NAME},
00608 { "capture-dev", 1, 0, OPT_CAPTURE_DEV},
00609 { "playback-dev", 1, 0, OPT_PLAYBACK_DEV},
00610 { "capture-lat", 1, 0, OPT_CAPTURE_LAT},
00611 { "playback-lat", 1, 0, OPT_PLAYBACK_LAT},
00612 { "stdout-refresh", 1, 0, OPT_STDOUT_REFRESH},
00613 { "stdout-refresh-text", 1, 0, OPT_STDOUT_REFRESH_TEXT},
00614 #ifdef _IONBF
00615 { "stdout-no-buf", 0, 0, OPT_STDOUT_NO_BUF },
00616 #endif
00617 { "snd-auto-close", 1, 0, OPT_SND_AUTO_CLOSE},
00618 { "no-tones", 0, 0, OPT_NO_TONES},
00619 { "jb-max-size", 1, 0, OPT_JB_MAX_SIZE},
00620 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
00621 { "ipv6", 0, 0, OPT_IPV6},
00622 #endif
00623 { "set-qos", 0, 0, OPT_QOS},
00624 { "use-timer", 0, 0, OPT_TIMER},
00625 { "timer-se", 1, 0, OPT_TIMER_SE},
00626 { "timer-min-se", 1, 0, OPT_TIMER_MIN_SE},
00627 { NULL, 0, 0, 0}
00628 };
00629 pj_status_t status;
00630 pjsua_acc_config *cur_acc;
00631 char *config_file = NULL;
00632 unsigned i;
00633
00634
00635 pj_optind = 0;
00636 while ((c=pj_getopt_long(argc, argv, "", long_options,
00637 &option_index)) != -1)
00638 {
00639 switch (c) {
00640 case OPT_CONFIG_FILE:
00641 config_file = pj_optarg;
00642 break;
00643 }
00644 if (config_file)
00645 break;
00646 }
00647
00648 if (config_file) {
00649 status = read_config_file(app_config.pool, config_file, &argc, &argv);
00650 if (status != 0)
00651 return status;
00652 }
00653
00654 cfg->acc_cnt = 0;
00655 cur_acc = &cfg->acc_cfg[0];
00656
00657
00658
00659
00660
00661 pj_optind = 0;
00662 while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
00663 pj_str_t tmp;
00664 long lval;
00665
00666 switch (c) {
00667
00668 case OPT_CONFIG_FILE:
00669
00670 break;
00671
00672 case OPT_LOG_FILE:
00673 cfg->log_cfg.log_filename = pj_str(pj_optarg);
00674 break;
00675
00676 case OPT_LOG_LEVEL:
00677 c = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00678 if (c < 0 || c > 6) {
00679 PJ_LOG(1,(THIS_FILE,
00680 "Error: expecting integer value 0-6 "
00681 "for --log-level"));
00682 return PJ_EINVAL;
00683 }
00684 cfg->log_cfg.level = c;
00685 pj_log_set_level( c );
00686 break;
00687
00688 case OPT_APP_LOG_LEVEL:
00689 cfg->log_cfg.console_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00690 if (cfg->log_cfg.console_level < 0 || cfg->log_cfg.console_level > 6) {
00691 PJ_LOG(1,(THIS_FILE,
00692 "Error: expecting integer value 0-6 "
00693 "for --app-log-level"));
00694 return PJ_EINVAL;
00695 }
00696 break;
00697
00698 case OPT_COLOR:
00699 cfg->log_cfg.decor |= PJ_LOG_HAS_COLOR;
00700 break;
00701
00702 case OPT_NO_COLOR:
00703 cfg->log_cfg.decor &= ~PJ_LOG_HAS_COLOR;
00704 break;
00705
00706 case OPT_LIGHT_BG:
00707 pj_log_set_color(1, PJ_TERM_COLOR_R);
00708 pj_log_set_color(2, PJ_TERM_COLOR_R | PJ_TERM_COLOR_G);
00709 pj_log_set_color(3, PJ_TERM_COLOR_B | PJ_TERM_COLOR_G);
00710 pj_log_set_color(4, 0);
00711 pj_log_set_color(5, 0);
00712 pj_log_set_color(77, 0);
00713 break;
00714
00715 case OPT_HELP:
00716 usage();
00717 return PJ_EINVAL;
00718
00719 case OPT_VERSION:
00720 pj_dump_config();
00721 return PJ_EINVAL;
00722
00723 case OPT_NULL_AUDIO:
00724 cfg->null_audio = PJ_TRUE;
00725 break;
00726
00727 case OPT_CLOCK_RATE:
00728 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00729 if (lval < 8000 || lval > 192000) {
00730 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
00731 "8000-192000 for conference clock rate"));
00732 return PJ_EINVAL;
00733 }
00734 cfg->media_cfg.clock_rate = lval;
00735 break;
00736
00737 case OPT_SND_CLOCK_RATE:
00738 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00739 if (lval < 8000 || lval > 192000) {
00740 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
00741 "8000-192000 for sound device clock rate"));
00742 return PJ_EINVAL;
00743 }
00744 cfg->media_cfg.snd_clock_rate = lval;
00745 break;
00746
00747 case OPT_STEREO:
00748 cfg->media_cfg.channel_count = 2;
00749 break;
00750
00751 case OPT_LOCAL_PORT:
00752 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00753 if (lval < 0 || lval > 65535) {
00754 PJ_LOG(1,(THIS_FILE,
00755 "Error: expecting integer value for "
00756 "--local-port"));
00757 return PJ_EINVAL;
00758 }
00759 cfg->udp_cfg.port = (pj_uint16_t)lval;
00760 break;
00761
00762 case OPT_IP_ADDR:
00763 cfg->udp_cfg.public_addr = pj_str(pj_optarg);
00764 cfg->rtp_cfg.public_addr = pj_str(pj_optarg);
00765 break;
00766
00767 case OPT_BOUND_ADDR:
00768 cfg->udp_cfg.bound_addr = pj_str(pj_optarg);
00769 cfg->rtp_cfg.bound_addr = pj_str(pj_optarg);
00770 break;
00771
00772 case OPT_NO_UDP:
00773 if (cfg->no_tcp) {
00774 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
00775 return PJ_EINVAL;
00776 }
00777
00778 cfg->no_udp = PJ_TRUE;
00779 break;
00780
00781 case OPT_NOREFERSUB:
00782 cfg->no_refersub = PJ_TRUE;
00783 break;
00784
00785 case OPT_NO_TCP:
00786 if (cfg->no_udp) {
00787 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
00788 return PJ_EINVAL;
00789 }
00790
00791 cfg->no_tcp = PJ_TRUE;
00792 break;
00793
00794 case OPT_PROXY:
00795 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00796 PJ_LOG(1,(THIS_FILE,
00797 "Error: invalid SIP URL '%s' "
00798 "in proxy argument", pj_optarg));
00799 return PJ_EINVAL;
00800 }
00801 cur_acc->proxy[cur_acc->proxy_cnt++] = pj_str(pj_optarg);
00802 break;
00803
00804 case OPT_OUTBOUND_PROXY:
00805 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00806 PJ_LOG(1,(THIS_FILE,
00807 "Error: invalid SIP URL '%s' "
00808 "in outbound proxy argument", pj_optarg));
00809 return PJ_EINVAL;
00810 }
00811 cfg->cfg.outbound_proxy[cfg->cfg.outbound_proxy_cnt++] = pj_str(pj_optarg);
00812 break;
00813
00814 case OPT_REGISTRAR:
00815 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00816 PJ_LOG(1,(THIS_FILE,
00817 "Error: invalid SIP URL '%s' in "
00818 "registrar argument", pj_optarg));
00819 return PJ_EINVAL;
00820 }
00821 cur_acc->reg_uri = pj_str(pj_optarg);
00822 break;
00823
00824 case OPT_REG_TIMEOUT:
00825 cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,pj_optarg));
00826 if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
00827 PJ_LOG(1,(THIS_FILE,
00828 "Error: invalid value for --reg-timeout "
00829 "(expecting 1-3600)"));
00830 return PJ_EINVAL;
00831 }
00832 break;
00833
00834 case OPT_PUBLISH:
00835 cur_acc->publish_enabled = PJ_TRUE;
00836 break;
00837
00838 case OPT_MWI:
00839 cur_acc->mwi_enabled = PJ_TRUE;
00840 break;
00841
00842 case OPT_100REL:
00843 cur_acc->require_100rel = PJ_TRUE;
00844 cfg->cfg.require_100rel = PJ_TRUE;
00845 break;
00846
00847 case OPT_TIMER:
00848 cur_acc->require_timer = PJ_TRUE;
00849 cfg->cfg.require_timer = PJ_TRUE;
00850 break;
00851
00852 case OPT_TIMER_SE:
00853 cur_acc->timer_setting.sess_expires = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00854 if (cur_acc->timer_setting.sess_expires < 90) {
00855 PJ_LOG(1,(THIS_FILE,
00856 "Error: invalid value for --timer-se "
00857 "(expecting higher than 90)"));
00858 return PJ_EINVAL;
00859 }
00860 cfg->cfg.timer_setting.sess_expires = cur_acc->timer_setting.sess_expires;
00861 break;
00862
00863 case OPT_TIMER_MIN_SE:
00864 cur_acc->timer_setting.min_se = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00865 if (cur_acc->timer_setting.min_se < 90) {
00866 PJ_LOG(1,(THIS_FILE,
00867 "Error: invalid value for --timer-min-se "
00868 "(expecting higher than 90)"));
00869 return PJ_EINVAL;
00870 }
00871 cfg->cfg.timer_setting.min_se = cur_acc->timer_setting.min_se;
00872 break;
00873
00874 case OPT_USE_IMS:
00875 cur_acc->auth_pref.initial_auth = PJ_TRUE;
00876 break;
00877
00878 case OPT_ID:
00879 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00880 PJ_LOG(1,(THIS_FILE,
00881 "Error: invalid SIP URL '%s' "
00882 "in local id argument", pj_optarg));
00883 return PJ_EINVAL;
00884 }
00885 cur_acc->id = pj_str(pj_optarg);
00886 break;
00887
00888 case OPT_CONTACT:
00889 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00890 PJ_LOG(1,(THIS_FILE,
00891 "Error: invalid SIP URL '%s' "
00892 "in contact argument", pj_optarg));
00893 return PJ_EINVAL;
00894 }
00895 cur_acc->force_contact = pj_str(pj_optarg);
00896 break;
00897
00898 case OPT_CONTACT_PARAMS:
00899 cur_acc->contact_params = pj_str(pj_optarg);
00900 break;
00901
00902 case OPT_CONTACT_URI_PARAMS:
00903 cur_acc->contact_uri_params = pj_str(pj_optarg);
00904 break;
00905
00906 case OPT_AUTO_UPDATE_NAT:
00907 cur_acc->allow_contact_rewrite = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00908 break;
00909
00910 case OPT_USE_COMPACT_FORM:
00911
00912 {
00913 extern pj_bool_t pjsip_use_compact_form;
00914 extern pj_bool_t pjsip_include_allow_hdr_in_dlg;
00915 extern pj_bool_t pjmedia_add_rtpmap_for_static_pt;
00916
00917 pjsip_use_compact_form = PJ_TRUE;
00918
00919 pjsip_include_allow_hdr_in_dlg = PJ_FALSE;
00920
00921 pjmedia_add_rtpmap_for_static_pt = PJ_FALSE;
00922 }
00923 break;
00924
00925 case OPT_ACCEPT_REDIRECT:
00926 cfg->redir_op = my_atoi(pj_optarg);
00927 if (cfg->redir_op<0 || cfg->redir_op>PJSIP_REDIRECT_STOP) {
00928 PJ_LOG(1,(THIS_FILE,
00929 "Error: accept-redirect value '%s' ", pj_optarg));
00930 return PJ_EINVAL;
00931 }
00932 break;
00933
00934 case OPT_NO_FORCE_LR:
00935 cfg->cfg.force_lr = PJ_FALSE;
00936 break;
00937
00938 case OPT_NEXT_ACCOUNT:
00939 cfg->acc_cnt++;
00940 cur_acc = &cfg->acc_cfg[cfg->acc_cnt];
00941 break;
00942
00943 case OPT_USERNAME:
00944 cur_acc->cred_info[cur_acc->cred_count].username = pj_str(pj_optarg);
00945 cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("Digest");
00946 break;
00947
00948 case OPT_REALM:
00949 cur_acc->cred_info[cur_acc->cred_count].realm = pj_str(pj_optarg);
00950 break;
00951
00952 case OPT_PASSWORD:
00953 cur_acc->cred_info[cur_acc->cred_count].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
00954 cur_acc->cred_info[cur_acc->cred_count].data = pj_str(pj_optarg);
00955 #if PJSIP_HAS_DIGEST_AKA_AUTH
00956 cur_acc->cred_info[cur_acc->cred_count].data_type |= PJSIP_CRED_DATA_EXT_AKA;
00957 cur_acc->cred_info[cur_acc->cred_count].ext.aka.k = pj_str(pj_optarg);
00958 cur_acc->cred_info[cur_acc->cred_count].ext.aka.cb = &pjsip_auth_create_aka_response;
00959 #endif
00960 break;
00961
00962 case OPT_NEXT_CRED:
00963 cur_acc->cred_count++;
00964 break;
00965
00966 case OPT_NAMESERVER:
00967 cfg->cfg.nameserver[cfg->cfg.nameserver_count++] = pj_str(pj_optarg);
00968 if (cfg->cfg.nameserver_count > PJ_ARRAY_SIZE(cfg->cfg.nameserver)) {
00969 PJ_LOG(1,(THIS_FILE, "Error: too many nameservers"));
00970 return PJ_ETOOMANY;
00971 }
00972 break;
00973
00974 case OPT_STUN_SRV:
00975 cfg->cfg.stun_host = pj_str(pj_optarg);
00976 if (cfg->cfg.stun_srv_cnt==PJ_ARRAY_SIZE(cfg->cfg.stun_srv)) {
00977 PJ_LOG(1,(THIS_FILE, "Error: too many STUN servers"));
00978 return PJ_ETOOMANY;
00979 }
00980 cfg->cfg.stun_srv[cfg->cfg.stun_srv_cnt++] = pj_str(pj_optarg);
00981 break;
00982
00983 case OPT_ADD_BUDDY:
00984 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00985 PJ_LOG(1,(THIS_FILE,
00986 "Error: invalid URL '%s' in "
00987 "--add-buddy option", pj_optarg));
00988 return -1;
00989 }
00990 if (cfg->buddy_cnt == PJ_ARRAY_SIZE(cfg->buddy_cfg)) {
00991 PJ_LOG(1,(THIS_FILE,
00992 "Error: too many buddies in buddy list."));
00993 return -1;
00994 }
00995 cfg->buddy_cfg[cfg->buddy_cnt].uri = pj_str(pj_optarg);
00996 cfg->buddy_cnt++;
00997 break;
00998
00999 case OPT_AUTO_PLAY:
01000 cfg->auto_play = 1;
01001 break;
01002
01003 case OPT_AUTO_PLAY_HANGUP:
01004 cfg->auto_play_hangup = 1;
01005 break;
01006
01007 case OPT_AUTO_REC:
01008 cfg->auto_rec = 1;
01009 break;
01010
01011 case OPT_AUTO_LOOP:
01012 cfg->auto_loop = 1;
01013 break;
01014
01015 case OPT_AUTO_CONF:
01016 cfg->auto_conf = 1;
01017 break;
01018
01019 case OPT_PLAY_FILE:
01020 cfg->wav_files[cfg->wav_count++] = pj_str(pj_optarg);
01021 break;
01022
01023 case OPT_PLAY_TONE:
01024 {
01025 int f1, f2, on, off;
01026 int n;
01027
01028 n = sscanf(pj_optarg, "%d,%d,%d,%d", &f1, &f2, &on, &off);
01029 if (n != 4) {
01030 puts("Expecting f1,f2,on,off in --play-tone");
01031 return -1;
01032 }
01033
01034 cfg->tones[cfg->tone_count].freq1 = (short)f1;
01035 cfg->tones[cfg->tone_count].freq2 = (short)f2;
01036 cfg->tones[cfg->tone_count].on_msec = (short)on;
01037 cfg->tones[cfg->tone_count].off_msec = (short)off;
01038 ++cfg->tone_count;
01039 }
01040 break;
01041
01042 case OPT_REC_FILE:
01043 cfg->rec_file = pj_str(pj_optarg);
01044 break;
01045
01046 case OPT_USE_ICE:
01047 cfg->media_cfg.enable_ice = PJ_TRUE;
01048 break;
01049
01050 case OPT_ICE_REGULAR:
01051 cfg->media_cfg.ice_opt.aggressive = PJ_FALSE;
01052 break;
01053
01054 case OPT_USE_TURN:
01055 cfg->media_cfg.enable_turn = PJ_TRUE;
01056 break;
01057
01058 case OPT_ICE_MAX_HOSTS:
01059 cfg->media_cfg.ice_max_host_cands = my_atoi(pj_optarg);
01060 break;
01061
01062 case OPT_ICE_NO_RTCP:
01063 cfg->media_cfg.ice_no_rtcp = PJ_TRUE;
01064 break;
01065
01066 case OPT_TURN_SRV:
01067 cfg->media_cfg.turn_server = pj_str(pj_optarg);
01068 break;
01069
01070 case OPT_TURN_TCP:
01071 cfg->media_cfg.turn_conn_type = PJ_TURN_TP_TCP;
01072 break;
01073
01074 case OPT_TURN_USER:
01075 cfg->media_cfg.turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
01076 cfg->media_cfg.turn_auth_cred.data.static_cred.realm = pj_str("*");
01077 cfg->media_cfg.turn_auth_cred.data.static_cred.username = pj_str(pj_optarg);
01078 break;
01079
01080 case OPT_TURN_PASSWD:
01081 cfg->media_cfg.turn_auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
01082 cfg->media_cfg.turn_auth_cred.data.static_cred.data = pj_str(pj_optarg);
01083 break;
01084
01085 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
01086 case OPT_USE_SRTP:
01087 app_config.cfg.use_srtp = my_atoi(pj_optarg);
01088 if (!pj_isdigit(*pj_optarg) || app_config.cfg.use_srtp > 2) {
01089 PJ_LOG(1,(THIS_FILE, "Invalid value for --use-srtp option"));
01090 return -1;
01091 }
01092 cur_acc->use_srtp = app_config.cfg.use_srtp;
01093 break;
01094 case OPT_SRTP_SECURE:
01095 app_config.cfg.srtp_secure_signaling = my_atoi(pj_optarg);
01096 if (!pj_isdigit(*pj_optarg) ||
01097 app_config.cfg.srtp_secure_signaling > 2)
01098 {
01099 PJ_LOG(1,(THIS_FILE, "Invalid value for --srtp-secure option"));
01100 return -1;
01101 }
01102 cur_acc->srtp_secure_signaling = app_config.cfg.srtp_secure_signaling;
01103 break;
01104 #endif
01105
01106 case OPT_RTP_PORT:
01107 cfg->rtp_cfg.port = my_atoi(pj_optarg);
01108 if (cfg->rtp_cfg.port == 0) {
01109 enum { START_PORT=4000 };
01110 unsigned range;
01111
01112 range = (65535-START_PORT-PJSUA_MAX_CALLS*2);
01113 cfg->rtp_cfg.port = START_PORT +
01114 ((pj_rand() % range) & 0xFFFE);
01115 }
01116
01117 if (cfg->rtp_cfg.port < 1 || cfg->rtp_cfg.port > 65535) {
01118 PJ_LOG(1,(THIS_FILE,
01119 "Error: rtp-port argument value "
01120 "(expecting 1-65535"));
01121 return -1;
01122 }
01123 break;
01124
01125 case OPT_DIS_CODEC:
01126 cfg->codec_dis[cfg->codec_dis_cnt++] = pj_str(pj_optarg);
01127 break;
01128
01129 case OPT_ADD_CODEC:
01130 cfg->codec_arg[cfg->codec_cnt++] = pj_str(pj_optarg);
01131 break;
01132
01133
01134
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145 case OPT_DURATION:
01146 cfg->duration = my_atoi(pj_optarg);
01147 break;
01148
01149 case OPT_THREAD_CNT:
01150 cfg->cfg.thread_cnt = my_atoi(pj_optarg);
01151 if (cfg->cfg.thread_cnt > 128) {
01152 PJ_LOG(1,(THIS_FILE,
01153 "Error: invalid --thread-cnt option"));
01154 return -1;
01155 }
01156 break;
01157
01158 case OPT_PTIME:
01159 cfg->media_cfg.ptime = my_atoi(pj_optarg);
01160 if (cfg->media_cfg.ptime < 10 || cfg->media_cfg.ptime > 1000) {
01161 PJ_LOG(1,(THIS_FILE,
01162 "Error: invalid --ptime option"));
01163 return -1;
01164 }
01165 break;
01166
01167 case OPT_NO_VAD:
01168 cfg->media_cfg.no_vad = PJ_TRUE;
01169 break;
01170
01171 case OPT_EC_TAIL:
01172 cfg->media_cfg.ec_tail_len = my_atoi(pj_optarg);
01173 if (cfg->media_cfg.ec_tail_len > 1000) {
01174 PJ_LOG(1,(THIS_FILE, "I think the ec-tail length setting "
01175 "is too big"));
01176 return -1;
01177 }
01178 break;
01179
01180 case OPT_EC_OPT:
01181 cfg->media_cfg.ec_options = my_atoi(pj_optarg);
01182 break;
01183
01184 case OPT_QUALITY:
01185 cfg->media_cfg.quality = my_atoi(pj_optarg);
01186 if (cfg->media_cfg.quality < 0 || cfg->media_cfg.quality > 10) {
01187 PJ_LOG(1,(THIS_FILE,
01188 "Error: invalid --quality (expecting 0-10"));
01189 return -1;
01190 }
01191 break;
01192
01193 case OPT_ILBC_MODE:
01194 cfg->media_cfg.ilbc_mode = my_atoi(pj_optarg);
01195 if (cfg->media_cfg.ilbc_mode!=20 && cfg->media_cfg.ilbc_mode!=30) {
01196 PJ_LOG(1,(THIS_FILE,
01197 "Error: invalid --ilbc-mode (expecting 20 or 30"));
01198 return -1;
01199 }
01200 break;
01201
01202 case OPT_RX_DROP_PCT:
01203 cfg->media_cfg.rx_drop_pct = my_atoi(pj_optarg);
01204 if (cfg->media_cfg.rx_drop_pct > 100) {
01205 PJ_LOG(1,(THIS_FILE,
01206 "Error: invalid --rx-drop-pct (expecting <= 100"));
01207 return -1;
01208 }
01209 break;
01210
01211 case OPT_TX_DROP_PCT:
01212 cfg->media_cfg.tx_drop_pct = my_atoi(pj_optarg);
01213 if (cfg->media_cfg.tx_drop_pct > 100) {
01214 PJ_LOG(1,(THIS_FILE,
01215 "Error: invalid --tx-drop-pct (expecting <= 100"));
01216 return -1;
01217 }
01218 break;
01219
01220 case OPT_AUTO_ANSWER:
01221 cfg->auto_answer = my_atoi(pj_optarg);
01222 if (cfg->auto_answer < 100 || cfg->auto_answer > 699) {
01223 PJ_LOG(1,(THIS_FILE,
01224 "Error: invalid code in --auto-answer "
01225 "(expecting 100-699"));
01226 return -1;
01227 }
01228 break;
01229
01230 case OPT_MAX_CALLS:
01231 cfg->cfg.max_calls = my_atoi(pj_optarg);
01232 if (cfg->cfg.max_calls < 1 || cfg->cfg.max_calls > PJSUA_MAX_CALLS) {
01233 PJ_LOG(1,(THIS_FILE,"Error: maximum call setting exceeds "
01234 "compile time limit (PJSUA_MAX_CALLS=%d)",
01235 PJSUA_MAX_CALLS));
01236 return -1;
01237 }
01238 break;
01239
01240 case OPT_USE_TLS:
01241 cfg->use_tls = PJ_TRUE;
01242 #if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
01243 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
01244 return -1;
01245 #endif
01246 break;
01247
01248 case OPT_TLS_CA_FILE:
01249 cfg->udp_cfg.tls_setting.ca_list_file = pj_str(pj_optarg);
01250 #if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
01251 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
01252 return -1;
01253 #endif
01254 break;
01255
01256 case OPT_TLS_CERT_FILE:
01257 cfg->udp_cfg.tls_setting.cert_file = pj_str(pj_optarg);
01258 #if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
01259 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
01260 return -1;
01261 #endif
01262 break;
01263
01264 case OPT_TLS_PRIV_FILE:
01265 cfg->udp_cfg.tls_setting.privkey_file = pj_str(pj_optarg);
01266 break;
01267
01268 case OPT_TLS_PASSWORD:
01269 cfg->udp_cfg.tls_setting.password = pj_str(pj_optarg);
01270 #if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0
01271 PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured"));
01272 return -1;
01273 #endif
01274 break;
01275
01276 case OPT_TLS_VERIFY_SERVER:
01277 cfg->udp_cfg.tls_setting.verify_server = PJ_TRUE;
01278 break;
01279
01280 case OPT_TLS_VERIFY_CLIENT:
01281 cfg->udp_cfg.tls_setting.verify_client = PJ_TRUE;
01282 break;
01283
01284 case OPT_TLS_NEG_TIMEOUT:
01285 cfg->udp_cfg.tls_setting.timeout.sec = atoi(pj_optarg);
01286 break;
01287
01288 case OPT_TLS_SRV_NAME:
01289 cfg->udp_cfg.tls_setting.server_name = pj_str(pj_optarg);
01290 break;
01291
01292 case OPT_CAPTURE_DEV:
01293 cfg->capture_dev = atoi(pj_optarg);
01294 break;
01295
01296 case OPT_PLAYBACK_DEV:
01297 cfg->playback_dev = atoi(pj_optarg);
01298 break;
01299
01300 case OPT_STDOUT_REFRESH:
01301 stdout_refresh = atoi(pj_optarg);
01302 break;
01303
01304 case OPT_STDOUT_REFRESH_TEXT:
01305 stdout_refresh_text = pj_optarg;
01306 break;
01307
01308 #ifdef _IONBF
01309 case OPT_STDOUT_NO_BUF:
01310 setvbuf(stdout, NULL, _IONBF, 0);
01311 break;
01312 #endif
01313
01314 case OPT_CAPTURE_LAT:
01315 cfg->capture_lat = atoi(pj_optarg);
01316 break;
01317
01318 case OPT_PLAYBACK_LAT:
01319 cfg->playback_lat = atoi(pj_optarg);
01320 break;
01321
01322 case OPT_SND_AUTO_CLOSE:
01323 cfg->media_cfg.snd_auto_close_time = atoi(pj_optarg);
01324 break;
01325
01326 case OPT_NO_TONES:
01327 cfg->no_tones = PJ_TRUE;
01328 break;
01329
01330 case OPT_JB_MAX_SIZE:
01331 cfg->media_cfg.jb_max = atoi(pj_optarg);
01332 break;
01333
01334 #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
01335 case OPT_IPV6:
01336 cfg->ipv6 = PJ_TRUE;
01337 break;
01338 #endif
01339 case OPT_QOS:
01340 cfg->enable_qos = PJ_TRUE;
01341
01342 cfg->rtp_cfg.qos_type = PJ_QOS_TYPE_VOICE;
01343
01344
01345
01346
01347 cfg->udp_cfg.qos_params.flags = PJ_QOS_PARAM_HAS_DSCP;
01348 cfg->udp_cfg.qos_params.dscp_val = 0x18;
01349 break;
01350 default:
01351 PJ_LOG(1,(THIS_FILE,
01352 "Argument \"%s\" is not valid. Use --help to see help",
01353 argv[pj_optind-1]));
01354 return -1;
01355 }
01356 }
01357
01358 if (pj_optind != argc) {
01359 pj_str_t uri_arg;
01360
01361 if (pjsua_verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
01362 PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
01363 return -1;
01364 }
01365 uri_arg = pj_str(argv[pj_optind]);
01366 if (uri_to_call)
01367 *uri_to_call = uri_arg;
01368 pj_optind++;
01369
01370
01371 for (i=0; i<cfg->buddy_cnt; ++i) {
01372 if (pj_stricmp(&cfg->buddy_cfg[i].uri, &uri_arg)==0)
01373 break;
01374 }
01375 if (i == cfg->buddy_cnt && cfg->buddy_cnt < PJSUA_MAX_BUDDIES) {
01376 cfg->buddy_cfg[cfg->buddy_cnt++].uri = uri_arg;
01377 }
01378
01379 } else {
01380 if (uri_to_call)
01381 uri_to_call->slen = 0;
01382 }
01383
01384 if (pj_optind != argc) {
01385 PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
01386 return PJ_EINVAL;
01387 }
01388
01389 if (cfg->acc_cfg[cfg->acc_cnt].id.slen)
01390 cfg->acc_cnt++;
01391
01392 for (i=0; i<cfg->acc_cnt; ++i) {
01393 pjsua_acc_config *acfg = &cfg->acc_cfg[i];
01394
01395 if (acfg->cred_info[acfg->cred_count].username.slen)
01396 {
01397 acfg->cred_count++;
01398 }
01399
01400
01401
01402
01403
01404
01405
01406 if (acfg->auth_pref.initial_auth && acfg->cred_count) {
01407
01408 if (*acfg->cred_info[0].realm.ptr=='*') {
01409 PJ_LOG(1,(THIS_FILE,
01410 "Error: cannot use '*' as realm with IMS"));
01411 return PJ_EINVAL;
01412 }
01413
01414
01415 if (strchr(acfg->cred_info[0].username.ptr, '@')==0) {
01416 PJ_LOG(1,(THIS_FILE,
01417 "Error: Username for authentication must "
01418 "be in user@domain format with IMS"));
01419 return PJ_EINVAL;
01420 }
01421 }
01422 }
01423
01424
01425 return PJ_SUCCESS;
01426 }
01427
01428
01429
01430
01431
01432 static void write_account_settings(int acc_index, pj_str_t *result)
01433 {
01434 unsigned i;
01435 char line[128];
01436 pjsua_acc_config *acc_cfg = &app_config.acc_cfg[acc_index];
01437
01438
01439 pj_ansi_sprintf(line, "\n#\n# Account %d:\n#\n", acc_index);
01440 pj_strcat2(result, line);
01441
01442
01443
01444 if (acc_cfg->id.slen) {
01445 pj_ansi_sprintf(line, "--id %.*s\n",
01446 (int)acc_cfg->id.slen,
01447 acc_cfg->id.ptr);
01448 pj_strcat2(result, line);
01449 }
01450
01451
01452 if (acc_cfg->reg_uri.slen) {
01453 pj_ansi_sprintf(line, "--registrar %.*s\n",
01454 (int)acc_cfg->reg_uri.slen,
01455 acc_cfg->reg_uri.ptr);
01456 pj_strcat2(result, line);
01457
01458 pj_ansi_sprintf(line, "--reg-timeout %u\n",
01459 acc_cfg->reg_timeout);
01460 pj_strcat2(result, line);
01461 }
01462
01463
01464 if (acc_cfg->force_contact.slen) {
01465 pj_ansi_sprintf(line, "--contact %.*s\n",
01466 (int)acc_cfg->force_contact.slen,
01467 acc_cfg->force_contact.ptr);
01468 pj_strcat2(result, line);
01469 }
01470
01471
01472 if (acc_cfg->contact_params.slen) {
01473 pj_ansi_sprintf(line, "--contact-params %.*s\n",
01474 (int)acc_cfg->contact_params.slen,
01475 acc_cfg->contact_params.ptr);
01476 pj_strcat2(result, line);
01477 }
01478
01479
01480 if (acc_cfg->contact_uri_params.slen) {
01481 pj_ansi_sprintf(line, "--contact-uri-params %.*s\n",
01482 (int)acc_cfg->contact_uri_params.slen,
01483 acc_cfg->contact_uri_params.ptr);
01484 pj_strcat2(result, line);
01485 }
01486
01487
01488 if (acc_cfg->allow_contact_rewrite!=1)
01489 {
01490 pj_ansi_sprintf(line, "--auto-update-nat %i\n",
01491 (int)acc_cfg->allow_contact_rewrite);
01492 pj_strcat2(result, line);
01493 }
01494
01495 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
01496
01497 if (acc_cfg->use_srtp) {
01498 pj_ansi_sprintf(line, "--use-srtp %i\n",
01499 (int)acc_cfg->use_srtp);
01500 pj_strcat2(result, line);
01501 }
01502 #endif
01503
01504
01505 for (i=0; i<acc_cfg->proxy_cnt; ++i) {
01506 pj_ansi_sprintf(line, "--proxy %.*s\n",
01507 (int)acc_cfg->proxy[i].slen,
01508 acc_cfg->proxy[i].ptr);
01509 pj_strcat2(result, line);
01510 }
01511
01512
01513 for (i=0; i<acc_cfg->cred_count; ++i) {
01514 if (acc_cfg->cred_info[i].realm.slen) {
01515 pj_ansi_sprintf(line, "--realm %.*s\n",
01516 (int)acc_cfg->cred_info[i].realm.slen,
01517 acc_cfg->cred_info[i].realm.ptr);
01518 pj_strcat2(result, line);
01519 }
01520
01521 if (acc_cfg->cred_info[i].username.slen) {
01522 pj_ansi_sprintf(line, "--username %.*s\n",
01523 (int)acc_cfg->cred_info[i].username.slen,
01524 acc_cfg->cred_info[i].username.ptr);
01525 pj_strcat2(result, line);
01526 }
01527
01528 if (acc_cfg->cred_info[i].data.slen) {
01529 pj_ansi_sprintf(line, "--password %.*s\n",
01530 (int)acc_cfg->cred_info[i].data.slen,
01531 acc_cfg->cred_info[i].data.ptr);
01532 pj_strcat2(result, line);
01533 }
01534
01535 if (i != acc_cfg->cred_count - 1)
01536 pj_strcat2(result, "--next-cred\n");
01537 }
01538
01539
01540 if (acc_cfg->require_100rel) {
01541 pj_strcat2(result, "--use-100rel\n");
01542 }
01543
01544
01545 if (acc_cfg->require_timer) {
01546 pj_strcat2(result, "--use-timer\n");
01547 }
01548 if (acc_cfg->timer_setting.min_se != 90) {
01549 pj_ansi_sprintf(line, "--timer-min-se %d\n",
01550 acc_cfg->timer_setting.min_se);
01551 pj_strcat2(result, line);
01552 }
01553 if (acc_cfg->timer_setting.sess_expires != PJSIP_SESS_TIMER_DEF_SE) {
01554 pj_ansi_sprintf(line, "--timer-se %d\n",
01555 acc_cfg->timer_setting.sess_expires);
01556 pj_strcat2(result, line);
01557 }
01558
01559
01560 if (acc_cfg->publish_enabled)
01561 pj_strcat2(result, "--publish\n");
01562
01563
01564 if (acc_cfg->mwi_enabled)
01565 pj_strcat2(result, "--mwi\n");
01566 }
01567
01568
01569
01570
01571
01572 static int write_settings(const struct app_config *config,
01573 char *buf, pj_size_t max)
01574 {
01575 unsigned acc_index;
01576 unsigned i;
01577 pj_str_t cfg;
01578 char line[128];
01579 extern pj_bool_t pjsip_use_compact_form;
01580
01581 PJ_UNUSED_ARG(max);
01582
01583 cfg.ptr = buf;
01584 cfg.slen = 0;
01585
01586
01587 pj_strcat2(&cfg, "#\n# Logging options:\n#\n");
01588 pj_ansi_sprintf(line, "--log-level %d\n",
01589 config->log_cfg.level);
01590 pj_strcat2(&cfg, line);
01591
01592 pj_ansi_sprintf(line, "--app-log-level %d\n",
01593 config->log_cfg.console_level);
01594 pj_strcat2(&cfg, line);
01595
01596 if (config->log_cfg.log_filename.slen) {
01597 pj_ansi_sprintf(line, "--log-file %.*s\n",
01598 (int)config->log_cfg.log_filename.slen,
01599 config->log_cfg.log_filename.ptr);
01600 pj_strcat2(&cfg, line);
01601 }
01602
01603
01604
01605 for (acc_index=0; acc_index < config->acc_cnt; ++acc_index) {
01606
01607 write_account_settings(acc_index, &cfg);
01608
01609 if (acc_index < config->acc_cnt-1)
01610 pj_strcat2(&cfg, "--next-account\n");
01611 }
01612
01613
01614 pj_strcat2(&cfg, "\n#\n# Network settings:\n#\n");
01615
01616
01617 for (i=0; i<config->cfg.nameserver_count; ++i) {
01618 pj_ansi_sprintf(line, "--nameserver %.*s\n",
01619 (int)config->cfg.nameserver[i].slen,
01620 config->cfg.nameserver[i].ptr);
01621 pj_strcat2(&cfg, line);
01622 }
01623
01624
01625 for (i=0; i<config->cfg.outbound_proxy_cnt; ++i) {
01626 pj_ansi_sprintf(line, "--outbound %.*s\n",
01627 (int)config->cfg.outbound_proxy[i].slen,
01628 config->cfg.outbound_proxy[i].ptr);
01629 pj_strcat2(&cfg, line);
01630 }
01631
01632
01633 if (config->ipv6) {
01634 pj_strcat2(&cfg, "--ipv6\n");
01635 }
01636 if (config->enable_qos) {
01637 pj_strcat2(&cfg, "--set-qos\n");
01638 }
01639
01640
01641 pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port);
01642 pj_strcat2(&cfg, line);
01643
01644
01645 if (config->udp_cfg.public_addr.slen) {
01646 pj_ansi_sprintf(line, "--ip-addr %.*s\n",
01647 (int)config->udp_cfg.public_addr.slen,
01648 config->udp_cfg.public_addr.ptr);
01649 pj_strcat2(&cfg, line);
01650 }
01651
01652
01653 if (config->udp_cfg.bound_addr.slen) {
01654 pj_ansi_sprintf(line, "--bound-addr %.*s\n",
01655 (int)config->udp_cfg.bound_addr.slen,
01656 config->udp_cfg.bound_addr.ptr);
01657 pj_strcat2(&cfg, line);
01658 }
01659
01660
01661 if (config->no_tcp) {
01662 pj_strcat2(&cfg, "--no-tcp\n");
01663 }
01664
01665
01666 if (config->no_udp) {
01667 pj_strcat2(&cfg, "--no-udp\n");
01668 }
01669
01670
01671 for (i=0; i<config->cfg.stun_srv_cnt; ++i) {
01672 pj_ansi_sprintf(line, "--stun-srv %.*s\n",
01673 (int)config->cfg.stun_srv[i].slen,
01674 config->cfg.stun_srv[i].ptr);
01675 pj_strcat2(&cfg, line);
01676 }
01677
01678
01679 if (config->use_tls)
01680 pj_strcat2(&cfg, "--use-tls\n");
01681 if (config->udp_cfg.tls_setting.ca_list_file.slen) {
01682 pj_ansi_sprintf(line, "--tls-ca-file %.*s\n",
01683 (int)config->udp_cfg.tls_setting.ca_list_file.slen,
01684 config->udp_cfg.tls_setting.ca_list_file.ptr);
01685 pj_strcat2(&cfg, line);
01686 }
01687 if (config->udp_cfg.tls_setting.cert_file.slen) {
01688 pj_ansi_sprintf(line, "--tls-cert-file %.*s\n",
01689 (int)config->udp_cfg.tls_setting.cert_file.slen,
01690 config->udp_cfg.tls_setting.cert_file.ptr);
01691 pj_strcat2(&cfg, line);
01692 }
01693 if (config->udp_cfg.tls_setting.privkey_file.slen) {
01694 pj_ansi_sprintf(line, "--tls-privkey-file %.*s\n",
01695 (int)config->udp_cfg.tls_setting.privkey_file.slen,
01696 config->udp_cfg.tls_setting.privkey_file.ptr);
01697 pj_strcat2(&cfg, line);
01698 }
01699
01700 if (config->udp_cfg.tls_setting.password.slen) {
01701 pj_ansi_sprintf(line, "--tls-password %.*s\n",
01702 (int)config->udp_cfg.tls_setting.password.slen,
01703 config->udp_cfg.tls_setting.password.ptr);
01704 pj_strcat2(&cfg, line);
01705 }
01706
01707 if (config->udp_cfg.tls_setting.server_name.slen) {
01708 pj_ansi_sprintf(line, "--tls-srv-name %.*s\n",
01709 (int)config->udp_cfg.tls_setting.server_name.slen,
01710 config->udp_cfg.tls_setting.server_name.ptr);
01711 pj_strcat2(&cfg, line);
01712 }
01713
01714 if (config->udp_cfg.tls_setting.verify_server)
01715 pj_strcat2(&cfg, "--tls-verify-server\n");
01716
01717 if (config->udp_cfg.tls_setting.verify_client)
01718 pj_strcat2(&cfg, "--tls-verify-client\n");
01719
01720 if (config->udp_cfg.tls_setting.timeout.sec) {
01721 pj_ansi_sprintf(line, "--tls-neg-timeout %d\n",
01722 (int)config->udp_cfg.tls_setting.timeout.sec);
01723 pj_strcat2(&cfg, line);
01724 }
01725
01726 pj_strcat2(&cfg, "\n#\n# Media settings:\n#\n");
01727
01728
01729 #if PJMEDIA_HAS_SRTP
01730 if (app_config.cfg.use_srtp != PJSUA_DEFAULT_USE_SRTP) {
01731 pj_ansi_sprintf(line, "--use-srtp %d\n",
01732 app_config.cfg.use_srtp);
01733 pj_strcat2(&cfg, line);
01734 }
01735 if (app_config.cfg.srtp_secure_signaling !=
01736 PJSUA_DEFAULT_SRTP_SECURE_SIGNALING)
01737 {
01738 pj_ansi_sprintf(line, "--srtp-secure %d\n",
01739 app_config.cfg.srtp_secure_signaling);
01740 pj_strcat2(&cfg, line);
01741 }
01742 #endif
01743
01744
01745 if (config->media_cfg.enable_ice)
01746 pj_strcat2(&cfg, "--use-ice\n");
01747
01748 if (config->media_cfg.ice_opt.aggressive == PJ_FALSE)
01749 pj_strcat2(&cfg, "--ice-regular\n");
01750
01751 if (config->media_cfg.enable_turn)
01752 pj_strcat2(&cfg, "--use-turn\n");
01753
01754 if (config->media_cfg.ice_max_host_cands >= 0) {
01755 pj_ansi_sprintf(line, "--ice_max_host_cands %d\n",
01756 config->media_cfg.ice_max_host_cands);
01757 pj_strcat2(&cfg, line);
01758 }
01759
01760 if (config->media_cfg.ice_no_rtcp)
01761 pj_strcat2(&cfg, "--ice-no-rtcp\n");
01762
01763 if (config->media_cfg.turn_server.slen) {
01764 pj_ansi_sprintf(line, "--turn-srv %.*s\n",
01765 (int)config->media_cfg.turn_server.slen,
01766 config->media_cfg.turn_server.ptr);
01767 pj_strcat2(&cfg, line);
01768 }
01769
01770 if (config->media_cfg.turn_conn_type == PJ_TURN_TP_TCP)
01771 pj_strcat2(&cfg, "--turn-tcp\n");
01772
01773 if (config->media_cfg.turn_auth_cred.data.static_cred.username.slen) {
01774 pj_ansi_sprintf(line, "--turn-user %.*s\n",
01775 (int)config->media_cfg.turn_auth_cred.data.static_cred.username.slen,
01776 config->media_cfg.turn_auth_cred.data.static_cred.username.ptr);
01777 pj_strcat2(&cfg, line);
01778 }
01779
01780 if (config->media_cfg.turn_auth_cred.data.static_cred.data.slen) {
01781 pj_ansi_sprintf(line, "--turn-passwd %.*s\n",
01782 (int)config->media_cfg.turn_auth_cred.data.static_cred.data.slen,
01783 config->media_cfg.turn_auth_cred.data.static_cred.data.ptr);
01784 pj_strcat2(&cfg, line);
01785 }
01786
01787
01788 if (config->null_audio)
01789 pj_strcat2(&cfg, "--null-audio\n");
01790 if (config->auto_play)
01791 pj_strcat2(&cfg, "--auto-play\n");
01792 if (config->auto_loop)
01793 pj_strcat2(&cfg, "--auto-loop\n");
01794 if (config->auto_conf)
01795 pj_strcat2(&cfg, "--auto-conf\n");
01796 for (i=0; i<config->wav_count; ++i) {
01797 pj_ansi_sprintf(line, "--play-file %s\n",
01798 config->wav_files[i].ptr);
01799 pj_strcat2(&cfg, line);
01800 }
01801 for (i=0; i<config->tone_count; ++i) {
01802 pj_ansi_sprintf(line, "--play-tone %d,%d,%d,%d\n",
01803 config->tones[i].freq1, config->tones[i].freq2,
01804 config->tones[i].on_msec, config->tones[i].off_msec);
01805 pj_strcat2(&cfg, line);
01806 }
01807 if (config->rec_file.slen) {
01808 pj_ansi_sprintf(line, "--rec-file %s\n",
01809 config->rec_file.ptr);
01810 pj_strcat2(&cfg, line);
01811 }
01812 if (config->auto_rec)
01813 pj_strcat2(&cfg, "--auto-rec\n");
01814 if (config->capture_dev != PJSUA_INVALID_ID) {
01815 pj_ansi_sprintf(line, "--capture-dev %d\n", config->capture_dev);
01816 pj_strcat2(&cfg, line);
01817 }
01818 if (config->playback_dev != PJSUA_INVALID_ID) {
01819 pj_ansi_sprintf(line, "--playback-dev %d\n", config->playback_dev);
01820 pj_strcat2(&cfg, line);
01821 }
01822 if (config->media_cfg.snd_auto_close_time != -1) {
01823 pj_ansi_sprintf(line, "--snd-auto-close %d\n",
01824 config->media_cfg.snd_auto_close_time);
01825 pj_strcat2(&cfg, line);
01826 }
01827 if (config->no_tones) {
01828 pj_strcat2(&cfg, "--no-tones\n");
01829 }
01830 if (config->media_cfg.jb_max != -1) {
01831 pj_ansi_sprintf(line, "--jb-max-size %d\n",
01832 config->media_cfg.jb_max);
01833 pj_strcat2(&cfg, line);
01834 }
01835
01836
01837 if (config->capture_lat != PJMEDIA_SND_DEFAULT_REC_LATENCY) {
01838 pj_ansi_sprintf(line, "--capture-lat %d\n", config->capture_lat);
01839 pj_strcat2(&cfg, line);
01840 }
01841 if (config->playback_lat != PJMEDIA_SND_DEFAULT_PLAY_LATENCY) {
01842 pj_ansi_sprintf(line, "--playback-lat %d\n", config->playback_lat);
01843 pj_strcat2(&cfg, line);
01844 }
01845
01846
01847 if (config->media_cfg.clock_rate != PJSUA_DEFAULT_CLOCK_RATE) {
01848 pj_ansi_sprintf(line, "--clock-rate %d\n",
01849 config->media_cfg.clock_rate);
01850 pj_strcat2(&cfg, line);
01851 } else {
01852 pj_ansi_sprintf(line, "#using default --clock-rate %d\n",
01853 config->media_cfg.clock_rate);
01854 pj_strcat2(&cfg, line);
01855 }
01856
01857 if (config->media_cfg.snd_clock_rate &&
01858 config->media_cfg.snd_clock_rate != config->media_cfg.clock_rate)
01859 {
01860 pj_ansi_sprintf(line, "--snd-clock-rate %d\n",
01861 config->media_cfg.snd_clock_rate);
01862 pj_strcat2(&cfg, line);
01863 }
01864
01865
01866 if (config->media_cfg.channel_count == 2) {
01867 pj_ansi_sprintf(line, "--stereo\n");
01868 pj_strcat2(&cfg, line);
01869 }
01870
01871
01872 if (config->media_cfg.quality != PJSUA_DEFAULT_CODEC_QUALITY) {
01873 pj_ansi_sprintf(line, "--quality %d\n",
01874 config->media_cfg.quality);
01875 pj_strcat2(&cfg, line);
01876 } else {
01877 pj_ansi_sprintf(line, "#using default --quality %d\n",
01878 config->media_cfg.quality);
01879 pj_strcat2(&cfg, line);
01880 }
01881
01882
01883
01884 if (config->media_cfg.ptime) {
01885 pj_ansi_sprintf(line, "--ptime %d\n",
01886 config->media_cfg.ptime);
01887 pj_strcat2(&cfg, line);
01888 }
01889
01890
01891 if (config->media_cfg.no_vad) {
01892 pj_strcat2(&cfg, "--no-vad\n");
01893 }
01894
01895
01896 if (config->media_cfg.ec_tail_len != PJSUA_DEFAULT_EC_TAIL_LEN) {
01897 pj_ansi_sprintf(line, "--ec-tail %d\n",
01898 config->media_cfg.ec_tail_len);
01899 pj_strcat2(&cfg, line);
01900 } else {
01901 pj_ansi_sprintf(line, "#using default --ec-tail %d\n",
01902 config->media_cfg.ec_tail_len);
01903 pj_strcat2(&cfg, line);
01904 }
01905
01906
01907 if (config->media_cfg.ec_options != 0) {
01908 pj_ansi_sprintf(line, "--ec-opt %d\n",
01909 config->media_cfg.ec_options);
01910 pj_strcat2(&cfg, line);
01911 }
01912
01913
01914 if (config->media_cfg.ilbc_mode != PJSUA_DEFAULT_ILBC_MODE) {
01915 pj_ansi_sprintf(line, "--ilbc-mode %d\n",
01916 config->media_cfg.ilbc_mode);
01917 pj_strcat2(&cfg, line);
01918 } else {
01919 pj_ansi_sprintf(line, "#using default --ilbc-mode %d\n",
01920 config->media_cfg.ilbc_mode);
01921 pj_strcat2(&cfg, line);
01922 }
01923
01924
01925 if (config->media_cfg.tx_drop_pct) {
01926 pj_ansi_sprintf(line, "--tx-drop-pct %d\n",
01927 config->media_cfg.tx_drop_pct);
01928 pj_strcat2(&cfg, line);
01929
01930 }
01931 if (config->media_cfg.rx_drop_pct) {
01932 pj_ansi_sprintf(line, "--rx-drop-pct %d\n",
01933 config->media_cfg.rx_drop_pct);
01934 pj_strcat2(&cfg, line);
01935
01936 }
01937
01938
01939
01940 pj_ansi_sprintf(line, "--rtp-port %d\n",
01941 config->rtp_cfg.port);
01942 pj_strcat2(&cfg, line);
01943
01944
01945 for (i=0; i<config->codec_cnt; ++i) {
01946 pj_ansi_sprintf(line, "--add-codec %s\n",
01947 config->codec_arg[i].ptr);
01948 pj_strcat2(&cfg, line);
01949 }
01950
01951 for (i=0; i<config->codec_dis_cnt; ++i) {
01952 pj_ansi_sprintf(line, "--dis-codec %s\n",
01953 config->codec_dis[i].ptr);
01954 pj_strcat2(&cfg, line);
01955 }
01956
01957 pj_strcat2(&cfg, "\n#\n# User agent:\n#\n");
01958
01959
01960 if (config->auto_answer != 0) {
01961 pj_ansi_sprintf(line, "--auto-answer %d\n",
01962 config->auto_answer);
01963 pj_strcat2(&cfg, line);
01964 }
01965
01966
01967 if (config->redir_op != PJSIP_REDIRECT_ACCEPT) {
01968 pj_ansi_sprintf(line, "--accept-redirect %d\n",
01969 config->redir_op);
01970 pj_strcat2(&cfg, line);
01971 }
01972
01973
01974 pj_ansi_sprintf(line, "--max-calls %d\n",
01975 config->cfg.max_calls);
01976 pj_strcat2(&cfg, line);
01977
01978
01979 if (config->duration != NO_LIMIT) {
01980 pj_ansi_sprintf(line, "--duration %d\n",
01981 config->duration);
01982 pj_strcat2(&cfg, line);
01983 }
01984
01985
01986 if (config->no_refersub) {
01987 pj_strcat2(&cfg, "--norefersub\n");
01988 }
01989
01990 if (pjsip_use_compact_form)
01991 {
01992 pj_strcat2(&cfg, "--use-compact-form\n");
01993 }
01994
01995 if (!config->cfg.force_lr) {
01996 pj_strcat2(&cfg, "--no-force-lr\n");
01997 }
01998
01999 pj_strcat2(&cfg, "\n#\n# Buddies:\n#\n");
02000
02001
02002 for (i=0; i<config->buddy_cnt; ++i) {
02003 pj_ansi_sprintf(line, "--add-buddy %.*s\n",
02004 (int)config->buddy_cfg[i].uri.slen,
02005 config->buddy_cfg[i].uri.ptr);
02006 pj_strcat2(&cfg, line);
02007 }
02008
02009
02010 pj_strcat2(&cfg, "\n#\n# SIP extensions:\n#\n");
02011
02012 if (config->cfg.require_100rel) {
02013 pj_strcat2(&cfg, "--use-100rel\n");
02014 }
02015
02016 if (config->cfg.require_timer) {
02017 pj_strcat2(&cfg, "--use-timer\n");
02018 }
02019 if (config->cfg.timer_setting.min_se != 90) {
02020 pj_ansi_sprintf(line, "--timer-min-se %d\n",
02021 config->cfg.timer_setting.min_se);
02022 pj_strcat2(&cfg, line);
02023 }
02024 if (config->cfg.timer_setting.sess_expires != PJSIP_SESS_TIMER_DEF_SE) {
02025 pj_ansi_sprintf(line, "--timer-se %d\n",
02026 config->cfg.timer_setting.sess_expires);
02027 pj_strcat2(&cfg, line);
02028 }
02029
02030 *(cfg.ptr + cfg.slen) = '\0';
02031 return cfg.slen;
02032 }
02033
02034
02035
02036
02037
02038 static void app_dump(pj_bool_t detail)
02039 {
02040 pjsua_dump(detail);
02041 }
02042
02043
02044
02045
02046
02047
02048 static void log_call_dump(int call_id)
02049 {
02050 unsigned call_dump_len;
02051 unsigned part_len;
02052 unsigned part_idx;
02053 unsigned log_decor;
02054
02055 pjsua_call_dump(call_id, PJ_TRUE, some_buf,
02056 sizeof(some_buf), " ");
02057 call_dump_len = strlen(some_buf);
02058
02059 log_decor = pj_log_get_decor();
02060 pj_log_set_decor(log_decor & ~(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
02061 PJ_LOG(3,(THIS_FILE, "\n"));
02062 pj_log_set_decor(0);
02063
02064 part_idx = 0;
02065 part_len = PJ_LOG_MAX_SIZE-80;
02066 while (part_idx < call_dump_len) {
02067 char p_orig, *p;
02068
02069 p = &some_buf[part_idx];
02070 if (part_idx + part_len > call_dump_len)
02071 part_len = call_dump_len - part_idx;
02072 p_orig = p[part_len];
02073 p[part_len] = '\0';
02074 PJ_LOG(3,(THIS_FILE, "%s", p));
02075 p[part_len] = p_orig;
02076 part_idx += part_len;
02077 }
02078 pj_log_set_decor(log_decor);
02079 }
02080
02081
02082
02083
02084
02085 static void ringback_start(pjsua_call_id call_id)
02086 {
02087 if (app_config.no_tones)
02088 return;
02089
02090 if (app_config.call_data[call_id].ringback_on)
02091 return;
02092
02093 app_config.call_data[call_id].ringback_on = PJ_TRUE;
02094
02095 if (++app_config.ringback_cnt==1 &&
02096 app_config.ringback_slot!=PJSUA_INVALID_ID)
02097 {
02098 pjsua_conf_connect(app_config.ringback_slot, 0);
02099 }
02100 }
02101
02102 static void ring_stop(pjsua_call_id call_id)
02103 {
02104 if (app_config.no_tones)
02105 return;
02106
02107 if (app_config.call_data[call_id].ringback_on) {
02108 app_config.call_data[call_id].ringback_on = PJ_FALSE;
02109
02110 pj_assert(app_config.ringback_cnt>0);
02111 if (--app_config.ringback_cnt == 0 &&
02112 app_config.ringback_slot!=PJSUA_INVALID_ID)
02113 {
02114 pjsua_conf_disconnect(app_config.ringback_slot, 0);
02115 pjmedia_tonegen_rewind(app_config.ringback_port);
02116 }
02117 }
02118
02119 if (app_config.call_data[call_id].ring_on) {
02120 app_config.call_data[call_id].ring_on = PJ_FALSE;
02121
02122 pj_assert(app_config.ring_cnt>0);
02123 if (--app_config.ring_cnt == 0 &&
02124 app_config.ring_slot!=PJSUA_INVALID_ID)
02125 {
02126 pjsua_conf_disconnect(app_config.ring_slot, 0);
02127 pjmedia_tonegen_rewind(app_config.ring_port);
02128 }
02129 }
02130 }
02131
02132 static void ring_start(pjsua_call_id call_id)
02133 {
02134 if (app_config.no_tones)
02135 return;
02136
02137 if (app_config.call_data[call_id].ring_on)
02138 return;
02139
02140 app_config.call_data[call_id].ring_on = PJ_TRUE;
02141
02142 if (++app_config.ring_cnt==1 &&
02143 app_config.ring_slot!=PJSUA_INVALID_ID)
02144 {
02145 pjsua_conf_connect(app_config.ring_slot, 0);
02146 }
02147 }
02148
02149
02150
02151
02152
02153 static pj_bool_t find_next_call(void)
02154 {
02155 int i, max;
02156
02157 max = pjsua_call_get_max_count();
02158 for (i=current_call+1; i<max; ++i) {
02159 if (pjsua_call_is_active(i)) {
02160 current_call = i;
02161 return PJ_TRUE;
02162 }
02163 }
02164
02165 for (i=0; i<current_call; ++i) {
02166 if (pjsua_call_is_active(i)) {
02167 current_call = i;
02168 return PJ_TRUE;
02169 }
02170 }
02171
02172 current_call = PJSUA_INVALID_ID;
02173 return PJ_FALSE;
02174 }
02175
02176
02177
02178
02179
02180 static pj_bool_t find_prev_call(void)
02181 {
02182 int i, max;
02183
02184 max = pjsua_call_get_max_count();
02185 for (i=current_call-1; i>=0; --i) {
02186 if (pjsua_call_is_active(i)) {
02187 current_call = i;
02188 return PJ_TRUE;
02189 }
02190 }
02191
02192 for (i=max-1; i>current_call; --i) {
02193 if (pjsua_call_is_active(i)) {
02194 current_call = i;
02195 return PJ_TRUE;
02196 }
02197 }
02198
02199 current_call = PJSUA_INVALID_ID;
02200 return PJ_FALSE;
02201 }
02202
02203
02204
02205
02206
02207 static void call_timeout_callback(pj_timer_heap_t *timer_heap,
02208 struct pj_timer_entry *entry)
02209 {
02210 pjsua_call_id call_id = entry->id;
02211 pjsua_msg_data msg_data;
02212 pjsip_generic_string_hdr warn;
02213 pj_str_t hname = pj_str("Warning");
02214 pj_str_t hvalue = pj_str("399 pjsua \"Call duration exceeded\"");
02215
02216 PJ_UNUSED_ARG(timer_heap);
02217
02218 if (call_id == PJSUA_INVALID_ID) {
02219 PJ_LOG(1,(THIS_FILE, "Invalid call ID in timer callback"));
02220 return;
02221 }
02222
02223
02224 pjsua_msg_data_init(&msg_data);
02225 pjsip_generic_string_hdr_init2(&warn, &hname, &hvalue);
02226 pj_list_push_back(&msg_data.hdr_list, &warn);
02227
02228
02229 PJ_LOG(3,(THIS_FILE, "Duration (%d seconds) has been exceeded "
02230 "for call %d, disconnecting the call",
02231 app_config.duration, call_id));
02232 entry->id = PJSUA_INVALID_ID;
02233 pjsua_call_hangup(call_id, 200, NULL, &msg_data);
02234 }
02235
02236
02237
02238
02239
02240 static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
02241 {
02242 pjsua_call_info call_info;
02243
02244 PJ_UNUSED_ARG(e);
02245
02246 pjsua_call_get_info(call_id, &call_info);
02247
02248 if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
02249
02250
02251 ring_stop(call_id);
02252
02253
02254 if (app_config.call_data[call_id].timer.id != PJSUA_INVALID_ID) {
02255 struct call_data *cd = &app_config.call_data[call_id];
02256 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
02257
02258 cd->timer.id = PJSUA_INVALID_ID;
02259 pjsip_endpt_cancel_timer(endpt, &cd->timer);
02260 }
02261
02262
02263
02264
02265 if (app_config.auto_play_hangup)
02266 pjsua_player_set_pos(app_config.wav_id, 0);
02267
02268
02269 PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]",
02270 call_id,
02271 call_info.last_status,
02272 call_info.last_status_text.ptr));
02273
02274 if (call_id == current_call) {
02275 find_next_call();
02276 }
02277
02278
02279 if (1) {
02280 PJ_LOG(5,(THIS_FILE,
02281 "Call %d disconnected, dumping media stats..",
02282 call_id));
02283 log_call_dump(call_id);
02284 }
02285
02286 } else {
02287
02288 if (app_config.duration!=NO_LIMIT &&
02289 call_info.state == PJSIP_INV_STATE_CONFIRMED)
02290 {
02291
02292 struct call_data *cd = &app_config.call_data[call_id];
02293 pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
02294 pj_time_val delay;
02295
02296 cd->timer.id = call_id;
02297 delay.sec = app_config.duration;
02298 delay.msec = 0;
02299 pjsip_endpt_schedule_timer(endpt, &cd->timer, &delay);
02300 }
02301
02302 if (call_info.state == PJSIP_INV_STATE_EARLY) {
02303 int code;
02304 pj_str_t reason;
02305 pjsip_msg *msg;
02306
02307
02308 pj_assert(e->type == PJSIP_EVENT_TSX_STATE);
02309
02310 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
02311 msg = e->body.tsx_state.src.rdata->msg_info.msg;
02312 } else {
02313 msg = e->body.tsx_state.src.tdata->msg;
02314 }
02315
02316 code = msg->line.status.code;
02317 reason = msg->line.status.reason;
02318
02319
02320 if (call_info.role==PJSIP_ROLE_UAC && code==180 &&
02321 msg->body == NULL &&
02322 call_info.media_status==PJSUA_CALL_MEDIA_NONE)
02323 {
02324 ringback_start(call_id);
02325 }
02326
02327 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s (%d %.*s)",
02328 call_id, call_info.state_text.ptr,
02329 code, (int)reason.slen, reason.ptr));
02330 } else {
02331 PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
02332 call_id,
02333 call_info.state_text.ptr));
02334 }
02335
02336 if (current_call==PJSUA_INVALID_ID)
02337 current_call = call_id;
02338
02339 }
02340 }
02341
02342
02346 static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
02347 pjsip_rx_data *rdata)
02348 {
02349 pjsua_call_info call_info;
02350
02351 PJ_UNUSED_ARG(acc_id);
02352 PJ_UNUSED_ARG(rdata);
02353
02354 pjsua_call_get_info(call_id, &call_info);
02355
02356
02357 ring_start(call_id);
02358
02359 if (current_call==PJSUA_INVALID_ID)
02360 current_call = call_id;
02361
02362 if (app_config.auto_answer > 0) {
02363 pjsua_call_answer(call_id, app_config.auto_answer, NULL, NULL);
02364 }
02365
02366 if (app_config.auto_answer < 200) {
02367 PJ_LOG(3,(THIS_FILE,
02368 "Incoming call for account %d!\n"
02369 "From: %s\n"
02370 "To: %s\n"
02371 "Press a to answer or h to reject call",
02372 acc_id,
02373 call_info.remote_info.ptr,
02374 call_info.local_info.ptr));
02375 }
02376 }
02377
02378
02379
02380
02381
02382 static void on_call_tsx_state(pjsua_call_id call_id,
02383 pjsip_transaction *tsx,
02384 pjsip_event *e)
02385 {
02386 const pjsip_method info_method =
02387 {
02388 PJSIP_OTHER_METHOD,
02389 { "INFO", 4 }
02390 };
02391
02392 if (pjsip_method_cmp(&tsx->method, &info_method)==0) {
02393
02394
02395
02396 if (tsx->role == PJSIP_ROLE_UAC &&
02397 (tsx->state == PJSIP_TSX_STATE_COMPLETED ||
02398 (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
02399 e->body.tsx_state.prev_state != PJSIP_TSX_STATE_COMPLETED)))
02400 {
02401
02402 if (tsx->status_code >= 200 && tsx->status_code < 300) {
02403 PJ_LOG(4,(THIS_FILE,
02404 "Call %d: DTMF sent successfully with INFO",
02405 call_id));
02406 } else if (tsx->status_code >= 300) {
02407 PJ_LOG(4,(THIS_FILE,
02408 "Call %d: Failed to send DTMF with INFO: %d/%.*s",
02409 call_id,
02410 tsx->status_code,
02411 (int)tsx->status_text.slen,
02412 tsx->status_text.ptr));
02413 }
02414 } else if (tsx->role == PJSIP_ROLE_UAS &&
02415 tsx->state == PJSIP_TSX_STATE_TRYING)
02416 {
02417
02418 pjsip_rx_data *rdata;
02419 pjsip_tx_data *tdata;
02420 pj_status_t status;
02421
02422 rdata = e->body.tsx_state.src.rdata;
02423
02424 if (rdata->msg_info.msg->body) {
02425 status = pjsip_endpt_create_response(tsx->endpt, rdata,
02426 200, NULL, &tdata);
02427 if (status == PJ_SUCCESS)
02428 status = pjsip_tsx_send_msg(tsx, tdata);
02429
02430 PJ_LOG(3,(THIS_FILE, "Call %d: incoming INFO:\n%.*s",
02431 call_id,
02432 (int)rdata->msg_info.msg->body->len,
02433 rdata->msg_info.msg->body->data));
02434 } else {
02435 status = pjsip_endpt_create_response(tsx->endpt, rdata,
02436 400, NULL, &tdata);
02437 if (status == PJ_SUCCESS)
02438 status = pjsip_tsx_send_msg(tsx, tdata);
02439 }
02440 }
02441 }
02442 }
02443
02444
02445
02446
02447
02448
02449
02450 static void on_call_media_state(pjsua_call_id call_id)
02451 {
02452 pjsua_call_info call_info;
02453
02454 pjsua_call_get_info(call_id, &call_info);
02455
02456
02457 ring_stop(call_id);
02458
02459
02460
02461
02462 if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE ||
02463 call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD)
02464 {
02465 pj_bool_t connect_sound = PJ_TRUE;
02466
02467
02468 if (app_config.auto_loop) {
02469 pjsua_conf_connect(call_info.conf_slot, call_info.conf_slot);
02470 connect_sound = PJ_FALSE;
02471 }
02472
02473
02474 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
02475 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
02476 }
02477
02478
02479 if ((app_config.auto_play || app_config.auto_play_hangup) &&
02480 app_config.wav_port != PJSUA_INVALID_ID)
02481 {
02482 pjsua_conf_connect(app_config.wav_port, call_info.conf_slot);
02483 connect_sound = PJ_FALSE;
02484 }
02485
02486
02487 if (app_config.auto_conf) {
02488 pjsua_call_id call_ids[PJSUA_MAX_CALLS];
02489 unsigned call_cnt=PJ_ARRAY_SIZE(call_ids);
02490 unsigned i;
02491
02492
02493
02494
02495 pjsua_enum_calls(call_ids, &call_cnt);
02496
02497 for (i=0; i<call_cnt; ++i) {
02498 if (call_ids[i] == call_id)
02499 continue;
02500
02501 if (!pjsua_call_has_media(call_ids[i]))
02502 continue;
02503
02504 pjsua_conf_connect(call_info.conf_slot,
02505 pjsua_call_get_conf_port(call_ids[i]));
02506 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
02507 call_info.conf_slot);
02508
02509
02510 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
02511 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
02512 app_config.rec_port);
02513 }
02514
02515 }
02516
02517
02518 connect_sound = PJ_TRUE;
02519 }
02520
02521
02522 if (connect_sound) {
02523 pjsua_conf_connect(call_info.conf_slot, 0);
02524 pjsua_conf_connect(0, call_info.conf_slot);
02525
02526
02527 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
02528 pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
02529 pjsua_conf_connect(0, app_config.rec_port);
02530 }
02531 }
02532 }
02533
02534
02535 switch (call_info.media_status) {
02536 case PJSUA_CALL_MEDIA_ACTIVE:
02537 PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id));
02538 break;
02539
02540 case PJSUA_CALL_MEDIA_LOCAL_HOLD:
02541 PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local",
02542 call_id));
02543 break;
02544
02545 case PJSUA_CALL_MEDIA_REMOTE_HOLD:
02546 PJ_LOG(3,(THIS_FILE,
02547 "Media for call %d is suspended (hold) by remote",
02548 call_id));
02549 break;
02550
02551 case PJSUA_CALL_MEDIA_ERROR:
02552 PJ_LOG(3,(THIS_FILE,
02553 "Media has reported error, disconnecting call"));
02554 {
02555 pj_str_t reason = pj_str("ICE negotiation failed");
02556 pjsua_call_hangup(call_id, 500, &reason, NULL);
02557 }
02558 break;
02559
02560 case PJSUA_CALL_MEDIA_NONE:
02561 PJ_LOG(3,(THIS_FILE,
02562 "Media for call %d is inactive",
02563 call_id));
02564 break;
02565
02566 default:
02567 pj_assert(!"Unhandled media status");
02568 break;
02569 }
02570 }
02571
02572
02573
02574
02575 static void call_on_dtmf_callback(pjsua_call_id call_id, int dtmf)
02576 {
02577 PJ_LOG(3,(THIS_FILE, "Incoming DTMF on call %d: %c", call_id, dtmf));
02578 }
02579
02580
02581
02582
02583 static pjsip_redirect_op call_on_redirected(pjsua_call_id call_id,
02584 const pjsip_uri *target,
02585 const pjsip_event *e)
02586 {
02587 PJ_UNUSED_ARG(e);
02588
02589 if (app_config.redir_op == PJSIP_REDIRECT_PENDING) {
02590 char uristr[PJSIP_MAX_URL_SIZE];
02591 int len;
02592
02593 len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, target, uristr,
02594 sizeof(uristr));
02595 if (len < 1) {
02596 pj_ansi_strcpy(uristr, "--URI too long--");
02597 }
02598
02599 PJ_LOG(3,(THIS_FILE, "Call %d is being redirected to %.*s. "
02600 "Press 'Ra' to accept, 'Rr' to reject, or 'Rd' to "
02601 "disconnect.",
02602 call_id, len, uristr));
02603 }
02604
02605 return app_config.redir_op;
02606 }
02607
02608
02609
02610
02611 static void on_reg_state(pjsua_acc_id acc_id)
02612 {
02613 PJ_UNUSED_ARG(acc_id);
02614
02615
02616 }
02617
02618
02619
02620
02621
02622 static void on_incoming_subscribe(pjsua_acc_id acc_id,
02623 pjsua_srv_pres *srv_pres,
02624 pjsua_buddy_id buddy_id,
02625 const pj_str_t *from,
02626 pjsip_rx_data *rdata,
02627 pjsip_status_code *code,
02628 pj_str_t *reason,
02629 pjsua_msg_data *msg_data)
02630 {
02631
02632 PJ_UNUSED_ARG(acc_id);
02633 PJ_UNUSED_ARG(srv_pres);
02634 PJ_UNUSED_ARG(buddy_id);
02635 PJ_UNUSED_ARG(from);
02636 PJ_UNUSED_ARG(rdata);
02637 PJ_UNUSED_ARG(code);
02638 PJ_UNUSED_ARG(reason);
02639 PJ_UNUSED_ARG(msg_data);
02640 }
02641
02642
02643
02644
02645
02646 static void on_buddy_state(pjsua_buddy_id buddy_id)
02647 {
02648 pjsua_buddy_info info;
02649 pjsua_buddy_get_info(buddy_id, &info);
02650
02651 PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s, subscription state is %s "
02652 "(last termination reason code=%d %.*s)",
02653 (int)info.uri.slen,
02654 info.uri.ptr,
02655 (int)info.status_text.slen,
02656 info.status_text.ptr,
02657 info.sub_state_name,
02658 info.sub_term_code,
02659 (int)info.sub_term_reason.slen,
02660 info.sub_term_reason.ptr));
02661 }
02662
02663
02667 static void on_pager(pjsua_call_id call_id, const pj_str_t *from,
02668 const pj_str_t *to, const pj_str_t *contact,
02669 const pj_str_t *mime_type, const pj_str_t *text)
02670 {
02671
02672 PJ_UNUSED_ARG(call_id);
02673 PJ_UNUSED_ARG(to);
02674 PJ_UNUSED_ARG(contact);
02675 PJ_UNUSED_ARG(mime_type);
02676
02677 PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s (%.*s)",
02678 (int)from->slen, from->ptr,
02679 (int)text->slen, text->ptr,
02680 (int)mime_type->slen, mime_type->ptr));
02681 }
02682
02683
02687 static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
02688 const pj_str_t *to, const pj_str_t *contact,
02689 pj_bool_t is_typing)
02690 {
02691 PJ_UNUSED_ARG(call_id);
02692 PJ_UNUSED_ARG(to);
02693 PJ_UNUSED_ARG(contact);
02694
02695 PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
02696 (int)from->slen, from->ptr,
02697 (is_typing?"is typing..":"has stopped typing")));
02698 }
02699
02700
02704 static void on_call_transfer_status(pjsua_call_id call_id,
02705 int status_code,
02706 const pj_str_t *status_text,
02707 pj_bool_t final,
02708 pj_bool_t *p_cont)
02709 {
02710 PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
02711 call_id, status_code,
02712 (int)status_text->slen, status_text->ptr,
02713 (final ? "[final]" : "")));
02714
02715 if (status_code/100 == 2) {
02716 PJ_LOG(3,(THIS_FILE,
02717 "Call %d: call transfered successfully, disconnecting call",
02718 call_id));
02719 pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
02720 *p_cont = PJ_FALSE;
02721 }
02722 }
02723
02724
02725
02726
02727
02728 static void on_call_replaced(pjsua_call_id old_call_id,
02729 pjsua_call_id new_call_id)
02730 {
02731 pjsua_call_info old_ci, new_ci;
02732
02733 pjsua_call_get_info(old_call_id, &old_ci);
02734 pjsua_call_get_info(new_call_id, &new_ci);
02735
02736 PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by "
02737 "call %d with %.*s",
02738 old_call_id,
02739 (int)old_ci.remote_info.slen, old_ci.remote_info.ptr,
02740 new_call_id,
02741 (int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
02742 }
02743
02744
02745
02746
02747
02748 static void on_nat_detect(const pj_stun_nat_detect_result *res)
02749 {
02750 if (res->status != PJ_SUCCESS) {
02751 pjsua_perror(THIS_FILE, "NAT detection failed", res->status);
02752 } else {
02753 PJ_LOG(3, (THIS_FILE, "NAT detected as %s", res->nat_type_name));
02754 }
02755 }
02756
02757
02758
02759
02760
02761 static void on_mwi_info(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info)
02762 {
02763 pj_str_t body;
02764
02765 PJ_LOG(3,(THIS_FILE, "Received MWI for acc %d:", acc_id));
02766
02767 if (mwi_info->rdata->msg_info.ctype) {
02768 const pjsip_ctype_hdr *ctype = mwi_info->rdata->msg_info.ctype;
02769
02770 PJ_LOG(3,(THIS_FILE, " Content-Type: %.*s/%.*s",
02771 (int)ctype->media.type.slen,
02772 ctype->media.type.ptr,
02773 (int)ctype->media.subtype.slen,
02774 ctype->media.subtype.ptr));
02775 }
02776
02777 if (!mwi_info->rdata->msg_info.msg->body) {
02778 PJ_LOG(3,(THIS_FILE, " no message body"));
02779 return;
02780 }
02781
02782 body.ptr = mwi_info->rdata->msg_info.msg->body->data;
02783 body.slen = mwi_info->rdata->msg_info.msg->body->len;
02784
02785 PJ_LOG(3,(THIS_FILE, " Body:\n%.*s", (int)body.slen, body.ptr));
02786 }
02787
02788
02789
02790
02791
02792 static void print_buddy_list(void)
02793 {
02794 pjsua_buddy_id ids[64];
02795 int i;
02796 unsigned count = PJ_ARRAY_SIZE(ids);
02797
02798 puts("Buddy list:");
02799
02800 pjsua_enum_buddies(ids, &count);
02801
02802 if (count == 0)
02803 puts(" -none-");
02804 else {
02805 for (i=0; i<(int)count; ++i) {
02806 pjsua_buddy_info info;
02807
02808 if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
02809 continue;
02810
02811 printf(" [%2d] <%.*s> %.*s\n",
02812 ids[i]+1,
02813 (int)info.status_text.slen,
02814 info.status_text.ptr,
02815 (int)info.uri.slen,
02816 info.uri.ptr);
02817 }
02818 }
02819 puts("");
02820 }
02821
02822
02823
02824
02825
02826 static void print_acc_status(int acc_id)
02827 {
02828 char buf[80];
02829 pjsua_acc_info info;
02830
02831 pjsua_acc_get_info(acc_id, &info);
02832
02833 if (!info.has_registration) {
02834 pj_ansi_snprintf(buf, sizeof(buf), "%.*s",
02835 (int)info.status_text.slen,
02836 info.status_text.ptr);
02837
02838 } else {
02839 pj_ansi_snprintf(buf, sizeof(buf),
02840 "%d/%.*s (expires=%d)",
02841 info.status,
02842 (int)info.status_text.slen,
02843 info.status_text.ptr,
02844 info.expires);
02845
02846 }
02847
02848 printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
02849 acc_id, (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
02850 printf(" Online status: %.*s\n",
02851 (int)info.online_status_text.slen,
02852 info.online_status_text.ptr);
02853 }
02854
02855
02856 pj_status_t on_playfile_done(pjmedia_port *port, void *usr_data)
02857 {
02858 pj_time_val delay;
02859
02860 PJ_UNUSED_ARG(port);
02861 PJ_UNUSED_ARG(usr_data);
02862
02863
02864 if (pjsua_call_get_count() == 0) {
02865 pjsua_player_set_pos(app_config.wav_id, 0);
02866 return PJ_SUCCESS;
02867 }
02868
02869
02870 if (app_config.auto_hangup_timer.id == 1)
02871 return PJ_SUCCESS;
02872
02873 app_config.auto_hangup_timer.id = 1;
02874 delay.sec = 0;
02875 delay.msec = 200;
02876 pjsip_endpt_schedule_timer(pjsua_get_pjsip_endpt(),
02877 &app_config.auto_hangup_timer,
02878 &delay);
02879
02880 return PJ_SUCCESS;
02881 }
02882
02883
02884 static void hangup_timeout_callback(pj_timer_heap_t *timer_heap,
02885 struct pj_timer_entry *entry)
02886 {
02887 PJ_UNUSED_ARG(timer_heap);
02888 PJ_UNUSED_ARG(entry);
02889
02890 app_config.auto_hangup_timer.id = 0;
02891 pjsua_call_hangup_all();
02892 }
02893
02894
02895
02896
02897 static void keystroke_help(void)
02898 {
02899 pjsua_acc_id acc_ids[16];
02900 unsigned count = PJ_ARRAY_SIZE(acc_ids);
02901 int i;
02902
02903 printf(">>>>\n");
02904
02905 pjsua_enum_accs(acc_ids, &count);
02906
02907 printf("Account list:\n");
02908 for (i=0; i<(int)count; ++i)
02909 print_acc_status(acc_ids[i]);
02910
02911 print_buddy_list();
02912
02913
02914 puts("+=============================================================================+");
02915 puts("| Call Commands: | Buddy, IM & Presence: | Account: |");
02916 puts("| | | |");
02917 puts("| m Make new call | +b Add new buddy .| +a Add new accnt |");
02918 puts("| M Make multiple calls | -b Delete buddy | -a Delete accnt. |");
02919 puts("| a Answer call | i Send IM | !a Modify accnt. |");
02920 puts("| h Hangup call (ha=all) | s Subscribe presence | rr (Re-)register |");
02921 puts("| H Hold call | u Unsubscribe presence | ru Unregister |");
02922 puts("| v re-inVite (release hold) | t ToGgle Online status | > Cycle next ac.|");
02923 puts("| U send UPDATE | T Set online status | < Cycle prev ac.|");
02924 puts("| ],[ Select next/prev call +--------------------------+-------------------+");
02925 puts("| x Xfer call | Media Commands: | Status & Config: |");
02926 puts("| X Xfer with Replaces | | |");
02927 puts("| # Send RFC 2833 DTMF | cl List ports | d Dump status |");
02928 puts("| * Send DTMF with INFO | cc Connect port | dd Dump detailed |");
02929 puts("| dq Dump curr. call quality | cd Disconnect port | dc Dump config |");
02930 puts("| | V Adjust audio Volume | f Save config |");
02931 puts("| S Send arbitrary REQUEST | Cp Codec priorities | f Save config |");
02932 puts("+------------------------------+--------------------------+-------------------+");
02933 puts("| q QUIT L ReLoad sleep MS echo [0|1|txt] n: detect NAT type |");
02934 puts("+=============================================================================+");
02935
02936 i = pjsua_call_get_count();
02937 printf("You have %d active call%s\n", i, (i>1?"s":""));
02938
02939 if (current_call != PJSUA_INVALID_ID) {
02940 pjsua_call_info ci;
02941 if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
02942 printf("Current call id=%d to %.*s [%.*s]\n", current_call,
02943 (int)ci.remote_info.slen, ci.remote_info.ptr,
02944 (int)ci.state_text.slen, ci.state_text.ptr);
02945 }
02946 }
02947
02948
02949
02950
02951
02952 static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
02953 {
02954 char *p;
02955
02956 printf("%s (empty to cancel): ", title); fflush(stdout);
02957 if (fgets(buf, len, stdin) == NULL)
02958 return PJ_FALSE;
02959
02960
02961 for (p=buf; ; ++p) {
02962 if (*p=='\r' || *p=='\n') *p='\0';
02963 else if (!*p) break;
02964 }
02965
02966 if (!*buf)
02967 return PJ_FALSE;
02968
02969 return PJ_TRUE;
02970 }
02971
02972
02973 #define NO_NB -2
02974 struct input_result
02975 {
02976 int nb_result;
02977 char *uri_result;
02978 };
02979
02980
02981
02982
02983
02984 static void ui_input_url(const char *title, char *buf, int len,
02985 struct input_result *result)
02986 {
02987 result->nb_result = NO_NB;
02988 result->uri_result = NULL;
02989
02990 print_buddy_list();
02991
02992 printf("Choices:\n"
02993 " 0 For current dialog.\n"
02994 " -1 All %d buddies in buddy list\n"
02995 " [1 -%2d] Select from buddy list\n"
02996 " URL An URL\n"
02997 " <Enter> Empty input (or 'q') to cancel\n"
02998 , pjsua_get_buddy_count(), pjsua_get_buddy_count());
02999 printf("%s: ", title);
03000
03001 fflush(stdout);
03002 if (fgets(buf, len, stdin) == NULL)
03003 return;
03004 len = strlen(buf);
03005
03006
03007 while (pj_isspace(*buf)) {
03008 ++buf;
03009 --len;
03010 }
03011
03012
03013 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
03014 buf[--len] = '\0';
03015
03016 if (len == 0 || buf[0]=='q')
03017 return;
03018
03019 if (pj_isdigit(*buf) || *buf=='-') {
03020
03021 int i;
03022
03023 if (*buf=='-')
03024 i = 1;
03025 else
03026 i = 0;
03027
03028 for (; i<len; ++i) {
03029 if (!pj_isdigit(buf[i])) {
03030 puts("Invalid input");
03031 return;
03032 }
03033 }
03034
03035 result->nb_result = my_atoi(buf);
03036
03037 if (result->nb_result >= 0 &&
03038 result->nb_result <= (int)pjsua_get_buddy_count())
03039 {
03040 return;
03041 }
03042 if (result->nb_result == -1)
03043 return;
03044
03045 puts("Invalid input");
03046 result->nb_result = NO_NB;
03047 return;
03048
03049 } else {
03050 pj_status_t status;
03051
03052 if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
03053 pjsua_perror(THIS_FILE, "Invalid URL", status);
03054 return;
03055 }
03056
03057 result->uri_result = buf;
03058 }
03059 }
03060
03061
03062
03063
03064 static void conf_list(void)
03065 {
03066 unsigned i, count;
03067 pjsua_conf_port_id id[PJSUA_MAX_CALLS];
03068
03069 printf("Conference ports:\n");
03070
03071 count = PJ_ARRAY_SIZE(id);
03072 pjsua_enum_conf_ports(id, &count);
03073
03074 for (i=0; i<count; ++i) {
03075 char txlist[PJSUA_MAX_CALLS*4+10];
03076 unsigned j;
03077 pjsua_conf_port_info info;
03078
03079 pjsua_conf_get_port_info(id[i], &info);
03080
03081 txlist[0] = '\0';
03082 for (j=0; j<info.listener_cnt; ++j) {
03083 char s[10];
03084 pj_ansi_sprintf(s, "#%d ", info.listeners[j]);
03085 pj_ansi_strcat(txlist, s);
03086 }
03087 printf("Port #%02d[%2dKHz/%dms/%d] %20.*s transmitting to: %s\n",
03088 info.slot_id,
03089 info.clock_rate/1000,
03090 info.samples_per_frame*1000/info.channel_count/info.clock_rate,
03091 info.channel_count,
03092 (int)info.name.slen,
03093 info.name.ptr,
03094 txlist);
03095
03096 }
03097 puts("");
03098 }
03099
03100
03101
03102
03103
03104 static void send_request(char *cstr_method, const pj_str_t *dst_uri)
03105 {
03106 pj_str_t str_method;
03107 pjsip_method method;
03108 pjsip_tx_data *tdata;
03109 pjsip_endpoint *endpt;
03110 pj_status_t status;
03111
03112 endpt = pjsua_get_pjsip_endpt();
03113
03114 str_method = pj_str(cstr_method);
03115 pjsip_method_init_np(&method, &str_method);
03116
03117 status = pjsua_acc_create_request(current_acc, &method, dst_uri, &tdata);
03118
03119 status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL);
03120 if (status != PJ_SUCCESS) {
03121 pjsua_perror(THIS_FILE, "Unable to send request", status);
03122 return;
03123 }
03124 }
03125
03126
03127
03128
03129
03130 static void change_online_status(void)
03131 {
03132 char menuin[32];
03133 pj_bool_t online_status;
03134 pjrpid_element elem;
03135 int i, choice;
03136
03137 enum {
03138 AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX
03139 };
03140
03141 struct opt {
03142 int id;
03143 char *name;
03144 } opts[] = {
03145 { AVAILABLE, "Available" },
03146 { BUSY, "Busy"},
03147 { OTP, "On the phone"},
03148 { IDLE, "Idle"},
03149 { AWAY, "Away"},
03150 { BRB, "Be right back"},
03151 { OFFLINE, "Offline"}
03152 };
03153
03154 printf("\n"
03155 "Choices:\n");
03156 for (i=0; i<PJ_ARRAY_SIZE(opts); ++i) {
03157 printf(" %d %s\n", opts[i].id+1, opts[i].name);
03158 }
03159
03160 if (!simple_input("Select status", menuin, sizeof(menuin)))
03161 return;
03162
03163 choice = atoi(menuin) - 1;
03164 if (choice < 0 || choice >= OPT_MAX) {
03165 puts("Invalid selection");
03166 return;
03167 }
03168
03169 pj_bzero(&elem, sizeof(elem));
03170 elem.type = PJRPID_ELEMENT_TYPE_PERSON;
03171
03172 online_status = PJ_TRUE;
03173
03174 switch (choice) {
03175 case AVAILABLE:
03176 break;
03177 case BUSY:
03178 elem.activity = PJRPID_ACTIVITY_BUSY;
03179 elem.note = pj_str("Busy");
03180 break;
03181 case OTP:
03182 elem.activity = PJRPID_ACTIVITY_BUSY;
03183 elem.note = pj_str("On the phone");
03184 break;
03185 case IDLE:
03186 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
03187 elem.note = pj_str("Idle");
03188 break;
03189 case AWAY:
03190 elem.activity = PJRPID_ACTIVITY_AWAY;
03191 elem.note = pj_str("Away");
03192 break;
03193 case BRB:
03194 elem.activity = PJRPID_ACTIVITY_UNKNOWN;
03195 elem.note = pj_str("Be right back");
03196 break;
03197 case OFFLINE:
03198 online_status = PJ_FALSE;
03199 break;
03200 }
03201
03202 pjsua_acc_set_online_status2(current_acc, online_status, &elem);
03203 }
03204
03205
03206
03207
03208
03209 static void manage_codec_prio(void)
03210 {
03211 pjsua_codec_info c[32];
03212 unsigned i, count = PJ_ARRAY_SIZE(c);
03213 char input[32];
03214 char *codec, *prio;
03215 pj_str_t id;
03216 int new_prio;
03217 pj_status_t status;
03218
03219 printf("List of codecs:\n");
03220
03221 pjsua_enum_codecs(c, &count);
03222 for (i=0; i<count; ++i) {
03223 printf(" %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen,
03224 c[i].codec_id.ptr);
03225 }
03226
03227 puts("");
03228 puts("Enter codec id and its new priority "
03229 "(e.g. \"speex/16000 200\"), empty to cancel:");
03230
03231 printf("Codec name (\"*\" for all) and priority: ");
03232 if (fgets(input, sizeof(input), stdin) == NULL)
03233 return;
03234 if (input[0]=='\r' || input[0]=='\n') {
03235 puts("Done");
03236 return;
03237 }
03238
03239 codec = strtok(input, " \t\r\n");
03240 prio = strtok(NULL, " \r\n");
03241
03242 if (!codec || !prio) {
03243 puts("Invalid input");
03244 return;
03245 }
03246
03247 new_prio = atoi(prio);
03248 if (new_prio < 0)
03249 new_prio = 0;
03250 else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST)
03251 new_prio = PJMEDIA_CODEC_PRIO_HIGHEST;
03252
03253 status = pjsua_codec_set_priority(pj_cstr(&id, codec),
03254 (pj_uint8_t)new_prio);
03255 if (status != PJ_SUCCESS)
03256 pjsua_perror(THIS_FILE, "Error setting codec priority", status);
03257 }
03258
03259
03260
03261
03262
03263 void console_app_main(const pj_str_t *uri_to_call)
03264 {
03265 char menuin[32];
03266 char buf[128];
03267 char text[128];
03268 int i, count;
03269 char *uri;
03270 pj_str_t tmp;
03271 struct input_result result;
03272 pjsua_call_info call_info;
03273 pjsua_acc_info acc_info;
03274
03275
03276
03277 if (uri_to_call->slen) {
03278 pjsua_call_make_call( current_acc, uri_to_call, 0, NULL, NULL, NULL);
03279 }
03280
03281 keystroke_help();
03282
03283 for (;;) {
03284
03285 printf(">>> ");
03286 fflush(stdout);
03287
03288 if (fgets(menuin, sizeof(menuin), stdin) == NULL) {
03289
03290
03291
03292
03293
03294
03295 #if defined(PJ_WIN32) && PJ_WIN32!=0
03296 if (freopen ("CONIN$", "r", stdin) == NULL) {
03297 #else
03298 if (1) {
03299 #endif
03300 puts("Cannot switch back to console from file redirection");
03301 menuin[0] = 'q';
03302 menuin[1] = '\0';
03303 } else {
03304 puts("Switched back to console from file redirection");
03305 continue;
03306 }
03307 }
03308
03309 if (cmd_echo) {
03310 printf("%s", menuin);
03311 }
03312
03313 switch (menuin[0]) {
03314
03315 case 'm':
03316
03317 printf("(You currently have %d calls)\n",
03318 pjsua_call_get_count());
03319
03320 uri = NULL;
03321 ui_input_url("Make call", buf, sizeof(buf), &result);
03322 if (result.nb_result != NO_NB) {
03323
03324 if (result.nb_result == -1 || result.nb_result == 0) {
03325 puts("You can't do that with make call!");
03326 continue;
03327 } else {
03328 pjsua_buddy_info binfo;
03329 pjsua_buddy_get_info(result.nb_result-1, &binfo);
03330 tmp.ptr = buf;
03331 pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
03332 }
03333
03334 } else if (result.uri_result) {
03335 tmp = pj_str(result.uri_result);
03336 } else {
03337 tmp.slen = 0;
03338 }
03339
03340 pjsua_call_make_call( current_acc, &tmp, 0, NULL, NULL, NULL);
03341 break;
03342
03343 case 'M':
03344
03345 printf("(You currently have %d calls)\n",
03346 pjsua_call_get_count());
03347
03348 if (!simple_input("Number of calls", menuin, sizeof(menuin)))
03349 continue;
03350
03351 count = my_atoi(menuin);
03352 if (count < 1)
03353 continue;
03354
03355 ui_input_url("Make call", buf, sizeof(buf), &result);
03356 if (result.nb_result != NO_NB) {
03357 pjsua_buddy_info binfo;
03358 if (result.nb_result == -1 || result.nb_result == 0) {
03359 puts("You can't do that with make call!");
03360 continue;
03361 }
03362 pjsua_buddy_get_info(result.nb_result-1, &binfo);
03363 tmp.ptr = buf;
03364 pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
03365 } else {
03366 tmp = pj_str(result.uri_result);
03367 }
03368
03369 for (i=0; i<my_atoi(menuin); ++i) {
03370 pj_status_t status;
03371
03372 status = pjsua_call_make_call(current_acc, &tmp, 0, NULL,
03373 NULL, NULL);
03374 if (status != PJ_SUCCESS)
03375 break;
03376 }
03377 break;
03378
03379 case 'n':
03380 i = pjsua_detect_nat_type();
03381 if (i != PJ_SUCCESS)
03382 pjsua_perror(THIS_FILE, "Error", i);
03383 break;
03384
03385 case 'i':
03386
03387
03388
03389 i = -1;
03390
03391
03392 uri = NULL;
03393
03394
03395 ui_input_url("Send IM to", buf, sizeof(buf), &result);
03396 if (result.nb_result != NO_NB) {
03397
03398 if (result.nb_result == -1) {
03399 puts("You can't send broadcast IM like that!");
03400 continue;
03401
03402 } else if (result.nb_result == 0) {
03403
03404 i = current_call;
03405
03406 } else {
03407 pjsua_buddy_info binfo;
03408 pjsua_buddy_get_info(result.nb_result-1, &binfo);
03409 tmp.ptr = buf;
03410 pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
03411 uri = buf;
03412 }
03413
03414 } else if (result.uri_result) {
03415 uri = result.uri_result;
03416 }
03417
03418
03419
03420 if (i != -1)
03421 pjsua_call_send_typing_ind(i, PJ_TRUE, NULL);
03422 else {
03423 pj_str_t tmp_uri = pj_str(uri);
03424 pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL);
03425 }
03426
03427
03428 if (!simple_input("Message", text, sizeof(text))) {
03429
03430
03431
03432
03433 if (i != -1)
03434 pjsua_call_send_typing_ind(i, PJ_FALSE, NULL);
03435 else {
03436 pj_str_t tmp_uri = pj_str(uri);
03437 pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL);
03438 }
03439 continue;
03440 }
03441
03442 tmp = pj_str(text);
03443
03444
03445 if (i != -1)
03446 pjsua_call_send_im(i, NULL, &tmp, NULL, NULL);
03447 else {
03448 pj_str_t tmp_uri = pj_str(uri);
03449 pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL);
03450 }
03451
03452 break;
03453
03454 case 'a':
03455
03456 if (current_call != -1) {
03457 pjsua_call_get_info(current_call, &call_info);
03458 } else {
03459
03460 call_info.role = PJSIP_ROLE_UAC;
03461 call_info.state = PJSIP_INV_STATE_DISCONNECTED;
03462 }
03463
03464 if (current_call == -1 ||
03465 call_info.role != PJSIP_ROLE_UAS ||
03466 call_info.state >= PJSIP_INV_STATE_CONNECTING)
03467 {
03468 puts("No pending incoming call");
03469 fflush(stdout);
03470 continue;
03471
03472 } else {
03473 int st_code;
03474 char contact[120];
03475 pj_str_t hname = { "Contact", 7 };
03476 pj_str_t hvalue;
03477 pjsip_generic_string_hdr hcontact;
03478 pjsua_msg_data msg_data;
03479
03480 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
03481 continue;
03482
03483 st_code = my_atoi(buf);
03484 if (st_code < 100)
03485 continue;
03486
03487 pjsua_msg_data_init(&msg_data);
03488
03489 if (st_code/100 == 3) {
03490 if (!simple_input("Enter URL to be put in Contact",
03491 contact, sizeof(contact)))
03492 continue;
03493 hvalue = pj_str(contact);
03494 pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue);
03495
03496 pj_list_push_back(&msg_data.hdr_list, &hcontact);
03497 }
03498
03499
03500
03501
03502
03503
03504 if (current_call == -1) {
03505 puts("Call has been disconnected");
03506 fflush(stdout);
03507 continue;
03508 }
03509
03510 pjsua_call_answer(current_call, st_code, NULL, &msg_data);
03511 }
03512
03513 break;
03514
03515
03516 case 'h':
03517
03518 if (current_call == -1) {
03519 puts("No current call");
03520 fflush(stdout);
03521 continue;
03522
03523 } else if (menuin[1] == 'a') {
03524
03525
03526 pjsua_call_hangup_all();
03527
03528 } else {
03529
03530
03531 pjsua_call_hangup(current_call, 0, NULL, NULL);
03532 }
03533 break;
03534
03535 case ']':
03536 case '[':
03537
03538
03539
03540 if (menuin[0] == ']') {
03541 find_next_call();
03542
03543 } else {
03544 find_prev_call();
03545 }
03546
03547 if (current_call != -1) {
03548
03549 pjsua_call_get_info(current_call, &call_info);
03550 PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s",
03551 (int)call_info.remote_info.slen,
03552 call_info.remote_info.ptr));
03553
03554 } else {
03555 PJ_LOG(3,(THIS_FILE,"No current dialog"));
03556 }
03557 break;
03558
03559
03560 case '>':
03561 case '<':
03562 if (!simple_input("Enter account ID to select", buf, sizeof(buf)))
03563 break;
03564
03565 i = my_atoi(buf);
03566 if (pjsua_acc_is_valid(i)) {
03567 pjsua_acc_set_default(i);
03568 PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i));
03569 } else {
03570 PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i));
03571 }
03572 break;
03573
03574
03575 case '+':
03576 if (menuin[1] == 'b') {
03577
03578 pjsua_buddy_config buddy_cfg;
03579 pjsua_buddy_id buddy_id;
03580 pj_status_t status;
03581
03582 if (!simple_input("Enter buddy's URI:", buf, sizeof(buf)))
03583 break;
03584
03585 if (pjsua_verify_sip_url(buf) != PJ_SUCCESS) {
03586 printf("Invalid SIP URI '%s'\n", buf);
03587 break;
03588 }
03589
03590 pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config));
03591
03592 buddy_cfg.uri = pj_str(buf);
03593 buddy_cfg.subscribe = PJ_TRUE;
03594
03595 status = pjsua_buddy_add(&buddy_cfg, &buddy_id);
03596 if (status == PJ_SUCCESS) {
03597 printf("New buddy '%s' added at index %d\n",
03598 buf, buddy_id+1);
03599 }
03600
03601 } else if (menuin[1] == 'a') {
03602
03603 char id[80], registrar[80], realm[80], uname[80], passwd[30];
03604 pjsua_acc_config acc_cfg;
03605 pj_status_t status;
03606
03607 if (!simple_input("Your SIP URL:", id, sizeof(id)))
03608 break;
03609 if (!simple_input("URL of the registrar:", registrar, sizeof(registrar)))
03610 break;
03611 if (!simple_input("Auth Realm:", realm, sizeof(realm)))
03612 break;
03613 if (!simple_input("Auth Username:", uname, sizeof(uname)))
03614 break;
03615 if (!simple_input("Auth Password:", passwd, sizeof(passwd)))
03616 break;
03617
03618 pjsua_acc_config_default(&acc_cfg);
03619 acc_cfg.id = pj_str(id);
03620 acc_cfg.reg_uri = pj_str(registrar);
03621 acc_cfg.cred_count = 1;
03622 acc_cfg.cred_info[0].scheme = pj_str("Digest");
03623 acc_cfg.cred_info[0].realm = pj_str(realm);
03624 acc_cfg.cred_info[0].username = pj_str(uname);
03625 acc_cfg.cred_info[0].data_type = 0;
03626 acc_cfg.cred_info[0].data = pj_str(passwd);
03627
03628 status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
03629 if (status != PJ_SUCCESS) {
03630 pjsua_perror(THIS_FILE, "Error adding new account", status);
03631 }
03632
03633 } else {
03634 printf("Invalid input %s\n", menuin);
03635 }
03636 break;
03637
03638 case '-':
03639 if (menuin[1] == 'b') {
03640 if (!simple_input("Enter buddy ID to delete",buf,sizeof(buf)))
03641 break;
03642
03643 i = my_atoi(buf) - 1;
03644
03645 if (!pjsua_buddy_is_valid(i)) {
03646 printf("Invalid buddy id %d\n", i);
03647 } else {
03648 pjsua_buddy_del(i);
03649 printf("Buddy %d deleted\n", i);
03650 }
03651
03652 } else if (menuin[1] == 'a') {
03653
03654 if (!simple_input("Enter account ID to delete",buf,sizeof(buf)))
03655 break;
03656
03657 i = my_atoi(buf);
03658
03659 if (!pjsua_acc_is_valid(i)) {
03660 printf("Invalid account id %d\n", i);
03661 } else {
03662 pjsua_acc_del(i);
03663 printf("Account %d deleted\n", i);
03664 }
03665
03666 } else {
03667 printf("Invalid input %s\n", menuin);
03668 }
03669 break;
03670
03671 case 'H':
03672
03673
03674
03675 if (current_call != -1) {
03676
03677 pjsua_call_set_hold(current_call, NULL);
03678
03679 } else {
03680 PJ_LOG(3,(THIS_FILE, "No current call"));
03681 }
03682 break;
03683
03684 case 'v':
03685
03686
03687
03688 if (current_call != -1) {
03689
03690 pjsua_call_reinvite(current_call, PJ_TRUE, NULL);
03691
03692 } else {
03693 PJ_LOG(3,(THIS_FILE, "No current call"));
03694 }
03695 break;
03696
03697 case 'U':
03698
03699
03700
03701 if (current_call != -1) {
03702
03703 pjsua_call_update(current_call, 0, NULL);
03704
03705 } else {
03706 PJ_LOG(3,(THIS_FILE, "No current call"));
03707 }
03708 break;
03709
03710 case 'C':
03711 if (menuin[1] == 'p') {
03712 manage_codec_prio();
03713 }
03714 break;
03715
03716 case 'x':
03717
03718
03719
03720 if (current_call == -1) {
03721
03722 PJ_LOG(3,(THIS_FILE, "No current call"));
03723
03724 } else {
03725 int call = current_call;
03726 pjsua_msg_data msg_data;
03727 pjsip_generic_string_hdr refer_sub;
03728 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
03729 pj_str_t STR_FALSE = { "false", 5 };
03730 pjsua_call_info ci;
03731
03732 pjsua_call_get_info(current_call, &ci);
03733 printf("Transfering current call [%d] %.*s\n",
03734 current_call,
03735 (int)ci.remote_info.slen, ci.remote_info.ptr);
03736
03737 ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
03738
03739
03740
03741 if (call != current_call) {
03742 puts("Call has been disconnected");
03743 continue;
03744 }
03745
03746 pjsua_msg_data_init(&msg_data);
03747 if (app_config.no_refersub) {
03748
03749 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
03750 &STR_FALSE);
03751 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
03752 }
03753 if (result.nb_result != NO_NB) {
03754 if (result.nb_result == -1 || result.nb_result == 0)
03755 puts("You can't do that with transfer call!");
03756 else {
03757 pjsua_buddy_info binfo;
03758 pjsua_buddy_get_info(result.nb_result-1, &binfo);
03759 pjsua_call_xfer( current_call, &binfo.uri, &msg_data);
03760 }
03761
03762 } else if (result.uri_result) {
03763 pj_str_t tmp;
03764 tmp = pj_str(result.uri_result);
03765 pjsua_call_xfer( current_call, &tmp, &msg_data);
03766 }
03767 }
03768 break;
03769
03770 case 'X':
03771
03772
03773
03774 if (current_call == -1) {
03775
03776 PJ_LOG(3,(THIS_FILE, "No current call"));
03777
03778 } else {
03779 int call = current_call;
03780 int dst_call;
03781 pjsua_msg_data msg_data;
03782 pjsip_generic_string_hdr refer_sub;
03783 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
03784 pj_str_t STR_FALSE = { "false", 5 };
03785 pjsua_call_id ids[PJSUA_MAX_CALLS];
03786 pjsua_call_info ci;
03787 unsigned i, count;
03788
03789 count = PJ_ARRAY_SIZE(ids);
03790 pjsua_enum_calls(ids, &count);
03791
03792 if (count <= 1) {
03793 puts("There are no other calls");
03794 continue;
03795 }
03796
03797 pjsua_call_get_info(current_call, &ci);
03798 printf("Transfer call [%d] %.*s to one of the following:\n",
03799 current_call,
03800 (int)ci.remote_info.slen, ci.remote_info.ptr);
03801
03802 for (i=0; i<count; ++i) {
03803 pjsua_call_info call_info;
03804
03805 if (ids[i] == call)
03806 continue;
03807
03808 pjsua_call_get_info(ids[i], &call_info);
03809 printf("%d %.*s [%.*s]\n",
03810 ids[i],
03811 (int)call_info.remote_info.slen,
03812 call_info.remote_info.ptr,
03813 (int)call_info.state_text.slen,
03814 call_info.state_text.ptr);
03815 }
03816
03817 if (!simple_input("Enter call number to be replaced",
03818 buf, sizeof(buf)))
03819 continue;
03820
03821 dst_call = my_atoi(buf);
03822
03823
03824
03825 if (call != current_call) {
03826 puts("Call has been disconnected");
03827 continue;
03828 }
03829
03830
03831 if (dst_call == call) {
03832 puts("Destination call number must not be the same "
03833 "as the call being transfered");
03834 continue;
03835 }
03836 if (dst_call >= PJSUA_MAX_CALLS) {
03837 puts("Invalid destination call number");
03838 continue;
03839 }
03840 if (!pjsua_call_is_active(dst_call)) {
03841 puts("Invalid destination call number");
03842 continue;
03843 }
03844
03845 pjsua_msg_data_init(&msg_data);
03846 if (app_config.no_refersub) {
03847
03848 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
03849 &STR_FALSE);
03850 pj_list_push_back(&msg_data.hdr_list, &refer_sub);
03851 }
03852
03853 pjsua_call_xfer_replaces(call, dst_call,
03854 PJSUA_XFER_NO_REQUIRE_REPLACES,
03855 &msg_data);
03856 }
03857 break;
03858
03859 case '#':
03860
03861
03862
03863 if (current_call == -1) {
03864
03865 PJ_LOG(3,(THIS_FILE, "No current call"));
03866
03867 } else if (!pjsua_call_has_media(current_call)) {
03868
03869 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
03870
03871 } else {
03872 pj_str_t digits;
03873 int call = current_call;
03874 pj_status_t status;
03875
03876 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
03877 sizeof(buf)))
03878 {
03879 break;
03880 }
03881
03882 if (call != current_call) {
03883 puts("Call has been disconnected");
03884 continue;
03885 }
03886
03887 digits = pj_str(buf);
03888 status = pjsua_call_dial_dtmf(current_call, &digits);
03889 if (status != PJ_SUCCESS) {
03890 pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
03891 } else {
03892 puts("DTMF digits enqueued for transmission");
03893 }
03894 }
03895 break;
03896
03897 case '*':
03898
03899 if (current_call == -1) {
03900
03901 PJ_LOG(3,(THIS_FILE, "No current call"));
03902
03903 } else {
03904 const pj_str_t SIP_INFO = pj_str("INFO");
03905 pj_str_t digits;
03906 int call = current_call;
03907 int i;
03908 pj_status_t status;
03909
03910 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,
03911 sizeof(buf)))
03912 {
03913 break;
03914 }
03915
03916 if (call != current_call) {
03917 puts("Call has been disconnected");
03918 continue;
03919 }
03920
03921 digits = pj_str(buf);
03922 for (i=0; i<digits.slen; ++i) {
03923 pjsua_msg_data msg_data;
03924 char body[80];
03925
03926 pjsua_msg_data_init(&msg_data);
03927 msg_data.content_type = pj_str("application/dtmf-relay");
03928
03929 pj_ansi_snprintf(body, sizeof(body),
03930 "Signal=%c\r\n"
03931 "Duration=160",
03932 buf[i]);
03933 msg_data.msg_body = pj_str(body);
03934
03935 status = pjsua_call_send_request(current_call, &SIP_INFO,
03936 &msg_data);
03937 if (status != PJ_SUCCESS) {
03938 break;
03939 }
03940 }
03941 }
03942 break;
03943
03944 case 'S':
03945
03946
03947
03948 if (pjsua_acc_get_count() == 0) {
03949 puts("Sorry, need at least one account configured");
03950 break;
03951 }
03952
03953 puts("Send arbitrary request to remote host");
03954
03955
03956 if (!simple_input("Request method:",text,sizeof(text)))
03957 break;
03958
03959
03960 uri = NULL;
03961 ui_input_url("Destination URI", buf, sizeof(buf), &result);
03962 if (result.nb_result != NO_NB) {
03963
03964 if (result.nb_result == -1) {
03965 puts("Sorry you can't do that!");
03966 continue;
03967 } else if (result.nb_result == 0) {
03968 uri = NULL;
03969 if (current_call == PJSUA_INVALID_ID) {
03970 puts("No current call");
03971 continue;
03972 }
03973 } else {
03974 pjsua_buddy_info binfo;
03975 pjsua_buddy_get_info(result.nb_result-1, &binfo);
03976 tmp.ptr = buf;
03977 pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
03978 uri = buf;
03979 }
03980
03981 } else if (result.uri_result) {
03982 uri = result.uri_result;
03983 }
03984
03985 if (uri) {
03986 tmp = pj_str(uri);
03987 send_request(text, &tmp);
03988 } else {
03989
03990
03991
03992
03993
03994 pj_str_t method = pj_str(text);
03995 pjsua_call_send_request(current_call, &method, NULL);
03996 }
03997 break;
03998
03999 case 'e':
04000 if (pj_ansi_strnicmp(menuin, "echo", 4)==0) {
04001 pj_str_t tmp;
04002
04003 tmp.ptr = menuin+5;
04004 tmp.slen = pj_ansi_strlen(menuin)-6;
04005
04006 if (tmp.slen < 1) {
04007 puts("Usage: echo [0|1]");
04008 break;
04009 }
04010
04011 cmd_echo = *tmp.ptr != '0' || tmp.slen!=1;
04012 }
04013 break;
04014
04015 case 's':
04016 if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
04017 pj_str_t tmp;
04018 int delay;
04019
04020 tmp.ptr = menuin+6;
04021 tmp.slen = pj_ansi_strlen(menuin)-7;
04022
04023 if (tmp.slen < 1) {
04024 puts("Usage: sleep MSEC");
04025 break;
04026 }
04027
04028 delay = pj_strtoul(&tmp);
04029 if (delay < 0) delay = 0;
04030 pj_thread_sleep(delay);
04031 break;
04032 }
04033
04034
04035 case 'u':
04036
04037
04038
04039 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
04040 if (result.nb_result != NO_NB) {
04041 if (result.nb_result == -1) {
04042 int i, count;
04043 count = pjsua_get_buddy_count();
04044 for (i=0; i<count; ++i)
04045 pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
04046 } else if (result.nb_result == 0) {
04047 puts("Sorry, can only subscribe to buddy's presence, "
04048 "not from existing call");
04049 } else {
04050 pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
04051 }
04052
04053 } else if (result.uri_result) {
04054 puts("Sorry, can only subscribe to buddy's presence, "
04055 "not arbitrary URL (for now)");
04056 }
04057
04058 break;
04059
04060 case 'r':
04061 switch (menuin[1]) {
04062 case 'r':
04063
04064
04065
04066 pjsua_acc_set_registration(current_acc, PJ_TRUE);
04067 break;
04068 case 'u':
04069
04070
04071
04072 pjsua_acc_set_registration(current_acc, PJ_FALSE);
04073 break;
04074 }
04075 break;
04076
04077 case 't':
04078 pjsua_acc_get_info(current_acc, &acc_info);
04079 acc_info.online_status = !acc_info.online_status;
04080 pjsua_acc_set_online_status(current_acc, acc_info.online_status);
04081 printf("Setting %s online status to %s\n",
04082 acc_info.acc_uri.ptr,
04083 (acc_info.online_status?"online":"offline"));
04084 break;
04085
04086 case 'T':
04087 change_online_status();
04088 break;
04089
04090 case 'c':
04091 switch (menuin[1]) {
04092 case 'l':
04093 conf_list();
04094 break;
04095 case 'c':
04096 case 'd':
04097 {
04098 char tmp[10], src_port[10], dst_port[10];
04099 pj_status_t status;
04100 int cnt;
04101 const char *src_title, *dst_title;
04102
04103 cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port);
04104
04105 if (cnt != 3) {
04106 conf_list();
04107
04108 src_title = (menuin[1]=='c'?
04109 "Connect src port #":
04110 "Disconnect src port #");
04111 dst_title = (menuin[1]=='c'?
04112 "To dst port #":
04113 "From dst port #");
04114
04115 if (!simple_input(src_title, src_port, sizeof(src_port)))
04116 break;
04117
04118 if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
04119 break;
04120 }
04121
04122 if (menuin[1]=='c') {
04123 status = pjsua_conf_connect(my_atoi(src_port),
04124 my_atoi(dst_port));
04125 } else {
04126 status = pjsua_conf_disconnect(my_atoi(src_port),
04127 my_atoi(dst_port));
04128 }
04129 if (status == PJ_SUCCESS) {
04130 puts("Success");
04131 } else {
04132 puts("ERROR!!");
04133 }
04134 }
04135 break;
04136 }
04137 break;
04138
04139 case 'V':
04140
04141 sprintf(buf, "Adjust mic level: [%4.1fx] ", app_config.mic_level);
04142 if (simple_input(buf,text,sizeof(text))) {
04143 char *err;
04144 app_config.mic_level = (float)strtod(text, &err);
04145 pjsua_conf_adjust_rx_level(0, app_config.mic_level);
04146 }
04147 sprintf(buf, "Adjust speaker level: [%4.1fx] ",
04148 app_config.speaker_level);
04149 if (simple_input(buf,text,sizeof(text))) {
04150 char *err;
04151 app_config.speaker_level = (float)strtod(text, &err);
04152 pjsua_conf_adjust_tx_level(0, app_config.speaker_level);
04153 }
04154
04155 break;
04156
04157 case 'd':
04158 if (menuin[1] == 'c') {
04159 char settings[2000];
04160 int len;
04161
04162 len = write_settings(&app_config, settings, sizeof(settings));
04163 if (len < 1)
04164 PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
04165 else
04166 PJ_LOG(3,(THIS_FILE,
04167 "Dumping configuration (%d bytes):\n%s\n",
04168 len, settings));
04169
04170 } else if (menuin[1] == 'q') {
04171
04172 if (current_call != PJSUA_INVALID_ID) {
04173 log_call_dump(current_call);
04174 } else {
04175 PJ_LOG(3,(THIS_FILE, "No current call"));
04176 }
04177
04178 } else {
04179 app_dump(menuin[1]=='d');
04180 }
04181 break;
04182
04183
04184 case 'f':
04185 if (simple_input("Enter output filename", buf, sizeof(buf))) {
04186 char settings[2000];
04187 int len;
04188
04189 len = write_settings(&app_config, settings, sizeof(settings));
04190 if (len < 1)
04191 PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
04192 else {
04193 pj_oshandle_t fd;
04194 pj_status_t status;
04195
04196 status = pj_file_open(app_config.pool, buf,
04197 PJ_O_WRONLY, &fd);
04198 if (status != PJ_SUCCESS) {
04199 pjsua_perror(THIS_FILE, "Unable to open file", status);
04200 } else {
04201 pj_ssize_t size = len;
04202 pj_file_write(fd, settings, &size);
04203 pj_file_close(fd);
04204
04205 printf("Settings successfully written to '%s'\n", buf);
04206 }
04207 }
04208
04209 }
04210 break;
04211
04212
04213 case 'L':
04214 app_restart = PJ_TRUE;
04215
04216
04217 case 'q':
04218 goto on_exit;
04219
04220 case 'R':
04221 if (!pjsua_call_is_active(current_call)) {
04222 PJ_LOG(1,(THIS_FILE, "Call %d has gone", current_call));
04223 } else if (menuin[1] == 'a') {
04224 pjsua_call_process_redirect(current_call,
04225 PJSIP_REDIRECT_ACCEPT);
04226 } else if (menuin[1] == 'r') {
04227 pjsua_call_process_redirect(current_call,
04228 PJSIP_REDIRECT_REJECT);
04229 } else {
04230 pjsua_call_process_redirect(current_call,
04231 PJSIP_REDIRECT_STOP);
04232 }
04233 break;
04234
04235 default:
04236 if (menuin[0] != '\n' && menuin[0] != '\r') {
04237 printf("Invalid input %s", menuin);
04238 }
04239 keystroke_help();
04240 break;
04241 }
04242 }
04243
04244 on_exit:
04245 ;
04246 }
04247
04248
04249
04250
04251
04252
04253
04254 static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata)
04255 {
04256 pjsip_tx_data *tdata;
04257 pjsip_status_code status_code;
04258 pj_status_t status;
04259
04260
04261 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
04262 &pjsip_ack_method) == 0)
04263 return PJ_TRUE;
04264
04265
04266 if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
04267 &pjsip_notify_method) == 0)
04268 {
04269
04270 status_code = PJSIP_SC_BAD_REQUEST;
04271 } else {
04272
04273 status_code = PJSIP_SC_METHOD_NOT_ALLOWED;
04274 }
04275 status = pjsip_endpt_create_response(pjsua_get_pjsip_endpt(),
04276 rdata, status_code,
04277 NULL, &tdata);
04278 if (status != PJ_SUCCESS) {
04279 pjsua_perror(THIS_FILE, "Unable to create response", status);
04280 return PJ_TRUE;
04281 }
04282
04283
04284 if (status_code == PJSIP_SC_METHOD_NOT_ALLOWED) {
04285 const pjsip_hdr *cap_hdr;
04286 cap_hdr = pjsip_endpt_get_capability(pjsua_get_pjsip_endpt(),
04287 PJSIP_H_ALLOW, NULL);
04288 if (cap_hdr) {
04289 pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool,
04290 cap_hdr));
04291 }
04292 }
04293
04294
04295 {
04296 pj_str_t user_agent;
04297 char tmp[80];
04298 const pj_str_t USER_AGENT = { "User-Agent", 10};
04299 pjsip_hdr *h;
04300
04301 pj_ansi_snprintf(tmp, sizeof(tmp), "PJSUA v%s/%s",
04302 pj_get_version(), PJ_OS_NAME);
04303 pj_strdup2_with_null(tdata->pool, &user_agent, tmp);
04304
04305 h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
04306 &USER_AGENT,
04307 &user_agent);
04308 pjsip_msg_add_hdr(tdata->msg, h);
04309 }
04310
04311 pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(), rdata, tdata,
04312 NULL, NULL);
04313
04314 return PJ_TRUE;
04315 }
04316
04317
04318
04319 static pjsip_module mod_default_handler =
04320 {
04321 NULL, NULL,
04322 { "mod-default-handler", 19 },
04323 -1,
04324 PJSIP_MOD_PRIORITY_APPLICATION+99,
04325 NULL,
04326 NULL,
04327 NULL,
04328 NULL,
04329 &default_mod_on_rx_request,
04330 NULL,
04331 NULL,
04332 NULL,
04333 NULL,
04334
04335 };
04336
04337
04338
04339
04340
04341
04342
04343
04344 pj_status_t app_init(int argc, char *argv[])
04345 {
04346 pjsua_transport_id transport_id = -1;
04347 pjsua_transport_config tcp_cfg;
04348 unsigned i;
04349 pj_status_t status;
04350
04351 app_restart = PJ_FALSE;
04352
04353
04354 status = pjsua_create();
04355 if (status != PJ_SUCCESS)
04356 return status;
04357
04358
04359 app_config.pool = pjsua_pool_create("pjsua-app", 1000, 1000);
04360
04361
04362 default_config(&app_config);
04363
04364
04365 status = parse_args(argc, argv, &app_config, &uri_arg);
04366 if (status != PJ_SUCCESS)
04367 return status;
04368
04369
04370 app_config.cfg.cb.on_call_state = &on_call_state;
04371 app_config.cfg.cb.on_call_media_state = &on_call_media_state;
04372 app_config.cfg.cb.on_incoming_call = &on_incoming_call;
04373 app_config.cfg.cb.on_call_tsx_state = &on_call_tsx_state;
04374 app_config.cfg.cb.on_dtmf_digit = &call_on_dtmf_callback;
04375 app_config.cfg.cb.on_call_redirected = &call_on_redirected;
04376 app_config.cfg.cb.on_reg_state = &on_reg_state;
04377 app_config.cfg.cb.on_incoming_subscribe = &on_incoming_subscribe;
04378 app_config.cfg.cb.on_buddy_state = &on_buddy_state;
04379 app_config.cfg.cb.on_pager = &on_pager;
04380 app_config.cfg.cb.on_typing = &on_typing;
04381 app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
04382 app_config.cfg.cb.on_call_replaced = &on_call_replaced;
04383 app_config.cfg.cb.on_nat_detect = &on_nat_detect;
04384 app_config.cfg.cb.on_mwi_info = &on_mwi_info;
04385
04386
04387 if (app_config.capture_lat > 0)
04388 app_config.media_cfg.snd_rec_latency = app_config.capture_lat;
04389 if (app_config.playback_lat)
04390 app_config.media_cfg.snd_play_latency = app_config.playback_lat;
04391
04392
04393 status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
04394 &app_config.media_cfg);
04395 if (status != PJ_SUCCESS)
04396 return status;
04397
04398
04399 status = pjsip_endpt_register_module(pjsua_get_pjsip_endpt(),
04400 &mod_default_handler);
04401 if (status != PJ_SUCCESS)
04402 return status;
04403
04404 #ifdef STEREO_DEMO
04405 stereo_demo();
04406 #endif
04407
04408
04409 for (i=0; i<PJ_ARRAY_SIZE(app_config.call_data); ++i) {
04410 app_config.call_data[i].timer.id = PJSUA_INVALID_ID;
04411 app_config.call_data[i].timer.cb = &call_timeout_callback;
04412 }
04413
04414
04415 for (i=0; i<app_config.wav_count; ++i) {
04416 pjsua_player_id wav_id;
04417 unsigned play_options = 0;
04418
04419 if (app_config.auto_play_hangup)
04420 play_options |= PJMEDIA_FILE_NO_LOOP;
04421
04422 status = pjsua_player_create(&app_config.wav_files[i], play_options,
04423 &wav_id);
04424 if (status != PJ_SUCCESS)
04425 goto on_error;
04426
04427 if (app_config.wav_id == PJSUA_INVALID_ID) {
04428 app_config.wav_id = wav_id;
04429 app_config.wav_port = pjsua_player_get_conf_port(app_config.wav_id);
04430 if (app_config.auto_play_hangup) {
04431 pjmedia_port *port;
04432
04433 pjsua_player_get_port(app_config.wav_id, &port);
04434 status = pjmedia_wav_player_set_eof_cb(port, NULL,
04435 &on_playfile_done);
04436 if (status != PJ_SUCCESS)
04437 goto on_error;
04438
04439 pj_timer_entry_init(&app_config.auto_hangup_timer, 0, NULL,
04440 &hangup_timeout_callback);
04441 }
04442 }
04443 }
04444
04445
04446 for (i=0; i<app_config.tone_count; ++i) {
04447 pjmedia_port *tport;
04448 char name[80];
04449 pj_str_t label;
04450 pj_status_t status;
04451
04452 pj_ansi_snprintf(name, sizeof(name), "tone-%d,%d",
04453 app_config.tones[i].freq1,
04454 app_config.tones[i].freq2);
04455 label = pj_str(name);
04456 status = pjmedia_tonegen_create2(app_config.pool, &label,
04457 8000, 1, 160, 16,
04458 PJMEDIA_TONEGEN_LOOP, &tport);
04459 if (status != PJ_SUCCESS) {
04460 pjsua_perror(THIS_FILE, "Unable to create tone generator", status);
04461 goto on_error;
04462 }
04463
04464 status = pjsua_conf_add_port(app_config.pool, tport,
04465 &app_config.tone_slots[i]);
04466 pj_assert(status == PJ_SUCCESS);
04467
04468 status = pjmedia_tonegen_play(tport, 1, &app_config.tones[i], 0);
04469 pj_assert(status == PJ_SUCCESS);
04470 }
04471
04472
04473 if (app_config.rec_file.slen) {
04474 status = pjsua_recorder_create(&app_config.rec_file, 0, NULL, 0, 0,
04475 &app_config.rec_id);
04476 if (status != PJ_SUCCESS)
04477 goto on_error;
04478
04479 app_config.rec_port = pjsua_recorder_get_conf_port(app_config.rec_id);
04480 }
04481
04482 pj_memcpy(&tcp_cfg, &app_config.udp_cfg, sizeof(tcp_cfg));
04483
04484
04485 if (app_config.no_tones == PJ_FALSE) {
04486 unsigned i, samples_per_frame;
04487 pjmedia_tone_desc tone[RING_CNT+RINGBACK_CNT];
04488 pj_str_t name;
04489
04490 samples_per_frame = app_config.media_cfg.audio_frame_ptime *
04491 app_config.media_cfg.clock_rate *
04492 app_config.media_cfg.channel_count / 1000;
04493
04494
04495 name = pj_str("ringback");
04496 status = pjmedia_tonegen_create2(app_config.pool, &name,
04497 app_config.media_cfg.clock_rate,
04498 app_config.media_cfg.channel_count,
04499 samples_per_frame,
04500 16, PJMEDIA_TONEGEN_LOOP,
04501 &app_config.ringback_port);
04502 if (status != PJ_SUCCESS)
04503 goto on_error;
04504
04505 pj_bzero(&tone, sizeof(tone));
04506 for (i=0; i<RINGBACK_CNT; ++i) {
04507 tone[i].freq1 = RINGBACK_FREQ1;
04508 tone[i].freq2 = RINGBACK_FREQ2;
04509 tone[i].on_msec = RINGBACK_ON;
04510 tone[i].off_msec = RINGBACK_OFF;
04511 }
04512 tone[RINGBACK_CNT-1].off_msec = RINGBACK_INTERVAL;
04513
04514 pjmedia_tonegen_play(app_config.ringback_port, RINGBACK_CNT, tone,
04515 PJMEDIA_TONEGEN_LOOP);
04516
04517
04518 status = pjsua_conf_add_port(app_config.pool, app_config.ringback_port,
04519 &app_config.ringback_slot);
04520 if (status != PJ_SUCCESS)
04521 goto on_error;
04522
04523
04524 name = pj_str("ring");
04525 status = pjmedia_tonegen_create2(app_config.pool, &name,
04526 app_config.media_cfg.clock_rate,
04527 app_config.media_cfg.channel_count,
04528 samples_per_frame,
04529 16, PJMEDIA_TONEGEN_LOOP,
04530 &app_config.ring_port);
04531 if (status != PJ_SUCCESS)
04532 goto on_error;
04533
04534 for (i=0; i<RING_CNT; ++i) {
04535 tone[i].freq1 = RING_FREQ1;
04536 tone[i].freq2 = RING_FREQ2;
04537 tone[i].on_msec = RING_ON;
04538 tone[i].off_msec = RING_OFF;
04539 }
04540 tone[RING_CNT-1].off_msec = RING_INTERVAL;
04541
04542 pjmedia_tonegen_play(app_config.ring_port, RING_CNT,
04543 tone, PJMEDIA_TONEGEN_LOOP);
04544
04545 status = pjsua_conf_add_port(app_config.pool, app_config.ring_port,
04546 &app_config.ring_slot);
04547 if (status != PJ_SUCCESS)
04548 goto on_error;
04549
04550 }
04551
04552
04553 if (!app_config.no_udp) {
04554 pjsua_acc_id aid;
04555 pjsip_transport_type_e type = PJSIP_TRANSPORT_UDP;
04556
04557 if (app_config.ipv6)
04558 type = PJSIP_TRANSPORT_UDP6;
04559
04560 status = pjsua_transport_create(type,
04561 &app_config.udp_cfg,
04562 &transport_id);
04563 if (status != PJ_SUCCESS)
04564 goto on_error;
04565
04566
04567 pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
04568
04569 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
04570
04571 if (app_config.udp_cfg.port == 0) {
04572 pjsua_transport_info ti;
04573 pj_sockaddr_in *a;
04574
04575 pjsua_transport_get_info(transport_id, &ti);
04576 a = (pj_sockaddr_in*)&ti.local_addr;
04577
04578 tcp_cfg.port = pj_ntohs(a->sin_port);
04579 }
04580 }
04581
04582
04583 if (!app_config.no_tcp) {
04584 status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
04585 &tcp_cfg,
04586 &transport_id);
04587 if (status != PJ_SUCCESS)
04588 goto on_error;
04589
04590
04591 pjsua_acc_add_local(transport_id, PJ_TRUE, NULL);
04592 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
04593
04594 }
04595
04596
04597 #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
04598
04599 if (app_config.use_tls) {
04600
04601 pjsua_acc_id acc_id;
04602
04603
04604 tcp_cfg.port++;
04605 status = pjsua_transport_create(PJSIP_TRANSPORT_TLS,
04606 &tcp_cfg,
04607 &transport_id);
04608 tcp_cfg.port--;
04609 if (status != PJ_SUCCESS)
04610 goto on_error;
04611
04612
04613 pjsua_acc_add_local(transport_id, PJ_FALSE, &acc_id);
04614 pjsua_acc_set_online_status(acc_id, PJ_TRUE);
04615 }
04616 #endif
04617
04618 if (transport_id == -1) {
04619 PJ_LOG(1,(THIS_FILE, "Error: no transport is configured"));
04620 status = -1;
04621 goto on_error;
04622 }
04623
04624
04625
04626 for (i=0; i<app_config.acc_cnt; ++i) {
04627 status = pjsua_acc_add(&app_config.acc_cfg[i], PJ_TRUE, NULL);
04628 if (status != PJ_SUCCESS)
04629 goto on_error;
04630 pjsua_acc_set_online_status(current_acc, PJ_TRUE);
04631 }
04632
04633
04634 for (i=0; i<app_config.buddy_cnt; ++i) {
04635 status = pjsua_buddy_add(&app_config.buddy_cfg[i], NULL);
04636 if (status != PJ_SUCCESS)
04637 goto on_error;
04638 }
04639
04640
04641 for (i=0; i<app_config.codec_cnt; ++i) {
04642 pjsua_codec_set_priority(&app_config.codec_arg[i],
04643 (pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9));
04644 }
04645
04646
04647 for (i=0; i<app_config.codec_dis_cnt; ++i) {
04648 pjsua_codec_set_priority(&app_config.codec_dis[i],PJMEDIA_CODEC_PRIO_DISABLED);
04649 }
04650
04651
04652 #ifdef TRANSPORT_ADAPTER_SAMPLE
04653 status = transport_adapter_sample();
04654
04655 #else
04656 if (app_config.ipv6)
04657 status = create_ipv6_media_transports();
04658 else
04659 status = pjsua_media_transports_create(&app_config.rtp_cfg);
04660 #endif
04661 if (status != PJ_SUCCESS)
04662 goto on_error;
04663
04664
04665 #ifndef STEREO_DEMO
04666 if (app_config.null_audio) {
04667 status = pjsua_set_null_snd_dev();
04668 if (status != PJ_SUCCESS)
04669 return status;
04670 }
04671 #endif
04672
04673 if (app_config.capture_dev != PJSUA_INVALID_ID ||
04674 app_config.playback_dev != PJSUA_INVALID_ID)
04675 {
04676 status = pjsua_set_snd_dev(app_config.capture_dev,
04677 app_config.playback_dev);
04678 if (status != PJ_SUCCESS)
04679 goto on_error;
04680 }
04681
04682 return PJ_SUCCESS;
04683
04684 on_error:
04685 app_destroy();
04686 return status;
04687 }
04688
04689
04690 static int stdout_refresh_proc(void *arg)
04691 {
04692 PJ_UNUSED_ARG(arg);
04693
04694
04695
04696
04697 pj_thread_set_prio(pj_thread_this(),
04698 pj_thread_get_prio_min(pj_thread_this()));
04699
04700 while (!stdout_refresh_quit) {
04701 pj_thread_sleep(stdout_refresh * 1000);
04702 puts(stdout_refresh_text);
04703 fflush(stdout);
04704 }
04705
04706 return 0;
04707 }
04708
04709 pj_status_t app_main(void)
04710 {
04711 pj_thread_t *stdout_refresh_thread = NULL;
04712 pj_status_t status;
04713
04714
04715 status = pjsua_start();
04716 if (status != PJ_SUCCESS) {
04717 app_destroy();
04718 return status;
04719 }
04720
04721
04722 if (stdout_refresh > 0) {
04723 pj_thread_create(app_config.pool, "stdout", &stdout_refresh_proc,
04724 NULL, 0, 0, &stdout_refresh_thread);
04725 }
04726
04727 console_app_main(&uri_arg);
04728
04729 if (stdout_refresh_thread) {
04730 stdout_refresh_quit = PJ_TRUE;
04731 pj_thread_join(stdout_refresh_thread);
04732 pj_thread_destroy(stdout_refresh_thread);
04733 }
04734
04735 return PJ_SUCCESS;
04736 }
04737
04738 pj_status_t app_destroy(void)
04739 {
04740 pj_status_t status;
04741 unsigned i;
04742
04743 #ifdef STEREO_DEMO
04744 if (app_config.snd) {
04745 pjmedia_snd_port_destroy(app_config.snd);
04746 app_config.snd = NULL;
04747 }
04748 if (app_config.sc_ch1) {
04749 pjsua_conf_remove_port(app_config.sc_ch1_slot);
04750 app_config.sc_ch1_slot = PJSUA_INVALID_ID;
04751 pjmedia_port_destroy(app_config.sc_ch1);
04752 app_config.sc_ch1 = NULL;
04753 }
04754 if (app_config.sc) {
04755 pjmedia_port_destroy(app_config.sc);
04756 app_config.sc = NULL;
04757 }
04758 #endif
04759
04760
04761 if (app_config.ringback_port &&
04762 app_config.ringback_slot != PJSUA_INVALID_ID)
04763 {
04764 pjsua_conf_remove_port(app_config.ringback_slot);
04765 app_config.ringback_slot = PJSUA_INVALID_ID;
04766 pjmedia_port_destroy(app_config.ringback_port);
04767 app_config.ringback_port = NULL;
04768 }
04769
04770
04771 if (app_config.ring_port && app_config.ring_slot != PJSUA_INVALID_ID) {
04772 pjsua_conf_remove_port(app_config.ring_slot);
04773 app_config.ring_slot = PJSUA_INVALID_ID;
04774 pjmedia_port_destroy(app_config.ring_port);
04775 app_config.ring_port = NULL;
04776 }
04777
04778
04779 for (i=0; i<app_config.tone_count; ++i) {
04780 pjsua_conf_remove_port(app_config.tone_slots[i]);
04781 }
04782
04783 if (app_config.pool) {
04784 pj_pool_release(app_config.pool);
04785 app_config.pool = NULL;
04786 }
04787
04788 status = pjsua_destroy();
04789
04790 pj_bzero(&app_config, sizeof(app_config));
04791
04792 return status;
04793 }
04794
04795
04796 #ifdef STEREO_DEMO
04797
04798
04799
04800
04801
04802
04803
04804
04805
04806
04807
04808
04809
04810
04811
04812
04813 static void stereo_demo()
04814 {
04815 pjmedia_port *conf;
04816 pj_status_t status;
04817
04818
04819 conf = pjsua_set_no_snd_dev();
04820
04821
04822 status = pjmedia_splitcomb_create(app_config.pool,
04823 conf->info.clock_rate ,
04824 2 ,
04825 2 * conf->info.samples_per_frame,
04826 conf->info.bits_per_sample,
04827 0 ,
04828 &app_config.sc);
04829 pj_assert(status == PJ_SUCCESS);
04830
04831
04832 status = pjmedia_splitcomb_set_channel(app_config.sc, 0 ,
04833 0 ,
04834 conf);
04835 pj_assert(status == PJ_SUCCESS);
04836
04837
04838 status = pjmedia_splitcomb_create_rev_channel(app_config.pool,
04839 app_config.sc,
04840 1 ,
04841 0 ,
04842 &app_config.sc_ch1);
04843 pj_assert(status == PJ_SUCCESS);
04844
04845
04846
04847
04848 status = pjsua_conf_add_port(app_config.pool, app_config.sc_ch1,
04849 &app_config.sc_ch1_slot);
04850 pj_assert(status == PJ_SUCCESS);
04851
04852
04853 status = pjmedia_snd_port_create(app_config.pool, -1, -1,
04854 conf->info.clock_rate,
04855 2 ,
04856 2 * conf->info.samples_per_frame,
04857 conf->info.bits_per_sample,
04858 0, &app_config.snd);
04859 pj_assert(status == PJ_SUCCESS);
04860
04861
04862
04863 status = pjmedia_snd_port_connect(app_config.snd, app_config.sc);
04864 pj_assert(status == PJ_SUCCESS);
04865
04866 }
04867 #endif
04868
04869 #ifdef TRANSPORT_ADAPTER_SAMPLE
04870 static pj_status_t create_transport_adapter(pjmedia_endpt *med_endpt, int port,
04871 pjmedia_transport **p_tp)
04872 {
04873 pjmedia_transport *udp;
04874 pj_status_t status;
04875
04876
04877 status = pjmedia_transport_udp_create(med_endpt, NULL, port, 0, &udp);
04878 if (status != PJ_SUCCESS)
04879 return status;
04880
04881
04882 status = pjmedia_tp_adapter_create(med_endpt, NULL, udp, p_tp);
04883 if (status != PJ_SUCCESS) {
04884 pjmedia_transport_close(udp);
04885 return status;
04886 }
04887
04888 return PJ_SUCCESS;
04889 }
04890
04891 static pj_status_t transport_adapter_sample(void)
04892 {
04893 pjsua_media_transport tp[PJSUA_MAX_CALLS];
04894 pj_status_t status;
04895 int port = 7000;
04896 unsigned i;
04897
04898 for (i=0; i<app_config.cfg.max_calls; ++i) {
04899 status = create_transport_adapter(pjsua_get_pjmedia_endpt(),
04900 port + i*10,
04901 &tp[i].transport);
04902 if (status != PJ_SUCCESS)
04903 return status;
04904 }
04905
04906 return pjsua_media_transports_attach(tp, i, PJ_TRUE);
04907 }
04908 #endif
04909
04910 static pj_status_t create_ipv6_media_transports(void)
04911 {
04912 pjsua_media_transport tp[PJSUA_MAX_CALLS];
04913 pj_status_t status;
04914 int port = app_config.rtp_cfg.port;
04915 unsigned i;
04916
04917 for (i=0; i<app_config.cfg.max_calls; ++i) {
04918 enum { MAX_RETRY = 10 };
04919 pj_sock_t sock[2];
04920 pjmedia_sock_info si;
04921 unsigned j;
04922
04923
04924 status = PJ_SUCCESS;
04925
04926 for (j=0; j<MAX_RETRY; ++j) {
04927 unsigned k;
04928
04929 for (k=0; k<2; ++k) {
04930 pj_sockaddr bound_addr;
04931
04932 status = pj_sock_socket(pj_AF_INET6(), pj_SOCK_DGRAM(), 0, &sock[k]);
04933 if (status != PJ_SUCCESS)
04934 break;
04935
04936 status = pj_sockaddr_init(pj_AF_INET6(), &bound_addr,
04937 &app_config.rtp_cfg.bound_addr,
04938 (unsigned short)(port+k));
04939 if (status != PJ_SUCCESS)
04940 break;
04941
04942 status = pj_sock_bind(sock[k], &bound_addr,
04943 pj_sockaddr_get_len(&bound_addr));
04944 if (status != PJ_SUCCESS)
04945 break;
04946 }
04947 if (status != PJ_SUCCESS) {
04948 if (k==1)
04949 pj_sock_close(sock[0]);
04950
04951 if (port != 0)
04952 port += 10;
04953 else
04954 break;
04955
04956 continue;
04957 }
04958
04959 pj_bzero(&si, sizeof(si));
04960 si.rtp_sock = sock[0];
04961 si.rtcp_sock = sock[1];
04962
04963 pj_sockaddr_init(pj_AF_INET6(), &si.rtp_addr_name,
04964 &app_config.rtp_cfg.public_addr,
04965 (unsigned short)(port));
04966 pj_sockaddr_init(pj_AF_INET6(), &si.rtcp_addr_name,
04967 &app_config.rtp_cfg.public_addr,
04968 (unsigned short)(port+1));
04969
04970 status = pjmedia_transport_udp_attach(pjsua_get_pjmedia_endpt(),
04971 NULL,
04972 &si,
04973 0,
04974 &tp[i].transport);
04975 if (port != 0)
04976 port += 10;
04977 else
04978 break;
04979
04980 if (status == PJ_SUCCESS)
04981 break;
04982 }
04983
04984 if (status != PJ_SUCCESS) {
04985 pjsua_perror(THIS_FILE, "Error creating IPv6 UDP media transport",
04986 status);
04987 for (j=0; j<i; ++j) {
04988 pjmedia_transport_close(tp[j].transport);
04989 }
04990 return status;
04991 }
04992 }
04993
04994 return pjsua_media_transports_attach(tp, i, PJ_TRUE);
04995 }
PJSIP Open Source, high performance, small footprint, and very very portable SIP stack
Copyright (C) 2006-2008 Teluu Inc.
|
|