|
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 #include <pjsua-lib/pjsua.h>
00020
00021
00022 #define THIS_FILE "pjsua_app.c"
00023 #define NO_LIMIT (int)0x7FFFFFFF
00024
00025
00026
00027
00028 #define RINGBACK_FREQ1 440
00029 #define RINGBACK_FREQ2 480
00030 #define RINGBACK_ON 2000
00031 #define RINGBACK_OFF 4000
00032 #define RINGBACK_CNT 1
00033 #define RINGBACK_INTERVAL 4000
00034
00035 #define RING_FREQ1 800
00036 #define RING_FREQ2 640
00037 #define RING_ON 200
00038 #define RING_OFF 100
00039 #define RING_CNT 3
00040 #define RING_INTERVAL 3000
00041
00042
00043
00044 struct call_data
00045 {
00046 pj_timer_entry timer;
00047 pj_bool_t ringback_on;
00048 pj_bool_t ring_on;
00049 };
00050
00051
00052
00053 static struct app_config
00054 {
00055 pjsua_config cfg;
00056 pjsua_logging_config log_cfg;
00057 pjsua_media_config media_cfg;
00058 pj_bool_t no_refersub;
00059 pj_bool_t no_tcp;
00060 pj_bool_t no_udp;
00061 pj_bool_t use_tls;
00062 pjsua_transport_config udp_cfg;
00063 pjsua_transport_config rtp_cfg;
00064
00065 unsigned acc_cnt;
00066 pjsua_acc_config acc_cfg[PJSUA_MAX_ACC];
00067
00068 unsigned buddy_cnt;
00069 pjsua_buddy_config buddy_cfg[PJSUA_MAX_BUDDIES];
00070
00071 struct call_data call_data[PJSUA_MAX_CALLS];
00072
00073 pj_pool_t *pool;
00074
00075
00076 unsigned codec_cnt;
00077 pj_str_t codec_arg[32];
00078 unsigned codec_dis_cnt;
00079 pj_str_t codec_dis[32];
00080 pj_bool_t null_audio;
00081 unsigned wav_count;
00082 pj_str_t wav_files[32];
00083 unsigned tone_count;
00084 pjmedia_tone_desc tones[32];
00085 pjsua_conf_port_id tone_slots[32];
00086 pjsua_player_id wav_id;
00087 pjsua_conf_port_id wav_port;
00088 pj_bool_t auto_play;
00089 pj_bool_t auto_loop;
00090 pj_bool_t auto_conf;
00091 pj_str_t rec_file;
00092 pj_bool_t auto_rec;
00093 pjsua_recorder_id rec_id;
00094 pjsua_conf_port_id rec_port;
00095 unsigned auto_answer;
00096 unsigned duration;
00097
00098 #ifdef STEREO_DEMO
00099 pjmedia_snd_port *snd;
00100 #endif
00101
00102 float mic_level,
00103 speaker_level;
00104
00105 int capture_dev, playback_dev;
00106 unsigned capture_lat, playback_lat;
00107
00108 pj_bool_t no_tones;
00109 int ringback_slot;
00110 int ringback_cnt;
00111 pjmedia_port *ringback_port;
00112 int ring_slot;
00113 int ring_cnt;
00114 pjmedia_port *ring_port;
00115
00116 } app_config;
00117
00118
00119
00120 #define current_acc pjsua_acc_get_default()
00121 static pjsua_call_id current_call = PJSUA_INVALID_ID;
00122 static pj_bool_t cmd_echo;
00123 static int stdout_refresh = -1;
00124 static const char *stdout_refresh_text = "STDOUT_REFRESH";
00125 static pj_bool_t stdout_refresh_quit = PJ_FALSE;
00126 static pj_str_t uri_arg;
00127
00128 static char some_buf[2048];
00129
00130 #ifdef STEREO_DEMO
00131 static void stereo_demo();
00132 #endif
00133 pj_status_t app_destroy(void);
00134
00135 static void ringback_start(pjsua_call_id call_id);
00136 static void ring_start(pjsua_call_id call_id);
00137 static void ring_stop(pjsua_call_id call_id);
00138
00139
00140
00141
00142
00143
00144
00145 static void usage(void)
00146 {
00147 puts ("Usage:");
00148 puts (" pjsua [options] [SIP URL to call]");
00149 puts ("");
00150 puts ("General options:");
00151 puts (" --config-file=file Read the config/arguments from file.");
00152 puts (" --help Display this help screen");
00153 puts (" --version Display version info");
00154 puts ("");
00155 puts ("Logging options:");
00156 puts (" --log-file=fname Log to filename (default stderr)");
00157 puts (" --log-level=N Set log max level to N (0(none) to 6(trace)) (default=5)");
00158 puts (" --app-log-level=N Set log max level for stdout display (default=4)");
00159 puts ("");
00160 puts ("SIP Account options:");
00161 puts (" --use-ims Enable 3GPP/IMS related settings on this account");
00162 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00163 puts (" --use-srtp=N Use SRTP? 0:disabled, 1:optional, 2:mandatory (def:0)");
00164 puts (" --srtp-secure=N SRTP require secure SIP? 0:no, 1:tls, 1:sips (def:1)");
00165 #endif
00166 puts (" --registrar=url Set the URL of registrar server");
00167 puts (" --id=url Set the URL of local ID (used in From header)");
00168 puts (" --contact=url Optionally override the Contact information");
00169 puts (" --proxy=url Optional URL of proxy server to visit");
00170 puts (" May be specified multiple times");
00171 puts (" --reg-timeout=SEC Optional registration interval (default 55)");
00172 puts (" --realm=string Set realm");
00173 puts (" --username=string Set authentication username");
00174 puts (" --password=string Set authentication password");
00175 puts (" --publish Send presence PUBLISH for this account");
00176 puts (" --use-100rel Require reliable provisional response (100rel)");
00177 puts (" --auto-update-nat=N Where N is 0 or 1 to enable/disable SIP traversal behind");
00178 puts (" symmetric NAT (default 1)");
00179 puts (" --next-cred Add another credentials");
00180 puts ("");
00181 puts ("SIP Account Control:");
00182 puts (" --next-account Add more account");
00183 puts ("");
00184 puts ("Transport Options:");
00185 puts (" --local-port=port Set TCP/UDP port. This implicitly enables both ");
00186 puts (" TCP and UDP transports on the specified port, unless");
00187 puts (" if TCP or UDP is disabled.");
00188 puts (" --ip-addr=IP Use the specifed address as SIP and RTP addresses.");
00189 puts (" (Hint: the IP may be the public IP of the NAT/router)");
00190 puts (" --no-tcp Disable TCP transport.");
00191 puts (" --no-udp Disable UDP transport.");
00192 puts (" --nameserver=NS Add the specified nameserver to enable SRV resolution");
00193 puts (" This option can be specified multiple times.");
00194 puts (" --outbound=url Set the URL of global outbound proxy server");
00195 puts (" May be specified multiple times");
00196 puts (" --stun-srv=name Set STUN server host or domain");
00197 puts ("");
00198 puts ("TLS Options:");
00199 puts (" --use-tls Enable TLS transport (default=no)");
00200 puts (" --tls-ca-file Specify TLS CA file (default=none)");
00201 puts (" --tls-cert-file Specify TLS certificate file (default=none)");
00202 puts (" --tls-privkey-file Specify TLS private key file (default=none)");
00203 puts (" --tls-password Specify TLS password to private key file (default=none)");
00204 puts (" --tls-verify-server Verify server's certificate (default=no)");
00205 puts (" --tls-verify-client Verify client's certificate (default=no)");
00206 puts (" --tls-neg-timeout Specify TLS negotiation timeout (default=no)");
00207
00208 puts ("");
00209 puts ("Media Options:");
00210 puts (" --add-codec=name Manually add codec (default is to enable all)");
00211 puts (" --dis-codec=name Disable codec (can be specified multiple times)");
00212 puts (" --clock-rate=N Override conference bridge clock rate");
00213 puts (" --snd-clock-rate=N Override sound device clock rate");
00214 puts (" --stereo Audio device and conference bridge opened in stereo mode");
00215 puts (" --null-audio Use NULL audio device");
00216 puts (" --play-file=file Register WAV file in conference bridge.");
00217 puts (" This can be specified multiple times.");
00218 puts (" --play-tone=FORMAT Register tone to the conference bridge.");
00219 puts (" FORMAT is 'F1,F2,ON,OFF', where F1,F2 are");
00220 puts (" frequencies, and ON,OFF=on/off duration in msec.");
00221 puts (" This can be specified multiple times.");
00222 puts (" --auto-play Automatically play the file (to incoming calls only)");
00223 puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP");
00224 puts (" --auto-conf Automatically put calls in conference with others");
00225 puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");
00226 puts (" --auto-rec Automatically record conversation");
00227 puts (" --quality=N Specify media quality (0-10, default=6)");
00228 puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)");
00229 puts (" --no-vad Disable VAD/silence detector (default=vad enabled)");
00230 puts (" --ec-tail=MSEC Set echo canceller tail length (default=256)");
00231 puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is 20)");
00232 puts (" --capture-dev=id Audio capture device ID (default=-1)");
00233 puts (" --playback-dev=id Audio playback device ID (default=-1)");
00234 puts (" --capture-lat=N Audio capture latency, in ms (default=10)");
00235 puts (" --playback-lat=N Audio playback latency, in ms (default=100)");
00236 puts (" --snd-auto-close=N Auto close audio device when it is idle for N seconds.");
00237 puts (" Specify N=-1 (default) to disable this feature.");
00238 puts (" Specify N=0 for instant close when unused.");
00239 puts (" --no-tones Disable audible tones");
00240
00241 puts ("");
00242 puts ("Media Transport Options:");
00243 puts (" --use-ice Enable ICE (default:no)");
00244 puts (" --ice-no-host Disable ICE host candidates");
00245 puts (" --rtp-port=N Base port to try for RTP (default=4000)");
00246 puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
00247 puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
00248 puts (" --use-turn Enable TURN relay with ICE (default:no)");
00249 puts (" --turn-srv Domain or host name of TURN server (\"NAME:PORT\" format)");
00250 puts (" --turn-tcp Use TCP connection to TURN server (default no)");
00251 puts (" --turn-user TURN username");
00252 puts (" --turn-passwd TURN password");
00253
00254 puts ("");
00255 puts ("Buddy List (can be more than one):");
00256 puts (" --add-buddy url Add the specified URL to the buddy list.");
00257 puts ("");
00258 puts ("User Agent options:");
00259 puts (" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)");
00260 puts (" --max-calls=N Maximum number of concurrent calls (default:4, max:255)");
00261 puts (" --thread-cnt=N Number of worker threads (default:1)");
00262 puts (" --duration=SEC Set maximum call duration (default:no limit)");
00263 puts (" --norefersub Suppress event subscription when transfering calls");
00264 puts (" --use-compact-form Minimize SIP message size");
00265
00266 puts ("");
00267 puts ("When URL is specified, pjsua will immediately initiate call to that URL");
00268 puts ("");
00269
00270 fflush(stdout);
00271 }
00272
00273
00274
00275 static void default_config(struct app_config *cfg)
00276 {
00277 char tmp[80];
00278 unsigned i;
00279
00280 pjsua_config_default(&cfg->cfg);
00281 pj_ansi_sprintf(tmp, "PJSUA v%s/%s", pj_get_version(), PJ_OS_NAME);
00282 pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp);
00283
00284 pjsua_logging_config_default(&cfg->log_cfg);
00285 pjsua_media_config_default(&cfg->media_cfg);
00286 pjsua_transport_config_default(&cfg->udp_cfg);
00287 cfg->udp_cfg.port = 5060;
00288 pjsua_transport_config_default(&cfg->rtp_cfg);
00289 cfg->rtp_cfg.port = 4000;
00290 cfg->duration = NO_LIMIT;
00291 cfg->wav_id = PJSUA_INVALID_ID;
00292 cfg->rec_id = PJSUA_INVALID_ID;
00293 cfg->wav_port = PJSUA_INVALID_ID;
00294 cfg->rec_port = PJSUA_INVALID_ID;
00295 cfg->mic_level = cfg->speaker_level = 1.0;
00296 cfg->capture_dev = PJSUA_INVALID_ID;
00297 cfg->playback_dev = PJSUA_INVALID_ID;
00298 cfg->capture_lat = PJMEDIA_SND_DEFAULT_REC_LATENCY;
00299 cfg->playback_lat = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
00300 cfg->ringback_slot = PJSUA_INVALID_ID;
00301 cfg->ring_slot = PJSUA_INVALID_ID;
00302
00303 for (i=0; i<PJ_ARRAY_SIZE(cfg->acc_cfg); ++i)
00304 pjsua_acc_config_default(&cfg->acc_cfg[i]);
00305
00306 for (i=0; i<PJ_ARRAY_SIZE(cfg->buddy_cfg); ++i)
00307 pjsua_buddy_config_default(&cfg->buddy_cfg[i]);
00308 }
00309
00310
00311
00312
00313
00314 static int read_config_file(pj_pool_t *pool, const char *filename,
00315 int *app_argc, char ***app_argv)
00316 {
00317 int i;
00318 FILE *fhnd;
00319 char line[200];
00320 int argc = 0;
00321 char **argv;
00322 enum { MAX_ARGS = 64 };
00323
00324
00325 argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
00326 argv[argc++] = *app_argv[0];
00327
00328
00329 fhnd = fopen(filename, "rt");
00330 if (!fhnd) {
00331 PJ_LOG(1,(THIS_FILE, "Unable to open config file %s", filename));
00332 fflush(stdout);
00333 return -1;
00334 }
00335
00336
00337 while (argc < MAX_ARGS && !feof(fhnd)) {
00338 char *token;
00339 char *p;
00340 const char *whitespace = " \t\r\n";
00341 char cDelimiter;
00342 int len, token_len;
00343
00344 if (fgets(line, sizeof(line), fhnd) == NULL) break;
00345
00346
00347 len = strlen(line);
00348 if (line[len-1]=='\n')
00349 line[--len] = '\0';
00350 if (line[len-1]=='\r')
00351 line[--len] = '\0';
00352
00353 if (len==0) continue;
00354
00355 for (p = line; *p != '\0' && argc < MAX_ARGS; p++) {
00356
00357 while (*p != '\0' && strchr(whitespace, *p) != NULL) p++;
00358
00359 if (*p == '\0')
00360 break;
00361
00362 if (*p == '"' || *p == '\'') {
00363 cDelimiter = *p++;
00364 token = p;
00365
00366 while (*p != '\0' && *p != cDelimiter) p++;
00367
00368 if (*p == '\0')
00369 cDelimiter = '\0';
00370
00371 } else {
00372 token = p;
00373
00374 while (*p != '\0' && strchr(whitespace, *p) == NULL) p++;
00375
00376 cDelimiter = *p;
00377 }
00378
00379 *p = '\0';
00380 token_len = p-token;
00381
00382 if (token_len > 0) {
00383 if (*token == '#')
00384 break;
00385
00386 argv[argc] = pj_pool_alloc(pool, token_len + 1);
00387 pj_memcpy(argv[argc], token, token_len + 1);
00388 ++argc;
00389 }
00390
00391 *p = cDelimiter;
00392 }
00393 }
00394
00395
00396 for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
00397 argv[argc++] = (*app_argv)[i];
00398
00399 if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
00400 PJ_LOG(1,(THIS_FILE,
00401 "Too many arguments specified in cmd line/config file"));
00402 fflush(stdout);
00403 fclose(fhnd);
00404 return -1;
00405 }
00406
00407 fclose(fhnd);
00408
00409
00410 *app_argc = argc;
00411 *app_argv = argv;
00412 return 0;
00413
00414 }
00415
00416 static int my_atoi(const char *cs)
00417 {
00418 pj_str_t s;
00419
00420 pj_cstr(&s, cs);
00421 if (cs[0] == '-') {
00422 s.ptr++, s.slen--;
00423 return 0 - (int)pj_strtoul(&s);
00424 } else if (cs[0] == '+') {
00425 s.ptr++, s.slen--;
00426 return pj_strtoul(&s);
00427 } else {
00428 return pj_strtoul(&s);
00429 }
00430 }
00431
00432
00433
00434 static pj_status_t parse_args(int argc, char *argv[],
00435 struct app_config *cfg,
00436 pj_str_t *uri_to_call)
00437 {
00438 int c;
00439 int option_index;
00440 enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
00441 OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, OPT_SND_AUTO_CLOSE,
00442 OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY,
00443 OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
00444 OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
00445 OPT_NAMESERVER, OPT_STUN_DOMAIN, OPT_STUN_SRV,
00446 OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
00447 OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
00448 OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
00449 OPT_USE_ICE, OPT_USE_SRTP, OPT_SRTP_SECURE,
00450 OPT_USE_TURN,OPT_ICE_NO_HOST, OPT_TURN_SRV, OPT_TURN_TCP,
00451 OPT_TURN_USER, OPT_TURN_PASSWD,
00452 OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC,
00453 OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
00454 OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD,
00455 OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL,
00456 OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS,
00457 OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT,
00458 OPT_NOREFERSUB,
00459 OPT_USE_TLS, OPT_TLS_CA_FILE, OPT_TLS_CERT_FILE, OPT_TLS_PRIV_FILE,
00460 OPT_TLS_PASSWORD, OPT_TLS_VERIFY_SERVER, OPT_TLS_VERIFY_CLIENT,
00461 OPT_TLS_NEG_TIMEOUT,
00462 OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV,
00463 OPT_CAPTURE_LAT, OPT_PLAYBACK_LAT, OPT_NO_TONES,
00464 OPT_STDOUT_REFRESH, OPT_STDOUT_REFRESH_TEXT,
00465 OPT_AUTO_UPDATE_NAT,OPT_USE_COMPACT_FORM,OPT_DIS_CODEC
00466 };
00467 struct pj_getopt_option long_options[] = {
00468 { "config-file",1, 0, OPT_CONFIG_FILE},
00469 { "log-file", 1, 0, OPT_LOG_FILE},
00470 { "log-level", 1, 0, OPT_LOG_LEVEL},
00471 { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
00472 { "help", 0, 0, OPT_HELP},
00473 { "version", 0, 0, OPT_VERSION},
00474 { "clock-rate", 1, 0, OPT_CLOCK_RATE},
00475 { "snd-clock-rate", 1, 0, OPT_SND_CLOCK_RATE},
00476 { "stereo", 0, 0, OPT_STEREO},
00477 { "null-audio", 0, 0, OPT_NULL_AUDIO},
00478 { "local-port", 1, 0, OPT_LOCAL_PORT},
00479 { "ip-addr", 1, 0, OPT_IP_ADDR},
00480 { "no-tcp", 0, 0, OPT_NO_TCP},
00481 { "no-udp", 0, 0, OPT_NO_UDP},
00482 { "norefersub", 0, 0, OPT_NOREFERSUB},
00483 { "proxy", 1, 0, OPT_PROXY},
00484 { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
00485 { "registrar", 1, 0, OPT_REGISTRAR},
00486 { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
00487 { "publish", 0, 0, OPT_PUBLISH},
00488 { "use-100rel", 0, 0, OPT_100REL},
00489 { "use-ims", 0, 0, OPT_USE_IMS},
00490 { "id", 1, 0, OPT_ID},
00491 { "contact", 1, 0, OPT_CONTACT},
00492 { "auto-update-nat", 1, 0, OPT_AUTO_UPDATE_NAT},
00493 { "use-compact-form", 0, 0, OPT_USE_COMPACT_FORM},
00494 { "realm", 1, 0, OPT_REALM},
00495 { "username", 1, 0, OPT_USERNAME},
00496 { "password", 1, 0, OPT_PASSWORD},
00497 { "nameserver", 1, 0, OPT_NAMESERVER},
00498 { "stun-domain",1, 0, OPT_STUN_DOMAIN},
00499 { "stun-srv", 1, 0, OPT_STUN_SRV},
00500 { "add-buddy", 1, 0, OPT_ADD_BUDDY},
00501 { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
00502 { "no-presence", 0, 0, OPT_NO_PRESENCE},
00503 { "auto-answer",1, 0, OPT_AUTO_ANSWER},
00504 { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
00505 { "auto-play", 0, 0, OPT_AUTO_PLAY},
00506 { "auto-rec", 0, 0, OPT_AUTO_REC},
00507 { "auto-loop", 0, 0, OPT_AUTO_LOOP},
00508 { "auto-conf", 0, 0, OPT_AUTO_CONF},
00509 { "play-file", 1, 0, OPT_PLAY_FILE},
00510 { "play-tone", 1, 0, OPT_PLAY_TONE},
00511 { "rec-file", 1, 0, OPT_REC_FILE},
00512 { "rtp-port", 1, 0, OPT_RTP_PORT},
00513
00514 { "use-ice", 0, 0, OPT_USE_ICE},
00515 { "use-turn", 0, 0, OPT_USE_TURN},
00516 { "ice-no-host",0, 0, OPT_ICE_NO_HOST},
00517 { "turn-srv", 1, 0, OPT_TURN_SRV},
00518 { "turn-tcp", 0, 0, OPT_TURN_TCP},
00519 { "turn-user", 1, 0, OPT_TURN_USER},
00520 { "turn-passwd",1, 0, OPT_TURN_PASSWD},
00521
00522 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00523 { "use-srtp", 1, 0, OPT_USE_SRTP},
00524 { "srtp-secure",1, 0, OPT_SRTP_SECURE},
00525 #endif
00526 { "add-codec", 1, 0, OPT_ADD_CODEC},
00527 { "dis-codec", 1, 0, OPT_DIS_CODEC},
00528 { "complexity", 1, 0, OPT_COMPLEXITY},
00529 { "quality", 1, 0, OPT_QUALITY},
00530 { "ptime", 1, 0, OPT_PTIME},
00531 { "no-vad", 0, 0, OPT_NO_VAD},
00532 { "ec-tail", 1, 0, OPT_EC_TAIL},
00533 { "ilbc-mode", 1, 0, OPT_ILBC_MODE},
00534 { "rx-drop-pct",1, 0, OPT_RX_DROP_PCT},
00535 { "tx-drop-pct",1, 0, OPT_TX_DROP_PCT},
00536 { "next-account",0,0, OPT_NEXT_ACCOUNT},
00537 { "next-cred", 0, 0, OPT_NEXT_CRED},
00538 { "max-calls", 1, 0, OPT_MAX_CALLS},
00539 { "duration", 1, 0, OPT_DURATION},
00540 { "thread-cnt", 1, 0, OPT_THREAD_CNT},
00541 { "use-tls", 0, 0, OPT_USE_TLS},
00542 { "tls-ca-file",1, 0, OPT_TLS_CA_FILE},
00543 { "tls-cert-file",1,0, OPT_TLS_CERT_FILE},
00544 { "tls-privkey-file",1,0, OPT_TLS_PRIV_FILE},
00545 { "tls-password",1,0, OPT_TLS_PASSWORD},
00546 { "tls-verify-server", 0, 0, OPT_TLS_VERIFY_SERVER},
00547 { "tls-verify-client", 0, 0, OPT_TLS_VERIFY_CLIENT},
00548 { "tls-neg-timeout", 1, 0, OPT_TLS_NEG_TIMEOUT},
00549 { "capture-dev", 1, 0, OPT_CAPTURE_DEV},
00550 { "playback-dev", 1, 0, OPT_PLAYBACK_DEV},
00551 { "capture-lat", 1, 0, OPT_CAPTURE_LAT},
00552 { "playback-lat", 1, 0, OPT_PLAYBACK_LAT},
00553 { "stdout-refresh", 1, 0, OPT_STDOUT_REFRESH},
00554 { "stdout-refresh-text", 1, 0, OPT_STDOUT_REFRESH_TEXT},
00555 { "snd-auto-close", 1, 0, OPT_SND_AUTO_CLOSE},
00556 { "no-tones", 0, 0, OPT_NO_TONES},
00557 { NULL, 0, 0, 0}
00558 };
00559 pj_status_t status;
00560 pjsua_acc_config *cur_acc;
00561 char *config_file = NULL;
00562 unsigned i;
00563
00564
00565 pj_optind = 0;
00566 while ((c=pj_getopt_long(argc, argv, "", long_options,
00567 &option_index)) != -1)
00568 {
00569 switch (c) {
00570 case OPT_CONFIG_FILE:
00571 config_file = pj_optarg;
00572 break;
00573 }
00574 if (config_file)
00575 break;
00576 }
00577
00578 if (config_file) {
00579 status = read_config_file(app_config.pool, config_file, &argc, &argv);
00580 if (status != 0)
00581 return status;
00582 }
00583
00584 cfg->acc_cnt = 0;
00585 cur_acc = &cfg->acc_cfg[0];
00586
00587
00588
00589
00590
00591 pj_optind = 0;
00592 while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
00593 pj_str_t tmp;
00594 long lval;
00595
00596 switch (c) {
00597
00598 case OPT_CONFIG_FILE:
00599
00600 break;
00601
00602 case OPT_LOG_FILE:
00603 cfg->log_cfg.log_filename = pj_str(pj_optarg);
00604 break;
00605
00606 case OPT_LOG_LEVEL:
00607 c = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00608 if (c < 0 || c > 6) {
00609 PJ_LOG(1,(THIS_FILE,
00610 "Error: expecting integer value 0-6 "
00611 "for --log-level"));
00612 return PJ_EINVAL;
00613 }
00614 cfg->log_cfg.level = c;
00615 pj_log_set_level( c );
00616 break;
00617
00618 case OPT_APP_LOG_LEVEL:
00619 cfg->log_cfg.console_level = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00620 if (cfg->log_cfg.console_level < 0 || cfg->log_cfg.console_level > 6) {
00621 PJ_LOG(1,(THIS_FILE,
00622 "Error: expecting integer value 0-6 "
00623 "for --app-log-level"));
00624 return PJ_EINVAL;
00625 }
00626 break;
00627
00628 case OPT_HELP:
00629 usage();
00630 return PJ_EINVAL;
00631
00632 case OPT_VERSION:
00633 pj_dump_config();
00634 return PJ_EINVAL;
00635
00636 case OPT_NULL_AUDIO:
00637 cfg->null_audio = PJ_TRUE;
00638 break;
00639
00640 case OPT_CLOCK_RATE:
00641 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00642 if (lval < 8000 || lval > 192000) {
00643 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
00644 "8000-192000 for conference clock rate"));
00645 return PJ_EINVAL;
00646 }
00647 cfg->media_cfg.clock_rate = lval;
00648 break;
00649
00650 case OPT_SND_CLOCK_RATE:
00651 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00652 if (lval < 8000 || lval > 192000) {
00653 PJ_LOG(1,(THIS_FILE, "Error: expecting value between "
00654 "8000-192000 for sound device clock rate"));
00655 return PJ_EINVAL;
00656 }
00657 cfg->media_cfg.snd_clock_rate = lval;
00658 break;
00659
00660 case OPT_STEREO:
00661 cfg->media_cfg.channel_count = 2;
00662 break;
00663
00664 case OPT_LOCAL_PORT:
00665 lval = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00666 if (lval < 0 || lval > 65535) {
00667 PJ_LOG(1,(THIS_FILE,
00668 "Error: expecting integer value for "
00669 "--local-port"));
00670 return PJ_EINVAL;
00671 }
00672 cfg->udp_cfg.port = (pj_uint16_t)lval;
00673 break;
00674
00675 case OPT_IP_ADDR:
00676 cfg->udp_cfg.public_addr = pj_str(pj_optarg);
00677 cfg->rtp_cfg.public_addr = pj_str(pj_optarg);
00678 break;
00679
00680 case OPT_NO_UDP:
00681 if (cfg->no_tcp) {
00682 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
00683 return PJ_EINVAL;
00684 }
00685
00686 cfg->no_udp = PJ_TRUE;
00687 break;
00688
00689 case OPT_NOREFERSUB:
00690 cfg->no_refersub = PJ_TRUE;
00691 break;
00692
00693 case OPT_NO_TCP:
00694 if (cfg->no_udp) {
00695 PJ_LOG(1,(THIS_FILE,"Error: can not disable both TCP and UDP"));
00696 return PJ_EINVAL;
00697 }
00698
00699 cfg->no_tcp = PJ_TRUE;
00700 break;
00701
00702 case OPT_PROXY:
00703 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00704 PJ_LOG(1,(THIS_FILE,
00705 "Error: invalid SIP URL '%s' "
00706 "in proxy argument", pj_optarg));
00707 return PJ_EINVAL;
00708 }
00709 cur_acc->proxy[cur_acc->proxy_cnt++] = pj_str(pj_optarg);
00710 break;
00711
00712 case OPT_OUTBOUND_PROXY:
00713 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00714 PJ_LOG(1,(THIS_FILE,
00715 "Error: invalid SIP URL '%s' "
00716 "in outbound proxy argument", pj_optarg));
00717 return PJ_EINVAL;
00718 }
00719 cfg->cfg.outbound_proxy[cfg->cfg.outbound_proxy_cnt++] = pj_str(pj_optarg);
00720 break;
00721
00722 case OPT_REGISTRAR:
00723 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00724 PJ_LOG(1,(THIS_FILE,
00725 "Error: invalid SIP URL '%s' in "
00726 "registrar argument", pj_optarg));
00727 return PJ_EINVAL;
00728 }
00729 cur_acc->reg_uri = pj_str(pj_optarg);
00730 break;
00731
00732 case OPT_REG_TIMEOUT:
00733 cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,pj_optarg));
00734 if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) {
00735 PJ_LOG(1,(THIS_FILE,
00736 "Error: invalid value for --reg-timeout "
00737 "(expecting 1-3600)"));
00738 return PJ_EINVAL;
00739 }
00740 break;
00741
00742 case OPT_PUBLISH:
00743 cur_acc->publish_enabled = PJ_TRUE;
00744 break;
00745
00746 case OPT_100REL:
00747 cur_acc->require_100rel = PJ_TRUE;
00748 cfg->cfg.require_100rel = PJ_TRUE;
00749 break;
00750
00751 case OPT_USE_IMS:
00752 cur_acc->auth_pref.initial_auth = PJ_TRUE;
00753 break;
00754
00755 case OPT_ID:
00756 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00757 PJ_LOG(1,(THIS_FILE,
00758 "Error: invalid SIP URL '%s' "
00759 "in local id argument", pj_optarg));
00760 return PJ_EINVAL;
00761 }
00762 cur_acc->id = pj_str(pj_optarg);
00763 break;
00764
00765 case OPT_CONTACT:
00766 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00767 PJ_LOG(1,(THIS_FILE,
00768 "Error: invalid SIP URL '%s' "
00769 "in contact argument", pj_optarg));
00770 return PJ_EINVAL;
00771 }
00772 cur_acc->force_contact = pj_str(pj_optarg);
00773 break;
00774
00775 case OPT_AUTO_UPDATE_NAT:
00776 cur_acc->allow_contact_rewrite = pj_strtoul(pj_cstr(&tmp, pj_optarg));
00777 break;
00778
00779 case OPT_USE_COMPACT_FORM:
00780
00781 {
00782 extern pj_bool_t pjsip_use_compact_form;
00783 extern pj_bool_t pjsip_include_allow_hdr_in_dlg;
00784 extern pj_bool_t pjmedia_add_rtpmap_for_static_pt;
00785
00786 pjsip_use_compact_form = PJ_TRUE;
00787
00788 pjsip_include_allow_hdr_in_dlg = PJ_FALSE;
00789
00790 pjmedia_add_rtpmap_for_static_pt = PJ_FALSE;
00791 }
00792 break;
00793
00794 case OPT_NEXT_ACCOUNT:
00795 cfg->acc_cnt++;
00796 cur_acc = &cfg->acc_cfg[cfg->acc_cnt];
00797 break;
00798
00799 case OPT_USERNAME:
00800 cur_acc->cred_info[cur_acc->cred_count].username = pj_str(pj_optarg);
00801 cur_acc->cred_info[cur_acc->cred_count].scheme = pj_str("Digest");
00802 break;
00803
00804 case OPT_REALM:
00805 cur_acc->cred_info[cur_acc->cred_count].realm = pj_str(pj_optarg);
00806 break;
00807
00808 case OPT_PASSWORD:
00809 cur_acc->cred_info[cur_acc->cred_count].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
00810 cur_acc->cred_info[cur_acc->cred_count].data = pj_str(pj_optarg);
00811 #if PJSIP_HAS_DIGEST_AKA_AUTH
00812 cur_acc->cred_info[cur_acc->cred_count].data_type |= PJSIP_CRED_DATA_EXT_AKA;
00813 cur_acc->cred_info[cur_acc->cred_count].ext.aka.k = pj_str(pj_optarg);
00814 cur_acc->cred_info[cur_acc->cred_count].ext.aka.cb = &pjsip_auth_create_aka_response;
00815 #endif
00816 break;
00817
00818 case OPT_NEXT_CRED:
00819 cur_acc->cred_count++;
00820 break;
00821
00822 case OPT_NAMESERVER:
00823 cfg->cfg.nameserver[cfg->cfg.nameserver_count++] = pj_str(pj_optarg);
00824 if (cfg->cfg.nameserver_count > PJ_ARRAY_SIZE(cfg->cfg.nameserver)) {
00825 PJ_LOG(1,(THIS_FILE, "Error: too many nameservers"));
00826 return PJ_ETOOMANY;
00827 }
00828 break;
00829
00830 case OPT_STUN_DOMAIN:
00831 cfg->cfg.stun_domain = pj_str(pj_optarg);
00832 break;
00833
00834 case OPT_STUN_SRV:
00835 cfg->cfg.stun_host = pj_str(pj_optarg);
00836 break;
00837
00838 case OPT_ADD_BUDDY:
00839 if (pjsua_verify_sip_url(pj_optarg) != 0) {
00840 PJ_LOG(1,(THIS_FILE,
00841 "Error: invalid URL '%s' in "
00842 "--add-buddy option", pj_optarg));
00843 return -1;
00844 }
00845 if (cfg->buddy_cnt == PJ_ARRAY_SIZE(cfg->buddy_cfg)) {
00846 PJ_LOG(1,(THIS_FILE,
00847 "Error: too many buddies in buddy list."));
00848 return -1;
00849 }
00850 cfg->buddy_cfg[cfg->buddy_cnt].uri = pj_str(pj_optarg);
00851 cfg->buddy_cnt++;
00852 break;
00853
00854 case OPT_AUTO_PLAY:
00855 cfg->auto_play = 1;
00856 break;
00857
00858 case OPT_AUTO_REC:
00859 cfg->auto_rec = 1;
00860 break;
00861
00862 case OPT_AUTO_LOOP:
00863 cfg->auto_loop = 1;
00864 break;
00865
00866 case OPT_AUTO_CONF:
00867 cfg->auto_conf = 1;
00868 break;
00869
00870 case OPT_PLAY_FILE:
00871 cfg->wav_files[cfg->wav_count++] = pj_str(pj_optarg);
00872 break;
00873
00874 case OPT_PLAY_TONE:
00875 {
00876 int f1, f2, on, off;
00877 int n;
00878
00879 n = sscanf(pj_optarg, "%d,%d,%d,%d", &f1, &f2, &on, &off);
00880 if (n != 4) {
00881 puts("Expecting f1,f2,on,off in --play-tone");
00882 return -1;
00883 }
00884
00885 cfg->tones[cfg->tone_count].freq1 = (short)f1;
00886 cfg->tones[cfg->tone_count].freq2 = (short)f2;
00887 cfg->tones[cfg->tone_count].on_msec = (short)on;
00888 cfg->tones[cfg->tone_count].off_msec = (short)off;
00889 ++cfg->tone_count;
00890 }
00891 break;
00892
00893 case OPT_REC_FILE:
00894 cfg->rec_file = pj_str(pj_optarg);
00895 break;
00896
00897 case OPT_USE_ICE:
00898 cfg->media_cfg.enable_ice = PJ_TRUE;
00899 break;
00900
00901 case OPT_USE_TURN:
00902 cfg->media_cfg.enable_turn = PJ_TRUE;
00903 break;
00904
00905 case OPT_ICE_NO_HOST:
00906 cfg->media_cfg.ice_no_host_cands = PJ_TRUE;
00907 break;
00908
00909 case OPT_TURN_SRV:
00910 cfg->media_cfg.turn_server = pj_str(pj_optarg);
00911 break;
00912
00913 case OPT_TURN_TCP:
00914 cfg->media_cfg.turn_conn_type = PJ_TURN_TP_TCP;
00915 break;
00916
00917 case OPT_TURN_USER:
00918 cfg->media_cfg.turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
00919 cfg->media_cfg.turn_auth_cred.data.static_cred.realm = pj_str("*");
00920 cfg->media_cfg.turn_auth_cred.data.static_cred.username = pj_str(pj_optarg);
00921 break;
00922
00923 case OPT_TURN_PASSWD:
00924 cfg->media_cfg.turn_auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
00925 cfg->media_cfg.turn_auth_cred.data.static_cred.data = pj_str(pj_optarg);
00926 break;
00927
00928 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00929 case OPT_USE_SRTP:
00930 app_config.cfg.use_srtp = my_atoi(pj_optarg);
00931 if (!pj_isdigit(*pj_optarg) || app_config.cfg.use_srtp > 2) {
00932 PJ_LOG(1,(THIS_FILE, "Invalid value for --use-srtp option"));
00933 return -1;
00934 }
00935 cur_acc->use_srtp = app_config.cfg.use_srtp;
00936 break;
00937 case OPT_SRTP_SECURE:
00938 app_config.cfg.srtp_secure_signaling = my_atoi(pj_optarg);
00939 if (!pj_isdigit(*pj_optarg) ||
00940 app_config.cfg.srtp_secure_signaling > 2)
00941 {
00942 PJ_LOG(1,(THIS_FILE, "Invalid value for --srtp-secure option"));
00943 return -1;
00944 }
00945 cur_acc->srtp_secure_signaling = app_config.cfg.srtp_secure_signaling;
00946 break;
00947 #endif
00948
00949 case OPT_RTP_PORT:
00950 cfg->rtp_cfg.port = my_atoi(pj_optarg);
00951 if (cfg->rtp_cfg.port == 0) {
00952 enum { START_PORT=4000 };
00953 unsigned range;
00954
00955 range = (65535-START_PORT-PJSUA_MAX_CALLS*2);
00956 cfg->rtp_cfg.port = START_PORT +
00957 ((pj_rand() % range) & 0xFFFE);
00958 }
00959
00960 if (cfg->rtp_cfg.port < 1 || cfg->rtp_cfg.port > 65535) {
00961 |