首页 > 代码库 > 使用 pjsip 代码独立开发

使用 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] = ‘\0‘;
 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 =http://www.mamicode.com/ 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 =http://www.mamicode.com/ 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\n",
 587         pj_ice_get_cand_type_name(cand->type));
 588 
 589     if (p == buffer+maxlen)
 590         return -PJ_ETOOSMALL;
 591 
 592     *p = \0;
 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\no=- 3414953978 3414953978 IN IP4 localhost\ns=ice\nt=0 0\n");
 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\na=ice-pwd:%.*s\n",
 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\n"
 639                 "c=IN IP4 %s\n",
 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\n",
 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\n",
 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 = \0;
 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\n", 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\n", 
 721         pj_ice_strans_get_running_comp_cnt(icedemo.icest));
 722     printf("Role               : %s\n",
 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):\n"
 732         "--------------------------------------\n"
 733         "%s\n", buffer);
 734 
 735 
 736     puts("");
 737     puts("Remote info:\n"
 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\n", icedemo.rem.ufrag);
 748         printf("Remote password    : %s\n", icedemo.rem.pwd);
 749         printf("Remote cand. cnt.  : %d\n", 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] = \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] == \r || linebuf[len-1] == \n))
 794             linebuf[--len] = \0;
 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, ": \t\r\n");
 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]==\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\n"
1103         "to use the SIP protocol. To use this application, you will need to run\n"
1104         "two instances of this application, to simulate two ICE agents.\n");
1105 
1106     puts("Basic ICE flow:\n"
1107         " create instance [menu \"c\"]\n"
1108         " repeat these steps as wanted:\n"
1109         "   - init session as offerer or answerer [menu \"i\"]\n"
1110         "   - display our SDP [menu \"s\"]\n"
1111         "   - \"send\" our SDP from the \"show\" output above to remote, by\n"
1112         "     copy-pasting the SDP to the other icedemo application\n"
1113         "   - parse remote SDP, by pasting SDP generated by the other icedemo\n"
1114         "     instance [menu \"r\"]\n"
1115         "   - begin ICE negotiation in our end [menu \"b\"], and \n"
1116         "   - immediately begin ICE negotiation in the other icedemo instance\n"
1117         "   - ICE negotiation will run, and result will be printed to screen\n"
1118         "   - send application data to remote [menu \"x\"]\n"
1119         "   - end/stop ICE session [menu \"e\"]\n"
1120         " destroy instance [menu \"d\"]\n"
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 = " \t\r\n";
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]==\r || input[len-1]==\n))
1177             input[--len] = \0;
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 = http://www.mamicode.com/comp + strlen(comp) + 1;
1226                 if (!data)
1227                     data = http://www.mamicode.com/"";
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‘\n", 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\n", 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.6\build\vs\pjproject-vs8-debug-static-defaults.vsprops
..\pjproject-2.6\build\vs\pjproject-vs8-win32-common-defaults.vsprops

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

使用 pjsip 代码独立开发