|
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
This example mainly demonstrates how to stream media file to remote peer using RTP.
This file is pjsip-apps/src/samples/streamutil.c
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00032 #include <pjlib.h>
00033 #include <pjlib-util.h>
00034 #include <pjmedia.h>
00035 #include <pjmedia-codec.h>
00036 #include <pjmedia/transport_srtp.h>
00037
00038 #include <stdlib.h>
00039 #include <stdio.h>
00040
00041 #include "util.h"
00042
00043
00044 static const char *desc =
00045 " streamutil \n"
00046 " \n"
00047 " PURPOSE: \n"
00048 " Demonstrate how to use pjmedia stream component to transmit/receive \n"
00049 " RTP packets to/from sound device. \n"
00050 "\n"
00051 "\n"
00052 " USAGE: \n"
00053 " streamutil [options] \n"
00054 "\n"
00055 "\n"
00056 " Options:\n"
00057 " --codec=CODEC Set the codec name. \n"
00058 " --local-port=PORT Set local RTP port (default=4000) \n"
00059 " --remote=IP:PORT Set the remote peer. If this option is set, \n"
00060 " the program will transmit RTP audio to the \n"
00061 " specified address. (default: recv only) \n"
00062 " --play-file=WAV Send audio from the WAV file instead of from \n"
00063 " the sound device. \n"
00064 " --record-file=WAV Record incoming audio to WAV file instead of \n"
00065 " playing it to sound device. \n"
00066 " --send-recv Set stream direction to bidirectional. \n"
00067 " --send-only Set stream direction to send only \n"
00068 " --recv-only Set stream direction to recv only (default) \n"
00069
00070 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00071 " --use-srtp[=NAME] Enable SRTP with crypto suite NAME \n"
00072 " e.g: AES_CM_128_HMAC_SHA1_80 (default), \n"
00073 " AES_CM_128_HMAC_SHA1_32 \n"
00074 " Use this option along with the TX & RX keys, \n"
00075 " formated of 60 hex digits (e.g: E148DA..) \n"
00076 " --srtp-tx-key SRTP key for transmiting \n"
00077 " --srtp-rx-key SRTP key for receiving \n"
00078 #endif
00079
00080 "\n"
00081 ;
00082
00083
00084
00085
00086 #define THIS_FILE "stream.c"
00087
00088
00089
00090
00091 static void print_stream_stat(pjmedia_stream *stream);
00092
00093
00094 int hex_string_to_octet_string(char *raw, char *hex, int len);
00095
00096
00097
00098
00099 static pj_status_t init_codecs(pjmedia_endpt *med_endpt)
00100 {
00101 pj_status_t status;
00102
00103
00104 PJ_UNUSED_ARG(status);
00105
00106 #if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
00107 status = pjmedia_codec_g711_init(med_endpt);
00108 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00109 #endif
00110
00111 #if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0
00112 status = pjmedia_codec_gsm_init(med_endpt);
00113 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00114 #endif
00115
00116 #if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
00117 status = pjmedia_codec_speex_init(med_endpt, 0, -1, -1);
00118 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00119 #endif
00120
00121 #if defined(PJMEDIA_HAS_G722_CODEC) && PJMEDIA_HAS_G722_CODEC!=0
00122 status = pjmedia_codec_g722_init(med_endpt);
00123 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00124 #endif
00125
00126 #if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC!=0
00127 status = pjmedia_codec_l16_init(med_endpt, 0);
00128 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00129 #endif
00130
00131 return PJ_SUCCESS;
00132 }
00133
00134
00135
00136
00137
00138 static pj_status_t create_stream( pj_pool_t *pool,
00139 pjmedia_endpt *med_endpt,
00140 const pjmedia_codec_info *codec_info,
00141 pjmedia_dir dir,
00142 pj_uint16_t local_port,
00143 const pj_sockaddr_in *rem_addr,
00144 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00145 pj_bool_t use_srtp,
00146 const pj_str_t *crypto_suite,
00147 const pj_str_t *srtp_tx_key,
00148 const pj_str_t *srtp_rx_key,
00149 #endif
00150 pjmedia_stream **p_stream )
00151 {
00152 pjmedia_stream_info info;
00153 pjmedia_transport *transport = NULL;
00154 pj_status_t status;
00155 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00156 pjmedia_transport *srtp_tp = NULL;
00157 #endif
00158
00159
00160
00161 pj_bzero(&info, sizeof(info));
00162
00163
00164
00165 info.type = PJMEDIA_TYPE_AUDIO;
00166 info.dir = dir;
00167 pj_memcpy(&info.fmt, codec_info, sizeof(pjmedia_codec_info));
00168 info.tx_pt = codec_info->pt;
00169 info.ssrc = pj_rand();
00170
00171 #if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
00172
00173 info.rtcp_xr_enabled = PJ_TRUE;
00174 #endif
00175
00176
00177 pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
00178
00179
00180
00181
00182 if (info.rem_addr.addr.sa_family == 0) {
00183 const pj_str_t addr = pj_str("127.0.0.1");
00184 pj_sockaddr_in_init(&info.rem_addr.ipv4, &addr, 0);
00185 }
00186
00187
00188 status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
00189 0, &transport);
00190 if (status != PJ_SUCCESS)
00191 return status;
00192
00193 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00194
00195 if (use_srtp) {
00196 pjmedia_srtp_crypto tx_plc, rx_plc;
00197
00198 status = pjmedia_transport_srtp_create(med_endpt, transport,
00199 NULL, &srtp_tp);
00200 if (status != PJ_SUCCESS)
00201 return status;
00202
00203 pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto));
00204 pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto));
00205
00206 tx_plc.key = *srtp_tx_key;
00207 tx_plc.name = *crypto_suite;
00208 rx_plc.key = *srtp_rx_key;
00209 rx_plc.name = *crypto_suite;
00210
00211 status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc);
00212 if (status != PJ_SUCCESS)
00213 return status;
00214
00215 transport = srtp_tp;
00216 }
00217 #endif
00218
00219
00220
00221
00222
00223 status = pjmedia_stream_create( med_endpt, pool, &info,
00224 transport,
00225 NULL, p_stream);
00226
00227 if (status != PJ_SUCCESS) {
00228 app_perror(THIS_FILE, "Error creating stream", status);
00229 pjmedia_transport_close(transport);
00230 return status;
00231 }
00232
00233
00234 return PJ_SUCCESS;
00235 }
00236
00237
00238
00239
00240
00241 static void usage()
00242 {
00243 puts(desc);
00244 }
00245
00246
00247
00248
00249 int main(int argc, char *argv[])
00250 {
00251 pj_caching_pool cp;
00252 pjmedia_endpt *med_endpt;
00253 pj_pool_t *pool;
00254 pjmedia_port *rec_file_port = NULL, *play_file_port = NULL;
00255 pjmedia_master_port *master_port = NULL;
00256 pjmedia_snd_port *snd_port = NULL;
00257 pjmedia_stream *stream = NULL;
00258 pjmedia_port *stream_port;
00259 char tmp[10];
00260 pj_status_t status;
00261
00262 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00263
00264 pj_bool_t use_srtp = PJ_FALSE;
00265 char tmp_tx_key[64];
00266 char tmp_rx_key[64];
00267 pj_str_t srtp_tx_key = {NULL, 0};
00268 pj_str_t srtp_rx_key = {NULL, 0};
00269 pj_str_t srtp_crypto_suite = {NULL, 0};
00270 int tmp_key_len;
00271 #endif
00272
00273
00274 const pjmedia_codec_info *codec_info;
00275 pjmedia_dir dir = PJMEDIA_DIR_DECODING;
00276 pj_sockaddr_in remote_addr;
00277 pj_uint16_t local_port = 4000;
00278 char *codec_id = NULL;
00279 char *rec_file = NULL;
00280 char *play_file = NULL;
00281
00282 enum {
00283 OPT_CODEC = 'c',
00284 OPT_LOCAL_PORT = 'p',
00285 OPT_REMOTE = 'r',
00286 OPT_PLAY_FILE = 'w',
00287 OPT_RECORD_FILE = 'R',
00288 OPT_SEND_RECV = 'b',
00289 OPT_SEND_ONLY = 's',
00290 OPT_RECV_ONLY = 'i',
00291 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00292 OPT_USE_SRTP = 'S',
00293 #endif
00294 OPT_SRTP_TX_KEY = 'x',
00295 OPT_SRTP_RX_KEY = 'y',
00296 OPT_HELP = 'h',
00297 };
00298
00299 struct pj_getopt_option long_options[] = {
00300 { "codec", 1, 0, OPT_CODEC },
00301 { "local-port", 1, 0, OPT_LOCAL_PORT },
00302 { "remote", 1, 0, OPT_REMOTE },
00303 { "play-file", 1, 0, OPT_PLAY_FILE },
00304 { "record-file", 1, 0, OPT_RECORD_FILE },
00305 { "send-recv", 0, 0, OPT_SEND_RECV },
00306 { "send-only", 0, 0, OPT_SEND_ONLY },
00307 { "recv-only", 0, 0, OPT_RECV_ONLY },
00308 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00309 { "use-srtp", 2, 0, OPT_USE_SRTP },
00310 { "srtp-tx-key", 1, 0, OPT_SRTP_TX_KEY },
00311 { "srtp-rx-key", 1, 0, OPT_SRTP_RX_KEY },
00312 #endif
00313 { "help", 0, 0, OPT_HELP },
00314 { NULL, 0, 0, 0 },
00315 };
00316
00317 int c;
00318 int option_index;
00319
00320
00321 pj_bzero(&remote_addr, sizeof(remote_addr));
00322
00323
00324
00325 status = pj_init();
00326 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00327
00328
00329
00330 pj_optind = 0;
00331 while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1) {
00332
00333 switch (c) {
00334 case OPT_CODEC:
00335 codec_id = pj_optarg;
00336 break;
00337
00338 case OPT_LOCAL_PORT:
00339 local_port = (pj_uint16_t) atoi(pj_optarg);
00340 if (local_port < 1) {
00341 printf("Error: invalid local port %s\n", pj_optarg);
00342 return 1;
00343 }
00344 break;
00345
00346 case OPT_REMOTE:
00347 {
00348 pj_str_t ip = pj_str(strtok(pj_optarg, ":"));
00349 pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":"));
00350
00351 status = pj_sockaddr_in_init(&remote_addr, &ip, port);
00352 if (status != PJ_SUCCESS) {
00353 app_perror(THIS_FILE, "Invalid remote address", status);
00354 return 1;
00355 }
00356 }
00357 break;
00358
00359 case OPT_PLAY_FILE:
00360 play_file = pj_optarg;
00361 break;
00362
00363 case OPT_RECORD_FILE:
00364 rec_file = pj_optarg;
00365 break;
00366
00367 case OPT_SEND_RECV:
00368 dir = PJMEDIA_DIR_ENCODING_DECODING;
00369 break;
00370
00371 case OPT_SEND_ONLY:
00372 dir = PJMEDIA_DIR_ENCODING;
00373 break;
00374
00375 case OPT_RECV_ONLY:
00376 dir = PJMEDIA_DIR_DECODING;
00377 break;
00378
00379 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00380 case OPT_USE_SRTP:
00381 use_srtp = PJ_TRUE;
00382 if (pj_optarg) {
00383 pj_strset(&srtp_crypto_suite, pj_optarg, strlen(pj_optarg));
00384 } else {
00385 srtp_crypto_suite = pj_str("AES_CM_128_HMAC_SHA1_80");
00386 }
00387 break;
00388
00389 case OPT_SRTP_TX_KEY:
00390 tmp_key_len = hex_string_to_octet_string(tmp_tx_key, pj_optarg, strlen(pj_optarg));
00391 pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2);
00392 break;
00393
00394 case OPT_SRTP_RX_KEY:
00395 tmp_key_len = hex_string_to_octet_string(tmp_rx_key, pj_optarg, strlen(pj_optarg));
00396 pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2);
00397 break;
00398 #endif
00399
00400 case OPT_HELP:
00401 usage();
00402 return 1;
00403
00404 default:
00405 printf("Invalid options %s\n", argv[pj_optind]);
00406 return 1;
00407 }
00408
00409 }
00410
00411
00412
00413 if (dir & PJMEDIA_DIR_ENCODING) {
00414 if (remote_addr.sin_addr.s_addr == 0) {
00415 printf("Error: remote address must be set\n");
00416 return 1;
00417 }
00418 }
00419
00420 if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) {
00421 printf("Direction is set to --send-only because of --play-file\n");
00422 dir = PJMEDIA_DIR_ENCODING;
00423 }
00424
00425 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00426
00427 if (use_srtp) {
00428 if (!srtp_tx_key.slen || !srtp_rx_key.slen)
00429 {
00430 printf("Error: Key for each SRTP stream direction must be set\n");
00431 return 1;
00432 }
00433 }
00434 #endif
00435
00436
00437 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
00438
00439
00440
00441
00442
00443 status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
00444 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00445
00446
00447 pool = pj_pool_create( &cp.factory,
00448 "app",
00449 4000,
00450 4000,
00451 NULL
00452 );
00453
00454
00455
00456 status = init_codecs(med_endpt);
00457 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00458
00459
00460
00461 if (codec_id) {
00462 unsigned count = 1;
00463 pj_str_t str_codec_id = pj_str(codec_id);
00464 pjmedia_codec_mgr *codec_mgr = pjmedia_endpt_get_codec_mgr(med_endpt);
00465 status = pjmedia_codec_mgr_find_codecs_by_id( codec_mgr,
00466 &str_codec_id, &count,
00467 &codec_info, NULL);
00468 if (status != PJ_SUCCESS) {
00469 printf("Error: unable to find codec %s\n", codec_id);
00470 return 1;
00471 }
00472 } else {
00473
00474 pjmedia_codec_mgr_get_codec_info( pjmedia_endpt_get_codec_mgr(med_endpt),
00475 0, &codec_info);
00476 }
00477
00478
00479 status = create_stream(pool, med_endpt, codec_info, dir, local_port,
00480 &remote_addr,
00481 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
00482 use_srtp, &srtp_crypto_suite,
00483 &srtp_tx_key, &srtp_rx_key,
00484 #endif
00485 &stream);
00486 if (status != PJ_SUCCESS)
00487 goto on_exit;
00488
00489
00490
00491 status = pjmedia_stream_get_port( stream, &stream_port);
00492 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00493
00494
00495 if (play_file) {
00496 unsigned wav_ptime;
00497
00498 wav_ptime = stream_port->info.samples_per_frame * 1000 /
00499 stream_port->info.clock_rate;
00500 status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime,
00501 0, -1, &play_file_port);
00502 if (status != PJ_SUCCESS) {
00503 app_perror(THIS_FILE, "Unable to use file", status);
00504 goto on_exit;
00505 }
00506
00507 status = pjmedia_master_port_create(pool, play_file_port, stream_port,
00508 0, &master_port);
00509 if (status != PJ_SUCCESS) {
00510 app_perror(THIS_FILE, "Unable to create master port", status);
00511 goto on_exit;
00512 }
00513
00514 status = pjmedia_master_port_start(master_port);
00515 if (status != PJ_SUCCESS) {
00516 app_perror(THIS_FILE, "Error starting master port", status);
00517 goto on_exit;
00518 }
00519
00520 printf("Playing from WAV file %s..\n", play_file);
00521
00522 } else if (rec_file) {
00523
00524 status = pjmedia_wav_writer_port_create(pool, rec_file,
00525 stream_port->info.clock_rate,
00526 stream_port->info.channel_count,
00527 stream_port->info.samples_per_frame,
00528 stream_port->info.bits_per_sample,
00529 0, 0, &rec_file_port);
00530 if (status != PJ_SUCCESS) {
00531 app_perror(THIS_FILE, "Unable to use file", status);
00532 goto on_exit;
00533 }
00534
00535 status = pjmedia_master_port_create(pool, stream_port, rec_file_port,
00536 0, &master_port);
00537 if (status != PJ_SUCCESS) {
00538 app_perror(THIS_FILE, "Unable to create master port", status);
00539 goto on_exit;
00540 }
00541
00542 status = pjmedia_master_port_start(master_port);
00543 if (status != PJ_SUCCESS) {
00544 app_perror(THIS_FILE, "Error starting master port", status);
00545 goto on_exit;
00546 }
00547
00548 printf("Recording to WAV file %s..\n", rec_file);
00549
00550 } else {
00551
00552
00553 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
00554 status = pjmedia_snd_port_create(pool, -1, -1,
00555 stream_port->info.clock_rate,
00556 stream_port->info.channel_count,
00557 stream_port->info.samples_per_frame,
00558 stream_port->info.bits_per_sample,
00559 0, &snd_port);
00560 else if (dir == PJMEDIA_DIR_ENCODING)
00561 status = pjmedia_snd_port_create_rec(pool, -1,
00562 stream_port->info.clock_rate,
00563 stream_port->info.channel_count,
00564 stream_port->info.samples_per_frame,
00565 stream_port->info.bits_per_sample,
00566 0, &snd_port);
00567 else
00568 status = pjmedia_snd_port_create_player(pool, -1,
00569 stream_port->info.clock_rate,
00570 stream_port->info.channel_count,
00571 stream_port->info.samples_per_frame,
00572 stream_port->info.bits_per_sample,
00573 0, &snd_port);
00574
00575
00576 if (status != PJ_SUCCESS) {
00577 app_perror(THIS_FILE, "Unable to create sound port", status);
00578 goto on_exit;
00579 }
00580
00581
00582 status = pjmedia_snd_port_connect( snd_port, stream_port );
00583 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00584
00585 }
00586
00587
00588 pjmedia_stream_start(stream);
00589
00590
00591
00592
00593 if (dir == PJMEDIA_DIR_DECODING)
00594 printf("Stream is active, dir is recv-only, local port is %d\n",
00595 local_port);
00596 else if (dir == PJMEDIA_DIR_ENCODING)
00597 printf("Stream is active, dir is send-only, sending to %s:%d\n",
00598 pj_inet_ntoa(remote_addr.sin_addr),
00599 pj_ntohs(remote_addr.sin_port));
00600 else
00601 printf("Stream is active, send/recv, local port is %d, "
00602 "sending to %s:%d\n",
00603 local_port,
00604 pj_inet_ntoa(remote_addr.sin_addr),
00605 pj_ntohs(remote_addr.sin_port));
00606
00607
00608 for (;;) {
00609
00610 puts("");
00611 puts("Commands:");
00612 puts(" s Display media statistics");
00613 puts(" q Quit");
00614 puts("");
00615
00616 printf("Command: "); fflush(stdout);
00617
00618 fgets(tmp, sizeof(tmp), stdin);
00619
00620 if (tmp[0] == 's')
00621 print_stream_stat(stream);
00622 else if (tmp[0] == 'q')
00623 break;
00624
00625 }
00626
00627
00628
00629
00630 on_exit:
00631
00632
00633 if (snd_port) {
00634 pjmedia_snd_port_destroy( snd_port );
00635 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00636 }
00637
00638
00639
00640
00641
00642 if (master_port) {
00643 pjmedia_master_port_destroy(master_port, PJ_TRUE);
00644 play_file_port = NULL;
00645 stream = NULL;
00646 }
00647
00648
00649 if (stream) {
00650 pjmedia_transport *tp;
00651
00652 tp = pjmedia_stream_get_transport(stream);
00653 pjmedia_stream_destroy(stream);
00654
00655 pjmedia_transport_close(tp);
00656 }
00657
00658
00659 if (play_file_port)
00660 pjmedia_port_destroy( play_file_port );
00661 if (rec_file_port)
00662 pjmedia_port_destroy( rec_file_port );
00663
00664
00665
00666 pj_pool_release( pool );
00667
00668
00669 pjmedia_endpt_destroy( med_endpt );
00670
00671
00672 pj_caching_pool_destroy( &cp );
00673
00674
00675 pj_shutdown();
00676
00677
00678 return (status == PJ_SUCCESS) ? 0 : 1;
00679 }
00680
00681
00682
00683
00684 static const char *good_number(char *buf, pj_int32_t val)
00685 {
00686 if (val < 1000) {
00687 pj_ansi_sprintf(buf, "%d", val);
00688 } else if (val < 1000000) {
00689 pj_ansi_sprintf(buf, "%d.%dK",
00690 val / 1000,
00691 (val % 1000) / 100);
00692 } else {
00693 pj_ansi_sprintf(buf, "%d.%02dM",
00694 val / 1000000,
00695 (val % 1000000) / 10000);
00696 }
00697
00698 return buf;
00699 }
00700
00701
00702 #define SAMPLES_TO_USEC(usec, samples, clock_rate) \
00703 do { \
00704 if (samples <= 4294) \
00705 usec = samples * 1000000 / clock_rate; \
00706 else { \
00707 usec = samples * 1000 / clock_rate; \
00708 usec *= 1000; \
00709 } \
00710 } while(0)
00711
00712 #define PRINT_VOIP_MTC_VAL(s, v) \
00713 if (v == 127) \
00714 sprintf(s, "(na)"); \
00715 else \
00716 sprintf(s, "%d", v)
00717
00718
00719
00720
00721
00722 static void print_stream_stat(pjmedia_stream *stream)
00723 {
00724 char duration[80], last_update[80];
00725 char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16];
00726 pjmedia_port *port;
00727 pjmedia_rtcp_stat stat;
00728 pj_time_val now;
00729
00730
00731 pj_gettimeofday(&now);
00732 pjmedia_stream_get_stat(stream, &stat);
00733 pjmedia_stream_get_port(stream, &port);
00734
00735 puts("Stream statistics:");
00736
00737
00738 PJ_TIME_VAL_SUB(now, stat.start);
00739 sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld",
00740 now.sec / 3600,
00741 (now.sec % 3600) / 60,
00742 (now.sec % 60),
00743 now.msec);
00744
00745
00746 printf(" Info: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n",
00747 (int)port->info.encoding_name.slen,
00748 port->info.encoding_name.ptr,
00749 port->info.clock_rate,
00750 port->info.samples_per_frame * 1000 / port->info.clock_rate,
00751 good_number(bps, port->info.bytes_per_frame * port->info.clock_rate /
00752 port->info.samples_per_frame),
00753 good_number(ipbps, (port->info.bytes_per_frame+32) *
00754 port->info.clock_rate / port->info.clock_rate));
00755
00756 if (stat.rx.update_cnt == 0)
00757 strcpy(last_update, "never");
00758 else {
00759 pj_gettimeofday(&now);
00760 PJ_TIME_VAL_SUB(now, stat.rx.update);
00761 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
00762 now.sec / 3600,
00763 (now.sec % 3600) / 60,
00764 now.sec % 60,
00765 now.msec);
00766 }
00767
00768 printf(" RX stat last update: %s\n"
00769 " total %s packets %sB received (%sB +IP hdr)%s\n"
00770 " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
00771 " (msec) min avg max last dev\n"
00772 " loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n"
00773 " jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
00774 last_update,
00775 good_number(packets, stat.rx.pkt),
00776 good_number(bytes, stat.rx.bytes),
00777 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
00778 "",
00779 stat.rx.loss,
00780 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
00781 stat.rx.dup,
00782 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
00783 stat.rx.reorder,
00784 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
00785 "",
00786 stat.rx.loss_period.min / 1000.0,
00787 stat.rx.loss_period.mean / 1000.0,
00788 stat.rx.loss_period.max / 1000.0,
00789 stat.rx.loss_period.last / 1000.0,
00790 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
00791 "",
00792 stat.rx.jitter.min / 1000.0,
00793 stat.rx.jitter.mean / 1000.0,
00794 stat.rx.jitter.max / 1000.0,
00795 stat.rx.jitter.last / 1000.0,
00796 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
00797 ""
00798 );
00799
00800
00801 if (stat.tx.update_cnt == 0)
00802 strcpy(last_update, "never");
00803 else {
00804 pj_gettimeofday(&now);
00805 PJ_TIME_VAL_SUB(now, stat.tx.update);
00806 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
00807 now.sec / 3600,
00808 (now.sec % 3600) / 60,
00809 now.sec % 60,
00810 now.msec);
00811 }
00812
00813 printf(" TX stat last update: %s\n"
00814 " total %s packets %sB sent (%sB +IP hdr)%s\n"
00815 " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
00816 " (msec) min avg max last dev\n"
00817 " loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n"
00818 " jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
00819 last_update,
00820 good_number(packets, stat.tx.pkt),
00821 good_number(bytes, stat.tx.bytes),
00822 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
00823 "",
00824 stat.tx.loss,
00825 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
00826 stat.tx.dup,
00827 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
00828 stat.tx.reorder,
00829 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
00830 "",
00831 stat.tx.loss_period.min / 1000.0,
00832 stat.tx.loss_period.mean / 1000.0,
00833 stat.tx.loss_period.max / 1000.0,
00834 stat.tx.loss_period.last / 1000.0,
00835 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
00836 "",
00837 stat.tx.jitter.min / 1000.0,
00838 stat.tx.jitter.mean / 1000.0,
00839 stat.tx.jitter.max / 1000.0,
00840 stat.tx.jitter.last / 1000.0,
00841 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
00842 ""
00843 );
00844
00845
00846 printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
00847 stat.rtt.min / 1000.0,
00848 stat.rtt.mean / 1000.0,
00849 stat.rtt.max / 1000.0,
00850 stat.rtt.last / 1000.0,
00851 pj_math_stat_get_stddev(&stat.rtt) / 1000.0,
00852 ""
00853 );
00854
00855 #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
00856
00857 do {
00858 char loss[16], dup[16];
00859 char jitter[80];
00860 char toh[80];
00861 char plc[16], jba[16], jbr[16];
00862 char signal_lvl[16], noise_lvl[16], rerl[16];
00863 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
00864 pjmedia_rtcp_xr_stat xr_stat;
00865
00866 if (pjmedia_stream_get_stat_xr(stream, &xr_stat) != PJ_SUCCESS)
00867 break;
00868
00869 puts("\nExtended reports:");
00870
00871
00872 puts(" Statistics Summary");
00873
00874 if (xr_stat.rx.stat_sum.l)
00875 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
00876 else
00877 sprintf(loss, "(na)");
00878
00879 if (xr_stat.rx.stat_sum.d)
00880 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
00881 else
00882 sprintf(dup, "(na)");
00883
00884 if (xr_stat.rx.stat_sum.j) {
00885 unsigned jmin, jmax, jmean, jdev;
00886
00887 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
00888 port->info.clock_rate);
00889 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
00890 port->info.clock_rate);
00891 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
00892 port->info.clock_rate);
00893 SAMPLES_TO_USEC(jdev,
00894 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
00895 port->info.clock_rate);
00896 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
00897 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
00898 } else
00899 sprintf(jitter, "(report not available)");
00900
00901 if (xr_stat.rx.stat_sum.t) {
00902 sprintf(toh, "%11d %11d %11d %11d",
00903 xr_stat.rx.stat_sum.toh.min,
00904 xr_stat.rx.stat_sum.toh.mean,
00905 xr_stat.rx.stat_sum.toh.max,
00906 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
00907 } else
00908 sprintf(toh, "(report not available)");
00909
00910 if (xr_stat.rx.stat_sum.update.sec == 0)
00911 strcpy(last_update, "never");
00912 else {
00913 pj_gettimeofday(&now);
00914 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
00915 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
00916 now.sec / 3600,
00917 (now.sec % 3600) / 60,
00918 now.sec % 60,
00919 now.msec);
00920 }
00921
00922 printf(" RX last update: %s\n"
00923 " begin seq=%d, end seq=%d%s\n"
00924 " pkt loss=%s, dup=%s%s\n"
00925 " (msec) min avg max dev\n"
00926 " jitter : %s\n"
00927 " toh : %s\n",
00928 last_update,
00929 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
00930 "",
00931 loss, dup,
00932 "",
00933 jitter,
00934 toh
00935 );
00936
00937 if (xr_stat.tx.stat_sum.l)
00938 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
00939 else
00940 sprintf(loss, "(na)");
00941
00942 if (xr_stat.tx.stat_sum.d)
00943 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
00944 else
00945 sprintf(dup, "(na)");
00946
00947 if (xr_stat.tx.stat_sum.j) {
00948 unsigned jmin, jmax, jmean, jdev;
00949
00950 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
00951 port->info.clock_rate);
00952 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
00953 port->info.clock_rate);
00954 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
00955 port->info.clock_rate);
00956 SAMPLES_TO_USEC(jdev,
00957 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
00958 port->info.clock_rate);
00959 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
00960 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
00961 } else
00962 sprintf(jitter, "(report not available)");
00963
00964 if (xr_stat.tx.stat_sum.t) {
00965 sprintf(toh, "%11d %11d %11d %11d",
00966 xr_stat.tx.stat_sum.toh.min,
00967 xr_stat.tx.stat_sum.toh.mean,
00968 xr_stat.tx.stat_sum.toh.max,
00969 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
00970 } else
00971 sprintf(toh, "(report not available)");
00972
00973 if (xr_stat.tx.stat_sum.update.sec == 0)
00974 strcpy(last_update, "never");
00975 else {
00976 pj_gettimeofday(&now);
00977 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
00978 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
00979 now.sec / 3600,
00980 (now.sec % 3600) / 60,
00981 now.sec % 60,
00982 now.msec);
00983 }
00984
00985 printf(" TX last update: %s\n"
00986 " begin seq=%d, end seq=%d%s\n"
00987 " pkt loss=%s, dup=%s%s\n"
00988 " (msec) min avg max dev\n"
00989 " jitter : %s\n"
00990 " toh : %s\n",
00991 last_update,
00992 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
|