pjsip logo pjsip.org
Open source SIP stack and media stack for presence, im/instant messaging, and multimedia communication

HOME

SIP/media Features
High Performance SIP
Small Footprint SIP
Symbian Port

FAQ

Documentation

Licensing

Download

Development (Trac)

Projects using pjsip

Mailing List

Open Source Links


About: PJLIB, PJLIB-UTIL, PJSIP, and PJMEDIA are created by: Benny Prijono
<bennylp@pjsip.org>


 

Home --> Documentations --> PJMEDIA Reference

Samples: Remote Streaming

This example mainly demonstrates how to stream media file to remote peer using RTP.

This file is pjsip-apps/src/samples/streamutil.c

00001 /* $Id: streamutil.c 2408 2009-01-01 22:08:21Z bennylp $ */
00002 /* 
00003  * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
00004  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
00019  */
00020 
00021 
00033 #include <pjlib.h>
00034 #include <pjlib-util.h>
00035 #include <pjmedia.h>
00036 #include <pjmedia-codec.h>
00037 #include <pjmedia/transport_srtp.h>
00038 
00039 #include <stdlib.h>     /* atoi() */
00040 #include <stdio.h>
00041 
00042 #include "util.h"
00043 
00044 
00045 static const char *desc = 
00046  " streamutil                                                           \n"
00047  "                                                                      \n"
00048  " PURPOSE:                                                             \n"
00049  "  Demonstrate how to use pjmedia stream component to transmit/receive \n"
00050  "  RTP packets to/from sound device.                                   \n"
00051  "\n"
00052  "\n"
00053  " USAGE:                                                               \n"
00054  "  streamutil [options]                                                \n"
00055  "\n"
00056  "\n"
00057  " Options:\n"
00058  "  --codec=CODEC         Set the codec name.                           \n"
00059  "  --local-port=PORT     Set local RTP port (default=4000)             \n"
00060  "  --remote=IP:PORT      Set the remote peer. If this option is set,   \n"
00061  "                        the program will transmit RTP audio to the    \n"
00062  "                        specified address. (default: recv only)       \n"
00063  "  --play-file=WAV       Send audio from the WAV file instead of from  \n"
00064  "                        the sound device.                             \n"
00065  "  --record-file=WAV     Record incoming audio to WAV file instead of  \n"
00066  "                        playing it to sound device.                   \n"
00067  "  --send-recv           Set stream direction to bidirectional.        \n"
00068  "  --send-only           Set stream direction to send only             \n"
00069  "  --recv-only           Set stream direction to recv only (default)   \n"
00070 
00071 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00072  "  --use-srtp[=NAME]     Enable SRTP with crypto suite NAME            \n"
00073  "                        e.g: AES_CM_128_HMAC_SHA1_80 (default),       \n"
00074  "                             AES_CM_128_HMAC_SHA1_32                  \n"
00075  "                        Use this option along with the TX & RX keys,  \n"
00076  "                        formated of 60 hex digits (e.g: E148DA..)      \n"
00077  "  --srtp-tx-key         SRTP key for transmiting                      \n"
00078  "  --srtp-rx-key         SRTP key for receiving                        \n"
00079 #endif
00080 
00081  "\n"
00082 ;
00083 
00084 
00085 
00086 
00087 #define THIS_FILE       "stream.c"
00088 
00089 
00090 
00091 /* Prototype */
00092 static void print_stream_stat(pjmedia_stream *stream);
00093 
00094 /* Prototype for LIBSRTP utility in file datatypes.c */
00095 int hex_string_to_octet_string(char *raw, char *hex, int len);
00096 
00097 /* 
00098  * Register all codecs. 
00099  */
00100 static pj_status_t init_codecs(pjmedia_endpt *med_endpt)
00101 {
00102     pj_status_t status;
00103 
00104     /* To suppress warning about unused var when all codecs are disabled */
00105     PJ_UNUSED_ARG(status);
00106 
00107 #if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
00108     status = pjmedia_codec_g711_init(med_endpt);
00109     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00110 #endif
00111 
00112 #if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0
00113     status = pjmedia_codec_gsm_init(med_endpt);
00114     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00115 #endif
00116 
00117 #if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
00118     status = pjmedia_codec_speex_init(med_endpt, 0, -1, -1);
00119     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00120 #endif
00121 
00122 #if defined(PJMEDIA_HAS_G722_CODEC) && PJMEDIA_HAS_G722_CODEC!=0
00123     status = pjmedia_codec_g722_init(med_endpt);
00124     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00125 #endif
00126 
00127 #if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC!=0
00128     status = pjmedia_codec_l16_init(med_endpt, 0);
00129     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00130 #endif
00131 
00132     return PJ_SUCCESS;
00133 }
00134 
00135 
00136 /* 
00137  * Create stream based on the codec, dir, remote address, etc. 
00138  */
00139 static pj_status_t create_stream( pj_pool_t *pool,
00140                                   pjmedia_endpt *med_endpt,
00141                                   const pjmedia_codec_info *codec_info,
00142                                   pjmedia_dir dir,
00143                                   pj_uint16_t local_port,
00144                                   const pj_sockaddr_in *rem_addr,
00145 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00146                                   pj_bool_t use_srtp,
00147                                   const pj_str_t *crypto_suite,
00148                                   const pj_str_t *srtp_tx_key,
00149                                   const pj_str_t *srtp_rx_key,
00150 #endif
00151                                   pjmedia_stream **p_stream )
00152 {
00153     pjmedia_stream_info info;
00154     pjmedia_transport *transport = NULL;
00155     pj_status_t status;
00156 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00157     pjmedia_transport *srtp_tp = NULL;
00158 #endif
00159 
00160 
00161     /* Reset stream info. */
00162     pj_bzero(&info, sizeof(info));
00163 
00164 
00165     /* Initialize stream info formats */
00166     info.type = PJMEDIA_TYPE_AUDIO;
00167     info.dir = dir;
00168     pj_memcpy(&info.fmt, codec_info, sizeof(pjmedia_codec_info));
00169     info.tx_pt = codec_info->pt;
00170     info.ssrc = pj_rand();
00171     
00172 #if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
00173     /* Set default RTCP XR enabled/disabled */
00174     info.rtcp_xr_enabled = PJ_TRUE;
00175 #endif
00176 
00177     /* Copy remote address */
00178     pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
00179 
00180     /* If remote address is not set, set to an arbitrary address
00181      * (otherwise stream will assert).
00182      */
00183     if (info.rem_addr.addr.sa_family == 0) {
00184         const pj_str_t addr = pj_str("127.0.0.1");
00185         pj_sockaddr_in_init(&info.rem_addr.ipv4, &addr, 0);
00186     }
00187 
00188     /* Create media transport */
00189     status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
00190                                           0, &transport);
00191     if (status != PJ_SUCCESS)
00192         return status;
00193 
00194 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00195     /* Check if SRTP enabled */
00196     if (use_srtp) {
00197         pjmedia_srtp_crypto tx_plc, rx_plc;
00198 
00199         status = pjmedia_transport_srtp_create(med_endpt, transport, 
00200                                                NULL, &srtp_tp);
00201         if (status != PJ_SUCCESS)
00202             return status;
00203 
00204         pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto));
00205         pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto));
00206 
00207         tx_plc.key = *srtp_tx_key;
00208         tx_plc.name = *crypto_suite;
00209         rx_plc.key = *srtp_rx_key;
00210         rx_plc.name = *crypto_suite;
00211         
00212         status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc);
00213         if (status != PJ_SUCCESS)
00214             return status;
00215 
00216         transport = srtp_tp;
00217     }
00218 #endif
00219 
00220     /* Now that the stream info is initialized, we can create the 
00221      * stream.
00222      */
00223 
00224     status = pjmedia_stream_create( med_endpt, pool, &info, 
00225                                     transport, 
00226                                     NULL, p_stream);
00227 
00228     if (status != PJ_SUCCESS) {
00229         app_perror(THIS_FILE, "Error creating stream", status);
00230         pjmedia_transport_close(transport);
00231         return status;
00232     }
00233 
00234 
00235     return PJ_SUCCESS;
00236 }
00237 
00238 
00239 /*
00240  * usage()
00241  */
00242 static void usage()
00243 {
00244     puts(desc);
00245 }
00246 
00247 /*
00248  * main()
00249  */
00250 int main(int argc, char *argv[])
00251 {
00252     pj_caching_pool cp;
00253     pjmedia_endpt *med_endpt;
00254     pj_pool_t *pool;
00255     pjmedia_port *rec_file_port = NULL, *play_file_port = NULL;
00256     pjmedia_master_port *master_port = NULL;
00257     pjmedia_snd_port *snd_port = NULL;
00258     pjmedia_stream *stream = NULL;
00259     pjmedia_port *stream_port;
00260     char tmp[10];
00261     pj_status_t status; 
00262 
00263 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00264     /* SRTP variables */
00265     pj_bool_t use_srtp = PJ_FALSE;
00266     char tmp_tx_key[64];
00267     char tmp_rx_key[64];
00268     pj_str_t  srtp_tx_key = {NULL, 0};
00269     pj_str_t  srtp_rx_key = {NULL, 0};
00270     pj_str_t  srtp_crypto_suite = {NULL, 0};
00271     int tmp_key_len;
00272 #endif
00273 
00274     /* Default values */
00275     const pjmedia_codec_info *codec_info;
00276     pjmedia_dir dir = PJMEDIA_DIR_DECODING;
00277     pj_sockaddr_in remote_addr;
00278     pj_uint16_t local_port = 4000;
00279     char *codec_id = NULL;
00280     char *rec_file = NULL;
00281     char *play_file = NULL;
00282 
00283     enum {
00284         OPT_CODEC       = 'c',
00285         OPT_LOCAL_PORT  = 'p',
00286         OPT_REMOTE      = 'r',
00287         OPT_PLAY_FILE   = 'w',
00288         OPT_RECORD_FILE = 'R',
00289         OPT_SEND_RECV   = 'b',
00290         OPT_SEND_ONLY   = 's',
00291         OPT_RECV_ONLY   = 'i',
00292 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00293         OPT_USE_SRTP    = 'S',
00294 #endif
00295         OPT_SRTP_TX_KEY = 'x',
00296         OPT_SRTP_RX_KEY = 'y',
00297         OPT_HELP        = 'h',
00298     };
00299 
00300     struct pj_getopt_option long_options[] = {
00301         { "codec",          1, 0, OPT_CODEC },
00302         { "local-port",     1, 0, OPT_LOCAL_PORT },
00303         { "remote",         1, 0, OPT_REMOTE },
00304         { "play-file",      1, 0, OPT_PLAY_FILE },
00305         { "record-file",    1, 0, OPT_RECORD_FILE },
00306         { "send-recv",      0, 0, OPT_SEND_RECV },
00307         { "send-only",      0, 0, OPT_SEND_ONLY },
00308         { "recv-only",      0, 0, OPT_RECV_ONLY },
00309 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00310         { "use-srtp",       2, 0, OPT_USE_SRTP },
00311         { "srtp-tx-key",    1, 0, OPT_SRTP_TX_KEY },
00312         { "srtp-rx-key",    1, 0, OPT_SRTP_RX_KEY },
00313 #endif
00314         { "help",           0, 0, OPT_HELP },
00315         { NULL, 0, 0, 0 },
00316     };
00317 
00318     int c;
00319     int option_index;
00320 
00321 
00322     pj_bzero(&remote_addr, sizeof(remote_addr));
00323 
00324 
00325     /* init PJLIB : */
00326     status = pj_init();
00327     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00328 
00329 
00330     /* Parse arguments */
00331     pj_optind = 0;
00332     while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1) {
00333 
00334         switch (c) {
00335         case OPT_CODEC:
00336             codec_id = pj_optarg;
00337             break;
00338 
00339         case OPT_LOCAL_PORT:
00340             local_port = (pj_uint16_t) atoi(pj_optarg);
00341             if (local_port < 1) {
00342                 printf("Error: invalid local port %s\n", pj_optarg);
00343                 return 1;
00344             }
00345             break;
00346 
00347         case OPT_REMOTE:
00348             {
00349                 pj_str_t ip = pj_str(strtok(pj_optarg, ":"));
00350                 pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":"));
00351 
00352                 status = pj_sockaddr_in_init(&remote_addr, &ip, port);
00353                 if (status != PJ_SUCCESS) {
00354                     app_perror(THIS_FILE, "Invalid remote address", status);
00355                     return 1;
00356                 }
00357             }
00358             break;
00359 
00360         case OPT_PLAY_FILE:
00361             play_file = pj_optarg;
00362             break;
00363 
00364         case OPT_RECORD_FILE:
00365             rec_file = pj_optarg;
00366             break;
00367 
00368         case OPT_SEND_RECV:
00369             dir = PJMEDIA_DIR_ENCODING_DECODING;
00370             break;
00371 
00372         case OPT_SEND_ONLY:
00373             dir = PJMEDIA_DIR_ENCODING;
00374             break;
00375 
00376         case OPT_RECV_ONLY:
00377             dir = PJMEDIA_DIR_DECODING;
00378             break;
00379 
00380 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00381         case OPT_USE_SRTP:
00382             use_srtp = PJ_TRUE;
00383             if (pj_optarg) {
00384                 pj_strset(&srtp_crypto_suite, pj_optarg, strlen(pj_optarg));
00385             } else {
00386                 srtp_crypto_suite = pj_str("AES_CM_128_HMAC_SHA1_80");
00387             }
00388             break;
00389 
00390         case OPT_SRTP_TX_KEY:
00391             tmp_key_len = hex_string_to_octet_string(tmp_tx_key, pj_optarg, strlen(pj_optarg));
00392             pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2);
00393             break;
00394 
00395         case OPT_SRTP_RX_KEY:
00396             tmp_key_len = hex_string_to_octet_string(tmp_rx_key, pj_optarg, strlen(pj_optarg));
00397             pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2);
00398             break;
00399 #endif
00400 
00401         case OPT_HELP:
00402             usage();
00403             return 1;
00404 
00405         default:
00406             printf("Invalid options %s\n", argv[pj_optind]);
00407             return 1;
00408         }
00409 
00410     }
00411 
00412 
00413     /* Verify arguments. */
00414     if (dir & PJMEDIA_DIR_ENCODING) {
00415         if (remote_addr.sin_addr.s_addr == 0) {
00416             printf("Error: remote address must be set\n");
00417             return 1;
00418         }
00419     }
00420 
00421     if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) {
00422         printf("Direction is set to --send-only because of --play-file\n");
00423         dir = PJMEDIA_DIR_ENCODING;
00424     }
00425 
00426 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00427     /* SRTP validation */
00428     if (use_srtp) {
00429         if (!srtp_tx_key.slen || !srtp_rx_key.slen)
00430         {
00431             printf("Error: Key for each SRTP stream direction must be set\n");
00432             return 1;
00433         }
00434     }
00435 #endif
00436 
00437     /* Must create a pool factory before we can allocate any memory. */
00438     pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
00439 
00440     /* 
00441      * Initialize media endpoint.
00442      * This will implicitly initialize PJMEDIA too.
00443      */
00444     status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
00445     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00446 
00447     /* Create memory pool for application purpose */
00448     pool = pj_pool_create( &cp.factory,     /* pool factory         */
00449                            "app",           /* pool name.           */
00450                            4000,            /* init size            */
00451                            4000,            /* increment size       */
00452                            NULL             /* callback on error    */
00453                            );
00454 
00455 
00456     /* Register all supported codecs */
00457     status = init_codecs(med_endpt);
00458     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00459 
00460 
00461     /* Find which codec to use. */
00462     if (codec_id) {
00463         unsigned count = 1;
00464         pj_str_t str_codec_id = pj_str(codec_id);
00465         pjmedia_codec_mgr *codec_mgr = pjmedia_endpt_get_codec_mgr(med_endpt);
00466         status = pjmedia_codec_mgr_find_codecs_by_id( codec_mgr,
00467                                                       &str_codec_id, &count,
00468                                                       &codec_info, NULL);
00469         if (status != PJ_SUCCESS) {
00470             printf("Error: unable to find codec %s\n", codec_id);
00471             return 1;
00472         }
00473     } else {
00474         /* Default to pcmu */
00475         pjmedia_codec_mgr_get_codec_info( pjmedia_endpt_get_codec_mgr(med_endpt),
00476                                           0, &codec_info);
00477     }
00478 
00479     /* Create stream based on program arguments */
00480     status = create_stream(pool, med_endpt, codec_info, dir, local_port, 
00481                            &remote_addr, 
00482 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00483                            use_srtp, &srtp_crypto_suite, 
00484                            &srtp_tx_key, &srtp_rx_key,
00485 #endif
00486                            &stream);
00487     if (status != PJ_SUCCESS)
00488         goto on_exit;
00489 
00490 
00491     /* Get the port interface of the stream */
00492     status = pjmedia_stream_get_port( stream, &stream_port);
00493     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00494 
00495 
00496     if (play_file) {
00497         unsigned wav_ptime;
00498 
00499         wav_ptime = stream_port->info.samples_per_frame * 1000 /
00500                     stream_port->info.clock_rate;
00501         status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime,
00502                                                 0, -1, &play_file_port);
00503         if (status != PJ_SUCCESS) {
00504             app_perror(THIS_FILE, "Unable to use file", status);
00505             goto on_exit;
00506         }
00507 
00508         status = pjmedia_master_port_create(pool, play_file_port, stream_port,
00509                                             0, &master_port);
00510         if (status != PJ_SUCCESS) {
00511             app_perror(THIS_FILE, "Unable to create master port", status);
00512             goto on_exit;
00513         }
00514 
00515         status = pjmedia_master_port_start(master_port);
00516         if (status != PJ_SUCCESS) {
00517             app_perror(THIS_FILE, "Error starting master port", status);
00518             goto on_exit;
00519         }
00520 
00521         printf("Playing from WAV file %s..\n", play_file);
00522 
00523     } else if (rec_file) {
00524 
00525         status = pjmedia_wav_writer_port_create(pool, rec_file,
00526                                                 stream_port->info.clock_rate,
00527                                                 stream_port->info.channel_count,
00528                                                 stream_port->info.samples_per_frame,
00529                                                 stream_port->info.bits_per_sample,
00530                                                 0, 0, &rec_file_port);
00531         if (status != PJ_SUCCESS) {
00532             app_perror(THIS_FILE, "Unable to use file", status);
00533             goto on_exit;
00534         }
00535 
00536         status = pjmedia_master_port_create(pool, stream_port, rec_file_port, 
00537                                             0, &master_port);
00538         if (status != PJ_SUCCESS) {
00539             app_perror(THIS_FILE, "Unable to create master port", status);
00540             goto on_exit;
00541         }
00542 
00543         status = pjmedia_master_port_start(master_port);
00544         if (status != PJ_SUCCESS) {
00545             app_perror(THIS_FILE, "Error starting master port", status);
00546             goto on_exit;
00547         }
00548 
00549         printf("Recording to WAV file %s..\n", rec_file);
00550         
00551     } else {
00552 
00553         /* Create sound device port. */
00554         if (dir == PJMEDIA_DIR_ENCODING_DECODING)
00555             status = pjmedia_snd_port_create(pool, -1, -1, 
00556                                         stream_port->info.clock_rate,
00557                                         stream_port->info.channel_count,
00558                                         stream_port->info.samples_per_frame,
00559                                         stream_port->info.bits_per_sample,
00560                                         0, &snd_port);
00561         else if (dir == PJMEDIA_DIR_ENCODING)
00562             status = pjmedia_snd_port_create_rec(pool, -1, 
00563                                         stream_port->info.clock_rate,
00564                                         stream_port->info.channel_count,
00565                                         stream_port->info.samples_per_frame,
00566                                         stream_port->info.bits_per_sample,
00567                                         0, &snd_port);
00568         else
00569             status = pjmedia_snd_port_create_player(pool, -1, 
00570                                         stream_port->info.clock_rate,
00571                                         stream_port->info.channel_count,
00572                                         stream_port->info.samples_per_frame,
00573                                         stream_port->info.bits_per_sample,
00574                                         0, &snd_port);
00575 
00576 
00577         if (status != PJ_SUCCESS) {
00578             app_perror(THIS_FILE, "Unable to create sound port", status);
00579             goto on_exit;
00580         }
00581 
00582         /* Connect sound port to stream */
00583         status = pjmedia_snd_port_connect( snd_port, stream_port );
00584         PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00585 
00586     }
00587 
00588     /* Start streaming */
00589     pjmedia_stream_start(stream);
00590 
00591 
00592     /* Done */
00593 
00594     if (dir == PJMEDIA_DIR_DECODING)
00595         printf("Stream is active, dir is recv-only, local port is %d\n",
00596                local_port);
00597     else if (dir == PJMEDIA_DIR_ENCODING)
00598         printf("Stream is active, dir is send-only, sending to %s:%d\n",
00599                pj_inet_ntoa(remote_addr.sin_addr),
00600                pj_ntohs(remote_addr.sin_port));
00601     else
00602         printf("Stream is active, send/recv, local port is %d, "
00603                "sending to %s:%d\n",
00604                local_port,
00605                pj_inet_ntoa(remote_addr.sin_addr),
00606                pj_ntohs(remote_addr.sin_port));
00607 
00608 
00609     for (;;) {
00610 
00611         puts("");
00612         puts("Commands:");
00613         puts("  s     Display media statistics");
00614         puts("  q     Quit");
00615         puts("");
00616 
00617         printf("Command: "); fflush(stdout);
00618 
00619         if (fgets(tmp, sizeof(tmp), stdin) == NULL) {
00620             puts("EOF while reading stdin, will quit now..");
00621             break;
00622         }
00623 
00624         if (tmp[0] == 's')
00625             print_stream_stat(stream);
00626         else if (tmp[0] == 'q')
00627             break;
00628 
00629     }
00630 
00631 
00632 
00633     /* Start deinitialization: */
00634 on_exit:
00635 
00636     /* Destroy sound device */
00637     if (snd_port) {
00638         pjmedia_snd_port_destroy( snd_port );
00639         PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00640     }
00641 
00642     /* If there is master port, then we just need to destroy master port
00643      * (it will recursively destroy upstream and downstream ports, which
00644      * in this case are file_port and stream_port).
00645      */
00646     if (master_port) {
00647         pjmedia_master_port_destroy(master_port, PJ_TRUE);
00648         play_file_port = NULL;
00649         stream = NULL;
00650     }
00651 
00652     /* Destroy stream */
00653     if (stream) {
00654         pjmedia_transport *tp;
00655 
00656         tp = pjmedia_stream_get_transport(stream);
00657         pjmedia_stream_destroy(stream);
00658         
00659         pjmedia_transport_close(tp);
00660     }
00661 
00662     /* Destroy file ports */
00663     if (play_file_port)
00664         pjmedia_port_destroy( play_file_port );
00665     if (rec_file_port)
00666         pjmedia_port_destroy( rec_file_port );
00667 
00668 
00669     /* Release application pool */
00670     pj_pool_release( pool );
00671 
00672     /* Destroy media endpoint. */
00673     pjmedia_endpt_destroy( med_endpt );
00674 
00675     /* Destroy pool factory */
00676     pj_caching_pool_destroy( &cp );
00677 
00678     /* Shutdown PJLIB */
00679     pj_shutdown();
00680 
00681 
00682     return (status == PJ_SUCCESS) ? 0 : 1;
00683 }
00684 
00685 
00686 
00687 
00688 static const char *good_number(char *buf, pj_int32_t val)
00689 {
00690     if (val < 1000) {
00691         pj_ansi_sprintf(buf, "%d", val);
00692     } else if (val < 1000000) {
00693         pj_ansi_sprintf(buf, "%d.%dK", 
00694                         val / 1000,
00695                         (val % 1000) / 100);
00696     } else {
00697         pj_ansi_sprintf(buf, "%d.%02dM", 
00698                         val / 1000000,
00699                         (val % 1000000) / 10000);
00700     }
00701 
00702     return buf;
00703 }
00704 
00705 
00706 #define SAMPLES_TO_USEC(usec, samples, clock_rate) \
00707     do { \
00708         if (samples <= 4294) \
00709             usec = samples * 1000000 / clock_rate; \
00710         else { \
00711             usec = samples * 1000 / clock_rate; \
00712             usec *= 1000; \
00713         } \
00714     } while(0)
00715 
00716 #define PRINT_VOIP_MTC_VAL(s, v) \
00717     if (v == 127) \
00718         sprintf(s, "(na)"); \
00719     else \
00720         sprintf(s, "%d", v)
00721 
00722 
00723 /*
00724  * Print stream statistics
00725  */
00726 static void print_stream_stat(pjmedia_stream *stream)
00727 {
00728     char duration[80], last_update[80];
00729     char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16];
00730     pjmedia_port *port;
00731     pjmedia_rtcp_stat stat;
00732     pj_time_val now;
00733 
00734 
00735     pj_gettimeofday(&now);
00736     pjmedia_stream_get_stat(stream, &stat);
00737     pjmedia_stream_get_port(stream, &port);
00738 
00739     puts("Stream statistics:");
00740 
00741     /* Print duration */
00742     PJ_TIME_VAL_SUB(now, stat.start);
00743     sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld",
00744             now.sec / 3600,
00745             (now.sec % 3600) / 60,
00746             (now.sec % 60),
00747             now.msec);
00748 
00749 
00750     printf(" Info: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n",
00751         (int)port->info.encoding_name.slen,
00752         port->info.encoding_name.ptr,
00753         port->info.clock_rate,
00754         port->info.samples_per_frame * 1000 / port->info.clock_rate,
00755         good_number(bps, port->info.bytes_per_frame * port->info.clock_rate /
00756                     port->info.samples_per_frame),
00757         good_number(ipbps, (port->info.bytes_per_frame+32) * 
00758                             port->info.clock_rate / port->info.clock_rate));
00759 
00760     if (stat.rx.update_cnt == 0)
00761         strcpy(last_update, "never");
00762     else {
00763         pj_gettimeofday(&now);
00764         PJ_TIME_VAL_SUB(now, stat.rx.update);
00765         sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
00766                 now.sec / 3600,
00767                 (now.sec % 3600) / 60,
00768                 now.sec % 60,
00769                 now.msec);
00770     }
00771 
00772     printf(" RX stat last update: %s\n"
00773            "    total %s packets %sB received (%sB +IP hdr)%s\n"
00774            "    pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
00775            "          (msec)    min     avg     max     last    dev\n"
00776            "    loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n"
00777            "    jitter     : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
00778            last_update,
00779            good_number(packets, stat.rx.pkt),
00780            good_number(bytes, stat.rx.bytes),
00781            good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
00782            "",
00783            stat.rx.loss,
00784            stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
00785            stat.rx.dup, 
00786            stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
00787            stat.rx.reorder, 
00788            stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
00789            "",
00790            stat.rx.loss_period.min / 1000.0, 
00791            stat.rx.loss_period.mean / 1000.0, 
00792            stat.rx.loss_period.max / 1000.0,
00793            stat.rx.loss_period.last / 1000.0,
00794            pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
00795            "",
00796            stat.rx.jitter.min / 1000.0,
00797            stat.rx.jitter.mean / 1000.0,
00798            stat.rx.jitter.max / 1000.0,
00799            stat.rx.jitter.last / 1000.0,
00800            pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
00801            ""
00802            );
00803 
00804 
00805     if (stat.tx.update_cnt == 0)
00806         strcpy(last_update, "never");
00807     else {
00808         pj_gettimeofday(&now);
00809         PJ_TIME_VAL_SUB(now, stat.tx.update);
00810         sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
00811                 now.sec / 3600,
00812                 (now.sec % 3600) / 60,
00813                 now.sec % 60,
00814                 now.msec);
00815     }
00816 
00817     printf(" TX stat last update: %s\n"
00818            "    total %s packets %sB sent (%sB +IP hdr)%s\n"
00819            "    pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
00820            "          (msec)    min     avg     max     last    dev\n"
00821            "    loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n"
00822            "    jitter     : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
00823            last_update,
00824            good_number(packets, stat.tx.pkt),
00825            good_number(bytes, stat.tx.bytes),
00826            good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
00827            "",
00828            stat.tx.loss,
00829            stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
00830            stat.tx.dup, 
00831            stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
00832            stat.tx.reorder, 
00833            stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
00834            "",
00835            stat.tx.loss_period.min / 1000.0, 
00836            stat.tx.loss_period.mean / 1000.0, 
00837            stat.tx.loss_period.max / 1000.0,
00838            stat.tx.loss_period.last / 1000.0,
00839            pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
00840            "",
00841            stat.tx.jitter.min / 1000.0,
00842            stat.tx.jitter.mean / 1000.0,
00843            stat.tx.jitter.max / 1000.0,
00844            stat.tx.jitter.last / 1000.0,
00845            pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
00846            ""
00847            );
00848 
00849 
00850     printf(" RTT delay     : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n", 
00851            stat.rtt.min / 1000.0,
00852            stat.rtt.mean / 1000.0,
00853            stat.rtt.max / 1000.0,
00854            stat.rtt.last / 1000.0,
00855            pj_math_stat_get_stddev(&stat.rtt) / 1000.0,
00856            ""
00857            );
00858 
00859 #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
00860     /* RTCP XR Reports */
00861     do {
00862         char loss[16], dup[16];
00863         char jitter[80];
00864         char toh[80];
00865         char plc[16], jba[16], jbr[16];
00866         char signal_lvl[16], noise_lvl[16], rerl[16];
00867         char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
00868         pjmedia_rtcp_xr_stat xr_stat;
00869 
00870         if (pjmedia_stream_get_stat_xr(stream, &xr_stat) != PJ_SUCCESS)
00871             break;
00872 
00873         puts("\nExtended reports:");
00874 
00875         /* Statistics Summary */
00876         puts(" Statistics Summary");
00877 
00878         if (xr_stat.rx.stat_sum.l)
00879             sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
00880         else
00881             sprintf(loss, "(na)");
00882 
00883         if (xr_stat.rx.stat_sum.d)
00884             sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
00885         else
00886             sprintf(dup, "(na)");
00887 
00888         if (xr_stat.rx.stat_sum.j) {
00889             unsigned jmin, jmax, jmean, jdev;
00890 
00891             SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min, 
00892                             port->info.clock_rate);
00893             SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max, 
00894                             port->info.clock_rate);
00895             SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean, 
00896                             port->info.clock_rate);
00897             SAMPLES_TO_USEC(jdev, 
00898                            pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
00899                            port->info.clock_rate);
00900             sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f", 
00901                     jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
00902         } else
00903             sprintf(jitter, "(report not available)");
00904 
00905         if (xr_stat.rx.stat_sum.t) {
00906             sprintf(toh, "%11d %11d %11d %11d", 
00907                     xr_stat.rx.stat_sum.toh.min,
00908                     xr_stat.rx.stat_sum.toh.mean,
00909                     xr_stat.rx.stat_sum.toh.max,
00910                     pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
00911         } else
00912             sprintf(toh, "(report not available)");
00913 
00914         if (xr_stat.rx.stat_sum.update.sec == 0)
00915             strcpy(last_update, "never");
00916         else {
00917             pj_gettimeofday(&now);
00918             PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
00919             sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
00920                     now.sec / 3600,
00921                     (now.sec % 3600) / 60,
00922                     now.sec % 60,
00923                     now.msec);
00924         }
00925 
00926         printf(" RX last update: %s\n"
00927                "    begin seq=%d, end seq=%d%s\n"
00928                "    pkt loss=%s, dup=%s%s\n"
00929                "          (msec)    min     avg     max     dev\n"
00930                "    jitter     : %s\n"
00931                "    toh        : %s\n",
00932                last_update,
00933                xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
00934                "",
00935                loss, dup,
00936                "",
00937                jitter,
00938                toh
00939                );
00940 
00941         if (xr_stat.tx.stat_sum.l)
00942             sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
00943         else
00944             sprintf(loss, "(na)");
00945 
00946         if (xr_stat.tx.stat_sum.d)
00947             sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
00948         else
00949             sprintf(dup, "(na)");
00950 
00951         if (xr_stat.tx.stat_sum.j) {
00952             unsigned jmin, jmax, jmean, jdev;
00953 
00954             SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min, 
00955                             port->info.clock_rate);
00956             SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max, 
00957                             port->info.clock_rate);
00958             SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean, 
00959                             port->info.clock_rate);
00960             SAMPLES_TO_USEC(jdev, 
00961                            pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
00962                            port->info.clock_rate);
00963             sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f", 
00964                     jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
00965         } else
00966             sprintf(jitter, "(report not available)");
00967 
00968         if (xr_stat.tx.stat_sum.t) {
00969             sprintf(toh, "%11d %11d %11d %11d", 
00970                     xr_stat.tx.stat_sum.toh.min,
00971                     xr_stat.tx.stat_sum.toh.mean,
00972                     xr_stat.tx.stat_sum.toh.max,
00973                     pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
00974         } else
00975             sprintf(toh,    "(report not available)");
00976 
00977         if (xr_stat.tx.stat_sum.update.sec == 0)
00978             strcpy(last_update, "never");
00979         else {
00980             pj_gettimeofday(&now);
00981             PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
00982             sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
00983                     now.sec / 3600,
00984                     (now.sec % 3600) / 60,
00985                     now.sec % 60,
00986                     now.msec);
00987         }
00988 
00989         printf(" TX last update: %s\n"
00990                "    begin seq=%d, end seq=%d%s\n"
00991                "    pkt loss=%s, dup=%s%s\n"
00992                "          (msec)    min     avg     max     dev\n"
00993                "    jitter     : %s\n"
00994                "    toh        : %s\n",
00995                last_update,
00996                xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
00997                "",
00998                loss, dup,
00999                "",
01000                jitter,
01001                toh
01002                );
01003 
01004         /* VoIP Metrics */
01005         puts(" VoIP Metrics");
01006 
01007         PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
01008         PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
01009         PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
01010         PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
01011         PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
01012         PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
01013         PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
01014 
01015         switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
01016             case PJMEDIA_RTCP_XR_PLC_DIS:
01017                 sprintf(plc, "DISABLED");
01018                 break;
01019             case PJMEDIA_RTCP_XR_PLC_ENH:
01020                 sprintf(plc, "ENHANCED");
01021                 break;
01022             case PJMEDIA_RTCP_XR_PLC_STD:
01023                 sprintf(plc, "STANDARD");
01024                 break;
01025             case PJMEDIA_RTCP_XR_PLC_UNK:
01026             default:
01027                 sprintf(plc, "UNKNOWN");
01028                 break;
01029         }
01030 
01031         switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
01032             case PJMEDIA_RTCP_XR_JB_FIXED:
01033                 sprintf(jba, "FIXED");
01034                 break;
01035             case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
01036                 sprintf(jba, "ADAPTIVE");
01037                 break;
01038             default:
01039                 sprintf(jba, "UNKNOWN");
01040                 break;
01041         }
01042 
01043         sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
01044 
01045         if (xr_stat.rx.voip_mtc.update.sec == 0)
01046             strcpy(last_update, "never");
01047         else {
01048             pj_gettimeofday(&now);
01049             PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
01050             sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
01051                     now.sec / 3600,
01052                     (now.sec % 3600) / 60,
01053                     now.sec % 60,
01054                     now.msec);
01055         }
01056 
01057         printf(" RX last update: %s\n"
01058                "    packets    : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
01059                "    burst      : density=%d (%.2f%%), duration=%d%s\n"
01060                "    gap        : density=%d (%.2f%%), duration=%d%s\n"
01061                "    delay      : round trip=%d%s, end system=%d%s\n"
01062                "    level      : signal=%s%s, noise=%s%s, RERL=%s%s\n"
01063                "    quality    : R factor=%s, ext R factor=%s\n"
01064                "                 MOS LQ=%s, MOS CQ=%s\n"
01065                "    config     : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
01066                "    JB delay   : cur=%d%s, max=%d%s, abs max=%d%s\n",
01067                last_update,
01068                /* pakcets */
01069                xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
01070                xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
01071                /* burst */
01072                xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
01073                xr_stat.rx.voip_mtc.burst_dur, "ms",
01074                /* gap */
01075                xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
01076                xr_stat.rx.voip_mtc.gap_dur, "ms",
01077                /* delay */
01078                xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
01079                xr_stat.rx.voip_mtc.end_sys_delay, "ms",
01080                /* level */
01081                signal_lvl, "dB",
01082                noise_lvl, "dB",
01083                rerl, "",
01084                /* quality */
01085                r_factor, ext_r_factor, mos_lq, mos_cq,
01086                /* config */
01087                plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
01088                /* JB delay */
01089                xr_stat.rx.voip_mtc.jb_nom, "ms",
01090                xr_stat.rx.voip_mtc.jb_max, "ms",
01091                xr_stat.rx.voip_mtc.jb_abs_max, "ms"
01092                );
01093 
01094         PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
01095         PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
01096         PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
01097         PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
01098         PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
01099         PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
01100         PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
01101 
01102         switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
01103             case PJMEDIA_RTCP_XR_PLC_DIS:
01104                 sprintf(plc, "DISABLED");
01105                 break;
01106             case PJMEDIA_RTCP_XR_PLC_ENH:
01107                 sprintf(plc, "ENHANCED");
01108                 break;
01109             case PJMEDIA_RTCP_XR_PLC_STD:
01110                 sprintf(plc, "STANDARD");
01111                 break;
01112             case PJMEDIA_RTCP_XR_PLC_UNK:
01113             default:
01114                 sprintf(plc, "unknown");
01115                 break;
01116         }
01117 
01118         switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
01119             case PJMEDIA_RTCP_XR_JB_FIXED:
01120                 sprintf(jba, "FIXED");
01121                 break;
01122             case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
01123                 sprintf(jba, "ADAPTIVE");
01124                 break;
01125             default:
01126                 sprintf(jba, "unknown");
01127                 break;
01128         }
01129 
01130         sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
01131 
01132         if (xr_stat.tx.voip_mtc.update.sec == 0)
01133             strcpy(last_update, "never");
01134         else {
01135             pj_gettimeofday(&now);
01136             PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
01137             sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
01138                     now.sec / 3600,
01139                     (now.sec % 3600) / 60,
01140                     now.sec % 60,
01141                     now.msec);
01142         }
01143 
01144         printf(" TX last update: %s\n"
01145                "    packets    : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
01146                "    burst      : density=%d (%.2f%%), duration=%d%s\n"
01147                "    gap        : density=%d (%.2f%%), duration=%d%s\n"
01148                "    delay      : round trip=%d%s, end system=%d%s\n"
01149                "    level      : signal=%s%s, noise=%s%s, RERL=%s%s\n"
01150                "    quality    : R factor=%s, ext R factor=%s\n"
01151                "                 MOS LQ=%s, MOS CQ=%s\n"
01152                "    config     : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
01153                "    JB delay   : cur=%d%s, max=%d%s, abs max=%d%s\n",
01154                last_update,
01155                /* pakcets */
01156                xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
01157                xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
01158                /* burst */
01159                xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
01160                xr_stat.tx.voip_mtc.burst_dur, "ms",
01161                /* gap */
01162                xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
01163                xr_stat.tx.voip_mtc.gap_dur, "ms",
01164                /* delay */
01165                xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
01166                xr_stat.tx.voip_mtc.end_sys_delay, "ms",
01167                /* level */
01168                signal_lvl, "dB",
01169                noise_lvl, "dB",
01170                rerl, "",
01171                /* quality */
01172                r_factor, ext_r_factor, mos_lq, mos_cq,
01173                /* config */
01174                plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
01175                /* JB delay */
01176                xr_stat.tx.voip_mtc.jb_nom, "ms",
01177                xr_stat.tx.voip_mtc.jb_max, "ms",
01178                xr_stat.tx.voip_mtc.jb_abs_max, "ms"
01179                );
01180 
01181 
01182         /* RTT delay (by receiver side) */
01183         printf("          (msec)    min     avg     max     last    dev\n");
01184         printf(" RTT delay     : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n", 
01185                xr_stat.rtt.min / 1000.0,
01186                xr_stat.rtt.mean / 1000.0,
01187                xr_stat.rtt.max / 1000.0,
01188                xr_stat.rtt.last / 1000.0,
01189                pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0,
01190                ""
01191                );
01192     } while (0);
01193 #endif /* PJMEDIA_HAS_RTCP_XR */
01194 
01195 }
01196 

 


PJMEDIA small footprint Open Source media stack
Copyright (C) 2006-2008 Teluu Inc.