Changeset 4476 for pjproject/trunk/pjsip-apps/src/pjsua/pjsua_cli.c
- Timestamp:
- Apr 19, 2013 6:05:06 AM (11 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
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 }
Note: See TracChangeset
for help on using the changeset viewer.