使用 pjsip 代码独立开发

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 = '';
 593 
 594     return (int)(p-buffer);
 595 }
 596 
 597 /* 
 598 * Encode ICE information in SDP.
 599 */
 600 static int encode_session(char buffer[], unsigned maxlen)
 601 {
 602     char *p = buffer;
 603     unsigned comp;
 604     int printed;
 605     pj_str_t local_ufrag, local_pwd;
 606     pj_status_t status;
 607 
 608     /* Write "dummy" SDP v=, o=, s=, and t= lines */
 609     PRINT("v=0
o=- 3414953978 3414953978 IN IP4 localhost
s=ice
t=0 0
");
 610 
 611     /* Get ufrag and pwd from current session */
 612     pj_ice_strans_get_ufrag_pwd(icedemo.icest, &local_ufrag, &local_pwd,
 613         NULL, NULL);
 614 
 615     /* Write the a=ice-ufrag and a=ice-pwd attributes */
 616     PRINT("a=ice-ufrag:%.*s
a=ice-pwd:%.*s
",
 617         (int)local_ufrag.slen,
 618         local_ufrag.ptr,
 619         (int)local_pwd.slen,
 620         local_pwd.ptr);
 621 
 622     /* Write each component */
 623     for (comp=0; comp<icedemo.opt.comp_cnt; ++comp) 
 624     {
 625         unsigned j, cand_cnt;
 626         pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND];
 627         char ipaddr[PJ_INET6_ADDRSTRLEN];
 628 
 629         /* Get default candidate for the component */
 630         status = pj_ice_strans_get_def_cand(icedemo.icest, comp+1, &cand[0]);
 631         if (status != PJ_SUCCESS)
 632             return -status;
 633 
 634         /* Write the default address */
 635         if (comp==0) 
 636         {
 637             /* For component 1, default address is in m= and c= lines */
 638             PRINT("m=audio %d RTP/AVP 0
"
 639                 "c=IN IP4 %s
",
 640                 (int)pj_sockaddr_get_port(&cand[0].addr),
 641                 pj_sockaddr_print(&cand[0].addr, ipaddr,
 642                 sizeof(ipaddr), 0));
 643         }
 644         else if (comp==1) 
 645         {
 646             /* For component 2, default address is in a=rtcp line */
 647             PRINT("a=rtcp:%d IN IP4 %s
",
 648                 (int)pj_sockaddr_get_port(&cand[0].addr),
 649                 pj_sockaddr_print(&cand[0].addr, ipaddr,
 650                 sizeof(ipaddr), 0));
 651         }
 652         else 
 653         {
 654             /* For other components, we'll just invent this.. */
 655             PRINT("a=Xice-defcand:%d IN IP4 %s
",
 656                 (int)pj_sockaddr_get_port(&cand[0].addr),
 657                 pj_sockaddr_print(&cand[0].addr, ipaddr,
 658                 sizeof(ipaddr), 0));
 659         }
 660 
 661         /* Enumerate all candidates for this component */
 662         cand_cnt = PJ_ARRAY_SIZE(cand);
 663         status = pj_ice_strans_enum_cands(icedemo.icest, comp+1,
 664             &cand_cnt, cand);
 665         if (status != PJ_SUCCESS)
 666             return -status;
 667 
 668         /* And encode the candidates as SDP */
 669         for (j=0; j<cand_cnt; ++j) 
 670         {
 671             printed = print_cand(p, maxlen - (unsigned)(p-buffer), &cand[j]);
 672             if (printed < 0)
 673                 return -PJ_ETOOSMALL;
 674             p += printed;
 675         }
 676     }
 677 
 678     if (p == buffer+maxlen)
 679         return -PJ_ETOOSMALL;
 680 
 681     *p = '';
 682     return (int)(p - buffer);
 683 }
 684 
 685 
 686 /*
 687 * Show information contained in the ICE stream transport. This is
 688 * invoked from the menu.
 689 */
 690 static void icedemo_show_ice(void)
 691 {
 692     static char buffer[1000];
 693     int len;
 694 
 695     if (icedemo.icest == NULL) 
 696     {
 697         PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
 698         return;
 699     }
 700 
 701     puts("General info");
 702     puts("---------------");
 703     printf("Component count    : %d
", icedemo.opt.comp_cnt);
 704     printf("Status             : ");
 705     if (pj_ice_strans_sess_is_complete(icedemo.icest))
 706         puts("negotiation complete");
 707     else if (pj_ice_strans_sess_is_running(icedemo.icest))
 708         puts("negotiation is in progress");
 709     else if (pj_ice_strans_has_sess(icedemo.icest))
 710         puts("session ready");
 711     else
 712         puts("session not created");
 713 
 714     if (!pj_ice_strans_has_sess(icedemo.icest)) 
 715     {
 716         puts("Create the session first to see more info");
 717         return;
 718     }
 719 
 720     printf("Negotiated comp_cnt: %d
", 
 721         pj_ice_strans_get_running_comp_cnt(icedemo.icest));
 722     printf("Role               : %s
",
 723         pj_ice_strans_get_role(icedemo.icest)==PJ_ICE_SESS_ROLE_CONTROLLED ?
 724         "controlled" : "controlling");
 725 
 726     len = encode_session(buffer, sizeof(buffer));
 727     if (len < 0)
 728         err_exit("not enough buffer to show ICE status", -len);
 729 
 730     puts("");
 731     printf("Local SDP (paste this to remote host):
"
 732         "--------------------------------------
"
 733         "%s
", buffer);
 734 
 735 
 736     puts("");
 737     puts("Remote info:
"
 738         "----------------------");
 739     if (icedemo.rem.cand_cnt==0) 
 740     {
 741         puts("No remote info yet");
 742     }
 743     else 
 744     {
 745         unsigned i;
 746 
 747         printf("Remote ufrag       : %s
", icedemo.rem.ufrag);
 748         printf("Remote password    : %s
", icedemo.rem.pwd);
 749         printf("Remote cand. cnt.  : %d
", icedemo.rem.cand_cnt);
 750 
 751         for (i=0; i<icedemo.rem.cand_cnt; ++i) 
 752         {
 753             len = print_cand(buffer, sizeof(buffer), &icedemo.rem.cand[i]);
 754             if (len < 0)
 755                 err_exit("not enough buffer to show ICE status", -len);
 756 
 757             printf("  %s", buffer);
 758         }
 759     }
 760 }
 761 
 762 
 763 /*
 764 * Input and parse SDP from the remote (containing remote's ICE information) 
 765 * and save it to global variables.
 766 */
 767 static void icedemo_input_remote(void)
 768 {
 769     char linebuf[80];
 770     unsigned media_cnt = 0;
 771     unsigned comp0_port = 0;
 772     char     comp0_addr[80];
 773     pj_bool_t done = PJ_FALSE;
 774 
 775     puts("Paste SDP from remote host, end with empty line");
 776 
 777     reset_rem_info();
 778 
 779     comp0_addr[0] = '';
 780 
 781     while (!done) 
 782     {
 783         pj_size_t len;
 784         char *line;
 785 
 786         printf(">");
 787         if (stdout) fflush(stdout);
 788 
 789         if (fgets(linebuf, sizeof(linebuf), stdin)==NULL)
 790             break;
 791 
 792         len = strlen(linebuf);
 793         while (len && (linebuf[len-1] == '
' || linebuf[len-1] == '
'))
 794             linebuf[--len] = '';
 795 
 796         line = linebuf;
 797         while (len && pj_isspace(*line))
 798             ++line, --len;
 799 
 800         if (len==0)
 801             break;
 802 
 803         /* Ignore subsequent media descriptors */
 804         if (media_cnt > 1)
 805             continue;
 806 
 807         switch (line[0]) 
 808         {
 809         case 'm':
 810 
 811             {
 812                 int cnt;
 813                 char media[32], portstr[32];
 814 
 815                 ++media_cnt;
 816                 if (media_cnt > 1) 
 817                 {
 818                     puts("Media line ignored");
 819                     break;
 820                 }
 821 
 822                 cnt = sscanf(line+2, "%s %s RTP/", media, portstr);
 823                 if (cnt != 2) 
 824                 {
 825                     PJ_LOG(1,(THIS_FILE, "Error parsing media line"));
 826                     goto on_error;
 827                 }
 828 
 829                 comp0_port = atoi(portstr);
 830 
 831             }
 832             break;
 833         case 'c':
 834 
 835             {
 836                 int cnt;
 837                 char c[32], net[32], ip[80];
 838 
 839                 cnt = sscanf(line+2, "%s %s %s", c, net, ip);
 840                 if (cnt != 3) 
 841                 {
 842                     PJ_LOG(1,(THIS_FILE, "Error parsing connection line"));
 843                     goto on_error;
 844                 }
 845 
 846                 strcpy(comp0_addr, ip);
 847             }
 848             break;
 849         case 'a':
 850 
 851             {
 852                 char *attr = strtok(line+2, ": 	
");
 853                 if (strcmp(attr, "ice-ufrag")==0) 
 854                 {
 855                     strcpy(icedemo.rem.ufrag, attr+strlen(attr)+1);
 856                 }
 857                 else if (strcmp(attr, "ice-pwd")==0) 
 858                 {
 859                     strcpy(icedemo.rem.pwd, attr+strlen(attr)+1);
 860                 }
 861                 else if (strcmp(attr, "rtcp")==0) 
 862                 {
 863                     char *val = attr+strlen(attr)+1;
 864                     int af, cnt;
 865                     int port;
 866                     char net[32], ip[64];
 867                     pj_str_t tmp_addr;
 868                     pj_status_t status;
 869 
 870                     cnt = sscanf(val, "%d IN %s %s", &port, net, ip);
 871                     if (cnt != 3) 
 872                     {
 873                         PJ_LOG(1,(THIS_FILE, "Error parsing rtcp attribute"));
 874                         goto on_error;
 875                     }
 876 
 877                     if (strchr(ip, ':'))
 878                         af = pj_AF_INET6();
 879                     else
 880                         af = pj_AF_INET();
 881 
 882                     pj_sockaddr_init(af, &icedemo.rem.def_addr[1], NULL, 0);
 883                     tmp_addr = pj_str(ip);
 884                     status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[1],
 885                         &tmp_addr);
 886                     if (status != PJ_SUCCESS) 
 887                     {
 888                         PJ_LOG(1,(THIS_FILE, "Invalid IP address"));
 889                         goto on_error;
 890                     }
 891                     pj_sockaddr_set_port(&icedemo.rem.def_addr[1], (pj_uint16_t)port);
 892 
 893                 }
 894                 else if (strcmp(attr, "candidate")==0) 
 895                 {
 896                     char *sdpcand = attr+strlen(attr)+1;
 897                     int af, cnt;
 898                     char foundation[32], transport[12], ipaddr[80], type[32];
 899                     pj_str_t tmpaddr;
 900                     int comp_id, prio, port;
 901                     pj_ice_sess_cand *cand;
 902                     pj_status_t status;
 903 
 904                     cnt = sscanf(sdpcand, "%s %d %s %d %s %d typ %s",
 905                         foundation,
 906                         &comp_id,
 907                         transport,
 908                         &prio,
 909                         ipaddr,
 910                         &port,
 911                         type);
 912                     if (cnt != 7) 
 913                     {
 914                         PJ_LOG(1, (THIS_FILE, "error: Invalid ICE candidate line"));
 915                         goto on_error;
 916                     }
 917 
 918                     cand = &icedemo.rem.cand[icedemo.rem.cand_cnt];
 919                     pj_bzero(cand, sizeof(*cand));
 920 
 921                     if (strcmp(type, "host")==0)
 922                         cand->type = PJ_ICE_CAND_TYPE_HOST;
 923                     else if (strcmp(type, "srflx")==0)
 924                         cand->type = PJ_ICE_CAND_TYPE_SRFLX;
 925                     else if (strcmp(type, "relay")==0)
 926                         cand->type = PJ_ICE_CAND_TYPE_RELAYED;
 927                     else 
 928                     {
 929                         PJ_LOG(1, (THIS_FILE, "Error: invalid candidate type '%s'", 
 930                             type));
 931                         goto on_error;
 932                     }
 933 
 934                     cand->comp_id = (pj_uint8_t)comp_id;
 935                     pj_strdup2(icedemo.pool, &cand->foundation, foundation);
 936                     cand->prio = prio;
 937 
 938                     if (strchr(ipaddr, ':'))
 939                         af = pj_AF_INET6();
 940                     else
 941                         af = pj_AF_INET();
 942 
 943                     tmpaddr = pj_str(ipaddr);
 944                     pj_sockaddr_init(af, &cand->addr, NULL, 0);
 945                     status = pj_sockaddr_set_str_addr(af, &cand->addr, &tmpaddr);
 946                     if (status != PJ_SUCCESS) 
 947                     {
 948                         PJ_LOG(1,(THIS_FILE, "Error: invalid IP address '%s'",
 949                             ipaddr));
 950                         goto on_error;
 951                     }
 952 
 953                     pj_sockaddr_set_port(&cand->addr, (pj_uint16_t)port);
 954 
 955                     ++icedemo.rem.cand_cnt;
 956 
 957                     if (cand->comp_id > icedemo.rem.comp_cnt)
 958                         icedemo.rem.comp_cnt = cand->comp_id;
 959                 }
 960             }
 961             break;
 962         }
 963     }
 964 
 965     if (icedemo.rem.cand_cnt==0 ||
 966         icedemo.rem.ufrag[0]==0 ||
 967         icedemo.rem.pwd[0]==0 ||
 968         icedemo.rem.comp_cnt == 0)
 969     {
 970         PJ_LOG(1, (THIS_FILE, "Error: not enough info"));
 971         goto on_error;
 972     }
 973 
 974     if (comp0_port==0 || comp0_addr[0]=='') 
 975     {
 976         PJ_LOG(1, (THIS_FILE, "Error: default address for component 0 not found"));
 977         goto on_error;
 978     }
 979     else 
 980     {
 981         int af;
 982         pj_str_t tmp_addr;
 983         pj_status_t status;
 984 
 985         if (strchr(comp0_addr, ':'))
 986             af = pj_AF_INET6();
 987         else
 988             af = pj_AF_INET();
 989 
 990         pj_sockaddr_init(af, &icedemo.rem.def_addr[0], NULL, 0);
 991         tmp_addr = pj_str(comp0_addr);
 992         status = pj_sockaddr_set_str_addr(af, &icedemo.rem.def_addr[0],
 993             &tmp_addr);
 994         if (status != PJ_SUCCESS) 
 995         {
 996             PJ_LOG(1,(THIS_FILE, "Invalid IP address in c= line"));
 997             goto on_error;
 998         }
 999         pj_sockaddr_set_port(&icedemo.rem.def_addr[0], (pj_uint16_t)comp0_port);
1000     }
1001 
1002     PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added", 
1003         icedemo.rem.cand_cnt));
1004     return;
1005 
1006 on_error:
1007     reset_rem_info();
1008 }
1009 
1010 
1011 /*
1012 * Start ICE negotiation! This function is invoked from the menu.
1013 */
1014 static void icedemo_start_nego(void)
1015 {
1016     pj_str_t rufrag, rpwd;
1017     pj_status_t status;
1018 
1019     if (icedemo.icest == NULL) 
1020     {
1021         PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
1022         return;
1023     }
1024 
1025     if (!pj_ice_strans_has_sess(icedemo.icest)) 
1026     {
1027         PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first"));
1028         return;
1029     }
1030 
1031     if (icedemo.rem.cand_cnt == 0) 
1032     {
1033         PJ_LOG(1,(THIS_FILE, "Error: No remote info, input remote info first"));
1034         return;
1035     }
1036 
1037     PJ_LOG(3,(THIS_FILE, "Starting ICE negotiation.."));
1038 
1039     status = pj_ice_strans_start_ice(icedemo.icest, 
1040         pj_cstr(&rufrag, icedemo.rem.ufrag),
1041         pj_cstr(&rpwd, icedemo.rem.pwd),
1042         icedemo.rem.cand_cnt,
1043         icedemo.rem.cand);
1044     if (status != PJ_SUCCESS)
1045         icedemo_perror("Error starting ICE", status);
1046     else
1047         PJ_LOG(3,(THIS_FILE, "ICE negotiation started"));
1048 }
1049 
1050 
1051 /*
1052 * Send application data to remote agent.
1053 */
1054 static void icedemo_send_data(unsigned comp_id, const char *data)
1055 {
1056     pj_status_t status;
1057 
1058     if (icedemo.icest == NULL) 
1059     {
1060         PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first"));
1061         return;
1062     }
1063 
1064     if (!pj_ice_strans_has_sess(icedemo.icest)) 
1065     {
1066         PJ_LOG(1,(THIS_FILE, "Error: No ICE session, initialize first"));
1067         return;
1068     }
1069 
1070     /*
1071     if (!pj_ice_strans_sess_is_complete(icedemo.icest)) 
1072     {
1073     PJ_LOG(1,(THIS_FILE, "Error: ICE negotiation has not been started or is in progress"));
1074     return;
1075     }
1076     */
1077 
1078     if (comp_id<1||comp_id>pj_ice_strans_get_running_comp_cnt(icedemo.icest)) 
1079     {
1080         PJ_LOG(1,(THIS_FILE, "Error: invalid component ID"));
1081         return;
1082     }
1083 
1084     status = pj_ice_strans_sendto(icedemo.icest, comp_id, data, strlen(data),
1085         &icedemo.rem.def_addr[comp_id-1],
1086         pj_sockaddr_get_len(&icedemo.rem.def_addr[comp_id-1]));
1087     if (status != PJ_SUCCESS)
1088         icedemo_perror("Error sending data", status);
1089     else
1090         PJ_LOG(3,(THIS_FILE, "Data sent"));
1091 }
1092 
1093 
1094 /*
1095 * Display help for the menu.
1096 */
1097 static void icedemo_help_menu(void)
1098 {
1099     puts("");
1100     puts("-= Help on using ICE and this icedemo program =-");
1101     puts("");
1102     puts("This application demonstrates how to use ICE in pjnath without having
"
1103         "to use the SIP protocol. To use this application, you will need to run
"
1104         "two instances of this application, to simulate two ICE agents.
");
1105 
1106     puts("Basic ICE flow:
"
1107         " create instance [menu "c"]
"
1108         " repeat these steps as wanted:
"
1109         "   - init session as offerer or answerer [menu "i"]
"
1110         "   - display our SDP [menu "s"]
"
1111         "   - "send" our SDP from the "show" output above to remote, by
"
1112         "     copy-pasting the SDP to the other icedemo application
"
1113         "   - parse remote SDP, by pasting SDP generated by the other icedemo
"
1114         "     instance [menu "r"]
"
1115         "   - begin ICE negotiation in our end [menu "b"], and 
"
1116         "   - immediately begin ICE negotiation in the other icedemo instance
"
1117         "   - ICE negotiation will run, and result will be printed to screen
"
1118         "   - send application data to remote [menu "x"]
"
1119         "   - end/stop ICE session [menu "e"]
"
1120         " destroy instance [menu "d"]
"
1121         "");
1122 
1123     puts("");
1124     puts("This concludes the help screen.");
1125     puts("");
1126 }
1127 
1128 
1129 /*
1130 * Display console menu
1131 */
1132 static void icedemo_print_menu(void)
1133 {
1134     puts("");
1135     puts("+----------------------------------------------------------------------+");
1136     puts("|                    M E N U                                           |");
1137     puts("+---+------------------------------------------------------------------+");
1138     puts("| c | create           Create the instance                             |");
1139     puts("| d | destroy          Destroy the instance                            |");
1140     puts("| i | init o|a         Initialize ICE session as offerer or answerer   |");
1141     puts("| e | stop             End/stop ICE session                            |");
1142     puts("| s | show             Display local ICE info                          |");
1143     puts("| r | remote           Input remote ICE info                           |");
1144     puts("| b | start            Begin ICE negotiation                           |");
1145     puts("| x | send <compid> .. Send data to remote                             |");
1146     puts("+---+------------------------------------------------------------------+");
1147     puts("| h |  help            * Help! *                                       |");
1148     puts("| q |  quit            Quit                                            |");
1149     puts("+----------------------------------------------------------------------+");
1150 }
1151 
1152 
1153 /*
1154 * Main console loop.
1155 */
1156 static void icedemo_console(void)
1157 {
1158     pj_bool_t app_quit = PJ_FALSE;
1159 
1160     while (!app_quit)
1161     {
1162         char input[80], *cmd;
1163         const char *SEP = " 	
";
1164         pj_size_t len;
1165 
1166         icedemo_print_menu();
1167 
1168         printf("Input: ");
1169         if (stdout) fflush(stdout);
1170 
1171         pj_bzero(input, sizeof(input));
1172         if (fgets(input, sizeof(input), stdin) == NULL)
1173             break;
1174 
1175         len = strlen(input);
1176         while (len && (input[len-1]=='
' || input[len-1]=='
'))
1177             input[--len] = '';
1178 
1179         cmd = strtok(input, SEP);
1180         if (!cmd)
1181             continue;
1182 
1183         if (strcmp(cmd, "create")==0 || strcmp(cmd, "c")==0) 
1184         {
1185             icedemo_create_instance();
1186         } 
1187         else if (strcmp(cmd, "destroy")==0 || strcmp(cmd, "d")==0) 
1188         {
1189             icedemo_destroy_instance();
1190         } 
1191         else if (strcmp(cmd, "init")==0 || strcmp(cmd, "i")==0) 
1192         {
1193             char *role = strtok(NULL, SEP);
1194             if (role)
1195                 icedemo_init_session(*role);
1196             else
1197                 puts("error: Role required");
1198         } 
1199         else if (strcmp(cmd, "stop")==0 || strcmp(cmd, "e")==0)
1200         {
1201             icedemo_stop_session();
1202         } 
1203         else if (strcmp(cmd, "show")==0 || strcmp(cmd, "s")==0)
1204         {
1205             icedemo_show_ice();
1206         } 
1207         else if (strcmp(cmd, "remote")==0 || strcmp(cmd, "r")==0)
1208         {
1209             icedemo_input_remote();
1210         } 
1211         else if (strcmp(cmd, "start")==0 || strcmp(cmd, "b")==0) 
1212         {
1213             icedemo_start_nego();
1214         } 
1215         else if (strcmp(cmd, "send")==0 || strcmp(cmd, "x")==0) 
1216         {
1217             char *comp = strtok(NULL, SEP);
1218 
1219             if (!comp) 
1220             {
1221                 PJ_LOG(1,(THIS_FILE, "Error: component ID required"));
1222             }
1223             else 
1224             {
1225                 char *data = comp + strlen(comp) + 1;
1226                 if (!data)
1227                     data = "";
1228                 icedemo_send_data(atoi(comp), data);
1229             }
1230 
1231         }
1232         else if (strcmp(cmd, "help")==0 || strcmp(cmd, "h")==0) 
1233         {
1234             icedemo_help_menu();
1235         }
1236         else if (strcmp(cmd, "quit")==0 || strcmp(cmd, "q")==0) 
1237         {
1238             app_quit = PJ_TRUE;
1239         }
1240         else 
1241         {
1242             printf("Invalid command '%s'
", cmd);
1243 
1244         }
1245     }
1246 }
1247 
1248 
1249 /*
1250 * Display program usage.
1251 */
1252 static void icedemo_usage()
1253 {
1254     puts("Usage: icedemo [optons]");
1255     printf("icedemo v%s by pjsip.org
", pj_get_version());
1256     puts("");
1257     puts("General options:");
1258     puts(" --comp-cnt, -c N          Component count (default=1)");
1259     puts(" --nameserver, -n IP       Configure nameserver to activate DNS SRV");
1260     puts("                           resolution");
1261     puts(" --max-host, -H N          Set max number of host candidates to N");
1262     puts(" --regular, -R             Use regular nomination (default aggressive)");
1263     puts(" --log-file, -L FILE       Save output to log FILE");
1264     puts(" --help, -h                Display this screen.");
1265     puts("");
1266     puts("STUN related options:");
1267     puts(" --stun-srv, -s HOSTDOM    Enable srflx candidate by resolving to STUN server.");
1268     puts("                           HOSTDOM may be a "host_or_ip[:port]" or a domain");
1269     puts("                           name if DNS SRV resolution is used.");
1270     puts("");
1271     puts("TURN related options:");
1272     puts(" --turn-srv, -t HOSTDOM    Enable relayed candidate by using this TURN server.");
1273     puts("                           HOSTDOM may be a "host_or_ip[:port]" or a domain");
1274     puts("                           name if DNS SRV resolution is used.");
1275     puts(" --turn-tcp, -T            Use TCP to connect to TURN server");
1276     puts(" --turn-username, -u UID   Set TURN username of the credential to UID");
1277     puts(" --turn-password, -p PWD   Set password of the credential to WPWD");
1278     puts(" --turn-fingerprint, -F    Use fingerprint for outgoing TURN requests");
1279     puts("");
1280 }
1281 
1282 
1283 /*
1284 * And here's the main()
1285 */
1286 int main(int argc, char *argv[])
1287 {
1288 
1289     struct pj_getopt_option long_options[] = 
1290     {
1291         { "comp-cnt",           1, 0, 'c'},
1292         { "nameserver",        1, 0, 'n'},
1293         { "max-host",        1, 0, 'H'},
1294         { "help",        0, 0, 'h'},
1295         { "stun-srv",        1, 0, 's'},
1296         { "turn-srv",        1, 0, 't'},
1297         { "turn-tcp",        0, 0, 'T'},
1298         { "turn-username",    1, 0, 'u'},
1299         { "turn-password",    1, 0, 'p'},
1300         { "turn-fingerprint",    0, 0, 'F'},
1301         { "regular",        0, 0, 'R'},
1302         { "log-file",        1, 0, 'L'},
1303     };
1304     int c, opt_id;
1305     pj_status_t status;
1306 
1307     icedemo.opt.comp_cnt = 1;
1308     icedemo.opt.max_host = -1;
1309 
1310     while((c=pj_getopt_long(argc,argv, "c:n:s:t:u:p:H:L:hTFR", long_options, &opt_id))!=-1) 
1311     {
1312         switch (c) 
1313         {
1314         case 'c':
1315             icedemo.opt.comp_cnt = atoi(pj_optarg);
1316             if (icedemo.opt.comp_cnt < 1 || icedemo.opt.comp_cnt >= PJ_ICE_MAX_COMP) 
1317             {
1318                 puts("Invalid component count value");
1319                 return 1;
1320             }
1321             break;
1322         case 'n':
1323             icedemo.opt.ns = pj_str(pj_optarg);
1324             break;
1325         case 'H':
1326             icedemo.opt.max_host = atoi(pj_optarg);
1327             break;
1328         case 'h':
1329             icedemo_usage();
1330             return 0;
1331         case 's':
1332             icedemo.opt.stun_srv = pj_str(pj_optarg);
1333             break;
1334         case 't':
1335             icedemo.opt.turn_srv = pj_str(pj_optarg);
1336             break;
1337         case 'T':
1338             icedemo.opt.turn_tcp = PJ_TRUE;
1339             break;
1340         case 'u':
1341             icedemo.opt.turn_username = pj_str(pj_optarg);
1342             break;
1343         case 'p':
1344             icedemo.opt.turn_password = pj_str(pj_optarg);
1345             break;
1346         case 'F':
1347             icedemo.opt.turn_fingerprint = PJ_TRUE;
1348             break;
1349         case 'R':
1350             icedemo.opt.regular = PJ_TRUE;
1351             break;
1352         case 'L':
1353             icedemo.opt.log_file = pj_optarg;
1354             break;
1355         default:
1356             printf("Argument "%s" is not valid. Use -h to see help",
1357                 argv[pj_optind]);
1358             return 1;
1359         }
1360     }
1361 
1362     status = icedemo_init();
1363     if (status != PJ_SUCCESS)
1364         return 1;
1365 
1366     icedemo_console();
1367 
1368     err_exit("Quitting..", PJ_SUCCESS);
1369     return 0;
1370 }
View Code

当然,其中的一些设置也可以写在代码文件中,该文不予介绍。

另外,也可以使用设置“继承的项目属性表”的方式进行设置,很多输出路径、编译路径、输出文件名称等繁琐的设置可以一次搞定,比如可以引入

..pjproject-2.6uildvspjproject-vs8-debug-static-defaults.vsprops
..pjproject-2.6uildvspjproject-vs8-win32-common-defaults.vsprops

在debug模式下就可以按照属性表的设置编译出对应的内容,对于要开发很多项目的情况,可以使用该方法,减少繁琐的设置。若项目工程不多,大可不必使用导入继承的项目属性表的方法,直接设置来的反而开发更快。

5、如果独立的DEMO程序位于pjproject-2.6文件夹内,则只需要修改相对路径即可。

以 “pjproject-2.6DemoDemo.vcproj”为例,其内部有一个源文件“pjproject-2.6Demomain.cpp”

通过编译调试的工程文件Demo.vcproj:

<?xml version="1.0" encoding="gb2312"?>
<VisualStudioProject
    ProjectType="Visual C++"
    Version="9.00"
    Name="Demo"
    ProjectGUID="{1C1689E1-8C19-4197-9751-D8373A753CCA}"
    RootNamespace="Demo"
    Keyword="Win32Proj"
    TargetFrameworkVersion="196613"
    >
    <Platforms>
        <Platform
            Name="Win32"
        />
    </Platforms>
    <ToolFiles>
    </ToolFiles>
    <Configurations>
        <Configuration
            Name="Debug|Win32"
            OutputDirectory="$(SolutionDir)$(ConfigurationName)"
            IntermediateDirectory="$(ConfigurationName)"
            ConfigurationType="1"
            CharacterSet="1"
            >
            <Tool
                Name="VCPreBuildEventTool"
            />
            <Tool
                Name="VCCustomBuildTool"
            />
            <Tool
                Name="VCXMLDataGeneratorTool"
            />
            <Tool
                Name="VCWebServiceProxyGeneratorTool"
            />
            <Tool
                Name="VCMIDLTool"
            />
            <Tool
                Name="VCCLCompilerTool"
                Optimization="0"
                AdditionalIncludeDirectories="../pjlib/include/;&quot;../pjlib-util/include/&quot;;../pjnath/include/"
                PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
                MinimalRebuild="true"
                BasicRuntimeChecks="3"
                RuntimeLibrary="3"
                UsePrecompiledHeader="0"
                WarningLevel="3"
                DebugInformationFormat="4"
            />
            <Tool
                Name="VCManagedResourceCompilerTool"
            />
            <Tool
                Name="VCResourceCompilerTool"
            />
            <Tool
                Name="VCPreLinkEventTool"
            />
            <Tool
                Name="VCLinkerTool"
                AdditionalDependencies="ws2_32.lib pjlib-i386-Win32-vc8-Debug.lib pjlib-util-i386-Win32-vc8-Debug.lib pjnath-i386-Win32-vc8-Debug.lib"
                LinkIncremental="2"
                AdditionalLibraryDirectories="../pjlib/lib/;&quot;../pjlib-util/lib/&quot;;../pjnath/lib/"
                GenerateDebugInformation="true"
                SubSystem="1"
                TargetMachine="1"
            />
            <Tool
                Name="VCALinkTool"
            />
            <Tool
                Name="VCManifestTool"
            />
            <Tool
                Name="VCXDCMakeTool"
            />
            <Tool
                Name="VCBscMakeTool"
            />
            <Tool
                Name="VCFxCopTool"
            />
            <Tool
                Name="VCAppVerifierTool"
            />
            <Tool
                Name="VCPostBuildEventTool"
            />
        </Configuration>
        <Configuration
            Name="Release|Win32"
            OutputDirectory="$(SolutionDir)$(ConfigurationName)"
            IntermediateDirectory="$(ConfigurationName)"
            ConfigurationType="1"
            CharacterSet="1"
            WholeProgramOptimization="1"
            >
            <Tool
                Name="VCPreBuildEventTool"
            />
            <Tool
                Name="VCCustomBuildTool"
            />
            <Tool
                Name="VCXMLDataGeneratorTool"
            />
            <Tool
                Name="VCWebServiceProxyGeneratorTool"
            />
            <Tool
                Name="VCMIDLTool"
            />
            <Tool
                Name="VCCLCompilerTool"
                Optimization="2"
                EnableIntrinsicFunctions="true"
                AdditionalIncludeDirectories="../pjlib/include/;&quot;../pjlib-util/include/&quot;;../pjnath/include/"
                PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
                RuntimeLibrary="2"
                EnableFunctionLevelLinking="true"
                UsePrecompiledHeader="0"
                WarningLevel="3"
                DebugInformationFormat="3"
            />
            <Tool
                Name="VCManagedResourceCompilerTool"
            />
            <Tool
                Name="VCResourceCompilerTool"
            />
            <Tool
                Name="VCPreLinkEventTool"
            />
            <Tool
                Name="VCLinkerTool"
                AdditionalDependencies="ws2_32.lib pjlib-i386-Win32-vc8-Release.lib pjlib-util-i386-Win32-vc8-Release.lib pjnath-i386-Win32-vc8-Release.lib"
                LinkIncremental="1"
                AdditionalLibraryDirectories="../pjlib/lib/;&quot;../pjlib-util/lib/&quot;;../pjnath/lib/"
                GenerateDebugInformation="true"
                SubSystem="1"
                OptimizeReferences="2"
                EnableCOMDATFolding="2"
                TargetMachine="1"
            />
            <Tool
                Name="VCALinkTool"
            />
            <Tool
                Name="VCManifestTool"
            />
            <Tool
                Name="VCXDCMakeTool"
            />
            <Tool
                Name="VCBscMakeTool"
            />
            <Tool
                Name="VCFxCopTool"
            />
            <Tool
                Name="VCAppVerifierTool"
            />
            <Tool
                Name="VCPostBuildEventTool"
            />
        </Configuration>
    </Configurations>
    <References>
    </References>
    <Files>
        <Filter
            Name="源文件"
            Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
            UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
            >
            <File
                RelativePath=".main.cpp"
                >
            </File>
        </Filter>
        <Filter
            Name="头文件"
            Filter="h;hpp;hxx;hm;inl;inc;xsd"
            UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
            >
        </Filter>
        <Filter
            Name="资源文件"
            Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
            UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
            >
        </Filter>
    </Files>
    <Globals>
    </Globals>
</VisualStudioProject>
View Code
原文地址:https://www.cnblogs.com/mobilecard/p/6605322.html