Ignore:
Timestamp:
Jul 19, 2011 3:42:28 AM (13 years ago)
Author:
nanang
Message:

Re #1326: Initial code integration from branch 2.0-dev to trunk as "2.0-pre-alpha-svn".

Location:
pjproject/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk

  • pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c

    r3553 r3664  
    102102{ 
    103103    pjsua_call *call = &pjsua_var.calls[id]; 
    104  
     104    unsigned i; 
     105 
     106    pj_bzero(call, sizeof(*call)); 
    105107    call->index = id; 
    106     call->inv = NULL; 
    107     call->user_data = NULL; 
    108     call->session = NULL; 
    109     call->audio_idx = -1; 
    110     call->ssrc = pj_rand(); 
    111     call->rtp_tx_seq = 0; 
    112     call->rtp_tx_ts = 0; 
    113     call->rtp_tx_seq_ts_set = 0; 
    114     call->xfer_sub = NULL; 
    115     call->last_code = (pjsip_status_code) 0; 
    116     call->conf_slot = PJSUA_INVALID_ID; 
    117108    call->last_text.ptr = call->last_text_buf_; 
    118     call->last_text.slen = 0; 
    119     call->conn_time.sec = 0; 
    120     call->conn_time.msec = 0; 
    121     call->res_time.sec = 0; 
    122     call->res_time.msec = 0; 
    123     call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN; 
    124     call->rem_srtp_use = PJMEDIA_SRTP_DISABLED; 
    125     call->local_hold = PJ_FALSE; 
    126     pj_bzero(&call->lock_codec, sizeof(call->lock_codec)); 
     109    for (i=0; i<PJ_ARRAY_SIZE(call->media); ++i) { 
     110        pjsua_call_media *call_med = &call->media[i]; 
     111        call_med->ssrc = pj_rand(); 
     112        call_med->strm.a.conf_slot = PJSUA_INVALID_ID; 
     113        call_med->strm.v.cap_win_id = PJSUA_INVALID_ID; 
     114        call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID; 
     115        call_med->call = call; 
     116        call_med->idx = i; 
     117        call_med->tp_auto_del = PJ_TRUE; 
     118    } 
    127119} 
    128120 
     
    824816    status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,  
    825817                                      call->secure_level,  
    826                                       rdata->tp_info.pool, offer, 
     818                                      rdata->tp_info.pool, 
     819                                      offer, 
    827820                                      &sip_err_code); 
    828821    if (status != PJ_SUCCESS) { 
     
    11191112PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id) 
    11201113{ 
     1114    pjsua_call *call = &pjsua_var.calls[call_id]; 
    11211115    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,  
    11221116                     PJ_EINVAL); 
    1123     return pjsua_var.calls[call_id].session != NULL; 
    1124 } 
    1125  
    1126  
    1127 /* 
    1128  * Retrieve the media session associated with this call. 
    1129  */ 
    1130 PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id) 
    1131 { 
    1132     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,  
    1133                      NULL); 
    1134     return pjsua_var.calls[call_id].session; 
    1135 } 
    1136  
    1137  
    1138 /* 
    1139  * Retrieve the media transport instance that is used for this call. 
    1140  */ 
    1141 PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid) 
    1142 { 
    1143     PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,  
    1144                      NULL); 
    1145     return pjsua_var.calls[cid].med_tp; 
     1117    return call->audio_idx >= 0 && call->media[call->audio_idx].strm.a.stream; 
    11461118} 
    11471119 
     
    12391211        return PJSUA_INVALID_ID; 
    12401212 
    1241     port_id = call->conf_slot; 
     1213    port_id = call->media[call->audio_idx].strm.a.conf_slot; 
    12421214 
    12431215    pjsip_dlg_dec_lock(dlg); 
     
    12561228    pjsua_call *call; 
    12571229    pjsip_dialog *dlg; 
     1230    unsigned mi; 
    12581231    pj_status_t status; 
    12591232 
     
    13301303    } 
    13311304     
    1332     /* media status and dir */ 
    1333     info->media_status = call->media_st; 
    1334     info->media_dir = call->media_dir; 
    1335  
    1336  
    1337     /* conference slot number */ 
    1338     info->conf_slot = call->conf_slot; 
     1305    /* Build array of media status and dir */ 
     1306    info->media_cnt = 0; 
     1307    for (mi=0; mi < call->med_cnt && 
     1308               info->media_cnt < PJ_ARRAY_SIZE(info->media); ++mi) 
     1309    { 
     1310        pjsua_call_media *call_med = &call->media[mi]; 
     1311 
     1312        info->media[info->media_cnt].index = mi; 
     1313        info->media[info->media_cnt].status = call_med->state; 
     1314        info->media[info->media_cnt].dir = call_med->dir; 
     1315        info->media[info->media_cnt].type = call_med->type; 
     1316 
     1317        if (call_med->type == PJMEDIA_TYPE_AUDIO) { 
     1318            info->media[info->media_cnt].stream.aud.conf_slot = 
     1319                                                call_med->strm.a.conf_slot; 
     1320        } else if (call_med->type == PJMEDIA_TYPE_VIDEO) { 
     1321            pjmedia_vid_dev_index cap_dev = PJMEDIA_VID_INVALID_DEV; 
     1322 
     1323            info->media[info->media_cnt].stream.vid.win_in =  
     1324                                                call_med->strm.v.rdr_win_id; 
     1325 
     1326            if (call_med->strm.v.cap_win_id != PJSUA_INVALID_ID) { 
     1327                pjsua_vid_win *w = &pjsua_var.win[call_med->strm.v.cap_win_id]; 
     1328                cap_dev = w->preview_cap_id; 
     1329            } 
     1330            info->media[info->media_cnt].stream.vid.cap_dev = cap_dev; 
     1331        } else { 
     1332            continue; 
     1333        } 
     1334        ++info->media_cnt; 
     1335    } 
     1336 
     1337    if (call->audio_idx != -1) { 
     1338        info->media_status = call->media[call->audio_idx].state; 
     1339        info->media_dir = call->media[call->audio_idx].dir; 
     1340        info->conf_slot = call->media[call->audio_idx].strm.a.conf_slot; 
     1341    } 
    13391342 
    13401343    /* calculate duration */ 
     
    14301433    *p_type = pjsua_var.calls[call_id].rem_nat_type; 
    14311434    return PJ_SUCCESS; 
     1435} 
     1436 
     1437 
     1438/* 
     1439 * Get media stream info for the specified media index. 
     1440 */ 
     1441PJ_DEF(pj_status_t) pjsua_call_get_stream_info( pjsua_call_id call_id, 
     1442                                                unsigned med_idx, 
     1443                                                pjsua_stream_info *psi) 
     1444{ 
     1445    pjsua_call *call; 
     1446    pjsua_call_media *call_med; 
     1447    pj_status_t status; 
     1448 
     1449    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
     1450                     PJ_EINVAL); 
     1451    PJ_ASSERT_RETURN(psi, PJ_EINVAL); 
     1452 
     1453    PJSUA_LOCK(); 
     1454 
     1455    call = &pjsua_var.calls[call_id]; 
     1456     
     1457    if (med_idx >= call->med_cnt) { 
     1458        PJSUA_UNLOCK(); 
     1459        return PJ_EINVAL; 
     1460    } 
     1461 
     1462    call_med = &call->media[med_idx]; 
     1463    psi->type = call_med->type; 
     1464    switch (call_med->type) { 
     1465    case PJMEDIA_TYPE_AUDIO: 
     1466        status = pjmedia_stream_get_info(call_med->strm.a.stream, 
     1467                                         &psi->info.aud); 
     1468        break; 
     1469    case PJMEDIA_TYPE_VIDEO: 
     1470        status = pjmedia_vid_stream_get_info(call_med->strm.v.stream, 
     1471                                             &psi->info.vid); 
     1472        break; 
     1473    default: 
     1474        status = PJMEDIA_EINVALIMEDIATYPE; 
     1475        break; 
     1476    } 
     1477     
     1478    PJSUA_UNLOCK(); 
     1479    return status; 
     1480} 
     1481 
     1482 
     1483/* 
     1484 *  Get media stream statistic for the specified media index. 
     1485 */ 
     1486PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id, 
     1487                                                unsigned med_idx, 
     1488                                                pjsua_stream_stat *stat) 
     1489{ 
     1490    pjsua_call *call; 
     1491    pjsua_call_media *call_med; 
     1492    pj_status_t status; 
     1493 
     1494    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
     1495                     PJ_EINVAL); 
     1496    PJ_ASSERT_RETURN(stat, PJ_EINVAL); 
     1497 
     1498    PJSUA_LOCK(); 
     1499 
     1500    call = &pjsua_var.calls[call_id]; 
     1501     
     1502    if (med_idx >= call->med_cnt) { 
     1503        PJSUA_UNLOCK(); 
     1504        return PJ_EINVAL; 
     1505    } 
     1506 
     1507    call_med = &call->media[med_idx]; 
     1508    switch (call_med->type) { 
     1509    case PJMEDIA_TYPE_AUDIO: 
     1510        status = pjmedia_stream_get_stat(call_med->strm.a.stream, 
     1511                                         &stat->rtcp); 
     1512        if (status == PJ_SUCCESS) 
     1513            status = pjmedia_stream_get_stat_jbuf(call_med->strm.a.stream, 
     1514                                                  &stat->jbuf); 
     1515        break; 
     1516    case PJMEDIA_TYPE_VIDEO: 
     1517        status = pjmedia_vid_stream_get_stat(call_med->strm.v.stream, 
     1518                                             &stat->rtcp); 
     1519        if (status == PJ_SUCCESS) 
     1520            status = pjmedia_vid_stream_get_stat_jbuf(call_med->strm.v.stream, 
     1521                                                  &stat->jbuf); 
     1522        break; 
     1523    default: 
     1524        status = PJMEDIA_EINVALIMEDIATYPE; 
     1525        break; 
     1526    } 
     1527     
     1528    PJSUA_UNLOCK(); 
     1529    return status; 
     1530} 
     1531 
     1532 
     1533/* 
     1534 * Get media transport info for the specified media index. 
     1535 */ 
     1536PJ_DEF(pj_status_t) pjsua_call_get_transport_info( pjsua_call_id call_id, 
     1537                                                   unsigned med_idx, 
     1538                                                   pjmedia_transport_info *t) 
     1539{ 
     1540    pjsua_call *call; 
     1541    pjsua_call_media *call_med; 
     1542    pj_status_t status; 
     1543 
     1544    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
     1545                     PJ_EINVAL); 
     1546    PJ_ASSERT_RETURN(t, PJ_EINVAL); 
     1547 
     1548    PJSUA_LOCK(); 
     1549 
     1550    call = &pjsua_var.calls[call_id]; 
     1551     
     1552    if (med_idx >= call->med_cnt) { 
     1553        PJSUA_UNLOCK(); 
     1554        return PJ_EINVAL; 
     1555    } 
     1556 
     1557    call_med = &call->media[med_idx]; 
     1558 
     1559    pjmedia_transport_info_init(t); 
     1560    status = pjmedia_transport_get_info(call_med->tp, t); 
     1561     
     1562    PJSUA_UNLOCK(); 
     1563    return status; 
    14321564} 
    14331565 
     
    19742106        return status; 
    19752107 
    1976     if (!call->session) { 
     2108    if (!pjsua_call_has_media(call_id)) { 
    19772109        PJ_LOG(3,(THIS_FILE, "Media is not established yet!")); 
    19782110        pjsip_dlg_dec_lock(dlg); 
     
    19802112    } 
    19812113 
    1982     status = pjmedia_session_dial_dtmf( call->session, 0, digits); 
     2114    status = pjmedia_stream_dial_dtmf( 
     2115                call->media[call->audio_idx].strm.a.stream, digits); 
    19832116 
    19842117    pjsip_dlg_dec_lock(dlg); 
     
    21782311} 
    21792312 
    2180  
    2181 const char *good_number(char *buf, pj_int32_t val) 
    2182 { 
    2183     if (val < 1000) { 
    2184         pj_ansi_sprintf(buf, "%d", val); 
    2185     } else if (val < 1000000) { 
    2186         pj_ansi_sprintf(buf, "%d.%dK",  
    2187                         val / 1000, 
    2188                         (val % 1000) / 100); 
    2189     } else { 
    2190         pj_ansi_sprintf(buf, "%d.%02dM",  
    2191                         val / 1000000, 
    2192                         (val % 1000000) / 10000); 
    2193     } 
    2194  
    2195     return buf; 
    2196 } 
    2197  
    2198  
    2199 /* Dump media session */ 
    2200 static void dump_media_session(const char *indent,  
    2201                                char *buf, unsigned maxlen, 
    2202                                pjsua_call *call) 
    2203 { 
    2204     unsigned i; 
    2205     char *p = buf, *end = buf+maxlen; 
    2206     int len; 
    2207     pjmedia_session_info info; 
    2208     pjmedia_session *session = call->session; 
    2209     pjmedia_transport_info tp_info; 
    2210  
    2211     pjmedia_transport_info_init(&tp_info); 
    2212  
    2213     pjmedia_transport_get_info(call->med_tp, &tp_info); 
    2214     pjmedia_session_get_info(session, &info); 
    2215  
    2216     for (i=0; i<info.stream_cnt; ++i) { 
    2217         pjmedia_rtcp_stat stat; 
    2218         char rem_addr_buf[80]; 
    2219         const char *rem_addr; 
    2220         const char *dir; 
    2221         char last_update[64]; 
    2222         char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32]; 
    2223         pj_time_val media_duration, now; 
    2224  
    2225         pjmedia_session_get_stream_stat(session, i, &stat); 
    2226         // rem_addr will contain actual address of RTP originator, instead of 
    2227         // remote RTP address specified by stream which is fetched from the SDP. 
    2228         // Please note that we are assuming only one stream per call. 
    2229         //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr, 
    2230         //                           rem_addr_buf, sizeof(rem_addr_buf), 3); 
    2231         if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) { 
    2232             rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,  
    2233                                          sizeof(rem_addr_buf), 3); 
    2234         } else { 
    2235             pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-"); 
    2236             rem_addr = rem_addr_buf; 
    2237         } 
    2238  
    2239         if (call->media_dir == PJMEDIA_DIR_NONE) { 
    2240             /* To handle when the stream that is currently being paused 
    2241              * (http://trac.pjsip.org/repos/ticket/1079) 
    2242              */ 
    2243             dir = "inactive"; 
    2244         } else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING) 
    2245             dir = "sendonly"; 
    2246         else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING) 
    2247             dir = "recvonly"; 
    2248         else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING) 
    2249             dir = "sendrecv"; 
    2250         else 
    2251             dir = "inactive"; 
    2252  
    2253          
    2254         len = pj_ansi_snprintf(buf, end-p,  
    2255                   "%s  #%d %.*s @%dKHz, %s, peer=%s", 
    2256                   indent, i, 
    2257                   (int)info.stream_info[i].fmt.encoding_name.slen, 
    2258                   info.stream_info[i].fmt.encoding_name.ptr, 
    2259                   info.stream_info[i].fmt.clock_rate / 1000, 
    2260                   dir, 
    2261                   rem_addr); 
    2262         if (len < 1 || len > end-p) { 
    2263             *p = '\0'; 
    2264             return; 
    2265         } 
    2266  
    2267         p += len; 
    2268         *p++ = '\n'; 
    2269         *p = '\0'; 
    2270  
    2271         if (stat.rx.update_cnt == 0) 
    2272             strcpy(last_update, "never"); 
    2273         else { 
    2274             pj_gettimeofday(&now); 
    2275             PJ_TIME_VAL_SUB(now, stat.rx.update); 
    2276             sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", 
    2277                     now.sec / 3600, 
    2278                     (now.sec % 3600) / 60, 
    2279                     now.sec % 60, 
    2280                     now.msec); 
    2281         } 
    2282  
    2283         pj_gettimeofday(&media_duration); 
    2284         PJ_TIME_VAL_SUB(media_duration, stat.start); 
    2285         if (PJ_TIME_VAL_MSEC(media_duration) == 0) 
    2286             media_duration.msec = 1; 
    2287  
    2288         /* protect against division by zero */ 
    2289         if (stat.rx.pkt == 0) 
    2290             stat.rx.pkt = 1; 
    2291         if (stat.tx.pkt == 0) 
    2292             stat.tx.pkt = 1; 
    2293  
    2294         len = pj_ansi_snprintf(p, end-p, 
    2295                "%s     RX pt=%d, stat last update: %s\n" 
    2296                "%s        total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n" 
    2297                "%s        pkt loss=%d (%3.1f%%), discrd=%d (%3.1f%%), dup=%d (%2.1f%%), reord=%d (%3.1f%%)\n" 
    2298                "%s              (msec)    min     avg     max     last    dev\n" 
    2299                "%s        loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n" 
    2300                "%s        jitter     : %7.3f %7.3f %7.3f %7.3f %7.3f" 
    2301 #if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0 
    2302                "\n" 
    2303                "%s        raw jitter : %7.3f %7.3f %7.3f %7.3f %7.3f" 
    2304 #endif 
    2305 #if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0 
    2306                "\n" 
    2307                "%s        IPDV       : %7.3f %7.3f %7.3f %7.3f %7.3f" 
    2308 #endif 
    2309                "%s", 
    2310                indent, info.stream_info[i].fmt.pt, 
    2311                last_update, 
    2312                indent, 
    2313                good_number(packets, stat.rx.pkt), 
    2314                good_number(bytes, stat.rx.bytes), 
    2315                good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40), 
    2316                good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))), 
    2317                good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.rx.bytes + stat.rx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))), 
    2318                indent, 
    2319                stat.rx.loss, 
    2320                stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss), 
    2321                stat.rx.discard,  
    2322                stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss), 
    2323                stat.rx.dup,  
    2324                stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss), 
    2325                stat.rx.reorder,  
    2326                stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss), 
    2327                indent, indent, 
    2328                stat.rx.loss_period.min / 1000.0,  
    2329                stat.rx.loss_period.mean / 1000.0,  
    2330                stat.rx.loss_period.max / 1000.0, 
    2331                stat.rx.loss_period.last / 1000.0, 
    2332                pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0, 
    2333                indent, 
    2334                stat.rx.jitter.min / 1000.0, 
    2335                stat.rx.jitter.mean / 1000.0, 
    2336                stat.rx.jitter.max / 1000.0, 
    2337                stat.rx.jitter.last / 1000.0, 
    2338                pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0, 
    2339 #if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0 
    2340                indent, 
    2341                stat.rx_raw_jitter.min / 1000.0, 
    2342                stat.rx_raw_jitter.mean / 1000.0, 
    2343                stat.rx_raw_jitter.max / 1000.0, 
    2344                stat.rx_raw_jitter.last / 1000.0, 
    2345                pj_math_stat_get_stddev(&stat.rx_raw_jitter) / 1000.0, 
    2346 #endif 
    2347 #if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0 
    2348                indent, 
    2349                stat.rx_ipdv.min / 1000.0, 
    2350                stat.rx_ipdv.mean / 1000.0, 
    2351                stat.rx_ipdv.max / 1000.0, 
    2352                stat.rx_ipdv.last / 1000.0, 
    2353                pj_math_stat_get_stddev(&stat.rx_ipdv) / 1000.0, 
    2354 #endif 
    2355                "" 
    2356                ); 
    2357  
    2358         if (len < 1 || len > end-p) { 
    2359             *p = '\0'; 
    2360             return; 
    2361         } 
    2362  
    2363         p += len; 
    2364         *p++ = '\n'; 
    2365         *p = '\0'; 
    2366          
    2367         if (stat.tx.update_cnt == 0) 
    2368             strcpy(last_update, "never"); 
    2369         else { 
    2370             pj_gettimeofday(&now); 
    2371             PJ_TIME_VAL_SUB(now, stat.tx.update); 
    2372             sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", 
    2373                     now.sec / 3600, 
    2374                     (now.sec % 3600) / 60, 
    2375                     now.sec % 60, 
    2376                     now.msec); 
    2377         } 
    2378  
    2379         len = pj_ansi_snprintf(p, end-p, 
    2380                "%s     TX pt=%d, ptime=%dms, stat last update: %s\n" 
    2381                "%s        total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n" 
    2382                "%s        pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n" 
    2383                "%s              (msec)    min     avg     max     last    dev \n" 
    2384                "%s        loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n" 
    2385                "%s        jitter     : %7.3f %7.3f %7.3f %7.3f %7.3f%s", 
    2386                indent, 
    2387                info.stream_info[i].tx_pt, 
    2388                info.stream_info[i].param->info.frm_ptime * 
    2389                 info.stream_info[i].param->setting.frm_per_pkt, 
    2390                last_update, 
    2391  
    2392                indent, 
    2393                good_number(packets, stat.tx.pkt), 
    2394                good_number(bytes, stat.tx.bytes), 
    2395                good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40), 
    2396                good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))), 
    2397                good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.tx.bytes + stat.tx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))), 
    2398  
    2399                indent, 
    2400                stat.tx.loss, 
    2401                stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss), 
    2402                stat.tx.dup,  
    2403                stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss), 
    2404                stat.tx.reorder,  
    2405                stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss), 
    2406  
    2407                indent, indent, 
    2408                stat.tx.loss_period.min / 1000.0,  
    2409                stat.tx.loss_period.mean / 1000.0,  
    2410                stat.tx.loss_period.max / 1000.0, 
    2411                stat.tx.loss_period.last / 1000.0, 
    2412                pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0, 
    2413                indent, 
    2414                stat.tx.jitter.min / 1000.0, 
    2415                stat.tx.jitter.mean / 1000.0, 
    2416                stat.tx.jitter.max / 1000.0, 
    2417                stat.tx.jitter.last / 1000.0, 
    2418                pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0, 
    2419                "" 
    2420                ); 
    2421  
    2422         if (len < 1 || len > end-p) { 
    2423             *p = '\0'; 
    2424             return; 
    2425         } 
    2426  
    2427         p += len; 
    2428         *p++ = '\n'; 
    2429         *p = '\0'; 
    2430  
    2431         len = pj_ansi_snprintf(p, end-p, 
    2432                "%s    RTT msec       : %7.3f %7.3f %7.3f %7.3f %7.3f",  
    2433                indent, 
    2434                stat.rtt.min / 1000.0, 
    2435                stat.rtt.mean / 1000.0, 
    2436                stat.rtt.max / 1000.0, 
    2437                stat.rtt.last / 1000.0, 
    2438                pj_math_stat_get_stddev(&stat.rtt) / 1000.0 
    2439                ); 
    2440         if (len < 1 || len > end-p) { 
    2441             *p = '\0'; 
    2442             return; 
    2443         } 
    2444  
    2445         p += len; 
    2446         *p++ = '\n'; 
    2447         *p = '\0'; 
    2448  
    2449 #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0) 
    2450 #   define SAMPLES_TO_USEC(usec, samples, clock_rate) \ 
    2451         do { \ 
    2452             if (samples <= 4294) \ 
    2453                 usec = samples * 1000000 / clock_rate; \ 
    2454             else { \ 
    2455                 usec = samples * 1000 / clock_rate; \ 
    2456                 usec *= 1000; \ 
    2457             } \ 
    2458         } while(0) 
    2459  
    2460 #   define PRINT_VOIP_MTC_VAL(s, v) \ 
    2461         if (v == 127) \ 
    2462             sprintf(s, "(na)"); \ 
    2463         else \ 
    2464             sprintf(s, "%d", v) 
    2465  
    2466 #   define VALIDATE_PRINT_BUF() \ 
    2467         if (len < 1 || len > end-p) { *p = '\0'; return; } \ 
    2468         p += len; *p++ = '\n'; *p = '\0' 
    2469  
    2470  
    2471         do { 
    2472             char loss[16], dup[16]; 
    2473             char jitter[80]; 
    2474             char toh[80]; 
    2475             char plc[16], jba[16], jbr[16]; 
    2476             char signal_lvl[16], noise_lvl[16], rerl[16]; 
    2477             char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16]; 
    2478             pjmedia_rtcp_xr_stat xr_stat; 
    2479             unsigned clock_rate; 
    2480  
    2481             if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=  
    2482                 PJ_SUCCESS) 
    2483             { 
    2484                 break; 
    2485             } 
    2486  
    2487             clock_rate = info.stream_info[i].fmt.clock_rate; 
    2488  
    2489             len = pj_ansi_snprintf(p, end-p, "\n%s  Extended reports:", indent); 
    2490             VALIDATE_PRINT_BUF(); 
    2491  
    2492             /* Statistics Summary */ 
    2493             len = pj_ansi_snprintf(p, end-p, "%s   Statistics Summary", indent); 
    2494             VALIDATE_PRINT_BUF(); 
    2495  
    2496             if (xr_stat.rx.stat_sum.l) 
    2497                 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost); 
    2498             else 
    2499                 sprintf(loss, "(na)"); 
    2500  
    2501             if (xr_stat.rx.stat_sum.d) 
    2502                 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup); 
    2503             else 
    2504                 sprintf(dup, "(na)"); 
    2505  
    2506             if (xr_stat.rx.stat_sum.j) { 
    2507                 unsigned jmin, jmax, jmean, jdev; 
    2508  
    2509                 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,  
    2510                                 clock_rate); 
    2511                 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,  
    2512                                 clock_rate); 
    2513                 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,  
    2514                                 clock_rate); 
    2515                 SAMPLES_TO_USEC(jdev,  
    2516                                pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter), 
    2517                                clock_rate); 
    2518                 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",  
    2519                         jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0); 
    2520             } else 
    2521                 sprintf(jitter, "(report not available)"); 
    2522  
    2523             if (xr_stat.rx.stat_sum.t) { 
    2524                 sprintf(toh, "%11d %11d %11d %11d",  
    2525                         xr_stat.rx.stat_sum.toh.min, 
    2526                         xr_stat.rx.stat_sum.toh.mean, 
    2527                         xr_stat.rx.stat_sum.toh.max, 
    2528                         pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh)); 
    2529             } else 
    2530                 sprintf(toh, "(report not available)"); 
    2531  
    2532             if (xr_stat.rx.stat_sum.update.sec == 0) 
    2533                 strcpy(last_update, "never"); 
    2534             else { 
    2535                 pj_gettimeofday(&now); 
    2536                 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update); 
    2537                 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", 
    2538                         now.sec / 3600, 
    2539                         (now.sec % 3600) / 60, 
    2540                         now.sec % 60, 
    2541                         now.msec); 
    2542             } 
    2543  
    2544             len = pj_ansi_snprintf(p, end-p,  
    2545                     "%s     RX last update: %s\n" 
    2546                     "%s        begin seq=%d, end seq=%d\n" 
    2547                     "%s        pkt loss=%s, dup=%s\n" 
    2548                     "%s              (msec)    min     avg     max     dev\n" 
    2549                     "%s        jitter     : %s\n" 
    2550                     "%s        toh        : %s", 
    2551                     indent, last_update, 
    2552                     indent, 
    2553                     xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq, 
    2554                     indent, loss, dup, 
    2555                     indent,  
    2556                     indent, jitter, 
    2557                     indent, toh 
    2558                     ); 
    2559             VALIDATE_PRINT_BUF(); 
    2560  
    2561             if (xr_stat.tx.stat_sum.l) 
    2562                 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost); 
    2563             else 
    2564                 sprintf(loss, "(na)"); 
    2565  
    2566             if (xr_stat.tx.stat_sum.d) 
    2567                 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup); 
    2568             else 
    2569                 sprintf(dup, "(na)"); 
    2570  
    2571             if (xr_stat.tx.stat_sum.j) { 
    2572                 unsigned jmin, jmax, jmean, jdev; 
    2573  
    2574                 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,  
    2575                                 clock_rate); 
    2576                 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,  
    2577                                 clock_rate); 
    2578                 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,  
    2579                                 clock_rate); 
    2580                 SAMPLES_TO_USEC(jdev,  
    2581                                pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter), 
    2582                                clock_rate); 
    2583                 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",  
    2584                         jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0); 
    2585             } else 
    2586                 sprintf(jitter, "(report not available)"); 
    2587  
    2588             if (xr_stat.tx.stat_sum.t) { 
    2589                 sprintf(toh, "%11d %11d %11d %11d",  
    2590                         xr_stat.tx.stat_sum.toh.min, 
    2591                         xr_stat.tx.stat_sum.toh.mean, 
    2592                         xr_stat.tx.stat_sum.toh.max, 
    2593                         pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh)); 
    2594             } else 
    2595                 sprintf(toh,    "(report not available)"); 
    2596  
    2597             if (xr_stat.tx.stat_sum.update.sec == 0) 
    2598                 strcpy(last_update, "never"); 
    2599             else { 
    2600                 pj_gettimeofday(&now); 
    2601                 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update); 
    2602                 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", 
    2603                         now.sec / 3600, 
    2604                         (now.sec % 3600) / 60, 
    2605                         now.sec % 60, 
    2606                         now.msec); 
    2607             } 
    2608  
    2609             len = pj_ansi_snprintf(p, end-p,  
    2610                     "%s     TX last update: %s\n" 
    2611                     "%s        begin seq=%d, end seq=%d\n" 
    2612                     "%s        pkt loss=%s, dup=%s\n" 
    2613                     "%s              (msec)    min     avg     max     dev\n" 
    2614                     "%s        jitter     : %s\n" 
    2615                     "%s        toh        : %s", 
    2616                     indent, last_update, 
    2617                     indent, 
    2618                     xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq, 
    2619                     indent, loss, dup, 
    2620                     indent, 
    2621                     indent, jitter, 
    2622                     indent, toh 
    2623                     ); 
    2624             VALIDATE_PRINT_BUF(); 
    2625  
    2626  
    2627             /* VoIP Metrics */ 
    2628             len = pj_ansi_snprintf(p, end-p, "%s   VoIP Metrics", indent); 
    2629             VALIDATE_PRINT_BUF(); 
    2630  
    2631             PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl); 
    2632             PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl); 
    2633             PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl); 
    2634             PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor); 
    2635             PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor); 
    2636             PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq); 
    2637             PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq); 
    2638  
    2639             switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) { 
    2640                 case PJMEDIA_RTCP_XR_PLC_DIS: 
    2641                     sprintf(plc, "DISABLED"); 
    2642                     break; 
    2643                 case PJMEDIA_RTCP_XR_PLC_ENH: 
    2644                     sprintf(plc, "ENHANCED"); 
    2645                     break; 
    2646                 case PJMEDIA_RTCP_XR_PLC_STD: 
    2647                     sprintf(plc, "STANDARD"); 
    2648                     break; 
    2649                 case PJMEDIA_RTCP_XR_PLC_UNK: 
    2650                 default: 
    2651                     sprintf(plc, "UNKNOWN"); 
    2652                     break; 
    2653             } 
    2654  
    2655             switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) { 
    2656                 case PJMEDIA_RTCP_XR_JB_FIXED: 
    2657                     sprintf(jba, "FIXED"); 
    2658                     break; 
    2659                 case PJMEDIA_RTCP_XR_JB_ADAPTIVE: 
    2660                     sprintf(jba, "ADAPTIVE"); 
    2661                     break; 
    2662                 default: 
    2663                     sprintf(jba, "UNKNOWN"); 
    2664                     break; 
    2665             } 
    2666  
    2667             sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F); 
    2668  
    2669             if (xr_stat.rx.voip_mtc.update.sec == 0) 
    2670                 strcpy(last_update, "never"); 
    2671             else { 
    2672                 pj_gettimeofday(&now); 
    2673                 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update); 
    2674                 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", 
    2675                         now.sec / 3600, 
    2676                         (now.sec % 3600) / 60, 
    2677                         now.sec % 60, 
    2678                         now.msec); 
    2679             } 
    2680  
    2681             len = pj_ansi_snprintf(p, end-p,  
    2682                     "%s     RX last update: %s\n" 
    2683                     "%s        packets    : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n" 
    2684                     "%s        burst      : density=%d (%.2f%%), duration=%d%s\n" 
    2685                     "%s        gap        : density=%d (%.2f%%), duration=%d%s\n" 
    2686                     "%s        delay      : round trip=%d%s, end system=%d%s\n" 
    2687                     "%s        level      : signal=%s%s, noise=%s%s, RERL=%s%s\n" 
    2688                     "%s        quality    : R factor=%s, ext R factor=%s\n" 
    2689                     "%s                     MOS LQ=%s, MOS CQ=%s\n" 
    2690                     "%s        config     : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n" 
    2691                     "%s        JB delay   : cur=%d%s, max=%d%s, abs max=%d%s", 
    2692                     indent, 
    2693                     last_update, 
    2694                     /* packets */ 
    2695                     indent, 
    2696                     xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256, 
    2697                     xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256, 
    2698                     /* burst */ 
    2699                     indent, 
    2700                     xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256, 
    2701                     xr_stat.rx.voip_mtc.burst_dur, "ms", 
    2702                     /* gap */ 
    2703                     indent, 
    2704                     xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256, 
    2705                     xr_stat.rx.voip_mtc.gap_dur, "ms", 
    2706                     /* delay */ 
    2707                     indent, 
    2708                     xr_stat.rx.voip_mtc.rnd_trip_delay, "ms", 
    2709                     xr_stat.rx.voip_mtc.end_sys_delay, "ms", 
    2710                     /* level */ 
    2711                     indent, 
    2712                     signal_lvl, "dB", 
    2713                     noise_lvl, "dB", 
    2714                     rerl, "", 
    2715                     /* quality */ 
    2716                     indent, 
    2717                     r_factor, ext_r_factor,  
    2718                     indent, 
    2719                     mos_lq, mos_cq, 
    2720                     /* config */ 
    2721                     indent, 
    2722                     plc, jba, jbr, xr_stat.rx.voip_mtc.gmin, 
    2723                     /* JB delay */ 
    2724                     indent, 
    2725                     xr_stat.rx.voip_mtc.jb_nom, "ms", 
    2726                     xr_stat.rx.voip_mtc.jb_max, "ms", 
    2727                     xr_stat.rx.voip_mtc.jb_abs_max, "ms" 
    2728                     ); 
    2729             VALIDATE_PRINT_BUF(); 
    2730  
    2731             PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl); 
    2732             PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl); 
    2733             PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl); 
    2734             PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor); 
    2735             PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor); 
    2736             PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq); 
    2737             PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq); 
    2738  
    2739             switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) { 
    2740                 case PJMEDIA_RTCP_XR_PLC_DIS: 
    2741                     sprintf(plc, "DISABLED"); 
    2742                     break; 
    2743                 case PJMEDIA_RTCP_XR_PLC_ENH: 
    2744                     sprintf(plc, "ENHANCED"); 
    2745                     break; 
    2746                 case PJMEDIA_RTCP_XR_PLC_STD: 
    2747                     sprintf(plc, "STANDARD"); 
    2748                     break; 
    2749                 case PJMEDIA_RTCP_XR_PLC_UNK: 
    2750                 default: 
    2751                     sprintf(plc, "unknown"); 
    2752                     break; 
    2753             } 
    2754  
    2755             switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) { 
    2756                 case PJMEDIA_RTCP_XR_JB_FIXED: 
    2757                     sprintf(jba, "FIXED"); 
    2758                     break; 
    2759                 case PJMEDIA_RTCP_XR_JB_ADAPTIVE: 
    2760                     sprintf(jba, "ADAPTIVE"); 
    2761                     break; 
    2762                 default: 
    2763                     sprintf(jba, "unknown"); 
    2764                     break; 
    2765             } 
    2766  
    2767             sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F); 
    2768  
    2769             if (xr_stat.tx.voip_mtc.update.sec == 0) 
    2770                 strcpy(last_update, "never"); 
    2771             else { 
    2772                 pj_gettimeofday(&now); 
    2773                 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update); 
    2774                 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", 
    2775                         now.sec / 3600, 
    2776                         (now.sec % 3600) / 60, 
    2777                         now.sec % 60, 
    2778                         now.msec); 
    2779             } 
    2780  
    2781             len = pj_ansi_snprintf(p, end-p,  
    2782                     "%s     TX last update: %s\n" 
    2783                     "%s        packets    : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n" 
    2784                     "%s        burst      : density=%d (%.2f%%), duration=%d%s\n" 
    2785                     "%s        gap        : density=%d (%.2f%%), duration=%d%s\n" 
    2786                     "%s        delay      : round trip=%d%s, end system=%d%s\n" 
    2787                     "%s        level      : signal=%s%s, noise=%s%s, RERL=%s%s\n" 
    2788                     "%s        quality    : R factor=%s, ext R factor=%s\n" 
    2789                     "%s                     MOS LQ=%s, MOS CQ=%s\n" 
    2790                     "%s        config     : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n" 
    2791                     "%s        JB delay   : cur=%d%s, max=%d%s, abs max=%d%s", 
    2792                     indent, 
    2793                     last_update, 
    2794                     /* pakcets */ 
    2795                     indent, 
    2796                     xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256, 
    2797                     xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256, 
    2798                     /* burst */ 
    2799                     indent, 
    2800                     xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256, 
    2801                     xr_stat.tx.voip_mtc.burst_dur, "ms", 
    2802                     /* gap */ 
    2803                     indent, 
    2804                     xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256, 
    2805                     xr_stat.tx.voip_mtc.gap_dur, "ms", 
    2806                     /* delay */ 
    2807                     indent, 
    2808                     xr_stat.tx.voip_mtc.rnd_trip_delay, "ms", 
    2809                     xr_stat.tx.voip_mtc.end_sys_delay, "ms", 
    2810                     /* level */ 
    2811                     indent, 
    2812                     signal_lvl, "dB", 
    2813                     noise_lvl, "dB", 
    2814                     rerl, "", 
    2815                     /* quality */ 
    2816                     indent, 
    2817                     r_factor, ext_r_factor,  
    2818                     indent, 
    2819                     mos_lq, mos_cq, 
    2820                     /* config */ 
    2821                     indent, 
    2822                     plc, jba, jbr, xr_stat.tx.voip_mtc.gmin, 
    2823                     /* JB delay */ 
    2824                     indent, 
    2825                     xr_stat.tx.voip_mtc.jb_nom, "ms", 
    2826                     xr_stat.tx.voip_mtc.jb_max, "ms", 
    2827                     xr_stat.tx.voip_mtc.jb_abs_max, "ms" 
    2828                     ); 
    2829             VALIDATE_PRINT_BUF(); 
    2830  
    2831  
    2832             /* RTT delay (by receiver side) */ 
    2833             len = pj_ansi_snprintf(p, end-p,  
    2834                     "%s   RTT (from recv)      min     avg     max     last    dev", 
    2835                     indent); 
    2836             VALIDATE_PRINT_BUF(); 
    2837             len = pj_ansi_snprintf(p, end-p,  
    2838                     "%s     RTT msec      : %7.3f %7.3f %7.3f %7.3f %7.3f",  
    2839                     indent, 
    2840                     xr_stat.rtt.min / 1000.0, 
    2841                     xr_stat.rtt.mean / 1000.0, 
    2842                     xr_stat.rtt.max / 1000.0, 
    2843                     xr_stat.rtt.last / 1000.0, 
    2844                     pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0 
    2845                    ); 
    2846             VALIDATE_PRINT_BUF(); 
    2847         } while(0); 
    2848 #endif 
    2849  
    2850     } 
    2851 } 
    2852  
    2853  
    2854 /* Print call info */ 
    2855 void print_call(const char *title, 
    2856                 int call_id,  
    2857                 char *buf, pj_size_t size) 
    2858 { 
    2859     int len; 
    2860     pjsip_inv_session *inv = pjsua_var.calls[call_id].inv; 
    2861     pjsip_dialog *dlg = inv->dlg; 
    2862     char userinfo[128]; 
    2863  
    2864     /* Dump invite sesion info. */ 
    2865  
    2866     len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo)); 
    2867     if (len < 0) 
    2868         pj_ansi_strcpy(userinfo, "<--uri too long-->"); 
    2869     else 
    2870         userinfo[len] = '\0'; 
    2871      
    2872     len = pj_ansi_snprintf(buf, size, "%s[%s] %s", 
    2873                            title, 
    2874                            pjsip_inv_state_name(inv->state), 
    2875                            userinfo); 
    2876     if (len < 1 || len >= (int)size) { 
    2877         pj_ansi_strcpy(buf, "<--uri too long-->"); 
    2878         len = 18; 
    2879     } else 
    2880         buf[len] = '\0'; 
    2881 } 
    2882  
    2883  
    2884 /* 
    2885  * Dump call and media statistics to string. 
    2886  */ 
    2887 PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,  
    2888                                      pj_bool_t with_media,  
    2889                                      char *buffer,  
    2890                                      unsigned maxlen, 
    2891                                      const char *indent) 
    2892 { 
    2893     pjsua_call *call; 
    2894     pjsip_dialog *dlg; 
    2895     pj_time_val duration, res_delay, con_delay; 
    2896     char tmp[128]; 
    2897     char *p, *end; 
    2898     pj_status_t status; 
    2899     int len; 
    2900     pjmedia_transport_info tp_info; 
    2901  
    2902     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
    2903                      PJ_EINVAL); 
    2904  
    2905     status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg); 
    2906     if (status != PJ_SUCCESS) 
    2907         return status; 
    2908  
    2909     *buffer = '\0'; 
    2910     p = buffer; 
    2911     end = buffer + maxlen; 
    2912     len = 0; 
    2913  
    2914     print_call(indent, call_id, tmp, sizeof(tmp)); 
    2915      
    2916     len = pj_ansi_strlen(tmp); 
    2917     pj_ansi_strcpy(buffer, tmp); 
    2918  
    2919     p += len; 
    2920     *p++ = '\r'; 
    2921     *p++ = '\n'; 
    2922  
    2923     /* Calculate call duration */ 
    2924     if (call->conn_time.sec != 0) { 
    2925         pj_gettimeofday(&duration); 
    2926         PJ_TIME_VAL_SUB(duration, call->conn_time); 
    2927         con_delay = call->conn_time; 
    2928         PJ_TIME_VAL_SUB(con_delay, call->start_time); 
    2929     } else { 
    2930         duration.sec = duration.msec = 0; 
    2931         con_delay.sec = con_delay.msec = 0; 
    2932     } 
    2933  
    2934     /* Calculate first response delay */ 
    2935     if (call->res_time.sec != 0) { 
    2936         res_delay = call->res_time; 
    2937         PJ_TIME_VAL_SUB(res_delay, call->start_time); 
    2938     } else { 
    2939         res_delay.sec = res_delay.msec = 0; 
    2940     } 
    2941  
    2942     /* Print duration */ 
    2943     len = pj_ansi_snprintf(p, end-p,  
    2944                            "%s  Call time: %02dh:%02dm:%02ds, " 
    2945                            "1st res in %d ms, conn in %dms", 
    2946                            indent, 
    2947                            (int)(duration.sec / 3600), 
    2948                            (int)((duration.sec % 3600)/60), 
    2949                            (int)(duration.sec % 60), 
    2950                            (int)PJ_TIME_VAL_MSEC(res_delay),  
    2951                            (int)PJ_TIME_VAL_MSEC(con_delay)); 
    2952      
    2953     if (len > 0 && len < end-p) { 
    2954         p += len; 
    2955         *p++ = '\n'; 
    2956         *p = '\0'; 
    2957     } 
    2958  
    2959     /* Get and ICE SRTP status */ 
    2960     pjmedia_transport_info_init(&tp_info); 
    2961     pjmedia_transport_get_info(call->med_tp, &tp_info); 
    2962     if (tp_info.specific_info_cnt > 0) { 
    2963         unsigned i; 
    2964         for (i = 0; i < tp_info.specific_info_cnt; ++i) { 
    2965             if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)  
    2966             { 
    2967                 pjmedia_srtp_info *srtp_info =  
    2968                             (pjmedia_srtp_info*) tp_info.spc_info[i].buffer; 
    2969  
    2970                 len = pj_ansi_snprintf(p, end-p,  
    2971                                        "%s  SRTP status: %s Crypto-suite: %s", 
    2972                                        indent, 
    2973                                        (srtp_info->active?"Active":"Not active"), 
    2974                                        srtp_info->tx_policy.name.ptr); 
    2975                 if (len > 0 && len < end-p) { 
    2976                     p += len; 
    2977                     *p++ = '\n'; 
    2978                     *p = '\0'; 
    2979                 } 
    2980             } else if (tp_info.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) { 
    2981                 const pjmedia_ice_transport_info *ii; 
    2982  
    2983                 ii = (const pjmedia_ice_transport_info*)  
    2984                      tp_info.spc_info[i].buffer; 
    2985  
    2986                 len = pj_ansi_snprintf(p, end-p,  
    2987                                        "%s  ICE role: %s, state: %s, comp_cnt: %u", 
    2988                                        indent, 
    2989                                        pj_ice_sess_role_name(ii->role), 
    2990                                        pj_ice_strans_state_name(ii->sess_state), 
    2991                                        ii->comp_cnt); 
    2992                 if (len > 0 && len < end-p) { 
    2993                     p += len; 
    2994                     *p++ = '\n'; 
    2995                     *p = '\0'; 
    2996                 } 
    2997             } 
    2998         } 
    2999     } 
    3000  
    3001     /* Dump session statistics */ 
    3002     if (with_media && call->session) 
    3003         dump_media_session(indent, p, end-p, call); 
    3004  
    3005     pjsip_dlg_dec_lock(dlg); 
    3006  
    3007     return PJ_SUCCESS; 
    3008 } 
    30092313 
    30102314/* Proto */ 
     
    30762380    const pj_str_t STR_UPDATE = {"UPDATE", 6}; 
    30772381    const pjmedia_sdp_session *local_sdp = NULL, *new_sdp; 
    3078     const pjmedia_sdp_media *ref_m; 
    3079     pjmedia_sdp_media *m; 
    3080     unsigned i, codec_cnt = 0; 
     2382    unsigned i; 
    30812383    pj_bool_t rem_can_update; 
     2384    pj_bool_t need_lock_codec = PJ_FALSE; 
    30822385    pjsip_tx_data *tdata; 
    30832386    pj_status_t status; 
     
    31112414        return PJMEDIA_SDP_EINVER; 
    31122415 
    3113     /* Verify if media is deactivated */ 
    3114     if (call->media_st == PJSUA_CALL_MEDIA_NONE || 
    3115         call->media_st == PJSUA_CALL_MEDIA_ERROR || 
    3116         call->media_dir == PJMEDIA_DIR_NONE) 
    3117     { 
    3118         return PJ_EINVALIDOP; 
    3119     } 
    3120  
    31212416    PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec..")); 
    31222417 
     
    31262421     */ 
    31272422    new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp); 
    3128     m = new_sdp->media[call->audio_idx]; 
    3129     ref_m = local_sdp->media[call->audio_idx]; 
    3130     pj_assert(ref_m->desc.port); 
    3131     codec_cnt = 0; 
    3132     i = 0; 
    3133     while (i < m->desc.fmt_count) { 
    3134         pjmedia_sdp_attr *a; 
    3135         pj_str_t *fmt = &m->desc.fmt[i]; 
    3136  
    3137         if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) { 
    3138             ++i; 
     2423 
     2424    for (i = 0; i < call->med_cnt; ++i) { 
     2425        unsigned j = 0, codec_cnt = 0; 
     2426        const pjmedia_sdp_media *ref_m; 
     2427        pjmedia_sdp_media *m; 
     2428        pjsua_call_media *call_med = &call->media[i]; 
     2429 
     2430        /* Verify if media is deactivated */ 
     2431        if (call_med->state == PJSUA_CALL_MEDIA_NONE || 
     2432            call_med->state == PJSUA_CALL_MEDIA_ERROR || 
     2433            call_med->dir == PJMEDIA_DIR_NONE) 
     2434        { 
    31392435            continue; 
    31402436        } 
    31412437 
    3142         /* Remove format */ 
    3143         a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt); 
    3144         if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); 
    3145         a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt); 
    3146         if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); 
    3147         pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]), 
    3148                        m->desc.fmt_count, i); 
    3149         --m->desc.fmt_count; 
     2438        ref_m = local_sdp->media[i]; 
     2439        m = new_sdp->media[i]; 
     2440 
     2441        /* Verify that media must be active. */ 
     2442        pj_assert(ref_m->desc.port); 
     2443 
     2444        while (j < m->desc.fmt_count) { 
     2445            pjmedia_sdp_attr *a; 
     2446            pj_str_t *fmt = &m->desc.fmt[j]; 
     2447 
     2448            if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) { 
     2449                ++j; 
     2450                continue; 
     2451            } 
     2452 
     2453            /* Remove format */ 
     2454            a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt); 
     2455            if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); 
     2456            a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt); 
     2457            if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); 
     2458            pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]), 
     2459                           m->desc.fmt_count, j); 
     2460            --m->desc.fmt_count; 
     2461        } 
     2462         
     2463        need_lock_codec |= (ref_m->desc.fmt_count > m->desc.fmt_count); 
    31502464    } 
    31512465 
     
    31542468     * increase the SDP version (should not happen!). 
    31552469     */ 
    3156     if (ref_m->desc.fmt_count == m->desc.fmt_count) 
     2470    if (!need_lock_codec) 
    31572471        return PJ_SUCCESS; 
    31582472 
     
    32022516    pjsip_inv_session *inv = call->inv; 
    32032517    const pjmedia_sdp_session *local_sdp, *remote_sdp; 
    3204     const pjmedia_sdp_media *rem_m, *loc_m; 
    3205     unsigned codec_cnt=0, i; 
    32062518    pj_time_val delay = {0, 0}; 
    32072519    const pj_str_t st_update = {"UPDATE", 6}; 
     2520    unsigned i; 
     2521    pj_bool_t has_mult_fmt = PJ_FALSE; 
    32082522    pj_status_t status; 
    32092523 
     
    32172531    /* Skip this if we are the answerer */ 
    32182532    if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) { 
    3219         return PJ_SUCCESS; 
    3220     } 
    3221  
    3222     /* Skip this if the media is inactive or error */ 
    3223     if (call->media_st == PJSUA_CALL_MEDIA_NONE || 
    3224         call->media_st == PJSUA_CALL_MEDIA_ERROR || 
    3225         call->media_dir == PJMEDIA_DIR_NONE) 
    3226     { 
    32272533        return PJ_SUCCESS; 
    32282534    } 
     
    32462552        return status; 
    32472553 
    3248     PJ_ASSERT_RETURN(call->audio_idx>=0 && 
    3249                      call->audio_idx < (int)remote_sdp->media_count, 
    3250                      PJ_EINVALIDOP); 
    3251  
    3252     rem_m = remote_sdp->media[call->audio_idx]; 
    3253     loc_m = local_sdp->media[call->audio_idx]; 
    3254  
    3255     /* Verify that media must be active. */ 
    3256     pj_assert(loc_m->desc.port && rem_m->desc.port); 
    3257  
    3258     /* Count the formats in the answer. */ 
    3259     if (rem_m->desc.fmt_count==1) { 
    3260         codec_cnt = 1; 
    3261     } else { 
    3262         for (i=0; i<rem_m->desc.fmt_count && codec_cnt <= 1; ++i) { 
    3263             if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[i])) 
    3264                 ++codec_cnt; 
    3265         } 
    3266     } 
    3267     if (codec_cnt <= 1) { 
    3268         /* Answer contains single codec. */ 
    3269         call->lock_codec.retry_cnt = 0; 
     2554    /* Find multiple codecs answer in all media */ 
     2555    for (i = 0; i < call->med_cnt; ++i) { 
     2556        pjsua_call_media *call_med = &call->media[i]; 
     2557        const pjmedia_sdp_media *rem_m, *loc_m; 
     2558        unsigned codec_cnt = 0; 
     2559 
     2560        /* Skip this if the media is inactive or error */ 
     2561        if (call_med->state == PJSUA_CALL_MEDIA_NONE || 
     2562            call_med->state == PJSUA_CALL_MEDIA_ERROR || 
     2563            call_med->dir == PJMEDIA_DIR_NONE) 
     2564        { 
     2565            continue; 
     2566        } 
     2567 
     2568        /* Remote may answer with less media lines. */ 
     2569        if (i >= remote_sdp->media_count) 
     2570            continue; 
     2571 
     2572        rem_m = remote_sdp->media[i]; 
     2573        loc_m = local_sdp->media[i]; 
     2574 
     2575        /* Verify that media must be active. */ 
     2576        pj_assert(loc_m->desc.port && rem_m->desc.port); 
     2577 
     2578        /* Count the formats in the answer. */ 
     2579        if (rem_m->desc.fmt_count==1) { 
     2580            codec_cnt = 1; 
     2581        } else { 
     2582            unsigned j; 
     2583            for (j=0; j<rem_m->desc.fmt_count && codec_cnt <= 1; ++j) { 
     2584                if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j])) 
     2585                    ++codec_cnt; 
     2586            } 
     2587        } 
     2588 
     2589        if (codec_cnt > 1) { 
     2590            has_mult_fmt = PJ_TRUE; 
     2591            break; 
     2592        } 
     2593    } 
     2594 
     2595    /* Each media in the answer already contains single codec. */ 
     2596    if (!has_mult_fmt) { 
     2597        call->lock_codec.retry_cnt = 0; 
    32702598        return PJ_SUCCESS; 
    32712599    } 
     
    35392867 
    35402868    /* Add SDP in 488 status */ 
    3541     if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&  
     2869#if DISABLED_FOR_TICKET_1185 
     2870    if (call && call->tp && tdata->msg->type==PJSIP_RESPONSE_MSG && 
    35422871        code==PJSIP_SC_NOT_ACCEPTABLE_HERE)  
    35432872    { 
     
    35542883        } 
    35552884    } 
     2885#endif 
    35562886 
    35572887    pjsip_inv_send_msg(inv, tdata); 
     
    36612991                                           pjmedia_sdp_session *sdp) 
    36622992{ 
    3663     pjmedia_sdp_media *m; 
     2993    unsigned mi; 
    36642994 
    36652995    /* Call-hold is done by set the media direction to 'sendonly'  
     
    36692999     */ 
    36703000    /* http://trac.pjsip.org/repos/ticket/880  
    3671        if (call->media_dir != PJMEDIA_DIR_ENCODING) { 
     3001       if (call->dir != PJMEDIA_DIR_ENCODING) { 
    36723002     */ 
    36733003    /* https://trac.pjsip.org/repos/ticket/1142: 
     
    36753005     */ 
    36763006 
    3677     m = sdp->media[call->audio_idx]; 
    3678  
    3679     if (call->call_hold_type == PJSUA_CALL_HOLD_TYPE_RFC2543) { 
    3680         pjmedia_sdp_conn *conn; 
    3681         pjmedia_sdp_attr *attr; 
    3682  
    3683         /* Get SDP media connection line */ 
    3684         conn = m->conn; 
    3685         if (!conn) 
    3686             conn = sdp->conn; 
    3687  
    3688         /* Modify address */ 
    3689         conn->addr = pj_str("0.0.0.0"); 
    3690  
    3691         /* Remove existing directions attributes */ 
    3692         pjmedia_sdp_media_remove_all_attr(m, "sendrecv"); 
    3693         pjmedia_sdp_media_remove_all_attr(m, "sendonly"); 
    3694         pjmedia_sdp_media_remove_all_attr(m, "recvonly"); 
    3695         pjmedia_sdp_media_remove_all_attr(m, "inactive"); 
    3696  
    3697         /* Add inactive attribute */ 
    3698         attr = pjmedia_sdp_attr_create(pool, "inactive", NULL); 
    3699         pjmedia_sdp_media_add_attr(m, attr); 
    3700  
    3701  
    3702     } else { 
    3703         pjmedia_sdp_attr *attr; 
    3704  
    3705         /* Remove existing directions attributes */ 
    3706         pjmedia_sdp_media_remove_all_attr(m, "sendrecv"); 
    3707         pjmedia_sdp_media_remove_all_attr(m, "sendonly"); 
    3708         pjmedia_sdp_media_remove_all_attr(m, "recvonly"); 
    3709         pjmedia_sdp_media_remove_all_attr(m, "inactive"); 
    3710  
    3711         if (call->media_dir & PJMEDIA_DIR_ENCODING) { 
    3712             /* Add sendonly attribute */ 
    3713             attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL); 
    3714             pjmedia_sdp_media_add_attr(m, attr); 
    3715         } else { 
     3007    for (mi=0; mi<sdp->media_count; ++mi) { 
     3008        pjmedia_sdp_media *m = sdp->media[mi]; 
     3009 
     3010        if (call->call_hold_type == PJSUA_CALL_HOLD_TYPE_RFC2543) { 
     3011            pjmedia_sdp_conn *conn; 
     3012            pjmedia_sdp_attr *attr; 
     3013 
     3014            /* Get SDP media connection line */ 
     3015            conn = m->conn; 
     3016            if (!conn) 
     3017                conn = sdp->conn; 
     3018 
     3019            /* Modify address */ 
     3020            conn->addr = pj_str("0.0.0.0"); 
     3021 
     3022            /* Remove existing directions attributes */ 
     3023            pjmedia_sdp_media_remove_all_attr(m, "sendrecv"); 
     3024            pjmedia_sdp_media_remove_all_attr(m, "sendonly"); 
     3025            pjmedia_sdp_media_remove_all_attr(m, "recvonly"); 
     3026            pjmedia_sdp_media_remove_all_attr(m, "inactive"); 
     3027 
    37163028            /* Add inactive attribute */ 
    37173029            attr = pjmedia_sdp_attr_create(pool, "inactive", NULL); 
    37183030            pjmedia_sdp_media_add_attr(m, attr); 
     3031 
     3032 
     3033        } else { 
     3034            pjmedia_sdp_attr *attr; 
     3035 
     3036            /* Remove existing directions attributes */ 
     3037            pjmedia_sdp_media_remove_all_attr(m, "sendrecv"); 
     3038            pjmedia_sdp_media_remove_all_attr(m, "sendonly"); 
     3039            pjmedia_sdp_media_remove_all_attr(m, "recvonly"); 
     3040            pjmedia_sdp_media_remove_all_attr(m, "inactive"); 
     3041 
     3042            if (call->media[mi].dir & PJMEDIA_DIR_ENCODING) { 
     3043                /* Add sendonly attribute */ 
     3044                attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL); 
     3045                pjmedia_sdp_media_add_attr(m, attr); 
     3046            } else { 
     3047                /* Add inactive attribute */ 
     3048                attr = pjmedia_sdp_attr_create(pool, "inactive", NULL); 
     3049                pjmedia_sdp_media_add_attr(m, attr); 
     3050            } 
    37193051        } 
    37203052    } 
     
    37583090{ 
    37593091    pjsua_call *call; 
    3760     pjmedia_sdp_conn *conn = NULL; 
    37613092    pjmedia_sdp_session *answer; 
     3093    unsigned i; 
    37623094    pj_status_t status; 
    37633095 
     
    37653097 
    37663098    call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id]; 
    3767  
    3768     if (call->audio_idx < (int)offer->media_count) 
    3769         conn = offer->media[call->audio_idx]->conn; 
    3770  
    3771     if (!conn) 
    3772         conn = offer->conn; 
    37733099 
    37743100    /* Supply candidate answer */ 
     
    37853111    } 
    37863112 
     3113    /* Validate media count in the generated answer */ 
     3114    pj_assert(answer->media_count == offer->media_count); 
     3115 
    37873116    /* Check if offer's conn address is zero */ 
    3788     if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 || 
    3789         pj_strcmp2(&conn->addr, "0")==0) 
    3790     { 
    3791         /* Modify address */ 
    3792         answer->conn->addr = pj_str("0.0.0.0"); 
     3117    for (i = 0; i < answer->media_count; ++i) { 
     3118        pjmedia_sdp_conn *conn; 
     3119 
     3120        conn = offer->media[i]->conn; 
     3121        if (!conn) 
     3122            conn = offer->conn; 
     3123 
     3124        if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 || 
     3125            pj_strcmp2(&conn->addr, "0")==0) 
     3126        { 
     3127            pjmedia_sdp_conn *a_conn = answer->media[i]->conn; 
     3128 
     3129            /* Modify answer address */ 
     3130            if (a_conn) { 
     3131                a_conn->addr = pj_str("0.0.0.0"); 
     3132            } else if (answer->conn == NULL || 
     3133                       pj_strcmp2(&answer->conn->addr, "0.0.0.0") != 0) 
     3134            { 
     3135                a_conn = PJ_POOL_ZALLOC_T(call->inv->pool_prov, 
     3136                                          pjmedia_sdp_conn); 
     3137                a_conn->net_type = pj_str("IN"); 
     3138                a_conn->addr_type = pj_str("IP4"); 
     3139                a_conn->addr = pj_str("0.0.0.0"); 
     3140                answer->media[i]->conn = a_conn; 
     3141            } 
     3142        } 
    37933143    } 
    37943144 
Note: See TracChangeset for help on using the changeset viewer.