BLOG | DOCUMENTATION | TRAC

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

 


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