blob: 36809190188c3662890319b7e5f249e5c0e4320f [file] [log] [blame]
Benny Prijonobf13fee2006-04-20 11:13:32 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonobf13fee2006-04-20 11:13:32 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21
Benny Prijono1ec70b32006-06-20 15:39:07 +000022/**
23 * \page page_pjmedia_samples_streamutil_c Samples: Remote Streaming
24 *
25 * This example mainly demonstrates how to stream media file to remote
26 * peer using RTP.
27 *
28 * This file is pjsip-apps/src/samples/streamutil.c
29 *
30 * \includelineno streamutil.c
31 */
32
Benny Prijono48028712008-01-30 09:47:57 +000033#include <pjlib.h>
34#include <pjlib-util.h>
35#include <pjmedia.h>
36#include <pjmedia-codec.h>
37#include <pjmedia/transport_srtp.h>
38
39#include <stdlib.h> /* atoi() */
40#include <stdio.h>
41
42#include "util.h"
43
Benny Prijono1ec70b32006-06-20 15:39:07 +000044
Benny Prijonobf13fee2006-04-20 11:13:32 +000045static const char *desc =
Benny Prijono6d7a45f2006-04-24 23:13:00 +000046 " streamutil \n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000047 " \n"
48 " PURPOSE: \n"
49 " Demonstrate how to use pjmedia stream component to transmit/receive \n"
50 " RTP packets to/from sound device. \n"
51 "\n"
52 "\n"
53 " USAGE: \n"
Benny Prijono6d7a45f2006-04-24 23:13:00 +000054 " streamutil [options] \n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000055 "\n"
56 "\n"
57 " Options:\n"
Benny Prijono15953012006-04-27 22:37:08 +000058 " --codec=CODEC Set the codec name. \n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000059 " --local-port=PORT Set local RTP port (default=4000) \n"
60 " --remote=IP:PORT Set the remote peer. If this option is set, \n"
61 " the program will transmit RTP audio to the \n"
62 " specified address. (default: recv only) \n"
63 " --play-file=WAV Send audio from the WAV file instead of from \n"
64 " the sound device. \n"
Benny Prijono0d510782006-12-22 15:24:11 +000065 " --record-file=WAV Record incoming audio to WAV file instead of \n"
66 " playing it to sound device. \n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000067 " --send-recv Set stream direction to bidirectional. \n"
68 " --send-only Set stream direction to send only \n"
69 " --recv-only Set stream direction to recv only (default) \n"
Benny Prijonod8179652008-01-23 20:39:07 +000070
71#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
72 " --use-srtp[=NAME] Enable SRTP with crypto suite NAME \n"
73 " e.g: AES_CM_128_HMAC_SHA1_80 (default), \n"
74 " AES_CM_128_HMAC_SHA1_32 \n"
75 " Use this option along with the TX & RX keys, \n"
76 " formated of 60 hex digits (e.g: E148DA..) \n"
77 " --srtp-tx-key SRTP key for transmiting \n"
78 " --srtp-rx-key SRTP key for receiving \n"
79#endif
80
Benny Prijonobf13fee2006-04-20 11:13:32 +000081 "\n"
82;
83
84
85
Benny Prijonobf13fee2006-04-20 11:13:32 +000086
87#define THIS_FILE "stream.c"
88
89
Benny Prijonobf13fee2006-04-20 11:13:32 +000090
91/* Prototype */
92static void print_stream_stat(pjmedia_stream *stream);
93
Benny Prijonod8179652008-01-23 20:39:07 +000094/* Prototype for LIBSRTP utility in file datatypes.c */
95int hex_string_to_octet_string(char *raw, char *hex, int len);
Benny Prijonobf13fee2006-04-20 11:13:32 +000096
97/*
98 * Register all codecs.
99 */
100static pj_status_t init_codecs(pjmedia_endpt *med_endpt)
101{
102 pj_status_t status;
103
Benny Prijonofc24e692007-01-27 18:31:51 +0000104 /* To suppress warning about unused var when all codecs are disabled */
105 PJ_UNUSED_ARG(status);
106
Benny Prijonof9962132006-05-16 13:20:00 +0000107#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +0000108 status = pjmedia_codec_g711_init(med_endpt);
109 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +0000110#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000111
Benny Prijonof9962132006-05-16 13:20:00 +0000112#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +0000113 status = pjmedia_codec_gsm_init(med_endpt);
114 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +0000115#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000116
Benny Prijonof9962132006-05-16 13:20:00 +0000117#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +0000118 status = pjmedia_codec_speex_init(med_endpt, 0, -1, -1);
119 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +0000120#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000121
Benny Prijono7ffd7752008-03-17 14:07:53 +0000122#if defined(PJMEDIA_HAS_G722_CODEC) && PJMEDIA_HAS_G722_CODEC!=0
123 status = pjmedia_codec_g722_init(med_endpt);
124 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
125#endif
126
Benny Prijonof9962132006-05-16 13:20:00 +0000127#if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC!=0
Benny Prijono15953012006-04-27 22:37:08 +0000128 status = pjmedia_codec_l16_init(med_endpt, 0);
129 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +0000130#endif
Benny Prijono15953012006-04-27 22:37:08 +0000131
Benny Prijonobf13fee2006-04-20 11:13:32 +0000132 return PJ_SUCCESS;
133}
134
135
136/*
137 * Create stream based on the codec, dir, remote address, etc.
138 */
139static pj_status_t create_stream( pj_pool_t *pool,
140 pjmedia_endpt *med_endpt,
Benny Prijono15953012006-04-27 22:37:08 +0000141 const pjmedia_codec_info *codec_info,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000142 pjmedia_dir dir,
143 pj_uint16_t local_port,
144 const pj_sockaddr_in *rem_addr,
Benny Prijonod8179652008-01-23 20:39:07 +0000145#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
146 pj_bool_t use_srtp,
147 const pj_str_t *crypto_suite,
148 const pj_str_t *srtp_tx_key,
149 const pj_str_t *srtp_rx_key,
150#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000151 pjmedia_stream **p_stream )
152{
153 pjmedia_stream_info info;
Benny Prijonod8179652008-01-23 20:39:07 +0000154 pjmedia_transport *transport = NULL;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000155 pj_status_t status;
Benny Prijonod8179652008-01-23 20:39:07 +0000156#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
157 pjmedia_transport *srtp_tp = NULL;
158#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000159
160
161 /* Reset stream info. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000162 pj_bzero(&info, sizeof(info));
Benny Prijonobf13fee2006-04-20 11:13:32 +0000163
164
165 /* Initialize stream info formats */
166 info.type = PJMEDIA_TYPE_AUDIO;
167 info.dir = dir;
Benny Prijono15953012006-04-27 22:37:08 +0000168 pj_memcpy(&info.fmt, codec_info, sizeof(pjmedia_codec_info));
169 info.tx_pt = codec_info->pt;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000170 info.ssrc = pj_rand();
171
Nanang Izzuddin90f11cb2008-04-29 17:15:41 +0000172#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
173 /* Set default RTCP XR enabled/disabled */
174 info.rtcp_xr_enabled = PJ_TRUE;
175#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000176
177 /* Copy remote address */
178 pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
179
Benny Prijonoef0d7e72008-01-07 13:50:22 +0000180 /* If remote address is not set, set to an arbitrary address
181 * (otherwise stream will assert).
182 */
183 if (info.rem_addr.addr.sa_family == 0) {
184 const pj_str_t addr = pj_str("127.0.0.1");
185 pj_sockaddr_in_init(&info.rem_addr.ipv4, &addr, 0);
186 }
Benny Prijonobf13fee2006-04-20 11:13:32 +0000187
Benny Prijonob04c9e02006-05-17 17:17:39 +0000188 /* Create media transport */
189 status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
Benny Prijono30c658b2006-06-01 11:37:30 +0000190 0, &transport);
Benny Prijonob04c9e02006-05-17 17:17:39 +0000191 if (status != PJ_SUCCESS)
Benny Prijonobf13fee2006-04-20 11:13:32 +0000192 return status;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000193
Benny Prijonod8179652008-01-23 20:39:07 +0000194#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
195 /* Check if SRTP enabled */
196 if (use_srtp) {
197 pjmedia_srtp_crypto tx_plc, rx_plc;
198
199 status = pjmedia_transport_srtp_create(med_endpt, transport,
200 NULL, &srtp_tp);
201 if (status != PJ_SUCCESS)
202 return status;
203
204 pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto));
205 pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto));
206
207 tx_plc.key = *srtp_tx_key;
208 tx_plc.name = *crypto_suite;
209 rx_plc.key = *srtp_rx_key;
210 rx_plc.name = *crypto_suite;
211
212 status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc);
213 if (status != PJ_SUCCESS)
214 return status;
215
216 transport = srtp_tp;
217 }
218#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000219
220 /* Now that the stream info is initialized, we can create the
221 * stream.
222 */
223
Benny Prijonob04c9e02006-05-17 17:17:39 +0000224 status = pjmedia_stream_create( med_endpt, pool, &info,
Benny Prijonod8179652008-01-23 20:39:07 +0000225 transport,
226 NULL, p_stream);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000227
228 if (status != PJ_SUCCESS) {
229 app_perror(THIS_FILE, "Error creating stream", status);
Benny Prijonod8179652008-01-23 20:39:07 +0000230 pjmedia_transport_close(transport);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000231 return status;
232 }
233
234
235 return PJ_SUCCESS;
236}
237
238
239/*
Benny Prijono15953012006-04-27 22:37:08 +0000240 * usage()
241 */
242static void usage()
243{
244 puts(desc);
245}
246
247/*
Benny Prijonobf13fee2006-04-20 11:13:32 +0000248 * main()
249 */
250int main(int argc, char *argv[])
251{
252 pj_caching_pool cp;
253 pjmedia_endpt *med_endpt;
254 pj_pool_t *pool;
255 pjmedia_port *rec_file_port = NULL, *play_file_port = NULL;
256 pjmedia_master_port *master_port = NULL;
257 pjmedia_snd_port *snd_port = NULL;
258 pjmedia_stream *stream = NULL;
259 pjmedia_port *stream_port;
260 char tmp[10];
Benny Prijonob04c9e02006-05-17 17:17:39 +0000261 pj_status_t status;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000262
Benny Prijonod8179652008-01-23 20:39:07 +0000263#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
264 /* SRTP variables */
265 pj_bool_t use_srtp = PJ_FALSE;
266 char tmp_tx_key[64];
267 char tmp_rx_key[64];
268 pj_str_t srtp_tx_key = {NULL, 0};
269 pj_str_t srtp_rx_key = {NULL, 0};
270 pj_str_t srtp_crypto_suite = {NULL, 0};
271 int tmp_key_len;
272#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000273
274 /* Default values */
Benny Prijono15953012006-04-27 22:37:08 +0000275 const pjmedia_codec_info *codec_info;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000276 pjmedia_dir dir = PJMEDIA_DIR_DECODING;
277 pj_sockaddr_in remote_addr;
278 pj_uint16_t local_port = 4000;
Benny Prijono15953012006-04-27 22:37:08 +0000279 char *codec_id = NULL;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000280 char *rec_file = NULL;
281 char *play_file = NULL;
282
283 enum {
284 OPT_CODEC = 'c',
285 OPT_LOCAL_PORT = 'p',
286 OPT_REMOTE = 'r',
287 OPT_PLAY_FILE = 'w',
288 OPT_RECORD_FILE = 'R',
289 OPT_SEND_RECV = 'b',
290 OPT_SEND_ONLY = 's',
291 OPT_RECV_ONLY = 'i',
Benny Prijonod8179652008-01-23 20:39:07 +0000292#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
293 OPT_USE_SRTP = 'S',
294#endif
295 OPT_SRTP_TX_KEY = 'x',
296 OPT_SRTP_RX_KEY = 'y',
Benny Prijono15953012006-04-27 22:37:08 +0000297 OPT_HELP = 'h',
Benny Prijonobf13fee2006-04-20 11:13:32 +0000298 };
299
300 struct pj_getopt_option long_options[] = {
301 { "codec", 1, 0, OPT_CODEC },
302 { "local-port", 1, 0, OPT_LOCAL_PORT },
303 { "remote", 1, 0, OPT_REMOTE },
304 { "play-file", 1, 0, OPT_PLAY_FILE },
305 { "record-file", 1, 0, OPT_RECORD_FILE },
306 { "send-recv", 0, 0, OPT_SEND_RECV },
307 { "send-only", 0, 0, OPT_SEND_ONLY },
308 { "recv-only", 0, 0, OPT_RECV_ONLY },
Benny Prijonod8179652008-01-23 20:39:07 +0000309#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
310 { "use-srtp", 2, 0, OPT_USE_SRTP },
311 { "srtp-tx-key", 1, 0, OPT_SRTP_TX_KEY },
312 { "srtp-rx-key", 1, 0, OPT_SRTP_RX_KEY },
313#endif
Benny Prijono15953012006-04-27 22:37:08 +0000314 { "help", 0, 0, OPT_HELP },
Benny Prijonobf13fee2006-04-20 11:13:32 +0000315 { NULL, 0, 0, 0 },
316 };
317
318 int c;
319 int option_index;
320
321
Benny Prijonoac623b32006-07-03 15:19:31 +0000322 pj_bzero(&remote_addr, sizeof(remote_addr));
Benny Prijonobf13fee2006-04-20 11:13:32 +0000323
324
325 /* init PJLIB : */
326 status = pj_init();
327 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
328
329
330 /* Parse arguments */
331 pj_optind = 0;
Benny Prijono15953012006-04-27 22:37:08 +0000332 while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1) {
Benny Prijonobf13fee2006-04-20 11:13:32 +0000333
334 switch (c) {
335 case OPT_CODEC:
Benny Prijono15953012006-04-27 22:37:08 +0000336 codec_id = pj_optarg;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000337 break;
338
339 case OPT_LOCAL_PORT:
340 local_port = (pj_uint16_t) atoi(pj_optarg);
341 if (local_port < 1) {
342 printf("Error: invalid local port %s\n", pj_optarg);
343 return 1;
344 }
345 break;
346
347 case OPT_REMOTE:
348 {
349 pj_str_t ip = pj_str(strtok(pj_optarg, ":"));
350 pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":"));
351
352 status = pj_sockaddr_in_init(&remote_addr, &ip, port);
353 if (status != PJ_SUCCESS) {
354 app_perror(THIS_FILE, "Invalid remote address", status);
355 return 1;
356 }
357 }
358 break;
359
360 case OPT_PLAY_FILE:
361 play_file = pj_optarg;
362 break;
363
364 case OPT_RECORD_FILE:
365 rec_file = pj_optarg;
366 break;
367
368 case OPT_SEND_RECV:
369 dir = PJMEDIA_DIR_ENCODING_DECODING;
370 break;
371
372 case OPT_SEND_ONLY:
373 dir = PJMEDIA_DIR_ENCODING;
374 break;
375
376 case OPT_RECV_ONLY:
377 dir = PJMEDIA_DIR_DECODING;
378 break;
379
Benny Prijonod8179652008-01-23 20:39:07 +0000380#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
381 case OPT_USE_SRTP:
382 use_srtp = PJ_TRUE;
383 if (pj_optarg) {
384 pj_strset(&srtp_crypto_suite, pj_optarg, strlen(pj_optarg));
385 } else {
386 srtp_crypto_suite = pj_str("AES_CM_128_HMAC_SHA1_80");
387 }
388 break;
389
390 case OPT_SRTP_TX_KEY:
391 tmp_key_len = hex_string_to_octet_string(tmp_tx_key, pj_optarg, strlen(pj_optarg));
392 pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2);
393 break;
394
395 case OPT_SRTP_RX_KEY:
396 tmp_key_len = hex_string_to_octet_string(tmp_rx_key, pj_optarg, strlen(pj_optarg));
397 pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2);
398 break;
399#endif
400
Benny Prijono15953012006-04-27 22:37:08 +0000401 case OPT_HELP:
402 usage();
403 return 1;
404
Benny Prijonobf13fee2006-04-20 11:13:32 +0000405 default:
406 printf("Invalid options %s\n", argv[pj_optind]);
407 return 1;
408 }
409
410 }
411
412
413 /* Verify arguments. */
414 if (dir & PJMEDIA_DIR_ENCODING) {
415 if (remote_addr.sin_addr.s_addr == 0) {
416 printf("Error: remote address must be set\n");
417 return 1;
418 }
419 }
420
421 if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) {
422 printf("Direction is set to --send-only because of --play-file\n");
423 dir = PJMEDIA_DIR_ENCODING;
424 }
425
Benny Prijonod8179652008-01-23 20:39:07 +0000426#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
427 /* SRTP validation */
428 if (use_srtp) {
429 if (!srtp_tx_key.slen || !srtp_rx_key.slen)
430 {
431 printf("Error: Key for each SRTP stream direction must be set\n");
432 return 1;
433 }
434 }
435#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000436
437 /* Must create a pool factory before we can allocate any memory. */
438 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
439
440 /*
441 * Initialize media endpoint.
442 * This will implicitly initialize PJMEDIA too.
443 */
444 status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
445 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
446
447 /* Create memory pool for application purpose */
448 pool = pj_pool_create( &cp.factory, /* pool factory */
449 "app", /* pool name. */
450 4000, /* init size */
451 4000, /* increment size */
452 NULL /* callback on error */
453 );
454
455
456 /* Register all supported codecs */
457 status = init_codecs(med_endpt);
458 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
459
460
Benny Prijono15953012006-04-27 22:37:08 +0000461 /* Find which codec to use. */
462 if (codec_id) {
463 unsigned count = 1;
464 pj_str_t str_codec_id = pj_str(codec_id);
465 pjmedia_codec_mgr *codec_mgr = pjmedia_endpt_get_codec_mgr(med_endpt);
466 status = pjmedia_codec_mgr_find_codecs_by_id( codec_mgr,
467 &str_codec_id, &count,
468 &codec_info, NULL);
469 if (status != PJ_SUCCESS) {
470 printf("Error: unable to find codec %s\n", codec_id);
471 return 1;
472 }
473 } else {
474 /* Default to pcmu */
475 pjmedia_codec_mgr_get_codec_info( pjmedia_endpt_get_codec_mgr(med_endpt),
476 0, &codec_info);
477 }
478
Benny Prijonobf13fee2006-04-20 11:13:32 +0000479 /* Create stream based on program arguments */
Benny Prijono15953012006-04-27 22:37:08 +0000480 status = create_stream(pool, med_endpt, codec_info, dir, local_port,
Benny Prijonod8179652008-01-23 20:39:07 +0000481 &remote_addr,
482#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
483 use_srtp, &srtp_crypto_suite,
484 &srtp_tx_key, &srtp_rx_key,
485#endif
486 &stream);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000487 if (status != PJ_SUCCESS)
488 goto on_exit;
489
490
491 /* Get the port interface of the stream */
492 status = pjmedia_stream_get_port( stream, &stream_port);
493 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
494
495
496 if (play_file) {
Benny Prijono15953012006-04-27 22:37:08 +0000497 unsigned wav_ptime;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000498
Benny Prijono15953012006-04-27 22:37:08 +0000499 wav_ptime = stream_port->info.samples_per_frame * 1000 /
500 stream_port->info.clock_rate;
501 status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime,
Benny Prijono6fd4b8f2006-06-22 18:51:50 +0000502 0, -1, &play_file_port);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000503 if (status != PJ_SUCCESS) {
504 app_perror(THIS_FILE, "Unable to use file", status);
505 goto on_exit;
506 }
507
508 status = pjmedia_master_port_create(pool, play_file_port, stream_port,
509 0, &master_port);
510 if (status != PJ_SUCCESS) {
511 app_perror(THIS_FILE, "Unable to create master port", status);
512 goto on_exit;
513 }
514
515 status = pjmedia_master_port_start(master_port);
516 if (status != PJ_SUCCESS) {
517 app_perror(THIS_FILE, "Error starting master port", status);
518 goto on_exit;
519 }
520
Benny Prijono0d510782006-12-22 15:24:11 +0000521 printf("Playing from WAV file %s..\n", play_file);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000522
Benny Prijono0d510782006-12-22 15:24:11 +0000523 } else if (rec_file) {
524
525 status = pjmedia_wav_writer_port_create(pool, rec_file,
526 stream_port->info.clock_rate,
527 stream_port->info.channel_count,
528 stream_port->info.samples_per_frame,
529 stream_port->info.bits_per_sample,
530 0, 0, &rec_file_port);
531 if (status != PJ_SUCCESS) {
532 app_perror(THIS_FILE, "Unable to use file", status);
533 goto on_exit;
534 }
535
536 status = pjmedia_master_port_create(pool, stream_port, rec_file_port,
537 0, &master_port);
538 if (status != PJ_SUCCESS) {
539 app_perror(THIS_FILE, "Unable to create master port", status);
540 goto on_exit;
541 }
542
543 status = pjmedia_master_port_start(master_port);
544 if (status != PJ_SUCCESS) {
545 app_perror(THIS_FILE, "Error starting master port", status);
546 goto on_exit;
547 }
548
549 printf("Recording to WAV file %s..\n", rec_file);
550
Benny Prijonobf13fee2006-04-20 11:13:32 +0000551 } else {
552
553 /* Create sound device port. */
554 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
555 status = pjmedia_snd_port_create(pool, -1, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000556 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000557 stream_port->info.channel_count,
558 stream_port->info.samples_per_frame,
559 stream_port->info.bits_per_sample,
560 0, &snd_port);
561 else if (dir == PJMEDIA_DIR_ENCODING)
562 status = pjmedia_snd_port_create_rec(pool, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000563 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000564 stream_port->info.channel_count,
565 stream_port->info.samples_per_frame,
566 stream_port->info.bits_per_sample,
567 0, &snd_port);
568 else
569 status = pjmedia_snd_port_create_player(pool, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000570 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000571 stream_port->info.channel_count,
572 stream_port->info.samples_per_frame,
573 stream_port->info.bits_per_sample,
574 0, &snd_port);
575
576
577 if (status != PJ_SUCCESS) {
578 app_perror(THIS_FILE, "Unable to create sound port", status);
579 goto on_exit;
580 }
581
582 /* Connect sound port to stream */
583 status = pjmedia_snd_port_connect( snd_port, stream_port );
584 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
585
586 }
587
Benny Prijono69d9d192006-05-21 19:00:28 +0000588 /* Start streaming */
589 pjmedia_stream_start(stream);
590
Benny Prijonobf13fee2006-04-20 11:13:32 +0000591
592 /* Done */
593
594 if (dir == PJMEDIA_DIR_DECODING)
595 printf("Stream is active, dir is recv-only, local port is %d\n",
596 local_port);
597 else if (dir == PJMEDIA_DIR_ENCODING)
598 printf("Stream is active, dir is send-only, sending to %s:%d\n",
599 pj_inet_ntoa(remote_addr.sin_addr),
600 pj_ntohs(remote_addr.sin_port));
601 else
602 printf("Stream is active, send/recv, local port is %d, "
603 "sending to %s:%d\n",
604 local_port,
605 pj_inet_ntoa(remote_addr.sin_addr),
606 pj_ntohs(remote_addr.sin_port));
607
608
609 for (;;) {
610
611 puts("");
612 puts("Commands:");
613 puts(" s Display media statistics");
614 puts(" q Quit");
615 puts("");
616
617 printf("Command: "); fflush(stdout);
618
Benny Prijono32d267b2009-01-01 22:08:21 +0000619 if (fgets(tmp, sizeof(tmp), stdin) == NULL) {
620 puts("EOF while reading stdin, will quit now..");
621 break;
622 }
Benny Prijonobf13fee2006-04-20 11:13:32 +0000623
624 if (tmp[0] == 's')
625 print_stream_stat(stream);
626 else if (tmp[0] == 'q')
627 break;
628
629 }
630
631
632
633 /* Start deinitialization: */
634on_exit:
635
636 /* Destroy sound device */
637 if (snd_port) {
638 pjmedia_snd_port_destroy( snd_port );
639 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
640 }
641
642 /* If there is master port, then we just need to destroy master port
643 * (it will recursively destroy upstream and downstream ports, which
644 * in this case are file_port and stream_port).
645 */
646 if (master_port) {
Benny Prijono22a300a2006-06-14 20:04:55 +0000647 pjmedia_master_port_destroy(master_port, PJ_TRUE);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000648 play_file_port = NULL;
649 stream = NULL;
650 }
651
652 /* Destroy stream */
653 if (stream) {
Benny Prijonob04c9e02006-05-17 17:17:39 +0000654 pjmedia_transport *tp;
655
656 tp = pjmedia_stream_get_transport(stream);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000657 pjmedia_stream_destroy(stream);
Benny Prijonod8179652008-01-23 20:39:07 +0000658
659 pjmedia_transport_close(tp);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000660 }
661
662 /* Destroy file ports */
663 if (play_file_port)
664 pjmedia_port_destroy( play_file_port );
665 if (rec_file_port)
666 pjmedia_port_destroy( rec_file_port );
667
668
669 /* Release application pool */
670 pj_pool_release( pool );
671
672 /* Destroy media endpoint. */
673 pjmedia_endpt_destroy( med_endpt );
674
675 /* Destroy pool factory */
676 pj_caching_pool_destroy( &cp );
677
Benny Prijonoaf1bb1e2006-11-21 12:39:31 +0000678 /* Shutdown PJLIB */
679 pj_shutdown();
680
Benny Prijonobf13fee2006-04-20 11:13:32 +0000681
682 return (status == PJ_SUCCESS) ? 0 : 1;
683}
684
685
686
687
688static const char *good_number(char *buf, pj_int32_t val)
689{
690 if (val < 1000) {
691 pj_ansi_sprintf(buf, "%d", val);
692 } else if (val < 1000000) {
693 pj_ansi_sprintf(buf, "%d.%dK",
694 val / 1000,
695 (val % 1000) / 100);
696 } else {
697 pj_ansi_sprintf(buf, "%d.%02dM",
698 val / 1000000,
699 (val % 1000000) / 10000);
700 }
701
702 return buf;
703}
704
705
Nanang Izzuddin90f11cb2008-04-29 17:15:41 +0000706#define SAMPLES_TO_USEC(usec, samples, clock_rate) \
707 do { \
708 if (samples <= 4294) \
709 usec = samples * 1000000 / clock_rate; \
710 else { \
711 usec = samples * 1000 / clock_rate; \
712 usec *= 1000; \
713 } \
714 } while(0)
715
716#define PRINT_VOIP_MTC_VAL(s, v) \
717 if (v == 127) \
718 sprintf(s, "(na)"); \
719 else \
720 sprintf(s, "%d", v)
721
Benny Prijonobf13fee2006-04-20 11:13:32 +0000722
723/*
724 * Print stream statistics
725 */
726static void print_stream_stat(pjmedia_stream *stream)
727{
728 char duration[80], last_update[80];
729 char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16];
730 pjmedia_port *port;
731 pjmedia_rtcp_stat stat;
732 pj_time_val now;
733
734
735 pj_gettimeofday(&now);
736 pjmedia_stream_get_stat(stream, &stat);
737 pjmedia_stream_get_port(stream, &port);
738
739 puts("Stream statistics:");
740
741 /* Print duration */
742 PJ_TIME_VAL_SUB(now, stat.start);
743 sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld",
744 now.sec / 3600,
745 (now.sec % 3600) / 60,
746 (now.sec % 60),
747 now.msec);
748
749
750 printf(" Info: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n",
751 (int)port->info.encoding_name.slen,
752 port->info.encoding_name.ptr,
Benny Prijono15953012006-04-27 22:37:08 +0000753 port->info.clock_rate,
754 port->info.samples_per_frame * 1000 / port->info.clock_rate,
755 good_number(bps, port->info.bytes_per_frame * port->info.clock_rate /
756 port->info.samples_per_frame),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000757 good_number(ipbps, (port->info.bytes_per_frame+32) *
Benny Prijono15953012006-04-27 22:37:08 +0000758 port->info.clock_rate / port->info.clock_rate));
Benny Prijonobf13fee2006-04-20 11:13:32 +0000759
760 if (stat.rx.update_cnt == 0)
761 strcpy(last_update, "never");
762 else {
763 pj_gettimeofday(&now);
764 PJ_TIME_VAL_SUB(now, stat.rx.update);
765 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
766 now.sec / 3600,
767 (now.sec % 3600) / 60,
768 now.sec % 60,
769 now.msec);
770 }
771
772 printf(" RX stat last update: %s\n"
773 " total %s packets %sB received (%sB +IP hdr)%s\n"
774 " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000775 " (msec) min avg max last dev\n"
776 " loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n"
777 " jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
Benny Prijonobf13fee2006-04-20 11:13:32 +0000778 last_update,
779 good_number(packets, stat.rx.pkt),
780 good_number(bytes, stat.rx.bytes),
781 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
782 "",
783 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +0000784 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000785 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +0000786 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000787 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +0000788 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000789 "",
790 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000791 stat.rx.loss_period.mean / 1000.0,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000792 stat.rx.loss_period.max / 1000.0,
793 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000794 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000795 "",
796 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000797 stat.rx.jitter.mean / 1000.0,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000798 stat.rx.jitter.max / 1000.0,
799 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000800 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000801 ""
802 );
803
804
805 if (stat.tx.update_cnt == 0)
806 strcpy(last_update, "never");
807 else {
808 pj_gettimeofday(&now);
809 PJ_TIME_VAL_SUB(now, stat.tx.update);
810 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
811 now.sec / 3600,
812 (now.sec % 3600) / 60,
813 now.sec % 60,
814 now.msec);
815 }
816
817 printf(" TX stat last update: %s\n"
818 " total %s packets %sB sent (%sB +IP hdr)%s\n"
819 " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000820 " (msec) min avg max last dev\n"
821 " loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n"
822 " jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
Benny Prijonobf13fee2006-04-20 11:13:32 +0000823 last_update,
824 good_number(packets, stat.tx.pkt),
825 good_number(bytes, stat.tx.bytes),
826 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
827 "",
828 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +0000829 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000830 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +0000831 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000832 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +0000833 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000834 "",
835 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000836 stat.tx.loss_period.mean / 1000.0,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000837 stat.tx.loss_period.max / 1000.0,
838 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000839 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000840 "",
841 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000842 stat.tx.jitter.mean / 1000.0,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000843 stat.tx.jitter.max / 1000.0,
844 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000845 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000846 ""
847 );
848
849
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000850 printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
Benny Prijonobf13fee2006-04-20 11:13:32 +0000851 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000852 stat.rtt.mean / 1000.0,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000853 stat.rtt.max / 1000.0,
854 stat.rtt.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000855 pj_math_stat_get_stddev(&stat.rtt) / 1000.0,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000856 ""
857 );
858
Nanang Izzuddin90f11cb2008-04-29 17:15:41 +0000859#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
860 /* RTCP XR Reports */
861 do {
862 char loss[16], dup[16];
863 char jitter[80];
864 char toh[80];
865 char plc[16], jba[16], jbr[16];
866 char signal_lvl[16], noise_lvl[16], rerl[16];
867 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
868 pjmedia_rtcp_xr_stat xr_stat;
869
870 if (pjmedia_stream_get_stat_xr(stream, &xr_stat) != PJ_SUCCESS)
871 break;
872
873 puts("\nExtended reports:");
874
875 /* Statistics Summary */
876 puts(" Statistics Summary");
877
878 if (xr_stat.rx.stat_sum.l)
879 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
880 else
881 sprintf(loss, "(na)");
882
883 if (xr_stat.rx.stat_sum.d)
884 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
885 else
886 sprintf(dup, "(na)");
887
888 if (xr_stat.rx.stat_sum.j) {
889 unsigned jmin, jmax, jmean, jdev;
890
891 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
892 port->info.clock_rate);
893 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
894 port->info.clock_rate);
895 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
896 port->info.clock_rate);
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000897 SAMPLES_TO_USEC(jdev,
898 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
899 port->info.clock_rate);
Nanang Izzuddin90f11cb2008-04-29 17:15:41 +0000900 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
901 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
902 } else
903 sprintf(jitter, "(report not available)");
904
905 if (xr_stat.rx.stat_sum.t) {
906 sprintf(toh, "%11d %11d %11d %11d",
907 xr_stat.rx.stat_sum.toh.min,
908 xr_stat.rx.stat_sum.toh.mean,
909 xr_stat.rx.stat_sum.toh.max,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000910 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
Nanang Izzuddin90f11cb2008-04-29 17:15:41 +0000911 } else
912 sprintf(toh, "(report not available)");
913
914 if (xr_stat.rx.stat_sum.update.sec == 0)
915 strcpy(last_update, "never");
916 else {
917 pj_gettimeofday(&now);
918 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
919 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
920 now.sec / 3600,
921 (now.sec % 3600) / 60,
922 now.sec % 60,
923 now.msec);
924 }
925
926 printf(" RX last update: %s\n"
927 " begin seq=%d, end seq=%d%s\n"
928 " pkt loss=%s, dup=%s%s\n"
929 " (msec) min avg max dev\n"
930 " jitter : %s\n"
931 " toh : %s\n",
932 last_update,
933 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
934 "",
935 loss, dup,
936 "",
937 jitter,
938 toh
939 );
940
941 if (xr_stat.tx.stat_sum.l)
942 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
943 else
944 sprintf(loss, "(na)");
945
946 if (xr_stat.tx.stat_sum.d)
947 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
948 else
949 sprintf(dup, "(na)");
950
951 if (xr_stat.tx.stat_sum.j) {
952 unsigned jmin, jmax, jmean, jdev;
953
954 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
955 port->info.clock_rate);
956 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
957 port->info.clock_rate);
958 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
959 port->info.clock_rate);
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000960 SAMPLES_TO_USEC(jdev,
961 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
962 port->info.clock_rate);
Nanang Izzuddin90f11cb2008-04-29 17:15:41 +0000963 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
964 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
965 } else
966 sprintf(jitter, "(report not available)");
967
968 if (xr_stat.tx.stat_sum.t) {
969 sprintf(toh, "%11d %11d %11d %11d",
970 xr_stat.tx.stat_sum.toh.min,
971 xr_stat.tx.stat_sum.toh.mean,
972 xr_stat.tx.stat_sum.toh.max,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +0000973 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
Nanang Izzuddin90f11cb2008-04-29 17:15:41 +0000974 } else
975 sprintf(toh, "(report not available)");
976
977 if (xr_stat.tx.stat_sum.update.sec == 0)
978 strcpy(last_update, "never");
979 else {
980 pj_gettimeofday(&now);
981 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
982 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
983 now.sec / 3600,
984 (now.sec % 3600) / 60,
985 now.sec % 60,
986 now.msec);
987 }
988
989 printf(" TX last update: %s\n"
990 " begin seq=%d, end seq=%d%s\n"
991 " pkt loss=%s, dup=%s%s\n"
992 " (msec) min avg max dev\n"
993 " jitter : %s\n"
994 " toh : %s\n",
995 last_update,
996 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
997 "",
998 loss, dup,
999 "",
1000 jitter,
1001 toh
1002 );
1003
1004 /* VoIP Metrics */
1005 puts(" VoIP Metrics");
1006
1007 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
1008 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
1009 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
1010 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
1011 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
1012 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
1013 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
1014
1015 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
1016 case PJMEDIA_RTCP_XR_PLC_DIS:
1017 sprintf(plc, "DISABLED");
1018 break;
1019 case PJMEDIA_RTCP_XR_PLC_ENH:
1020 sprintf(plc, "ENHANCED");
1021 break;
1022 case PJMEDIA_RTCP_XR_PLC_STD:
1023 sprintf(plc, "STANDARD");
1024 break;
1025 case PJMEDIA_RTCP_XR_PLC_UNK:
1026 default:
1027 sprintf(plc, "UNKNOWN");
1028 break;
1029 }
1030
1031 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
1032 case PJMEDIA_RTCP_XR_JB_FIXED:
1033 sprintf(jba, "FIXED");
1034 break;
1035 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
1036 sprintf(jba, "ADAPTIVE");
1037 break;
1038 default:
1039 sprintf(jba, "UNKNOWN");
1040 break;
1041 }
1042
1043 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
1044
1045 if (xr_stat.rx.voip_mtc.update.sec == 0)
1046 strcpy(last_update, "never");
1047 else {
1048 pj_gettimeofday(&now);
1049 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
1050 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1051 now.sec / 3600,
1052 (now.sec % 3600) / 60,
1053 now.sec % 60,
1054 now.msec);
1055 }
1056
1057 printf(" RX last update: %s\n"
1058 " packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
1059 " burst : density=%d (%.2f%%), duration=%d%s\n"
1060 " gap : density=%d (%.2f%%), duration=%d%s\n"
1061 " delay : round trip=%d%s, end system=%d%s\n"
1062 " level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
1063 " quality : R factor=%s, ext R factor=%s\n"
1064 " MOS LQ=%s, MOS CQ=%s\n"
1065 " config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
1066 " JB delay : cur=%d%s, max=%d%s, abs max=%d%s\n",
1067 last_update,
1068 /* pakcets */
1069 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
1070 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
1071 /* burst */
1072 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
1073 xr_stat.rx.voip_mtc.burst_dur, "ms",
1074 /* gap */
1075 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
1076 xr_stat.rx.voip_mtc.gap_dur, "ms",
1077 /* delay */
1078 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
1079 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
1080 /* level */
1081 signal_lvl, "dB",
1082 noise_lvl, "dB",
1083 rerl, "",
1084 /* quality */
1085 r_factor, ext_r_factor, mos_lq, mos_cq,
1086 /* config */
1087 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
1088 /* JB delay */
1089 xr_stat.rx.voip_mtc.jb_nom, "ms",
1090 xr_stat.rx.voip_mtc.jb_max, "ms",
1091 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
1092 );
1093
1094 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
1095 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
1096 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
1097 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
1098 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
1099 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
1100 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
1101
1102 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
1103 case PJMEDIA_RTCP_XR_PLC_DIS:
1104 sprintf(plc, "DISABLED");
1105 break;
1106 case PJMEDIA_RTCP_XR_PLC_ENH:
1107 sprintf(plc, "ENHANCED");
1108 break;
1109 case PJMEDIA_RTCP_XR_PLC_STD:
1110 sprintf(plc, "STANDARD");
1111 break;
1112 case PJMEDIA_RTCP_XR_PLC_UNK:
1113 default:
1114 sprintf(plc, "unknown");
1115 break;
1116 }
1117
1118 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
1119 case PJMEDIA_RTCP_XR_JB_FIXED:
1120 sprintf(jba, "FIXED");
1121 break;
1122 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
1123 sprintf(jba, "ADAPTIVE");
1124 break;
1125 default:
1126 sprintf(jba, "unknown");
1127 break;
1128 }
1129
1130 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
1131
1132 if (xr_stat.tx.voip_mtc.update.sec == 0)
1133 strcpy(last_update, "never");
1134 else {
1135 pj_gettimeofday(&now);
1136 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
1137 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1138 now.sec / 3600,
1139 (now.sec % 3600) / 60,
1140 now.sec % 60,
1141 now.msec);
1142 }
1143
1144 printf(" TX last update: %s\n"
1145 " packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
1146 " burst : density=%d (%.2f%%), duration=%d%s\n"
1147 " gap : density=%d (%.2f%%), duration=%d%s\n"
1148 " delay : round trip=%d%s, end system=%d%s\n"
1149 " level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
1150 " quality : R factor=%s, ext R factor=%s\n"
1151 " MOS LQ=%s, MOS CQ=%s\n"
1152 " config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
1153 " JB delay : cur=%d%s, max=%d%s, abs max=%d%s\n",
1154 last_update,
1155 /* pakcets */
1156 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
1157 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
1158 /* burst */
1159 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
1160 xr_stat.tx.voip_mtc.burst_dur, "ms",
1161 /* gap */
1162 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
1163 xr_stat.tx.voip_mtc.gap_dur, "ms",
1164 /* delay */
1165 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
1166 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
1167 /* level */
1168 signal_lvl, "dB",
1169 noise_lvl, "dB",
1170 rerl, "",
1171 /* quality */
1172 r_factor, ext_r_factor, mos_lq, mos_cq,
1173 /* config */
1174 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
1175 /* JB delay */
1176 xr_stat.tx.voip_mtc.jb_nom, "ms",
1177 xr_stat.tx.voip_mtc.jb_max, "ms",
1178 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
1179 );
1180
1181
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00001182 /* RTT delay (by receiver side) */
1183 printf(" (msec) min avg max last dev\n");
1184 printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
Nanang Izzuddin90f11cb2008-04-29 17:15:41 +00001185 xr_stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00001186 xr_stat.rtt.mean / 1000.0,
Nanang Izzuddin90f11cb2008-04-29 17:15:41 +00001187 xr_stat.rtt.max / 1000.0,
1188 xr_stat.rtt.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00001189 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0,
Nanang Izzuddin90f11cb2008-04-29 17:15:41 +00001190 ""
1191 );
1192 } while (0);
1193#endif /* PJMEDIA_HAS_RTCP_XR */
1194
Benny Prijonobf13fee2006-04-20 11:13:32 +00001195}
1196