Changeset 4476
- Timestamp:
- Apr 19, 2013 6:05:06 AM (12 years ago)
- Location:
- pjproject/trunk
- Files:
-
- 1 added
- 1 deleted
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjlib-util/include/pjlib-util.h
r4461 r4476 64 64 #include <pjlib-util/http_client.h> 65 65 66 /** CLI Telnet **/ 66 /** CLI **/ 67 #include <pjlib-util/cli.h> 68 #include <pjlib-util/cli_console.h> 67 69 #include <pjlib-util/cli_telnet.h> 68 70 -
pjproject/trunk/pjlib-util/include/pjlib-util/cli_imp.h
r4440 r4476 48 48 */ 49 49 #ifndef PJ_CLI_TELNET_LOG_LEVEL 50 # define PJ_CLI_TELNET_LOG_LEVEL 350 # define PJ_CLI_TELNET_LOG_LEVEL 4 51 51 #endif 52 52 -
pjproject/trunk/pjlib-util/include/pjlib-util/cli_telnet.h
r4461 r4476 51 51 */ 52 52 pj_uint16_t port; 53 54 /* Internal buffer for IP address */ 55 char buf_[32]; 56 53 57 } pj_cli_telnet_info; 54 58 … … 56 60 * This specifies the callback called when telnet is started 57 61 * 58 * @param telnet_info The telnet runtime information.62 * @param status The status of telnet startup process. 59 63 * 60 64 */ 61 typedef void (*pj_cli_telnet_on_started)(pj_ cli_telnet_info *telnet_info);65 typedef void (*pj_cli_telnet_on_started)(pj_status_t status); 62 66 63 67 /** … … 143 147 pj_cli_front_end **p_fe); 144 148 149 150 /** 151 * Retrieve cli telnet info. 152 * 153 * @param telnet_info The telnet runtime information. 154 * 155 * @return PJ_SUCCESS on success. 156 */ 157 PJ_DECL(pj_status_t) pj_cli_telnet_get_info(pj_cli_front_end *fe, 158 pj_cli_telnet_info *info); 159 145 160 /** 146 161 * @} -
pjproject/trunk/pjlib-util/src/pjlib-util/cli_telnet.c
r4461 r4476 146 146 { 147 147 TRANSMIT_BINARY = 0, /* Transmit Binary. */ 148 ECHO = 1, /* Echo. */148 TERM_ECHO = 1, /* Echo. */ 149 149 RECONNECT = 2, /* Reconnection. */ 150 150 SUPPRESS_GA = 3, /* Suppress Go Aheah. */ … … 896 896 static pj_bool_t handle_alfa_num(cli_telnet_sess *sess, unsigned char *data) 897 897 { 898 if (is_local_option_state_ena(sess, ECHO)) {898 if (is_local_option_state_ena(sess, TERM_ECHO)) { 899 899 if (recv_buf_right_len(sess->rcmd) > 0) { 900 900 /* Cursor is not at EOL, insert character */ … … 1062 1062 1063 1063 status = pj_cli_sess_exec(&sess->base, (char *)&sess->rcmd->rbuf, 1064 pool, &info); 1064 pool, &info); 1065 1065 1066 1066 switch (status) { … … 1646 1646 set_peer_option(sess, STATUS, PJ_TRUE); 1647 1647 set_peer_option(sess, TIMING_MARK, PJ_TRUE); 1648 set_peer_option(sess, ECHO, PJ_TRUE);1648 set_peer_option(sess, TERM_ECHO, PJ_TRUE); 1649 1649 1650 1650 send_cmd_do(sess, SUPPRESS_GA); 1651 send_cmd_will(sess, ECHO);1651 send_cmd_will(sess, TERM_ECHO); 1652 1652 send_cmd_will(sess, STATUS); 1653 1653 send_cmd_will(sess, SUPPRESS_GA); … … 1728 1728 1729 1729 /* Start telnet daemon */ 1730 telnet_start(fe); 1730 status = telnet_start(fe); 1731 if (status != PJ_SUCCESS) 1732 goto on_exit; 1731 1733 1732 1734 pj_cli_register_front_end(cli, &fe->base); … … 1770 1772 &val, sizeof(val)); 1771 1773 1772 if (status != PJ_SUCCESS) 1773 goto on_exit; 1774 if (status != PJ_SUCCESS) { 1775 PJ_LOG(3, (THIS_FILE, "Failed setting socket options")); 1776 } 1774 1777 1775 1778 /* The loop is silly, but what else can we do? */ … … 1834 1837 } 1835 1838 1836 /** Fill telnet information and call pj_cli_telnet_on_started callback */ 1839 return PJ_SUCCESS; 1840 1841 on_exit: 1837 1842 if (fe->cfg.on_started) { 1838 char ip_addr[32]; 1839 pj_cli_telnet_info telnet_info; 1840 pj_sockaddr hostip; 1841 1842 telnet_info.ip_address.ptr = ip_addr; 1843 telnet_info.ip_address.slen = 0; 1844 1845 status = pj_gethostip(pj_AF_INET(), &hostip); 1846 if (status != PJ_SUCCESS) 1847 goto on_exit; 1848 1849 pj_strcpy2(&telnet_info.ip_address, 1850 pj_inet_ntoa(hostip.ipv4.sin_addr)); 1851 1852 telnet_info.port = fe->cfg.port; 1853 1854 (*fe->cfg.on_started)(&telnet_info); 1855 } 1856 1857 return PJ_SUCCESS; 1858 1859 on_exit: 1843 (*fe->cfg.on_started)(status); 1844 } 1845 1860 1846 if (fe->asock) 1861 1847 pj_activesock_close(fe->asock); … … 1908 1894 /** Start Telnet **/ 1909 1895 status = telnet_start(fe); 1910 if (status == PJ_SUCCESS) 1896 if (status == PJ_SUCCESS) { 1897 if (fe->cfg.on_started) { 1898 (*fe->cfg.on_started)(status); 1899 } 1911 1900 TRACE_((THIS_FILE, "Telnet Restarted")); 1912 1901 } 1913 1902 on_exit: 1914 1903 return status; 1915 1904 } 1905 1906 PJ_DEF(pj_status_t) pj_cli_telnet_get_info(pj_cli_front_end *fe, 1907 pj_cli_telnet_info *info) 1908 { 1909 pj_sockaddr hostip; 1910 pj_status_t status; 1911 cli_telnet_fe *tfe = (cli_telnet_fe*) fe; 1912 1913 PJ_ASSERT_RETURN(fe && (fe->type == PJ_CLI_TELNET_FRONT_END) && info, 1914 PJ_EINVAL); 1915 1916 pj_strset(&info->ip_address, info->buf_, 0); 1917 1918 status = pj_gethostip(pj_AF_INET(), &hostip); 1919 if (status != PJ_SUCCESS) 1920 return status; 1921 1922 pj_strcpy2(&info->ip_address, pj_inet_ntoa(hostip.ipv4.sin_addr)); 1923 1924 info->port = tfe->cfg.port; 1925 1926 return PJ_SUCCESS; 1927 } -
pjproject/trunk/pjsip-apps/build/Makefile
r4461 r4476 34 34 export PJSUA_SRCDIR = ../src/pjsua 35 35 export PJSUA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ 36 main.o pjsua_app.o pjsua_cli.o pjsua_c li_cmd.o pjsua_common.o \36 main.o pjsua_app.o pjsua_cli.o pjsua_common.o \ 37 37 pjsua_config.o pjsua_legacy.o 38 38 export PJSUA_CFLAGS += $(_CFLAGS) -
pjproject/trunk/pjsip-apps/build/pjsua.vcproj
r4461 r4476 3625 3625 </File> 3626 3626 <File 3627 RelativePath="..\src\pjsua\pjsua_cli_cmd.c"3628 >3629 <FileConfiguration3630 Name="Release|Win32"3631 >3632 <Tool3633 Name="VCCLCompilerTool"3634 AdditionalIncludeDirectories=""3635 PreprocessorDefinitions=""3636 />3637 </FileConfiguration>3638 <FileConfiguration3639 Name="Release|Pocket PC 2003 (ARMV4)"3640 >3641 <Tool3642 Name="VCCLCompilerTool"3643 AdditionalIncludeDirectories=""3644 PreprocessorDefinitions=""3645 />3646 </FileConfiguration>3647 <FileConfiguration3648 Name="Release|Smartphone 2003 (ARMV4)"3649 >3650 <Tool3651 Name="VCCLCompilerTool"3652 AdditionalIncludeDirectories=""3653 PreprocessorDefinitions=""3654 />3655 </FileConfiguration>3656 <FileConfiguration3657 Name="Debug|Win32"3658 >3659 <Tool3660 Name="VCCLCompilerTool"3661 AdditionalIncludeDirectories=""3662 PreprocessorDefinitions=""3663 />3664 </FileConfiguration>3665 <FileConfiguration3666 Name="Debug|Pocket PC 2003 (ARMV4)"3667 >3668 <Tool3669 Name="VCCLCompilerTool"3670 AdditionalIncludeDirectories=""3671 PreprocessorDefinitions=""3672 />3673 </FileConfiguration>3674 <FileConfiguration3675 Name="Debug|Smartphone 2003 (ARMV4)"3676 >3677 <Tool3678 Name="VCCLCompilerTool"3679 AdditionalIncludeDirectories=""3680 PreprocessorDefinitions=""3681 />3682 </FileConfiguration>3683 <FileConfiguration3684 Name="Debug-Static|Win32"3685 >3686 <Tool3687 Name="VCCLCompilerTool"3688 AdditionalIncludeDirectories=""3689 PreprocessorDefinitions=""3690 />3691 </FileConfiguration>3692 <FileConfiguration3693 Name="Debug-Static|Pocket PC 2003 (ARMV4)"3694 >3695 <Tool3696 Name="VCCLCompilerTool"3697 AdditionalIncludeDirectories=""3698 PreprocessorDefinitions=""3699 />3700 </FileConfiguration>3701 <FileConfiguration3702 Name="Debug-Static|Smartphone 2003 (ARMV4)"3703 >3704 <Tool3705 Name="VCCLCompilerTool"3706 AdditionalIncludeDirectories=""3707 PreprocessorDefinitions=""3708 />3709 </FileConfiguration>3710 <FileConfiguration3711 Name="Release-Dynamic|Win32"3712 >3713 <Tool3714 Name="VCCLCompilerTool"3715 AdditionalIncludeDirectories=""3716 PreprocessorDefinitions=""3717 />3718 </FileConfiguration>3719 <FileConfiguration3720 Name="Release-Dynamic|Pocket PC 2003 (ARMV4)"3721 >3722 <Tool3723 Name="VCCLCompilerTool"3724 AdditionalIncludeDirectories=""3725 PreprocessorDefinitions=""3726 />3727 </FileConfiguration>3728 <FileConfiguration3729 Name="Release-Dynamic|Smartphone 2003 (ARMV4)"3730 >3731 <Tool3732 Name="VCCLCompilerTool"3733 AdditionalIncludeDirectories=""3734 PreprocessorDefinitions=""3735 />3736 </FileConfiguration>3737 <FileConfiguration3738 Name="Debug-Dynamic|Win32"3739 >3740 <Tool3741 Name="VCCLCompilerTool"3742 AdditionalIncludeDirectories=""3743 PreprocessorDefinitions=""3744 />3745 </FileConfiguration>3746 <FileConfiguration3747 Name="Debug-Dynamic|Pocket PC 2003 (ARMV4)"3748 >3749 <Tool3750 Name="VCCLCompilerTool"3751 AdditionalIncludeDirectories=""3752 PreprocessorDefinitions=""3753 />3754 </FileConfiguration>3755 <FileConfiguration3756 Name="Debug-Dynamic|Smartphone 2003 (ARMV4)"3757 >3758 <Tool3759 Name="VCCLCompilerTool"3760 AdditionalIncludeDirectories=""3761 PreprocessorDefinitions=""3762 />3763 </FileConfiguration>3764 <FileConfiguration3765 Name="Release-Static|Win32"3766 >3767 <Tool3768 Name="VCCLCompilerTool"3769 AdditionalIncludeDirectories=""3770 PreprocessorDefinitions=""3771 />3772 </FileConfiguration>3773 <FileConfiguration3774 Name="Release-Static|Pocket PC 2003 (ARMV4)"3775 >3776 <Tool3777 Name="VCCLCompilerTool"3778 AdditionalIncludeDirectories=""3779 PreprocessorDefinitions=""3780 />3781 </FileConfiguration>3782 <FileConfiguration3783 Name="Release-Static|Smartphone 2003 (ARMV4)"3784 >3785 <Tool3786 Name="VCCLCompilerTool"3787 AdditionalIncludeDirectories=""3788 PreprocessorDefinitions=""3789 />3790 </FileConfiguration>3791 <FileConfiguration3792 Name="Release|Windows Mobile 6 Standard SDK (ARMV4I)"3793 >3794 <Tool3795 Name="VCCLCompilerTool"3796 AdditionalIncludeDirectories=""3797 PreprocessorDefinitions=""3798 />3799 </FileConfiguration>3800 <FileConfiguration3801 Name="Release|Windows Mobile 6 Professional SDK (ARMV4I)"3802 >3803 <Tool3804 Name="VCCLCompilerTool"3805 AdditionalIncludeDirectories=""3806 PreprocessorDefinitions=""3807 />3808 </FileConfiguration>3809 <FileConfiguration3810 Name="Release|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"3811 >3812 <Tool3813 Name="VCCLCompilerTool"3814 AdditionalIncludeDirectories=""3815 PreprocessorDefinitions=""3816 />3817 </FileConfiguration>3818 <FileConfiguration3819 Name="Release|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"3820 >3821 <Tool3822 Name="VCCLCompilerTool"3823 AdditionalIncludeDirectories=""3824 PreprocessorDefinitions=""3825 />3826 </FileConfiguration>3827 <FileConfiguration3828 Name="Debug|Windows Mobile 6 Standard SDK (ARMV4I)"3829 >3830 <Tool3831 Name="VCCLCompilerTool"3832 AdditionalIncludeDirectories=""3833 PreprocessorDefinitions=""3834 />3835 </FileConfiguration>3836 <FileConfiguration3837 Name="Debug|Windows Mobile 6 Professional SDK (ARMV4I)"3838 >3839 <Tool3840 Name="VCCLCompilerTool"3841 AdditionalIncludeDirectories=""3842 PreprocessorDefinitions=""3843 />3844 </FileConfiguration>3845 <FileConfiguration3846 Name="Debug|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"3847 >3848 <Tool3849 Name="VCCLCompilerTool"3850 AdditionalIncludeDirectories=""3851 PreprocessorDefinitions=""3852 />3853 </FileConfiguration>3854 <FileConfiguration3855 Name="Debug|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"3856 >3857 <Tool3858 Name="VCCLCompilerTool"3859 AdditionalIncludeDirectories=""3860 PreprocessorDefinitions=""3861 />3862 </FileConfiguration>3863 <FileConfiguration3864 Name="Debug-Static|Windows Mobile 6 Standard SDK (ARMV4I)"3865 >3866 <Tool3867 Name="VCCLCompilerTool"3868 AdditionalIncludeDirectories=""3869 PreprocessorDefinitions=""3870 />3871 </FileConfiguration>3872 <FileConfiguration3873 Name="Debug-Static|Windows Mobile 6 Professional SDK (ARMV4I)"3874 >3875 <Tool3876 Name="VCCLCompilerTool"3877 AdditionalIncludeDirectories=""3878 PreprocessorDefinitions=""3879 />3880 </FileConfiguration>3881 <FileConfiguration3882 Name="Debug-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"3883 >3884 <Tool3885 Name="VCCLCompilerTool"3886 AdditionalIncludeDirectories=""3887 PreprocessorDefinitions=""3888 />3889 </FileConfiguration>3890 <FileConfiguration3891 Name="Debug-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"3892 >3893 <Tool3894 Name="VCCLCompilerTool"3895 AdditionalIncludeDirectories=""3896 PreprocessorDefinitions=""3897 />3898 </FileConfiguration>3899 <FileConfiguration3900 Name="Release-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"3901 >3902 <Tool3903 Name="VCCLCompilerTool"3904 AdditionalIncludeDirectories=""3905 PreprocessorDefinitions=""3906 />3907 </FileConfiguration>3908 <FileConfiguration3909 Name="Release-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"3910 >3911 <Tool3912 Name="VCCLCompilerTool"3913 AdditionalIncludeDirectories=""3914 PreprocessorDefinitions=""3915 />3916 </FileConfiguration>3917 <FileConfiguration3918 Name="Release-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"3919 >3920 <Tool3921 Name="VCCLCompilerTool"3922 AdditionalIncludeDirectories=""3923 PreprocessorDefinitions=""3924 />3925 </FileConfiguration>3926 <FileConfiguration3927 Name="Release-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"3928 >3929 <Tool3930 Name="VCCLCompilerTool"3931 AdditionalIncludeDirectories=""3932 PreprocessorDefinitions=""3933 />3934 </FileConfiguration>3935 <FileConfiguration3936 Name="Debug-Dynamic|Windows Mobile 6 Standard SDK (ARMV4I)"3937 >3938 <Tool3939 Name="VCCLCompilerTool"3940 AdditionalIncludeDirectories=""3941 PreprocessorDefinitions=""3942 />3943 </FileConfiguration>3944 <FileConfiguration3945 Name="Debug-Dynamic|Windows Mobile 6 Professional SDK (ARMV4I)"3946 >3947 <Tool3948 Name="VCCLCompilerTool"3949 AdditionalIncludeDirectories=""3950 PreprocessorDefinitions=""3951 />3952 </FileConfiguration>3953 <FileConfiguration3954 Name="Debug-Dynamic|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"3955 >3956 <Tool3957 Name="VCCLCompilerTool"3958 AdditionalIncludeDirectories=""3959 PreprocessorDefinitions=""3960 />3961 </FileConfiguration>3962 <FileConfiguration3963 Name="Debug-Dynamic|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"3964 >3965 <Tool3966 Name="VCCLCompilerTool"3967 AdditionalIncludeDirectories=""3968 PreprocessorDefinitions=""3969 />3970 </FileConfiguration>3971 <FileConfiguration3972 Name="Release-Static|Windows Mobile 6 Standard SDK (ARMV4I)"3973 >3974 <Tool3975 Name="VCCLCompilerTool"3976 AdditionalIncludeDirectories=""3977 PreprocessorDefinitions=""3978 />3979 </FileConfiguration>3980 <FileConfiguration3981 Name="Release-Static|Windows Mobile 6 Professional SDK (ARMV4I)"3982 >3983 <Tool3984 Name="VCCLCompilerTool"3985 AdditionalIncludeDirectories=""3986 PreprocessorDefinitions=""3987 />3988 </FileConfiguration>3989 <FileConfiguration3990 Name="Release-Static|Windows Mobile 5.0 Pocket PC SDK (ARMV4I)"3991 >3992 <Tool3993 Name="VCCLCompilerTool"3994 AdditionalIncludeDirectories=""3995 PreprocessorDefinitions=""3996 />3997 </FileConfiguration>3998 <FileConfiguration3999 Name="Release-Static|Windows Mobile 5.0 Smartphone SDK (ARMV4I)"4000 >4001 <Tool4002 Name="VCCLCompilerTool"4003 AdditionalIncludeDirectories=""4004 PreprocessorDefinitions=""4005 />4006 </FileConfiguration>4007 </File>4008 <File4009 3627 RelativePath="..\src\pjsua\pjsua_common.c" 4010 3628 > … … 4024 3642 > 4025 3643 <File 3644 RelativePath="..\src\pjsua\pjsua_app.h" 3645 > 3646 </File> 3647 <File 4026 3648 RelativePath="..\src\pjsua\pjsua_common.h" 4027 3649 > -
pjproject/trunk/pjsip-apps/src/pjsua/main.c
r4461 r4476 18 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 19 */ 20 #include <pjsua-lib/pjsua.h> 21 20 #include "pjsua_app.h" 22 21 23 22 #define THIS_FILE "main.c" 24 23 24 static pj_bool_t running = PJ_TRUE; 25 static pj_status_t receive_end_sig; 26 static pj_thread_t *sig_thread; 27 static app_cfg_t cfg; 25 28 26 /* 27 * These are defined in pjsua_app.c. 28 */ 29 int main_func(int argc, char *argv[]); 29 /* Called when CLI (re)started */ 30 void on_app_started(pj_status_t status, const char *msg) 31 { 32 pj_perror(3, THIS_FILE, status, (msg)?msg:""); 33 } 34 35 pj_bool_t on_app_stopped(pj_bool_t restart, int argc, char** argv) 36 { 37 cfg.argc = argc; 38 cfg.argv = argv; 39 40 running = restart; 41 return PJ_TRUE; 42 } 43 44 #if defined(PJ_WIN32) && PJ_WIN32!=0 45 #include <windows.h> 46 47 static pj_thread_desc handler_desc; 48 49 static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) 50 { 51 switch (fdwCtrlType) 52 { 53 // Handle the CTRL+C signal. 54 55 case CTRL_C_EVENT: 56 case CTRL_CLOSE_EVENT: 57 case CTRL_BREAK_EVENT: 58 case CTRL_LOGOFF_EVENT: 59 case CTRL_SHUTDOWN_EVENT: 60 pj_thread_register("ctrlhandler", handler_desc, &sig_thread); 61 PJ_LOG(3,(THIS_FILE, "Ctrl-C detected, quitting..")); 62 receive_end_sig = PJ_TRUE; 63 app_destroy(); 64 ExitProcess(1); 65 PJ_UNREACHED(return TRUE;) 66 67 default: 68 69 return FALSE; 70 } 71 } 72 73 static void setup_socket_signal() 74 { 75 } 76 77 static void setup_signal_handler(void) 78 { 79 SetConsoleCtrlHandler(&CtrlHandler, TRUE); 80 } 81 82 #else 83 #include <signal.h> 84 85 static void setup_socket_signal() 86 { 87 signal(SIGPIPE, SIG_IGN); 88 } 89 90 static void setup_signal_handler(void) {} 91 #endif 30 92 31 93 int main(int argc, char *argv[]) 32 94 { 33 return pj_run_app(&main_func, argc, argv, 0); 95 pj_status_t status = PJ_TRUE; 96 97 pj_bzero(&cfg, sizeof(cfg)); 98 cfg.on_started = &on_app_started; 99 cfg.on_stopped = &on_app_stopped; 100 cfg.argc = argc; 101 cfg.argv = argv; 102 103 setup_signal_handler(); 104 setup_socket_signal(); 105 106 while (running) { 107 status = app_init(&cfg); 108 if (status == PJ_SUCCESS) { 109 status = app_run(PJ_TRUE); 110 } else { 111 pj_perror(3, THIS_FILE, status, "Failed init"); 112 running = PJ_FALSE; 113 } 114 115 if (!receive_end_sig) { 116 app_destroy(); 117 118 /* This is on purpose */ 119 app_destroy(); 120 } else { 121 pj_thread_join(sig_thread); 122 } 123 } 34 124 } -
pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
r4465 r4476 18 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 19 */ 20 #include "pjsua_ common.h"20 #include "pjsua_app.h" 21 21 22 22 #define THIS_FILE "pjsua_app.c" … … 52 52 static void ring_start(pjsua_call_id call_id); 53 53 static void ring_stop(pjsua_call_id call_id); 54 55 static pj_status_t receive_end_sig; 56 static pj_thread_t *sig_thread; 54 static pj_status_t pjsua_app_init(); 55 static pj_status_t pjsua_app_destroy(); 56 57 static app_cfg_t app_cfg; 57 58 pj_str_t uri_arg; 58 pj_bool_t app_restart;59 59 pj_bool_t app_running = PJ_FALSE; 60 pj_log_func *log_cb = NULL;61 62 /** Forward declaration **/63 /** Defined in pjsua_common.c **/64 void app_config_init_video(pjsua_acc_config *acc_cfg);65 /** Defined in pjsua_legacy.c **/66 void start_ui_main(pj_str_t *uri_to_call, pj_bool_t *app_restart);67 /** Defined in pjsua_cli.c **/68 void start_cli_main(pj_str_t *uri_to_call, pj_bool_t *app_restart);69 pj_status_t setup_cli(pj_bool_t with_console, pj_bool_t with_telnet,70 pj_uint16_t telnet_port,71 pj_cli_telnet_on_started on_started_cb,72 pj_cli_on_quit on_quit_cb,73 pj_cli_on_destroy on_destroy_cb,74 pj_cli_on_restart_pjsua on_restart_pjsua_cb);75 void destroy_cli(pj_bool_t app_restart);76 60 77 61 /***************************************************************************** … … 178 162 } 179 163 180 181 164 /* 182 165 * Handler when invite state has changed. … … 230 213 } else { 231 214 232 if (app_config.duration != NO_LIMIT_DURATION &&215 if (app_config.duration != PJSUA_APP_NO_LIMIT_DURATION && 233 216 call_info.state == PJSIP_INV_STATE_CONFIRMED) 234 217 { … … 315 298 call_opt.vid_cnt = app_config.vid.vid_cnt; 316 299 317 pjsua_call_answer2(call_id, &call_opt, app_config.auto_answer, NULL, NULL); 300 pjsua_call_answer2(call_id, &call_opt, app_config.auto_answer, NULL, 301 NULL); 318 302 } 319 303 … … 533 517 534 518 /* Automatically record conversation, if desired */ 535 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) { 519 if (app_config.auto_rec && app_config.rec_port != 520 PJSUA_INVALID_ID) 521 { 536 522 pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]), 537 523 app_config.rec_port); … … 551 537 552 538 /* Automatically record conversation, if desired */ 553 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) { 539 if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) 540 { 554 541 pjsua_conf_connect(call_conf_slot, app_config.rec_port); 555 542 pjsua_conf_connect(0, app_config.rec_port); … … 616 603 PJ_LOG(3,(THIS_FILE, 617 604 "Just rejected incoming video offer on call %d, " 618 "use \"vid call enable %d\" or \"vid call add\" to enable video!",619 call_id, vid_idx));605 "use \"vid call enable %d\" or \"vid call add\" to " 606 "enable video!", call_id, vid_idx)); 620 607 } 621 608 } … … 896 883 char buf[100]; 897 884 898 snprintf(buf, sizeof(buf), "SIP %s transport is disconnected from %s",899 tp->type_name, host_port);885 snprintf(buf, sizeof(buf), "SIP %s transport is disconnected " 886 "from %s", tp->type_name, host_port); 900 887 pjsua_perror(THIS_FILE, buf, info->status); 901 888 } … … 1227 1214 }; 1228 1215 1229 #if defined(PJ_WIN32) && PJ_WIN32!=0 1230 #include <windows.h> 1231 1232 static pj_thread_desc handler_desc; 1233 1234 static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) 1235 { 1236 switch (fdwCtrlType) 1237 { 1238 // Handle the CTRL+C signal. 1239 1240 case CTRL_C_EVENT: 1241 case CTRL_CLOSE_EVENT: 1242 case CTRL_BREAK_EVENT: 1243 case CTRL_LOGOFF_EVENT: 1244 case CTRL_SHUTDOWN_EVENT: 1245 pj_thread_register("ctrlhandler", handler_desc, &sig_thread); 1246 PJ_LOG(3,(THIS_FILE, "Ctrl-C detected, quitting..")); 1247 receive_end_sig = PJ_TRUE; 1248 app_destroy(); 1249 ExitProcess(1); 1250 PJ_UNREACHED(return TRUE;) 1251 1252 default: 1253 1254 return FALSE; 1255 } 1256 } 1257 1258 static void setup_socket_signal() 1259 { 1260 } 1261 1262 #else 1263 #include <signal.h> 1264 1265 static void setup_socket_signal() 1266 { 1267 signal(SIGPIPE, SIG_IGN); 1268 } 1269 1270 #endif 1271 1272 static pj_status_t setup_pjsua() 1273 { 1274 pj_status_t status = pjsua_destroy(); 1275 if (status != PJ_SUCCESS) 1276 return status; 1277 1278 /* Create pjsua */ 1279 status = pjsua_create(); 1280 if (status != PJ_SUCCESS) 1281 return status; 1282 1283 /* Create pool for application */ 1284 app_config.pool = pjsua_pool_create("pjsua-app", 1000, 1000); 1285 1286 return status; 1287 } 1216 /** CLI callback **/ 1217 1218 /* Called on CLI (re)started, e.g: initial start, after iOS bg */ 1219 PJ_DEF(void) cli_on_started(pj_status_t status) 1220 { 1221 /* Notify app */ 1222 if (app_cfg.on_started) { 1223 if (status == PJ_SUCCESS) { 1224 char info[128]; 1225 cli_get_info(info, sizeof(info)); 1226 if (app_cfg.on_started) { 1227 (*app_cfg.on_started)(status, info); 1228 } 1229 } else { 1230 if (app_cfg.on_started) { 1231 (*app_cfg.on_started)(status, NULL); 1232 } 1233 } 1234 } 1235 } 1236 1237 /* Called on CLI quit */ 1238 PJ_DEF(pj_bool_t) cli_on_stopped(pj_bool_t restart, int argc, char* argv[]) 1239 { 1240 /* Notify app */ 1241 if (app_cfg.on_stopped) 1242 return (*app_cfg.on_stopped)(restart, argc, argv); 1243 1244 return PJ_SUCCESS; 1245 } 1246 1247 1248 /* Called on pjsua legacy quit */ 1249 PJ_DEF(pj_bool_t) legacy_on_stopped(pj_bool_t restart) 1250 { 1251 /* Notify app */ 1252 if (app_cfg.on_stopped) 1253 return (*app_cfg.on_stopped)(restart, 0, NULL); 1254 1255 return PJ_SUCCESS; 1256 } 1257 1258 1288 1259 1289 1260 /***************************************************************************** 1290 1261 * Public API 1291 1262 */ 1292 1293 #if defined(PJ_WIN32) && PJ_WIN32!=01294 PJ_DEF(void) setup_signal_handler(void)1295 {1296 SetConsoleCtrlHandler(&CtrlHandler, TRUE);1297 }1298 #else1299 PJ_DEF(void) setup_signal_handler(void)1300 {1301 }1302 #endif1303 1263 1304 1264 int stdout_refresh_proc(void *arg) … … 1323 1283 } 1324 1284 1325 PJ_DEF(pj_status_t) app_init(pj_cli_telnet_on_started on_started_cb, 1326 pj_cli_on_quit on_quit_cb, 1327 pj_cli_on_destroy on_destroy_cb, 1328 pj_cli_on_restart_pjsua on_restart_pjsua_cb) 1285 static pj_status_t pjsua_app_init() 1329 1286 { 1330 1287 pjsua_transport_id transport_id = -1; … … 1333 1290 pj_status_t status; 1334 1291 1335 /** Setuppjsua **/1336 status = setup_pjsua();1292 /** Create pjsua **/ 1293 status = pjsua_create(); 1337 1294 if (status != PJ_SUCCESS) 1338 1295 return status; 1339 1296 1340 /** Load config **/ 1341 status = load_config(&app_config, &uri_arg, app_running); 1297 /* Create pool for application */ 1298 app_config.pool = pjsua_pool_create("pjsua-app", 1000, 1000); 1299 1300 /* Init CLI & its FE settings */ 1301 if (!app_running) { 1302 pj_cli_cfg_default(&app_config.cli_cfg.cfg); 1303 pj_cli_telnet_cfg_default(&app_config.cli_cfg.telnet_cfg); 1304 pj_cli_console_cfg_default(&app_config.cli_cfg.console_cfg); 1305 app_config.cli_cfg.telnet_cfg.on_started = cli_on_started; 1306 } 1307 1308 /** Parse args **/ 1309 status = load_config(app_cfg.argc, app_cfg.argv, &uri_arg); 1342 1310 if (status != PJ_SUCCESS) 1343 return status; 1344 1345 #if defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0 1346 /* Disable threading on Symbian */ 1347 app_config.cfg.thread_cnt = 0; 1348 app_config.media_cfg.thread_cnt = 0; 1349 app_config.media_cfg.has_ioqueue = PJ_FALSE; 1350 #endif 1311 return status; 1351 1312 1352 1313 /* Initialize application callbacks */ … … 1374 1335 app_config.cfg.cb.on_create_media_transport = &on_create_media_transport; 1375 1336 #endif 1376 app_config.log_cfg.cb = log_cb;1377 1337 1378 1338 /* Set sound device latency */ … … 1381 1341 if (app_config.playback_lat) 1382 1342 app_config.media_cfg.snd_play_latency = app_config.playback_lat; 1343 1344 if (app_cfg.on_config_init) 1345 (*app_cfg.on_config_init)(&app_config); 1383 1346 1384 1347 /* Initialize pjsua */ … … 1834 1797 /* Optionally disable some codec */ 1835 1798 for (i=0; i<app_config.codec_dis_cnt; ++i) { 1836 pjsua_codec_set_priority(&app_config.codec_dis[i],PJMEDIA_CODEC_PRIO_DISABLED); 1799 pjsua_codec_set_priority(&app_config.codec_dis[i], 1800 PJMEDIA_CODEC_PRIO_DISABLED); 1837 1801 #if PJSUA_HAS_VIDEO 1838 pjsua_vid_codec_set_priority(&app_config.codec_dis[i],PJMEDIA_CODEC_PRIO_DISABLED); 1802 pjsua_vid_codec_set_priority(&app_config.codec_dis[i], 1803 PJMEDIA_CODEC_PRIO_DISABLED); 1839 1804 #endif 1840 1805 } … … 1846 1811 #if PJSUA_HAS_VIDEO 1847 1812 pjsua_vid_codec_set_priority(&app_config.codec_arg[i], 1848 1813 (pj_uint8_t)(PJMEDIA_CODEC_PRIO_NORMAL+i+9)); 1849 1814 #endif 1850 1815 } … … 1871 1836 pjsua_call_setting_default(&call_opt); 1872 1837 call_opt.aud_cnt = app_config.aud_cnt; 1873 call_opt.vid_cnt = app_config.vid.vid_cnt; 1838 call_opt.vid_cnt = app_config.vid.vid_cnt; 1839 1840 return PJ_SUCCESS; 1841 1842 on_error: 1843 app_destroy(); 1844 return status; 1845 } 1846 1847 PJ_DEF(pj_status_t) app_init(const app_cfg_t *cfg) 1848 { 1849 pj_status_t status; 1850 pj_memcpy(&app_cfg, cfg, sizeof(app_cfg)); 1851 1852 status = pjsua_app_init(); 1853 if (status != PJ_SUCCESS) 1854 return status; 1874 1855 1875 1856 /* Init CLI if configured */ 1876 1857 if (app_config.use_cli) { 1877 if (app_restart) { 1878 pj_uint16_t port = (pj_uint16_t)app_config.cli_telnet_port; 1879 status = setup_cli(!app_config.disable_cli_console, 1880 app_config.cli_telnet_port >= 0, port, 1881 on_started_cb, on_quit_cb, on_destroy_cb, 1882 on_restart_pjsua_cb); 1883 if (status != PJ_SUCCESS) 1884 goto on_error; 1885 } 1886 } 1887 1888 return PJ_SUCCESS; 1889 1890 on_error: 1891 app_restart = PJ_FALSE; 1892 app_destroy(); 1858 status = cli_init(); 1859 } 1893 1860 return status; 1894 1861 } 1895 1862 1896 pj_status_t app_ main(void)1863 pj_status_t app_run(pj_bool_t wait_telnet_cli) 1897 1864 { 1898 1865 pj_thread_t *stdout_refresh_thread = NULL; 1866 pj_status_t status; 1899 1867 1900 1868 /* Start console refresh thread */ … … 1904 1872 } 1905 1873 1874 status = pjsua_start(); 1875 if (status != PJ_SUCCESS) 1876 goto on_return; 1877 1878 if (app_config.use_cli && (app_config.cli_cfg.cli_fe & CLI_FE_TELNET)) { 1879 char info[128]; 1880 cli_get_info(info, sizeof(info)); 1881 if (app_cfg.on_started) { 1882 (*app_cfg.on_started)(status, info); 1883 } 1884 } else { 1885 if (app_cfg.on_started) { 1886 (*app_cfg.on_started)(status, "Ready"); 1887 } 1888 } 1889 1890 /* If user specifies URI to call, then call the URI */ 1891 if (uri_arg.slen) { 1892 pjsua_call_setting_default(&call_opt); 1893 call_opt.aud_cnt = app_config.aud_cnt; 1894 call_opt.vid_cnt = app_config.vid.vid_cnt; 1895 1896 pjsua_call_make_call(current_acc, &uri_arg, &call_opt, NULL, 1897 NULL, NULL); 1898 } 1899 1900 app_running = PJ_TRUE; 1901 1906 1902 if (app_config.use_cli) 1907 start_cli_main(&uri_arg, &app_restart);1903 cli_main(wait_telnet_cli); 1908 1904 else 1909 start_ui_main(&uri_arg, &app_restart); 1910 1905 legacy_main(); 1906 1907 status = PJ_SUCCESS; 1908 1909 on_return: 1911 1910 if (stdout_refresh_thread) { 1912 1911 stdout_refresh_quit = PJ_TRUE; … … 1914 1913 pj_thread_destroy(stdout_refresh_thread); 1915 1914 } 1916 1917 return PJ_SUCCESS; 1918 } 1919 1920 pj_status_t app_destroy() 1921 { 1922 pj_status_t status; 1915 return status; 1916 } 1917 1918 static pj_status_t pjsua_app_destroy() 1919 { 1920 pj_status_t status = PJ_SUCCESS; 1923 1921 unsigned i; 1924 1922 … … 1973 1971 } 1974 1972 1975 if (app_config.use_cli) {1976 destroy_cli(app_restart);1977 }1978 1979 1973 if (app_config.pool) { 1980 1974 pj_pool_release(app_config.pool); 1981 1975 app_config.pool = NULL; 1982 1976 } 1983 1977 1984 1978 status = pjsua_destroy(); 1985 1986 if (!app_restart) 1987 pj_bzero(&app_config, sizeof(app_config)); 1979 1980 return status; 1981 } 1982 1983 pj_status_t app_destroy() 1984 { 1985 pj_status_t status; 1986 1987 status = pjsua_app_destroy(); 1988 1989 if (app_config.use_cli) { 1990 cli_destroy(); 1991 } 1988 1992 1989 1993 return status; 1990 1994 } 1991 1995 1992 /** === CLI Callback == **/1993 1994 static void cli_telnet_started(pj_cli_telnet_info *telnet_info)1995 {1996 PJ_LOG(3,(THIS_FILE, "Telnet daemon listening at %.*s:%d",1997 telnet_info->ip_address.slen, telnet_info->ip_address.ptr,1998 telnet_info->port));1999 }2000 2001 static void cli_on_quit (pj_bool_t is_restarted)2002 {2003 PJ_LOG(3,(THIS_FILE, "CLI quit, restart(%d)", is_restarted));2004 }2005 2006 static void cli_on_destroy(void)2007 {2008 PJ_LOG(3,(THIS_FILE, "CLI destroyed"));2009 }2010 2011 static void cli_on_restart_pjsua(void)2012 {2013 PJ_LOG(3,(THIS_FILE, "Restart pjsua"));2014 }2015 2016 pj_cli_telnet_on_started on_started_cb = &cli_telnet_started;2017 pj_cli_on_quit on_quit_cb = &cli_on_quit;2018 pj_cli_on_destroy on_destroy_cb = &cli_on_destroy;2019 pj_cli_on_restart_pjsua on_restart_pjsua_cb = &cli_on_restart_pjsua;2020 2021 1996 /** ======================= **/ 2022 2023 int main_func(int argc, char *argv[])2024 {2025 pj_status_t status;2026 2027 setup_socket_signal();2028 2029 receive_end_sig = PJ_FALSE;2030 app_restart = PJ_TRUE;2031 2032 add_startup_config(argc, argv);2033 2034 do {2035 if (app_restart) {2036 status = app_init(on_started_cb, on_quit_cb,2037 on_destroy_cb, on_restart_pjsua_cb);2038 if (status != PJ_SUCCESS)2039 return 1;2040 }2041 2042 app_running = PJ_TRUE;2043 2044 app_main();2045 if (!receive_end_sig) {2046 app_destroy();2047 2048 /* This is on purpose */2049 app_destroy();2050 } else {2051 pj_thread_join(sig_thread);2052 }2053 } while (app_restart);2054 return 0;2055 }2056 1997 2057 1998 #ifdef STEREO_DEMO -
pjproject/trunk/pjsip-apps/src/pjsua/pjsua_cli.c
r4461 r4476 20 20 21 21 #include "pjsua_common.h" 22 #include <pjlib-util/cli.h>23 #include <pjlib-util/cli_imp.h>24 #include <pjlib-util/cli_console.h>25 #include <pjlib-util/cli_telnet.h>26 #include <pjlib-util/scanner.h>27 22 28 23 #define THIS_FILE "pjsua_cli.c" 29 24 30 pj_cli_telnet_on_started cli_telnet_on_started_cb = NULL; 31 pj_cli_on_quit cli_on_quit_cb = NULL; 32 pj_cli_on_destroy cli_on_destroy_cb = NULL; 33 pj_cli_on_restart_pjsua cli_on_restart_pjsua_cb = NULL; 34 35 pj_bool_t pjsua_restarted = PJ_TRUE; 25 #define CHECK_PJSUA_RUNNING() if (pjsua_get_state()!=PJSUA_STATE_RUNNING) \ 26 return PJ_EINVALIDOP 27 28 /* CLI command id */ 29 /* level 1 command */ 30 #define CMD_CALL 100 31 #define CMD_PRESENCE 200 32 #define CMD_ACCOUNT 300 33 #define CMD_MEDIA 400 34 #define CMD_CONFIG 500 35 #define CMD_VIDEO 600 36 #define CMD_SLEEP 700 37 #define CMD_ECHO 800 38 #define CMD_NETWORK 900 39 #define CMD_QUIT 110 40 #define CMD_RESTART 120 41 42 /* call level 2 command */ 43 #define CMD_CALL_NEW ((CMD_CALL*10)+1) 44 #define CMD_CALL_MULTI ((CMD_CALL*10)+2) 45 #define CMD_CALL_ANSWER ((CMD_CALL*10)+3) 46 #define CMD_CALL_HANGUP ((CMD_CALL*10)+4) 47 #define CMD_CALL_HANGUP_ALL ((CMD_CALL*10)+5) 48 #define CMD_CALL_HOLD ((CMD_CALL*10)+6) 49 #define CMD_CALL_REINVITE ((CMD_CALL*10)+7) 50 #define CMD_CALL_UPDATE ((CMD_CALL*10)+8) 51 #define CMD_CALL_NEXT ((CMD_CALL*10)+9) 52 #define CMD_CALL_PREVIOUS ((CMD_CALL*10)+10) 53 #define CMD_CALL_TRANSFER ((CMD_CALL*10)+11) 54 #define CMD_CALL_TRANSFER_REPLACE ((CMD_CALL*10)+12) 55 #define CMD_CALL_REDIRECT ((CMD_CALL*10)+13) 56 #define CMD_CALL_D2833 ((CMD_CALL*10)+14) 57 #define CMD_CALL_INFO ((CMD_CALL*10)+15) 58 #define CMD_CALL_DUMP_Q ((CMD_CALL*10)+16) 59 #define CMD_CALL_SEND_ARB ((CMD_CALL*10)+17) 60 #define CMD_CALL_LIST ((CMD_CALL*10)+18) 61 62 /* im & presence level 2 command */ 63 #define CMD_PRESENCE_ADD_BUDDY ((CMD_PRESENCE*10)+1) 64 #define CMD_PRESENCE_DEL_BUDDY ((CMD_PRESENCE*10)+2) 65 #define CMD_PRESENCE_SEND_IM ((CMD_PRESENCE*10)+3) 66 #define CMD_PRESENCE_SUB ((CMD_PRESENCE*10)+4) 67 #define CMD_PRESENCE_UNSUB ((CMD_PRESENCE*10)+5) 68 #define CMD_PRESENCE_TOG_STATE ((CMD_PRESENCE*10)+6) 69 #define CMD_PRESENCE_TEXT ((CMD_PRESENCE*10)+7) 70 #define CMD_PRESENCE_LIST ((CMD_PRESENCE*10)+8) 71 72 /* account level 2 command */ 73 #define CMD_ACCOUNT_ADD ((CMD_ACCOUNT*10)+1) 74 #define CMD_ACCOUNT_DEL ((CMD_ACCOUNT*10)+2) 75 #define CMD_ACCOUNT_MOD ((CMD_ACCOUNT*10)+3) 76 #define CMD_ACCOUNT_REG ((CMD_ACCOUNT*10)+4) 77 #define CMD_ACCOUNT_UNREG ((CMD_ACCOUNT*10)+5) 78 #define CMD_ACCOUNT_NEXT ((CMD_ACCOUNT*10)+6) 79 #define CMD_ACCOUNT_PREV ((CMD_ACCOUNT*10)+7) 80 #define CMD_ACCOUNT_SHOW ((CMD_ACCOUNT*10)+8) 81 82 /* conference & media level 2 command */ 83 #define CMD_MEDIA_LIST ((CMD_MEDIA*10)+1) 84 #define CMD_MEDIA_CONF_CONNECT ((CMD_MEDIA*10)+2) 85 #define CMD_MEDIA_CONF_DISCONNECT ((CMD_MEDIA*10)+3) 86 #define CMD_MEDIA_ADJUST_VOL ((CMD_MEDIA*10)+4) 87 #define CMD_MEDIA_CODEC_PRIO ((CMD_MEDIA*10)+5) 88 89 /* status & config level 2 command */ 90 #define CMD_CONFIG_DUMP_STAT ((CMD_CONFIG*10)+1) 91 #define CMD_CONFIG_DUMP_DETAIL ((CMD_CONFIG*10)+2) 92 #define CMD_CONFIG_DUMP_CONF ((CMD_CONFIG*10)+3) 93 #define CMD_CONFIG_WRITE_SETTING ((CMD_CONFIG*10)+4) 94 95 /* video level 2 command */ 96 #define CMD_VIDEO_ENABLE ((CMD_VIDEO*10)+1) 97 #define CMD_VIDEO_DISABLE ((CMD_VIDEO*10)+2) 98 #define CMD_VIDEO_ACC ((CMD_VIDEO*10)+3) 99 #define CMD_VIDEO_CALL ((CMD_VIDEO*10)+4) 100 #define CMD_VIDEO_DEVICE ((CMD_VIDEO*10)+5) 101 #define CMD_VIDEO_CODEC ((CMD_VIDEO*10)+6) 102 #define CMD_VIDEO_WIN ((CMD_VIDEO*10)+7) 103 104 /* video level 3 command */ 105 #define CMD_VIDEO_ACC_SHOW ((CMD_VIDEO_ACC*10)+1) 106 #define CMD_VIDEO_ACC_AUTORX ((CMD_VIDEO_ACC*10)+2) 107 #define CMD_VIDEO_ACC_AUTOTX ((CMD_VIDEO_ACC*10)+3) 108 #define CMD_VIDEO_ACC_CAP_ID ((CMD_VIDEO_ACC*10)+4) 109 #define CMD_VIDEO_ACC_REN_ID ((CMD_VIDEO_ACC*10)+5) 110 #define CMD_VIDEO_CALL_RX ((CMD_VIDEO_CALL*10)+1) 111 #define CMD_VIDEO_CALL_TX ((CMD_VIDEO_CALL*10)+2) 112 #define CMD_VIDEO_CALL_ADD ((CMD_VIDEO_CALL*10)+3) 113 #define CMD_VIDEO_CALL_ENABLE ((CMD_VIDEO_CALL*10)+4) 114 #define CMD_VIDEO_CALL_DISABLE ((CMD_VIDEO_CALL*10)+5) 115 #define CMD_VIDEO_CALL_CAP ((CMD_VIDEO_CALL*10)+6) 116 #define CMD_VIDEO_DEVICE_LIST ((CMD_VIDEO_DEVICE*10)+1) 117 #define CMD_VIDEO_DEVICE_REFRESH ((CMD_VIDEO_DEVICE*10)+2) 118 #define CMD_VIDEO_DEVICE_PREVIEW ((CMD_VIDEO_DEVICE*10)+3) 119 #define CMD_VIDEO_CODEC_LIST ((CMD_VIDEO_CODEC*10)+1) 120 #define CMD_VIDEO_CODEC_PRIO ((CMD_VIDEO_CODEC*10)+2) 121 #define CMD_VIDEO_CODEC_FPS ((CMD_VIDEO_CODEC*10)+3) 122 #define CMD_VIDEO_CODEC_BITRATE ((CMD_VIDEO_CODEC*10)+4) 123 #define CMD_VIDEO_CODEC_SIZE ((CMD_VIDEO_CODEC*10)+5) 124 #define CMD_VIDEO_WIN_LIST ((CMD_VIDEO_WIN*10)+1) 125 #define CMD_VIDEO_WIN_ARRANGE ((CMD_VIDEO_WIN*10)+2) 126 #define CMD_VIDEO_WIN_SHOW ((CMD_VIDEO_WIN*10)+3) 127 #define CMD_VIDEO_WIN_HIDE ((CMD_VIDEO_WIN*10)+4) 128 #define CMD_VIDEO_WIN_MOVE ((CMD_VIDEO_WIN*10)+5) 129 #define CMD_VIDEO_WIN_RESIZE ((CMD_VIDEO_WIN*10)+6) 130 131 /* dynamic choice argument list */ 132 #define DYN_CHOICE_START 9900 133 #define DYN_CHOICE_BUDDY_ID (DYN_CHOICE_START)+1 134 #define DYN_CHOICE_ACCOUNT_ID (DYN_CHOICE_START)+2 135 #define DYN_CHOICE_MEDIA_PORT (DYN_CHOICE_START)+3 136 #define DYN_CHOICE_AUDIO_CODEC_ID (DYN_CHOICE_START)+4 137 #define DYN_CHOICE_CAP_DEV_ID (DYN_CHOICE_START)+5 138 #define DYN_CHOICE_REN_DEV_ID (DYN_CHOICE_START)+6 139 #define DYN_CHOICE_VID_DEV_ID (DYN_CHOICE_START)+7 140 #define DYN_CHOICE_STREAM_ID (DYN_CHOICE_START)+8 141 #define DYN_CHOICE_VIDEO_CODEC_ID (DYN_CHOICE_START)+9 142 #define DYN_CHOICE_WIN_ID (DYN_CHOICE_START)+10 143 #define DYN_CHOICE_CALL_ID (DYN_CHOICE_START)+11 144 #define DYN_CHOICE_ADDED_BUDDY_ID (DYN_CHOICE_START)+12 145 36 146 static pj_bool_t pj_inited = PJ_FALSE; 37 147 static pj_caching_pool cli_cp; 148 static pj_bool_t cli_cp_inited = PJ_FALSE; 38 149 static pj_cli_t *cli = NULL; 39 150 static pj_cli_sess *cli_cons_sess = NULL; 151 static pj_cli_front_end *telnet_front_end = NULL; 40 152 41 153 /** Forward declaration **/ 42 pj_status_t setup_command(pj_cli_t *cli); 43 44 static void log_writer(int level, const char *buffer, int len) 154 pj_status_t cli_setup_command(pj_cli_t *cli); 155 void cli_destroy(); 156 157 PJ_DEF(void) cli_get_info(char *info, pj_size_t size) 158 { 159 pj_cli_telnet_info telnet_info; 160 pj_cli_telnet_get_info(telnet_front_end, &telnet_info); 161 162 pj_ansi_snprintf(info, size, "Telnet to %.*s:%d", 163 telnet_info.ip_address.slen, telnet_info.ip_address.ptr, 164 telnet_info.port); 165 } 166 167 static void cli_log_writer(int level, const char *buffer, int len) 45 168 { 46 169 if (cli) 47 pj_cli_write_log(cli, level, buffer, len); 48 49 if (app_config.disable_cli_console) 50 pj_log_write(level, buffer, len); 51 } 52 53 void destroy_cli(pj_bool_t app_restart) 54 { 55 pj_log_set_log_func(&pj_log_write); 56 57 if (cli) { 58 pj_cli_destroy(cli); 59 cli = NULL; 60 } 61 62 if (cli_cp.factory.create_pool) { 63 pj_caching_pool_destroy(&cli_cp); 64 pj_bzero(&cli_cp, sizeof(cli_cp)); 65 } 66 67 if (pj_inited) { 68 pj_shutdown(); 69 pj_inited = PJ_FALSE; 70 } 71 if (!app_restart) { 72 if (cli_on_destroy_cb) 73 (*cli_on_destroy_cb)(); 74 } 75 } 76 77 pj_status_t setup_cli(pj_bool_t with_console, pj_bool_t with_telnet, 78 pj_uint16_t telnet_port, 79 pj_cli_telnet_on_started on_started_cb, 80 pj_cli_on_quit on_quit_cb, 81 pj_cli_on_destroy on_destroy_cb, 82 pj_cli_on_restart_pjsua on_restart_pjsua_cb) 83 { 84 pj_cli_cfg cli_cfg; 170 pj_cli_write_log(cli, level, buffer, len); 171 } 172 173 pj_status_t cli_init() 174 { 85 175 pj_status_t status; 86 176 87 /* Destroy CLI if initialized */ 88 destroy_cli(PJ_TRUE); 177 pj_cli_cfg *cfg = &app_config.cli_cfg.cfg; 89 178 90 179 /* Init PJLIB */ … … 102 191 /* Init CLI */ 103 192 pj_caching_pool_init(&cli_cp, NULL, 0); 104 pj_cli_cfg_default(&cli_cfg);105 c li_cfg.pf = &cli_cp.factory;106 c li_cfg.name = pj_str("pjsua_cli");107 c li_cfg.title = pj_str("Pjsua CLI Application");108 status = pj_cli_create( &cli_cfg, &cli);193 cli_cp_inited = PJ_TRUE; 194 cfg->pf = &cli_cp.factory; 195 cfg->name = pj_str("pjsua_cli"); 196 cfg->title = pj_str("Pjsua CLI Application"); 197 status = pj_cli_create(cfg, &cli); 109 198 if (status != PJ_SUCCESS) 110 199 goto on_error; 111 200 112 status = setup_command(cli);201 status = cli_setup_command(cli); 113 202 if (status != PJ_SUCCESS) 114 203 goto on_error; 115 204 116 if (on_destroy_cb)117 cli_on_destroy_cb = on_destroy_cb;118 119 if (on_restart_pjsua_cb)120 cli_on_restart_pjsua_cb = on_restart_pjsua_cb;121 122 if (on_quit_cb)123 cli_on_quit_cb = on_quit_cb;124 125 205 /* Init telnet frontend */ 126 if ( with_telnet) {127 pj_cli_telnet_cfg telnet_cfg;206 if (app_config.cli_cfg.cli_fe & CLI_FE_TELNET) { 207 pj_cli_telnet_cfg *fe_cfg = &app_config.cli_cfg.telnet_cfg; 128 208 pj_pool_t *pool; 129 209 130 pool = pj_pool_create( &cli_cp.factory, "cli_cp", 128, 128, NULL);210 pool = pj_pool_create(cfg->pf, "cli_cp", 128, 128, NULL); 131 211 pj_assert(pool); 132 212 133 pj_cli_telnet_cfg_default(&telnet_cfg); 134 telnet_cfg.log_level = 5; 135 telnet_cfg.port = telnet_port; 136 if (on_started_cb) 137 cli_telnet_on_started_cb = on_started_cb; 138 139 telnet_cfg.on_started = cli_telnet_on_started_cb; 140 141 status = pj_cli_telnet_create(cli, &telnet_cfg, NULL); 213 status = pj_cli_telnet_create(cli, fe_cfg, &telnet_front_end); 142 214 if (status != PJ_SUCCESS) 143 215 goto on_error; … … 145 217 146 218 /* Init console frontend */ 147 if ( with_console) {148 pj_cli_console_cfg console_cfg;219 if (app_config.cli_cfg.cli_fe & CLI_FE_CONSOLE) { 220 pj_cli_console_cfg *fe_cfg = &app_config.cli_cfg.console_cfg; 149 221 150 pj_cli_console_cfg_default(&console_cfg); 151 console_cfg.quit_command = pj_str("shutdown"); 152 status = pj_cli_console_create(cli, &console_cfg, 222 fe_cfg->quit_command = pj_str("shutdown"); 223 status = pj_cli_console_create(cli, fe_cfg, 153 224 &cli_cons_sess, NULL); 154 225 if (status != PJ_SUCCESS) … … 159 230 160 231 on_error: 161 destroy_cli(PJ_FALSE);232 cli_destroy(); 162 233 return status; 163 234 } 164 235 165 PJ_DEF(pj_bool_t) is_cli_inited() 166 { 167 return (cli != NULL); 168 } 169 170 static pj_status_t setup_timer(pj_timer_heap_t **timer, 171 pj_ioqueue_t **ioqueue) 172 { 173 pj_status_t status = pj_timer_heap_create(app_config.pool, 16, timer); 174 236 pj_status_t cli_main(pj_bool_t wait_telnet_cli) 237 { 238 char cmdline[PJ_CLI_MAX_CMDBUF]; 239 240 /* ReInit logging */ 241 app_config.log_cfg.cb = &cli_log_writer; 242 pjsua_reconfigure_logging(&app_config.log_cfg); 243 244 if (app_config.cli_cfg.cli_fe & CLI_FE_CONSOLE) { 245 /* Main loop for CLI FE console */ 246 while (!pj_cli_is_quitting(cli)) { 247 pj_cli_console_process(cli_cons_sess, &cmdline[0], sizeof(cmdline)); 248 } 249 } else if (wait_telnet_cli) { 250 /* Just wait for CLI quit */ 251 while (!pj_cli_is_quitting(cli)) { 252 pj_thread_sleep(200); 253 } 254 } 255 256 return PJ_SUCCESS; 257 } 258 259 void cli_destroy() 260 { 261 /* Destroy CLI, it will automatically destroy any FEs */ 262 if (cli) { 263 pj_cli_destroy(cli); 264 cli = NULL; 265 } 266 267 /* Destroy CLI caching pool factory */ 268 if (cli_cp_inited) { 269 pj_caching_pool_destroy(&cli_cp); 270 cli_cp_inited = PJ_FALSE; 271 } 272 273 /* Shutdown PJLIB */ 274 if (pj_inited) { 275 pj_shutdown(); 276 pj_inited = PJ_FALSE; 277 } 278 } 279 280 /* Get input URL */ 281 static void get_input_url(char *buf, 282 int len, 283 pj_cli_cmd_val *cval, 284 struct input_result *result) 285 { 286 static const pj_str_t err_invalid_input = {"Invalid input\n", 15}; 287 result->nb_result = PJSUA_APP_NO_NB; 288 result->uri_result = NULL; 289 290 len = strlen(buf); 291 292 /* Left trim */ 293 while (pj_isspace(*buf)) { 294 ++buf; 295 --len; 296 } 297 298 /* Remove trailing newlines */ 299 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n')) 300 buf[--len] = '\0'; 301 302 if (len == 0 || buf[0]=='q') 303 return; 304 305 if (pj_isdigit(*buf) || *buf=='-') { 306 307 int i; 308 309 if (*buf=='-') 310 i = 1; 311 else 312 i = 0; 313 314 for (; i<len; ++i) { 315 if (!pj_isdigit(buf[i])) { 316 pj_cli_sess_write_msg(cval->sess, err_invalid_input.ptr, 317 err_invalid_input.slen); 318 return; 319 } 320 } 321 322 result->nb_result = my_atoi(buf); 323 324 if (result->nb_result >= 0 && 325 result->nb_result <= (int)pjsua_get_buddy_count()) 326 { 327 return; 328 } 329 if (result->nb_result == -1) 330 return; 331 332 pj_cli_sess_write_msg(cval->sess, err_invalid_input.ptr, 333 err_invalid_input.slen); 334 result->nb_result = PJSUA_APP_NO_NB; 335 return; 336 337 } else { 338 pj_status_t status; 339 340 if ((status=pjsua_verify_url(buf)) != PJ_SUCCESS) { 341 pjsua_perror(THIS_FILE, "Invalid URL", status); 342 return; 343 } 344 345 result->uri_result = buf; 346 } 347 } 348 349 /* CLI dynamic choice handler */ 350 /* Get buddy id */ 351 static void get_buddy_id(pj_cli_dyn_choice_param *param) 352 { 353 if (param->cnt < param->max_cnt) { 354 pjsua_buddy_id ids[64]; 355 int i = 0; 356 unsigned count = PJ_ARRAY_SIZE(ids); 357 char data_out[64]; 358 359 pjsua_enum_buddies(ids, &count); 360 361 if (count > 0) { 362 for (i=0; i<(int)count; ++i) { 363 pjsua_buddy_info info; 364 365 if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS) 366 continue; 367 368 /* Fill buddy id */ 369 pj_ansi_snprintf(data_out, sizeof(data_out), "%d", ids[i]+1); 370 pj_strdup2(param->pool, ¶m->choice[i].value, data_out); 371 pj_bzero(data_out, PJ_ARRAY_SIZE(data_out)); 372 373 /* Format & fill description */ 374 pj_ansi_snprintf(data_out, 375 sizeof(data_out), 376 "<%.*s> %.*s", 377 (int)info.status_text.slen, 378 info.status_text.ptr, 379 (int)info.uri.slen, 380 info.uri.ptr); 381 382 pj_strdup2(param->pool, ¶m->choice[i].desc, data_out); 383 if (++param->cnt >= (param->max_cnt-1)) 384 break; 385 } 386 } 387 if (param->arg_id == DYN_CHOICE_BUDDY_ID) { 388 /* Add URL input option */ 389 pj_ansi_snprintf(data_out, sizeof(data_out), "URL"); 390 pj_strdup2(param->pool, ¶m->choice[i].value, data_out); 391 pj_ansi_snprintf(data_out, sizeof(data_out), "An URL"); 392 pj_strdup2(param->pool, ¶m->choice[i].desc, data_out); 393 ++param->cnt; 394 } 395 } 396 } 397 398 static void get_account_id(pj_cli_dyn_choice_param *param) 399 { 400 if (param->cnt < param->max_cnt) { 401 char buf[8]; 402 char buf_out[80]; 403 pjsua_acc_info info; 404 405 pjsua_acc_id acc_ids[16]; 406 unsigned count = PJ_ARRAY_SIZE(acc_ids); 407 int i; 408 409 pjsua_enum_accs(acc_ids, &count); 410 411 for (i=0; i<(int)count; ++i) { 412 pj_bzero(&buf_out[0], PJ_ARRAY_SIZE(buf_out)); 413 414 pjsua_acc_get_info(acc_ids[i], &info); 415 416 pj_ansi_snprintf(buf_out, 417 sizeof(buf_out), 418 "%c%.*s", 419 (acc_ids[i]==current_acc?'*':' '), 420 (int)info.acc_uri.slen, 421 info.acc_uri.ptr); 422 423 pj_bzero(buf, sizeof(buf)); 424 pj_ansi_snprintf(buf, sizeof(buf), "%d", acc_ids[i]); 425 pj_strdup2(param->pool, ¶m->choice[i].value, buf); 426 pj_strdup2(param->pool, ¶m->choice[i].desc, buf_out); 427 if (++param->cnt >= param->max_cnt) 428 break; 429 } 430 } 431 } 432 433 static void get_media_port(pj_cli_dyn_choice_param *param) 434 { 435 unsigned i, count; 436 pjsua_conf_port_id id[PJSUA_MAX_CONF_PORTS]; 437 438 count = PJ_ARRAY_SIZE(id); 439 pjsua_enum_conf_ports(id, &count); 440 441 for (i=0; i<count; ++i) { 442 char slot_id[8]; 443 char desc[256]; 444 char txlist[256]; 445 unsigned j; 446 pjsua_conf_port_info info; 447 448 pjsua_conf_get_port_info(id[i], &info); 449 450 pj_ansi_snprintf(slot_id, sizeof(slot_id), 451 "%d", info.slot_id); 452 pj_strdup2(param->pool, ¶m->choice[i].value, slot_id); 453 454 txlist[0] = '\0'; 455 for (j=0; j<info.listener_cnt; ++j) { 456 char s[10]; 457 pj_ansi_snprintf(s, sizeof(s), "#%d ", info.listeners[j]); 458 pj_ansi_strcat(txlist, s); 459 } 460 461 pj_ansi_snprintf(desc, 462 sizeof(desc), 463 "[%2dKHz/%dms/%d] %20.*s transmitting to: %s", 464 info.clock_rate/1000, 465 info.samples_per_frame*1000/info.channel_count/info.clock_rate, 466 info.channel_count, 467 (int)info.name.slen, 468 info.name.ptr, 469 txlist); 470 471 pj_strdup2(param->pool, ¶m->choice[i].desc, desc); 472 if (++param->cnt >= param->max_cnt) 473 break; 474 } 475 } 476 477 static void get_audio_codec_id(pj_cli_dyn_choice_param *param) 478 { 479 if (param->cnt < param->max_cnt) { 480 pjsua_codec_info c[32]; 481 unsigned i, count = PJ_ARRAY_SIZE(c); 482 char codec_id[64]; 483 char desc[128]; 484 485 pjsua_enum_codecs(c, &count); 486 for (i=0; i<count; ++i) { 487 pj_ansi_snprintf(codec_id, sizeof(codec_id), 488 "%.*s", (int)c[i].codec_id.slen, 489 c[i].codec_id.ptr); 490 491 pj_strdup2(param->pool, ¶m->choice[param->cnt].value, codec_id); 492 493 pj_ansi_snprintf(desc, sizeof(desc), 494 "Audio, prio: %d%s%.*s", 495 c[i].priority, 496 c[i].desc.slen? " - ":"", 497 (int)c[i].desc.slen, 498 c[i].desc.ptr); 499 500 pj_strdup2(param->pool, ¶m->choice[param->cnt].desc, desc); 501 if (++param->cnt >= param->max_cnt) 502 break; 503 } 504 } 505 } 506 507 #if PJSUA_HAS_VIDEO 508 static void get_video_stream_id(pj_cli_dyn_choice_param *param) 509 { 510 if (param->cnt < param->max_cnt) { 511 pjsua_call_info call_info; 512 513 if (current_call != PJSUA_INVALID_ID) { 514 unsigned i; 515 pjsua_call_get_info(current_call, &call_info); 516 for (i=0; i<call_info.media_cnt; ++i) { 517 if (call_info.media[i].type == PJMEDIA_TYPE_VIDEO) { 518 char med_idx[8]; 519 pj_ansi_snprintf(med_idx, sizeof(med_idx), "%d", 520 call_info.media[i].index); 521 pj_strdup2(param->pool, ¶m->choice[i].value, med_idx); 522 523 switch (call_info.media[i].status) { 524 case PJSUA_CALL_MEDIA_NONE: 525 pj_strdup2(param->pool, ¶m->choice[i].desc, 526 "Status:None"); 527 break; 528 case PJSUA_CALL_MEDIA_ACTIVE: 529 pj_strdup2(param->pool, ¶m->choice[i].desc, 530 "Status:Active"); 531 break; 532 case PJSUA_CALL_MEDIA_LOCAL_HOLD: 533 pj_strdup2(param->pool, ¶m->choice[i].desc, 534 "Status:Local Hold"); 535 break; 536 case PJSUA_CALL_MEDIA_REMOTE_HOLD: 537 pj_strdup2(param->pool, ¶m->choice[i].desc, 538 "Status:Remote Hold"); 539 break; 540 case PJSUA_CALL_MEDIA_ERROR: 541 pj_strdup2(param->pool, ¶m->choice[i].desc, 542 "Status:Media Error"); 543 break; 544 } 545 if (++param->cnt >= param->max_cnt) 546 break; 547 } 548 } 549 } 550 } 551 } 552 553 static void get_video_dev_hint(pj_cli_dyn_choice_param *param, 554 pjmedia_vid_dev_info *vdi, 555 unsigned vid_dev_id) 556 { 557 char desc[128]; 558 char dev_id[8]; 559 pj_ansi_snprintf(dev_id, sizeof(dev_id), 560 "%d", vid_dev_id); 561 pj_ansi_snprintf(desc, sizeof(desc), "%s [%s]", 562 vdi->name, vdi->driver); 563 564 pj_strdup2(param->pool, ¶m->choice[param->cnt].value, 565 dev_id); 566 pj_strdup2(param->pool, ¶m->choice[param->cnt].desc, 567 desc); 568 } 569 570 static void get_video_dev_id(pj_cli_dyn_choice_param *param, 571 pj_bool_t all, 572 pj_bool_t capture) 573 { 574 if (param->cnt < param->max_cnt) { 575 unsigned i, count; 576 pjmedia_vid_dev_info vdi; 577 pj_status_t status; 578 579 count = pjsua_vid_dev_count(); 580 if (count == 0) { 581 return; 582 } 583 584 for (i=0; i<count; ++i) { 585 status = pjsua_vid_dev_get_info(i, &vdi); 586 if (status == PJ_SUCCESS) { 587 if ((all) || 588 ((capture) && (vdi.dir == PJMEDIA_DIR_CAPTURE)) || 589 ((!capture) && (vdi.dir == PJMEDIA_DIR_RENDER))) 590 { 591 get_video_dev_hint(param, &vdi, i); 592 if (++param->cnt >= param->max_cnt) 593 break; 594 } 595 } 596 } 597 } 598 } 599 600 static void get_video_codec_id(pj_cli_dyn_choice_param *param) 601 { 602 if (param->cnt < param->max_cnt) { 603 pjsua_codec_info ci[32]; 604 unsigned i, count = PJ_ARRAY_SIZE(ci); 605 char codec_id[64]; 606 char desc[128]; 607 608 pjsua_vid_enum_codecs(ci, &count); 609 for (i=0; i<count; ++i) { 610 pjmedia_vid_codec_param cp; 611 pjmedia_video_format_detail *vfd; 612 pj_status_t status = PJ_SUCCESS; 613 614 status = pjsua_vid_codec_get_param(&ci[i].codec_id, &cp); 615 if (status != PJ_SUCCESS) 616 continue; 617 618 vfd = pjmedia_format_get_video_format_detail(&cp.enc_fmt, PJ_TRUE); 619 620 pj_ansi_snprintf(codec_id, sizeof(codec_id), 621 "%.*s", (int)ci[i].codec_id.slen, 622 ci[i].codec_id.ptr); 623 624 pj_strdup2(param->pool, ¶m->choice[param->cnt].value, codec_id); 625 626 pj_ansi_snprintf(desc, sizeof(desc), 627 "Video, p[%d], f[%.2f], b[%d/%d], s[%dx%d]", 628 ci[i].priority, 629 (vfd->fps.num*1.0/vfd->fps.denum), 630 vfd->avg_bps/1000, vfd->max_bps/1000, 631 vfd->size.w, vfd->size.h); 632 633 pj_strdup2(param->pool, ¶m->choice[param->cnt].desc, desc); 634 if (++param->cnt >= param->max_cnt) 635 break; 636 } 637 } 638 } 639 640 static void get_video_window_id(pj_cli_dyn_choice_param *param) 641 { 642 if (param->cnt < param->max_cnt) { 643 pjsua_vid_win_id wids[PJSUA_MAX_VID_WINS]; 644 unsigned i, cnt = PJ_ARRAY_SIZE(wids); 645 char win_id[64]; 646 char desc[128]; 647 648 pjsua_vid_enum_wins(wids, &cnt); 649 650 for (i = 0; i < cnt; ++i) { 651 pjsua_vid_win_info wi; 652 653 pjsua_vid_win_get_info(wids[i], &wi); 654 pj_ansi_snprintf(win_id, sizeof(win_id), "%d", wids[i]); 655 pj_strdup2(param->pool, ¶m->choice[i].value, win_id); 656 657 pj_ansi_snprintf(desc, sizeof(desc), 658 "Show:%c Pos(%d,%d) Size(%dx%d)", 659 (wi.show?'Y':'N'), wi.pos.x, wi.pos.y, 660 wi.size.w, wi.size.h); 661 662 pj_strdup2(param->pool, ¶m->choice[i].desc, desc); 663 if (++param->cnt >= param->max_cnt) 664 break; 665 } 666 } 667 } 668 669 static void get_call_id(pj_cli_dyn_choice_param *param) 670 { 671 if (param->cnt < param->max_cnt) { 672 char call_id[64]; 673 char desc[128]; 674 unsigned i, count; 675 pjsua_call_id ids[PJSUA_MAX_CALLS]; 676 int call = current_call; 677 678 count = PJ_ARRAY_SIZE(ids); 679 pjsua_enum_calls(ids, &count); 680 681 if (count > 1) { 682 for (i=0; i<count; ++i) { 683 pjsua_call_info call_info; 684 685 if (ids[i] == call) 686 return; 687 688 pjsua_call_get_info(ids[i], &call_info); 689 pj_ansi_snprintf(call_id, sizeof(call_id), "%d", ids[i]); 690 pj_strdup2(param->pool, ¶m->choice[i].value, call_id); 691 pj_ansi_snprintf(desc, sizeof(desc), "%.*s [%.*s]", 692 (int)call_info.remote_info.slen, 693 call_info.remote_info.ptr, 694 (int)call_info.state_text.slen, 695 call_info.state_text.ptr); 696 pj_strdup2(param->pool, ¶m->choice[i].desc, desc); 697 if (++param->cnt >= param->max_cnt) 698 break; 699 700 } 701 } 702 } 703 } 704 705 #endif 706 707 static void get_choice_value(pj_cli_dyn_choice_param *param) 708 { 709 switch (param->arg_id) { 710 case DYN_CHOICE_BUDDY_ID: 711 case DYN_CHOICE_ADDED_BUDDY_ID: 712 get_buddy_id(param); 713 break; 714 case DYN_CHOICE_ACCOUNT_ID: 715 get_account_id(param); 716 break; 717 case DYN_CHOICE_MEDIA_PORT: 718 get_media_port(param); 719 break; 720 case DYN_CHOICE_AUDIO_CODEC_ID: 721 get_audio_codec_id(param); 722 break; 723 #if PJSUA_HAS_VIDEO 724 case DYN_CHOICE_CAP_DEV_ID: 725 case DYN_CHOICE_REN_DEV_ID: 726 case DYN_CHOICE_VID_DEV_ID: 727 get_video_dev_id(param, 728 (param->arg_id==DYN_CHOICE_VID_DEV_ID), 729 (param->arg_id==DYN_CHOICE_CAP_DEV_ID)); 730 break; 731 case DYN_CHOICE_STREAM_ID: 732 get_video_stream_id(param); 733 break; 734 case DYN_CHOICE_VIDEO_CODEC_ID: 735 get_video_codec_id(param); 736 break; 737 case DYN_CHOICE_WIN_ID: 738 get_video_window_id(param); 739 break; 740 case DYN_CHOICE_CALL_ID: 741 get_call_id(param); 742 break; 743 #endif 744 default: 745 param->cnt = 0; 746 break; 747 } 748 } 749 750 /* 751 * CLI command handler 752 */ 753 754 /* Add account */ 755 static pj_status_t cmd_add_account(pj_cli_cmd_val *cval) 756 { 757 pjsua_acc_config acc_cfg; 758 pj_status_t status; 759 760 pjsua_acc_config_default(&acc_cfg); 761 acc_cfg.id = cval->argv[1]; 762 acc_cfg.reg_uri = cval->argv[2]; 763 acc_cfg.cred_count = 1; 764 acc_cfg.cred_info[0].scheme = pj_str("Digest"); 765 acc_cfg.cred_info[0].realm = cval->argv[3]; 766 acc_cfg.cred_info[0].username = cval->argv[4]; 767 acc_cfg.cred_info[0].data_type = 0; 768 acc_cfg.cred_info[0].data = cval->argv[5]; 769 770 acc_cfg.rtp_cfg = app_config.rtp_cfg; 771 app_config_init_video(&acc_cfg); 772 773 status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL); 774 if (status != PJ_SUCCESS) { 775 pjsua_perror(THIS_FILE, "Error adding new account", status); 776 } 777 778 return status; 779 } 780 781 /* Delete account */ 782 static pj_status_t cmd_del_account(pj_cli_cmd_val *cval) 783 { 784 char out_str[64]; 785 unsigned str_len; 786 787 int i = my_atoi(cval->argv[1].ptr); 788 789 if (!pjsua_acc_is_valid(i)) { 790 pj_ansi_snprintf(out_str, sizeof(out_str), 791 "Invalid account id %d\n", i); 792 str_len = pj_ansi_strlen(out_str); 793 pj_cli_sess_write_msg(cval->sess, out_str, str_len); 794 } else { 795 pjsua_acc_del(i); 796 pj_ansi_snprintf(out_str, sizeof(out_str), 797 "Account %d deleted\n", i); 798 str_len = pj_ansi_strlen(out_str); 799 pj_cli_sess_write_msg(cval->sess, out_str, str_len); 800 } 801 return PJ_SUCCESS; 802 } 803 804 /* Modify account */ 805 static pj_status_t cmd_mod_account(pj_cli_cmd_val *cval) 806 { 807 PJ_UNUSED_ARG(cval); 808 return PJ_SUCCESS; 809 } 810 811 /* Register account */ 812 static pj_status_t cmd_reg_account() 813 { 814 pjsua_acc_set_registration(current_acc, PJ_TRUE); 815 return PJ_SUCCESS; 816 } 817 818 /* Unregister account */ 819 static pj_status_t cmd_unreg_account() 820 { 821 pjsua_acc_set_registration(current_acc, PJ_FALSE); 822 return PJ_SUCCESS; 823 } 824 825 /* Select account to be used for sending outgoing request */ 826 static pj_status_t cmd_next_account(pj_cli_cmd_val *cval) 827 { 828 int i = my_atoi(cval->argv[1].ptr); 829 if (pjsua_acc_is_valid(i)) { 830 pjsua_acc_set_default(i); 831 PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i)); 832 } else { 833 PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i)); 834 } 835 return PJ_SUCCESS; 836 } 837 838 /* Show account list */ 839 static pj_status_t cmd_show_account(pj_cli_cmd_val *cval) 840 { 841 pjsua_acc_id acc_ids[16]; 842 unsigned count = PJ_ARRAY_SIZE(acc_ids); 843 int i; 844 static const pj_str_t header = {"Account list:\n", 15}; 845 846 pjsua_enum_accs(acc_ids, &count); 847 848 pj_cli_sess_write_msg(cval->sess, header.ptr, header.slen); 849 for (i=0; i<(int)count; ++i) { 850 char acc_info[80]; 851 char out_str[160]; 852 pjsua_acc_info info; 853 854 pjsua_acc_get_info(acc_ids[i], &info); 855 856 if (!info.has_registration) { 857 pj_ansi_snprintf(acc_info, sizeof(acc_info), "%.*s", 858 (int)info.status_text.slen, 859 info.status_text.ptr); 860 861 } else { 862 pj_ansi_snprintf(acc_info, sizeof(acc_info), 863 "%d/%.*s (expires=%d)", 864 info.status, 865 (int)info.status_text.slen, 866 info.status_text.ptr, 867 info.expires); 868 869 } 870 871 pj_ansi_snprintf(out_str, sizeof(out_str), 872 " %c[%2d] %.*s: %s\n", 873 (acc_ids[i]==current_acc?'*':' '), acc_ids[i], 874 (int)info.acc_uri.slen, info.acc_uri.ptr, 875 acc_info); 876 pj_cli_sess_write_msg(cval->sess, out_str, pj_ansi_strlen(out_str)); 877 878 pj_bzero(out_str, sizeof(out_str)); 879 pj_ansi_snprintf(out_str, sizeof(out_str), 880 " Online status: %.*s\n", 881 (int)info.online_status_text.slen, 882 info.online_status_text.ptr); 883 884 pj_cli_sess_write_msg(cval->sess, out_str, pj_ansi_strlen(out_str)); 885 } 886 887 return PJ_SUCCESS; 888 } 889 890 /* Account command handler */ 891 pj_status_t cmd_account_handler(pj_cli_cmd_val *cval) 892 { 893 pj_status_t status = PJ_SUCCESS; 894 895 CHECK_PJSUA_RUNNING(); 896 897 switch(pj_cli_get_cmd_id(cval->cmd)) { 898 case CMD_ACCOUNT_ADD: 899 status = cmd_add_account(cval); 900 break; 901 case CMD_ACCOUNT_DEL: 902 status = cmd_del_account(cval); 903 break; 904 case CMD_ACCOUNT_MOD: 905 status = cmd_mod_account(cval); 906 break; 907 case CMD_ACCOUNT_REG: 908 status = cmd_reg_account(); 909 break; 910 case CMD_ACCOUNT_UNREG: 911 status = cmd_unreg_account(); 912 break; 913 case CMD_ACCOUNT_NEXT: 914 case CMD_ACCOUNT_PREV: 915 status = cmd_next_account(cval); 916 break; 917 case CMD_ACCOUNT_SHOW: 918 status = cmd_show_account(cval); 919 break; 920 } 921 return status; 922 } 923 924 /* Add buddy */ 925 static pj_status_t cmd_add_buddy(pj_cli_cmd_val *cval) 926 { 927 char out_str[80]; 928 pjsua_buddy_config buddy_cfg; 929 pjsua_buddy_id buddy_id; 930 pj_status_t status = PJ_SUCCESS; 931 cval->argv[1].ptr[cval->argv[1].slen] = 0; 932 933 if (pjsua_verify_url(cval->argv[1].ptr) != PJ_SUCCESS) { 934 pj_ansi_snprintf(out_str, sizeof(out_str), 935 "Invalid URI '%s'\n", cval->argv[1].ptr); 936 } else { 937 pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config)); 938 939 buddy_cfg.uri = pj_str(cval->argv[1].ptr); 940 buddy_cfg.subscribe = PJ_TRUE; 941 942 status = pjsua_buddy_add(&buddy_cfg, &buddy_id); 943 if (status == PJ_SUCCESS) { 944 pj_ansi_snprintf(out_str, sizeof(out_str), 945 "New buddy '%s' added at index %d\n", 946 cval->argv[1].ptr, buddy_id+1); 947 } 948 } 949 pj_cli_sess_write_msg(cval->sess, out_str, pj_ansi_strlen(out_str)); 950 return status; 951 } 952 953 /* Delete buddy */ 954 static pj_status_t cmd_del_buddy(pj_cli_cmd_val *cval) 955 { 956 int i = my_atoi(cval->argv[1].ptr) - 1; 957 char out_str[80]; 958 959 if (!pjsua_buddy_is_valid(i)) { 960 pj_ansi_snprintf(out_str, sizeof(out_str), 961 "Invalid buddy id %d\n", i); 962 } else { 963 pjsua_buddy_del(i); 964 pj_ansi_snprintf(out_str, sizeof(out_str), 965 "Buddy %d deleted\n", i); 966 } 967 pj_cli_sess_write_msg(cval->sess, out_str, pj_ansi_strlen(out_str)); 968 return PJ_SUCCESS; 969 } 970 971 /* Send IM */ 972 static pj_status_t cmd_send_im(pj_cli_cmd_val *cval) 973 { 974 int i = -1; 975 struct input_result result; 976 char dest[64]; 977 pj_str_t tmp = pj_str(dest); 978 979 /* make compiler happy. */ 980 char *uri = NULL; 981 982 pj_strncpy_with_null(&tmp, &cval->argv[1], sizeof(dest)); 983 984 /* input destination. */ 985 get_input_url(tmp.ptr, tmp.slen, cval, &result); 986 if (result.nb_result != PJSUA_APP_NO_NB) { 987 988 if (result.nb_result == -1) { 989 static const pj_str_t err_msg = {"you can't send broadcast im " 990 "like that!\n", 40 }; 991 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 992 return PJ_SUCCESS; 993 } else if (result.nb_result == 0) { 994 i = current_call; 995 } else { 996 pjsua_buddy_info binfo; 997 pjsua_buddy_get_info(result.nb_result-1, &binfo); 998 pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(dest)); 999 uri = tmp.ptr; 1000 } 1001 1002 } else if (result.uri_result) { 1003 uri = result.uri_result; 1004 } 1005 1006 /* send typing indication. */ 1007 if (i != -1) 1008 pjsua_call_send_typing_ind(i, PJ_TRUE, NULL); 1009 else { 1010 pj_str_t tmp_uri = pj_str(uri); 1011 pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL); 1012 } 1013 1014 /* send the im */ 1015 if (i != -1) 1016 pjsua_call_send_im(i, NULL, &cval->argv[2], NULL, NULL); 1017 else { 1018 pj_str_t tmp_uri = pj_str(uri); 1019 pjsua_im_send(current_acc, &tmp_uri, NULL, &cval->argv[2], NULL, NULL); 1020 } 1021 return PJ_SUCCESS; 1022 } 1023 1024 /* Subscribe/unsubscribe presence */ 1025 static pj_status_t cmd_subs_pres(pj_cli_cmd_val *cval, pj_bool_t subscribe) 1026 { 1027 struct input_result result; 1028 char dest[64] = {0}; 1029 pj_str_t tmp = pj_str(dest); 1030 1031 pj_strncpy_with_null(&tmp, &cval->argv[1], sizeof(dest)); 1032 get_input_url(tmp.ptr, tmp.slen, cval, &result); 1033 if (result.nb_result != PJSUA_APP_NO_NB) { 1034 if (result.nb_result == -1) { 1035 int i, count; 1036 count = pjsua_get_buddy_count(); 1037 for (i=0; i<count; ++i) 1038 pjsua_buddy_subscribe_pres(i, subscribe); 1039 } else if (result.nb_result == 0) { 1040 static const pj_str_t err_msg = {"Sorry, can only subscribe to " 1041 "buddy's presence, not from " 1042 "existing call\n", 71}; 1043 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1044 } else { 1045 pjsua_buddy_subscribe_pres(result.nb_result-1, subscribe); 1046 } 1047 1048 } else if (result.uri_result) { 1049 static const pj_str_t err_msg = {"Sorry, can only subscribe to " 1050 "buddy's presence, not arbitrary " 1051 "URL (for now)\n", 76}; 1052 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1053 } 1054 return PJ_SUCCESS; 1055 } 1056 1057 /* Toggle online state */ 1058 static pj_status_t cmd_toggle_state(pj_cli_cmd_val *cval) 1059 { 1060 char out_str[128]; 1061 pjsua_acc_info acc_info; 1062 1063 pjsua_acc_get_info(current_acc, &acc_info); 1064 acc_info.online_status = !acc_info.online_status; 1065 pjsua_acc_set_online_status(current_acc, acc_info.online_status); 1066 pj_ansi_snprintf(out_str, sizeof(out_str), 1067 "Setting %s online status to %s\n", 1068 acc_info.acc_uri.ptr, 1069 (acc_info.online_status?"online":"offline")); 1070 pj_cli_sess_write_msg(cval->sess, out_str, pj_ansi_strlen(out_str)); 1071 return PJ_SUCCESS; 1072 } 1073 1074 /* Set presence text */ 1075 static pj_status_t cmd_set_presence_text(pj_cli_cmd_val *cval) 1076 { 1077 pjrpid_element elem; 1078 int choice; 1079 pj_bool_t online_status; 1080 1081 enum { 1082 AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX 1083 }; 1084 1085 choice = pj_strtol(&cval->argv[1]) - 1; 1086 1087 pj_bzero(&elem, sizeof(elem)); 1088 elem.type = PJRPID_ELEMENT_TYPE_PERSON; 1089 1090 online_status = PJ_TRUE; 1091 1092 switch (choice) { 1093 case AVAILABLE: 1094 break; 1095 case BUSY: 1096 elem.activity = PJRPID_ACTIVITY_BUSY; 1097 elem.note = pj_str("Busy"); 1098 break; 1099 case OTP: 1100 elem.activity = PJRPID_ACTIVITY_BUSY; 1101 elem.note = pj_str("On the phone"); 1102 break; 1103 case IDLE: 1104 elem.activity = PJRPID_ACTIVITY_UNKNOWN; 1105 elem.note = pj_str("Idle"); 1106 break; 1107 case AWAY: 1108 elem.activity = PJRPID_ACTIVITY_AWAY; 1109 elem.note = pj_str("Away"); 1110 break; 1111 case BRB: 1112 elem.activity = PJRPID_ACTIVITY_UNKNOWN; 1113 elem.note = pj_str("Be right back"); 1114 break; 1115 case OFFLINE: 1116 online_status = PJ_FALSE; 1117 break; 1118 } 1119 pjsua_acc_set_online_status2(current_acc, online_status, &elem); 1120 return PJ_SUCCESS; 1121 } 1122 1123 /* Show buddy list */ 1124 static pj_status_t cmd_show_buddy(pj_cli_cmd_val *cval) 1125 { 1126 pjsua_buddy_id ids[64]; 1127 int i; 1128 unsigned count = PJ_ARRAY_SIZE(ids); 1129 static const pj_str_t header = {"Buddy list:\n", 13}; 1130 char out_str[64]; 1131 1132 pj_cli_sess_write_msg(cval->sess, header.ptr, header.slen); 1133 1134 pjsua_enum_buddies(ids, &count); 1135 1136 if (count == 0) { 1137 pj_ansi_snprintf(out_str, sizeof(out_str), " -none-\n"); 1138 pj_cli_sess_write_msg(cval->sess, out_str, pj_ansi_strlen(out_str)); 1139 } else { 1140 for (i=0; i<(int)count; ++i) { 1141 pjsua_buddy_info info; 1142 pj_bzero(out_str, sizeof(out_str)); 1143 1144 if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS) 1145 continue; 1146 1147 pj_ansi_snprintf(out_str, sizeof(out_str), 1148 " [%2d] <%.*s> %.*s\n", 1149 ids[i]+1, 1150 (int)info.status_text.slen, 1151 info.status_text.ptr, 1152 (int)info.uri.slen, 1153 info.uri.ptr); 1154 1155 pj_cli_sess_write_msg(cval->sess, out_str, pj_ansi_strlen(out_str)); 1156 } 1157 } 1158 return PJ_SUCCESS; 1159 } 1160 1161 /* Presence/buddy command handler */ 1162 pj_status_t cmd_presence_handler(pj_cli_cmd_val *cval) 1163 { 1164 pj_status_t status = PJ_SUCCESS; 1165 1166 CHECK_PJSUA_RUNNING(); 1167 1168 switch(pj_cli_get_cmd_id(cval->cmd)) { 1169 case CMD_PRESENCE_ADD_BUDDY: 1170 status = cmd_add_buddy(cval); 1171 break; 1172 case CMD_PRESENCE_DEL_BUDDY: 1173 status = cmd_del_buddy(cval); 1174 break; 1175 case CMD_PRESENCE_SEND_IM: 1176 status = cmd_send_im(cval); 1177 break; 1178 case CMD_PRESENCE_SUB: 1179 case CMD_PRESENCE_UNSUB: 1180 status = cmd_subs_pres(cval, 1181 pj_cli_get_cmd_id(cval->cmd)==CMD_PRESENCE_SUB); 1182 break; 1183 case CMD_PRESENCE_TOG_STATE: 1184 status = cmd_toggle_state(cval); 1185 break; 1186 case CMD_PRESENCE_TEXT: 1187 status = cmd_set_presence_text(cval); 1188 break; 1189 case CMD_PRESENCE_LIST: 1190 status = cmd_show_buddy(cval); 1191 break; 1192 } 1193 1194 return status; 1195 } 1196 1197 /* Show conference list */ 1198 static pj_status_t cmd_media_list(pj_cli_cmd_val *cval) 1199 { 1200 unsigned i, count; 1201 pjsua_conf_port_id id[PJSUA_MAX_CONF_PORTS]; 1202 static const pj_str_t header = {"Conference ports:\n", 19}; 1203 1204 pj_cli_sess_write_msg(cval->sess, header.ptr, header.slen); 1205 1206 count = PJ_ARRAY_SIZE(id); 1207 pjsua_enum_conf_ports(id, &count); 1208 1209 for (i=0; i<count; ++i) { 1210 char out_str[128]; 1211 char txlist[16]; 1212 unsigned j; 1213 pjsua_conf_port_info info; 1214 1215 pjsua_conf_get_port_info(id[i], &info); 1216 1217 pj_bzero(txlist, sizeof(txlist)); 1218 for (j=0; j<info.listener_cnt; ++j) { 1219 char s[10]; 1220 pj_ansi_snprintf(s, sizeof(s), "#%d ", info.listeners[j]); 1221 pj_ansi_strcat(txlist, s); 1222 } 1223 pj_ansi_snprintf(out_str, 1224 sizeof(out_str), 1225 "Port #%02d[%2dKHz/%dms/%d] %20.*s transmitting to: %s\n", 1226 info.slot_id, 1227 info.clock_rate/1000, 1228 info.samples_per_frame*1000/info.channel_count/info.clock_rate, 1229 info.channel_count, 1230 (int)info.name.slen, 1231 info.name.ptr, 1232 txlist); 1233 pj_cli_sess_write_msg(cval->sess, out_str, pj_ansi_strlen(out_str)); 1234 } 1235 return PJ_SUCCESS; 1236 } 1237 1238 /* Conference connect/disconnect */ 1239 static pj_status_t cmd_media_connect(pj_cli_cmd_val *cval, pj_bool_t connect) 1240 { 1241 pj_status_t status; 1242 1243 if (connect) 1244 status = pjsua_conf_connect(pj_strtol(&cval->argv[1]), 1245 pj_strtol(&cval->argv[2])); 1246 else 1247 status = pjsua_conf_disconnect(pj_strtol(&cval->argv[1]), 1248 pj_strtol(&cval->argv[2])); 1249 1250 if (status == PJ_SUCCESS) { 1251 static const pj_str_t success_msg = {"Success\n", 9}; 1252 pj_cli_sess_write_msg(cval->sess, success_msg.ptr, success_msg.slen); 1253 } else { 1254 static const pj_str_t err_msg = {"ERROR!!\n", 9}; 1255 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1256 } 1257 return status; 1258 } 1259 1260 /* Adjust audio volume */ 1261 static pj_status_t cmd_adjust_vol(pj_cli_cmd_val *cval) 1262 { 1263 char buf[80]; 1264 float orig_level; 1265 char *err; 1266 char level_val[16] = {0}; 1267 pj_str_t tmp = pj_str(level_val); 1268 1269 /* Adjust mic level */ 1270 orig_level = app_config.mic_level; 1271 pj_strncpy_with_null(&tmp, &cval->argv[1], sizeof(level_val)); 1272 app_config.mic_level = (float)strtod(level_val, &err); 1273 pjsua_conf_adjust_rx_level(0, app_config.mic_level); 1274 1275 pj_ansi_snprintf(buf, sizeof(buf), 1276 "Adjust mic level: [%4.1fx] -> [%4.1fx]\n", 1277 orig_level, app_config.mic_level); 1278 1279 pj_cli_sess_write_msg(cval->sess, buf, pj_ansi_strlen(buf)); 1280 1281 /* Adjust speaker level */ 1282 orig_level = app_config.speaker_level; 1283 pj_strncpy_with_null(&tmp, &cval->argv[2], sizeof(level_val)); 1284 app_config.speaker_level = (float)strtod(level_val, &err); 1285 pjsua_conf_adjust_tx_level(0, app_config.speaker_level); 1286 1287 pj_ansi_snprintf(buf, sizeof(buf), 1288 "Adjust speaker level: [%4.1fx] -> [%4.1fx]\n", 1289 orig_level, app_config.speaker_level); 1290 1291 pj_cli_sess_write_msg(cval->sess, buf, pj_ansi_strlen(buf)); 1292 1293 return PJ_SUCCESS; 1294 } 1295 1296 /* Set codec priority */ 1297 static pj_status_t cmd_set_codec_prio(pj_cli_cmd_val *cval) 1298 { 1299 int new_prio; 1300 pj_status_t status; 1301 1302 new_prio = pj_strtol(&cval->argv[2]); 1303 if (new_prio < 0) 1304 new_prio = 0; 1305 else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST) 1306 new_prio = PJMEDIA_CODEC_PRIO_HIGHEST; 1307 1308 status = pjsua_codec_set_priority(&cval->argv[1], 1309 (pj_uint8_t)new_prio); 1310 #if PJSUA_HAS_VIDEO 1311 if (status != PJ_SUCCESS) { 1312 status = pjsua_vid_codec_set_priority(&cval->argv[1], 1313 (pj_uint8_t)new_prio); 1314 } 1315 #endif 1316 if (status != PJ_SUCCESS) 1317 pjsua_perror(THIS_FILE, "Error setting codec priority", status); 1318 1319 return status; 1320 } 1321 1322 /* Conference/media command handler */ 1323 pj_status_t cmd_media_handler(pj_cli_cmd_val *cval) 1324 { 1325 pj_status_t status = PJ_SUCCESS; 1326 1327 CHECK_PJSUA_RUNNING(); 1328 1329 switch(pj_cli_get_cmd_id(cval->cmd)) { 1330 case CMD_MEDIA_LIST: 1331 status = cmd_media_list(cval); 1332 break; 1333 case CMD_MEDIA_CONF_CONNECT: 1334 case CMD_MEDIA_CONF_DISCONNECT: 1335 status = cmd_media_connect(cval, 1336 pj_cli_get_cmd_id(cval->cmd)==CMD_MEDIA_CONF_CONNECT); 1337 break; 1338 case CMD_MEDIA_ADJUST_VOL: 1339 status = cmd_adjust_vol(cval); 1340 break; 1341 case CMD_MEDIA_CODEC_PRIO: 1342 status = cmd_set_codec_prio(cval); 1343 break; 1344 } 1345 1346 return status; 1347 } 1348 1349 /* Dump status */ 1350 static pj_status_t cmd_stat_dump(pj_bool_t detail) 1351 { 1352 pjsua_dump(detail); 1353 return PJ_SUCCESS; 1354 } 1355 1356 static pj_status_t cmd_show_config() 1357 { 1358 char settings[2000]; 1359 int len; 1360 1361 len = write_settings(&app_config, settings, sizeof(settings)); 1362 if (len < 1) 1363 PJ_LOG(1,(THIS_FILE, "Error: not enough buffer")); 1364 else 1365 PJ_LOG(3,(THIS_FILE, 1366 "Dumping configuration (%d bytes):\n%s\n", 1367 len, settings)); 1368 1369 return PJ_SUCCESS; 1370 } 1371 1372 static pj_status_t cmd_write_config(pj_cli_cmd_val *cval) 1373 { 1374 char settings[2000]; 1375 char buf[128] = {0}; 1376 int len; 1377 pj_str_t tmp = pj_str(buf); 1378 1379 pj_strncpy_with_null(&tmp, &cval->argv[1], sizeof(buf)); 1380 1381 len = write_settings(&app_config, settings, sizeof(settings)); 1382 if (len < 1) 1383 PJ_LOG(1,(THIS_FILE, "Error: not enough buffer")); 1384 else { 1385 pj_oshandle_t fd; 1386 pj_status_t status; 1387 1388 status = pj_file_open(app_config.pool, buf, PJ_O_WRONLY, &fd); 1389 if (status != PJ_SUCCESS) { 1390 pjsua_perror(THIS_FILE, "Unable to open file", status); 1391 } else { 1392 char out_str[256]; 1393 pj_ssize_t size = len; 1394 pj_file_write(fd, settings, &size); 1395 pj_file_close(fd); 1396 1397 pj_ansi_snprintf(out_str, sizeof(out_str), 1398 "Settings successfully written to '%s'\n", buf); 1399 1400 pj_cli_sess_write_msg(cval->sess, out_str, pj_ansi_strlen(out_str)); 1401 } 1402 } 1403 1404 return PJ_SUCCESS; 1405 } 1406 1407 /* Status and config command handler */ 1408 pj_status_t cmd_config_handler(pj_cli_cmd_val *cval) 1409 { 1410 pj_status_t status = PJ_SUCCESS; 1411 1412 CHECK_PJSUA_RUNNING(); 1413 1414 switch(pj_cli_get_cmd_id(cval->cmd)) { 1415 case CMD_CONFIG_DUMP_STAT: 1416 status = cmd_stat_dump(PJ_FALSE); 1417 break; 1418 case CMD_CONFIG_DUMP_DETAIL: 1419 status = cmd_stat_dump(PJ_TRUE); 1420 break; 1421 case CMD_CONFIG_DUMP_CONF: 1422 status = cmd_show_config(); 1423 break; 1424 case CMD_CONFIG_WRITE_SETTING: 1425 status = cmd_write_config(cval); 1426 break; 1427 } 1428 1429 return status; 1430 } 1431 1432 /* Make single call */ 1433 static pj_status_t cmd_make_single_call(pj_cli_cmd_val *cval) 1434 { 1435 struct input_result result; 1436 char dest[64] = {0}; 1437 char out_str[128]; 1438 pj_str_t tmp = pj_str(dest); 1439 1440 pj_strncpy_with_null(&tmp, &cval->argv[1], sizeof(dest)); 1441 1442 pj_ansi_snprintf(out_str, 1443 sizeof(out_str), 1444 "(You currently have %d calls)\n", 1445 pjsua_call_get_count()); 1446 1447 pj_cli_sess_write_msg(cval->sess, out_str, pj_ansi_strlen(out_str)); 1448 1449 /* input destination. */ 1450 get_input_url(tmp.ptr, tmp.slen, cval, &result); 1451 if (result.nb_result != PJSUA_APP_NO_NB) { 1452 pjsua_buddy_info binfo; 1453 if (result.nb_result == -1 || result.nb_result == 0) { 1454 static const pj_str_t err_msg = 1455 {"You can't do that with make call!\n", 35}; 1456 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1457 return PJ_SUCCESS; 1458 } 1459 pjsua_buddy_get_info(result.nb_result-1, &binfo); 1460 pj_strncpy(&tmp, &binfo.uri, sizeof(dest)); 1461 } else if (result.uri_result) { 1462 tmp = pj_str(result.uri_result); 1463 } else { 1464 tmp.slen = 0; 1465 } 1466 1467 pjsua_msg_data_init(&msg_data); 1468 TEST_MULTIPART(&msg_data); 1469 pjsua_call_make_call(current_acc, &tmp, &call_opt, NULL, 1470 &msg_data, ¤t_call); 1471 return PJ_SUCCESS; 1472 } 1473 1474 /* Make multi call */ 1475 static pj_status_t cmd_make_multi_call(pj_cli_cmd_val *cval) 1476 { 1477 struct input_result result; 1478 char dest[64] = {0}; 1479 char out_str[128]; 1480 int i, count; 1481 pj_str_t tmp = pj_str(dest); 1482 1483 pj_ansi_snprintf(out_str, 1484 sizeof(out_str), 1485 "(You currently have %d calls)\n", 1486 pjsua_call_get_count()); 1487 1488 count = pj_strtol(&cval->argv[1]); 1489 if (count < 1) 1490 return PJ_SUCCESS; 1491 1492 pj_strncpy_with_null(&tmp, &cval->argv[2], sizeof(dest)); 1493 1494 /* input destination. */ 1495 get_input_url(tmp.ptr, tmp.slen, cval, &result); 1496 if (result.nb_result != PJSUA_APP_NO_NB) { 1497 pjsua_buddy_info binfo; 1498 if (result.nb_result == -1 || result.nb_result == 0) { 1499 static const pj_str_t err_msg = 1500 {"You can't do that with make call!\n", 35}; 1501 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1502 return PJ_SUCCESS; 1503 } 1504 pjsua_buddy_get_info(result.nb_result-1, &binfo); 1505 pj_strncpy(&tmp, &binfo.uri, sizeof(dest)); 1506 } else { 1507 tmp = pj_str(result.uri_result); 1508 } 1509 1510 for (i=0; i<count; ++i) { 1511 pj_status_t status; 1512 1513 status = pjsua_call_make_call(current_acc, &tmp, &call_opt, NULL, 1514 NULL, NULL); 1515 if (status != PJ_SUCCESS) 1516 break; 1517 } 1518 return PJ_SUCCESS; 1519 } 1520 1521 /* Answer call */ 1522 static pj_status_t cmd_answer_call(pj_cli_cmd_val *cval) 1523 { 1524 pjsua_call_info call_info; 1525 1526 if (current_call != PJSUA_INVALID_ID) { 1527 pjsua_call_get_info(current_call, &call_info); 1528 } else { 1529 /* Make compiler happy */ 1530 call_info.role = PJSIP_ROLE_UAC; 1531 call_info.state = PJSIP_INV_STATE_DISCONNECTED; 1532 } 1533 1534 if (current_call == PJSUA_INVALID_ID || 1535 call_info.role != PJSIP_ROLE_UAS || 1536 call_info.state >= PJSIP_INV_STATE_CONNECTING) 1537 { 1538 static const pj_str_t err_msg = {"No pending incoming call\n", 26}; 1539 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1540 1541 } else { 1542 int st_code; 1543 char contact[120]; 1544 pj_str_t hname = { "Contact", 7 }; 1545 pj_str_t hvalue; 1546 pjsip_generic_string_hdr hcontact; 1547 1548 st_code = pj_strtol(&cval->argv[1]); 1549 if ((st_code < 100) || (st_code > 699)) 1550 return PJ_SUCCESS; 1551 1552 pjsua_msg_data_init(&msg_data); 1553 1554 if (st_code/100 == 3) { 1555 if (cval->argc < 3) { 1556 static const pj_str_t err_msg = {"Enter URL to be put " 1557 "in Contact\n", 32}; 1558 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1559 return PJ_SUCCESS; 1560 } 1561 1562 hvalue = pj_str(contact); 1563 pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue); 1564 1565 pj_list_push_back(&msg_data.hdr_list, &hcontact); 1566 } 1567 1568 /* 1569 * Must check again! 1570 * Call may have been disconnected while we're waiting for 1571 * keyboard input. 1572 */ 1573 if (current_call == PJSUA_INVALID_ID) { 1574 static const pj_str_t err_msg = {"Call has been disconnected\n", 1575 28}; 1576 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1577 } 1578 1579 pjsua_call_answer2(current_call, &call_opt, st_code, NULL, &msg_data); 1580 } 1581 return PJ_SUCCESS; 1582 } 1583 1584 /* Hangup call */ 1585 static pj_status_t cmd_hangup_call(pj_cli_cmd_val *cval, pj_bool_t all) 1586 { 1587 if (current_call == PJSUA_INVALID_ID) { 1588 static const pj_str_t err_msg = {"No current call\n", 17}; 1589 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1590 } else { 1591 if (all) 1592 pjsua_call_hangup_all(); 1593 else 1594 pjsua_call_hangup(current_call, 0, NULL, NULL); 1595 } 1596 return PJ_SUCCESS; 1597 } 1598 1599 /* Hold call */ 1600 static pj_status_t cmd_hold_call() 1601 { 1602 if (current_call != PJSUA_INVALID_ID) { 1603 pjsua_call_set_hold(current_call, NULL); 1604 1605 } else { 1606 PJ_LOG(3,(THIS_FILE, "No current call")); 1607 } 1608 return PJ_SUCCESS; 1609 } 1610 1611 /* Call reinvite */ 1612 static pj_status_t cmd_call_reinvite() 1613 { 1614 if (current_call != PJSUA_INVALID_ID) { 1615 /* 1616 * re-INVITE 1617 */ 1618 call_opt.flag |= PJSUA_CALL_UNHOLD; 1619 pjsua_call_reinvite2(current_call, &call_opt, NULL); 1620 1621 } else { 1622 PJ_LOG(3,(THIS_FILE, "No current call")); 1623 } 1624 return PJ_SUCCESS; 1625 } 1626 1627 /* Send update */ 1628 static pj_status_t cmd_call_update() 1629 { 1630 if (current_call != PJSUA_INVALID_ID) { 1631 pjsua_call_update2(current_call, &call_opt, NULL); 1632 } else { 1633 PJ_LOG(3,(THIS_FILE, "No current call")); 1634 } 1635 return PJ_SUCCESS; 1636 } 1637 1638 /* Select next call */ 1639 static pj_status_t cmd_next_call(pj_bool_t next) 1640 { 1641 /* 1642 * Cycle next/prev dialog. 1643 */ 1644 if (next) { 1645 find_next_call(current_call); 1646 } else { 1647 find_prev_call(current_call); 1648 } 1649 1650 if (current_call != PJSUA_INVALID_ID) { 1651 pjsua_call_info call_info; 1652 1653 pjsua_call_get_info(current_call, &call_info); 1654 PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s", 1655 (int)call_info.remote_info.slen, 1656 call_info.remote_info.ptr)); 1657 1658 } else { 1659 PJ_LOG(3,(THIS_FILE,"No current dialog")); 1660 } 1661 return PJ_SUCCESS; 1662 } 1663 1664 /* Transfer call */ 1665 static pj_status_t cmd_transfer_call(pj_cli_cmd_val *cval) 1666 { 1667 if (current_call == PJSUA_INVALID_ID) { 1668 1669 PJ_LOG(3,(THIS_FILE, "No current call")); 1670 1671 } else { 1672 char out_str[64]; 1673 int call = current_call; 1674 char dest[64] = {0}; 1675 pj_str_t tmp = pj_str(dest); 1676 struct input_result result; 1677 pjsip_generic_string_hdr refer_sub; 1678 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 }; 1679 pj_str_t STR_FALSE = { "false", 5 }; 1680 pjsua_call_info ci; 1681 1682 pj_strncpy_with_null(&tmp, &cval->argv[1], sizeof(dest)); 1683 1684 pjsua_call_get_info(current_call, &ci); 1685 pj_ansi_snprintf(out_str, 1686 sizeof(out_str), 1687 "Transfering current call [%d] %.*s\n", 1688 current_call, 1689 (int)ci.remote_info.slen, 1690 ci.remote_info.ptr); 1691 1692 get_input_url(tmp.ptr, tmp.slen, cval, &result); 1693 1694 /* Check if call is still there. */ 1695 1696 if (call != current_call) { 1697 puts("Call has been disconnected"); 1698 return PJ_SUCCESS; 1699 } 1700 1701 pjsua_msg_data_init(&msg_data); 1702 if (app_config.no_refersub) { 1703 /* Add Refer-Sub: false in outgoing REFER request */ 1704 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB, 1705 &STR_FALSE); 1706 pj_list_push_back(&msg_data.hdr_list, &refer_sub); 1707 } 1708 if (result.nb_result != PJSUA_APP_NO_NB) { 1709 if (result.nb_result == -1 || result.nb_result == 0) { 1710 static const pj_str_t err_msg = {"You can't do that with " 1711 "transfer call!\n", 39}; 1712 1713 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1714 } else { 1715 pjsua_buddy_info binfo; 1716 pjsua_buddy_get_info(result.nb_result-1, &binfo); 1717 pjsua_call_xfer( current_call, &binfo.uri, &msg_data); 1718 } 1719 } else if (result.uri_result) { 1720 pj_str_t tmp; 1721 tmp = pj_str(result.uri_result); 1722 pjsua_call_xfer( current_call, &tmp, &msg_data); 1723 } 1724 } 1725 return PJ_SUCCESS; 1726 } 1727 1728 /* Transfer call */ 1729 static pj_status_t cmd_transfer_replace_call(pj_cli_cmd_val *cval) 1730 { 1731 if (current_call == -1) { 1732 PJ_LOG(3,(THIS_FILE, "No current call")); 1733 } else { 1734 int call = current_call; 1735 int dst_call; 1736 pjsip_generic_string_hdr refer_sub; 1737 pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 }; 1738 pj_str_t STR_FALSE = { "false", 5 }; 1739 pjsua_call_id ids[PJSUA_MAX_CALLS]; 1740 pjsua_msg_data msg_data; 1741 char buf[8] = {0}; 1742 pj_str_t tmp = pj_str(buf); 1743 unsigned count; 1744 static const pj_str_t err_invalid_num = 1745 {"Invalid destination call number\n", 32 }; 1746 count = PJ_ARRAY_SIZE(ids); 1747 pjsua_enum_calls(ids, &count); 1748 1749 if (count <= 1) { 1750 static const pj_str_t err_no_other_call = 1751 {"There are no other calls\n", 25}; 1752 1753 pj_cli_sess_write_msg(cval->sess, err_no_other_call.ptr, 1754 err_no_other_call.slen); 1755 return PJ_SUCCESS; 1756 } 1757 1758 pj_strncpy_with_null(&tmp, &cval->argv[1], sizeof(buf)); 1759 dst_call = my_atoi(tmp.ptr); 1760 1761 /* Check if call is still there. */ 1762 if (call != current_call) { 1763 static pj_str_t err_call_dc = 1764 {"Call has been disconnected\n", 27}; 1765 1766 pj_cli_sess_write_msg(cval->sess, err_call_dc.ptr, 1767 err_call_dc.slen); 1768 return PJ_SUCCESS; 1769 } 1770 1771 /* Check that destination call is valid. */ 1772 if (dst_call == call) { 1773 static pj_str_t err_same_num = 1774 {"Destination call number must not be the " 1775 "same as the call being transfered\n", 74}; 1776 1777 pj_cli_sess_write_msg(cval->sess, err_same_num.ptr, 1778 err_same_num.slen); 1779 return PJ_SUCCESS; 1780 } 1781 1782 if (dst_call >= PJSUA_MAX_CALLS) { 1783 pj_cli_sess_write_msg(cval->sess, err_invalid_num.ptr, 1784 err_invalid_num.slen); 1785 return PJ_SUCCESS; 1786 } 1787 1788 if (!pjsua_call_is_active(dst_call)) { 1789 pj_cli_sess_write_msg(cval->sess, err_invalid_num.ptr, 1790 err_invalid_num.slen); 1791 return PJ_SUCCESS; 1792 } 1793 1794 pjsua_msg_data_init(&msg_data); 1795 if (app_config.no_refersub) { 1796 /* Add Refer-Sub: false in outgoing REFER request */ 1797 pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB, 1798 &STR_FALSE); 1799 pj_list_push_back(&msg_data.hdr_list, &refer_sub); 1800 } 1801 1802 pjsua_call_xfer_replaces(call, dst_call, 1803 PJSUA_XFER_NO_REQUIRE_REPLACES, 1804 &msg_data); 1805 } 1806 return PJ_SUCCESS; 1807 } 1808 1809 static pj_status_t cmd_redirect_call(pj_cli_cmd_val *cval) 1810 { 1811 if (current_call == PJSUA_INVALID_ID) { 1812 PJ_LOG(3,(THIS_FILE, "No current call")); 1813 return PJ_SUCCESS; 1814 } 1815 if (!pjsua_call_is_active(current_call)) { 1816 PJ_LOG(1,(THIS_FILE, "Call %d has gone", current_call)); 1817 } else { 1818 enum { 1819 ACCEPT_REPLACE, ACCEPT, REJECT, STOP 1820 }; 1821 int choice = pj_strtol(&cval->argv[1]); 1822 1823 switch (choice) { 1824 case ACCEPT_REPLACE: 1825 pjsua_call_process_redirect(current_call, 1826 PJSIP_REDIRECT_ACCEPT_REPLACE); 1827 break; 1828 case ACCEPT: 1829 pjsua_call_process_redirect(current_call, PJSIP_REDIRECT_ACCEPT); 1830 break; 1831 case REJECT: 1832 pjsua_call_process_redirect(current_call, PJSIP_REDIRECT_REJECT); 1833 break; 1834 default: 1835 pjsua_call_process_redirect(current_call, PJSIP_REDIRECT_STOP); 1836 break; 1837 } 1838 } 1839 return PJ_SUCCESS; 1840 } 1841 1842 /* Send DTMF (RFC2833) */ 1843 static pj_status_t cmd_dtmf_2833(pj_cli_cmd_val *cval) 1844 { 1845 if (current_call == PJSUA_INVALID_ID) { 1846 1847 PJ_LOG(3,(THIS_FILE, "No current call")); 1848 1849 } else if (!pjsua_call_has_media(current_call)) { 1850 1851 PJ_LOG(3,(THIS_FILE, "Media is not established yet!")); 1852 1853 } else { 1854 int call = current_call; 1855 pj_status_t status; 1856 1857 if (call != current_call) { 1858 static const pj_str_t err_msg = {"Call has been disconnected\n", 1859 28}; 1860 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1861 return PJ_SUCCESS;; 1862 } 1863 1864 status = pjsua_call_dial_dtmf(current_call, &cval->argv[1]); 1865 if (status != PJ_SUCCESS) { 1866 pjsua_perror(THIS_FILE, "Unable to send DTMF", status); 1867 } else { 1868 static const pj_str_t msg = {"DTMF digits enqueued " 1869 "for transmission\n", 39}; 1870 pj_cli_sess_write_msg(cval->sess, msg.ptr, msg.slen); 1871 } 1872 } 1873 return PJ_SUCCESS; 1874 } 1875 1876 /* Send DTMF with SIP Info */ 1877 static pj_status_t cmd_call_info(pj_cli_cmd_val *cval) 1878 { 1879 if (current_call == PJSUA_INVALID_ID) { 1880 1881 PJ_LOG(3,(THIS_FILE, "No current call")); 1882 1883 } else { 1884 const pj_str_t SIP_INFO = pj_str("INFO"); 1885 int call = current_call; 1886 int i; 1887 pj_status_t status; 1888 1889 if (call != current_call) { 1890 static const pj_str_t err_msg = {"Call has been disconnected\n", 1891 28}; 1892 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1893 return PJ_SUCCESS;; 1894 } 1895 1896 for (i=0; i<cval->argv[1].slen; ++i) { 1897 char body[64]; 1898 1899 pjsua_msg_data_init(&msg_data); 1900 msg_data.content_type = pj_str("application/dtmf-relay"); 1901 1902 pj_ansi_snprintf(body, 1903 sizeof(body), 1904 "Signal=%c\n" 1905 "Duration=160", 1906 cval->argv[1].ptr[i]); 1907 1908 msg_data.msg_body = pj_str(body); 1909 1910 status = pjsua_call_send_request(current_call, &SIP_INFO, 1911 &msg_data); 1912 if (status != PJ_SUCCESS) { 1913 break; 1914 } 1915 } 1916 } 1917 return PJ_SUCCESS; 1918 } 1919 1920 /* Dump call quality */ 1921 static pj_status_t cmd_call_quality() 1922 { 1923 if (current_call != PJSUA_INVALID_ID) { 1924 log_call_dump(current_call); 1925 } else { 1926 PJ_LOG(3,(THIS_FILE, "No current call")); 1927 } 1928 return PJ_SUCCESS; 1929 } 1930 1931 /* Send arbitrary request */ 1932 static pj_status_t cmd_send_arbitrary(pj_cli_cmd_val *cval) 1933 { 1934 if (pjsua_acc_get_count() == 0) { 1935 static const pj_str_t err_msg = {"Sorry, need at least one " 1936 "account configured\n", 45}; 1937 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1938 } else { 1939 char *uri; 1940 char dest[64] = {0}; 1941 pj_str_t tmp = pj_str(dest); 1942 struct input_result result; 1943 static const pj_str_t header = {"Send arbitrary request to " 1944 "remote host\n", 39}; 1945 1946 pj_cli_sess_write_msg(cval->sess, header.ptr, header.slen); 1947 1948 pj_strncpy_with_null(&tmp, &cval->argv[2], sizeof(dest)); 1949 /* Input destination URI */ 1950 uri = NULL; 1951 get_input_url(tmp.ptr, tmp.slen, cval, &result); 1952 if (result.nb_result != PJSUA_APP_NO_NB) { 1953 if (result.nb_result == -1) { 1954 static const pj_str_t err_msg = {"Sorry you can't do that!\n", 1955 26}; 1956 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, err_msg.slen); 1957 return PJ_SUCCESS; 1958 } else if (result.nb_result == 0) { 1959 uri = NULL; 1960 if (current_call == PJSUA_INVALID_ID) { 1961 static const pj_str_t err_msg = {"No current call\n", 1962 17}; 1963 pj_cli_sess_write_msg(cval->sess, err_msg.ptr, 1964 err_msg.slen); 1965 1966 return PJ_SUCCESS; 1967 } 1968 } else { 1969 pjsua_buddy_info binfo; 1970 pjsua_buddy_get_info(result.nb_result-1, &binfo); 1971 pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(dest)); 1972 uri = tmp.ptr; 1973 } 1974 } else if (result.uri_result) { 1975 uri = result.uri_result; 1976 } else { 1977 return PJ_SUCCESS;; 1978 } 1979 1980 if (uri) { 1981 char method[64] = {0}; 1982 pj_str_t tmp_method = pj_str(method); 1983 pj_strncpy_with_null(&tmp_method, &cval->argv[1], sizeof(method)); 1984 tmp = pj_str(uri); 1985 send_request(method, &tmp); 1986 } else { 1987 /* If you send call control request using this method 1988 * (such requests includes BYE, CANCEL, etc.), it will 1989 * not go well with the call state, so don't do it 1990 * unless it's for testing. 1991 */ 1992 pjsua_call_send_request(current_call, &cval->argv[1], NULL); 1993 } 1994 } 1995 return PJ_SUCCESS; 1996 } 1997 1998 static pj_status_t cmd_show_current_call(pj_cli_cmd_val *cval) 1999 { 2000 char out_str[128]; 2001 int i = pjsua_call_get_count(); 2002 pj_ansi_snprintf(out_str, sizeof(out_str), 2003 "You have %d active call%s\n", i, (i>1?"s":"")); 2004 2005 pj_cli_sess_write_msg(cval->sess, out_str, 2006 pj_ansi_strlen(out_str)); 2007 2008 if (current_call != PJSUA_INVALID_ID) { 2009 pjsua_call_info ci; 2010 if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS) { 2011 pj_ansi_snprintf(out_str, sizeof(out_str), 2012 "Current call id=%d to %.*s [%.*s]\n", current_call, 2013 (int)ci.remote_info.slen, ci.remote_info.ptr, 2014 (int)ci.state_text.slen, ci.state_text.ptr); 2015 2016 pj_cli_sess_write_msg(cval->sess, out_str, 2017 pj_ansi_strlen(out_str)); 2018 } 2019 } 2020 return PJ_SUCCESS; 2021 } 2022 2023 /* Call handler */ 2024 pj_status_t cmd_call_handler(pj_cli_cmd_val *cval) 2025 { 2026 pj_status_t status = PJ_SUCCESS; 2027 pj_cli_cmd_id cmd_id = pj_cli_get_cmd_id(cval->cmd); 2028 2029 CHECK_PJSUA_RUNNING(); 2030 2031 switch(cmd_id) { 2032 case CMD_CALL_NEW: 2033 status = cmd_make_single_call(cval); 2034 break; 2035 case CMD_CALL_MULTI: 2036 status = cmd_make_multi_call(cval); 2037 break; 2038 case CMD_CALL_ANSWER: 2039 status = cmd_answer_call(cval); 2040 break; 2041 case CMD_CALL_HANGUP: 2042 case CMD_CALL_HANGUP_ALL: 2043 status = cmd_hangup_call(cval, (cmd_id==CMD_CALL_HANGUP_ALL)); 2044 break; 2045 case CMD_CALL_HOLD: 2046 status = cmd_hold_call(); 2047 break; 2048 case CMD_CALL_REINVITE: 2049 status = cmd_call_reinvite(); 2050 break; 2051 case CMD_CALL_UPDATE: 2052 status = cmd_call_update(); 2053 break; 2054 case CMD_CALL_NEXT: 2055 case CMD_CALL_PREVIOUS: 2056 status = cmd_next_call(cmd_id==CMD_CALL_NEXT); 2057 break; 2058 case CMD_CALL_TRANSFER: 2059 status = cmd_transfer_call(cval); 2060 break; 2061 case CMD_CALL_TRANSFER_REPLACE: 2062 status = cmd_transfer_replace_call(cval); 2063 break; 2064 case CMD_CALL_REDIRECT: 2065 status = cmd_redirect_call(cval); 2066 break; 2067 case CMD_CALL_D2833: 2068 status = cmd_dtmf_2833(cval); 2069 break; 2070 case CMD_CALL_INFO: 2071 status = cmd_call_info(cval); 2072 break; 2073 case CMD_CALL_DUMP_Q: 2074 status = cmd_call_quality(); 2075 break; 2076 case CMD_CALL_SEND_ARB: 2077 status = cmd_send_arbitrary(cval); 2078 break; 2079 case CMD_CALL_LIST: 2080 status = cmd_show_current_call(cval); 2081 break; 2082 } 2083 2084 return status; 2085 } 2086 2087 #if PJSUA_HAS_VIDEO 2088 static pj_status_t cmd_set_video_enable(pj_bool_t enabled) 2089 { 2090 app_config.vid.vid_cnt = (enabled ? 1 : 0); 2091 PJ_LOG(3,(THIS_FILE, "Video will be %s in next offer/answer", 2092 (enabled?"enabled":"disabled"))); 2093 2094 return PJ_SUCCESS; 2095 } 2096 2097 static pj_status_t modify_video_account(pjsua_acc_config *acc_cfg) 2098 { 2099 pj_status_t status = pjsua_acc_modify(current_acc, acc_cfg); 2100 if (status != PJ_SUCCESS) 2101 PJ_PERROR(1,(THIS_FILE, status, "Error modifying account %d", 2102 current_acc)); 2103 2104 return status; 2105 } 2106 2107 static pj_status_t cmd_show_account_video() 2108 { 2109 pjsua_acc_config acc_cfg; 2110 2111 pjsua_acc_get_config(current_acc, &acc_cfg); 2112 app_config_show_video(current_acc, &acc_cfg); 2113 return PJ_SUCCESS; 2114 } 2115 2116 static pj_status_t cmd_video_acc_handler(pj_cli_cmd_val *cval) 2117 { 2118 pjsua_acc_config acc_cfg; 2119 pj_cli_cmd_id cmd_id = pj_cli_get_cmd_id(cval->cmd); 2120 2121 CHECK_PJSUA_RUNNING(); 2122 2123 pjsua_acc_get_config(current_acc, &acc_cfg); 2124 2125 switch(cmd_id) { 2126 case CMD_VIDEO_ACC_AUTORX: 2127 case CMD_VIDEO_ACC_AUTOTX: 2128 { 2129 int on = (pj_ansi_strnicmp(cval->argv[1].ptr, "On", 2)==0); 2130 2131 if (cmd_id == CMD_VIDEO_ACC_AUTORX) 2132 acc_cfg.vid_in_auto_show = on; 2133 else 2134 acc_cfg.vid_out_auto_transmit = on; 2135 } 2136 break; 2137 case CMD_VIDEO_ACC_CAP_ID: 2138 case CMD_VIDEO_ACC_REN_ID: 2139 { 2140 int dev = pj_strtol(&cval->argv[1]); 2141 2142 if (cmd_id == CMD_VIDEO_ACC_CAP_ID) 2143 acc_cfg.vid_cap_dev = dev; 2144 else 2145 acc_cfg.vid_rend_dev = dev; 2146 } 2147 break; 2148 } 2149 modify_video_account(&acc_cfg); 2150 return PJ_SUCCESS; 2151 } 2152 2153 static pj_status_t cmd_add_vid_strm() 2154 { 2155 return pjsua_call_set_vid_strm(current_call, 2156 PJSUA_CALL_VID_STRM_ADD, NULL); 2157 } 2158 2159 static pj_status_t cmd_enable_vid_rx(pj_cli_cmd_val *cval) 2160 { 2161 pjsua_call_vid_strm_op_param param; 2162 pjsua_stream_info si; 2163 pj_status_t status = PJ_SUCCESS; 2164 pj_bool_t on = (pj_ansi_strnicmp(cval->argv[1].ptr, "On", 2) == 0); 2165 2166 pjsua_call_vid_strm_op_param_default(¶m); 2167 2168 param.med_idx = pj_strtol(&cval->argv[2]); 2169 if (pjsua_call_get_stream_info(current_call, param.med_idx, &si) || 2170 si.type != PJMEDIA_TYPE_VIDEO) 2171 { 2172 PJ_PERROR(1,(THIS_FILE, PJ_EINVAL, "Invalid stream")); 2173 return status; 2174 } 2175 2176 if (on) param.dir = (si.info.vid.dir | PJMEDIA_DIR_DECODING); 2177 else param.dir = (si.info.vid.dir & PJMEDIA_DIR_ENCODING); 2178 2179 status = pjsua_call_set_vid_strm(current_call, 2180 PJSUA_CALL_VID_STRM_CHANGE_DIR, 2181 ¶m); 2182 return status; 2183 } 2184 2185 static pj_status_t cmd_enable_vid_tx(pj_cli_cmd_val *cval) 2186 { 2187 pjsua_call_vid_strm_op_param param; 2188 pj_status_t status = PJ_SUCCESS; 2189 pj_bool_t on = (pj_ansi_strnicmp(cval->argv[1].ptr, "On", 2) == 0); 2190 2191 pjsua_call_vid_strm_op op = on? PJSUA_CALL_VID_STRM_START_TRANSMIT : 2192 PJSUA_CALL_VID_STRM_STOP_TRANSMIT; 2193 2194 pjsua_call_vid_strm_op_param_default(¶m); 2195 2196 param.med_idx = pj_strtol(&cval->argv[2]); 2197 2198 status = pjsua_call_set_vid_strm(current_call, op, ¶m); 2199 return status; 2200 } 2201 2202 static pj_status_t cmd_enable_vid_stream(pj_cli_cmd_val *cval, 2203 pj_bool_t enable) 2204 { 2205 pjsua_call_vid_strm_op_param param; 2206 pjsua_call_vid_strm_op op = enable? PJSUA_CALL_VID_STRM_CHANGE_DIR : 2207 PJSUA_CALL_VID_STRM_REMOVE; 2208 2209 pjsua_call_vid_strm_op_param_default(¶m); 2210 2211 param.med_idx = cval->argc > 1 ? pj_strtol(&cval->argv[1]) : -1; 2212 param.dir = PJMEDIA_DIR_ENCODING_DECODING; 2213 return pjsua_call_set_vid_strm(current_call, op, ¶m); 2214 } 2215 2216 static pj_status_t cmd_set_cap_dev_id(pj_cli_cmd_val *cval) 2217 { 2218 pjsua_call_vid_strm_op_param param; 2219 2220 pjsua_call_vid_strm_op_param_default(¶m); 2221 param.med_idx = cval->argc > 1? pj_strtol(&cval->argv[1]) : -1; 2222 param.cap_dev = cval->argc > 2? pj_strtol(&cval->argv[2]) : 2223 PJMEDIA_VID_DEFAULT_CAPTURE_DEV; 2224 2225 return pjsua_call_set_vid_strm(current_call, 2226 PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV, 2227 ¶m); 2228 } 2229 2230 static pj_status_t cmd_list_vid_dev() 2231 { 2232 vid_list_devs(); 2233 return PJ_SUCCESS; 2234 } 2235 2236 static pj_status_t cmd_vid_device_refresh() 2237 { 2238 pjmedia_vid_dev_refresh(); 2239 return PJ_SUCCESS; 2240 } 2241 2242 static pj_status_t cmd_vid_device_preview(pj_cli_cmd_val *cval) 2243 { 2244 int dev_id = pj_strtol(&cval->argv[2]); 2245 pj_bool_t on = (pj_ansi_strnicmp(cval->argv[1].ptr, "On", 2) == 0); 2246 2247 if (on) { 2248 pjsua_vid_preview_param param; 2249 2250 pjsua_vid_preview_param_default(¶m); 2251 param.wnd_flags = PJMEDIA_VID_DEV_WND_BORDER | 2252 PJMEDIA_VID_DEV_WND_RESIZABLE; 2253 pjsua_vid_preview_start(dev_id, ¶m); 2254 arrange_window(pjsua_vid_preview_get_win(dev_id)); 2255 } else { 2256 pjsua_vid_win_id wid; 2257 wid = pjsua_vid_preview_get_win(dev_id); 2258 if (wid != PJSUA_INVALID_ID) { 2259 /* Preview window hiding once it is stopped is 2260 * responsibility of app */ 2261 pjsua_vid_win_set_show(wid, PJ_FALSE); 2262 pjsua_vid_preview_stop(dev_id); 2263 } 2264 } 2265 return PJ_SUCCESS; 2266 } 2267 2268 static pj_status_t cmd_vid_codec_list() 2269 { 2270 pjsua_codec_info ci[PJMEDIA_CODEC_MGR_MAX_CODECS]; 2271 unsigned count = PJ_ARRAY_SIZE(ci); 2272 pj_status_t status = pjsua_vid_enum_codecs(ci, &count); 2273 if (status != PJ_SUCCESS) { 2274 PJ_PERROR(1,(THIS_FILE, status, "Error enumerating codecs")); 2275 } else { 2276 unsigned i; 2277 PJ_LOG(3,(THIS_FILE, "Found %d video codecs:", count)); 2278 PJ_LOG(3,(THIS_FILE, "codec id prio fps bw(kbps) size")); 2279 PJ_LOG(3,(THIS_FILE, "------------------------------------------")); 2280 for (i=0; i<count; ++i) { 2281 pjmedia_vid_codec_param cp; 2282 pjmedia_video_format_detail *vfd; 2283 2284 status = pjsua_vid_codec_get_param(&ci[i].codec_id, &cp); 2285 if (status != PJ_SUCCESS) 2286 continue; 2287 2288 vfd = pjmedia_format_get_video_format_detail(&cp.enc_fmt, 2289 PJ_TRUE); 2290 PJ_LOG(3,(THIS_FILE, "%.*s%.*s %3d %7.2f %4d/%4d %dx%d", 2291 (int)ci[i].codec_id.slen, ci[i].codec_id.ptr, 2292 13-(int)ci[i].codec_id.slen, " ", 2293 ci[i].priority, 2294 (vfd->fps.num*1.0/vfd->fps.denum), 2295 vfd->avg_bps/1000, vfd->max_bps/1000, 2296 vfd->size.w, vfd->size.h)); 2297 } 2298 } 2299 return PJ_SUCCESS; 2300 } 2301 2302 static pj_status_t cmd_set_vid_codec_prio(pj_cli_cmd_val *cval) 2303 { 2304 int prio = pj_strtol(&cval->argv[2]); 2305 pj_status_t status; 2306 2307 status = pjsua_vid_codec_set_priority(&cval->argv[1], (pj_uint8_t)prio); 2308 if (status != PJ_SUCCESS) 2309 PJ_PERROR(1,(THIS_FILE, status, "Set codec priority error")); 2310 2311 return PJ_SUCCESS; 2312 } 2313 2314 static pj_status_t cmd_set_vid_codec_fps(pj_cli_cmd_val *cval) 2315 { 2316 pjmedia_vid_codec_param cp; 2317 int M, N; 2318 pj_status_t status; 2319 2320 M = pj_strtol(&cval->argv[2]); 2321 N = pj_strtol(&cval->argv[3]); 2322 status = pjsua_vid_codec_get_param(&cval->argv[1], &cp); 2323 if (status == PJ_SUCCESS) { 2324 cp.enc_fmt.det.vid.fps.num = M; 2325 cp.enc_fmt.det.vid.fps.denum = N; 2326 status = pjsua_vid_codec_set_param(&cval->argv[1], &cp); 2327 } 2328 if (status != PJ_SUCCESS) 2329 PJ_PERROR(1,(THIS_FILE, status, "Set codec framerate error")); 2330 2331 return PJ_SUCCESS; 2332 } 2333 2334 static pj_status_t cmd_set_vid_codec_bitrate(pj_cli_cmd_val *cval) 2335 { 2336 pjmedia_vid_codec_param cp; 2337 int M, N; 2338 pj_status_t status; 2339 2340 M = pj_strtol(&cval->argv[2]); 2341 N = pj_strtol(&cval->argv[3]); 2342 status = pjsua_vid_codec_get_param(&cval->argv[1], &cp); 2343 if (status == PJ_SUCCESS) { 2344 cp.enc_fmt.det.vid.avg_bps = M * 1000; 2345 cp.enc_fmt.det.vid.max_bps = N * 1000; 2346 status = pjsua_vid_codec_set_param(&cval->argv[1], &cp); 2347 } 2348 if (status != PJ_SUCCESS) 2349 PJ_PERROR(1,(THIS_FILE, status, "Set codec bitrate error")); 2350 2351 return status; 2352 } 2353 2354 static pj_status_t cmd_set_vid_codec_size(pj_cli_cmd_val *cval) 2355 { 2356 pjmedia_vid_codec_param cp; 2357 int M, N; 2358 pj_status_t status; 2359 2360 M = pj_strtol(&cval->argv[2]); 2361 N = pj_strtol(&cval->argv[3]); 2362 status = pjsua_vid_codec_get_param(&cval->argv[1], &cp); 2363 if (status == PJ_SUCCESS) { 2364 cp.enc_fmt.det.vid.size.w = M; 2365 cp.enc_fmt.det.vid.size.h = N; 2366 status = pjsua_vid_codec_set_param(&cval->argv[1], &cp); 2367 } 2368 if (status != PJ_SUCCESS) 2369 PJ_PERROR(1,(THIS_FILE, status, "Set codec size error")); 2370 2371 return status; 2372 } 2373 2374 static pj_status_t cmd_vid_win_list() 2375 { 2376 pjsua_vid_win_id wids[PJSUA_MAX_VID_WINS]; 2377 unsigned i, cnt = PJ_ARRAY_SIZE(wids); 2378 2379 pjsua_vid_enum_wins(wids, &cnt); 2380 2381 PJ_LOG(3,(THIS_FILE, "Found %d video windows:", cnt)); 2382 PJ_LOG(3,(THIS_FILE, "WID show pos size")); 2383 PJ_LOG(3,(THIS_FILE, "------------------------------")); 2384 for (i = 0; i < cnt; ++i) { 2385 pjsua_vid_win_info wi; 2386 pjsua_vid_win_get_info(wids[i], &wi); 2387 PJ_LOG(3,(THIS_FILE, "%3d %c (%d,%d) %dx%d", 2388 wids[i], (wi.show?'Y':'N'), wi.pos.x, wi.pos.y, 2389 wi.size.w, wi.size.h)); 2390 } 2391 return PJ_SUCCESS; 2392 } 2393 2394 static pj_status_t cmd_arrange_vid_win() 2395 { 2396 arrange_window(PJSUA_INVALID_ID); 2397 return PJ_SUCCESS; 2398 } 2399 2400 static pj_status_t cmd_show_vid_win(pj_cli_cmd_val *cval, pj_bool_t show) 2401 { 2402 pjsua_vid_win_id wid = pj_strtol(&cval->argv[1]); 2403 return pjsua_vid_win_set_show(wid, show); 2404 } 2405 2406 static pj_status_t cmd_move_vid_win(pj_cli_cmd_val *cval) 2407 { 2408 pjsua_vid_win_id wid = pj_strtol(&cval->argv[1]); 2409 pjmedia_coord pos; 2410 2411 pos.x = pj_strtol(&cval->argv[2]); 2412 pos.y = pj_strtol(&cval->argv[3]); 2413 return pjsua_vid_win_set_pos(wid, &pos); 2414 } 2415 2416 static pj_status_t cmd_resize_vid_win(pj_cli_cmd_val *cval) 2417 { 2418 pjsua_vid_win_id wid = pj_strtol(&cval->argv[1]); 2419 pjmedia_rect_size size; 2420 2421 size.w = pj_strtol(&cval->argv[2]); 2422 size.h = pj_strtol(&cval->argv[3]); 2423 return pjsua_vid_win_set_size(wid, &size); 2424 } 2425 2426 /* Video handler */ 2427 static pj_status_t cmd_video_handler(pj_cli_cmd_val *cval) 2428 { 2429 pj_status_t status = PJ_SUCCESS; 2430 pj_cli_cmd_id cmd_id = pj_cli_get_cmd_id(cval->cmd); 2431 2432 CHECK_PJSUA_RUNNING(); 2433 2434 switch(cmd_id) { 2435 case CMD_VIDEO_ENABLE: 2436 status = cmd_set_video_enable(PJ_TRUE); 2437 break; 2438 case CMD_VIDEO_DISABLE: 2439 status = cmd_set_video_enable(PJ_FALSE); 2440 break; 2441 case CMD_VIDEO_ACC_SHOW: 2442 status = cmd_show_account_video(); 2443 break; 2444 case CMD_VIDEO_ACC_AUTORX: 2445 case CMD_VIDEO_ACC_AUTOTX: 2446 case CMD_VIDEO_ACC_CAP_ID: 2447 case CMD_VIDEO_ACC_REN_ID: 2448 status = cmd_video_acc_handler(cval); 2449 break; 2450 case CMD_VIDEO_CALL_ADD: 2451 status = cmd_add_vid_strm(); 2452 break; 2453 case CMD_VIDEO_CALL_RX: 2454 status = cmd_enable_vid_rx(cval); 2455 break; 2456 case CMD_VIDEO_CALL_TX: 2457 status = cmd_enable_vid_tx(cval); 2458 break; 2459 case CMD_VIDEO_CALL_ENABLE: 2460 case CMD_VIDEO_CALL_DISABLE: 2461 status = cmd_enable_vid_stream(cval, (cmd_id==CMD_VIDEO_CALL_ENABLE)); 2462 break; 2463 case CMD_VIDEO_CALL_CAP: 2464 status = cmd_set_cap_dev_id(cval); 2465 break; 2466 case CMD_VIDEO_DEVICE_LIST: 2467 status = cmd_list_vid_dev(); 2468 break; 2469 case CMD_VIDEO_DEVICE_REFRESH: 2470 status = cmd_vid_device_refresh(); 2471 break; 2472 case CMD_VIDEO_DEVICE_PREVIEW: 2473 status = cmd_vid_device_preview(cval); 2474 break; 2475 case CMD_VIDEO_CODEC_LIST: 2476 status = cmd_vid_codec_list(); 2477 break; 2478 case CMD_VIDEO_CODEC_PRIO: 2479 status = cmd_set_vid_codec_prio(cval); 2480 break; 2481 case CMD_VIDEO_CODEC_FPS: 2482 status = cmd_set_vid_codec_fps(cval); 2483 break; 2484 case CMD_VIDEO_CODEC_BITRATE: 2485 status = cmd_set_vid_codec_bitrate(cval); 2486 break; 2487 case CMD_VIDEO_CODEC_SIZE: 2488 status = cmd_set_vid_codec_size(cval); 2489 break; 2490 case CMD_VIDEO_WIN_LIST: 2491 status = cmd_vid_win_list(); 2492 break; 2493 case CMD_VIDEO_WIN_ARRANGE: 2494 status = cmd_arrange_vid_win(cval); 2495 break; 2496 case CMD_VIDEO_WIN_SHOW: 2497 case CMD_VIDEO_WIN_HIDE: 2498 status = cmd_show_vid_win(cval, (cmd_id==CMD_VIDEO_WIN_SHOW)); 2499 break; 2500 case CMD_VIDEO_WIN_MOVE: 2501 status = cmd_move_vid_win(cval); 2502 break; 2503 case CMD_VIDEO_WIN_RESIZE: 2504 status = cmd_resize_vid_win(cval); 2505 break; 2506 } 2507 2508 return status; 2509 } 2510 #endif 2511 2512 /* Other command handler */ 2513 static pj_status_t cmd_sleep_handler(pj_cli_cmd_val *cval) 2514 { 2515 int delay; 2516 2517 delay = pj_strtoul(&cval->argv[1]); 2518 if (delay < 0) delay = 0; 2519 pj_thread_sleep(delay); 2520 2521 return PJ_SUCCESS; 2522 } 2523 2524 static pj_status_t cmd_network_handler(pj_cli_cmd_val *cval) 2525 { 2526 pj_status_t status = PJ_SUCCESS; 2527 PJ_UNUSED_ARG(cval); 2528 2529 CHECK_PJSUA_RUNNING(); 2530 2531 status = pjsua_detect_nat_type(); 2532 if (status != PJ_SUCCESS) 2533 pjsua_perror(THIS_FILE, "Error", status); 2534 2535 return status; 2536 } 2537 2538 static pj_status_t cmd_quit_handler(pj_cli_cmd_val *cval) 2539 { 2540 PJ_LOG(3,(THIS_FILE, "Quitting app..")); 2541 pj_cli_quit(cval->sess->fe->cli, cval->sess, PJ_FALSE); 2542 2543 /* Invoke CLI stop callback (defined in pjsua_app.c) */ 2544 cli_on_stopped(PJ_FALSE, 0, NULL); 2545 2546 return PJ_SUCCESS; 2547 } 2548 2549 /* 2550 * Syntax error handler for parser. 2551 */ 2552 static void on_syntax_error(pj_scanner *scanner) 2553 { 2554 PJ_UNUSED_ARG(scanner); 2555 PJ_THROW(PJ_EINVAL); 2556 } 2557 2558 /* 2559 * This method will parse buffer string info array of argument string 2560 * @argc On input, maximum array size of argument. On output, number of argument 2561 * parsed 2562 * @argv Array of argument string 2563 */ 2564 static pj_status_t get_options(pj_str_t *options, unsigned *argc, 2565 pj_str_t argv[]) 2566 { 2567 pj_scanner scanner; 2568 unsigned max_argc = *argc; 2569 2570 PJ_USE_EXCEPTION; 2571 2572 if (!options) 2573 return PJ_SUCCESS; 2574 2575 pj_scan_init(&scanner, options->ptr, options->slen, PJ_SCAN_AUTOSKIP_WS, 2576 &on_syntax_error); 2577 PJ_TRY { 2578 *argc = 0; 2579 while (!pj_scan_is_eof(&scanner) && (max_argc > *argc)) { 2580 pj_str_t str; 2581 2582 pj_scan_get_until_chr(&scanner, " \t\r\n", &str); 2583 argv[*argc] = str; 2584 ++(*argc); 2585 } 2586 } 2587 PJ_CATCH_ANY { 2588 pj_scan_fini(&scanner); 2589 return PJ_GET_EXCEPTION(); 2590 } 2591 PJ_END; 2592 return PJ_SUCCESS; 2593 } 2594 2595 static pj_status_t cmd_restart_handler(pj_cli_cmd_val *cval) 2596 { 2597 enum { MAX_ARGC = 64 }; 2598 int i; 2599 unsigned argc = 1; 2600 static char argv_buffer[PJ_CLI_MAX_CMDBUF]; 2601 static char *argv[MAX_ARGC] = {NULL}; 2602 char *pbuf = argv_buffer; 2603 2604 PJ_LOG(3,(THIS_FILE, "Restarting app..")); 2605 pj_cli_quit(cval->sess->fe->cli, cval->sess, PJ_TRUE); 2606 2607 /** Get the pjsua option **/ 2608 for (i=1; i < cval->argc; i++) { 2609 pj_str_t argvst[MAX_ARGC]; 2610 unsigned j, ac; 2611 2612 ac = MAX_ARGC - argc; 2613 get_options(&cval->argv[i], &ac, argvst); 2614 for (j = 0; j < ac; j++) { 2615 pj_ansi_strncpy(pbuf, argvst[j].ptr, argvst[j].slen); 2616 pbuf[argvst[j].slen] = '\0'; 2617 argv[argc + j] = pbuf; 2618 pbuf += argvst[j].slen + 1; 2619 } 2620 argc += ac; 2621 } 2622 2623 /* Invoke CLI stop callback (defined in pjsua_app.c) */ 2624 cli_on_stopped(PJ_TRUE, argc, (char**)argv); 2625 2626 return PJ_SUCCESS; 2627 } 2628 2629 static pj_status_t add_call_command(pj_cli_t *cli) 2630 { 2631 char* call_command = 2632 "<CMD name='call' id='100' desc='Call related commands'>" 2633 " <CMD name='new' id='1001' desc='Show Help'>" 2634 " <ARG name='buddy_id' type='choice' id='9901' validate='0' " 2635 " desc='Buddy Id'>" 2636 " <CHOICE value='-1' desc='All buddies'/>" 2637 " <CHOICE value='0' desc='Current dialog'/>" 2638 " </ARG>" 2639 " </CMD>" 2640 " <CMD name='multi' id='1002' desc='Make multiple calls'>" 2641 " <ARG name='number_of_calls' type='int' desc='Number of calls'/>" 2642 " <ARG name='buddy_id' type='choice' id='9901' validate='0' " 2643 " desc='Buddy Id'>" 2644 " <CHOICE value='-1' desc='All buddies'/>" 2645 " <CHOICE value='0' desc='Current dialog'/>" 2646 " </ARG>" 2647 " </CMD>" 2648 " <CMD name='answer' id='1003' desc='Answer call'>" 2649 " <ARG name='code' type='int' desc='Answer code'/>" 2650 " <ARG name='new_url' type='string' optional='1' " 2651 " desc='New URL(for 3xx resp)'/>" 2652 " </CMD>" 2653 " <CMD name='hangup' id='1004' sc='g' desc='Hangup call'/>" 2654 " <CMD name='hangup_all' id='1005' sc='hA' desc='Hangup all call'/>" 2655 " <CMD name='hold' id='1006' sc='H' desc='Hold call'/>" 2656 " <CMD name='reinvite' id='1007' sc='v' " 2657 " desc='Re-invite (release hold)'/>" 2658 " <CMD name='update' id='1008' sc='U' desc='Send Update request'/>" 2659 " <CMD name='next' id='1009' sc='>' desc='Select next call'/>" 2660 " <CMD name='previous' id='1010' sc='<' desc='Select previous call'/>" 2661 " <CMD name='transfer' id='1011' sc='x' desc='Transfer call'>" 2662 " <ARG name='buddy_id' type='choice' id='9901' validate='0' " 2663 " desc='Buddy Id'>" 2664 " <CHOICE value='-1' desc='All buddies'/>" 2665 " <CHOICE value='0' desc='Current dialog'/>" 2666 " </ARG>" 2667 " </CMD>" 2668 " <CMD name='transfer_replaces' id='1012' sc='X' " 2669 " desc='Transfer replace call'>" 2670 " <ARG name='call_id' type='choice' id='9911' desc='Call Id'/>" 2671 " </CMD>" 2672 " <CMD name='redirect' id='1013' sc='R' desc='Redirect call'>" 2673 " <ARG name='redirect_option' type='choice' desc='Redirect option'>" 2674 " <CHOICE value='0' desc='Redirect accept replace'/>" 2675 " <CHOICE value='1' desc='Redirect accept'/>" 2676 " <CHOICE value='2' desc='Redirect reject'/>" 2677 " <CHOICE value='3' desc='Redirect stop'/>" 2678 " </ARG>" 2679 " </CMD>" 2680 " <CMD name='d_2833' id='1014' sc='#' desc='Send DTMF (RFC 2833)'>" 2681 " <ARG name='dtmf_to_send' type='string' " 2682 " desc='DTMF String to send'/>" 2683 " </CMD>" 2684 " <CMD name='d_info' id='1015' sc='*' desc='Send DTMF with SIP INFO'>" 2685 " <ARG name='dtmf_to_send' type='string' " 2686 " desc='DTMF String to send'/>" 2687 " </CMD>" 2688 " <CMD name='dump_q' id='1016' sc='dq' desc='Dump (call) quality'/>" 2689 " <CMD name='send_arb' id='1017' sc='S' desc='Send arbitrary request'>" 2690 " <ARG name='request_method' type='string' desc='Request method'/>" 2691 " <ARG name='buddy_id' type='choice' id='9901' validate='0' " 2692 " desc='Buddy Id'>" 2693 " <CHOICE value='-1' desc='All buddies'/>" 2694 " <CHOICE value='0' desc='Current dialog'/>" 2695 " </ARG>" 2696 " </CMD>" 2697 " <CMD name='list' id='1018' desc='Show current call'/>" 2698 "</CMD>"; 2699 2700 pj_str_t xml = pj_str(call_command); 2701 return pj_cli_add_cmd_from_xml(cli, NULL, 2702 &xml, cmd_call_handler, 2703 NULL, get_choice_value); 2704 } 2705 2706 static pj_status_t add_presence_command(pj_cli_t *cli) 2707 { 2708 char* presence_command = 2709 "<CMD name='im' id='200' desc='IM and Presence Commands'>" 2710 " <CMD name='add_b' id='2001' sc='+b' desc='Add buddy'>" 2711 " <ARG name='buddy_uri' type='string' desc='Buddy URI'/>" 2712 " </CMD>" 2713 " <CMD name='del_b' id='2002' sc='-b' desc='Delete buddy'>" 2714 " <ARG name='added_buddy_id' type='choice' id='9912' " 2715 " desc='Buddy ID'/>" 2716 " </CMD>" 2717 " <CMD name='send_im' id='2003' sc='i' desc='Send IM'>" 2718 " <ARG name='buddy_id' type='choice' id='9901' validate='0' " 2719 " desc='Buddy Id'>" 2720 " <CHOICE value='-1' desc='All buddies'/>" 2721 " <CHOICE value='0' desc='Current dialog'/>" 2722 " </ARG>" 2723 " <ARG name='message_content' type='string' desc='Message Content'/>" 2724 " </CMD>" 2725 " <CMD name='sub_pre' id='2004' desc='Subscribe presence'>" 2726 " <ARG name='buddy_id' type='choice' id='9901' validate='0' " 2727 " desc='Buddy Id'>" 2728 " <CHOICE value='-1' desc='All buddies'/>" 2729 " <CHOICE value='0' desc='Current dialog'/>" 2730 " </ARG>" 2731 " </CMD>" 2732 " <CMD name='unsub_pre' id='2005' desc='Unsubscribe Presence'>" 2733 " <ARG name='buddy_id' type='choice' id='9901' validate='0' " 2734 " desc='Buddy Id'>" 2735 " <CHOICE value='-1' desc='All buddies'/>" 2736 " <CHOICE value='0' desc='Current dialog'/>" 2737 " </ARG>" 2738 " </CMD>" 2739 " <CMD name='tog_state' id='2006' desc='Toggle online state'/>" 2740 " <CMD name='pre_text' id='2007' sc='T' " 2741 " desc='Specify custom presence text'>" 2742 " <ARG name='online_state' type='choice' desc='Online state'>" 2743 " <CHOICE value='1' desc='Available'/>" 2744 " <CHOICE value='2' desc='Busy'/>" 2745 " <CHOICE value='3' desc='On The Phone'/>" 2746 " <CHOICE value='4' desc='Idle'/>" 2747 " <CHOICE value='5' desc='Away'/>" 2748 " <CHOICE value='6' desc='Be Right Back'/>" 2749 " <CHOICE value='7' desc='Offline'/>" 2750 " </ARG>" 2751 " </CMD>" 2752 " <CMD name='bud_list' id='2008' sc='l' desc='Show buddy list'/>" 2753 "</CMD>"; 2754 2755 pj_str_t xml = pj_str(presence_command); 2756 2757 return pj_cli_add_cmd_from_xml(cli, NULL, 2758 &xml, cmd_presence_handler, 2759 NULL, get_choice_value); 2760 } 2761 2762 static pj_status_t add_account_command(pj_cli_t *cli) 2763 { 2764 char* account_command = 2765 "<CMD name='acc' id='300' desc='Account commands'>" 2766 " <CMD name='add' id='3001' sc='+a' desc='Add new account'>" 2767 " <ARG name='sip_url' type='string' desc='Your SIP URL'/>" 2768 " <ARG name='registrar_url' type='string' " 2769 " desc='URL of the registrar'/>" 2770 " <ARG name='auth_realm' type='string' desc='Auth realm'/>" 2771 " <ARG name='auth_username' type='string' desc='Auth username'/>" 2772 " <ARG name='auth_password' type='string' desc='Auth password'/>" 2773 " </CMD>" 2774 " <CMD name='del' id='3002' sc='-a' desc='Delete account'>" 2775 " <ARG name='account_id' type='choice' id='9902' desc='Account Id'/>" 2776 " </CMD>" 2777 " <CMD name='mod' id='3003' sc='!a' desc='Modify account'>" 2778 " <ARG name='account_id' type='choice' id='9902' desc='Account Id'/>" 2779 " <ARG name='sip_url' type='string' desc='Your SIP URL'/>" 2780 " <ARG name='registrar_url' type='string' " 2781 " desc='URL of the registrar'/>" 2782 " <ARG name='auth_realm' type='string' desc='Auth realm'/>" 2783 " <ARG name='auth_username' type='string' desc='Auth username'/>" 2784 " <ARG name='auth_password' type='string' desc='Auth password'/>" 2785 " </CMD>" 2786 " <CMD name='reg' id='3004' sc='rr' " 2787 " desc='Send (Refresh) Register request to register'/>" 2788 " <CMD name='unreg' id='3005' sc='ru' " 2789 " desc='Send Register request to unregister'/>" 2790 " <CMD name='next' id='3006' sc='<' " 2791 " desc='Select the next account for sending outgoing requests'>" 2792 " <ARG name='account_id' type='choice' id='9902' desc='Account Id'/>" 2793 " </CMD>" 2794 " <CMD name='prev' id='3007' sc='>' " 2795 " desc='Select the previous account for sending outgoing requests'>" 2796 " <ARG name='account_id' type='choice' id='9902' desc='Account Id'/>" 2797 " </CMD>" 2798 " <CMD name='show' id='3008' sc='l' desc='Show account list'/>" 2799 "</CMD>"; 2800 2801 pj_str_t xml = pj_str(account_command); 2802 return pj_cli_add_cmd_from_xml(cli, NULL, 2803 &xml, cmd_account_handler, 2804 NULL, get_choice_value); 2805 } 2806 2807 static pj_status_t add_media_command(pj_cli_t *cli) 2808 { 2809 char* media_command = 2810 "<CMD name='audio' id='400' desc='Conference and Media commands'>" 2811 " <CMD name='list' id='4001' sc='cl' desc='Show conference list'/>" 2812 " <CMD name='conf_con' id='4002' sc='cc' desc='Conference connect'>" 2813 " <ARG name='source_port' type='choice' id='9903' " 2814 " desc='Source Port'/>" 2815 " <ARG name='destination_port' type='choice' id='9903' " 2816 " desc='Destination Port'/>" 2817 " </CMD>" 2818 " <CMD name='conf_dis' id='4003' sc='cd' desc='Conference disconnect'>" 2819 " <ARG name='source_port' type='choice' id='9903' " 2820 " desc='Source Port'/>" 2821 " <ARG name='destination_port' type='choice' id='9903' " 2822 " desc='Destination Port'/>" 2823 " </CMD>" 2824 " <CMD name='adjust_vol' id='4004' sc='V' desc='Adjust volume'>" 2825 " <ARG name='mic_level' type='int' desc='Mic Level'/>" 2826 " <ARG name='speaker_port' type='int' desc='Speaker Level'/>" 2827 " </CMD>" 2828 " <CMD name='codec_prio' id='4005' sc='Cp' " 2829 " desc='Arrange codec priorities'>" 2830 " <ARG name='codec_id' type='choice' id='9904' desc='Codec Id'/>" 2831 " <ARG name='priority' type='int' desc='Codec Priority'/>" 2832 " </CMD>" 2833 "</CMD>"; 2834 2835 pj_str_t xml = pj_str(media_command); 2836 return pj_cli_add_cmd_from_xml(cli, NULL, 2837 &xml, cmd_media_handler, 2838 NULL, get_choice_value); 2839 } 2840 2841 static pj_status_t add_config_command(pj_cli_t *cli) 2842 { 2843 char* config_command = 2844 "<CMD name='stat' id='500' desc='Status and config commands'>" 2845 " <CMD name='dump_stat' id='5001' sc='ds' desc='Dump status'/>" 2846 " <CMD name='dump_detail' id='5002' sc='dd' " 2847 " desc='Dump detail status'/>" 2848 " <CMD name='dump_conf' id='5003' sc='dc' " 2849 " desc='Dump configuration to screen'/>" 2850 " <CMD name='write_setting' id='5004' sc='f' " 2851 " desc='Write current configuration file'>" 2852 " <ARG name='output_file' type='string' desc='Output filename'/>" 2853 " </CMD>" 2854 "</CMD>"; 2855 2856 pj_str_t xml = pj_str(config_command); 2857 return pj_cli_add_cmd_from_xml(cli, NULL, 2858 &xml, cmd_config_handler, 2859 NULL, get_choice_value); 2860 } 2861 2862 #if PJSUA_HAS_VIDEO 2863 static pj_status_t add_video_command(pj_cli_t *cli) 2864 { 2865 char* video_command = 2866 "<CMD name='video' id='600' desc='Video commands'>" 2867 " <CMD name='enable' id='6001' desc='Enable video'/>" 2868 " <CMD name='disable' id='6002' desc='Disable video'/>" 2869 " <CMD name='acc' id='6003' desc='Video setting for current account'>" 2870 " <CMD name='show' id='60031' " 2871 " desc='Show video setting for current account'/>" 2872 " <CMD name='autorx' id='60032' sc='ar' " 2873 " desc='Automatically show incoming video'>" 2874 " <ARG name='enable_option' type='choice' " 2875 " desc='Enable/Disable option'>" 2876 " <CHOICE value='On' desc='Enable'/>" 2877 " <CHOICE value='Off' desc='Disable'/>" 2878 " </ARG>" 2879 " </CMD>" 2880 " <CMD name='autotx' id='60033' sc='at' " 2881 " desc='Automatically offer video'>" 2882 " <ARG name='enable_option' type='choice' " 2883 " desc='Enable/Disable option'>" 2884 " <CHOICE value='On' desc='Enable'/>" 2885 " <CHOICE value='Off' desc='Disable'/>" 2886 " </ARG>" 2887 " </CMD>" 2888 " <CMD name='cap_id' id='60034' sc='ci' " 2889 " desc='Set default capture device for current account'>" 2890 " <ARG name='cap_dev_id' type='choice' id='9905' " 2891 " desc='Capture device Id'/>" 2892 " </CMD>" 2893 " <CMD name='ren_id' id='60035' sc='ri' " 2894 " desc='Set default renderer device for current account'>" 2895 " <ARG name='ren_dev_id' type='choice' id='9906' " 2896 " desc='Renderer device Id'/>" 2897 " </CMD>" 2898 " </CMD>" 2899 " <CMD name='call' id='6004' sc='cl' " 2900 " desc='Video call commands/settings'>" 2901 " <CMD name='rx' id='60041' sc='rx' " 2902 " desc='Enable/disable video RX for stream in curr call'>" 2903 " <ARG name='enable_option' type='choice' " 2904 " desc='Enable/Disable option'>" 2905 " <CHOICE value='On' desc='Enable'/>" 2906 " <CHOICE value='Off' desc='Disable'/>" 2907 " </ARG>" 2908 " <ARG name='stream_id' type='choice' id='9908' desc='Stream Id'/>" 2909 " </CMD>" 2910 " <CMD name='tx' id='60042' " 2911 " desc='Enable/disable video TX for stream in curr call'>" 2912 " <ARG name='enable_option' type='choice' " 2913 " desc='Enable/Disable option'>" 2914 " <CHOICE value='On' desc='Enable'/>" 2915 " <CHOICE value='Off' desc='Disable'/>" 2916 " </ARG>" 2917 " <ARG name='stream_id' type='choice' id='9908' desc='Stream Id'/>" 2918 " </CMD>" 2919 " <CMD name='add' id='60043' " 2920 " desc='Add video stream for current call'/>" 2921 " <CMD name='enable' id='60044' " 2922 " desc='Enable stream #N in current call'>" 2923 " <ARG name='stream_id' type='choice' id='9908' optional='1' " 2924 " desc='Stream Id'/>" 2925 " </CMD>" 2926 " <CMD name='disable' id='60045' " 2927 " desc='Disable stream #N in current call'>" 2928 " <ARG name='stream_id' type='choice' id='9908' optional='1' " 2929 " desc='Stream Id'/>" 2930 " </CMD>" 2931 " <CMD name='cap' id='60046' " 2932 " desc='Set capture dev ID for stream #N in current call'>" 2933 " <ARG name='stream_id' type='choice' id='9908' desc='Stream Id'/>" 2934 " <ARG name='cap_device_id' type='choice' id='9905' " 2935 " desc='Device Id'/>" 2936 " </CMD>" 2937 " </CMD>" 2938 " <CMD name='device' id='6005' sc='v' desc='Video device commands'>" 2939 " <CMD name='list' id='60051' desc='Show all video devices'/>" 2940 " <CMD name='refresh' id='60052' desc='Refresh video device list'/>" 2941 " <CMD name='prev' id='60053' " 2942 " desc='Enable/disable preview for specified device ID'>" 2943 " <ARG name='enable_option' type='choice' " 2944 " desc='Enable/Disable option'>" 2945 " <CHOICE value='On' desc='Enable'/>" 2946 " <CHOICE value='Off' desc='Disable'/>" 2947 " </ARG>" 2948 " <ARG name='device_id' type='choice' id='9907' " 2949 " desc='Video Device Id'/>" 2950 " </CMD>" 2951 " </CMD>" 2952 " <CMD name='codec' id='6006' desc='Video codec commands'>" 2953 " <CMD name='list' id='60061' desc='Show video codec list'/>" 2954 " <CMD name='prio' id='60062' desc='Set video codec priority'>" 2955 " <ARG name='codec_id' type='choice' id='9909' desc='Codec Id'/>" 2956 " <ARG name='priority' type='int' desc='Priority'/>" 2957 " </CMD>" 2958 " <CMD name='fps' id='60063' desc='Set video codec framerate'>" 2959 " <ARG name='codec_id' type='choice' id='9909' desc='Codec Id'/>" 2960 " <ARG name='num' type='int' desc='Numerator'/>" 2961 " <ARG name='denum' type='int' desc='Denumerator'/>" 2962 " </CMD>" 2963 " <CMD name='bitrate' id='60064' desc='Set video codec bitrate'>" 2964 " <ARG name='codec_id' type='choice' id='9909' desc='Codec Id'/>" 2965 " <ARG name='avg' type='int' desc='Average bps'/>" 2966 " <ARG name='max' type='int' desc='Maximum bps'/>" 2967 " </CMD>" 2968 " <CMD name='size' id='60065' desc='Set codec ID size/resolution'>" 2969 " <ARG name='codec_id' type='choice' id='9909' desc='Codec Id'/>" 2970 " <ARG name='width' type='int' desc='Width'/>" 2971 " <ARG name='height' type='int' desc='Height'/>" 2972 " </CMD>" 2973 " </CMD>" 2974 " <CMD name='win' id='6007' desc='Video windows settings/commands'>" 2975 " <CMD name='list' id='60071' desc='List all active video windows'/>" 2976 " <CMD name='arrange' id='60072' desc='Auto arrange windows'/>" 2977 " <CMD name='show' id='60073' desc='Show specific windows'>" 2978 " <ARG name='window_id' type='choice' id='9910' " 2979 " desc='Windows Id'/>" 2980 " </CMD>" 2981 " <CMD name='hide' id='60074' desc='Hide specific windows'>" 2982 " <ARG name='window_id' type='choice' id='9910' " 2983 " desc='Windows Id'/>" 2984 " </CMD>" 2985 " <CMD name='move' id='60075' desc='Move window position'>" 2986 " <ARG name='window_id' type='choice' id='9910' " 2987 " desc='Windows Id'/>" 2988 " <ARG name='x' type='int' desc='Horizontal position'/>" 2989 " <ARG name='y' type='int' desc='Vertical position'/>" 2990 " </CMD>" 2991 " <CMD name='resize' id='60076' " 2992 " desc='Resize window to specific width/height'>" 2993 " <ARG name='window_id' type='choice' id='9910' " 2994 " desc='Windows Id'/>" 2995 " <ARG name='width' type='int' desc='Width'/>" 2996 " <ARG name='height' type='int' desc='Height'/>" 2997 " </CMD>" 2998 " </CMD>" 2999 "</CMD>"; 3000 3001 pj_str_t xml = pj_str(video_command); 3002 return pj_cli_add_cmd_from_xml(cli, NULL, 3003 &xml, cmd_video_handler, 3004 NULL, get_choice_value); 3005 } 3006 #endif 3007 3008 static pj_status_t add_other_command(pj_cli_t *cli) 3009 { 3010 char* sleep_command = 3011 "<CMD name='sleep' id='700' desc='Suspend keyboard input'>" 3012 " <ARG name='msec' type='int' desc='Millisecond'/>" 3013 "</CMD>"; 3014 3015 char* network_command = 3016 "<CMD name='network' id='900' desc='Detect network type'/>"; 3017 3018 char* shutdown_command = 3019 "<CMD name='shutdown' id='110' desc='Shutdown application'/>"; 3020 3021 char* restart_command = 3022 "<CMD name='restart' id='120' desc='Restart application'>" 3023 " <ARG name='options1' type='string' desc='Options' optional='1'/>" 3024 " <ARG name='options2' type='string' desc='Options' optional='1'/>" 3025 " <ARG name='options3' type='string' desc='Options' optional='1'/>" 3026 " <ARG name='options4' type='string' desc='Options' optional='1'/>" 3027 "</CMD>"; 3028 3029 pj_status_t status; 3030 pj_str_t sleep_xml = pj_str(sleep_command); 3031 pj_str_t network_xml = pj_str(network_command); 3032 pj_str_t shutdown_xml = pj_str(shutdown_command); 3033 pj_str_t restart_xml = pj_str(restart_command); 3034 3035 status = pj_cli_add_cmd_from_xml(cli, NULL, 3036 &sleep_xml, cmd_sleep_handler, 3037 NULL, NULL); 175 3038 if (status != PJ_SUCCESS) 176 3039 return status; 177 3040 178 status = pj_ioqueue_create(app_config.pool, 16, ioqueue); 179 180 return status; 181 } 182 183 static pj_status_t stop_timer(pj_timer_heap_t *timer, 184 pj_ioqueue_t *ioqueue) 185 { 186 if ((!timer) || (!ioqueue)) 187 return PJ_SUCCESS; 188 189 pj_timer_heap_destroy(timer); 190 191 return pj_ioqueue_destroy(ioqueue); 192 } 193 194 pj_status_t cli_pjsua_start(pj_str_t *uri_to_call, 195 pj_timer_heap_t **main_timer_heap, 196 pj_ioqueue_t **main_ioqueue) 197 { 198 pj_status_t status = PJ_SUCCESS; 199 200 pjsua_restarted = PJ_FALSE; 201 202 if (app_config.disable_cli_console) { 203 status = setup_timer(main_timer_heap, main_ioqueue); 204 if (status != PJ_SUCCESS) 205 return status; 206 } 207 208 status = pjsua_start(); 3041 status = pj_cli_add_cmd_from_xml(cli, NULL, 3042 &network_xml, cmd_network_handler, 3043 NULL, NULL); 209 3044 if (status != PJ_SUCCESS) 210 3045 return status; 211 3046 212 pj_log_set_log_func(&log_writer); 213 214 setup_signal_handler(); 215 216 /* If user specifies URI to call, then call the URI */ 217 if (uri_to_call->slen) { 218 pjsua_call_make_call(current_acc, uri_to_call, &call_opt, NULL, 219 NULL, NULL); 220 } 221 222 if (!app_config.disable_cli_console) 223 PJ_LOG(3,(THIS_FILE, "CLI console is ready, press '?' for help")); 3047 status = pj_cli_add_cmd_from_xml(cli, NULL, 3048 &shutdown_xml, cmd_quit_handler, 3049 NULL, NULL); 3050 3051 if (status != PJ_SUCCESS) 3052 return status; 3053 3054 status = pj_cli_add_cmd_from_xml(cli, NULL, 3055 &restart_xml, cmd_restart_handler, 3056 NULL, NULL); 224 3057 225 3058 return status; 226 3059 } 227 3060 228 void start_cli_main(pj_str_t *uri_to_call, pj_bool_t *app_restart)229 { 3061 pj_status_t cli_setup_command(pj_cli_t *cli) 3062 { 230 3063 pj_status_t status; 231 char cmdline[PJ_CLI_MAX_CMDBUF]; 232 pj_timer_heap_t *main_timer_heap = NULL; 233 pj_ioqueue_t *main_ioqueue = NULL; 234 235 *app_restart = PJ_FALSE; 236 pjsua_restarted = PJ_TRUE; 237 238 do { 239 if (pjsua_restarted) { 240 status = cli_pjsua_start(uri_to_call, &main_timer_heap, 241 &main_ioqueue); 242 243 if (status != PJ_SUCCESS) 244 return; 245 } 246 247 if (app_config.disable_cli_console) { 248 pj_time_val delay = {0, 10}; 249 pj_ioqueue_poll(main_ioqueue, &delay); 250 if (pj_cli_is_quitting(cli)) 251 continue; 252 pj_timer_heap_poll(main_timer_heap, NULL); 253 } else { 254 pj_cli_console_process(cli_cons_sess, &cmdline[0], sizeof(cmdline)); 255 } 256 if (pjsua_restarted) { 257 status = stop_timer(main_timer_heap, main_ioqueue); 258 if (status != PJ_SUCCESS) 259 return; 260 261 status = app_init(NULL, NULL, NULL, NULL); 262 if (status != PJ_SUCCESS) 263 return; 264 } 265 266 } while (!pj_cli_is_quitting(cli)); 267 stop_timer(main_timer_heap, main_ioqueue); 268 *app_restart = pj_cli_is_restarting(cli); 269 } 3064 3065 status = add_call_command(cli); 3066 if (status != PJ_SUCCESS) 3067 return status; 3068 3069 status = add_presence_command(cli); 3070 if (status != PJ_SUCCESS) 3071 return status; 3072 3073 status = add_account_command(cli); 3074 if (status != PJ_SUCCESS) 3075 return status; 3076 3077 status = add_media_command(cli); 3078 if (status != PJ_SUCCESS) 3079 return status; 3080 3081 status = add_config_command(cli); 3082 if (status != PJ_SUCCESS) 3083 return status; 3084 3085 #if PJSUA_HAS_VIDEO 3086 status = add_video_command(cli); 3087 if (status != PJ_SUCCESS) 3088 return status; 3089 #endif 3090 3091 status = add_other_command(cli); 3092 3093 return status; 3094 } -
pjproject/trunk/pjsip-apps/src/pjsua/pjsua_common.h
r4461 r4476 26 26 #define current_acc pjsua_acc_get_default() 27 27 28 #define NO_LIMIT_DURATION(int)0x7FFFFFFF29 #define MAX_AVI430 #define NO_NB-228 #define PJSUA_APP_NO_LIMIT_DURATION (int)0x7FFFFFFF 29 #define PJSUA_APP_MAX_AVI 4 30 #define PJSUA_APP_NO_NB -2 31 31 32 32 typedef struct input_result … … 53 53 pj_bool_t out_auto_transmit; 54 54 } app_vid; 55 56 /* Enumeration of CLI frontends */ 57 typedef enum { 58 CLI_FE_CONSOLE = 1, 59 CLI_FE_TELNET = 2 60 } CLI_FE; 61 62 /** CLI config **/ 63 typedef struct cli_cfg_t 64 { 65 /** Bitmask of CLI_FE **/ 66 int cli_fe; 67 pj_cli_cfg cfg; 68 pj_cli_telnet_cfg telnet_cfg; 69 pj_cli_console_cfg console_cfg; 70 } cli_cfg_t; 55 71 56 72 /* Pjsua application data */ … … 134 150 pjmedia_vid_dev_index dev_id; 135 151 pjsua_conf_port_id slot; 136 } avi[ MAX_AVI];152 } avi[PJSUA_APP_MAX_AVI]; 137 153 pj_bool_t avi_auto_play; 138 154 int avi_def_idx; … … 140 156 /* CLI setting */ 141 157 pj_bool_t use_cli; 142 int cli_telnet_port; 143 pj_bool_t disable_cli_console; 158 cli_cfg_t cli_cfg; 144 159 } pjsua_app_config; 145 146 /** CLI callback **/147 /** This specifies the callback called when cli quit is called. **/148 typedef void (*pj_cli_on_quit)(pj_bool_t is_restarting);149 /** This callback is called when the cli is completely destroyed **/150 typedef void (*pj_cli_on_destroy)(void);151 /** This callback is called when pjsua restart command is invode by cli **/152 typedef void (*pj_cli_on_restart_pjsua)(void);153 160 154 161 /** Extern variable declaration **/ … … 159 166 extern pjsua_call_setting call_opt; 160 167 extern pjsua_msg_data msg_data; 168 extern pj_bool_t app_running; 161 169 162 170 PJ_DECL(int) my_atoi(const char *cs); … … 172 180 PJ_DECL(pj_bool_t) is_cli_inited(); 173 181 174 /** Defined in pjsua_app.c **/175 PJ_DECL(pj_status_t) app_init(pj_cli_telnet_on_started on_started_cb,176 pj_cli_on_quit on_quit_cb,177 pj_cli_on_destroy on_destroy_cb,178 pj_cli_on_restart_pjsua on_restart_pjsua_cb);179 PJ_DECL(void) setup_signal_handler(void);180 181 182 /** Defined in pjsua_config.c **/ 182 /** This is to store the app runtime/startup options **/183 PJ_DECL(void) add_startup_config(int argc, char *argv[]);184 /** This is to store the app reload options **/185 PJ_DECL(void) add_reload_config(unsigned idx, pj_str_t *option);186 183 /** This is to load the configuration **/ 187 PJ_DECL(pj_status_t) load_config(pjsua_app_config *app_config, 188 pj_str_t *uri_arg, 189 pj_bool_t app_running); 184 PJ_DECL(pj_status_t) load_config(int argc, 185 char **argv, 186 pj_str_t *uri_arg); 187 188 /** Pjsua app callback **/ 189 /** This callback is called when CLI is started. **/ 190 PJ_DECL(void) cli_on_started(pj_status_t status); 191 192 /** This callback is called when "shutdown"/"restart" command is invoked **/ 193 PJ_DECL(pj_bool_t) cli_on_stopped(pj_bool_t restart, int argc, char **argv); 194 195 /** This callback is called when "quit"/"restart" command is invoked **/ 196 PJ_DECL(pj_bool_t) legacy_on_stopped(pj_bool_t restart); 197 198 /** Pjsua cli method **/ 199 PJ_DECL(pj_status_t) cli_init(); 200 PJ_DECL(pj_status_t) cli_main(pj_bool_t wait_telnet_cli); 201 PJ_DECL(void) cli_destroy(); 202 PJ_DECL(void) cli_get_info(char *info, pj_size_t size); 203 204 /** Legacy method **/ 205 PJ_DECL(void) legacy_main(); 190 206 191 207 #if PJSUA_HAS_VIDEO … … 211 227 PJ_END_DECL 212 228 213 #endif /* __PJSUA_C MD_H__ */214 229 #endif /* __PJSUA_COMMON_H__ */ 230 -
pjproject/trunk/pjsip-apps/src/pjsua/pjsua_config.c
r4461 r4476 18 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 19 */ 20 #include <pjsua-lib/pjsua.h>21 20 #include "pjsua_common.h" 22 21 … … 26 25 27 26 char *stdout_refresh_text = "STDOUT_REFRESH"; 28 29 /** This is for storing console runtime options **/30 static int cmd_argc;31 static char cmd_argv[MAX_APP_OPTIONS][MAX_APP_OPTIONS];32 27 33 28 /* Show usage */ … … 327 322 328 323 /* Parse arguments. */ 329 static pj_status_t parse_args(int argc, char *argv[], 330 pjsua_app_config *cfg, 324 static pj_status_t parse_args(int argc, char *argv[], 331 325 pj_str_t *uri_to_call) 332 326 { 333 327 int c; 334 328 int option_index; 329 pjsua_app_config *cfg = &app_config; 335 330 enum { OPT_CONFIG_FILE=127, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, 336 331 OPT_LOG_APPEND, OPT_COLOR, OPT_NO_COLOR, OPT_LIGHT_BG, OPT_NO_STDERR, … … 1314 1309 1315 1310 case OPT_PLAY_AVI: 1316 if (app_config.avi_cnt >= MAX_AVI) {1311 if (app_config.avi_cnt >= PJSUA_APP_MAX_AVI) { 1317 1312 PJ_LOG(1,(THIS_FILE, "Too many AVIs")); 1318 1313 return -1; … … 1330 1325 1331 1326 case OPT_CLI_TELNET_PORT: 1332 cfg->cli_telnet_port = atoi(pj_optarg); 1327 cfg->cli_cfg.telnet_cfg.port = (pj_uint16_t)atoi(pj_optarg); 1328 cfg->cli_cfg.cli_fe |= CLI_FE_TELNET; 1333 1329 break; 1334 1330 1335 1331 case OPT_DISABLE_CLI_CONSOLE: 1336 cfg-> disable_cli_console = PJ_TRUE;1332 cfg->cli_cfg.cli_fe &= (~CLI_FE_CONSOLE); 1337 1333 break; 1338 1334 … … 1421 1417 1422 1418 /* Set default config. */ 1423 static void default_config( pjsua_app_config *cfg)1419 static void default_config() 1424 1420 { 1425 1421 char tmp[80]; 1426 1422 unsigned i; 1423 pjsua_app_config *cfg = &app_config; 1427 1424 1428 1425 pjsua_config_default(&cfg->cfg); … … 1438 1435 cfg->rtp_cfg.port = 4000; 1439 1436 cfg->redir_op = PJSIP_REDIRECT_ACCEPT_REPLACE; 1440 cfg->duration = NO_LIMIT_DURATION;1437 cfg->duration = PJSUA_APP_NO_LIMIT_DURATION; 1441 1438 cfg->wav_id = PJSUA_INVALID_ID; 1442 1439 cfg->rec_id = PJSUA_INVALID_ID; … … 1464 1461 1465 1462 cfg->use_cli = PJ_FALSE; 1466 cfg-> disable_cli_console = PJ_FALSE;1467 cfg->cli_ telnet_port = -1;1463 cfg->cli_cfg.cli_fe = CLI_FE_CONSOLE; 1464 cfg->cli_cfg.telnet_cfg.port = 0; 1468 1465 } 1469 1466 1470 static pj_status_t parse_config(int argc, char *argv[], 1471 pjsua_app_config *app_config, pj_str_t *uri_arg) 1467 static pj_status_t parse_config(int argc, char *argv[], pj_str_t *uri_arg) 1472 1468 { 1473 1469 pj_status_t status; … … 1477 1473 1478 1474 /* Parse the arguments */ 1479 status = parse_args(argc, argv, app_config,uri_arg);1475 status = parse_args(argc, argv, uri_arg); 1480 1476 return status; 1481 1477 } 1482 1478 1483 PJ_DEF(void) add_startup_config(int argc, char *argv[]) 1479 PJ_DEF(pj_status_t) load_config(int argc, 1480 char **argv, 1481 pj_str_t *uri_arg) 1484 1482 { 1485 int i;1486 cmd_argc = argc;1487 1488 for (i=0;i<argc;++i)1489 {1490 pj_memcpy(&cmd_argv[i], argv[i], strlen(argv[i]));1491 cmd_argv[i][strlen(argv[i])] = 0;1492 }1493 }1494 1495 PJ_DEF(void) add_reload_config(unsigned idx, pj_str_t *option)1496 {1497 /** First command always contain the app path**/1498 pj_str_t cmd;1499 1500 pj_assert(idx < MAX_APP_OPTIONS);1501 1502 cmd = pj_str(&cmd_argv[idx][0]);1503 pj_strncpy_with_null(&cmd, option, 128);1504 cmd_argc = idx+1;1505 }1506 1507 PJ_DEF(pj_status_t) load_config(pjsua_app_config *app_config,1508 pj_str_t *uri_arg,1509 pj_bool_t app_running)1510 {1511 int i;1512 int argc = cmd_argc;1513 char *argv[128];1514 1483 pj_status_t status; 1515 1484 pj_bool_t use_cli = PJ_FALSE; 1516 pj_bool_t disable_cli_console = PJ_TRUE;1517 int cli_telnet_port = 0;1485 int cli_fe = 0; 1486 pj_uint16_t cli_telnet_port = 0; 1518 1487 1519 1488 /** CLI options are not changable **/ 1520 1489 if (app_running) { 1521 use_cli = app_config->use_cli; 1522 disable_cli_console = app_config->disable_cli_console; 1523 cli_telnet_port = app_config->cli_telnet_port; 1524 } 1525 1526 for (i=0;i<cmd_argc;++i) 1527 { 1528 argv[i] = &cmd_argv[i][0]; 1529 } 1530 1531 status = parse_config(argc, argv, app_config, uri_arg); 1490 use_cli = app_config.use_cli; 1491 cli_fe = app_config.cli_cfg.cli_fe; 1492 cli_telnet_port = app_config.cli_cfg.telnet_cfg.port; 1493 } 1494 1495 status = parse_config(argc, argv, uri_arg); 1532 1496 if (status != PJ_SUCCESS) 1533 1497 return status; 1534 1498 1535 1499 if (app_running) { 1536 app_config ->use_cli = use_cli;1537 app_config ->disable_cli_console = disable_cli_console;1538 app_config ->cli_telnet_port = cli_telnet_port;1500 app_config.use_cli = use_cli; 1501 app_config.cli_cfg.cli_fe = cli_fe; 1502 app_config.cli_cfg.telnet_cfg.port = cli_telnet_port; 1539 1503 } 1540 1504 … … 2156 2120 2157 2121 /* Uas-duration. */ 2158 if (config->duration != NO_LIMIT_DURATION) {2122 if (config->duration != PJSUA_APP_NO_LIMIT_DURATION) { 2159 2123 pj_ansi_sprintf(line, "--duration %d\n", 2160 2124 config->duration); -
pjproject/trunk/pjsip-apps/src/pjsua/pjsua_legacy.c
r4465 r4476 65 65 input_result *result) 66 66 { 67 result->nb_result = NO_NB;67 result->nb_result = PJSUA_APP_NO_NB; 68 68 result->uri_result = NULL; 69 69 … … 124 124 125 125 puts("Invalid input"); 126 result->nb_result = NO_NB;126 result->nb_result = PJSUA_APP_NO_NB; 127 127 return; 128 128 … … 597 597 598 598 ui_input_url("Make call", buf, sizeof(buf), &result); 599 if (result.nb_result != NO_NB) {599 if (result.nb_result != PJSUA_APP_NO_NB) { 600 600 601 601 if (result.nb_result == -1 || result.nb_result == 0) { … … 640 640 641 641 ui_input_url("Make call", buf, sizeof(buf), &result); 642 if (result.nb_result != NO_NB) {642 if (result.nb_result != PJSUA_APP_NO_NB) { 643 643 pjsua_buddy_info binfo; 644 644 if (result.nb_result == -1 || result.nb_result == 0) { … … 682 682 /* Input destination. */ 683 683 ui_input_url("Send IM to", buf, sizeof(buf), &result); 684 if (result.nb_result != NO_NB) {684 if (result.nb_result != PJSUA_APP_NO_NB) { 685 685 686 686 if (result.nb_result == -1) { … … 1085 1085 pj_list_push_back(&msg_data.hdr_list, &refer_sub); 1086 1086 } 1087 if (result.nb_result != NO_NB) {1087 if (result.nb_result != PJSUA_APP_NO_NB) { 1088 1088 if (result.nb_result == -1 || result.nb_result == 0) 1089 1089 puts("You can't do that with transfer call!"); … … 1288 1288 uri = NULL; 1289 1289 ui_input_url("Destination URI", buf, sizeof(buf), &result); 1290 if (result.nb_result != NO_NB) {1290 if (result.nb_result != PJSUA_APP_NO_NB) { 1291 1291 1292 1292 if (result.nb_result == -1) { … … 1369 1369 1370 1370 ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result); 1371 if (result.nb_result != NO_NB) {1371 if (result.nb_result != PJSUA_APP_NO_NB) { 1372 1372 if (result.nb_result == -1) { 1373 1373 int i, count; … … 1670 1670 * Main "user interface" loop. 1671 1671 */ 1672 void console_app_main(const pj_str_t *uri_to_call, pj_bool_t *app_restart)1672 PJ_DEF(void) legacy_main() 1673 1673 { 1674 1674 char menuin[80]; 1675 1675 char buf[128]; 1676 1677 pjsua_call_setting_default(&call_opt);1678 call_opt.aud_cnt = app_config.aud_cnt;1679 call_opt.vid_cnt = app_config.vid.vid_cnt;1680 1681 /* If user specifies URI to call, then call the URI */1682 if (uri_to_call->slen) {1683 pjsua_call_make_call( current_acc, uri_to_call, &call_opt,1684 NULL, NULL, NULL);1685 }1686 1676 1687 1677 keystroke_help(current_call); … … 1915 1905 1916 1906 case 'L': /* Restart */ 1917 *app_restart = PJ_TRUE;1918 /* Continues below */1919 1920 1907 case 'q': 1908 legacy_on_stopped(menuin[0]=='L'); 1921 1909 goto on_exit; 1922 1910 … … 1937 1925 ; 1938 1926 } 1939 1940 void start_ui_main(pj_str_t *uri_to_call, pj_bool_t *app_restart)1941 {1942 pj_status_t status;1943 *app_restart = PJ_FALSE;1944 1945 status = pjsua_start();1946 if (status != PJ_SUCCESS)1947 return;1948 1949 setup_signal_handler();1950 1951 /* If user specifies URI to call, then call the URI */1952 if (uri_to_call->slen) {1953 pjsua_call_make_call(current_acc, uri_to_call, &call_opt, NULL,1954 NULL, NULL);1955 }1956 1957 console_app_main(uri_to_call, app_restart);1958 }
Note: See TracChangeset
for help on using the changeset viewer.