Changeset 4440 for pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
- Timestamp:
- Mar 14, 2013 7:18:13 AM (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
r4439 r4440 18 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 19 */ 20 #include <pjsua-lib/pjsua.h>20 #include "pjsua_cmd.h" 21 21 #include "gui.h" 22 22 23 24 23 #define THIS_FILE "pjsua_app.c" 25 #define NO_LIMIT (int)0x7FFFFFFF26 24 27 25 //#define STEREO_DEMO … … 44 42 #define RING_INTERVAL 3000 45 43 46 #define MAX_AVI 447 48 /* Call specific data */49 struct call_data50 {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_vid58 {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_config68 {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_DEMO118 pjmedia_snd_port *snd;119 pjmedia_port *sc, *sc_ch1;120 pjsua_conf_port_id sc_ch1_slot;121 #endif122 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;154 44 #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 160 46 static 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 #else165 # define SOME_BUF_SIZE (1024 * 3)166 #endif167 168 static char some_buf[SOME_BUF_SIZE];169 47 170 48 #ifdef STEREO_DEMO 171 49 static void stereo_demo(); 172 50 #endif 51 173 52 pj_status_t app_destroy(void); 174 175 53 static void ringback_start(pjsua_call_id call_id); 176 54 static void ring_start(pjsua_call_id call_id); … … 179 57 pj_bool_t app_restart; 180 58 pj_log_func *log_cb = NULL; 59 static const char *stdout_refresh_text = "STDOUT_REFRESH"; 60 61 /** Forward declaration **/ 62 void console_app_main(const pj_str_t *uri_to_call, pj_bool_t *app_restart); 63 64 void cli_console_app_main(const pj_str_t *uri_to_call, pj_bool_t *app_restart); 65 void app_config_init_video(pjsua_acc_config *acc_cfg); 66 pj_status_t setup_cli(); 67 void destroy_cli(); 181 68 182 69 /***************************************************************************** … … 382 269 383 270 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 (""); 384 277 puts ("When URL is specified, pjsua will immediately initiate call to that URL"); 385 278 puts (""); … … 388 281 } 389 282 283 static 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 } 390 301 391 302 /* Set default config. */ 392 static void default_config( structapp_config *cfg)303 static void default_config(pjsua_app_config *cfg) 393 304 { 394 305 char tmp[80]; … … 407 318 cfg->rtp_cfg.port = 4000; 408 319 cfg->redir_op = PJSIP_REDIRECT_ACCEPT_REPLACE; 409 cfg->duration = NO_LIMIT ;320 cfg->duration = NO_LIMIT_DURATION; 410 321 cfg->wav_id = PJSUA_INVALID_ID; 411 322 cfg->rec_id = PJSUA_INVALID_ID; … … 431 342 432 343 cfg->avi_def_idx = PJSUA_INVALID_ID; 433 } 434 344 345 cfg->use_cli = PJ_FALSE; 346 cfg->cli_telnet_port = 0; 347 } 435 348 436 349 /* … … 467 380 int len, token_len; 468 381 382 pj_bzero(line, sizeof(line)); 469 383 if (fgets(line, sizeof(line), fhnd) == NULL) break; 470 384 … … 536 450 *app_argv = argv; 537 451 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 } 557 453 558 454 /* Parse arguments. */ 559 static pj_status_t parse_args(int argc, char *argv[], 560 structapp_config *cfg,455 static pj_status_t parse_args(int argc, char *argv[], 456 pjsua_app_config *cfg, 561 457 pj_str_t *uri_to_call) 562 458 { … … 598 494 OPT_TIMER, OPT_TIMER_SE, OPT_TIMER_MIN_SE, 599 495 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 601 498 }; 602 499 struct pj_getopt_option long_options[] = { … … 727 624 { "play-avi", 1, 0, OPT_PLAY_AVI}, 728 625 { "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}, 729 628 { NULL, 0, 0, 0} 730 629 }; … … 749 648 750 649 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); 752 651 if (status != 0) 753 652 return status; … … 1549 1448 case OPT_AUTO_PLAY_AVI: 1550 1449 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); 1551 1458 break; 1552 1459 … … 1631 1538 } 1632 1539 } 1633 1634 1635 1540 return PJ_SUCCESS; 1636 }1637 1638 1639 /*1640 * Save account settings1641 */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 #endif1725 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 #endif2017 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_SRTP2030 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 #endif2048 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 long2326 * 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);2359 1541 } 2360 1542 … … 2362 1544 * Console application 2363 1545 */ 2364 2365 1546 static void ringback_start(pjsua_call_id call_id) 2366 1547 { … … 2427 1608 } 2428 1609 2429 #ifdef HAVE_MULTIPART_TEST2430 /*2431 * Enable multipart in msg_data and add a dummy body into the2432 * 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 #else2456 # define TEST_MULTIPART(msg_data)2457 #endif2458 2459 /*2460 * Find next call when current call is disconnected or when user2461 * 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 2514 1610 /* Callback from timer when the maximum call duration has been 2515 1611 * exceeded. … … 2596 1692 } else { 2597 1693 2598 if (app_config.duration !=NO_LIMIT&&1694 if (app_config.duration != NO_LIMIT_DURATION && 2599 1695 call_info.state == PJSIP_INV_STATE_CONFIRMED) 2600 1696 { … … 2650 1746 } 2651 1747 2652 2653 1748 /** 2654 1749 * Handler when there is incoming call. … … 2704 1799 "From: %s\n" 2705 1800 "To: %s\n" 2706 "Press a to answer or hto reject call",1801 "Press %s to answer or %s to reject call", 2707 1802 acc_id, 2708 1803 call_info.rem_aud_cnt, … … 2710 1805 notif_st, 2711 1806 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"))); 2713 1810 } 2714 1811 } … … 2925 2022 } 2926 2023 2927 /* arrange windows. arg:2928 * -1: arrange all windows2929 * != -1: arrange only this window id2930 */2931 static void arrange_window(pjsua_vid_win_id wid)2932 {2933 #if PJSUA_HAS_VIDEO2934 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 #else2959 PJ_UNUSED_ARG(wid);2960 #endif2961 }2962 2963 2024 /* Process video media state. "mi" is the media index. */ 2964 2025 static void on_call_video_state(pjsua_call_info *ci, unsigned mi, … … 3070 2131 // Log already written. 3071 2132 } 3072 3073 2133 3074 2134 /* … … 3444 2504 #endif 3445 2505 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 3512 2506 /* Playfile done notification, set timer to hangup calls */ 3513 2507 pj_status_t on_playfile_done(pjmedia_port *port, void *usr_data) … … 3550 2544 3551 2545 /* 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_VIDEO3591 puts("| Video: \"vid help\" for more info |");3592 puts("+-----------------------------------------------------------------------------+");3593 #endif3594 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_VIDEO3611 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 #endif3649 3650 /*3651 * Input simple string3652 */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 -23675 struct input_result3676 {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 else3727 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 bridge3764 */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 host3804 */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_MAX3840 };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_VIDEO3928 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 #endif3940 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_VIDEO3970 if (status != PJ_SUCCESS) {3971 status = pjsua_vid_codec_set_priority(pj_cstr(&id, codec),3972 (pj_uint8_t)new_prio);3973 }3974 #endif3975 if (status != PJ_SUCCESS)3976 pjsua_perror(THIS_FILE, "Error setting codec priority", status);3977 }3978 3979 3980 #if PJSUA_HAS_VIDEO3981 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 borderless4065 * 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(¶m);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 ¶m);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, ¶m);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, ¶m);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 ¶m);4206 } else4207 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(¶m);4228 param.wnd_flags = PJMEDIA_VID_DEV_WND_BORDER |4229 PJMEDIA_VID_DEV_WND_RESIZABLE;4230 pjsua_vid_preview_start(dev_id, ¶m);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 is4237 * responsibility of app */4238 pjsua_vid_win_set_show(wid, PJ_FALSE);4239 pjsua_vid_preview_stop(dev_id);4240 }4241 }4242 }4243 } else4244 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 } else4287 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 } else4380 goto on_error;4381 } else4382 goto on_error;4383 4384 return;4385 4386 on_error:4387 PJ_LOG(1,(THIS_FILE, "Invalid command, use 'vid help'"));4388 }4389 4390 #else4391 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 into4436 * program, when file ends, resume with kbd.4437 * If exit is desired end script with q for quit4438 */4439 /* Reopen stdin/stdout/stderr to /dev/console */4440 #if defined(PJ_WIN32) && PJ_WIN32!=04441 if (freopen ("CONIN$", "r", stdin) == NULL) {4442 #else4443 if (1) {4444 #endif4445 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, ¤t_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 for4654 * 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_VIDEO4841 if (menuin[1]=='i' && menuin[2]=='d' && menuin[3]==' ') {4842 4843 vid_handle_menu(menuin);4844 4845 } else4846 #endif4847 if (current_call != -1) {4848 /*4849 * re-INVITE4850 */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 UPDATE4862 */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 request5106 */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 method5151 * (such requests includes BYE, CANCEL, etc.), it will5152 * not go well with the call state, so don't do it5153 * 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 * Unregister5232 */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 else5327 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 /*5413 2546 * A simple registrar, invoked by default_mod_on_rx_request() 5414 2547 */ … … 5460 2593 rdata, tdata, NULL, NULL); 5461 2594 } 5462 5463 5464 2595 5465 2596 /***************************************************************************** … … 5539 2670 return PJ_TRUE; 5540 2671 } 5541 5542 2672 5543 2673 /* The module instance. */ … … 5559 2689 5560 2690 }; 5561 5562 5563 5564 2691 5565 2692 /***************************************************************************** … … 6110 3237 } 6111 3238 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 6112 3251 return PJ_SUCCESS; 6113 3252 … … 6115 3254 app_destroy(); 6116 3255 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 clobber6125 * stdout output6126 */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;6137 3256 } 6138 3257 … … 6155 3274 } 6156 3275 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); 6158 3280 6159 3281 if (stdout_refresh_thread) { … … 6219 3341 for (i=0; i<app_config.tone_count; ++i) { 6220 3342 pjsua_conf_remove_port(app_config.tone_slots[i]); 3343 } 3344 3345 if (app_config.use_cli) { 3346 destroy_cli(); 6221 3347 } 6222 3348 … … 6303 3429 status = pjmedia_snd_port_connect(app_config.snd, app_config.sc); 6304 3430 pj_assert(status == PJ_SUCCESS); 6305 6306 3431 } 6307 3432 #endif
Note: See TracChangeset
for help on using the changeset viewer.