Ignore:
Timestamp:
Mar 14, 2013 7:18:13 AM (6 years ago)
Author:
riza
Message:

Re #1643: add initial support for CLI

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c

    r4439 r4440  
    1818 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
    1919 */ 
    20 #include <pjsua-lib/pjsua.h> 
     20#include "pjsua_cmd.h" 
    2121#include "gui.h" 
    2222 
    23  
    2423#define THIS_FILE       "pjsua_app.c" 
    25 #define NO_LIMIT        (int)0x7FFFFFFF 
    2624 
    2725//#define STEREO_DEMO 
     
    4442#define RING_INTERVAL       3000 
    4543 
    46 #define MAX_AVI             4 
    47  
    48 /* Call specific data */ 
    49 struct call_data 
    50 { 
    51     pj_timer_entry          timer; 
    52     pj_bool_t               ringback_on; 
    53     pj_bool_t               ring_on; 
    54 }; 
    55  
    56 /* Video settings */ 
    57 struct app_vid 
    58 { 
    59     unsigned                vid_cnt; 
    60     int                     vcapture_dev; 
    61     int                     vrender_dev; 
    62     pj_bool_t               in_auto_show; 
    63     pj_bool_t               out_auto_transmit; 
    64 }; 
    65  
    66 /* Pjsua application data */ 
    67 static struct app_config 
    68 { 
    69     pjsua_config            cfg; 
    70     pjsua_logging_config    log_cfg; 
    71     pjsua_media_config      media_cfg; 
    72     pj_bool_t               no_refersub; 
    73     pj_bool_t               ipv6; 
    74     pj_bool_t               enable_qos; 
    75     pj_bool_t               no_tcp; 
    76     pj_bool_t               no_udp; 
    77     pj_bool_t               use_tls; 
    78     pjsua_transport_config  udp_cfg; 
    79     pjsua_transport_config  rtp_cfg; 
    80     pjsip_redirect_op       redir_op; 
    81  
    82     unsigned                acc_cnt; 
    83     pjsua_acc_config        acc_cfg[PJSUA_MAX_ACC]; 
    84  
    85     unsigned                buddy_cnt; 
    86     pjsua_buddy_config      buddy_cfg[PJSUA_MAX_BUDDIES]; 
    87  
    88     struct call_data        call_data[PJSUA_MAX_CALLS]; 
    89  
    90     pj_pool_t              *pool; 
    91     /* Compatibility with older pjsua */ 
    92  
    93     unsigned                codec_cnt; 
    94     pj_str_t                codec_arg[32]; 
    95     unsigned                codec_dis_cnt; 
    96     pj_str_t                codec_dis[32]; 
    97     pj_bool_t               null_audio; 
    98     unsigned                wav_count; 
    99     pj_str_t                wav_files[32]; 
    100     unsigned                tone_count; 
    101     pjmedia_tone_desc       tones[32]; 
    102     pjsua_conf_port_id      tone_slots[32]; 
    103     pjsua_player_id         wav_id; 
    104     pjsua_conf_port_id      wav_port; 
    105     pj_bool_t               auto_play; 
    106     pj_bool_t               auto_play_hangup; 
    107     pj_timer_entry          auto_hangup_timer; 
    108     pj_bool_t               auto_loop; 
    109     pj_bool_t               auto_conf; 
    110     pj_str_t                rec_file; 
    111     pj_bool_t               auto_rec; 
    112     pjsua_recorder_id       rec_id; 
    113     pjsua_conf_port_id      rec_port; 
    114     unsigned                auto_answer; 
    115     unsigned                duration; 
    116  
    117 #ifdef STEREO_DEMO 
    118     pjmedia_snd_port       *snd; 
    119     pjmedia_port           *sc, *sc_ch1; 
    120     pjsua_conf_port_id      sc_ch1_slot; 
    121 #endif 
    122  
    123     float                   mic_level, 
    124                             speaker_level; 
    125  
    126     int                     capture_dev, playback_dev; 
    127     unsigned                capture_lat, playback_lat; 
    128  
    129     pj_bool_t               no_tones; 
    130     int                     ringback_slot; 
    131     int                     ringback_cnt; 
    132     pjmedia_port           *ringback_port; 
    133     int                     ring_slot; 
    134     int                     ring_cnt; 
    135     pjmedia_port           *ring_port; 
    136  
    137     struct app_vid          vid; 
    138     unsigned                aud_cnt; 
    139  
    140     /* AVI to play */ 
    141     unsigned                avi_cnt; 
    142     struct { 
    143         pj_str_t                path; 
    144         pjmedia_vid_dev_index   dev_id; 
    145         pjsua_conf_port_id      slot; 
    146     } avi[MAX_AVI]; 
    147     pj_bool_t               avi_auto_play; 
    148     int                     avi_def_idx; 
    149  
    150 } app_config; 
    151  
    152  
    153 //static pjsua_acc_id   current_acc; 
    15444#define current_acc     pjsua_acc_get_default() 
    155 static pjsua_call_id    current_call = PJSUA_INVALID_ID; 
    156 static pj_bool_t        cmd_echo; 
    157 static int              stdout_refresh = -1; 
    158 static const char      *stdout_refresh_text = "STDOUT_REFRESH"; 
    159 static pj_bool_t        stdout_refresh_quit = PJ_FALSE; 
     45 
    16046static pj_str_t         uri_arg; 
    161  
    162 #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) 
    163 #   define SOME_BUF_SIZE        (1024 * 10) 
    164 #else 
    165 #   define SOME_BUF_SIZE        (1024 * 3) 
    166 #endif 
    167  
    168 static char some_buf[SOME_BUF_SIZE]; 
    16947 
    17048#ifdef STEREO_DEMO 
    17149static void stereo_demo(); 
    17250#endif 
     51 
    17352pj_status_t app_destroy(void); 
    174  
    17553static void ringback_start(pjsua_call_id call_id); 
    17654static void ring_start(pjsua_call_id call_id); 
     
    17957pj_bool_t       app_restart; 
    18058pj_log_func     *log_cb = NULL; 
     59static const char      *stdout_refresh_text = "STDOUT_REFRESH"; 
     60 
     61/** Forward declaration **/ 
     62void console_app_main(const pj_str_t *uri_to_call, pj_bool_t *app_restart); 
     63 
     64void cli_console_app_main(const pj_str_t *uri_to_call, pj_bool_t *app_restart); 
     65void app_config_init_video(pjsua_acc_config *acc_cfg); 
     66pj_status_t setup_cli(); 
     67void destroy_cli(); 
    18168 
    18269/***************************************************************************** 
     
    382269 
    383270    puts  (""); 
     271    puts  ("CLI options:"); 
     272    puts  ("  --use-cli           Use CLI as user interface"); 
     273    puts  ("  --cli-telnet-port=N CLI telnet port"); 
     274    puts  (""); 
     275 
     276    puts  (""); 
    384277    puts  ("When URL is specified, pjsua will immediately initiate call to that URL"); 
    385278    puts  (""); 
     
    388281} 
    389282 
     283static int stdout_refresh_proc(void *arg) 
     284{ 
     285    PJ_UNUSED_ARG(arg); 
     286 
     287    /* Set thread to lowest priority so that it doesn't clobber 
     288     * stdout output 
     289     */ 
     290    pj_thread_set_prio(pj_thread_this(),  
     291                       pj_thread_get_prio_min(pj_thread_this())); 
     292 
     293    while (!stdout_refresh_quit) { 
     294        pj_thread_sleep(stdout_refresh * 1000); 
     295        puts(stdout_refresh_text); 
     296        fflush(stdout); 
     297    } 
     298 
     299    return 0; 
     300} 
    390301 
    391302/* Set default config. */ 
    392 static void default_config(struct app_config *cfg) 
     303static void default_config(pjsua_app_config *cfg) 
    393304{ 
    394305    char tmp[80]; 
     
    407318    cfg->rtp_cfg.port = 4000; 
    408319    cfg->redir_op = PJSIP_REDIRECT_ACCEPT_REPLACE; 
    409     cfg->duration = NO_LIMIT; 
     320    cfg->duration = NO_LIMIT_DURATION; 
    410321    cfg->wav_id = PJSUA_INVALID_ID; 
    411322    cfg->rec_id = PJSUA_INVALID_ID; 
     
    431342 
    432343    cfg->avi_def_idx = PJSUA_INVALID_ID; 
    433 } 
    434  
     344 
     345    cfg->use_cli = PJ_FALSE; 
     346    cfg->cli_telnet_port = 0; 
     347} 
    435348 
    436349/* 
     
    467380        int   len, token_len; 
    468381         
     382        pj_bzero(line, sizeof(line)); 
    469383        if (fgets(line, sizeof(line), fhnd) == NULL) break; 
    470384         
     
    536450    *app_argv = argv; 
    537451    return 0; 
    538  
    539 } 
    540  
    541 static int my_atoi(const char *cs) 
    542 { 
    543     pj_str_t s; 
    544  
    545     pj_cstr(&s, cs); 
    546     if (cs[0] == '-') { 
    547         s.ptr++, s.slen--; 
    548         return 0 - (int)pj_strtoul(&s); 
    549     } else if (cs[0] == '+') { 
    550         s.ptr++, s.slen--; 
    551         return pj_strtoul(&s); 
    552     } else { 
    553         return pj_strtoul(&s); 
    554     } 
    555 } 
    556  
     452} 
    557453 
    558454/* Parse arguments. */ 
    559 static pj_status_t parse_args(int argc, char *argv[], 
    560                               struct app_config *cfg, 
     455static pj_status_t parse_args(int argc, char *argv[],  
     456                              pjsua_app_config *cfg, 
    561457                              pj_str_t *uri_to_call) 
    562458{ 
     
    598494           OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE, 
    599495           OPT_VIDEO, OPT_EXTRA_AUDIO, 
    600            OPT_VCAPTURE_DEV, OPT_VRENDER_DEV, OPT_PLAY_AVI, OPT_AUTO_PLAY_AVI 
     496           OPT_VCAPTURE_DEV, OPT_VRENDER_DEV, OPT_PLAY_AVI, OPT_AUTO_PLAY_AVI, 
     497           OPT_USE_CLI, OPT_CLI_TELNET_PORT 
    601498    }; 
    602499    struct pj_getopt_option long_options[] = { 
     
    727624        { "play-avi",   1, 0, OPT_PLAY_AVI}, 
    728625        { "auto-play-avi", 0, 0, OPT_AUTO_PLAY_AVI}, 
     626        { "use-cli",    0, 0, OPT_USE_CLI}, 
     627        { "cli-telnet-port", 1, 0, OPT_CLI_TELNET_PORT}, 
    729628        { NULL, 0, 0, 0} 
    730629    }; 
     
    749648 
    750649    if (config_file) { 
    751         status = read_config_file(app_config.pool, config_file, &argc, &argv); 
     650        status = read_config_file(cfg->pool, config_file, &argc, &argv); 
    752651        if (status != 0) 
    753652            return status; 
     
    15491448        case OPT_AUTO_PLAY_AVI: 
    15501449            app_config.avi_auto_play = PJ_TRUE; 
     1450            break; 
     1451 
     1452        case OPT_USE_CLI: 
     1453            cfg->use_cli = PJ_TRUE; 
     1454            break; 
     1455 
     1456        case OPT_CLI_TELNET_PORT: 
     1457            cfg->cli_telnet_port = atoi(pj_optarg); 
    15511458            break; 
    15521459 
     
    16311538        } 
    16321539    } 
    1633  
    1634  
    16351540    return PJ_SUCCESS; 
    1636 } 
    1637  
    1638  
    1639 /* 
    1640  * Save account settings 
    1641  */ 
    1642 static void write_account_settings(int acc_index, pj_str_t *result) 
    1643 { 
    1644     unsigned i; 
    1645     char line[128]; 
    1646     pjsua_acc_config *acc_cfg = &app_config.acc_cfg[acc_index]; 
    1647  
    1648      
    1649     pj_ansi_sprintf(line, "\n#\n# Account %d:\n#\n", acc_index); 
    1650     pj_strcat2(result, line); 
    1651  
    1652  
    1653     /* Identity */ 
    1654     if (acc_cfg->id.slen) { 
    1655         pj_ansi_sprintf(line, "--id %.*s\n",  
    1656                         (int)acc_cfg->id.slen,  
    1657                         acc_cfg->id.ptr); 
    1658         pj_strcat2(result, line); 
    1659     } 
    1660  
    1661     /* Registrar server */ 
    1662     if (acc_cfg->reg_uri.slen) { 
    1663         pj_ansi_sprintf(line, "--registrar %.*s\n", 
    1664                               (int)acc_cfg->reg_uri.slen, 
    1665                               acc_cfg->reg_uri.ptr); 
    1666         pj_strcat2(result, line); 
    1667  
    1668         pj_ansi_sprintf(line, "--reg-timeout %u\n", 
    1669                               acc_cfg->reg_timeout); 
    1670         pj_strcat2(result, line); 
    1671     } 
    1672  
    1673     /* Contact */ 
    1674     if (acc_cfg->force_contact.slen) { 
    1675         pj_ansi_sprintf(line, "--contact %.*s\n",  
    1676                         (int)acc_cfg->force_contact.slen,  
    1677                         acc_cfg->force_contact.ptr); 
    1678         pj_strcat2(result, line); 
    1679     } 
    1680  
    1681     /* Contact header parameters */ 
    1682     if (acc_cfg->contact_params.slen) { 
    1683         pj_ansi_sprintf(line, "--contact-params %.*s\n",  
    1684                         (int)acc_cfg->contact_params.slen,  
    1685                         acc_cfg->contact_params.ptr); 
    1686         pj_strcat2(result, line); 
    1687     } 
    1688  
    1689     /* Contact URI parameters */ 
    1690     if (acc_cfg->contact_uri_params.slen) { 
    1691         pj_ansi_sprintf(line, "--contact-uri-params %.*s\n",  
    1692                         (int)acc_cfg->contact_uri_params.slen,  
    1693                         acc_cfg->contact_uri_params.ptr); 
    1694         pj_strcat2(result, line); 
    1695     } 
    1696  
    1697     /*  */ 
    1698     if (acc_cfg->allow_contact_rewrite!=1) 
    1699     { 
    1700         pj_ansi_sprintf(line, "--auto-update-nat %i\n", 
    1701                         (int)acc_cfg->allow_contact_rewrite); 
    1702         pj_strcat2(result, line); 
    1703     } 
    1704  
    1705 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) 
    1706     /* SRTP */ 
    1707     if (acc_cfg->use_srtp) { 
    1708         int use_srtp = (int)acc_cfg->use_srtp; 
    1709         if (use_srtp == PJMEDIA_SRTP_OPTIONAL &&  
    1710             acc_cfg->srtp_optional_dup_offer) 
    1711         { 
    1712             use_srtp = 3; 
    1713         } 
    1714         pj_ansi_sprintf(line, "--use-srtp %i\n", use_srtp); 
    1715         pj_strcat2(result, line); 
    1716     } 
    1717     if (acc_cfg->srtp_secure_signaling !=  
    1718         PJSUA_DEFAULT_SRTP_SECURE_SIGNALING)  
    1719     { 
    1720         pj_ansi_sprintf(line, "--srtp-secure %d\n", 
    1721                         acc_cfg->srtp_secure_signaling); 
    1722         pj_strcat2(result, line); 
    1723     } 
    1724 #endif 
    1725  
    1726     /* Proxy */ 
    1727     for (i=0; i<acc_cfg->proxy_cnt; ++i) { 
    1728         pj_ansi_sprintf(line, "--proxy %.*s\n", 
    1729                               (int)acc_cfg->proxy[i].slen, 
    1730                               acc_cfg->proxy[i].ptr); 
    1731         pj_strcat2(result, line); 
    1732     } 
    1733  
    1734     /* Credentials */ 
    1735     for (i=0; i<acc_cfg->cred_count; ++i) { 
    1736         if (acc_cfg->cred_info[i].realm.slen) { 
    1737             pj_ansi_sprintf(line, "--realm %.*s\n", 
    1738                                   (int)acc_cfg->cred_info[i].realm.slen, 
    1739                                   acc_cfg->cred_info[i].realm.ptr); 
    1740             pj_strcat2(result, line); 
    1741         } 
    1742  
    1743         if (acc_cfg->cred_info[i].username.slen) { 
    1744             pj_ansi_sprintf(line, "--username %.*s\n", 
    1745                                   (int)acc_cfg->cred_info[i].username.slen, 
    1746                                   acc_cfg->cred_info[i].username.ptr); 
    1747             pj_strcat2(result, line); 
    1748         } 
    1749  
    1750         if (acc_cfg->cred_info[i].data.slen) { 
    1751             pj_ansi_sprintf(line, "--password %.*s\n", 
    1752                                   (int)acc_cfg->cred_info[i].data.slen, 
    1753                                   acc_cfg->cred_info[i].data.ptr); 
    1754             pj_strcat2(result, line); 
    1755         } 
    1756  
    1757         if (i != acc_cfg->cred_count - 1) 
    1758             pj_strcat2(result, "--next-cred\n"); 
    1759     } 
    1760  
    1761     /* reg-use-proxy */ 
    1762     if (acc_cfg->reg_use_proxy != 3) { 
    1763         pj_ansi_sprintf(line, "--reg-use-proxy %d\n", 
    1764                               acc_cfg->reg_use_proxy); 
    1765         pj_strcat2(result, line); 
    1766     } 
    1767  
    1768     /* rereg-delay */ 
    1769     if (acc_cfg->reg_retry_interval != PJSUA_REG_RETRY_INTERVAL) { 
    1770         pj_ansi_sprintf(line, "--rereg-delay %d\n", 
    1771                               acc_cfg->reg_retry_interval); 
    1772         pj_strcat2(result, line); 
    1773     } 
    1774  
    1775     /* 100rel extension */ 
    1776     if (acc_cfg->require_100rel) { 
    1777         pj_strcat2(result, "--use-100rel\n"); 
    1778     } 
    1779  
    1780     /* Session Timer extension */ 
    1781     if (acc_cfg->use_timer) { 
    1782         pj_ansi_sprintf(line, "--use-timer %d\n", 
    1783                               acc_cfg->use_timer); 
    1784         pj_strcat2(result, line); 
    1785     } 
    1786     if (acc_cfg->timer_setting.min_se != 90) { 
    1787         pj_ansi_sprintf(line, "--timer-min-se %d\n", 
    1788                               acc_cfg->timer_setting.min_se); 
    1789         pj_strcat2(result, line); 
    1790     } 
    1791     if (acc_cfg->timer_setting.sess_expires != PJSIP_SESS_TIMER_DEF_SE) { 
    1792         pj_ansi_sprintf(line, "--timer-se %d\n", 
    1793                               acc_cfg->timer_setting.sess_expires); 
    1794         pj_strcat2(result, line); 
    1795     } 
    1796  
    1797     /* Publish */ 
    1798     if (acc_cfg->publish_enabled) 
    1799         pj_strcat2(result, "--publish\n"); 
    1800  
    1801     /* MWI */ 
    1802     if (acc_cfg->mwi_enabled) 
    1803         pj_strcat2(result, "--mwi\n"); 
    1804  
    1805     if (acc_cfg->sip_stun_use != PJSUA_STUN_USE_DEFAULT || 
    1806         acc_cfg->media_stun_use != PJSUA_STUN_USE_DEFAULT) 
    1807     { 
    1808         pj_strcat2(result, "--disable-stun\n"); 
    1809     } 
    1810  
    1811     /* Media Transport*/ 
    1812     if (acc_cfg->ice_cfg.enable_ice) 
    1813         pj_strcat2(result, "--use-ice\n"); 
    1814  
    1815     if (acc_cfg->ice_cfg.ice_opt.aggressive == PJ_FALSE) 
    1816         pj_strcat2(result, "--ice-regular\n"); 
    1817  
    1818     if (acc_cfg->turn_cfg.enable_turn) 
    1819         pj_strcat2(result, "--use-turn\n"); 
    1820  
    1821     if (acc_cfg->ice_cfg.ice_max_host_cands >= 0) { 
    1822         pj_ansi_sprintf(line, "--ice_max_host_cands %d\n", 
    1823                         acc_cfg->ice_cfg.ice_max_host_cands); 
    1824         pj_strcat2(result, line); 
    1825     } 
    1826  
    1827     if (acc_cfg->ice_cfg.ice_no_rtcp) 
    1828         pj_strcat2(result, "--ice-no-rtcp\n"); 
    1829  
    1830     if (acc_cfg->turn_cfg.turn_server.slen) { 
    1831         pj_ansi_sprintf(line, "--turn-srv %.*s\n", 
    1832                         (int)acc_cfg->turn_cfg.turn_server.slen, 
    1833                         acc_cfg->turn_cfg.turn_server.ptr); 
    1834         pj_strcat2(result, line); 
    1835     } 
    1836  
    1837     if (acc_cfg->turn_cfg.turn_conn_type == PJ_TURN_TP_TCP) 
    1838         pj_strcat2(result, "--turn-tcp\n"); 
    1839  
    1840     if (acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.username.slen) { 
    1841         pj_ansi_sprintf(line, "--turn-user %.*s\n", 
    1842                         (int)acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.username.slen, 
    1843                         acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.username.ptr); 
    1844         pj_strcat2(result, line); 
    1845     } 
    1846  
    1847     if (acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.data.slen) { 
    1848         pj_ansi_sprintf(line, "--turn-passwd %.*s\n", 
    1849                         (int)acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.data.slen, 
    1850                         acc_cfg->turn_cfg.turn_auth_cred.data.static_cred.data.ptr); 
    1851         pj_strcat2(result, line); 
    1852     } 
    1853 } 
    1854  
    1855  
    1856 /* 
    1857  * Write settings. 
    1858  */ 
    1859 static int write_settings(const struct app_config *config, 
    1860                           char *buf, pj_size_t max) 
    1861 { 
    1862     unsigned acc_index; 
    1863     unsigned i; 
    1864     pj_str_t cfg; 
    1865     char line[128]; 
    1866     extern pj_bool_t pjsip_use_compact_form; 
    1867  
    1868     PJ_UNUSED_ARG(max); 
    1869  
    1870     cfg.ptr = buf; 
    1871     cfg.slen = 0; 
    1872  
    1873     /* Logging. */ 
    1874     pj_strcat2(&cfg, "#\n# Logging options:\n#\n"); 
    1875     pj_ansi_sprintf(line, "--log-level %d\n", 
    1876                     config->log_cfg.level); 
    1877     pj_strcat2(&cfg, line); 
    1878  
    1879     pj_ansi_sprintf(line, "--app-log-level %d\n", 
    1880                     config->log_cfg.console_level); 
    1881     pj_strcat2(&cfg, line); 
    1882  
    1883     if (config->log_cfg.log_filename.slen) { 
    1884         pj_ansi_sprintf(line, "--log-file %.*s\n", 
    1885                         (int)config->log_cfg.log_filename.slen, 
    1886                         config->log_cfg.log_filename.ptr); 
    1887         pj_strcat2(&cfg, line); 
    1888     } 
    1889  
    1890     if (config->log_cfg.log_file_flags & PJ_O_APPEND) { 
    1891         pj_strcat2(&cfg, "--log-append\n"); 
    1892     } 
    1893  
    1894     /* Save account settings. */ 
    1895     for (acc_index=0; acc_index < config->acc_cnt; ++acc_index) { 
    1896          
    1897         write_account_settings(acc_index, &cfg); 
    1898  
    1899         if (acc_index < config->acc_cnt-1) 
    1900             pj_strcat2(&cfg, "--next-account\n"); 
    1901     } 
    1902  
    1903  
    1904     pj_strcat2(&cfg, "\n#\n# Network settings:\n#\n"); 
    1905  
    1906     /* Nameservers */ 
    1907     for (i=0; i<config->cfg.nameserver_count; ++i) { 
    1908         pj_ansi_sprintf(line, "--nameserver %.*s\n", 
    1909                               (int)config->cfg.nameserver[i].slen, 
    1910                               config->cfg.nameserver[i].ptr); 
    1911         pj_strcat2(&cfg, line); 
    1912     } 
    1913  
    1914     /* Outbound proxy */ 
    1915     for (i=0; i<config->cfg.outbound_proxy_cnt; ++i) { 
    1916         pj_ansi_sprintf(line, "--outbound %.*s\n", 
    1917                               (int)config->cfg.outbound_proxy[i].slen, 
    1918                               config->cfg.outbound_proxy[i].ptr); 
    1919         pj_strcat2(&cfg, line); 
    1920     } 
    1921  
    1922     /* Transport options */ 
    1923     if (config->ipv6) { 
    1924         pj_strcat2(&cfg, "--ipv6\n"); 
    1925     } 
    1926     if (config->enable_qos) { 
    1927         pj_strcat2(&cfg, "--set-qos\n"); 
    1928     } 
    1929  
    1930     /* UDP Transport. */ 
    1931     pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port); 
    1932     pj_strcat2(&cfg, line); 
    1933  
    1934     /* IP address, if any. */ 
    1935     if (config->udp_cfg.public_addr.slen) { 
    1936         pj_ansi_sprintf(line, "--ip-addr %.*s\n",  
    1937                         (int)config->udp_cfg.public_addr.slen, 
    1938                         config->udp_cfg.public_addr.ptr); 
    1939         pj_strcat2(&cfg, line); 
    1940     } 
    1941  
    1942     /* Bound IP address, if any. */ 
    1943     if (config->udp_cfg.bound_addr.slen) { 
    1944         pj_ansi_sprintf(line, "--bound-addr %.*s\n",  
    1945                         (int)config->udp_cfg.bound_addr.slen, 
    1946                         config->udp_cfg.bound_addr.ptr); 
    1947         pj_strcat2(&cfg, line); 
    1948     } 
    1949  
    1950     /* No TCP ? */ 
    1951     if (config->no_tcp) { 
    1952         pj_strcat2(&cfg, "--no-tcp\n"); 
    1953     } 
    1954  
    1955     /* No UDP ? */ 
    1956     if (config->no_udp) { 
    1957         pj_strcat2(&cfg, "--no-udp\n"); 
    1958     } 
    1959  
    1960     /* STUN */ 
    1961     for (i=0; i<config->cfg.stun_srv_cnt; ++i) { 
    1962         pj_ansi_sprintf(line, "--stun-srv %.*s\n", 
    1963                         (int)config->cfg.stun_srv[i].slen,  
    1964                         config->cfg.stun_srv[i].ptr); 
    1965         pj_strcat2(&cfg, line); 
    1966     } 
    1967  
    1968 #if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0) 
    1969     /* TLS */ 
    1970     if (config->use_tls) 
    1971         pj_strcat2(&cfg, "--use-tls\n"); 
    1972     if (config->udp_cfg.tls_setting.ca_list_file.slen) { 
    1973         pj_ansi_sprintf(line, "--tls-ca-file %.*s\n", 
    1974                         (int)config->udp_cfg.tls_setting.ca_list_file.slen,  
    1975                         config->udp_cfg.tls_setting.ca_list_file.ptr); 
    1976         pj_strcat2(&cfg, line); 
    1977     } 
    1978     if (config->udp_cfg.tls_setting.cert_file.slen) { 
    1979         pj_ansi_sprintf(line, "--tls-cert-file %.*s\n", 
    1980                         (int)config->udp_cfg.tls_setting.cert_file.slen,  
    1981                         config->udp_cfg.tls_setting.cert_file.ptr); 
    1982         pj_strcat2(&cfg, line); 
    1983     } 
    1984     if (config->udp_cfg.tls_setting.privkey_file.slen) { 
    1985         pj_ansi_sprintf(line, "--tls-privkey-file %.*s\n", 
    1986                         (int)config->udp_cfg.tls_setting.privkey_file.slen,  
    1987                         config->udp_cfg.tls_setting.privkey_file.ptr); 
    1988         pj_strcat2(&cfg, line); 
    1989     } 
    1990  
    1991     if (config->udp_cfg.tls_setting.password.slen) { 
    1992         pj_ansi_sprintf(line, "--tls-password %.*s\n", 
    1993                         (int)config->udp_cfg.tls_setting.password.slen,  
    1994                         config->udp_cfg.tls_setting.password.ptr); 
    1995         pj_strcat2(&cfg, line); 
    1996     } 
    1997  
    1998     if (config->udp_cfg.tls_setting.verify_server) 
    1999         pj_strcat2(&cfg, "--tls-verify-server\n"); 
    2000  
    2001     if (config->udp_cfg.tls_setting.verify_client) 
    2002         pj_strcat2(&cfg, "--tls-verify-client\n"); 
    2003  
    2004     if (config->udp_cfg.tls_setting.timeout.sec) { 
    2005         pj_ansi_sprintf(line, "--tls-neg-timeout %d\n", 
    2006                         (int)config->udp_cfg.tls_setting.timeout.sec); 
    2007         pj_strcat2(&cfg, line); 
    2008     } 
    2009  
    2010     for (i=0; i<config->udp_cfg.tls_setting.ciphers_num; ++i) { 
    2011         pj_ansi_sprintf(line, "--tls-cipher 0x%06X # %s\n", 
    2012                         config->udp_cfg.tls_setting.ciphers[i], 
    2013                         pj_ssl_cipher_name(config->udp_cfg.tls_setting.ciphers[i])); 
    2014         pj_strcat2(&cfg, line); 
    2015     } 
    2016 #endif 
    2017  
    2018     pj_strcat2(&cfg, "\n#\n# Media settings:\n#\n"); 
    2019  
    2020     /* Video & extra audio */ 
    2021     for (i=0; i<config->vid.vid_cnt; ++i) { 
    2022         pj_strcat2(&cfg, "--video\n"); 
    2023     } 
    2024     for (i=1; i<config->aud_cnt; ++i) { 
    2025         pj_strcat2(&cfg, "--extra-audio\n"); 
    2026     } 
    2027  
    2028     /* SRTP */ 
    2029 #if PJMEDIA_HAS_SRTP 
    2030     if (app_config.cfg.use_srtp != PJSUA_DEFAULT_USE_SRTP) { 
    2031         int use_srtp = (int)app_config.cfg.use_srtp; 
    2032         if (use_srtp == PJMEDIA_SRTP_OPTIONAL &&  
    2033             app_config.cfg.srtp_optional_dup_offer) 
    2034         { 
    2035             use_srtp = 3; 
    2036         } 
    2037         pj_ansi_sprintf(line, "--use-srtp %d\n", use_srtp); 
    2038         pj_strcat2(&cfg, line); 
    2039     } 
    2040     if (app_config.cfg.srtp_secure_signaling !=  
    2041         PJSUA_DEFAULT_SRTP_SECURE_SIGNALING)  
    2042     { 
    2043         pj_ansi_sprintf(line, "--srtp-secure %d\n", 
    2044                         app_config.cfg.srtp_secure_signaling); 
    2045         pj_strcat2(&cfg, line); 
    2046     } 
    2047 #endif 
    2048  
    2049     /* Media */ 
    2050     if (config->null_audio) 
    2051         pj_strcat2(&cfg, "--null-audio\n"); 
    2052     if (config->auto_play) 
    2053         pj_strcat2(&cfg, "--auto-play\n"); 
    2054     if (config->auto_loop) 
    2055         pj_strcat2(&cfg, "--auto-loop\n"); 
    2056     if (config->auto_conf) 
    2057         pj_strcat2(&cfg, "--auto-conf\n"); 
    2058     for (i=0; i<config->wav_count; ++i) { 
    2059         pj_ansi_sprintf(line, "--play-file %s\n", 
    2060                         config->wav_files[i].ptr); 
    2061         pj_strcat2(&cfg, line); 
    2062     } 
    2063     for (i=0; i<config->tone_count; ++i) { 
    2064         pj_ansi_sprintf(line, "--play-tone %d,%d,%d,%d\n", 
    2065                         config->tones[i].freq1, config->tones[i].freq2,  
    2066                         config->tones[i].on_msec, config->tones[i].off_msec); 
    2067         pj_strcat2(&cfg, line); 
    2068     } 
    2069     if (config->rec_file.slen) { 
    2070         pj_ansi_sprintf(line, "--rec-file %s\n", 
    2071                         config->rec_file.ptr); 
    2072         pj_strcat2(&cfg, line); 
    2073     } 
    2074     if (config->auto_rec) 
    2075         pj_strcat2(&cfg, "--auto-rec\n"); 
    2076     if (config->capture_dev != PJSUA_INVALID_ID) { 
    2077         pj_ansi_sprintf(line, "--capture-dev %d\n", config->capture_dev); 
    2078         pj_strcat2(&cfg, line); 
    2079     } 
    2080     if (config->playback_dev != PJSUA_INVALID_ID) { 
    2081         pj_ansi_sprintf(line, "--playback-dev %d\n", config->playback_dev); 
    2082         pj_strcat2(&cfg, line); 
    2083     } 
    2084     if (config->media_cfg.snd_auto_close_time != -1) { 
    2085         pj_ansi_sprintf(line, "--snd-auto-close %d\n",  
    2086                         config->media_cfg.snd_auto_close_time); 
    2087         pj_strcat2(&cfg, line); 
    2088     } 
    2089     if (config->no_tones) { 
    2090         pj_strcat2(&cfg, "--no-tones\n"); 
    2091     } 
    2092     if (config->media_cfg.jb_max != -1) { 
    2093         pj_ansi_sprintf(line, "--jb-max-size %d\n",  
    2094                         config->media_cfg.jb_max); 
    2095         pj_strcat2(&cfg, line); 
    2096     } 
    2097  
    2098     /* Sound device latency */ 
    2099     if (config->capture_lat != PJMEDIA_SND_DEFAULT_REC_LATENCY) { 
    2100         pj_ansi_sprintf(line, "--capture-lat %d\n", config->capture_lat); 
    2101         pj_strcat2(&cfg, line); 
    2102     } 
    2103     if (config->playback_lat != PJMEDIA_SND_DEFAULT_PLAY_LATENCY) { 
    2104         pj_ansi_sprintf(line, "--playback-lat %d\n", config->playback_lat); 
    2105         pj_strcat2(&cfg, line); 
    2106     } 
    2107  
    2108     /* Media clock rate. */ 
    2109     if (config->media_cfg.clock_rate != PJSUA_DEFAULT_CLOCK_RATE) { 
    2110         pj_ansi_sprintf(line, "--clock-rate %d\n", 
    2111                         config->media_cfg.clock_rate); 
    2112         pj_strcat2(&cfg, line); 
    2113     } else { 
    2114         pj_ansi_sprintf(line, "#using default --clock-rate %d\n", 
    2115                         config->media_cfg.clock_rate); 
    2116         pj_strcat2(&cfg, line); 
    2117     } 
    2118  
    2119     if (config->media_cfg.snd_clock_rate &&  
    2120         config->media_cfg.snd_clock_rate != config->media_cfg.clock_rate)  
    2121     { 
    2122         pj_ansi_sprintf(line, "--snd-clock-rate %d\n", 
    2123                         config->media_cfg.snd_clock_rate); 
    2124         pj_strcat2(&cfg, line); 
    2125     } 
    2126  
    2127     /* Stereo mode. */ 
    2128     if (config->media_cfg.channel_count == 2) { 
    2129         pj_ansi_sprintf(line, "--stereo\n"); 
    2130         pj_strcat2(&cfg, line); 
    2131     } 
    2132  
    2133     /* quality */ 
    2134     if (config->media_cfg.quality != PJSUA_DEFAULT_CODEC_QUALITY) { 
    2135         pj_ansi_sprintf(line, "--quality %d\n", 
    2136                         config->media_cfg.quality); 
    2137         pj_strcat2(&cfg, line); 
    2138     } else { 
    2139         pj_ansi_sprintf(line, "#using default --quality %d\n", 
    2140                         config->media_cfg.quality); 
    2141         pj_strcat2(&cfg, line); 
    2142     } 
    2143  
    2144     if (config->vid.vcapture_dev != PJMEDIA_VID_DEFAULT_CAPTURE_DEV) { 
    2145         pj_ansi_sprintf(line, "--vcapture-dev %d\n", config->vid.vcapture_dev); 
    2146         pj_strcat2(&cfg, line); 
    2147     } 
    2148     if (config->vid.vrender_dev != PJMEDIA_VID_DEFAULT_RENDER_DEV) { 
    2149         pj_ansi_sprintf(line, "--vrender-dev %d\n", config->vid.vrender_dev); 
    2150         pj_strcat2(&cfg, line); 
    2151     } 
    2152     for (i=0; i<config->avi_cnt; ++i) { 
    2153         pj_ansi_sprintf(line, "--play-avi %s\n", config->avi[i].path.ptr); 
    2154         pj_strcat2(&cfg, line); 
    2155     } 
    2156     if (config->avi_auto_play) { 
    2157         pj_ansi_sprintf(line, "--auto-play-avi\n"); 
    2158         pj_strcat2(&cfg, line); 
    2159     } 
    2160  
    2161     /* ptime */ 
    2162     if (config->media_cfg.ptime) { 
    2163         pj_ansi_sprintf(line, "--ptime %d\n", 
    2164                         config->media_cfg.ptime); 
    2165         pj_strcat2(&cfg, line); 
    2166     } 
    2167  
    2168     /* no-vad */ 
    2169     if (config->media_cfg.no_vad) { 
    2170         pj_strcat2(&cfg, "--no-vad\n"); 
    2171     } 
    2172  
    2173     /* ec-tail */ 
    2174     if (config->media_cfg.ec_tail_len != PJSUA_DEFAULT_EC_TAIL_LEN) { 
    2175         pj_ansi_sprintf(line, "--ec-tail %d\n", 
    2176                         config->media_cfg.ec_tail_len); 
    2177         pj_strcat2(&cfg, line); 
    2178     } else { 
    2179         pj_ansi_sprintf(line, "#using default --ec-tail %d\n", 
    2180                         config->media_cfg.ec_tail_len); 
    2181         pj_strcat2(&cfg, line); 
    2182     } 
    2183  
    2184     /* ec-opt */ 
    2185     if (config->media_cfg.ec_options != 0) { 
    2186         pj_ansi_sprintf(line, "--ec-opt %d\n", 
    2187                         config->media_cfg.ec_options); 
    2188         pj_strcat2(&cfg, line); 
    2189     }  
    2190  
    2191     /* ilbc-mode */ 
    2192     if (config->media_cfg.ilbc_mode != PJSUA_DEFAULT_ILBC_MODE) { 
    2193         pj_ansi_sprintf(line, "--ilbc-mode %d\n", 
    2194                         config->media_cfg.ilbc_mode); 
    2195         pj_strcat2(&cfg, line); 
    2196     } else { 
    2197         pj_ansi_sprintf(line, "#using default --ilbc-mode %d\n", 
    2198                         config->media_cfg.ilbc_mode); 
    2199         pj_strcat2(&cfg, line); 
    2200     } 
    2201  
    2202     /* RTP drop */ 
    2203     if (config->media_cfg.tx_drop_pct) { 
    2204         pj_ansi_sprintf(line, "--tx-drop-pct %d\n", 
    2205                         config->media_cfg.tx_drop_pct); 
    2206         pj_strcat2(&cfg, line); 
    2207  
    2208     } 
    2209     if (config->media_cfg.rx_drop_pct) { 
    2210         pj_ansi_sprintf(line, "--rx-drop-pct %d\n", 
    2211                         config->media_cfg.rx_drop_pct); 
    2212         pj_strcat2(&cfg, line); 
    2213  
    2214     } 
    2215  
    2216  
    2217     /* Start RTP port. */ 
    2218     pj_ansi_sprintf(line, "--rtp-port %d\n", 
    2219                     config->rtp_cfg.port); 
    2220     pj_strcat2(&cfg, line); 
    2221  
    2222     /* Disable codec */ 
    2223     for (i=0; i<config->codec_dis_cnt; ++i) { 
    2224         pj_ansi_sprintf(line, "--dis-codec %s\n", 
    2225                     config->codec_dis[i].ptr); 
    2226         pj_strcat2(&cfg, line); 
    2227     } 
    2228     /* Add codec. */ 
    2229     for (i=0; i<config->codec_cnt; ++i) { 
    2230         pj_ansi_sprintf(line, "--add-codec %s\n", 
    2231                     config->codec_arg[i].ptr); 
    2232         pj_strcat2(&cfg, line); 
    2233     } 
    2234  
    2235     pj_strcat2(&cfg, "\n#\n# User agent:\n#\n"); 
    2236  
    2237     /* Auto-answer. */ 
    2238     if (config->auto_answer != 0) { 
    2239         pj_ansi_sprintf(line, "--auto-answer %d\n", 
    2240                         config->auto_answer); 
    2241         pj_strcat2(&cfg, line); 
    2242     } 
    2243  
    2244     /* accept-redirect */ 
    2245     if (config->redir_op != PJSIP_REDIRECT_ACCEPT_REPLACE) { 
    2246         pj_ansi_sprintf(line, "--accept-redirect %d\n", 
    2247                         config->redir_op); 
    2248         pj_strcat2(&cfg, line); 
    2249     } 
    2250  
    2251     /* Max calls. */ 
    2252     pj_ansi_sprintf(line, "--max-calls %d\n", 
    2253                     config->cfg.max_calls); 
    2254     pj_strcat2(&cfg, line); 
    2255  
    2256     /* Uas-duration. */ 
    2257     if (config->duration != NO_LIMIT) { 
    2258         pj_ansi_sprintf(line, "--duration %d\n", 
    2259                         config->duration); 
    2260         pj_strcat2(&cfg, line); 
    2261     } 
    2262  
    2263     /* norefersub ? */ 
    2264     if (config->no_refersub) { 
    2265         pj_strcat2(&cfg, "--norefersub\n"); 
    2266     } 
    2267  
    2268     if (pjsip_use_compact_form) 
    2269     { 
    2270         pj_strcat2(&cfg, "--use-compact-form\n"); 
    2271     } 
    2272  
    2273     if (!config->cfg.force_lr) { 
    2274         pj_strcat2(&cfg, "--no-force-lr\n"); 
    2275     } 
    2276  
    2277     pj_strcat2(&cfg, "\n#\n# Buddies:\n#\n"); 
    2278  
    2279     /* Add buddies. */ 
    2280     for (i=0; i<config->buddy_cnt; ++i) { 
    2281         pj_ansi_sprintf(line, "--add-buddy %.*s\n", 
    2282                               (int)config->buddy_cfg[i].uri.slen, 
    2283                               config->buddy_cfg[i].uri.ptr); 
    2284         pj_strcat2(&cfg, line); 
    2285     } 
    2286  
    2287     /* SIP extensions. */ 
    2288     pj_strcat2(&cfg, "\n#\n# SIP extensions:\n#\n"); 
    2289     /* 100rel extension */ 
    2290     if (config->cfg.require_100rel) { 
    2291         pj_strcat2(&cfg, "--use-100rel\n"); 
    2292     } 
    2293     /* Session Timer extension */ 
    2294     if (config->cfg.use_timer) { 
    2295         pj_ansi_sprintf(line, "--use-timer %d\n", 
    2296                               config->cfg.use_timer); 
    2297         pj_strcat2(&cfg, line); 
    2298     } 
    2299     if (config->cfg.timer_setting.min_se != 90) { 
    2300         pj_ansi_sprintf(line, "--timer-min-se %d\n", 
    2301                               config->cfg.timer_setting.min_se); 
    2302         pj_strcat2(&cfg, line); 
    2303     } 
    2304     if (config->cfg.timer_setting.sess_expires != PJSIP_SESS_TIMER_DEF_SE) { 
    2305         pj_ansi_sprintf(line, "--timer-se %d\n", 
    2306                               config->cfg.timer_setting.sess_expires); 
    2307         pj_strcat2(&cfg, line); 
    2308     } 
    2309  
    2310     *(cfg.ptr + cfg.slen) = '\0'; 
    2311     return cfg.slen; 
    2312 } 
    2313  
    2314  
    2315 /* 
    2316  * Dump application states. 
    2317  */ 
    2318 static void app_dump(pj_bool_t detail) 
    2319 { 
    2320     pjsua_dump(detail); 
    2321 } 
    2322  
    2323 /* 
    2324  * Print log of call states. Since call states may be too long for logger, 
    2325  * printing it is a bit tricky, it should be printed part by part as long  
    2326  * as the logger can accept. 
    2327  */ 
    2328 static void log_call_dump(int call_id)  
    2329 { 
    2330     unsigned call_dump_len; 
    2331     unsigned part_len; 
    2332     unsigned part_idx; 
    2333     unsigned log_decor; 
    2334  
    2335     pjsua_call_dump(call_id, PJ_TRUE, some_buf,  
    2336                     sizeof(some_buf), "  "); 
    2337     call_dump_len = strlen(some_buf); 
    2338  
    2339     log_decor = pj_log_get_decor(); 
    2340     pj_log_set_decor(log_decor & ~(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR)); 
    2341     PJ_LOG(3,(THIS_FILE, "\n")); 
    2342     pj_log_set_decor(0); 
    2343  
    2344     part_idx = 0; 
    2345     part_len = PJ_LOG_MAX_SIZE-80; 
    2346     while (part_idx < call_dump_len) { 
    2347         char p_orig, *p; 
    2348  
    2349         p = &some_buf[part_idx]; 
    2350         if (part_idx + part_len > call_dump_len) 
    2351             part_len = call_dump_len - part_idx; 
    2352         p_orig = p[part_len]; 
    2353         p[part_len] = '\0'; 
    2354         PJ_LOG(3,(THIS_FILE, "%s", p)); 
    2355         p[part_len] = p_orig; 
    2356         part_idx += part_len; 
    2357     } 
    2358     pj_log_set_decor(log_decor); 
    23591541} 
    23601542 
     
    23621544 * Console application 
    23631545 */ 
    2364  
    23651546static void ringback_start(pjsua_call_id call_id) 
    23661547{ 
     
    24271608} 
    24281609 
    2429 #ifdef HAVE_MULTIPART_TEST 
    2430   /* 
    2431    * Enable multipart in msg_data and add a dummy body into the 
    2432    * multipart bodies. 
    2433    */ 
    2434   static void add_multipart(pjsua_msg_data *msg_data) 
    2435   { 
    2436       static pjsip_multipart_part *alt_part; 
    2437  
    2438       if (!alt_part) { 
    2439           pj_str_t type, subtype, content; 
    2440  
    2441           alt_part = pjsip_multipart_create_part(app_config.pool); 
    2442  
    2443           type = pj_str("text"); 
    2444           subtype = pj_str("plain"); 
    2445           content = pj_str("Sample text body of a multipart bodies"); 
    2446           alt_part->body = pjsip_msg_body_create(app_config.pool, &type, 
    2447                                                  &subtype, &content); 
    2448       } 
    2449  
    2450       msg_data->multipart_ctype.type = pj_str("multipart"); 
    2451       msg_data->multipart_ctype.subtype = pj_str("mixed"); 
    2452       pj_list_push_back(&msg_data->multipart_parts, alt_part); 
    2453   } 
    2454 #  define TEST_MULTIPART(msg_data)      add_multipart(msg_data) 
    2455 #else 
    2456 #  define TEST_MULTIPART(msg_data) 
    2457 #endif 
    2458  
    2459 /* 
    2460  * Find next call when current call is disconnected or when user 
    2461  * press ']' 
    2462  */ 
    2463 static pj_bool_t find_next_call(void) 
    2464 { 
    2465     int i, max; 
    2466  
    2467     max = pjsua_call_get_max_count(); 
    2468     for (i=current_call+1; i<max; ++i) { 
    2469         if (pjsua_call_is_active(i)) { 
    2470             current_call = i; 
    2471             return PJ_TRUE; 
    2472         } 
    2473     } 
    2474  
    2475     for (i=0; i<current_call; ++i) { 
    2476         if (pjsua_call_is_active(i)) { 
    2477             current_call = i; 
    2478             return PJ_TRUE; 
    2479         } 
    2480     } 
    2481  
    2482     current_call = PJSUA_INVALID_ID; 
    2483     return PJ_FALSE; 
    2484 } 
    2485  
    2486  
    2487 /* 
    2488  * Find previous call when user press '[' 
    2489  */ 
    2490 static pj_bool_t find_prev_call(void) 
    2491 { 
    2492     int i, max; 
    2493  
    2494     max = pjsua_call_get_max_count(); 
    2495     for (i=current_call-1; i>=0; --i) { 
    2496         if (pjsua_call_is_active(i)) { 
    2497             current_call = i; 
    2498             return PJ_TRUE; 
    2499         } 
    2500     } 
    2501  
    2502     for (i=max-1; i>current_call; --i) { 
    2503         if (pjsua_call_is_active(i)) { 
    2504             current_call = i; 
    2505             return PJ_TRUE; 
    2506         } 
    2507     } 
    2508  
    2509     current_call = PJSUA_INVALID_ID; 
    2510     return PJ_FALSE; 
    2511 } 
    2512  
    2513  
    25141610/* Callback from timer when the maximum call duration has been 
    25151611 * exceeded. 
     
    25961692    } else { 
    25971693 
    2598         if (app_config.duration!=NO_LIMIT &&  
     1694        if (app_config.duration != NO_LIMIT_DURATION &&  
    25991695            call_info.state == PJSIP_INV_STATE_CONFIRMED)  
    26001696        { 
     
    26501746} 
    26511747 
    2652  
    26531748/** 
    26541749 * Handler when there is incoming call. 
     
    27041799                  "From: %s\n" 
    27051800                  "To: %s\n" 
    2706                   "Press a to answer or h to reject call", 
     1801                  "Press %s to answer or %s to reject call", 
    27071802                  acc_id, 
    27081803                  call_info.rem_aud_cnt, 
     
    27101805                  notif_st, 
    27111806                  call_info.remote_info.ptr, 
    2712                   call_info.local_info.ptr)); 
     1807                  call_info.local_info.ptr, 
     1808                  (app_config.use_cli?"c a":"a"), 
     1809                  (app_config.use_cli?"c g":"h"))); 
    27131810    } 
    27141811} 
     
    29252022} 
    29262023 
    2927 /* arrange windows. arg: 
    2928  *   -1:    arrange all windows 
    2929  *   != -1: arrange only this window id 
    2930  */ 
    2931 static void arrange_window(pjsua_vid_win_id wid) 
    2932 { 
    2933 #if PJSUA_HAS_VIDEO 
    2934     pjmedia_coord pos; 
    2935     int i, last; 
    2936  
    2937     pos.x = 0; 
    2938     pos.y = 10; 
    2939     last = (wid == PJSUA_INVALID_ID) ? PJSUA_MAX_VID_WINS : wid; 
    2940  
    2941     for (i=0; i<last; ++i) { 
    2942         pjsua_vid_win_info wi; 
    2943         pj_status_t status; 
    2944  
    2945         status = pjsua_vid_win_get_info(i, &wi); 
    2946         if (status != PJ_SUCCESS) 
    2947             continue; 
    2948  
    2949         if (wid == PJSUA_INVALID_ID) 
    2950             pjsua_vid_win_set_pos(i, &pos); 
    2951  
    2952         if (wi.show) 
    2953             pos.y += wi.size.h; 
    2954     } 
    2955  
    2956     if (wid != PJSUA_INVALID_ID) 
    2957         pjsua_vid_win_set_pos(wid, &pos); 
    2958 #else 
    2959     PJ_UNUSED_ARG(wid); 
    2960 #endif 
    2961 } 
    2962  
    29632024/* Process video media state. "mi" is the media index. */ 
    29642025static void on_call_video_state(pjsua_call_info *ci, unsigned mi, 
     
    30702131    // Log already written. 
    30712132} 
    3072  
    30732133 
    30742134/* 
     
    34442504#endif 
    34452505 
    3446 /* 
    3447  * Print buddy list. 
    3448  */ 
    3449 static void print_buddy_list(void) 
    3450 { 
    3451     pjsua_buddy_id ids[64]; 
    3452     int i; 
    3453     unsigned count = PJ_ARRAY_SIZE(ids); 
    3454  
    3455     puts("Buddy list:"); 
    3456  
    3457     pjsua_enum_buddies(ids, &count); 
    3458  
    3459     if (count == 0) 
    3460         puts(" -none-"); 
    3461     else { 
    3462         for (i=0; i<(int)count; ++i) { 
    3463             pjsua_buddy_info info; 
    3464  
    3465             if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS) 
    3466                 continue; 
    3467  
    3468             printf(" [%2d] <%.*s>  %.*s\n",  
    3469                     ids[i]+1,  
    3470                     (int)info.status_text.slen, 
    3471                     info.status_text.ptr,  
    3472                     (int)info.uri.slen, 
    3473                     info.uri.ptr); 
    3474         } 
    3475     } 
    3476     puts(""); 
    3477 } 
    3478  
    3479  
    3480 /* 
    3481  * Print account status. 
    3482  */ 
    3483 static void print_acc_status(int acc_id) 
    3484 { 
    3485     char buf[80]; 
    3486     pjsua_acc_info info; 
    3487  
    3488     pjsua_acc_get_info(acc_id, &info); 
    3489  
    3490     if (!info.has_registration) { 
    3491         pj_ansi_snprintf(buf, sizeof(buf), "%.*s",  
    3492                          (int)info.status_text.slen, 
    3493                          info.status_text.ptr); 
    3494  
    3495     } else { 
    3496         pj_ansi_snprintf(buf, sizeof(buf), 
    3497                          "%d/%.*s (expires=%d)", 
    3498                          info.status, 
    3499                          (int)info.status_text.slen, 
    3500                          info.status_text.ptr, 
    3501                          info.expires); 
    3502  
    3503     } 
    3504  
    3505     printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '), 
    3506            acc_id,  (int)info.acc_uri.slen, info.acc_uri.ptr, buf); 
    3507     printf("       Online status: %.*s\n",  
    3508         (int)info.online_status_text.slen, 
    3509         info.online_status_text.ptr); 
    3510 } 
    3511  
    35122506/* Playfile done notification, set timer to hangup calls */ 
    35132507pj_status_t on_playfile_done(pjmedia_port *port, void *usr_data) 
     
    35502544 
    35512545/* 
    3552  * Show a bit of help. 
    3553  */ 
    3554 static void keystroke_help(void) 
    3555 { 
    3556     pjsua_acc_id acc_ids[16]; 
    3557     unsigned count = PJ_ARRAY_SIZE(acc_ids); 
    3558     int i; 
    3559  
    3560     printf(">>>>\n"); 
    3561  
    3562     pjsua_enum_accs(acc_ids, &count); 
    3563  
    3564     printf("Account list:\n"); 
    3565     for (i=0; i<(int)count; ++i) 
    3566         print_acc_status(acc_ids[i]); 
    3567  
    3568     print_buddy_list(); 
    3569      
    3570     //puts("Commands:"); 
    3571     puts("+=============================================================================+"); 
    3572     puts("|       Call Commands:         |   Buddy, IM & Presence:  |     Account:      |"); 
    3573     puts("|                              |                          |                   |"); 
    3574     puts("|  m  Make new call            | +b  Add new buddy       .| +a  Add new accnt |"); 
    3575     puts("|  M  Make multiple calls      | -b  Delete buddy         | -a  Delete accnt. |"); 
    3576     puts("|  a  Answer call              |  i  Send IM              | !a  Modify accnt. |"); 
    3577     puts("|  h  Hangup call  (ha=all)    |  s  Subscribe presence   | rr  (Re-)register |"); 
    3578     puts("|  H  Hold call                |  u  Unsubscribe presence | ru  Unregister    |"); 
    3579     puts("|  v  re-inVite (release hold) |  t  ToGgle Online status |  >  Cycle next ac.|"); 
    3580     puts("|  U  send UPDATE              |  T  Set online status    |  <  Cycle prev ac.|"); 
    3581     puts("| ],[ Select next/prev call    +--------------------------+-------------------+"); 
    3582     puts("|  x  Xfer call                |      Media Commands:     |  Status & Config: |"); 
    3583     puts("|  X  Xfer with Replaces       |                          |                   |"); 
    3584     puts("|  #  Send RFC 2833 DTMF       | cl  List ports           |  d  Dump status   |"); 
    3585     puts("|  *  Send DTMF with INFO      | cc  Connect port         | dd  Dump detailed |"); 
    3586     puts("| dq  Dump curr. call quality  | cd  Disconnect port      | dc  Dump config   |"); 
    3587     puts("|                              |  V  Adjust audio Volume  |  f  Save config   |"); 
    3588     puts("|  S  Send arbitrary REQUEST   | Cp  Codec priorities     |                   |"); 
    3589     puts("+-----------------------------------------------------------------------------+"); 
    3590 #if PJSUA_HAS_VIDEO 
    3591     puts("| Video: \"vid help\" for more info                                             |"); 
    3592     puts("+-----------------------------------------------------------------------------+"); 
    3593 #endif 
    3594     puts("|  q  QUIT   L  ReLoad   sleep MS   echo [0|1|txt]     n: detect NAT type     |"); 
    3595     puts("+=============================================================================+"); 
    3596  
    3597     i = pjsua_call_get_count(); 
    3598     printf("You have %d active call%s\n", i, (i>1?"s":"")); 
    3599  
    3600     if (current_call != PJSUA_INVALID_ID) { 
    3601         pjsua_call_info ci; 
    3602         if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS) 
    3603             printf("Current call id=%d to %.*s [%.*s]\n", current_call, 
    3604                    (int)ci.remote_info.slen, ci.remote_info.ptr, 
    3605                    (int)ci.state_text.slen, ci.state_text.ptr); 
    3606     } 
    3607 } 
    3608  
    3609 /* Help screen for video */ 
    3610 #if PJSUA_HAS_VIDEO 
    3611 static void vid_show_help(void) 
    3612 { 
    3613     pj_bool_t vid_enabled = (app_config.vid.vid_cnt > 0); 
    3614  
    3615     puts("+=============================================================================+"); 
    3616     puts("|                            Video commands:                                  |"); 
    3617     puts("|                                                                             |"); 
    3618     puts("| vid help                  Show this help screen                             |"); 
    3619     puts("| vid enable|disable        Enable or disable video in next offer/answer      |"); 
    3620     puts("| vid acc show              Show current account video settings               |"); 
    3621     puts("| vid acc autorx on|off     Automatically show incoming video on/off          |"); 
    3622     puts("| vid acc autotx on|off     Automatically offer video on/off                  |"); 
    3623     puts("| vid acc cap ID            Set default capture device for current acc        |"); 
    3624     puts("| vid acc rend ID           Set default renderer device for current acc       |"); 
    3625     puts("| vid call rx on|off N      Enable/disable video RX for stream N in curr call |"); 
    3626     puts("| vid call tx on|off N      Enable/disable video TX for stream N in curr call |"); 
    3627     puts("| vid call add              Add video stream for current call                 |"); 
    3628     puts("| vid call enable|disable N Enable/disable stream #N in current call          |"); 
    3629     puts("| vid call cap N ID         Set capture dev ID for stream #N in current call  |"); 
    3630     puts("| vid dev list              List all video devices                            |"); 
    3631     puts("| vid dev refresh           Refresh video device list                         |"); 
    3632     puts("| vid dev prev on|off ID    Enable/disable preview for specified device ID    |"); 
    3633     puts("| vid codec list            List video codecs                                 |"); 
    3634     puts("| vid codec prio ID PRIO    Set codec ID priority to PRIO                     |"); 
    3635     puts("| vid codec fps ID NUM DEN  Set codec ID framerate to (NUM/DEN) fps           |"); 
    3636     puts("| vid codec bw ID AVG MAX   Set codec ID bitrate to AVG & MAX kbps            |"); 
    3637     puts("| vid codec size ID W H     Set codec ID size/resolution to W x H             |"); 
    3638     puts("| vid win list              List all active video windows                     |"); 
    3639     puts("| vid win arrange           Auto arrange windows                              |"); 
    3640     puts("| vid win show|hide ID      Show/hide the specified video window ID           |"); 
    3641     puts("| vid win move ID X Y       Move window ID to position X,Y                    |"); 
    3642     puts("| vid win resize ID w h     Resize window ID to the specified width, height   |"); 
    3643     puts("+=============================================================================+"); 
    3644     printf("| Video will be %s in the next offer/answer %s                            |\n", 
    3645            (vid_enabled? "enabled" : "disabled"), (vid_enabled? " " : "")); 
    3646     puts("+=============================================================================+"); 
    3647 } 
    3648 #endif 
    3649  
    3650 /* 
    3651  * Input simple string 
    3652  */ 
    3653 static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len) 
    3654 { 
    3655     char *p; 
    3656  
    3657     printf("%s (empty to cancel): ", title); fflush(stdout); 
    3658     if (fgets(buf, len, stdin) == NULL) 
    3659         return PJ_FALSE; 
    3660  
    3661     /* Remove trailing newlines. */ 
    3662     for (p=buf; ; ++p) { 
    3663         if (*p=='\r' || *p=='\n') *p='\0'; 
    3664         else if (!*p) break; 
    3665     } 
    3666  
    3667     if (!*buf) 
    3668         return PJ_FALSE; 
    3669      
    3670     return PJ_TRUE; 
    3671 } 
    3672  
    3673  
    3674 #define NO_NB   -2 
    3675 struct input_result 
    3676 { 
    3677     int   nb_result; 
    3678     char *uri_result; 
    3679 }; 
    3680  
    3681  
    3682 /* 
    3683  * Input URL. 
    3684  */ 
    3685 static void ui_input_url(const char *title, char *buf, int len,  
    3686                          struct input_result *result) 
    3687 { 
    3688     result->nb_result = NO_NB; 
    3689     result->uri_result = NULL; 
    3690  
    3691     print_buddy_list(); 
    3692  
    3693     printf("Choices:\n" 
    3694            "   0         For current dialog.\n" 
    3695            "  -1         All %d buddies in buddy list\n" 
    3696            "  [1 -%2d]    Select from buddy list\n" 
    3697            "  URL        An URL\n" 
    3698            "  <Enter>    Empty input (or 'q') to cancel\n" 
    3699            , pjsua_get_buddy_count(), pjsua_get_buddy_count()); 
    3700     printf("%s: ", title); 
    3701  
    3702     fflush(stdout); 
    3703     if (fgets(buf, len, stdin) == NULL) 
    3704         return; 
    3705     len = strlen(buf); 
    3706  
    3707     /* Left trim */ 
    3708     while (pj_isspace(*buf)) { 
    3709         ++buf; 
    3710         --len; 
    3711     } 
    3712  
    3713     /* Remove trailing newlines */ 
    3714     while (len && (buf[len-1] == '\r' || buf[len-1] == '\n')) 
    3715         buf[--len] = '\0'; 
    3716  
    3717     if (len == 0 || buf[0]=='q') 
    3718         return; 
    3719  
    3720     if (pj_isdigit(*buf) || *buf=='-') { 
    3721          
    3722         int i; 
    3723          
    3724         if (*buf=='-') 
    3725             i = 1; 
    3726         else 
    3727             i = 0; 
    3728  
    3729         for (; i<len; ++i) { 
    3730             if (!pj_isdigit(buf[i])) { 
    3731                 puts("Invalid input"); 
    3732                 return; 
    3733             } 
    3734         } 
    3735  
    3736         result->nb_result = my_atoi(buf); 
    3737  
    3738         if (result->nb_result >= 0 &&  
    3739             result->nb_result <= (int)pjsua_get_buddy_count())  
    3740         { 
    3741             return; 
    3742         } 
    3743         if (result->nb_result == -1) 
    3744             return; 
    3745  
    3746         puts("Invalid input"); 
    3747         result->nb_result = NO_NB; 
    3748         return; 
    3749  
    3750     } else { 
    3751         pj_status_t status; 
    3752  
    3753         if ((status=pjsua_verify_url(buf)) != PJ_SUCCESS) { 
    3754             pjsua_perror(THIS_FILE, "Invalid URL", status); 
    3755             return; 
    3756         } 
    3757  
    3758         result->uri_result = buf; 
    3759     } 
    3760 } 
    3761  
    3762 /* 
    3763  * List the ports in conference bridge 
    3764  */ 
    3765 static void conf_list(void) 
    3766 { 
    3767     unsigned i, count; 
    3768     pjsua_conf_port_id id[PJSUA_MAX_CALLS]; 
    3769  
    3770     printf("Conference ports:\n"); 
    3771  
    3772     count = PJ_ARRAY_SIZE(id); 
    3773     pjsua_enum_conf_ports(id, &count); 
    3774  
    3775     for (i=0; i<count; ++i) { 
    3776         char txlist[PJSUA_MAX_CALLS*4+10]; 
    3777         unsigned j; 
    3778         pjsua_conf_port_info info; 
    3779  
    3780         pjsua_conf_get_port_info(id[i], &info); 
    3781  
    3782         txlist[0] = '\0'; 
    3783         for (j=0; j<info.listener_cnt; ++j) { 
    3784             char s[10]; 
    3785             pj_ansi_sprintf(s, "#%d ", info.listeners[j]); 
    3786             pj_ansi_strcat(txlist, s); 
    3787         } 
    3788         printf("Port #%02d[%2dKHz/%dms/%d] %20.*s  transmitting to: %s\n",  
    3789                info.slot_id,  
    3790                info.clock_rate/1000, 
    3791                info.samples_per_frame*1000/info.channel_count/info.clock_rate, 
    3792                info.channel_count, 
    3793                (int)info.name.slen,  
    3794                info.name.ptr, 
    3795                txlist); 
    3796  
    3797     } 
    3798     puts(""); 
    3799 } 
    3800  
    3801  
    3802 /* 
    3803  * Send arbitrary request to remote host 
    3804  */ 
    3805 static void send_request(char *cstr_method, const pj_str_t *dst_uri) 
    3806 { 
    3807     pj_str_t str_method; 
    3808     pjsip_method method; 
    3809     pjsip_tx_data *tdata; 
    3810     pjsip_endpoint *endpt; 
    3811     pj_status_t status; 
    3812  
    3813     endpt = pjsua_get_pjsip_endpt(); 
    3814  
    3815     str_method = pj_str(cstr_method); 
    3816     pjsip_method_init_np(&method, &str_method); 
    3817  
    3818     status = pjsua_acc_create_request(current_acc, &method, dst_uri, &tdata); 
    3819  
    3820     status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL); 
    3821     if (status != PJ_SUCCESS) { 
    3822         pjsua_perror(THIS_FILE, "Unable to send request", status); 
    3823         return; 
    3824     } 
    3825 } 
    3826  
    3827  
    3828 /* 
    3829  * Change extended online status. 
    3830  */ 
    3831 static void change_online_status(void) 
    3832 { 
    3833     char menuin[32]; 
    3834     pj_bool_t online_status; 
    3835     pjrpid_element elem; 
    3836     int i, choice; 
    3837  
    3838     enum { 
    3839         AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX 
    3840     }; 
    3841  
    3842     struct opt { 
    3843         int id; 
    3844         char *name; 
    3845     } opts[] = { 
    3846         { AVAILABLE, "Available" }, 
    3847         { BUSY, "Busy"}, 
    3848         { OTP, "On the phone"}, 
    3849         { IDLE, "Idle"}, 
    3850         { AWAY, "Away"}, 
    3851         { BRB, "Be right back"}, 
    3852         { OFFLINE, "Offline"} 
    3853     }; 
    3854  
    3855     printf("\n" 
    3856            "Choices:\n"); 
    3857     for (i=0; i<PJ_ARRAY_SIZE(opts); ++i) { 
    3858         printf("  %d  %s\n", opts[i].id+1, opts[i].name); 
    3859     } 
    3860  
    3861     if (!simple_input("Select status", menuin, sizeof(menuin))) 
    3862         return; 
    3863  
    3864     choice = atoi(menuin) - 1; 
    3865     if (choice < 0 || choice >= OPT_MAX) { 
    3866         puts("Invalid selection"); 
    3867         return; 
    3868     } 
    3869  
    3870     pj_bzero(&elem, sizeof(elem)); 
    3871     elem.type = PJRPID_ELEMENT_TYPE_PERSON; 
    3872  
    3873     online_status = PJ_TRUE; 
    3874  
    3875     switch (choice) { 
    3876     case AVAILABLE: 
    3877         break; 
    3878     case BUSY: 
    3879         elem.activity = PJRPID_ACTIVITY_BUSY; 
    3880         elem.note = pj_str("Busy"); 
    3881         break; 
    3882     case OTP: 
    3883         elem.activity = PJRPID_ACTIVITY_BUSY; 
    3884         elem.note = pj_str("On the phone"); 
    3885         break; 
    3886     case IDLE: 
    3887         elem.activity = PJRPID_ACTIVITY_UNKNOWN; 
    3888         elem.note = pj_str("Idle"); 
    3889         break; 
    3890     case AWAY: 
    3891         elem.activity = PJRPID_ACTIVITY_AWAY; 
    3892         elem.note = pj_str("Away"); 
    3893         break; 
    3894     case BRB: 
    3895         elem.activity = PJRPID_ACTIVITY_UNKNOWN; 
    3896         elem.note = pj_str("Be right back"); 
    3897         break; 
    3898     case OFFLINE: 
    3899         online_status = PJ_FALSE; 
    3900         break; 
    3901     } 
    3902  
    3903     pjsua_acc_set_online_status2(current_acc, online_status, &elem); 
    3904 } 
    3905  
    3906  
    3907 /* 
    3908  * Change codec priorities. 
    3909  */ 
    3910 static void manage_codec_prio(void) 
    3911 { 
    3912     pjsua_codec_info c[32]; 
    3913     unsigned i, count = PJ_ARRAY_SIZE(c); 
    3914     char input[32]; 
    3915     char *codec, *prio; 
    3916     pj_str_t id; 
    3917     int new_prio; 
    3918     pj_status_t status; 
    3919  
    3920     printf("List of audio codecs:\n"); 
    3921     pjsua_enum_codecs(c, &count); 
    3922     for (i=0; i<count; ++i) { 
    3923         printf("  %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen, 
    3924                                c[i].codec_id.ptr); 
    3925     } 
    3926  
    3927 #if PJSUA_HAS_VIDEO 
    3928     puts(""); 
    3929     printf("List of video codecs:\n"); 
    3930     pjsua_vid_enum_codecs(c, &count); 
    3931     for (i=0; i<count; ++i) { 
    3932         printf("  %d\t%.*s%s%.*s\n", c[i].priority, 
    3933                                      (int)c[i].codec_id.slen, 
    3934                                      c[i].codec_id.ptr, 
    3935                                      c[i].desc.slen? " - ":"", 
    3936                                      (int)c[i].desc.slen, 
    3937                                      c[i].desc.ptr); 
    3938     } 
    3939 #endif 
    3940  
    3941     puts(""); 
    3942     puts("Enter codec id and its new priority (e.g. \"speex/16000 200\", ""\"H263 200\"),"); 
    3943     puts("or empty to cancel."); 
    3944  
    3945     printf("Codec name (\"*\" for all) and priority: "); 
    3946     if (fgets(input, sizeof(input), stdin) == NULL) 
    3947         return; 
    3948     if (input[0]=='\r' || input[0]=='\n') { 
    3949         puts("Done"); 
    3950         return; 
    3951     } 
    3952  
    3953     codec = strtok(input, " \t\r\n"); 
    3954     prio = strtok(NULL, " \r\n"); 
    3955  
    3956     if (!codec || !prio) { 
    3957         puts("Invalid input"); 
    3958         return; 
    3959     } 
    3960  
    3961     new_prio = atoi(prio); 
    3962     if (new_prio < 0)  
    3963         new_prio = 0; 
    3964     else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST)  
    3965         new_prio = PJMEDIA_CODEC_PRIO_HIGHEST; 
    3966  
    3967     status = pjsua_codec_set_priority(pj_cstr(&id, codec),  
    3968                                       (pj_uint8_t)new_prio); 
    3969 #if PJSUA_HAS_VIDEO 
    3970     if (status != PJ_SUCCESS) { 
    3971         status = pjsua_vid_codec_set_priority(pj_cstr(&id, codec),  
    3972                                               (pj_uint8_t)new_prio); 
    3973     } 
    3974 #endif 
    3975     if (status != PJ_SUCCESS) 
    3976         pjsua_perror(THIS_FILE, "Error setting codec priority", status); 
    3977 } 
    3978  
    3979  
    3980 #if PJSUA_HAS_VIDEO 
    3981 static void vid_print_dev(int id, const pjmedia_vid_dev_info *vdi, 
    3982                           const char *title) 
    3983 { 
    3984     char capnames[120]; 
    3985     char formats[120]; 
    3986     const char *dirname; 
    3987     unsigned i; 
    3988  
    3989     if (vdi->dir == PJMEDIA_DIR_CAPTURE_RENDER) { 
    3990         dirname = "capture, render"; 
    3991     } else if (vdi->dir == PJMEDIA_DIR_CAPTURE) { 
    3992         dirname = "capture"; 
    3993     } else { 
    3994         dirname = "render"; 
    3995     } 
    3996  
    3997  
    3998     capnames[0] = '\0'; 
    3999     for (i=0; i<sizeof(int)*8 && (1 << i) < PJMEDIA_VID_DEV_CAP_MAX; ++i) { 
    4000         if (vdi->caps & (1 << i)) { 
    4001             const char *capname = pjmedia_vid_dev_cap_name(1 << i, NULL); 
    4002             if (capname) { 
    4003                 if (*capnames) 
    4004                     strcat(capnames, ", "); 
    4005                 strncat(capnames, capname, 
    4006                         sizeof(capnames)-strlen(capnames)-1); 
    4007             } 
    4008         } 
    4009     } 
    4010  
    4011     formats[0] = '\0'; 
    4012     for (i=0; i<vdi->fmt_cnt; ++i) { 
    4013         const pjmedia_video_format_info *vfi = 
    4014                 pjmedia_get_video_format_info(NULL, vdi->fmt[i].id); 
    4015         if (vfi) { 
    4016             if (*formats) 
    4017                 strcat(formats, ", "); 
    4018             strncat(formats, vfi->name, sizeof(formats)-strlen(formats)-1); 
    4019         } 
    4020     } 
    4021  
    4022     PJ_LOG(3,(THIS_FILE, "%3d %s [%s][%s] %s", id, vdi->name, vdi->driver, 
    4023               dirname, title)); 
    4024     PJ_LOG(3,(THIS_FILE, "    Supported capabilities: %s", capnames)); 
    4025     PJ_LOG(3,(THIS_FILE, "    Supported formats: %s", formats)); 
    4026 } 
    4027  
    4028 static void vid_list_devs(void) 
    4029 { 
    4030     unsigned i, count; 
    4031     pjmedia_vid_dev_info vdi; 
    4032     pj_status_t status; 
    4033  
    4034     PJ_LOG(3,(THIS_FILE, "Video device list:")); 
    4035     count = pjsua_vid_dev_count(); 
    4036     if (count == 0) { 
    4037         PJ_LOG(3,(THIS_FILE, " - no device detected -")); 
    4038         return; 
    4039     } else { 
    4040         PJ_LOG(3,(THIS_FILE, "%d device(s) detected:", count)); 
    4041     } 
    4042  
    4043     status = pjsua_vid_dev_get_info(PJMEDIA_VID_DEFAULT_RENDER_DEV, &vdi); 
    4044     if (status == PJ_SUCCESS) 
    4045         vid_print_dev(PJMEDIA_VID_DEFAULT_RENDER_DEV, &vdi, 
    4046                       "(default renderer device)"); 
    4047  
    4048     status = pjsua_vid_dev_get_info(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &vdi); 
    4049     if (status == PJ_SUCCESS) 
    4050         vid_print_dev(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &vdi, 
    4051                       "(default capture device)"); 
    4052  
    4053     for (i=0; i<count; ++i) { 
    4054         status = pjsua_vid_dev_get_info(i, &vdi); 
    4055         if (status == PJ_SUCCESS) 
    4056             vid_print_dev(i, &vdi, ""); 
    4057     } 
    4058 } 
    4059  
    4060 static void app_config_init_video(pjsua_acc_config *acc_cfg) 
    4061 { 
    4062     acc_cfg->vid_in_auto_show = app_config.vid.in_auto_show; 
    4063     acc_cfg->vid_out_auto_transmit = app_config.vid.out_auto_transmit; 
    4064     /* Note that normally GUI application will prefer a borderless 
    4065      * window. 
    4066      */ 
    4067     acc_cfg->vid_wnd_flags = PJMEDIA_VID_DEV_WND_BORDER | 
    4068                              PJMEDIA_VID_DEV_WND_RESIZABLE; 
    4069     acc_cfg->vid_cap_dev = app_config.vid.vcapture_dev; 
    4070     acc_cfg->vid_rend_dev = app_config.vid.vrender_dev; 
    4071  
    4072     if (app_config.avi_auto_play && 
    4073         app_config.avi_def_idx != PJSUA_INVALID_ID && 
    4074         app_config.avi[app_config.avi_def_idx].dev_id != PJMEDIA_VID_INVALID_DEV) 
    4075     { 
    4076         acc_cfg->vid_cap_dev = app_config.avi[app_config.avi_def_idx].dev_id; 
    4077     } 
    4078 } 
    4079  
    4080 static void app_config_show_video(int acc_id, const pjsua_acc_config *acc_cfg) 
    4081 { 
    4082     PJ_LOG(3,(THIS_FILE, 
    4083               "Account %d:\n" 
    4084               "  RX auto show:     %d\n" 
    4085               "  TX auto transmit: %d\n" 
    4086               "  Capture dev:      %d\n" 
    4087               "  Render dev:       %d", 
    4088               acc_id, 
    4089               acc_cfg->vid_in_auto_show, 
    4090               acc_cfg->vid_out_auto_transmit, 
    4091               acc_cfg->vid_cap_dev, 
    4092               acc_cfg->vid_rend_dev)); 
    4093 } 
    4094  
    4095 static void vid_handle_menu(char *menuin) 
    4096 { 
    4097     char *argv[8]; 
    4098     int argc = 0; 
    4099  
    4100     /* Tokenize */ 
    4101     argv[argc] = strtok(menuin, " \t\r\n"); 
    4102     while (argv[argc] && *argv[argc]) { 
    4103         argc++; 
    4104         argv[argc] = strtok(NULL, " \t\r\n"); 
    4105     } 
    4106  
    4107     if (argc == 1 || strcmp(argv[1], "help")==0) { 
    4108         vid_show_help(); 
    4109     } else if (argc == 2 && (strcmp(argv[1], "enable")==0 || 
    4110                              strcmp(argv[1], "disable")==0)) 
    4111     { 
    4112         pj_bool_t enabled = (strcmp(argv[1], "enable")==0); 
    4113         app_config.vid.vid_cnt = (enabled ? 1 : 0); 
    4114         PJ_LOG(3,(THIS_FILE, "Video will be %s in next offer/answer", 
    4115                   (enabled?"enabled":"disabled"))); 
    4116     } else if (strcmp(argv[1], "acc")==0) { 
    4117         pjsua_acc_config acc_cfg; 
    4118         pj_bool_t changed = PJ_FALSE; 
    4119  
    4120         pjsua_acc_get_config(current_acc, &acc_cfg); 
    4121  
    4122         if (argc == 3 && strcmp(argv[2], "show")==0) { 
    4123             app_config_show_video(current_acc, &acc_cfg); 
    4124         } else if (argc == 4 && strcmp(argv[2], "autorx")==0) { 
    4125             int on = (strcmp(argv[3], "on")==0); 
    4126             acc_cfg.vid_in_auto_show = on; 
    4127             changed = PJ_TRUE; 
    4128         } else if (argc == 4 && strcmp(argv[2], "autotx")==0) { 
    4129             int on = (strcmp(argv[3], "on")==0); 
    4130             acc_cfg.vid_out_auto_transmit = on; 
    4131             changed = PJ_TRUE; 
    4132         } else if (argc == 4 && strcmp(argv[2], "cap")==0) { 
    4133             int dev = atoi(argv[3]); 
    4134             acc_cfg.vid_cap_dev = dev; 
    4135             changed = PJ_TRUE; 
    4136         } else if (argc == 4 && strcmp(argv[2], "rend")==0) { 
    4137             int dev = atoi(argv[3]); 
    4138             acc_cfg.vid_rend_dev = dev; 
    4139             changed = PJ_TRUE; 
    4140         } else { 
    4141             goto on_error; 
    4142         } 
    4143  
    4144         if (changed) { 
    4145             pj_status_t status = pjsua_acc_modify(current_acc, &acc_cfg); 
    4146             if (status != PJ_SUCCESS) 
    4147                 PJ_PERROR(1,(THIS_FILE, status, "Error modifying account %d", 
    4148                              current_acc)); 
    4149         } 
    4150  
    4151     } else if (strcmp(argv[1], "call")==0) { 
    4152         pjsua_call_vid_strm_op_param param; 
    4153         pj_status_t status = PJ_SUCCESS; 
    4154  
    4155         pjsua_call_vid_strm_op_param_default(&param); 
    4156  
    4157         if (argc == 5 && strcmp(argv[2], "rx")==0) { 
    4158             pjsua_stream_info si; 
    4159             pj_bool_t on = (strcmp(argv[3], "on") == 0); 
    4160  
    4161             param.med_idx = atoi(argv[4]); 
    4162             if (pjsua_call_get_stream_info(current_call, param.med_idx, &si) || 
    4163                 si.type != PJMEDIA_TYPE_VIDEO) 
    4164             { 
    4165                 PJ_PERROR(1,(THIS_FILE, PJ_EINVAL, "Invalid stream")); 
    4166                 return; 
    4167             } 
    4168  
    4169             if (on) param.dir = (si.info.vid.dir | PJMEDIA_DIR_DECODING); 
    4170             else param.dir = (si.info.vid.dir & PJMEDIA_DIR_ENCODING); 
    4171  
    4172             status = pjsua_call_set_vid_strm(current_call, 
    4173                                              PJSUA_CALL_VID_STRM_CHANGE_DIR, 
    4174                                              &param); 
    4175         } 
    4176         else if (argc == 5 && strcmp(argv[2], "tx")==0) { 
    4177             pj_bool_t on = (strcmp(argv[3], "on") == 0); 
    4178             pjsua_call_vid_strm_op op = on? PJSUA_CALL_VID_STRM_START_TRANSMIT : 
    4179                                             PJSUA_CALL_VID_STRM_STOP_TRANSMIT; 
    4180  
    4181             param.med_idx = atoi(argv[4]); 
    4182  
    4183             status = pjsua_call_set_vid_strm(current_call, op, &param); 
    4184         } 
    4185         else if (argc == 3 && strcmp(argv[2], "add")==0) { 
    4186             status = pjsua_call_set_vid_strm(current_call, 
    4187                                              PJSUA_CALL_VID_STRM_ADD, NULL); 
    4188         } 
    4189         else if (argc >= 3 &&  
    4190                  (strcmp(argv[2], "disable")==0 || strcmp(argv[2], "enable")==0)) 
    4191         { 
    4192             pj_bool_t enable = (strcmp(argv[2], "enable") == 0); 
    4193             pjsua_call_vid_strm_op op = enable? PJSUA_CALL_VID_STRM_CHANGE_DIR : 
    4194                                                 PJSUA_CALL_VID_STRM_REMOVE; 
    4195  
    4196             param.med_idx = argc >= 4? atoi(argv[3]) : -1; 
    4197             param.dir = PJMEDIA_DIR_ENCODING_DECODING; 
    4198             status = pjsua_call_set_vid_strm(current_call, op, &param); 
    4199         } 
    4200         else if (argc >= 3 && strcmp(argv[2], "cap")==0) { 
    4201             param.med_idx = argc >= 4? atoi(argv[3]) : -1; 
    4202             param.cap_dev = argc >= 5? atoi(argv[4]) : PJMEDIA_VID_DEFAULT_CAPTURE_DEV; 
    4203             status = pjsua_call_set_vid_strm(current_call, 
    4204                                              PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV, 
    4205                                              &param); 
    4206         } else 
    4207             goto on_error; 
    4208  
    4209         if (status != PJ_SUCCESS) { 
    4210             PJ_PERROR(1,(THIS_FILE, status, "Error modifying video stream")); 
    4211         } 
    4212  
    4213     } else if (argc >= 3 && strcmp(argv[1], "dev")==0) { 
    4214         if (strcmp(argv[2], "list")==0) { 
    4215             vid_list_devs(); 
    4216         } else if (strcmp(argv[2], "refresh")==0) { 
    4217             pjmedia_vid_dev_refresh(); 
    4218         } else if (strcmp(argv[2], "prev")==0) { 
    4219             if (argc != 5) { 
    4220                 goto on_error; 
    4221             } else { 
    4222                 pj_bool_t on = (strcmp(argv[3], "on") == 0); 
    4223                 int dev_id = atoi(argv[4]); 
    4224                 if (on) { 
    4225                     pjsua_vid_preview_param param; 
    4226  
    4227                     pjsua_vid_preview_param_default(&param); 
    4228                     param.wnd_flags = PJMEDIA_VID_DEV_WND_BORDER | 
    4229                                       PJMEDIA_VID_DEV_WND_RESIZABLE; 
    4230                     pjsua_vid_preview_start(dev_id, &param); 
    4231                     arrange_window(pjsua_vid_preview_get_win(dev_id)); 
    4232                 } else { 
    4233                     pjsua_vid_win_id wid; 
    4234                     wid = pjsua_vid_preview_get_win(dev_id); 
    4235                     if (wid != PJSUA_INVALID_ID) { 
    4236                         /* Preview window hiding once it is stopped is 
    4237                          * responsibility of app */ 
    4238                         pjsua_vid_win_set_show(wid, PJ_FALSE); 
    4239                         pjsua_vid_preview_stop(dev_id); 
    4240                     } 
    4241                 } 
    4242             } 
    4243         } else 
    4244             goto on_error; 
    4245     } else if (strcmp(argv[1], "win")==0) { 
    4246         pj_status_t status = PJ_SUCCESS; 
    4247  
    4248         if (argc==3 && strcmp(argv[2], "list")==0) { 
    4249             pjsua_vid_win_id wids[PJSUA_MAX_VID_WINS]; 
    4250             unsigned i, cnt = PJ_ARRAY_SIZE(wids); 
    4251  
    4252             pjsua_vid_enum_wins(wids, &cnt); 
    4253  
    4254             PJ_LOG(3,(THIS_FILE, "Found %d video windows:", cnt)); 
    4255             PJ_LOG(3,(THIS_FILE, "WID show    pos       size")); 
    4256             PJ_LOG(3,(THIS_FILE, "------------------------------")); 
    4257             for (i = 0; i < cnt; ++i) { 
    4258                 pjsua_vid_win_info wi; 
    4259                 pjsua_vid_win_get_info(wids[i], &wi); 
    4260                 PJ_LOG(3,(THIS_FILE, "%3d   %c  (%d,%d)  %dx%d", 
    4261                           wids[i], (wi.show?'Y':'N'), wi.pos.x, wi.pos.y, 
    4262                           wi.size.w, wi.size.h)); 
    4263             } 
    4264         } else if (argc==4 && (strcmp(argv[2], "show")==0 || 
    4265                                strcmp(argv[2], "hide")==0)) 
    4266         { 
    4267             pj_bool_t show = (strcmp(argv[2], "show")==0); 
    4268             pjsua_vid_win_id wid = atoi(argv[3]); 
    4269             status = pjsua_vid_win_set_show(wid, show); 
    4270         } else if (argc==6 && strcmp(argv[2], "move")==0) { 
    4271             pjsua_vid_win_id wid = atoi(argv[3]); 
    4272             pjmedia_coord pos; 
    4273  
    4274             pos.x = atoi(argv[4]); 
    4275             pos.y = atoi(argv[5]); 
    4276             status = pjsua_vid_win_set_pos(wid, &pos); 
    4277         } else if (argc==6 && strcmp(argv[2], "resize")==0) { 
    4278             pjsua_vid_win_id wid = atoi(argv[3]); 
    4279             pjmedia_rect_size size; 
    4280  
    4281             size.w = atoi(argv[4]); 
    4282             size.h = atoi(argv[5]); 
    4283             status = pjsua_vid_win_set_size(wid, &size); 
    4284         } else if (argc==3 && strcmp(argv[2], "arrange")==0) { 
    4285             arrange_window(PJSUA_INVALID_ID); 
    4286         } else 
    4287             goto on_error; 
    4288  
    4289         if (status != PJ_SUCCESS) { 
    4290             PJ_PERROR(1,(THIS_FILE, status, "Window operation error")); 
    4291         } 
    4292  
    4293     } else if (strcmp(argv[1], "codec")==0) { 
    4294         pjsua_codec_info ci[PJMEDIA_CODEC_MGR_MAX_CODECS]; 
    4295         unsigned count = PJ_ARRAY_SIZE(ci); 
    4296         pj_status_t status; 
    4297  
    4298         if (argc==3 && strcmp(argv[2], "list")==0) { 
    4299             status = pjsua_vid_enum_codecs(ci, &count); 
    4300             if (status != PJ_SUCCESS) { 
    4301                 PJ_PERROR(1,(THIS_FILE, status, "Error enumerating codecs")); 
    4302             } else { 
    4303                 unsigned i; 
    4304                 PJ_LOG(3,(THIS_FILE, "Found %d video codecs:", count)); 
    4305                 PJ_LOG(3,(THIS_FILE, "codec id      prio  fps    bw(kbps)   size")); 
    4306                 PJ_LOG(3,(THIS_FILE, "------------------------------------------")); 
    4307                 for (i=0; i<count; ++i) { 
    4308                     pjmedia_vid_codec_param cp; 
    4309                     pjmedia_video_format_detail *vfd; 
    4310  
    4311                     status = pjsua_vid_codec_get_param(&ci[i].codec_id, &cp); 
    4312                     if (status != PJ_SUCCESS) 
    4313                         continue; 
    4314  
    4315                     vfd = pjmedia_format_get_video_format_detail(&cp.enc_fmt, 
    4316                                                                  PJ_TRUE); 
    4317                     PJ_LOG(3,(THIS_FILE, "%.*s%.*s %3d %7.2f  %4d/%4d  %dx%d",  
    4318                               (int)ci[i].codec_id.slen, ci[i].codec_id.ptr, 
    4319                               13-(int)ci[i].codec_id.slen, "                ", 
    4320                               ci[i].priority, 
    4321                               (vfd->fps.num*1.0/vfd->fps.denum), 
    4322                               vfd->avg_bps/1000, vfd->max_bps/1000, 
    4323                               vfd->size.w, vfd->size.h)); 
    4324                 } 
    4325             } 
    4326         } else if (argc==5 && strcmp(argv[2], "prio")==0) { 
    4327             pj_str_t cid; 
    4328             int prio; 
    4329             cid = pj_str(argv[3]); 
    4330             prio = atoi(argv[4]); 
    4331             status = pjsua_vid_codec_set_priority(&cid, (pj_uint8_t)prio); 
    4332             if (status != PJ_SUCCESS) 
    4333                 PJ_PERROR(1,(THIS_FILE, status, "Set codec priority error")); 
    4334         } else if (argc==6 && strcmp(argv[2], "fps")==0) { 
    4335             pjmedia_vid_codec_param cp; 
    4336             pj_str_t cid; 
    4337             int M, N; 
    4338             cid = pj_str(argv[3]); 
    4339             M = atoi(argv[4]); 
    4340             N = atoi(argv[5]); 
    4341             status = pjsua_vid_codec_get_param(&cid, &cp); 
    4342             if (status == PJ_SUCCESS) { 
    4343                 cp.enc_fmt.det.vid.fps.num = M; 
    4344                 cp.enc_fmt.det.vid.fps.denum = N; 
    4345                 status = pjsua_vid_codec_set_param(&cid, &cp); 
    4346             } 
    4347             if (status != PJ_SUCCESS) 
    4348                 PJ_PERROR(1,(THIS_FILE, status, "Set codec framerate error")); 
    4349         } else if (argc==6 && strcmp(argv[2], "bw")==0) { 
    4350             pjmedia_vid_codec_param cp; 
    4351             pj_str_t cid; 
    4352             int M, N; 
    4353             cid = pj_str(argv[3]); 
    4354             M = atoi(argv[4]); 
    4355             N = atoi(argv[5]); 
    4356             status = pjsua_vid_codec_get_param(&cid, &cp); 
    4357             if (status == PJ_SUCCESS) { 
    4358                 cp.enc_fmt.det.vid.avg_bps = M * 1000; 
    4359                 cp.enc_fmt.det.vid.max_bps = N * 1000; 
    4360                 status = pjsua_vid_codec_set_param(&cid, &cp); 
    4361             } 
    4362             if (status != PJ_SUCCESS) 
    4363                 PJ_PERROR(1,(THIS_FILE, status, "Set codec bitrate error")); 
    4364         } else if (argc==6 && strcmp(argv[2], "size")==0) { 
    4365             pjmedia_vid_codec_param cp; 
    4366             pj_str_t cid; 
    4367             int M, N; 
    4368             cid = pj_str(argv[3]); 
    4369             M = atoi(argv[4]); 
    4370             N = atoi(argv[5]); 
    4371             status = pjsua_vid_codec_get_param(&cid, &cp); 
    4372             if (status == PJ_SUCCESS) { 
    4373                 cp.enc_fmt.det.vid.size.w = M; 
    4374                 cp.enc_fmt.det.vid.size.h = N; 
    4375                 status = pjsua_vid_codec_set_param(&cid, &cp); 
    4376             } 
    4377             if (status != PJ_SUCCESS) 
    4378                 PJ_PERROR(1,(THIS_FILE, status, "Set codec size error")); 
    4379         } else 
    4380             goto on_error; 
    4381     } else 
    4382         goto on_error; 
    4383  
    4384     return; 
    4385  
    4386 on_error: 
    4387     PJ_LOG(1,(THIS_FILE, "Invalid command, use 'vid help'")); 
    4388 } 
    4389  
    4390 #else 
    4391  
    4392 static void app_config_init_video(pjsua_acc_config *acc_cfg) 
    4393 { 
    4394     PJ_UNUSED_ARG(acc_cfg); 
    4395 } 
    4396  
    4397 #endif /* PJSUA_HAS_VIDEO */ 
    4398  
    4399  
    4400 /* 
    4401  * Main "user interface" loop. 
    4402  */ 
    4403 void console_app_main(const pj_str_t *uri_to_call) 
    4404 { 
    4405     char menuin[80]; 
    4406     char buf[128]; 
    4407     char text[128]; 
    4408     int i, count; 
    4409     char *uri; 
    4410     pj_str_t tmp; 
    4411     struct input_result result; 
    4412     pjsua_msg_data msg_data; 
    4413     pjsua_call_info call_info; 
    4414     pjsua_acc_info acc_info; 
    4415     pjsua_call_setting call_opt; 
    4416  
    4417     pjsua_call_setting_default(&call_opt); 
    4418     call_opt.aud_cnt = app_config.aud_cnt; 
    4419     call_opt.vid_cnt = app_config.vid.vid_cnt; 
    4420  
    4421     /* If user specifies URI to call, then call the URI */ 
    4422     if (uri_to_call->slen) { 
    4423         pjsua_call_make_call( current_acc, uri_to_call, &call_opt, NULL, NULL, NULL); 
    4424     } 
    4425  
    4426     keystroke_help(); 
    4427  
    4428     for (;;) { 
    4429  
    4430         printf(">>> "); 
    4431         fflush(stdout); 
    4432  
    4433         if (fgets(menuin, sizeof(menuin), stdin) == NULL) { 
    4434             /*  
    4435              * Be friendly to users who redirect commands into 
    4436              * program, when file ends, resume with kbd. 
    4437              * If exit is desired end script with q for quit 
    4438              */ 
    4439             /* Reopen stdin/stdout/stderr to /dev/console */ 
    4440 #if defined(PJ_WIN32) && PJ_WIN32!=0 
    4441             if (freopen ("CONIN$", "r", stdin) == NULL) { 
    4442 #else 
    4443             if (1) { 
    4444 #endif 
    4445                 puts("Cannot switch back to console from file redirection"); 
    4446                 menuin[0] = 'q'; 
    4447                 menuin[1] = '\0'; 
    4448             } else { 
    4449                 puts("Switched back to console from file redirection"); 
    4450                 continue; 
    4451             } 
    4452         } 
    4453  
    4454         if (cmd_echo) { 
    4455             printf("%s", menuin); 
    4456         } 
    4457  
    4458         /* Update call setting */ 
    4459         pjsua_call_setting_default(&call_opt); 
    4460         call_opt.aud_cnt = app_config.aud_cnt; 
    4461         call_opt.vid_cnt = app_config.vid.vid_cnt; 
    4462  
    4463         switch (menuin[0]) { 
    4464  
    4465         case 'm': 
    4466             /* Make call! : */ 
    4467             printf("(You currently have %d calls)\n",  
    4468                      pjsua_call_get_count()); 
    4469              
    4470             uri = NULL; 
    4471             ui_input_url("Make call", buf, sizeof(buf), &result); 
    4472             if (result.nb_result != NO_NB) { 
    4473  
    4474                 if (result.nb_result == -1 || result.nb_result == 0) { 
    4475                     puts("You can't do that with make call!"); 
    4476                     continue; 
    4477                 } else { 
    4478                     pjsua_buddy_info binfo; 
    4479                     pjsua_buddy_get_info(result.nb_result-1, &binfo); 
    4480                     tmp.ptr = buf; 
    4481                     pj_strncpy(&tmp, &binfo.uri, sizeof(buf)); 
    4482                 } 
    4483  
    4484             } else if (result.uri_result) { 
    4485                 tmp = pj_str(result.uri_result); 
    4486             } else { 
    4487                 tmp.slen = 0; 
    4488             } 
    4489              
    4490             pjsua_msg_data_init(&msg_data); 
    4491             TEST_MULTIPART(&msg_data); 
    4492             pjsua_call_make_call( current_acc, &tmp, &call_opt, NULL, 
    4493                                   &msg_data, &current_call); 
    4494             break; 
    4495  
    4496         case 'M': 
    4497             /* Make multiple calls! : */ 
    4498             printf("(You currently have %d calls)\n",  
    4499                    pjsua_call_get_count()); 
    4500              
    4501             if (!simple_input("Number of calls", menuin, sizeof(menuin))) 
    4502                 continue; 
    4503  
    4504             count = my_atoi(menuin); 
    4505             if (count < 1) 
    4506                 continue; 
    4507  
    4508             ui_input_url("Make call", buf, sizeof(buf), &result); 
    4509             if (result.nb_result != NO_NB) { 
    4510                 pjsua_buddy_info binfo; 
    4511                 if (result.nb_result == -1 || result.nb_result == 0) { 
    4512                     puts("You can't do that with make call!"); 
    4513                     continue; 
    4514                 } 
    4515                 pjsua_buddy_get_info(result.nb_result-1, &binfo); 
    4516                 tmp.ptr = buf; 
    4517                 pj_strncpy(&tmp, &binfo.uri, sizeof(buf)); 
    4518             } else { 
    4519                 tmp = pj_str(result.uri_result); 
    4520             } 
    4521  
    4522             for (i=0; i<my_atoi(menuin); ++i) { 
    4523                 pj_status_t status; 
    4524              
    4525                 status = pjsua_call_make_call(current_acc, &tmp, &call_opt, NULL, 
    4526                                               NULL, NULL); 
    4527                 if (status != PJ_SUCCESS) 
    4528                     break; 
    4529             } 
    4530             break; 
    4531  
    4532         case 'n': 
    4533             i = pjsua_detect_nat_type(); 
    4534             if (i != PJ_SUCCESS) 
    4535                 pjsua_perror(THIS_FILE, "Error", i); 
    4536             break; 
    4537  
    4538         case 'i': 
    4539             /* Send instant messaeg */ 
    4540  
    4541             /* i is for call index to send message, if any */ 
    4542             i = -1; 
    4543      
    4544             /* Make compiler happy. */ 
    4545             uri = NULL; 
    4546  
    4547             /* Input destination. */ 
    4548             ui_input_url("Send IM to", buf, sizeof(buf), &result); 
    4549             if (result.nb_result != NO_NB) { 
    4550  
    4551                 if (result.nb_result == -1) { 
    4552                     puts("You can't send broadcast IM like that!"); 
    4553                     continue; 
    4554  
    4555                 } else if (result.nb_result == 0) { 
    4556      
    4557                     i = current_call; 
    4558  
    4559                 } else { 
    4560                     pjsua_buddy_info binfo; 
    4561                     pjsua_buddy_get_info(result.nb_result-1, &binfo); 
    4562                     tmp.ptr = buf; 
    4563                     pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf)); 
    4564                     uri = buf; 
    4565                 } 
    4566  
    4567             } else if (result.uri_result) { 
    4568                 uri = result.uri_result; 
    4569             } 
    4570              
    4571  
    4572             /* Send typing indication. */ 
    4573             if (i != -1) 
    4574                 pjsua_call_send_typing_ind(i, PJ_TRUE, NULL); 
    4575             else { 
    4576                 pj_str_t tmp_uri = pj_str(uri); 
    4577                 pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL); 
    4578             } 
    4579  
    4580             /* Input the IM . */ 
    4581             if (!simple_input("Message", text, sizeof(text))) { 
    4582                 /* 
    4583                  * Cancelled. 
    4584                  * Send typing notification too, saying we're not typing. 
    4585                  */ 
    4586                 if (i != -1) 
    4587                     pjsua_call_send_typing_ind(i, PJ_FALSE, NULL); 
    4588                 else { 
    4589                     pj_str_t tmp_uri = pj_str(uri); 
    4590                     pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL); 
    4591                 } 
    4592                 continue; 
    4593             } 
    4594  
    4595             tmp = pj_str(text); 
    4596  
    4597             /* Send the IM */ 
    4598             if (i != -1) 
    4599                 pjsua_call_send_im(i, NULL, &tmp, NULL, NULL); 
    4600             else { 
    4601                 pj_str_t tmp_uri = pj_str(uri); 
    4602                 pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL); 
    4603             } 
    4604  
    4605             break; 
    4606  
    4607         case 'a': 
    4608  
    4609             if (current_call != -1) { 
    4610                 pjsua_call_get_info(current_call, &call_info); 
    4611             } else { 
    4612                 /* Make compiler happy */ 
    4613                 call_info.role = PJSIP_ROLE_UAC; 
    4614                 call_info.state = PJSIP_INV_STATE_DISCONNECTED; 
    4615             } 
    4616  
    4617             if (current_call == -1 ||  
    4618                 call_info.role != PJSIP_ROLE_UAS || 
    4619                 call_info.state >= PJSIP_INV_STATE_CONNECTING) 
    4620             { 
    4621                 puts("No pending incoming call"); 
    4622                 fflush(stdout); 
    4623                 continue; 
    4624  
    4625             } else { 
    4626                 int st_code; 
    4627                 char contact[120]; 
    4628                 pj_str_t hname = { "Contact", 7 }; 
    4629                 pj_str_t hvalue; 
    4630                 pjsip_generic_string_hdr hcontact; 
    4631  
    4632                 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf))) 
    4633                     continue; 
    4634                  
    4635                 st_code = my_atoi(buf); 
    4636                 if (st_code < 100) 
    4637                     continue; 
    4638  
    4639                 pjsua_msg_data_init(&msg_data); 
    4640  
    4641                 if (st_code/100 == 3) { 
    4642                     if (!simple_input("Enter URL to be put in Contact",  
    4643                                       contact, sizeof(contact))) 
    4644                         continue; 
    4645                     hvalue = pj_str(contact); 
    4646                     pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue); 
    4647  
    4648                     pj_list_push_back(&msg_data.hdr_list, &hcontact); 
    4649                 } 
    4650  
    4651                 /* 
    4652                  * Must check again! 
    4653                  * Call may have been disconnected while we're waiting for  
    4654                  * keyboard input. 
    4655                  */ 
    4656                 if (current_call == -1) { 
    4657                     puts("Call has been disconnected"); 
    4658                     fflush(stdout); 
    4659                     continue; 
    4660                 } 
    4661  
    4662                 pjsua_call_answer2(current_call, &call_opt, st_code, NULL, &msg_data); 
    4663             } 
    4664  
    4665             break; 
    4666  
    4667  
    4668         case 'h': 
    4669  
    4670             if (current_call == -1) { 
    4671                 puts("No current call"); 
    4672                 fflush(stdout); 
    4673                 continue; 
    4674  
    4675             } else if (menuin[1] == 'a') { 
    4676                  
    4677                 /* Hangup all calls */ 
    4678                 pjsua_call_hangup_all(); 
    4679  
    4680             } else { 
    4681  
    4682                 /* Hangup current calls */ 
    4683                 pjsua_call_hangup(current_call, 0, NULL, NULL); 
    4684             } 
    4685             break; 
    4686  
    4687         case ']': 
    4688         case '[': 
    4689             /* 
    4690              * Cycle next/prev dialog. 
    4691              */ 
    4692             if (menuin[0] == ']') { 
    4693                 find_next_call(); 
    4694  
    4695             } else { 
    4696                 find_prev_call(); 
    4697             } 
    4698  
    4699             if (current_call != -1) { 
    4700                  
    4701                 pjsua_call_get_info(current_call, &call_info); 
    4702                 PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s",  
    4703                           (int)call_info.remote_info.slen,  
    4704                           call_info.remote_info.ptr)); 
    4705  
    4706             } else { 
    4707                 PJ_LOG(3,(THIS_FILE,"No current dialog")); 
    4708             } 
    4709             break; 
    4710  
    4711  
    4712         case '>': 
    4713         case '<': 
    4714             if (!simple_input("Enter account ID to select", buf, sizeof(buf))) 
    4715                 break; 
    4716  
    4717             i = my_atoi(buf); 
    4718             if (pjsua_acc_is_valid(i)) { 
    4719                 pjsua_acc_set_default(i); 
    4720                 PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i)); 
    4721             } else { 
    4722                 PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i)); 
    4723             } 
    4724             break; 
    4725  
    4726  
    4727         case '+': 
    4728             if (menuin[1] == 'b') { 
    4729              
    4730                 pjsua_buddy_config buddy_cfg; 
    4731                 pjsua_buddy_id buddy_id; 
    4732                 pj_status_t status; 
    4733  
    4734                 if (!simple_input("Enter buddy's URI:", buf, sizeof(buf))) 
    4735                     break; 
    4736  
    4737                 if (pjsua_verify_url(buf) != PJ_SUCCESS) { 
    4738                     printf("Invalid URI '%s'\n", buf); 
    4739                     break; 
    4740                 } 
    4741  
    4742                 pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config)); 
    4743  
    4744                 buddy_cfg.uri = pj_str(buf); 
    4745                 buddy_cfg.subscribe = PJ_TRUE; 
    4746  
    4747                 status = pjsua_buddy_add(&buddy_cfg, &buddy_id); 
    4748                 if (status == PJ_SUCCESS) { 
    4749                     printf("New buddy '%s' added at index %d\n", 
    4750                            buf, buddy_id+1); 
    4751                 } 
    4752  
    4753             } else if (menuin[1] == 'a') { 
    4754  
    4755                 char id[80], registrar[80], realm[80], uname[80], passwd[30]; 
    4756                 pjsua_acc_config acc_cfg; 
    4757                 pj_status_t status; 
    4758  
    4759                 if (!simple_input("Your SIP URL:", id, sizeof(id))) 
    4760                     break; 
    4761                 if (!simple_input("URL of the registrar:", registrar, sizeof(registrar))) 
    4762                     break; 
    4763                 if (!simple_input("Auth Realm:", realm, sizeof(realm))) 
    4764                     break; 
    4765                 if (!simple_input("Auth Username:", uname, sizeof(uname))) 
    4766                     break; 
    4767                 if (!simple_input("Auth Password:", passwd, sizeof(passwd))) 
    4768                     break; 
    4769  
    4770                 pjsua_acc_config_default(&acc_cfg); 
    4771                 acc_cfg.id = pj_str(id); 
    4772                 acc_cfg.reg_uri = pj_str(registrar); 
    4773                 acc_cfg.cred_count = 1; 
    4774                 acc_cfg.cred_info[0].scheme = pj_str("Digest"); 
    4775                 acc_cfg.cred_info[0].realm = pj_str(realm); 
    4776                 acc_cfg.cred_info[0].username = pj_str(uname); 
    4777                 acc_cfg.cred_info[0].data_type = 0; 
    4778                 acc_cfg.cred_info[0].data = pj_str(passwd); 
    4779  
    4780                 acc_cfg.rtp_cfg = app_config.rtp_cfg; 
    4781                 app_config_init_video(&acc_cfg); 
    4782  
    4783                 status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL); 
    4784                 if (status != PJ_SUCCESS) { 
    4785                     pjsua_perror(THIS_FILE, "Error adding new account", status); 
    4786                 } 
    4787  
    4788             } else { 
    4789                 printf("Invalid input %s\n", menuin); 
    4790             } 
    4791             break; 
    4792  
    4793         case '-': 
    4794             if (menuin[1] == 'b') { 
    4795                 if (!simple_input("Enter buddy ID to delete",buf,sizeof(buf))) 
    4796                     break; 
    4797  
    4798                 i = my_atoi(buf) - 1; 
    4799  
    4800                 if (!pjsua_buddy_is_valid(i)) { 
    4801                     printf("Invalid buddy id %d\n", i); 
    4802                 } else { 
    4803                     pjsua_buddy_del(i); 
    4804                     printf("Buddy %d deleted\n", i); 
    4805                 } 
    4806  
    4807             } else if (menuin[1] == 'a') { 
    4808  
    4809                 if (!simple_input("Enter account ID to delete",buf,sizeof(buf))) 
    4810                     break; 
    4811  
    4812                 i = my_atoi(buf); 
    4813  
    4814                 if (!pjsua_acc_is_valid(i)) { 
    4815                     printf("Invalid account id %d\n", i); 
    4816                 } else { 
    4817                     pjsua_acc_del(i); 
    4818                     printf("Account %d deleted\n", i); 
    4819                 } 
    4820  
    4821             } else { 
    4822                 printf("Invalid input %s\n", menuin); 
    4823             } 
    4824             break; 
    4825  
    4826         case 'H': 
    4827             /* 
    4828              * Hold call. 
    4829              */ 
    4830             if (current_call != -1) { 
    4831                  
    4832                 pjsua_call_set_hold(current_call, NULL); 
    4833  
    4834             } else { 
    4835                 PJ_LOG(3,(THIS_FILE, "No current call")); 
    4836             } 
    4837             break; 
    4838  
    4839         case 'v': 
    4840 #if PJSUA_HAS_VIDEO 
    4841             if (menuin[1]=='i' && menuin[2]=='d' && menuin[3]==' ') { 
    4842  
    4843                 vid_handle_menu(menuin); 
    4844  
    4845             } else 
    4846 #endif 
    4847             if (current_call != -1) { 
    4848                 /* 
    4849                  * re-INVITE 
    4850                  */ 
    4851                 call_opt.flag |= PJSUA_CALL_UNHOLD; 
    4852                 pjsua_call_reinvite2(current_call, &call_opt, NULL); 
    4853  
    4854             } else { 
    4855                 PJ_LOG(3,(THIS_FILE, "No current call")); 
    4856             } 
    4857             break; 
    4858  
    4859         case 'U': 
    4860             /* 
    4861              * Send UPDATE 
    4862              */ 
    4863             if (current_call != -1) { 
    4864                  
    4865                 pjsua_call_update2(current_call, &call_opt, NULL); 
    4866  
    4867             } else { 
    4868                 PJ_LOG(3,(THIS_FILE, "No current call")); 
    4869             } 
    4870             break; 
    4871  
    4872         case 'C': 
    4873             if (menuin[1] == 'p') { 
    4874                 manage_codec_prio(); 
    4875             } 
    4876             break; 
    4877  
    4878         case 'x': 
    4879             /* 
    4880              * Transfer call. 
    4881              */ 
    4882             if (current_call == -1) { 
    4883                  
    4884                 PJ_LOG(3,(THIS_FILE, "No current call")); 
    4885  
    4886             } else { 
    4887                 int call = current_call; 
    4888                 pjsip_generic_string_hdr refer_sub; 
    4889                 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 }; 
    4890                 pj_str_t STR_FALSE = { "false", 5 }; 
    4891                 pjsua_call_info ci; 
    4892  
    4893                 pjsua_call_get_info(current_call, &ci); 
    4894                 printf("Transfering current call [%d] %.*s\n", 
    4895                        current_call, 
    4896                        (int)ci.remote_info.slen, ci.remote_info.ptr); 
    4897  
    4898                 ui_input_url("Transfer to URL", buf, sizeof(buf), &result); 
    4899  
    4900                 /* Check if call is still there. */ 
    4901  
    4902                 if (call != current_call) { 
    4903                     puts("Call has been disconnected"); 
    4904                     continue; 
    4905                 } 
    4906  
    4907                 pjsua_msg_data_init(&msg_data); 
    4908                 if (app_config.no_refersub) { 
    4909                     /* Add Refer-Sub: false in outgoing REFER request */ 
    4910                     pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB, 
    4911                                                    &STR_FALSE); 
    4912                     pj_list_push_back(&msg_data.hdr_list, &refer_sub); 
    4913                 } 
    4914                 if (result.nb_result != NO_NB) { 
    4915                     if (result.nb_result == -1 || result.nb_result == 0) 
    4916                         puts("You can't do that with transfer call!"); 
    4917                     else { 
    4918                         pjsua_buddy_info binfo; 
    4919                         pjsua_buddy_get_info(result.nb_result-1, &binfo); 
    4920                         pjsua_call_xfer( current_call, &binfo.uri, &msg_data); 
    4921                     } 
    4922  
    4923                 } else if (result.uri_result) { 
    4924                     pj_str_t tmp; 
    4925                     tmp = pj_str(result.uri_result); 
    4926                     pjsua_call_xfer( current_call, &tmp, &msg_data); 
    4927                 } 
    4928             } 
    4929             break; 
    4930  
    4931         case 'X': 
    4932             /* 
    4933              * Transfer call with replaces. 
    4934              */ 
    4935             if (current_call == -1) { 
    4936                  
    4937                 PJ_LOG(3,(THIS_FILE, "No current call")); 
    4938  
    4939             } else { 
    4940                 int call = current_call; 
    4941                 int dst_call; 
    4942                 pjsip_generic_string_hdr refer_sub; 
    4943                 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 }; 
    4944                 pj_str_t STR_FALSE = { "false", 5 }; 
    4945                 pjsua_call_id ids[PJSUA_MAX_CALLS]; 
    4946                 pjsua_call_info ci; 
    4947                 unsigned i, count; 
    4948  
    4949                 count = PJ_ARRAY_SIZE(ids); 
    4950                 pjsua_enum_calls(ids, &count); 
    4951  
    4952                 if (count <= 1) { 
    4953                     puts("There are no other calls"); 
    4954                     continue; 
    4955                 } 
    4956  
    4957                 pjsua_call_get_info(current_call, &ci); 
    4958                 printf("Transfer call [%d] %.*s to one of the following:\n", 
    4959                        current_call, 
    4960                        (int)ci.remote_info.slen, ci.remote_info.ptr); 
    4961  
    4962                 for (i=0; i<count; ++i) { 
    4963                     pjsua_call_info call_info; 
    4964  
    4965                     if (ids[i] == call) 
    4966                         continue; 
    4967  
    4968                     pjsua_call_get_info(ids[i], &call_info); 
    4969                     printf("%d  %.*s [%.*s]\n", 
    4970                            ids[i], 
    4971                            (int)call_info.remote_info.slen, 
    4972                            call_info.remote_info.ptr, 
    4973                            (int)call_info.state_text.slen, 
    4974                            call_info.state_text.ptr); 
    4975                 } 
    4976  
    4977                 if (!simple_input("Enter call number to be replaced",  
    4978                                   buf, sizeof(buf))) 
    4979                     continue; 
    4980  
    4981                 dst_call = my_atoi(buf); 
    4982  
    4983                 /* Check if call is still there. */ 
    4984  
    4985                 if (call != current_call) { 
    4986                     puts("Call has been disconnected"); 
    4987                     continue; 
    4988                 } 
    4989  
    4990                 /* Check that destination call is valid. */ 
    4991                 if (dst_call == call) { 
    4992                     puts("Destination call number must not be the same " 
    4993                          "as the call being transfered"); 
    4994                     continue; 
    4995                 } 
    4996                 if (dst_call >= PJSUA_MAX_CALLS) { 
    4997                     puts("Invalid destination call number"); 
    4998                     continue; 
    4999                 } 
    5000                 if (!pjsua_call_is_active(dst_call)) { 
    5001                     puts("Invalid destination call number"); 
    5002                     continue; 
    5003                 } 
    5004  
    5005                 pjsua_msg_data_init(&msg_data); 
    5006                 if (app_config.no_refersub) { 
    5007                     /* Add Refer-Sub: false in outgoing REFER request */ 
    5008                     pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB, 
    5009                                                    &STR_FALSE); 
    5010                     pj_list_push_back(&msg_data.hdr_list, &refer_sub); 
    5011                 } 
    5012  
    5013                 pjsua_call_xfer_replaces(call, dst_call,  
    5014                                          PJSUA_XFER_NO_REQUIRE_REPLACES,  
    5015                                          &msg_data); 
    5016             } 
    5017             break; 
    5018  
    5019         case '#': 
    5020             /* 
    5021              * Send DTMF strings. 
    5022              */ 
    5023             if (current_call == -1) { 
    5024                  
    5025                 PJ_LOG(3,(THIS_FILE, "No current call")); 
    5026  
    5027             } else if (!pjsua_call_has_media(current_call)) { 
    5028  
    5029                 PJ_LOG(3,(THIS_FILE, "Media is not established yet!")); 
    5030  
    5031             } else { 
    5032                 pj_str_t digits; 
    5033                 int call = current_call; 
    5034                 pj_status_t status; 
    5035  
    5036                 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,  
    5037                                   sizeof(buf))) 
    5038                 { 
    5039                         break; 
    5040                 } 
    5041  
    5042                 if (call != current_call) { 
    5043                     puts("Call has been disconnected"); 
    5044                     continue; 
    5045                 } 
    5046  
    5047                 digits = pj_str(buf); 
    5048                 status = pjsua_call_dial_dtmf(current_call, &digits); 
    5049                 if (status != PJ_SUCCESS) { 
    5050                     pjsua_perror(THIS_FILE, "Unable to send DTMF", status); 
    5051                 } else { 
    5052                     puts("DTMF digits enqueued for transmission"); 
    5053                 } 
    5054             } 
    5055             break; 
    5056  
    5057         case '*': 
    5058             /* Send DTMF with INFO */ 
    5059             if (current_call == -1) { 
    5060                  
    5061                 PJ_LOG(3,(THIS_FILE, "No current call")); 
    5062  
    5063             } else { 
    5064                 const pj_str_t SIP_INFO = pj_str("INFO"); 
    5065                 pj_str_t digits; 
    5066                 int call = current_call; 
    5067                 int i; 
    5068                 pj_status_t status; 
    5069  
    5070                 if (!simple_input("DTMF strings to send (0-9*#A-B)", buf,  
    5071                                   sizeof(buf))) 
    5072                 { 
    5073                         break; 
    5074                 } 
    5075  
    5076                 if (call != current_call) { 
    5077                     puts("Call has been disconnected"); 
    5078                     continue; 
    5079                 } 
    5080  
    5081                 digits = pj_str(buf); 
    5082                 for (i=0; i<digits.slen; ++i) { 
    5083                     char body[80]; 
    5084  
    5085                     pjsua_msg_data_init(&msg_data); 
    5086                     msg_data.content_type = pj_str("application/dtmf-relay"); 
    5087                      
    5088                     pj_ansi_snprintf(body, sizeof(body), 
    5089                                      "Signal=%c\r\n" 
    5090                                      "Duration=160", 
    5091                                      buf[i]); 
    5092                     msg_data.msg_body = pj_str(body); 
    5093  
    5094                     status = pjsua_call_send_request(current_call, &SIP_INFO,  
    5095                                                      &msg_data); 
    5096                     if (status != PJ_SUCCESS) { 
    5097                         break; 
    5098                     } 
    5099                 } 
    5100             } 
    5101             break; 
    5102  
    5103         case 'S': 
    5104             /* 
    5105              * Send arbitrary request 
    5106              */ 
    5107             if (pjsua_acc_get_count() == 0) { 
    5108                 puts("Sorry, need at least one account configured"); 
    5109                 break; 
    5110             } 
    5111  
    5112             puts("Send arbitrary request to remote host"); 
    5113  
    5114             /* Input METHOD */ 
    5115             if (!simple_input("Request method:",text,sizeof(text))) 
    5116                 break; 
    5117  
    5118             /* Input destination URI */ 
    5119             uri = NULL; 
    5120             ui_input_url("Destination URI", buf, sizeof(buf), &result); 
    5121             if (result.nb_result != NO_NB) { 
    5122  
    5123                 if (result.nb_result == -1) { 
    5124                     puts("Sorry you can't do that!"); 
    5125                     continue; 
    5126                 } else if (result.nb_result == 0) { 
    5127                     uri = NULL; 
    5128                     if (current_call == PJSUA_INVALID_ID) { 
    5129                         puts("No current call"); 
    5130                         continue; 
    5131                     } 
    5132                 } else { 
    5133                     pjsua_buddy_info binfo; 
    5134                     pjsua_buddy_get_info(result.nb_result-1, &binfo); 
    5135                     tmp.ptr = buf; 
    5136                     pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf)); 
    5137                     uri = buf; 
    5138                 } 
    5139  
    5140             } else if (result.uri_result) { 
    5141                 uri = result.uri_result; 
    5142             } else { 
    5143                 continue; 
    5144             } 
    5145              
    5146             if (uri) { 
    5147                 tmp = pj_str(uri); 
    5148                 send_request(text, &tmp); 
    5149             } else { 
    5150                 /* If you send call control request using this method 
    5151                  * (such requests includes BYE, CANCEL, etc.), it will 
    5152                  * not go well with the call state, so don't do it 
    5153                  * unless it's for testing. 
    5154                  */ 
    5155                 pj_str_t method = pj_str(text); 
    5156                 pjsua_call_send_request(current_call, &method, NULL); 
    5157             } 
    5158             break; 
    5159  
    5160         case 'e': 
    5161             if (pj_ansi_strnicmp(menuin, "echo", 4)==0) { 
    5162                 pj_str_t tmp; 
    5163  
    5164                 tmp.ptr = menuin+5; 
    5165                 tmp.slen = pj_ansi_strlen(menuin)-6; 
    5166  
    5167                 if (tmp.slen < 1) { 
    5168                     puts("Usage: echo [0|1]"); 
    5169                     break; 
    5170                 } 
    5171  
    5172                 cmd_echo = *tmp.ptr != '0' || tmp.slen!=1; 
    5173             } 
    5174             break; 
    5175  
    5176         case 's': 
    5177             if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) { 
    5178                 pj_str_t tmp; 
    5179                 int delay; 
    5180  
    5181                 tmp.ptr = menuin+6; 
    5182                 tmp.slen = pj_ansi_strlen(menuin)-7; 
    5183  
    5184                 if (tmp.slen < 1) { 
    5185                     puts("Usage: sleep MSEC"); 
    5186                     break; 
    5187                 } 
    5188  
    5189                 delay = pj_strtoul(&tmp); 
    5190                 if (delay < 0) delay = 0; 
    5191                 pj_thread_sleep(delay); 
    5192                 break; 
    5193             } 
    5194             /* Continue below */ 
    5195  
    5196         case 'u': 
    5197             /* 
    5198              * Subscribe/unsubscribe presence. 
    5199              */ 
    5200             ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result); 
    5201             if (result.nb_result != NO_NB) { 
    5202                 if (result.nb_result == -1) { 
    5203                     int i, count; 
    5204                     count = pjsua_get_buddy_count(); 
    5205                     for (i=0; i<count; ++i) 
    5206                         pjsua_buddy_subscribe_pres(i, menuin[0]=='s'); 
    5207                 } else if (result.nb_result == 0) { 
    5208                     puts("Sorry, can only subscribe to buddy's presence, " 
    5209                          "not from existing call"); 
    5210                 } else { 
    5211                     pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s')); 
    5212                 } 
    5213  
    5214             } else if (result.uri_result) { 
    5215                 puts("Sorry, can only subscribe to buddy's presence, " 
    5216                      "not arbitrary URL (for now)"); 
    5217             } 
    5218  
    5219             break; 
    5220  
    5221         case 'r': 
    5222             switch (menuin[1]) { 
    5223             case 'r': 
    5224                 /* 
    5225                  * Re-Register. 
    5226                  */ 
    5227                 pjsua_acc_set_registration(current_acc, PJ_TRUE); 
    5228                 break; 
    5229             case 'u': 
    5230                 /* 
    5231                  * Unregister 
    5232                  */ 
    5233                 pjsua_acc_set_registration(current_acc, PJ_FALSE); 
    5234                 break; 
    5235             } 
    5236             break; 
    5237              
    5238         case 't': 
    5239             pjsua_acc_get_info(current_acc, &acc_info); 
    5240             acc_info.online_status = !acc_info.online_status; 
    5241             pjsua_acc_set_online_status(current_acc, acc_info.online_status); 
    5242             printf("Setting %s online status to %s\n", 
    5243                    acc_info.acc_uri.ptr, 
    5244                    (acc_info.online_status?"online":"offline")); 
    5245             break; 
    5246  
    5247         case 'T': 
    5248             change_online_status(); 
    5249             break; 
    5250  
    5251         case 'c': 
    5252             switch (menuin[1]) { 
    5253             case 'l': 
    5254                 conf_list(); 
    5255                 break; 
    5256             case 'c': 
    5257             case 'd': 
    5258                 { 
    5259                     char tmp[10], src_port[10], dst_port[10]; 
    5260                     pj_status_t status; 
    5261                     int cnt; 
    5262                     const char *src_title, *dst_title; 
    5263  
    5264                     cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port); 
    5265  
    5266                     if (cnt != 3) { 
    5267                         conf_list(); 
    5268  
    5269                         src_title = (menuin[1]=='c'? 
    5270                                      "Connect src port #": 
    5271                                      "Disconnect src port #"); 
    5272                         dst_title = (menuin[1]=='c'? 
    5273                                      "To dst port #": 
    5274                                      "From dst port #"); 
    5275  
    5276                         if (!simple_input(src_title, src_port, sizeof(src_port))) 
    5277                             break; 
    5278  
    5279                         if (!simple_input(dst_title, dst_port, sizeof(dst_port))) 
    5280                             break; 
    5281                     } 
    5282  
    5283                     if (menuin[1]=='c') { 
    5284                         status = pjsua_conf_connect(my_atoi(src_port),  
    5285                                                     my_atoi(dst_port)); 
    5286                     } else { 
    5287                         status = pjsua_conf_disconnect(my_atoi(src_port),  
    5288                                                        my_atoi(dst_port)); 
    5289                     } 
    5290                     if (status == PJ_SUCCESS) { 
    5291                         puts("Success"); 
    5292                     } else { 
    5293                         puts("ERROR!!"); 
    5294                     } 
    5295                 } 
    5296                 break; 
    5297             } 
    5298             break; 
    5299  
    5300         case 'V': 
    5301             /* Adjust audio volume */ 
    5302             sprintf(buf, "Adjust mic level: [%4.1fx] ", app_config.mic_level); 
    5303             if (simple_input(buf,text,sizeof(text))) { 
    5304                 char *err; 
    5305                 app_config.mic_level = (float)strtod(text, &err); 
    5306                 pjsua_conf_adjust_rx_level(0, app_config.mic_level); 
    5307             } 
    5308             sprintf(buf, "Adjust speaker level: [%4.1fx] ",  
    5309                     app_config.speaker_level); 
    5310             if (simple_input(buf,text,sizeof(text))) { 
    5311                 char *err; 
    5312                 app_config.speaker_level = (float)strtod(text, &err); 
    5313                 pjsua_conf_adjust_tx_level(0, app_config.speaker_level); 
    5314             } 
    5315  
    5316             break; 
    5317  
    5318         case 'd': 
    5319             if (menuin[1] == 'c') { 
    5320                 char settings[2000]; 
    5321                 int len; 
    5322  
    5323                 len = write_settings(&app_config, settings, sizeof(settings)); 
    5324                 if (len < 1) 
    5325                     PJ_LOG(1,(THIS_FILE, "Error: not enough buffer")); 
    5326                 else 
    5327                     PJ_LOG(3,(THIS_FILE,  
    5328                               "Dumping configuration (%d bytes):\n%s\n", 
    5329                               len, settings)); 
    5330  
    5331             } else if (menuin[1] == 'q') { 
    5332  
    5333                 if (current_call != PJSUA_INVALID_ID) { 
    5334                     log_call_dump(current_call); 
    5335                 } else { 
    5336                     PJ_LOG(3,(THIS_FILE, "No current call")); 
    5337                 } 
    5338  
    5339             } else { 
    5340                 app_dump(menuin[1]=='d'); 
    5341             } 
    5342             break; 
    5343  
    5344  
    5345         case 'f': 
    5346             if (simple_input("Enter output filename", buf, sizeof(buf))) { 
    5347                 char settings[2000]; 
    5348                 int len; 
    5349  
    5350                 len = write_settings(&app_config, settings, sizeof(settings)); 
    5351                 if (len < 1) 
    5352                     PJ_LOG(1,(THIS_FILE, "Error: not enough buffer")); 
    5353                 else { 
    5354                     pj_oshandle_t fd; 
    5355                     pj_status_t status; 
    5356  
    5357                     status = pj_file_open(app_config.pool, buf,  
    5358                                           PJ_O_WRONLY, &fd); 
    5359                     if (status != PJ_SUCCESS) { 
    5360                         pjsua_perror(THIS_FILE, "Unable to open file", status); 
    5361                     } else { 
    5362                         pj_ssize_t size = len; 
    5363                         pj_file_write(fd, settings, &size); 
    5364                         pj_file_close(fd); 
    5365  
    5366                         printf("Settings successfully written to '%s'\n", buf); 
    5367                     } 
    5368                 } 
    5369                  
    5370             } 
    5371             break; 
    5372  
    5373  
    5374         case 'L':   /* Restart */ 
    5375             app_restart = PJ_TRUE; 
    5376             /* Continues below */ 
    5377  
    5378         case 'q': 
    5379             goto on_exit; 
    5380  
    5381         case 'R': 
    5382             if (!pjsua_call_is_active(current_call)) { 
    5383                 PJ_LOG(1,(THIS_FILE, "Call %d has gone", current_call)); 
    5384             } else if (menuin[1] == 'a') { 
    5385                 pjsua_call_process_redirect(current_call,  
    5386                                             PJSIP_REDIRECT_ACCEPT_REPLACE); 
    5387             } else if (menuin[1] == 'A') { 
    5388                 pjsua_call_process_redirect(current_call,  
    5389                                             PJSIP_REDIRECT_ACCEPT); 
    5390             } else if (menuin[1] == 'r') { 
    5391                 pjsua_call_process_redirect(current_call, 
    5392                                             PJSIP_REDIRECT_REJECT); 
    5393             } else { 
    5394                 pjsua_call_process_redirect(current_call, 
    5395                                             PJSIP_REDIRECT_STOP); 
    5396             } 
    5397             break; 
    5398  
    5399         default: 
    5400             if (menuin[0] != '\n' && menuin[0] != '\r') { 
    5401                 printf("Invalid input %s", menuin); 
    5402             } 
    5403             keystroke_help(); 
    5404             break; 
    5405         } 
    5406     } 
    5407  
    5408 on_exit: 
    5409     ; 
    5410 } 
    5411  
    5412 /* 
    54132546 * A simple registrar, invoked by default_mod_on_rx_request() 
    54142547 */ 
     
    54602593                       rdata, tdata, NULL, NULL); 
    54612594} 
    5462  
    5463  
    54642595 
    54652596/***************************************************************************** 
     
    55392670    return PJ_TRUE; 
    55402671} 
    5541  
    55422672 
    55432673/* The module instance. */ 
     
    55592689 
    55602690}; 
    5561  
    5562  
    5563  
    55642691 
    55652692/***************************************************************************** 
     
    61103237    } 
    61113238 
     3239    if (app_config.use_cli) { 
     3240        /* 
     3241         * Init CLI 
     3242         */     
     3243        status = setup_cli(); 
     3244        if (status != PJ_SUCCESS) 
     3245            goto on_error; 
     3246         
     3247        PJ_LOG(3,(THIS_FILE, "CLI telnet daemon listening at port %d",  
     3248                  app_config.cli_telnet_port)); 
     3249    } 
     3250 
    61123251    return PJ_SUCCESS; 
    61133252 
     
    61153254    app_destroy(); 
    61163255    return status; 
    6117 } 
    6118  
    6119  
    6120 static int stdout_refresh_proc(void *arg) 
    6121 { 
    6122     PJ_UNUSED_ARG(arg); 
    6123  
    6124     /* Set thread to lowest priority so that it doesn't clobber 
    6125      * stdout output 
    6126      */ 
    6127     pj_thread_set_prio(pj_thread_this(),  
    6128                        pj_thread_get_prio_min(pj_thread_this())); 
    6129  
    6130     while (!stdout_refresh_quit) { 
    6131         pj_thread_sleep(stdout_refresh * 1000); 
    6132         puts(stdout_refresh_text); 
    6133         fflush(stdout); 
    6134     } 
    6135  
    6136     return 0; 
    61373256} 
    61383257 
     
    61553274    } 
    61563275 
    6157     console_app_main(&uri_arg); 
     3276    if (app_config.use_cli) 
     3277        cli_console_app_main(&uri_arg, &app_restart); 
     3278    else  
     3279        console_app_main(&uri_arg, &app_restart); 
    61583280 
    61593281    if (stdout_refresh_thread) { 
     
    62193341    for (i=0; i<app_config.tone_count; ++i) { 
    62203342        pjsua_conf_remove_port(app_config.tone_slots[i]); 
     3343    } 
     3344 
     3345    if (app_config.use_cli) {    
     3346        destroy_cli(); 
    62213347    } 
    62223348 
     
    63033429    status = pjmedia_snd_port_connect(app_config.snd, app_config.sc); 
    63043430    pj_assert(status == PJ_SUCCESS); 
    6305  
    63063431} 
    63073432#endif 
Note: See TracChangeset for help on using the changeset viewer.