1、在不改动pjsip代码的情况下,和pjsip工程目录并行建立win32控制台程序工程P2PTraversal
目录结构如下:
.
├── pjproject-2.6
└── pjsipdemo
2、在VS2008下,新建项目
3、工程引入pjsip的相关配置
本例按照引入pjlib、pjlib-util、pjnath三个模块进行设置,如果引入更多需要参考如下设置增加对应的lib或.h目录设置
① 增加“附加包含目录”(针对引用头文件)
为了减少配置次数,在配置页面选择“所有配置”,可以一次性将“Debug”和“Release”全部配置好。主要是针对头文件,静态库则需要分别设置。
在工程属性页面中,找到C/C++选项的常规配置,在附加包含目录中添加如下路径(相对路径,按照目录层次关系)
../pjproject-2.6/pjlib/include/
../pjproject-2.6/pjlib-util/include/
../pjproject-2.6/pjnath/include/
② 设置“附加库目录”
../pjproject-2.6/pjlib/lib/
../pjproject-2.6/pjlib-util/lib/
../pjproject-2.6/pjnath/lib/
③ 设置“附加依赖项”
Debug设置:
ws2_32.lib
pjlib-i386-Win32-vc8-Debug.lib
pjlib-util-i386-Win32-vc8-Debug.lib
pjnath-i386-Win32-vc8-Debug.lib
Release设置
ws2_32.lib
pjlib-i386-Win32-vc8-Release.lib
pjlib-util-i386-Win32-vc8-Release.lib
pjnath-i386-Win32-vc8-Release.lib
4、在main.cpp文件中加入实现(以改造的icedemo为例)
1 /* $Id: icedemo.c 4624 2013-10-21 06:37:30Z ming $ */ 2 /* 3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <pjlib.h> 22 #include <pjlib-util.h> 23 #include <pjnath.h> 24 25 26 #define THIS_FILE "icedemo.c" 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 */ 35 static struct app_t 36 { 37 /* Command line options are stored here */ 38 struct options 39 { 40 unsigned comp_cnt; 41 pj_str_t ns; 42 int max_host; 43 pj_bool_t regular; 44 pj_str_t stun_srv; 45 pj_str_t turn_srv; 46 pj_bool_t turn_tcp; 47 pj_str_t turn_username; 48 pj_str_t turn_password; 49 pj_bool_t turn_fingerprint; 50 const char *log_file; 51 } opt; 52 53 /* Our global variables */ 54 pj_caching_pool cp; 55 pj_pool_t *pool; 56 pj_thread_t *thread; 57 pj_bool_t thread_quit_flag; 58 pj_ice_strans_cfg ice_cfg; 59 pj_ice_strans *icest; 60 FILE *log_fhnd; 61 62 /* Variables to store parsed remote ICE info */ 63 struct rem_info 64 65 { 66 char ufrag[80]; 67 char pwd[80]; 68 unsigned comp_cnt; 69 pj_sockaddr def_addr[PJ_ICE_MAX_COMP]; 70 unsigned cand_cnt; 71 pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND]; 72 } rem; 73 74 } icedemo; 75 76 /* Utility to display error messages */ 77 static void icedemo_perror(const char *title, pj_status_t status) 78 { 79 char errmsg[PJ_ERR_MSG_SIZE]; 80 81 pj_strerror(status, errmsg, sizeof(errmsg)); 82 PJ_LOG(1,(THIS_FILE, "%s: %s", title, errmsg)); 83 } 84 85 /* Utility: display error message and exit application (usually 86 * because of fatal error. 87 */ 88 static void err_exit(const char *title, pj_status_t status) 89 { 90 if (status != PJ_SUCCESS) 91 { 92 icedemo_perror(title, status); 93 } 94 PJ_LOG(3,(THIS_FILE, "Shutting down..")); 95 96 if (icedemo.icest) 97 pj_ice_strans_destroy(icedemo.icest); 98 99 pj_thread_sleep(500); 100 101 icedemo.thread_quit_flag = PJ_TRUE; 102 if (icedemo.thread) 103 { 104 pj_thread_join(icedemo.thread); 105 pj_thread_destroy(icedemo.thread); 106 } 107 108 if (icedemo.ice_cfg.stun_cfg.ioqueue) 109 pj_ioqueue_destroy(icedemo.ice_cfg.stun_cfg.ioqueue); 110 111 if (icedemo.ice_cfg.stun_cfg.timer_heap) 112 pj_timer_heap_destroy(icedemo.ice_cfg.stun_cfg.timer_heap); 113 114 pj_caching_pool_destroy(&icedemo.cp); 115 116 pj_shutdown(); 117 118 if (icedemo.log_fhnd) 119 { 120 fclose(icedemo.log_fhnd); 121 icedemo.log_fhnd = NULL; 122 } 123 124 exit(status != PJ_SUCCESS); 125 } 126 127 #define CHECK(expr) status=expr; 128 if (status!=PJ_SUCCESS) { 129 err_exit(#expr, status); 130 } 131 132 /* 133 * This function checks for events from both timer and ioqueue (for 134 * network events). It is invoked by the worker thread. 135 */ 136 static pj_status_t handle_events(unsigned max_msec, unsigned *p_count) 137 { 138 enum { MAX_NET_EVENTS = 1 }; 139 pj_time_val max_timeout = {0, 0}; 140 pj_time_val timeout = { 0, 0}; 141 unsigned count = 0, net_event_count = 0; 142 int c; 143 144 max_timeout.msec = max_msec; 145 146 /* Poll the timer to run it and also to retrieve the earliest entry. */ 147 timeout.sec = timeout.msec = 0; 148 c = pj_timer_heap_poll( icedemo.ice_cfg.stun_cfg.timer_heap, &timeout ); 149 if (c > 0) 150 count += c; 151 152 /* timer_heap_poll should never ever returns negative value, or otherwise 153 * ioqueue_poll() will block forever! 154 */ 155 pj_assert(timeout.sec >= 0 && timeout.msec >= 0); 156 if (timeout.msec >= 1000) timeout.msec = 999; 157 158 /* compare the value with the timeout to wait from timer, and use the 159 * minimum value. 160 */ 161 if (PJ_TIME_VAL_GT(timeout, max_timeout)) 162 timeout = max_timeout; 163 164 /* Poll ioqueue. 165 * Repeat polling the ioqueue while we have immediate events, because 166 * timer heap may process more than one events, so if we only process 167 * one network events at a time (such as when IOCP backend is used), 168 * the ioqueue may have trouble keeping up with the request rate. 169 * 170 * For example, for each send() request, one network event will be 171 * reported by ioqueue for the send() completion. If we don't poll 172 * the ioqueue often enough, the send() completion will not be 173 * reported in timely manner. 174 */ 175 do 176 { 177 c = pj_ioqueue_poll( icedemo.ice_cfg.stun_cfg.ioqueue, &timeout); 178 if (c < 0) 179 { 180 pj_status_t err = pj_get_netos_error(); 181 pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout)); 182 if (p_count) 183 *p_count = count; 184 return err; 185 } 186 else if (c == 0) 187 { 188 break; 189 } 190 else 191 { 192 net_event_count += c; 193 timeout.sec = timeout.msec = 0; 194 } 195 } while (c > 0 && net_event_count < MAX_NET_EVENTS); 196 197 count += net_event_count; 198 if (p_count) 199 *p_count = count; 200 201 return PJ_SUCCESS; 202 203 } 204 205 /* 206 * This is the worker thread that polls event in the background. 207 */ 208 static int icedemo_worker_thread(void *unused) 209 { 210 PJ_UNUSED_ARG(unused); 211 212 while (!icedemo.thread_quit_flag) 213 { 214 handle_events(500, NULL); 215 } 216 217 return 0; 218 } 219 220 /* 221 * This is the callback that is registered to the ICE stream transport to 222 * receive notification about incoming data. By "data" it means application 223 * data such as RTP/RTCP, and not packets that belong to ICE signaling (such 224 * as STUN connectivity checks or TURN signaling). 225 */ 226 static void cb_on_rx_data(pj_ice_strans *ice_st, 227 unsigned comp_id, 228 void *pkt, pj_size_t size, 229 const pj_sockaddr_t *src_addr, 230 unsigned src_addr_len) 231 { 232 char ipstr[PJ_INET6_ADDRSTRLEN+10]; 233 234 PJ_UNUSED_ARG(ice_st); 235 PJ_UNUSED_ARG(src_addr_len); 236 PJ_UNUSED_ARG(pkt); 237 238 // Don't do this! It will ruin the packet buffer in case TCP is used! 239 //((char*)pkt)[size] = ' '; 240 241 PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s: "%.*s"", 242 comp_id, size, 243 pj_sockaddr_print(src_addr, ipstr, sizeof(ipstr), 3), 244 (unsigned)size, 245 (char*)pkt)); 246 } 247 248 /* 249 * This is the callback that is registered to the ICE stream transport to 250 * receive notification about ICE state progression. 251 */ 252 static void cb_on_ice_complete(pj_ice_strans *ice_st, 253 pj_ice_strans_op op, 254 pj_status_t status) 255 { 256 const char *opname = 257 (op==PJ_ICE_STRANS_OP_INIT? "initialization" : 258 (op==PJ_ICE_STRANS_OP_NEGOTIATION ? "negotiation" : "unknown_op")); 259 260 if (status == PJ_SUCCESS) 261 { 262 PJ_LOG(3,(THIS_FILE, "ICE %s successful", opname)); 263 } 264 else 265 { 266 char errmsg[PJ_ERR_MSG_SIZE]; 267 268 pj_strerror(status, errmsg, sizeof(errmsg)); 269 PJ_LOG(1,(THIS_FILE, "ICE %s failed: %s", opname, errmsg)); 270 pj_ice_strans_destroy(ice_st); 271 icedemo.icest = NULL; 272 } 273 } 274 275 /* log callback to write to file */ 276 static void log_func(int level, const char *data, int len) 277 { 278 pj_log_write(level, data, len); 279 if (icedemo.log_fhnd) 280 { 281 if (fwrite(data, len, 1, icedemo.log_fhnd) != 1) 282 return; 283 } 284 } 285 286 /* 287 * This is the main application initialization function. It is called 288 * once (and only once) during application initialization sequence by 289 * main(). 290 */ 291 static pj_status_t icedemo_init(void) 292 { 293 pj_status_t status; 294 295 296 //设置日志文件路径 297 icedemo.opt.log_file = pj_str(".\Demo.log").ptr; 298 pj_log_set_level(6); 299 300 if (icedemo.opt.log_file) 301 { 302 icedemo.log_fhnd = fopen(icedemo.opt.log_file, "a"); 303 pj_log_set_log_func(&log_func); 304 } 305 306 /* Initialize the libraries before anything else */ 307 CHECK( pj_init() ); 308 CHECK( pjlib_util_init() ); 309 CHECK( pjnath_init() ); 310 311 /* Must create pool factory, where memory allocations come from */ 312 pj_caching_pool_init(&icedemo.cp, NULL, 0); 313 314 /* Init our ICE settings with null values */ 315 pj_ice_strans_cfg_default(&icedemo.ice_cfg); 316 317 icedemo.ice_cfg.stun_cfg.pf = &icedemo.cp.factory; 318 319 /* Create application memory pool */ 320 icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", 321 512, 512, NULL); 322 323 /* Create timer heap for timer stuff */ 324 CHECK( pj_timer_heap_create(icedemo.pool, 100, 325 &icedemo.ice_cfg.stun_cfg.timer_heap) ); 326 327 /* and create ioqueue for network I/O stuff */ 328 CHECK( pj_ioqueue_create(icedemo.pool, 16, 329 &icedemo.ice_cfg.stun_cfg.ioqueue) ); 330 331 /* something must poll the timer heap and ioqueue, 332 * unless we're on Symbian where the timer heap and ioqueue run 333 * on themselves. 334 */ 335 CHECK( pj_thread_create(icedemo.pool, "icedemo", &icedemo_worker_thread, 336 NULL, 0, 0, &icedemo.thread) ); 337 338 icedemo.ice_cfg.af = pj_AF_INET(); 339 340 /* Create DNS resolver if nameserver is set */ 341 if (icedemo.opt.ns.slen) 342 { 343 CHECK( pj_dns_resolver_create(&icedemo.cp.factory, 344 "resolver", 345 0, 346 icedemo.ice_cfg.stun_cfg.timer_heap, 347 icedemo.ice_cfg.stun_cfg.ioqueue, 348 &icedemo.ice_cfg.resolver) ); 349 350 CHECK( pj_dns_resolver_set_ns(icedemo.ice_cfg.resolver, 1, 351 &icedemo.opt.ns, NULL) ); 352 } 353 354 /* -= Start initializing ICE stream transport config =- */ 355 356 /* Maximum number of host candidates */ 357 if (icedemo.opt.max_host != -1) 358 icedemo.ice_cfg.stun.max_host_cands = icedemo.opt.max_host; 359 360 /* Nomination strategy */ 361 if (icedemo.opt.regular) 362 icedemo.ice_cfg.opt.aggressive = PJ_FALSE; 363 else 364 icedemo.ice_cfg.opt.aggressive = PJ_TRUE; 365 366 /* 手动设置TURN服务信息 */ 367 icedemo.opt.turn_srv = pj_str("11.11.11.11:8888"); 368 icedemo.opt.stun_srv = icedemo.opt.turn_srv; 369 icedemo.opt.turn_username = pj_str("test"); 370 icedemo.opt.turn_password = pj_str("test"); 371 372 /* Configure STUN/srflx candidate resolution */ 373 if (icedemo.opt.stun_srv.slen) 374 { 375 char *pos; 376 377 /* Command line option may contain port number */ 378 if ((pos=pj_strchr(&icedemo.opt.stun_srv, ':')) != NULL) 379 { 380 icedemo.ice_cfg.stun.server.ptr = icedemo.opt.stun_srv.ptr; 381 icedemo.ice_cfg.stun.server.slen = (pos - icedemo.opt.stun_srv.ptr); 382 383 icedemo.ice_cfg.stun.port = (pj_uint16_t)atoi(pos+1); 384 } 385 else 386 { 387 icedemo.ice_cfg.stun.server = icedemo.opt.stun_srv; 388 icedemo.ice_cfg.stun.port = PJ_STUN_PORT; 389 } 390 391 /* For this demo app, configure longer STUN keep-alive time 392 * so that it does't clutter the screen output. 393 */ 394 icedemo.ice_cfg.stun.cfg.ka_interval = KA_INTERVAL; 395 } 396 397 /* Configure TURN candidate */ 398 if (icedemo.opt.turn_srv.slen) 399 { 400 char *pos; 401 402 /* Command line option may contain port number */ 403 if ((pos=pj_strchr(&icedemo.opt.turn_srv, ':')) != NULL) 404 { 405 icedemo.ice_cfg.turn.server.ptr = icedemo.opt.turn_srv.ptr; 406 icedemo.ice_cfg.turn.server.slen = (pos - icedemo.opt.turn_srv.ptr); 407 408 icedemo.ice_cfg.turn.port = (pj_uint16_t)atoi(pos+1); 409 } 410 else 411 { 412 icedemo.ice_cfg.turn.server = icedemo.opt.turn_srv; 413 icedemo.ice_cfg.turn.port = PJ_STUN_PORT; 414 } 415 416 /* TURN credential */ 417 icedemo.ice_cfg.turn.auth_cred.type = PJ_STUN_AUTH_CRED_STATIC; 418 icedemo.ice_cfg.turn.auth_cred.data.static_cred.username = icedemo.opt.turn_username; 419 icedemo.ice_cfg.turn.auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN; 420 icedemo.ice_cfg.turn.auth_cred.data.static_cred.data = icedemo.opt.turn_password; 421 422 /* Connection type to TURN server */ 423 //使用TCP协议 424 icedemo.opt.turn_tcp = PJ_TRUE; 425 if (icedemo.opt.turn_tcp) 426 icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_TCP; 427 else 428 icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_UDP; 429 430 /* For this demo app, configure longer keep-alive time 431 * so that it does't clutter the screen output. 432 */ 433 icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL; 434 } 435 436 /* -= That's it for now, initialization is complete =- */ 437 return PJ_SUCCESS; 438 } 439 440 441 /* 442 * Create ICE stream transport instance, invoked from the menu. 443 */ 444 static void icedemo_create_instance(void) 445 { 446 pj_ice_strans_cb icecb; 447 pj_status_t status; 448 449 if (icedemo.icest != NULL) 450 { 451 puts("ICE instance already created, destroy it first"); 452 return; 453 } 454 455 /* init the callback */ 456 pj_bzero(&icecb, sizeof(icecb)); 457 icecb.on_rx_data = cb_on_rx_data; 458 icecb.on_ice_complete = cb_on_ice_complete; 459 460 /* create the instance */ 461 status = pj_ice_strans_create("icedemo", /* object name */ 462 &icedemo.ice_cfg, /* settings */ 463 icedemo.opt.comp_cnt, /* comp_cnt */ 464 NULL, /* user data */ 465 &icecb, /* callback */ 466 &icedemo.icest) /* instance ptr */ 467 ; 468 if (status != PJ_SUCCESS) 469 icedemo_perror("error creating ice", status); 470 else 471 PJ_LOG(3,(THIS_FILE, "ICE instance successfully created")); 472 } 473 474 /* Utility to nullify parsed remote info */ 475 static void reset_rem_info(void) 476 { 477 pj_bzero(&icedemo.rem, sizeof(icedemo.rem)); 478 } 479 480 481 /* 482 * Destroy ICE stream transport instance, invoked from the menu. 483 */ 484 static void icedemo_destroy_instance(void) 485 { 486 if (icedemo.icest == NULL) 487 { 488 PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); 489 return; 490 } 491 492 pj_ice_strans_destroy(icedemo.icest); 493 icedemo.icest = NULL; 494 495 reset_rem_info(); 496 497 PJ_LOG(3,(THIS_FILE, "ICE instance destroyed")); 498 } 499 500 501 /* 502 * Create ICE session, invoked from the menu. 503 */ 504 static void icedemo_init_session(unsigned rolechar) 505 { 506 pj_ice_sess_role role = (pj_tolower((pj_uint8_t)rolechar)=='o' ? 507 PJ_ICE_SESS_ROLE_CONTROLLING : 508 PJ_ICE_SESS_ROLE_CONTROLLED); 509 pj_status_t status; 510 511 if (icedemo.icest == NULL) 512 { 513 PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); 514 return; 515 } 516 517 if (pj_ice_strans_has_sess(icedemo.icest)) 518 { 519 PJ_LOG(1,(THIS_FILE, "Error: Session already created")); 520 return; 521 } 522 523 status = pj_ice_strans_init_ice(icedemo.icest, role, NULL, NULL); 524 if (status != PJ_SUCCESS) 525 icedemo_perror("error creating session", status); 526 else 527 PJ_LOG(3,(THIS_FILE, "ICE session created")); 528 529 reset_rem_info(); 530 } 531 532 533 /* 534 * Stop/destroy ICE session, invoked from the menu. 535 */ 536 static void icedemo_stop_session(void) 537 { 538 pj_status_t status; 539 540 if (icedemo.icest == NULL) 541 { 542 PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); 543 return; 544 } 545 546 if (!pj_ice_strans_has_sess(icedemo.icest)) 547 { 548 PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first")); 549 return; 550 } 551 552 status = pj_ice_strans_stop_ice(icedemo.icest); 553 if (status != PJ_SUCCESS) 554 icedemo_perror("error stopping session", status); 555 else 556 PJ_LOG(3,(THIS_FILE, "ICE session stopped")); 557 558 reset_rem_info(); 559 } 560 561 #define PRINT(...) 562 printed = pj_ansi_snprintf(p, maxlen - (p-buffer), 563 __VA_ARGS__); 564 if (printed <= 0 || printed >= (int)(maxlen - (p-buffer))) 565 return -PJ_ETOOSMALL; 566 p += printed 567 568 569 /* Utility to create a=candidate SDP attribute */ 570 static int print_cand(char buffer[], unsigned maxlen, 571 const pj_ice_sess_cand *cand) 572 { 573 char ipaddr[PJ_INET6_ADDRSTRLEN]; 574 char *p = buffer; 575 int printed; 576 577 PRINT("a=candidate:%.*s %u UDP %u %s %u typ ", 578 (int)cand->foundation.slen, 579 cand->foundation.ptr, 580 (unsigned)cand->comp_id, 581 cand->prio, 582 pj_sockaddr_print(&cand->addr, ipaddr, 583 sizeof(ipaddr), 0), 584 (unsigned)pj_sockaddr_get_port(&cand->addr)); 585 586 PRINT("%s ", 587 pj_ice_get_cand_type_name(cand->type)); 588 589 if (p == buffer+maxlen) 590 return -PJ_ETOOSMALL; 591 592 *p = '