Ignore:
Timestamp:
Mar 23, 2007 4:34:20 PM (17 years ago)
Author:
bennylp
Message:

ICE (work in progress): integration with PJSUA

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c

    r1066 r1098  
    216216    pj_sock_t sock[2]; 
    217217 
     218    /* Make sure STUN server resolution has completed */ 
     219    status = pjsua_resolve_stun_server(PJ_TRUE); 
     220    if (status != PJ_SUCCESS) { 
     221        pjsua_perror(THIS_FILE, "Error resolving STUN server", status); 
     222        return status; 
     223    } 
     224 
     225 
    218226    if (next_rtp_port == 0) 
    219227        next_rtp_port = (pj_uint16_t)cfg->port; 
     
    273281         * and make sure that the mapped RTCP port is adjacent with the RTP. 
    274282         */ 
    275         if (cfg->stun_config.stun_srv1.slen) { 
     283        if (pjsua_var.stun_srv.addr.sa_family != 0) { 
     284            char ip_addr[32]; 
     285            pj_str_t stun_srv; 
     286 
     287            pj_ansi_strcpy(ip_addr,  
     288                           pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr)); 
     289            stun_srv = pj_str(ip_addr); 
     290 
    276291            status=pjstun_get_mapped_addr(&pjsua_var.cp.factory, 2, sock, 
    277                                            &cfg->stun_config.stun_srv1,  
    278                                            cfg->stun_config.stun_port1, 
    279                                            &cfg->stun_config.stun_srv2,  
    280                                            cfg->stun_config.stun_port2, 
     292                                           &stun_srv, 3478, 
     293                                           &stun_srv, 3478, 
    281294                                           mapped_addr); 
    282295            if (status != PJ_SUCCESS) { 
     
    489502 
    490503 
    491 /* 
    492  * Create UDP media transports for all the calls. This function creates 
    493  * one UDP media transport for each call. 
    494  */ 
    495 PJ_DEF(pj_status_t)  
    496 pjsua_media_transports_create(const pjsua_transport_config *app_cfg) 
    497 { 
    498     pjsua_transport_config cfg; 
     504/* Create normal UDP media transports */ 
     505static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg) 
     506{ 
    499507    unsigned i; 
    500508    pj_status_t status; 
    501509 
    502  
    503     /* Make sure pjsua_init() has been called */ 
    504     PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP); 
    505  
    506     PJSUA_LOCK(); 
    507  
    508     /* Delete existing media transports */ 
    509     for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { 
    510         if (pjsua_var.calls[i].med_tp != NULL) { 
    511             pjsua_var.calls[i].med_tp->op->destroy(pjsua_var.calls[i].med_tp); 
    512             pjsua_var.calls[i].med_tp = NULL; 
    513         } 
    514     } 
    515  
    516     /* Copy config */ 
    517     pj_memcpy(&cfg, app_cfg, sizeof(*app_cfg)); 
    518     pjsua_normalize_stun_config(&cfg.stun_config); 
    519  
    520510    /* Create each media transport */ 
    521511    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { 
    522512 
    523         status = create_rtp_rtcp_sock(&cfg, &pjsua_var.calls[i].skinfo); 
     513        status = create_rtp_rtcp_sock(cfg, &pjsua_var.calls[i].skinfo); 
    524514        if (status != PJ_SUCCESS) { 
    525515            pjsua_perror(THIS_FILE, "Unable to create RTP/RTCP socket", 
     
    546536    } 
    547537 
    548     PJSUA_UNLOCK(); 
    549  
    550538    return PJ_SUCCESS; 
    551539 
     
    553541    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { 
    554542        if (pjsua_var.calls[i].med_tp != NULL) { 
    555             pjsua_var.calls[i].med_tp->op->destroy(pjsua_var.calls[i].med_tp); 
     543            pjmedia_transport_close(pjsua_var.calls[i].med_tp); 
    556544            pjsua_var.calls[i].med_tp = NULL; 
    557545        } 
    558546    } 
    559547 
     548    return status; 
     549} 
     550 
     551 
     552/* Create ICE media transports (when ice is enabled) */ 
     553static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg) 
     554{ 
     555    unsigned i; 
     556    pj_status_t status; 
     557 
     558    /* Create each media transport */ 
     559    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { 
     560        pj_ice_st *ice_st; 
     561 
     562        status = pjmedia_ice_create(pjsua_var.med_endpt, NULL, 1, 
     563                                    &pjsua_var.stun_cfg,  
     564                                    &pjsua_var.calls[i].med_tp); 
     565        if (status != PJ_SUCCESS) { 
     566            pjsua_perror(THIS_FILE, "Unable to create ICE media transport", 
     567                         status); 
     568            goto on_error; 
     569        } 
     570 
     571        ice_st = pjmedia_ice_get_ice_st(pjsua_var.calls[i].med_tp); 
     572 
     573        /* Add host candidates for RTP */ 
     574        status = pj_ice_st_add_all_host_interfaces(ice_st, 1, 0,  
     575                                                   PJ_FALSE, NULL); 
     576        if (status != PJ_SUCCESS) { 
     577            pjsua_perror(THIS_FILE, "Error adding ICE host candidates", 
     578                         status); 
     579            goto on_error; 
     580        } 
     581 
     582        /* Configure STUN server */ 
     583        if (pjsua_var.stun_srv.addr.sa_family != 0) { 
     584         
     585            status = pj_ice_st_set_stun_addr(ice_st,  
     586                                             pjsua_var.media_cfg.enable_relay, 
     587                                             &pjsua_var.stun_srv.ipv4); 
     588            if (status != PJ_SUCCESS) { 
     589                pjsua_perror(THIS_FILE, "Error setting ICE's STUN server", 
     590                             status); 
     591                goto on_error; 
     592            } 
     593 
     594            /* Add STUN server reflexive candidate for RTP */ 
     595            status = pj_ice_st_add_stun_interface(ice_st, 1, 0,  
     596                                                  PJ_FALSE, NULL); 
     597            if (status != PJ_SUCCESS) { 
     598                pjsua_perror(THIS_FILE, "Error adding ICE address", 
     599                             status); 
     600                goto on_error; 
     601            } 
     602        } 
     603    } 
     604 
     605    /* Wait until all ICE transports are ready */ 
     606    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { 
     607        pj_ice_st *ice_st; 
     608 
     609        ice_st = pjmedia_ice_get_ice_st(pjsua_var.calls[i].med_tp); 
     610 
     611        /* Wait until interface status is PJ_SUCCESS */ 
     612        for (;;) { 
     613            status = pj_ice_st_get_interfaces_status(ice_st); 
     614            if (status == PJ_EPENDING) 
     615                pjsua_handle_events(100); 
     616            else 
     617                break; 
     618        } 
     619 
     620        if (status != PJ_SUCCESS) { 
     621            pjsua_perror(THIS_FILE,  
     622                         "Error adding STUN address to ICE media transport", 
     623                         status); 
     624            goto on_error; 
     625        } 
     626 
     627        /* Get transport info */ 
     628        pjmedia_transport_get_info(pjsua_var.calls[i].med_tp, 
     629                                   &pjsua_var.calls[i].skinfo); 
     630 
     631    } 
     632 
     633    return PJ_SUCCESS; 
     634 
     635on_error: 
     636    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { 
     637        if (pjsua_var.calls[i].med_tp != NULL) { 
     638            pjmedia_transport_close(pjsua_var.calls[i].med_tp); 
     639            pjsua_var.calls[i].med_tp = NULL; 
     640        } 
     641    } 
     642 
     643    return status; 
     644} 
     645 
     646 
     647/* 
     648 * Create UDP media transports for all the calls. This function creates 
     649 * one UDP media transport for each call. 
     650 */ 
     651PJ_DEF(pj_status_t)  
     652pjsua_media_transports_create(const pjsua_transport_config *app_cfg) 
     653{ 
     654    pjsua_transport_config cfg; 
     655    unsigned i; 
     656    pj_status_t status; 
     657 
     658 
     659    /* Make sure pjsua_init() has been called */ 
     660    PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP); 
     661 
     662    PJSUA_LOCK(); 
     663 
     664    /* Delete existing media transports */ 
     665    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { 
     666        if (pjsua_var.calls[i].med_tp != NULL) { 
     667            pjmedia_transport_close(pjsua_var.calls[i].med_tp); 
     668            pjsua_var.calls[i].med_tp = NULL; 
     669        } 
     670    } 
     671 
     672    /* Copy config */ 
     673    pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg); 
     674 
     675    if (pjsua_var.media_cfg.enable_ice) { 
     676        status = create_ice_media_transports(&cfg); 
     677    } else { 
     678        status = create_udp_media_transports(&cfg); 
     679    } 
     680 
     681 
    560682    PJSUA_UNLOCK(); 
    561683 
    562684    return status; 
     685} 
     686 
     687 
     688pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, 
     689                                     pjsip_role_e role) 
     690{ 
     691    pjsua_call *call = &pjsua_var.calls[call_id]; 
     692 
     693    if (pjsua_var.media_cfg.enable_ice) { 
     694        pj_ice_role ice_role; 
     695        pj_status_t status; 
     696 
     697        ice_role = (role==PJSIP_ROLE_UAC ? PJ_ICE_ROLE_CONTROLLING :  
     698                                           PJ_ICE_ROLE_CONTROLLED); 
     699 
     700        /* Restart ICE */ 
     701        pjmedia_ice_stop_ice(call->med_tp); 
     702 
     703        status = pjmedia_ice_init_ice(call->med_tp, ice_role, NULL, NULL); 
     704        if (status != PJ_SUCCESS) 
     705            return status; 
     706    } 
     707 
     708    return PJ_SUCCESS; 
     709} 
     710 
     711pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,  
     712                                           pj_pool_t *pool, 
     713                                           pjmedia_sdp_session **p_sdp) 
     714{ 
     715    pjmedia_sdp_session *sdp; 
     716    pjsua_call *call = &pjsua_var.calls[call_id]; 
     717    pj_status_t status; 
     718 
     719    status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pool, 1, 
     720                                      &call->skinfo, &sdp); 
     721    if (status != PJ_SUCCESS) 
     722        goto on_error; 
     723 
     724    if (pjsua_var.media_cfg.enable_ice) { 
     725        status = pjmedia_ice_modify_sdp(call->med_tp, pool, sdp); 
     726        if (status != PJ_SUCCESS) 
     727            goto on_error; 
     728    } 
     729 
     730    *p_sdp = sdp; 
     731    return PJ_SUCCESS; 
     732 
     733on_error: 
     734    pjsua_media_channel_deinit(call_id); 
     735    return status; 
     736 
     737} 
     738 
     739 
     740static void stop_media_session(pjsua_call_id call_id) 
     741{ 
     742    pjsua_call *call = &pjsua_var.calls[call_id]; 
     743 
     744    if (call->conf_slot != PJSUA_INVALID_ID) { 
     745        pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot); 
     746        call->conf_slot = PJSUA_INVALID_ID; 
     747    } 
     748 
     749    if (call->session) { 
     750        pjmedia_session_destroy(call->session); 
     751        call->session = NULL; 
     752 
     753        PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",  
     754                             call_id)); 
     755 
     756    } 
     757 
     758    call->media_st = PJSUA_CALL_MEDIA_NONE; 
     759} 
     760 
     761pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) 
     762{ 
     763    pjsua_call *call = &pjsua_var.calls[call_id]; 
     764 
     765    stop_media_session(call_id); 
     766 
     767    if (pjsua_var.media_cfg.enable_ice) { 
     768        pjmedia_ice_stop_ice(call->med_tp); 
     769    } 
     770 
     771    return PJ_SUCCESS; 
     772} 
     773 
     774 
     775/* 
     776 * DTMF callback from the stream. 
     777 */ 
     778static void dtmf_callback(pjmedia_stream *strm, void *user_data, 
     779                          int digit) 
     780{ 
     781    PJ_UNUSED_ARG(strm); 
     782 
     783    if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { 
     784        pjsua_call_id call_id; 
     785 
     786        call_id = (pjsua_call_id)user_data; 
     787        pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit); 
     788    } 
     789} 
     790 
     791 
     792pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, 
     793                                       pjmedia_sdp_session *local_sdp, 
     794                                       pjmedia_sdp_session *remote_sdp) 
     795{ 
     796    int prev_media_st = 0; 
     797    pjsua_call *call = &pjsua_var.calls[call_id]; 
     798    pjmedia_session_info sess_info; 
     799    pjmedia_port *media_port; 
     800    pj_str_t port_name; 
     801    char tmp[PJSIP_MAX_URL_SIZE]; 
     802    pj_status_t status; 
     803 
     804    /* Destroy existing media session, if any. */ 
     805    prev_media_st = call->media_st; 
     806    stop_media_session(call->index); 
     807 
     808    /* Create media session info based on SDP parameters.  
     809     * We only support one stream per session at the moment 
     810     */     
     811    status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,  
     812                                            pjsua_var.med_endpt,  
     813                                            1,&sess_info,  
     814                                            local_sdp, remote_sdp); 
     815    if (status != PJ_SUCCESS) 
     816        return status; 
     817 
     818 
     819    /* Check if media is put on-hold */ 
     820    if (sess_info.stream_cnt == 0 ||  
     821        sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE) 
     822    { 
     823 
     824        /* Determine who puts the call on-hold */ 
     825        if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) { 
     826            if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) { 
     827                /* It was local who offer hold */ 
     828                call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD; 
     829            } else { 
     830                call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD; 
     831            } 
     832        } 
     833 
     834        call->media_dir = PJMEDIA_DIR_NONE; 
     835 
     836        /* Shutdown transport */ 
     837        /* No need because we need keepalive? */ 
     838 
     839    } else { 
     840 
     841        /* Start ICE */ 
     842        if (pjsua_var.media_cfg.enable_ice) { 
     843            status = pjmedia_ice_start_ice(call->med_tp, call->inv->pool,  
     844                                           remote_sdp); 
     845            if (status != PJ_SUCCESS) 
     846                return status; 
     847        } 
     848 
     849        /* Override ptime, if this option is specified. */ 
     850        if (pjsua_var.media_cfg.ptime != 0) { 
     851            sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t) 
     852                (pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime); 
     853            if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0) 
     854                sess_info.stream_info[0].param->setting.frm_per_pkt = 1; 
     855        } 
     856 
     857        /* Disable VAD, if this option is specified. */ 
     858        if (pjsua_var.media_cfg.no_vad) { 
     859            sess_info.stream_info[0].param->setting.vad = 0; 
     860        } 
     861 
     862 
     863        /* Optionally, application may modify other stream settings here 
     864         * (such as jitter buffer parameters, codec ptime, etc.) 
     865         */ 
     866        sess_info.stream_info[0].jb_init = pjsua_var.media_cfg.jb_init; 
     867        sess_info.stream_info[0].jb_min_pre = pjsua_var.media_cfg.jb_min_pre; 
     868        sess_info.stream_info[0].jb_max_pre = pjsua_var.media_cfg.jb_max_pre; 
     869        sess_info.stream_info[0].jb_max = pjsua_var.media_cfg.jb_max; 
     870 
     871        /* Create session based on session info. */ 
     872        status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info, 
     873                                         &call->med_tp, 
     874                                         call, &call->session ); 
     875        if (status != PJ_SUCCESS) { 
     876            return status; 
     877        } 
     878 
     879        /* If DTMF callback is installed by application, install our 
     880         * callback to the session. 
     881         */ 
     882        if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { 
     883            pjmedia_session_set_dtmf_callback(call->session, 0,  
     884                                              &dtmf_callback,  
     885                                              (void*)(call->index)); 
     886        } 
     887 
     888        /* Get the port interface of the first stream in the session. 
     889         * We need the port interface to add to the conference bridge. 
     890         */ 
     891        pjmedia_session_get_port(call->session, 0, &media_port); 
     892 
     893 
     894        /* 
     895         * Add the call to conference bridge. 
     896         */ 
     897        port_name.ptr = tmp; 
     898        port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, 
     899                                         call->inv->dlg->remote.info->uri, 
     900                                         tmp, sizeof(tmp)); 
     901        if (port_name.slen < 1) { 
     902            port_name = pj_str("call"); 
     903        } 
     904        status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool, 
     905                                        media_port,  
     906                                        &port_name, 
     907                                        (unsigned*)&call->conf_slot); 
     908        if (status != PJ_SUCCESS) { 
     909            return status; 
     910        } 
     911 
     912        /* Call's media state is active */ 
     913        call->media_st = PJSUA_CALL_MEDIA_ACTIVE; 
     914        call->media_dir = sess_info.stream_info[0].dir; 
     915    } 
     916 
     917    /* Print info. */ 
     918    { 
     919        char info[80]; 
     920        int info_len = 0; 
     921        unsigned i; 
     922 
     923        for (i=0; i<sess_info.stream_cnt; ++i) { 
     924            int len; 
     925            const char *dir; 
     926            pjmedia_stream_info *strm_info = &sess_info.stream_info[i]; 
     927 
     928            switch (strm_info->dir) { 
     929            case PJMEDIA_DIR_NONE: 
     930                dir = "inactive"; 
     931                break; 
     932            case PJMEDIA_DIR_ENCODING: 
     933                dir = "sendonly"; 
     934                break; 
     935            case PJMEDIA_DIR_DECODING: 
     936                dir = "recvonly"; 
     937                break; 
     938            case PJMEDIA_DIR_ENCODING_DECODING: 
     939                dir = "sendrecv"; 
     940                break; 
     941            default: 
     942                dir = "unknown"; 
     943                break; 
     944            } 
     945            len = pj_ansi_sprintf( info+info_len, 
     946                                   ", stream #%d: %.*s (%s)", i, 
     947                                   (int)strm_info->fmt.encoding_name.slen, 
     948                                   strm_info->fmt.encoding_name.ptr, 
     949                                   dir); 
     950            if (len > 0) 
     951                info_len += len; 
     952        } 
     953        PJ_LOG(4,(THIS_FILE,"Media updates%s", info)); 
     954    } 
     955 
     956    return PJ_SUCCESS; 
    563957} 
    564958 
Note: See TracChangeset for help on using the changeset viewer.