- Timestamp:
- Apr 15, 2009 1:38:40 PM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjsip-apps/src/samples/icedemo.c
r2592 r2600 26 26 #define THIS_FILE "icedemo.c" 27 27 28 /* For this demo app, configure longer STUN keep-alive time 29 * so that it does't clutter the screen output. 30 */ 31 #define KA_INTERVAL 300 32 33 34 /* This is our global variables */ 28 35 static struct app_t 29 36 { 37 /* Command line options are stored here */ 30 38 struct options 31 39 { … … 41 49 } opt; 42 50 51 /* Our global variables */ 43 52 pj_caching_pool cp; 44 53 pj_pool_t *pool; … … 48 57 pj_ice_strans *icest; 49 58 59 /* Variables to store parsed remote ICE info */ 50 60 struct rem_info 51 61 { … … 60 70 } icedemo; 61 71 72 /* Utility to display error messages */ 62 73 static void icedemo_perror(const char *title, pj_status_t status) 63 74 { … … 68 79 } 69 80 81 /* Utility: display error message and exit application (usually 82 * because of fatal error. 83 */ 70 84 static void err_exit(const char *title, pj_status_t status) 71 85 { … … 103 117 } 104 118 105 static pj_status_t icedemo_handle_events(unsigned max_msec, 106 unsigned *p_count) 119 /* 120 * This function checks for events from both timer and ioqueue (for 121 * network events). It is invoked by the worker thread. 122 */ 123 static pj_status_t handle_events(unsigned max_msec, unsigned *p_count) 107 124 { 108 125 enum { MAX_NET_EVENTS = 1 }; … … 126 143 if (timeout.msec >= 1000) timeout.msec = 999; 127 144 128 /* compare the value with the timeout to wait from timer, and use the minimum value. */ 145 /* compare the value with the timeout to wait from timer, and use the 146 * minimum value. 147 */ 129 148 if (PJ_TIME_VAL_GT(timeout, max_timeout)) 130 149 timeout = max_timeout; … … 165 184 } 166 185 186 /* 187 * This is the worker thread that polls event in the background. 188 */ 167 189 static int icedemo_worker_thread(void *unused) 168 190 { … … 170 192 171 193 while (!icedemo.thread_quit_flag) { 172 icedemo_handle_events(500, NULL);194 handle_events(500, NULL); 173 195 } 174 196 … … 176 198 } 177 199 178 static void icedemo_on_rx_data(pj_ice_strans *ice_st, 179 unsigned comp_id, 180 void *pkt, pj_size_t size, 181 const pj_sockaddr_t *src_addr, 182 unsigned src_addr_len) 200 /* 201 * This is the callback that is registered to the ICE stream transport to 202 * receive notification about incoming data. By "data" it means application 203 * data such as RTP/RTCP, and not packets that belong to ICE signaling (such 204 * as STUN connectivity checks or TURN signaling). 205 */ 206 static void cb_on_rx_data(pj_ice_strans *ice_st, 207 unsigned comp_id, 208 void *pkt, pj_size_t size, 209 const pj_sockaddr_t *src_addr, 210 unsigned src_addr_len) 183 211 { 184 212 char ipstr[PJ_INET6_ADDRSTRLEN+10]; … … 188 216 PJ_UNUSED_ARG(pkt); 189 217 190 PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s", 218 ((char*)pkt)[size] = '\0'; 219 220 PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s: \"%s\"", 191 221 comp_id, size, 192 pj_sockaddr_print(src_addr, ipstr, sizeof(ipstr), 3))); 193 } 194 195 static void icedemo_on_ice_complete(pj_ice_strans *ice_st, 196 pj_ice_strans_op op, 197 pj_status_t status) 222 pj_sockaddr_print(src_addr, ipstr, sizeof(ipstr), 3), 223 (char*)pkt)); 224 } 225 226 /* 227 * This is the callback that is registered to the ICE stream transport to 228 * receive notification about ICE state progression. 229 */ 230 static void cb_on_ice_complete(pj_ice_strans *ice_st, 231 pj_ice_strans_op op, 232 pj_status_t status) 198 233 { 199 234 const char *opname = … … 213 248 } 214 249 250 251 /* 252 * This is the main application initialization function. It is called 253 * once (and only once) during application initialization sequence by 254 * main(). 255 */ 215 256 static pj_status_t icedemo_init(void) 216 257 { … … 231 272 232 273 /* Create application memory pool */ 233 icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", 512, 512, NULL); 274 icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", 275 512, 512, NULL); 234 276 235 277 /* Create timer heap for timer stuff */ 236 CHECK( pj_timer_heap_create(icedemo.pool, 100, &icedemo.ice_cfg.stun_cfg.timer_heap) ); 278 CHECK( pj_timer_heap_create(icedemo.pool, 100, 279 &icedemo.ice_cfg.stun_cfg.timer_heap) ); 237 280 238 281 /* and create ioqueue for network I/O stuff */ 239 CHECK( pj_ioqueue_create(icedemo.pool, 16, &icedemo.ice_cfg.stun_cfg.ioqueue) ); 240 241 /* something must poll the timer heap and ioqueue, unless we're on Symbian */ 242 CHECK( pj_thread_create(icedemo.pool, "icedemo", &icedemo_worker_thread, NULL, 0, 0, &icedemo.thread) ); 282 CHECK( pj_ioqueue_create(icedemo.pool, 16, 283 &icedemo.ice_cfg.stun_cfg.ioqueue) ); 284 285 /* something must poll the timer heap and ioqueue, 286 * unless we're on Symbian where the timer heap and ioqueue run 287 * on themselves. 288 */ 289 CHECK( pj_thread_create(icedemo.pool, "icedemo", &icedemo_worker_thread, 290 NULL, 0, 0, &icedemo.thread) ); 243 291 244 292 icedemo.ice_cfg.af = pj_AF_INET(); … … 249 297 "resolver", 250 298 0, 251 icedemo.ice_cfg.stun_cfg.timer_heap, 299 icedemo.ice_cfg.stun_cfg.timer_heap, 252 300 icedemo.ice_cfg.stun_cfg.ioqueue, 253 301 &icedemo.ice_cfg.resolver) ); … … 276 324 icedemo.ice_cfg.stun.port = PJ_STUN_PORT; 277 325 } 326 327 /* For this demo app, configure longer STUN keep-alive time 328 * so that it does't clutter the screen output. 329 */ 330 icedemo.ice_cfg.stun.cfg.ka_interval = KA_INTERVAL; 278 331 } 279 332 … … 290 343 } else { 291 344 icedemo.ice_cfg.turn.server = icedemo.opt.turn_srv; 292 icedemo.ice_cfg. stun.port = PJ_STUN_PORT;345 icedemo.ice_cfg.turn.port = PJ_STUN_PORT; 293 346 } 294 347 … … 305 358 icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_UDP; 306 359 307 /* You may further customize TURN settings, e.g.:308 icedemo.ice_cfg.turn.alloc_param.lifetime = 300360 /* For this demo app, configure longer keep-alive time 361 * so that it does't clutter the screen output. 309 362 */ 363 icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL; 310 364 } 311 365 … … 314 368 } 315 369 370 371 /* 372 * Create ICE stream transport instance, invoked from the menu. 373 */ 316 374 static void icedemo_create_instance(void) 317 375 { … … 326 384 /* init the callback */ 327 385 pj_bzero(&icecb, sizeof(icecb)); 328 icecb.on_rx_data = icedemo_on_rx_data;329 icecb.on_ice_complete = icedemo_on_ice_complete;386 icecb.on_rx_data = cb_on_rx_data; 387 icecb.on_ice_complete = cb_on_ice_complete; 330 388 331 389 /* create the instance */ … … 339 397 if (status != PJ_SUCCESS) 340 398 icedemo_perror("error creating ice", status); 341 } 342 399 else 400 PJ_LOG(3,(THIS_FILE, "ICE instance successfully created")); 401 } 402 403 /* Utility to nullify parsed remote info */ 343 404 static void reset_rem_info(void) 344 405 { … … 346 407 } 347 408 409 410 /* 411 * Destroy ICE stream transport instance, invoked from the menu. 412 */ 348 413 static void icedemo_destroy_instance(void) 349 414 { … … 357 422 358 423 reset_rem_info(); 359 } 360 424 425 PJ_LOG(3,(THIS_FILE, "ICE instance destroyed")); 426 } 427 428 429 /* 430 * Create ICE session, invoked from the menu. 431 */ 361 432 static void icedemo_init_session(unsigned rolechar) 362 433 { … … 379 450 if (status != PJ_SUCCESS) 380 451 icedemo_perror("error creating session", status); 452 else 453 PJ_LOG(3,(THIS_FILE, "ICE session created")); 381 454 382 455 reset_rem_info(); 383 456 } 384 457 458 459 /* 460 * Stop/destroy ICE session, invoked from the menu. 461 */ 385 462 static void icedemo_stop_session(void) 386 463 { … … 400 477 if (status != PJ_SUCCESS) 401 478 icedemo_perror("error stopping session", status); 479 else 480 PJ_LOG(3,(THIS_FILE, "ICE session stopped")); 402 481 403 482 reset_rem_info(); 404 483 } 405 406 484 407 485 #define PRINT(fmt, arg0, arg1, arg2, arg3, arg4, arg5) \ … … 411 489 p += printed 412 490 491 492 /* Utility to create a=candidate SDP attribute */ 413 493 static int print_cand(char buffer[], unsigned maxlen, 414 494 const pj_ice_sess_cand *cand) … … 427 507 (unsigned)pj_sockaddr_get_port(&cand->addr)); 428 508 429 switch (cand->type) { 430 case PJ_ICE_CAND_TYPE_HOST: 431 PRINT("host\n", 0, 0, 0, 0, 0, 0); 432 break; 433 case PJ_ICE_CAND_TYPE_SRFLX: 434 case PJ_ICE_CAND_TYPE_RELAYED: 435 case PJ_ICE_CAND_TYPE_PRFLX: 436 PRINT("%s raddr %s rport %d\n", 437 pj_ice_get_cand_type_name(cand->type), 438 pj_sockaddr_print(&cand->rel_addr, ipaddr, 439 sizeof(ipaddr), 0), 440 (int)pj_sockaddr_get_port(&cand->rel_addr), 441 0, 0, 0); 442 break; 443 default: 444 pj_assert(!"Invalid candidate type"); 445 return -PJ_EBUG; 446 } 509 PRINT("%s\n", 510 pj_ice_get_cand_type_name(cand->type), 511 0, 0, 0, 0, 0); 447 512 448 513 if (p == buffer+maxlen) … … 454 519 } 455 520 456 /* Encode ICE information in SDP */ 521 /* 522 * Encode ICE information in SDP. 523 */ 457 524 static int encode_session(char buffer[], unsigned maxlen) 458 525 { … … 463 530 pj_status_t status; 464 531 465 PRINT("v=0\no=- 3414953978 3414953978 IN IP4 127.0.0.1\ns=ice\nt=0 0\n", 532 /* Write "dummy" SDP v=, o=, s=, and t= lines */ 533 PRINT("v=0\no=- 3414953978 3414953978 IN IP4 localhost\ns=ice\nt=0 0\n", 466 534 0, 0, 0, 0, 0, 0); 467 535 … … 470 538 NULL, NULL); 471 539 540 /* Write the a=ice-ufrag and a=ice-pwd attributes */ 472 541 PRINT("a=ice-ufrag:%.*s\na=ice-pwd:%.*s\n", 473 542 (int)local_ufrag.slen, … … 477 546 0, 0); 478 547 548 /* Write each component */ 479 549 for (comp=0; comp<icedemo.opt.comp_cnt; ++comp) { 480 550 unsigned j, cand_cnt; … … 482 552 char ipaddr[PJ_INET6_ADDRSTRLEN]; 483 553 554 /* Get default candidate for the component */ 484 555 status = pj_ice_strans_get_def_cand(icedemo.icest, comp+1, &cand[0]); 485 556 if (status != PJ_SUCCESS) 486 557 return -status; 487 558 559 /* Write the default address */ 488 560 if (comp==0) { 561 /* For component 1, default address is in m= and c= lines */ 489 562 PRINT("m=audio %d RTP/AVP 0\n" 490 563 "c=IN IP4 %s\n", … … 494 567 0, 0, 0, 0); 495 568 } else if (comp==1) { 569 /* For component 2, default address is in a=rtcp line */ 496 570 PRINT("a=rtcp:%d IN IP4 %s\n", 497 571 (int)pj_sockaddr_get_port(&cand[0].addr), … … 500 574 0, 0, 0, 0); 501 575 } else { 576 /* For other components, we'll just invent this.. */ 502 577 PRINT("a=Xice-defcand:%d IN IP4 %s\n", 503 578 (int)pj_sockaddr_get_port(&cand[0].addr), … … 507 582 } 508 583 584 /* Enumerate all candidates for this component */ 509 585 status = pj_ice_strans_enum_cands(icedemo.icest, comp+1, 510 586 &cand_cnt, cand); … … 512 588 return -status; 513 589 590 /* And encode the candidates as SDP */ 514 591 for (j=0; j<cand_cnt; ++j) { 515 592 printed = print_cand(p, maxlen - (p-buffer), &cand[j]); … … 527 604 } 528 605 606 607 /* 608 * Show information contained in the ICE stream transport. This is 609 * invoked from the menu. 610 */ 529 611 static void icedemo_show_ice(void) 530 612 { … … 593 675 } 594 676 677 678 /* 679 * Input and parse SDP from the remote (containing remote's ICE information) 680 * and save it to global variables. 681 */ 595 682 static void icedemo_input_remote(void) 596 683 { 597 684 char linebuf[80]; 685 unsigned media_cnt = 0; 598 686 unsigned comp0_port = 0; 599 687 char comp0_addr[80]; … … 626 714 if (len==0) 627 715 break; 716 717 /* Ignore subsequent media descriptors */ 718 if (media_cnt > 1) 719 continue; 628 720 629 721 switch (line[0]) { … … 633 725 char media[32], portstr[32]; 634 726 727 ++media_cnt; 728 if (media_cnt > 1) { 729 puts("Media line ignored"); 730 break; 731 } 732 635 733 cnt = sscanf(line+2, "%s %s RTP/", media, portstr); 636 734 if (cnt != 2) { … … 640 738 641 739 comp0_port = atoi(portstr); 740 642 741 } 643 742 break; … … 792 891 } 793 892 794 printf("Done, %d remote candidates added\n", icedemo.rem.cand_cnt); 893 PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", 894 icedemo.rem.cand_cnt)); 795 895 return; 796 896 … … 799 899 } 800 900 901 902 /* 903 * Start ICE negotiation! This function is invoked from the menu. 904 */ 801 905 static void icedemo_start_nego(void) 802 906 { … … 818 922 return; 819 923 } 924 925 PJ_LOG(3,(THIS_FILE, "Starting ICE negotiation..")); 820 926 821 927 status = pj_ice_strans_start_ice(icedemo.icest, … … 830 936 } 831 937 938 939 /* 940 * Send application data to remote agent. 941 */ 832 942 static void icedemo_send_data(unsigned comp_id, const char *data) 833 943 { … … 844 954 } 845 955 956 /* 846 957 if (!pj_ice_strans_sess_is_complete(icedemo.icest)) { 847 958 PJ_LOG(1,(THIS_FILE, "Error: ICE negotiation has not been started or is in progress")); 848 959 return; 849 960 } 961 */ 850 962 851 963 if (comp_id > pj_ice_strans_get_running_comp_cnt(icedemo.icest)) { … … 863 975 } 864 976 977 978 /* 979 * Display help for the menu. 980 */ 865 981 static void icedemo_help_menu(void) 866 982 { 867 } 868 869 983 puts(""); 984 puts("-= Help on using ICE and this icedemo program =-"); 985 puts(""); 986 puts("This application demonstrates how to use ICE in pjnath without having\n" 987 "to use the SIP protocol. To use this application, you will need to run\n" 988 "two instances of this application, to simulate two ICE agents.\n"); 989 990 puts("Basic ICE flow:\n" 991 " create instance [menu \"c\"]\n" 992 " repeat these steps as wanted:\n" 993 " - init session as offerer or answerer [menu \"i\"]\n" 994 " - display our SDP [menu \"s\"]\n" 995 " - \"send\" our SDP from the \"show\" output above to remote, by\n" 996 " copy-pasting the SDP to the other icedemo application\n" 997 " - parse remote SDP, by pasting SDP generated by the other icedemo\n" 998 " instance [menu \"r\"]\n" 999 " - begin ICE negotiation in our end [menu \"b\"]\n" 1000 " - begin ICE negotiation in the other icedemo instance\n" 1001 " - ICE negotiation will run, and result will be printed to screen\n" 1002 " - send application data to remote [menu \"x\"]\n" 1003 " - end/stop ICE session [menu \"e\"]\n" 1004 " destroy instance [menu \"d\"]\n" 1005 ""); 1006 1007 puts(""); 1008 puts("This concludes the help screen."); 1009 puts(""); 1010 } 1011 1012 1013 /* 1014 * Display console menu 1015 */ 870 1016 static void icedemo_print_menu(void) 871 1017 { … … 889 1035 890 1036 1037 /* 1038 * Main console loop. 1039 */ 891 1040 static void icedemo_console(void) 892 1041 { … … 916 1065 917 1066 if (strcmp(cmd, "create")==0 || strcmp(cmd, "c")==0) { 1067 918 1068 icedemo_create_instance(); 1069 919 1070 } else if (strcmp(cmd, "destroy")==0 || strcmp(cmd, "d")==0) { 1071 920 1072 icedemo_destroy_instance(); 1073 921 1074 } else if (strcmp(cmd, "init")==0 || strcmp(cmd, "i")==0) { 1075 922 1076 char *role = strtok(NULL, SEP); 923 1077 if (role) … … 925 1079 else 926 1080 puts("error: Role required"); 1081 927 1082 } else if (strcmp(cmd, "stop")==0 || strcmp(cmd, "e")==0) { 1083 928 1084 icedemo_stop_session(); 1085 929 1086 } else if (strcmp(cmd, "show")==0 || strcmp(cmd, "s")==0) { 1087 930 1088 icedemo_show_ice(); 1089 931 1090 } else if (strcmp(cmd, "remote")==0 || strcmp(cmd, "r")==0) { 1091 932 1092 icedemo_input_remote(); 1093 933 1094 } else if (strcmp(cmd, "start")==0 || strcmp(cmd, "b")==0) { 1095 934 1096 icedemo_start_nego(); 1097 935 1098 } else if (strcmp(cmd, "send")==0 || strcmp(cmd, "x")==0) { 936 char *strcomp = strtok(NULL, SEP); 937 938 if (!strcmp) { 1099 1100 char *comp = strtok(NULL, SEP); 1101 1102 if (!comp) { 939 1103 PJ_LOG(1,(THIS_FILE, "Error: component ID required")); 940 1104 } else { 941 char *data = strcomp + strlen(strcomp) + 1; 942 icedemo_send_data(atoi(strcomp), data); 1105 char *data = comp + strlen(comp) + 1; 1106 if (!data) 1107 data = ""; 1108 icedemo_send_data(atoi(comp), data); 943 1109 } 1110 944 1111 } else if (strcmp(cmd, "help")==0 || strcmp(cmd, "h")==0) { 1112 945 1113 icedemo_help_menu(); 1114 946 1115 } else if (strcmp(cmd, "quit")==0 || strcmp(cmd, "q")==0) { 1116 947 1117 app_quit = PJ_TRUE; 1118 948 1119 } else { 1120 949 1121 printf("Invalid command '%s'\n", cmd); 1122 950 1123 } 951 1124 } … … 953 1126 954 1127 1128 /* 1129 * Display program usage. 1130 */ 955 1131 static void icedemo_usage() 956 1132 { … … 982 1158 983 1159 1160 /* 1161 * And here's the main() 1162 */ 984 1163 int main(int argc, char *argv[]) 985 1164 {
Note: See TracChangeset
for help on using the changeset viewer.