?? pjsua_app.c
字號:
/* $Id: pjsua_app.c 1271 2007-05-14 16:37:47Z bennylp $ *//* * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <pjsua-lib/pjsua.h>#define THIS_FILE "pjsua.c"#define NO_LIMIT (int)0x7FFFFFFF//#define STEREO_DEMO/* Call specific data */struct call_data{ pj_timer_entry timer;};/* Pjsua application data */static struct app_config{ pjsua_config cfg; pjsua_logging_config log_cfg; pjsua_media_config media_cfg; pj_bool_t no_refersub; pj_bool_t no_tcp; pj_bool_t no_udp; pj_bool_t use_tls; pjsua_transport_config udp_cfg; pjsua_transport_config rtp_cfg; unsigned acc_cnt; pjsua_acc_config acc_cfg[PJSUA_MAX_ACC]; unsigned buddy_cnt; pjsua_buddy_config buddy_cfg[PJSUA_MAX_BUDDIES]; struct call_data call_data[PJSUA_MAX_CALLS]; pj_pool_t *pool; /* Compatibility with older pjsua */ unsigned codec_cnt; pj_str_t codec_arg[32]; pj_bool_t null_audio; unsigned wav_count; pj_str_t wav_files[32]; unsigned tone_count; pjmedia_tone_desc tones[32]; pjsua_conf_port_id tone_slots[32]; pjsua_player_id wav_id; pjsua_conf_port_id wav_port; pj_bool_t auto_play; pj_bool_t auto_loop; pj_bool_t auto_conf; pj_str_t rec_file; pj_bool_t auto_rec; pjsua_recorder_id rec_id; pjsua_conf_port_id rec_port; unsigned ptime; unsigned auto_answer; unsigned duration;#ifdef STEREO_DEMO pjmedia_snd_port *snd;#endif float mic_level, speaker_level; int capture_dev, playback_dev;} app_config;//static pjsua_acc_id current_acc;#define current_acc pjsua_acc_get_default()static pjsua_call_id current_call = PJSUA_INVALID_ID;static pj_str_t uri_arg;#ifdef STEREO_DEMOstatic void stereo_demo();#endifpj_status_t app_destroy(void);/***************************************************************************** * Configuration manipulation *//* Show usage */static void usage(void){ puts ("Usage:"); puts (" pjsua [options] [SIP URL to call]"); puts (""); puts ("General options:"); puts (" --config-file=file Read the config/arguments from file."); puts (" --help Display this help screen"); puts (" --version Display version info"); puts (""); puts ("Logging options:"); puts (" --log-file=fname Log to filename (default stderr)"); puts (" --log-level=N Set log max level to N (0(none) to 6(trace)) (default=5)"); puts (" --app-log-level=N Set log max level for stdout display (default=4)"); puts (""); puts ("SIP Account options:"); puts (" --registrar=url Set the URL of registrar server"); puts (" --id=url Set the URL of local ID (used in From header)"); puts (" --contact=url Optionally override the Contact information"); puts (" --proxy=url Optional URL of proxy server to visit"); puts (" May be specified multiple times"); puts (" --reg-timeout=SEC Optional registration interval (default 55)"); puts (" --realm=string Set realm"); puts (" --username=string Set authentication username"); puts (" --password=string Set authentication password"); puts (" --publish Send presence PUBLISH for this account"); puts (" --next-cred Add another credentials"); puts (""); puts ("SIP Account Control:"); puts (" --next-account Add more account"); puts (""); puts ("Transport Options:"); puts (" --local-port=port Set TCP/UDP port. This implicitly enables both "); puts (" TCP and UDP transports on the specified port, unless"); puts (" if TCP or UDP is disabled."); puts (" --ip-addr=IP Use the specifed address as SIP and RTP addresses."); puts (" (Hint: the IP may be the public IP of the NAT/router)"); puts (" --no-tcp Disable TCP transport."); puts (" --no-udp Disable UDP transport."); puts (" --nameserver=NS Add the specified nameserver to enable SRV resolution"); puts (" This option can be specified multiple times."); puts (" --outbound=url Set the URL of global outbound proxy server"); puts (" May be specified multiple times"); puts (" --use-stun1=FORMAT where FORMAT=host[:port]"); puts (" --use-stun2=FORMAT Resolve local IP with the specified STUN servers"); puts (""); puts ("TLS Options:"); puts (" --use-tls Enable TLS transport (default=no)"); puts (" --tls-ca-file Specify TLS CA file (default=none)"); puts (" --tls-cert-file Specify TLS certificate file (default=none)"); puts (" --tls-privkey-file Specify TLS private key file (default=none)"); puts (" --tls-password Specify TLS password to private key file (default=none)"); puts (" --tls-verify-server Verify server's certificate (default=no)"); puts (" --tls-verify-client Verify client's certificate (default=no)"); puts (" --tls-neg-timeout Specify TLS negotiation timeout (default=no)"); puts (""); puts ("Media Options:"); puts (" --add-codec=name Manually add codec (default is to enable all)"); puts (" --clock-rate=N Override sound device clock rate"); puts (" --null-audio Use NULL audio device"); puts (" --play-file=file Register WAV file in conference bridge."); puts (" This can be specified multiple times."); puts (" --play-tone=FORMAT Register tone to the conference bridge."); puts (" FORMAT is 'F1,F2,ON,OFF', where F1,F2 are"); puts (" frequencies, and ON,OFF=on/off duration in msec."); puts (" This can be specified multiple times."); puts (" --auto-play Automatically play the file (to incoming calls only)"); puts (" --auto-loop Automatically loop incoming RTP to outgoing RTP"); puts (" --auto-conf Automatically put calls in conference with others"); puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3"); puts (" --auto-rec Automatically record conversation"); puts (" --rtp-port=N Base port to try for RTP (default=4000)"); puts (" --quality=N Specify media quality (0-10, default=6)"); puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)"); puts (" --no-vad Disable VAD/silence detector (default=vad enabled)"); puts (" --ec-tail=MSEC Set echo canceller tail length (default=256)"); puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is 20)"); puts (" --rx-drop-pct=PCT Drop PCT percent of RX RTP (for pkt lost sim, default: 0)"); puts (" --tx-drop-pct=PCT Drop PCT percent of TX RTP (for pkt lost sim, default: 0)"); puts (" --capture-dev=id Audio capture device ID (default=-1)"); puts (" --playback-dev=id Audio playback device ID (default=-1)"); puts (""); puts ("Buddy List (can be more than one):"); puts (" --add-buddy url Add the specified URL to the buddy list."); puts (""); puts ("User Agent options:"); puts (" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)"); puts (" --max-calls=N Maximum number of concurrent calls (default:4, max:255)"); puts (" --thread-cnt=N Number of worker threads (default:1)"); puts (" --duration=SEC Set maximum call duration (default:no limit)"); puts (" --norefersub Suppress event subscription when transfering calls"); puts (""); puts ("When URL is specified, pjsua will immediately initiate call to that URL"); puts (""); fflush(stdout);}/* Set default config. */static void default_config(struct app_config *cfg){ char tmp[80]; unsigned i; pjsua_config_default(&cfg->cfg); pj_ansi_sprintf(tmp, "PJSUA v%s/%s", PJ_VERSION, PJ_OS_NAME); pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp); pjsua_logging_config_default(&cfg->log_cfg); pjsua_media_config_default(&cfg->media_cfg); pjsua_transport_config_default(&cfg->udp_cfg); cfg->udp_cfg.port = 5060; pjsua_transport_config_default(&cfg->rtp_cfg); cfg->rtp_cfg.port = 4000; cfg->duration = NO_LIMIT; cfg->wav_id = PJSUA_INVALID_ID; cfg->rec_id = PJSUA_INVALID_ID; cfg->wav_port = PJSUA_INVALID_ID; cfg->rec_port = PJSUA_INVALID_ID; cfg->mic_level = cfg->speaker_level = 1.0; cfg->capture_dev = PJSUA_INVALID_ID; cfg->playback_dev = PJSUA_INVALID_ID; for (i=0; i<PJ_ARRAY_SIZE(cfg->acc_cfg); ++i) pjsua_acc_config_default(&cfg->acc_cfg[i]); for (i=0; i<PJ_ARRAY_SIZE(cfg->buddy_cfg); ++i) pjsua_buddy_config_default(&cfg->buddy_cfg[i]);}/* * Read command arguments from config file. */static int read_config_file(pj_pool_t *pool, const char *filename, int *app_argc, char ***app_argv){ int i; FILE *fhnd; char line[200]; int argc = 0; char **argv; enum { MAX_ARGS = 64 }; /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */ argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*)); argv[argc++] = *app_argv[0]; /* Open config file. */ fhnd = fopen(filename, "rt"); if (!fhnd) { PJ_LOG(1,(THIS_FILE, "Unable to open config file %s", filename)); fflush(stdout); return -1; } /* Scan tokens in the file. */ while (argc < MAX_ARGS && !feof(fhnd)) { char *token, *p = line; if (fgets(line, sizeof(line), fhnd) == NULL) break; for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS; token = strtok(NULL, " \t\r\n")) { int token_len; if (!token) break; if (*token == '#') break; token_len = strlen(token); if (!token_len) continue; argv[argc] = pj_pool_alloc(pool, token_len+1); pj_memcpy(argv[argc], token, token_len+1); ++argc; } } /* Copy arguments from command line */ for (i=1; i<*app_argc && argc < MAX_ARGS; ++i) argv[argc++] = (*app_argv)[i]; if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) { PJ_LOG(1,(THIS_FILE, "Too many arguments specified in cmd line/config file")); fflush(stdout); fclose(fhnd); return -1; } fclose(fhnd); /* Assign the new command line back to the original command line. */ *app_argc = argc; *app_argv = argv; return 0;}static int my_atoi(const char *cs){ pj_str_t s; return pj_strtoul(pj_cstr(&s, cs));}/* Parse arguments. */static pj_status_t parse_args(int argc, char *argv[], struct app_config *cfg, pj_str_t *uri_to_call){ int c; int option_index; enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, OPT_LOCAL_PORT, OPT_IP_ADDR, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT, OPT_REALM, OPT_USERNAME, OPT_PASSWORD, OPT_NAMESERVER, OPT_USE_STUN1, OPT_USE_STUN2, OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE, OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP, OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC, OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC, OPT_COMPLEXITY, OPT_QUALITY, OPT_PTIME, OPT_NO_VAD, OPT_RX_DROP_PCT, OPT_TX_DROP_PCT, OPT_EC_TAIL, OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS, OPT_DURATION, OPT_NO_TCP, OPT_NO_UDP, OPT_THREAD_CNT, OPT_NOREFERSUB, OPT_USE_TLS, OPT_TLS_CA_FILE, OPT_TLS_CERT_FILE, OPT_TLS_PRIV_FILE, OPT_TLS_PASSWORD, OPT_TLS_VERIFY_SERVER, OPT_TLS_VERIFY_CLIENT, OPT_TLS_NEG_TIMEOUT, OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV, }; struct pj_getopt_option long_options[] = { { "config-file",1, 0, OPT_CONFIG_FILE}, { "log-file", 1, 0, OPT_LOG_FILE}, { "log-level", 1, 0, OPT_LOG_LEVEL}, { "app-log-level",1,0,OPT_APP_LOG_LEVEL}, { "help", 0, 0, OPT_HELP}, { "version", 0, 0, OPT_VERSION}, { "clock-rate", 1, 0, OPT_CLOCK_RATE}, { "null-audio", 0, 0, OPT_NULL_AUDIO}, { "local-port", 1, 0, OPT_LOCAL_PORT}, { "ip-addr", 1, 0, OPT_IP_ADDR}, { "no-tcp", 0, 0, OPT_NO_TCP}, { "no-udp", 0, 0, OPT_NO_UDP}, { "norefersub", 0, 0, OPT_NOREFERSUB}, { "proxy", 1, 0, OPT_PROXY}, { "outbound", 1, 0, OPT_OUTBOUND_PROXY}, { "registrar", 1, 0, OPT_REGISTRAR}, { "reg-timeout",1, 0, OPT_REG_TIMEOUT}, { "publish", 0, 0, OPT_PUBLISH}, { "id", 1, 0, OPT_ID}, { "contact", 1, 0, OPT_CONTACT}, { "realm", 1, 0, OPT_REALM}, { "username", 1, 0, OPT_USERNAME}, { "password", 1, 0, OPT_PASSWORD}, { "nameserver", 1, 0, OPT_NAMESERVER}, { "use-stun1", 1, 0, OPT_USE_STUN1}, { "use-stun2", 1, 0, OPT_USE_STUN2}, { "add-buddy", 1, 0, OPT_ADD_BUDDY}, { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG}, { "no-presence", 0, 0, OPT_NO_PRESENCE}, { "auto-answer",1, 0, OPT_AUTO_ANSWER}, { "auto-hangup",1, 0, OPT_AUTO_HANGUP}, { "auto-play", 0, 0, OPT_AUTO_PLAY}, { "auto-rec", 0, 0, OPT_AUTO_REC}, { "auto-loop", 0, 0, OPT_AUTO_LOOP}, { "auto-conf", 0, 0, OPT_AUTO_CONF}, { "play-file", 1, 0, OPT_PLAY_FILE}, { "play-tone", 1, 0, OPT_PLAY_TONE}, { "rec-file", 1, 0, OPT_REC_FILE}, { "rtp-port", 1, 0, OPT_RTP_PORT}, { "add-codec", 1, 0, OPT_ADD_CODEC}, { "complexity", 1, 0, OPT_COMPLEXITY}, { "quality", 1, 0, OPT_QUALITY}, { "ptime", 1, 0, OPT_PTIME}, { "no-vad", 0, 0, OPT_NO_VAD}, { "ec-tail", 1, 0, OPT_EC_TAIL}, { "ilbc-mode", 1, 0, OPT_ILBC_MODE}, { "rx-drop-pct",1, 0, OPT_RX_DROP_PCT}, { "tx-drop-pct",1, 0, OPT_TX_DROP_PCT}, { "next-account",0,0, OPT_NEXT_ACCOUNT}, { "next-cred", 0, 0, OPT_NEXT_CRED}, { "max-calls", 1, 0, OPT_MAX_CALLS}, { "duration", 1, 0, OPT_DURATION}, { "thread-cnt", 1, 0, OPT_THREAD_CNT}, { "use-tls", 0, 0, OPT_USE_TLS}, { "tls-ca-file",1, 0, OPT_TLS_CA_FILE}, { "tls-cert-file",1,0, OPT_TLS_CERT_FILE}, { "tls-privkey-file",1,0, OPT_TLS_PRIV_FILE}, { "tls-password",1,0, OPT_TLS_PASSWORD}, { "tls-verify-server", 0, 0, OPT_TLS_VERIFY_SERVER}, { "tls-verify-client", 0, 0, OPT_TLS_VERIFY_CLIENT}, { "tls-neg-timeout", 1, 0, OPT_TLS_NEG_TIMEOUT}, { "capture-dev", 1, 0, OPT_CAPTURE_DEV}, { "playback-dev", 1, 0, OPT_PLAYBACK_DEV}, { NULL, 0, 0, 0} }; pj_status_t status; pjsua_acc_config *cur_acc; char *config_file = NULL; unsigned i; /* Run pj_getopt once to see if user specifies config file to read. */ pj_optind = 0; while ((c=pj_getopt_long(argc, argv, "", long_options, &option_index)) != -1) { switch (c) { case OPT_CONFIG_FILE: config_file = pj_optarg; break; } if (config_file) break; } if (config_file) { status = read_config_file(app_config.pool, config_file, &argc, &argv); if (status != 0) return status; } cfg->acc_cnt = 0; cur_acc = &cfg->acc_cfg[0]; /* Reinitialize and re-run pj_getopt again, possibly with new arguments * read from config file. */ pj_optind = 0; while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) { char *p; pj_str_t tmp; long lval; switch (c) { case OPT_CONFIG_FILE: /* Ignore as this has been processed before */ break; case OPT_LOG_FILE: cfg->log_cfg.log_filename = pj_str(pj_optarg); break; case OPT_LOG_LEVEL: c = pj_strtoul(pj_cstr(&tmp, pj_optarg)); if (c < 0 || c > 6) { PJ_LOG(1,(THIS_FILE, "Error: expecting integer value 0-6 " "for --log-level")); return PJ_EINVAL; } cfg->log_cfg.level = c; pj_log_set_level( c ); break; case OPT_APP_LOG_LEVEL: cfg->log_cfg.console_level = pj_strtoul(pj_cstr(&tmp, pj_optarg)); if (cfg->log_cfg.console_level < 0 || cfg->log_cfg.console_level > 6) { PJ_LOG(1,(THIS_FILE, "Error: expecting integer value 0-6 " "for --app-log-level")); return PJ_EINVAL; } break; case OPT_HELP:
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -