pjsip logo pjsip.org
Open source SIP stack and media stack for presence, im/instant messaging, and multimedia communication

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

PJSUA

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.jpg

pjsua on WinXP

00001 /* $Id: pjsua_app.c 2024 2008-06-14 22:43:56Z bennylp $ */
00002 /* 
00003  * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
00018  */
00019 #include <pjsua-lib/pjsua.h>
00020 
00021 
00022 #define THIS_FILE       "pjsua_app.c"
00023 #define NO_LIMIT        (int)0x7FFFFFFF
00024 
00025 //#define STEREO_DEMO
00026 
00027 /* Ringtones                US         UK  */
00028 #define RINGBACK_FREQ1      440     /* 400 */
00029 #define RINGBACK_FREQ2      480     /* 450 */
00030 #define RINGBACK_ON         2000    /* 400 */
00031 #define RINGBACK_OFF        4000    /* 200 */
00032 #define RINGBACK_CNT        1       /* 2   */
00033 #define RINGBACK_INTERVAL   4000    /* 2000 */
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 /* Call specific data */
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 /* Pjsua application data */
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     /* Compatibility with older pjsua */
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 //static pjsua_acc_id   current_acc;
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  * Configuration manipulation
00142  */
00143 
00144 /* Show usage */
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 /* Set default config. */
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  * Read command arguments from config file.
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     /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
00325     argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
00326     argv[argc++] = *app_argv[0];
00327 
00328     /* Open config file. */
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     /* Scan tokens in the file. */
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         // Trim ending newlines
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             // first, scan whitespaces
00357             while (*p != '\0' && strchr(whitespace, *p) != NULL) p++;
00358 
00359             if (*p == '\0')                 // are we done yet?
00360                 break;
00361             
00362             if (*p == '"' || *p == '\'') {    // is token a quoted string
00363                 cDelimiter = *p++;          // save quote delimiter
00364                 token = p;
00365                 
00366                 while (*p != '\0' && *p != cDelimiter) p++;
00367                 
00368                 if (*p == '\0')         // found end of the line, but,
00369                     cDelimiter = '\0';  // didn't find a matching quote
00370 
00371             } else {                    // token's not a quoted string
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;  // ignore remainder of line
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     /* Copy arguments from command line */
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     /* Assign the new command line back to the original command line. */
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 /* Parse arguments. */
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     /* Run pj_getopt once to see if user specifies config file to read. */ 
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     /* Reinitialize and re-run pj_getopt again, possibly with new arguments
00589      * read from config file.
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             /* Ignore as this has been processed before */
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:   /* 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:   /* 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: /* 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: /* 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: /* norefersub */
00690             cfg->no_refersub = PJ_TRUE;
00691             break;
00692 
00693         case OPT_NO_TCP: /* 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:   /* 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:   /* 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:   /* 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:   /* 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:   /* 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: /* Activate IMS settings */
00752             cur_acc->auth_pref.initial_auth = PJ_TRUE;
00753             break;
00754 
00755         case OPT_ID:   /* 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:   /* 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:   /* 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             /* enable compact form - from Ticket #342 */
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                 /* do not transmit Allow header */
00788                 pjsip_include_allow_hdr_in_dlg = PJ_FALSE;
00789                 /* Do not include rtpmap for static payload types (<96) */
00790                 pjmedia_add_rtpmap_for_static_pt = PJ_FALSE;
00791             }
00792             break;
00793 
00794         case OPT_NEXT_ACCOUNT: /* Add more account. */
00795             cfg->acc_cnt++;
00796             cur_acc = &cfg->acc_cfg[cfg->acc_cnt];
00797             break;
00798 
00799         case OPT_USERNAME:   /* Default authentication user */
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:     /* Default authentication realm. */
00805             cur_acc->cred_info[cur_acc->cred_count].realm = pj_str(pj_optarg);
00806             break;
00807 
00808         case OPT_PASSWORD:   /* authentication 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: /* next credential */
00819             cur_acc->cred_count++;
00820             break;
00821 
00822         case OPT_NAMESERVER: /* 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:   /* STUN domain */
00831             cfg->cfg.stun_domain = pj_str(pj_optarg);
00832             break;
00833 
00834         case OPT_STUN_SRV:   /* STUN server */
00835             cfg->cfg.stun_host = pj_str(pj_optarg);
00836             break;
00837 
00838         case OPT_ADD_BUDDY: /* Add to buddy list. */
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