Ignore:
Timestamp:
Apr 15, 2009 1:38:40 PM (10 years ago)
Author:
bennylp
Message:

More ticket #780: more work on icedemo sample application

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip-apps/src/samples/icedemo.c

    r2592 r2600  
    2626#define THIS_FILE   "icedemo.c" 
    2727 
     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 */ 
    2835static struct app_t 
    2936{ 
     37    /* Command line options are stored here */ 
    3038    struct options 
    3139    { 
     
    4149    } opt; 
    4250 
     51    /* Our global variables */ 
    4352    pj_caching_pool      cp; 
    4453    pj_pool_t           *pool; 
     
    4857    pj_ice_strans       *icest; 
    4958 
     59    /* Variables to store parsed remote ICE info */ 
    5060    struct rem_info 
    5161    { 
     
    6070} icedemo; 
    6171 
     72/* Utility to display error messages */ 
    6273static void icedemo_perror(const char *title, pj_status_t status) 
    6374{ 
     
    6879} 
    6980 
     81/* Utility: display error message and exit application (usually 
     82 * because of fatal error. 
     83 */ 
    7084static void err_exit(const char *title, pj_status_t status) 
    7185{ 
     
    103117                        } 
    104118 
    105 static pj_status_t icedemo_handle_events(unsigned max_msec, 
    106                                          unsigned *p_count) 
     119/* 
     120 * This function checks for events from both timer and ioqueue (for 
     121 * network events). It is invoked by the worker thread. 
     122 */ 
     123static pj_status_t handle_events(unsigned max_msec, unsigned *p_count) 
    107124{ 
    108125    enum { MAX_NET_EVENTS = 1 }; 
     
    126143    if (timeout.msec >= 1000) timeout.msec = 999; 
    127144 
    128     /* compare the value with the timeout to wait from timer, and use the minimum value. */ 
     145    /* compare the value with the timeout to wait from timer, and use the  
     146     * minimum value.  
     147    */ 
    129148    if (PJ_TIME_VAL_GT(timeout, max_timeout)) 
    130149        timeout = max_timeout; 
     
    165184} 
    166185 
     186/* 
     187 * This is the worker thread that polls event in the background. 
     188 */ 
    167189static int icedemo_worker_thread(void *unused) 
    168190{ 
     
    170192 
    171193    while (!icedemo.thread_quit_flag) { 
    172         icedemo_handle_events(500, NULL); 
     194        handle_events(500, NULL); 
    173195    } 
    174196 
     
    176198} 
    177199 
    178 static void icedemo_on_rx_data(pj_ice_strans *ice_st, 
    179                                unsigned comp_id,  
    180                                void *pkt, pj_size_t size, 
    181                                const pj_sockaddr_t *src_addr, 
    182                                unsigned src_addr_len) 
     200/* 
     201 * This is the callback that is registered to the ICE stream transport to 
     202 * receive notification about incoming data. By "data" it means application 
     203 * data such as RTP/RTCP, and not packets that belong to ICE signaling (such 
     204 * as STUN connectivity checks or TURN signaling). 
     205 */ 
     206static void cb_on_rx_data(pj_ice_strans *ice_st, 
     207                          unsigned comp_id,  
     208                          void *pkt, pj_size_t size, 
     209                          const pj_sockaddr_t *src_addr, 
     210                          unsigned src_addr_len) 
    183211{ 
    184212    char ipstr[PJ_INET6_ADDRSTRLEN+10]; 
     
    188216    PJ_UNUSED_ARG(pkt); 
    189217 
    190     PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s", 
     218    ((char*)pkt)[size] = '\0'; 
     219 
     220    PJ_LOG(3,(THIS_FILE, "Component %d: received %d bytes data from %s: \"%s\"", 
    191221              comp_id, size, 
    192               pj_sockaddr_print(src_addr, ipstr, sizeof(ipstr), 3))); 
    193 } 
    194  
    195 static void icedemo_on_ice_complete(pj_ice_strans *ice_st,  
    196                                     pj_ice_strans_op op, 
    197                                     pj_status_t status) 
     222              pj_sockaddr_print(src_addr, ipstr, sizeof(ipstr), 3), 
     223              (char*)pkt)); 
     224} 
     225 
     226/* 
     227 * This is the callback that is registered to the ICE stream transport to 
     228 * receive notification about ICE state progression. 
     229 */ 
     230static void cb_on_ice_complete(pj_ice_strans *ice_st,  
     231                               pj_ice_strans_op op, 
     232                               pj_status_t status) 
    198233{ 
    199234    const char *opname =  
     
    213248} 
    214249 
     250 
     251/* 
     252 * This is the main application initialization function. It is called 
     253 * once (and only once) during application initialization sequence by  
     254 * main(). 
     255 */ 
    215256static pj_status_t icedemo_init(void) 
    216257{ 
     
    231272 
    232273    /* Create application memory pool */ 
    233     icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo", 512, 512, NULL); 
     274    icedemo.pool = pj_pool_create(&icedemo.cp.factory, "icedemo",  
     275                                  512, 512, NULL); 
    234276 
    235277    /* Create timer heap for timer stuff */ 
    236     CHECK( pj_timer_heap_create(icedemo.pool, 100, &icedemo.ice_cfg.stun_cfg.timer_heap) ); 
     278    CHECK( pj_timer_heap_create(icedemo.pool, 100,  
     279                                &icedemo.ice_cfg.stun_cfg.timer_heap) ); 
    237280 
    238281    /* and create ioqueue for network I/O stuff */ 
    239     CHECK( pj_ioqueue_create(icedemo.pool, 16, &icedemo.ice_cfg.stun_cfg.ioqueue) ); 
    240  
    241     /* something must poll the timer heap and ioqueue, unless we're on Symbian */ 
    242     CHECK( pj_thread_create(icedemo.pool, "icedemo", &icedemo_worker_thread, NULL, 0, 0, &icedemo.thread) ); 
     282    CHECK( pj_ioqueue_create(icedemo.pool, 16,  
     283                             &icedemo.ice_cfg.stun_cfg.ioqueue) ); 
     284 
     285    /* something must poll the timer heap and ioqueue,  
     286     * unless we're on Symbian where the timer heap and ioqueue run 
     287     * on themselves. 
     288     */ 
     289    CHECK( pj_thread_create(icedemo.pool, "icedemo", &icedemo_worker_thread, 
     290                            NULL, 0, 0, &icedemo.thread) ); 
    243291 
    244292    icedemo.ice_cfg.af = pj_AF_INET(); 
     
    249297                                      "resolver",  
    250298                                      0,  
    251                                       icedemo.ice_cfg.stun_cfg.timer_heap,  
     299                                      icedemo.ice_cfg.stun_cfg.timer_heap, 
    252300                                      icedemo.ice_cfg.stun_cfg.ioqueue,  
    253301                                      &icedemo.ice_cfg.resolver) ); 
     
    276324            icedemo.ice_cfg.stun.port = PJ_STUN_PORT; 
    277325        } 
     326 
     327        /* For this demo app, configure longer STUN keep-alive time 
     328         * so that it does't clutter the screen output. 
     329         */ 
     330        icedemo.ice_cfg.stun.cfg.ka_interval = KA_INTERVAL; 
    278331    } 
    279332 
     
    290343        } else { 
    291344            icedemo.ice_cfg.turn.server = icedemo.opt.turn_srv; 
    292             icedemo.ice_cfg.stun.port = PJ_STUN_PORT; 
     345            icedemo.ice_cfg.turn.port = PJ_STUN_PORT; 
    293346        } 
    294347 
     
    305358            icedemo.ice_cfg.turn.conn_type = PJ_TURN_TP_UDP; 
    306359 
    307         /* You may further customize TURN settings, e.g.: 
    308             icedemo.ice_cfg.turn.alloc_param.lifetime = 300 
     360        /* For this demo app, configure longer keep-alive time 
     361         * so that it does't clutter the screen output. 
    309362         */ 
     363        icedemo.ice_cfg.turn.alloc_param.ka_interval = KA_INTERVAL; 
    310364    } 
    311365 
     
    314368} 
    315369 
     370 
     371/* 
     372 * Create ICE stream transport instance, invoked from the menu. 
     373 */ 
    316374static void icedemo_create_instance(void) 
    317375{ 
     
    326384    /* init the callback */ 
    327385    pj_bzero(&icecb, sizeof(icecb)); 
    328     icecb.on_rx_data = icedemo_on_rx_data; 
    329     icecb.on_ice_complete = icedemo_on_ice_complete; 
     386    icecb.on_rx_data = cb_on_rx_data; 
     387    icecb.on_ice_complete = cb_on_ice_complete; 
    330388 
    331389    /* create the instance */ 
     
    339397    if (status != PJ_SUCCESS) 
    340398        icedemo_perror("error creating ice", status); 
    341 } 
    342  
     399    else 
     400        PJ_LOG(3,(THIS_FILE, "ICE instance successfully created")); 
     401} 
     402 
     403/* Utility to nullify parsed remote info */ 
    343404static void reset_rem_info(void) 
    344405{ 
     
    346407} 
    347408 
     409 
     410/* 
     411 * Destroy ICE stream transport instance, invoked from the menu. 
     412 */ 
    348413static void icedemo_destroy_instance(void) 
    349414{ 
     
    357422 
    358423    reset_rem_info(); 
    359 } 
    360  
     424 
     425    PJ_LOG(3,(THIS_FILE, "ICE instance destroyed")); 
     426} 
     427 
     428 
     429/* 
     430 * Create ICE session, invoked from the menu. 
     431 */ 
    361432static void icedemo_init_session(unsigned rolechar) 
    362433{ 
     
    379450    if (status != PJ_SUCCESS) 
    380451        icedemo_perror("error creating session", status); 
     452    else 
     453        PJ_LOG(3,(THIS_FILE, "ICE session created")); 
    381454 
    382455    reset_rem_info(); 
    383456} 
    384457 
     458 
     459/* 
     460 * Stop/destroy ICE session, invoked from the menu. 
     461 */ 
    385462static void icedemo_stop_session(void) 
    386463{ 
     
    400477    if (status != PJ_SUCCESS) 
    401478        icedemo_perror("error stopping session", status); 
     479    else 
     480        PJ_LOG(3,(THIS_FILE, "ICE session stopped")); 
    402481 
    403482    reset_rem_info(); 
    404483} 
    405  
    406484 
    407485#define PRINT(fmt, arg0, arg1, arg2, arg3, arg4, arg5)      \ 
     
    411489        p += printed 
    412490 
     491 
     492/* Utility to create a=candidate SDP attribute */ 
    413493static int print_cand(char buffer[], unsigned maxlen, 
    414494                      const pj_ice_sess_cand *cand) 
     
    427507          (unsigned)pj_sockaddr_get_port(&cand->addr)); 
    428508 
    429     switch (cand->type) { 
    430     case PJ_ICE_CAND_TYPE_HOST: 
    431         PRINT("host\n", 0, 0, 0, 0, 0, 0); 
    432         break; 
    433     case PJ_ICE_CAND_TYPE_SRFLX: 
    434     case PJ_ICE_CAND_TYPE_RELAYED: 
    435     case PJ_ICE_CAND_TYPE_PRFLX: 
    436         PRINT("%s raddr %s rport %d\n", 
    437               pj_ice_get_cand_type_name(cand->type), 
    438               pj_sockaddr_print(&cand->rel_addr, ipaddr, 
    439                                 sizeof(ipaddr), 0), 
    440               (int)pj_sockaddr_get_port(&cand->rel_addr), 
    441               0, 0, 0); 
    442         break; 
    443     default: 
    444         pj_assert(!"Invalid candidate type"); 
    445         return -PJ_EBUG; 
    446     } 
     509    PRINT("%s\n", 
     510          pj_ice_get_cand_type_name(cand->type), 
     511          0, 0, 0, 0, 0); 
    447512 
    448513    if (p == buffer+maxlen) 
     
    454519} 
    455520 
    456 /* Encode ICE information in SDP */ 
     521/*  
     522 * Encode ICE information in SDP. 
     523 */ 
    457524static int encode_session(char buffer[], unsigned maxlen) 
    458525{ 
     
    463530    pj_status_t status; 
    464531 
    465     PRINT("v=0\no=- 3414953978 3414953978 IN IP4 127.0.0.1\ns=ice\nt=0 0\n",  
     532    /* Write "dummy" SDP v=, o=, s=, and t= lines */ 
     533    PRINT("v=0\no=- 3414953978 3414953978 IN IP4 localhost\ns=ice\nt=0 0\n",  
    466534          0, 0, 0, 0, 0, 0); 
    467535 
     
    470538                                NULL, NULL); 
    471539 
     540    /* Write the a=ice-ufrag and a=ice-pwd attributes */ 
    472541    PRINT("a=ice-ufrag:%.*s\na=ice-pwd:%.*s\n", 
    473542           (int)local_ufrag.slen, 
     
    477546           0, 0); 
    478547 
     548    /* Write each component */ 
    479549    for (comp=0; comp<icedemo.opt.comp_cnt; ++comp) { 
    480550        unsigned j, cand_cnt; 
     
    482552        char ipaddr[PJ_INET6_ADDRSTRLEN]; 
    483553 
     554        /* Get default candidate for the component */ 
    484555        status = pj_ice_strans_get_def_cand(icedemo.icest, comp+1, &cand[0]); 
    485556        if (status != PJ_SUCCESS) 
    486557            return -status; 
    487558 
     559        /* Write the default address */ 
    488560        if (comp==0) { 
     561            /* For component 1, default address is in m= and c= lines */ 
    489562            PRINT("m=audio %d RTP/AVP 0\n" 
    490563                  "c=IN IP4 %s\n", 
     
    494567                  0, 0, 0, 0); 
    495568        } else if (comp==1) { 
     569            /* For component 2, default address is in a=rtcp line */ 
    496570            PRINT("a=rtcp:%d IN IP4 %s\n", 
    497571                  (int)pj_sockaddr_get_port(&cand[0].addr), 
     
    500574                  0, 0, 0, 0); 
    501575        } else { 
     576            /* For other components, we'll just invent this.. */ 
    502577            PRINT("a=Xice-defcand:%d IN IP4 %s\n", 
    503578                  (int)pj_sockaddr_get_port(&cand[0].addr), 
     
    507582        } 
    508583 
     584        /* Enumerate all candidates for this component */ 
    509585        status = pj_ice_strans_enum_cands(icedemo.icest, comp+1, 
    510586                                          &cand_cnt, cand); 
     
    512588            return -status; 
    513589 
     590        /* And encode the candidates as SDP */ 
    514591        for (j=0; j<cand_cnt; ++j) { 
    515592            printed = print_cand(p, maxlen - (p-buffer), &cand[j]); 
     
    527604} 
    528605 
     606 
     607/* 
     608 * Show information contained in the ICE stream transport. This is 
     609 * invoked from the menu. 
     610 */ 
    529611static void icedemo_show_ice(void) 
    530612{ 
     
    593675} 
    594676 
     677 
     678/* 
     679 * Input and parse SDP from the remote (containing remote's ICE information)  
     680 * and save it to global variables. 
     681 */ 
    595682static void icedemo_input_remote(void) 
    596683{ 
    597684    char linebuf[80]; 
     685    unsigned media_cnt = 0; 
    598686    unsigned comp0_port = 0; 
    599687    char     comp0_addr[80]; 
     
    626714        if (len==0) 
    627715            break; 
     716 
     717        /* Ignore subsequent media descriptors */ 
     718        if (media_cnt > 1) 
     719            continue; 
    628720 
    629721        switch (line[0]) { 
     
    633725                char media[32], portstr[32]; 
    634726 
     727                ++media_cnt; 
     728                if (media_cnt > 1) { 
     729                    puts("Media line ignored"); 
     730                    break; 
     731                } 
     732 
    635733                cnt = sscanf(line+2, "%s %s RTP/", media, portstr); 
    636734                if (cnt != 2) { 
     
    640738 
    641739                comp0_port = atoi(portstr); 
     740                 
    642741            } 
    643742            break; 
     
    792891    } 
    793892 
    794     printf("Done, %d remote candidates added\n", icedemo.rem.cand_cnt); 
     893    PJ_LOG(3, (THIS_FILE, "Done, %d remote candidate(s) added",  
     894               icedemo.rem.cand_cnt)); 
    795895    return; 
    796896 
     
    799899} 
    800900 
     901 
     902/* 
     903 * Start ICE negotiation! This function is invoked from the menu. 
     904 */ 
    801905static void icedemo_start_nego(void) 
    802906{ 
     
    818922        return; 
    819923    } 
     924 
     925    PJ_LOG(3,(THIS_FILE, "Starting ICE negotiation..")); 
    820926 
    821927    status = pj_ice_strans_start_ice(icedemo.icest,  
     
    830936} 
    831937 
     938 
     939/* 
     940 * Send application data to remote agent. 
     941 */ 
    832942static void icedemo_send_data(unsigned comp_id, const char *data) 
    833943{ 
     
    844954    } 
    845955 
     956    /* 
    846957    if (!pj_ice_strans_sess_is_complete(icedemo.icest)) { 
    847958        PJ_LOG(1,(THIS_FILE, "Error: ICE negotiation has not been started or is in progress")); 
    848959        return; 
    849960    } 
     961    */ 
    850962 
    851963    if (comp_id > pj_ice_strans_get_running_comp_cnt(icedemo.icest)) { 
     
    863975} 
    864976 
     977 
     978/* 
     979 * Display help for the menu. 
     980 */ 
    865981static void icedemo_help_menu(void) 
    866982{ 
    867 } 
    868  
    869  
     983    puts(""); 
     984    puts("-= Help on using ICE and this icedemo program =-"); 
     985    puts(""); 
     986    puts("This application demonstrates how to use ICE in pjnath without having\n" 
     987         "to use the SIP protocol. To use this application, you will need to run\n" 
     988         "two instances of this application, to simulate two ICE agents.\n"); 
     989 
     990    puts("Basic ICE flow:\n" 
     991         " create instance [menu \"c\"]\n" 
     992         " repeat these steps as wanted:\n" 
     993         "   - init session as offerer or answerer [menu \"i\"]\n" 
     994         "   - display our SDP [menu \"s\"]\n" 
     995         "   - \"send\" our SDP from the \"show\" output above to remote, by\n" 
     996         "     copy-pasting the SDP to the other icedemo application\n" 
     997         "   - parse remote SDP, by pasting SDP generated by the other icedemo\n" 
     998         "     instance [menu \"r\"]\n" 
     999         "   - begin ICE negotiation in our end [menu \"b\"]\n" 
     1000         "   - begin ICE negotiation in the other icedemo instance\n" 
     1001         "   - ICE negotiation will run, and result will be printed to screen\n" 
     1002         "   - send application data to remote [menu \"x\"]\n" 
     1003         "   - end/stop ICE session [menu \"e\"]\n" 
     1004         " destroy instance [menu \"d\"]\n" 
     1005         ""); 
     1006 
     1007    puts(""); 
     1008    puts("This concludes the help screen."); 
     1009    puts(""); 
     1010} 
     1011 
     1012 
     1013/* 
     1014 * Display console menu 
     1015 */ 
    8701016static void icedemo_print_menu(void) 
    8711017{ 
     
    8891035 
    8901036 
     1037/* 
     1038 * Main console loop. 
     1039 */ 
    8911040static void icedemo_console(void) 
    8921041{ 
     
    9161065 
    9171066        if (strcmp(cmd, "create")==0 || strcmp(cmd, "c")==0) { 
     1067 
    9181068            icedemo_create_instance(); 
     1069 
    9191070        } else if (strcmp(cmd, "destroy")==0 || strcmp(cmd, "d")==0) { 
     1071 
    9201072            icedemo_destroy_instance(); 
     1073 
    9211074        } else if (strcmp(cmd, "init")==0 || strcmp(cmd, "i")==0) { 
     1075 
    9221076            char *role = strtok(NULL, SEP); 
    9231077            if (role) 
     
    9251079            else 
    9261080                puts("error: Role required"); 
     1081 
    9271082        } else if (strcmp(cmd, "stop")==0 || strcmp(cmd, "e")==0) { 
     1083 
    9281084            icedemo_stop_session(); 
     1085 
    9291086        } else if (strcmp(cmd, "show")==0 || strcmp(cmd, "s")==0) { 
     1087 
    9301088            icedemo_show_ice(); 
     1089 
    9311090        } else if (strcmp(cmd, "remote")==0 || strcmp(cmd, "r")==0) { 
     1091 
    9321092            icedemo_input_remote(); 
     1093 
    9331094        } else if (strcmp(cmd, "start")==0 || strcmp(cmd, "b")==0) { 
     1095 
    9341096            icedemo_start_nego(); 
     1097 
    9351098        } else if (strcmp(cmd, "send")==0 || strcmp(cmd, "x")==0) { 
    936             char *strcomp = strtok(NULL, SEP); 
    937  
    938             if (!strcmp) { 
     1099 
     1100            char *comp = strtok(NULL, SEP); 
     1101 
     1102            if (!comp) { 
    9391103                PJ_LOG(1,(THIS_FILE, "Error: component ID required")); 
    9401104            } else { 
    941                 char *data = strcomp + strlen(strcomp) + 1; 
    942                 icedemo_send_data(atoi(strcomp), data); 
     1105                char *data = comp + strlen(comp) + 1; 
     1106                if (!data) 
     1107                    data = ""; 
     1108                icedemo_send_data(atoi(comp), data); 
    9431109            } 
     1110 
    9441111        } else if (strcmp(cmd, "help")==0 || strcmp(cmd, "h")==0) { 
     1112 
    9451113            icedemo_help_menu(); 
     1114 
    9461115        } else if (strcmp(cmd, "quit")==0 || strcmp(cmd, "q")==0) { 
     1116 
    9471117            app_quit = PJ_TRUE; 
     1118 
    9481119        } else { 
     1120 
    9491121            printf("Invalid command '%s'\n", cmd); 
     1122 
    9501123        } 
    9511124    } 
     
    9531126 
    9541127 
     1128/* 
     1129 * Display program usage. 
     1130 */ 
    9551131static void icedemo_usage() 
    9561132{ 
     
    9821158 
    9831159 
     1160/* 
     1161 * And here's the main() 
     1162 */ 
    9841163int main(int argc, char *argv[]) 
    9851164{ 
Note: See TracChangeset for help on using the changeset viewer.