• 使用 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
  • 相关阅读:
    用Visual Studio 2005/2008提取EXE文件中的资源[图片|htm|光标文件]
    C# 操作Excel之旁门左道 [ C# | Excel ]
    ExtJs 备忘录(1)—— Form表单(一) [ 控件使用 ]
    Win7(64位)安装Microsoft SQL Server Management Studio Express[error 29506]
    ExtJs 备忘录(8)—— 管理界面搭建和其他部分控件介绍
    Visual Studio 模板 —— 自定义WebForm模板
    让Visual Studio 也支持JS代码折叠 [ Visual Studio | #region | #endregion ]
    ExtJs 备忘录(3)—— Form表单(三) [ 数据验证 ]
    ExtJs 备忘录(7)—— GirdPanl表格(三) [ 统计|查看、修改单行记录 ]
    让Visual Studio 也支持JS代码折叠 —— 续 [ Visual Studio | Js | ScriptOutline | SmallOutline ]
  • 原文地址:https://www.cnblogs.com/mobilecard/p/6605322.html
Copyright © 2020-2023  润新知