blob: bfe8b4ead5e1111e40926915323623e6910da141 [file] [log] [blame]
Benny Prijonobf13fee2006-04-20 11:13:32 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20
21static const char *desc =
Benny Prijono6d7a45f2006-04-24 23:13:00 +000022 " streamutil \n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000023 " \n"
24 " PURPOSE: \n"
25 " Demonstrate how to use pjmedia stream component to transmit/receive \n"
26 " RTP packets to/from sound device. \n"
27 "\n"
28 "\n"
29 " USAGE: \n"
Benny Prijono6d7a45f2006-04-24 23:13:00 +000030 " streamutil [options] \n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000031 "\n"
32 "\n"
33 " Options:\n"
Benny Prijono15953012006-04-27 22:37:08 +000034 " --codec=CODEC Set the codec name. \n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000035 " --local-port=PORT Set local RTP port (default=4000) \n"
36 " --remote=IP:PORT Set the remote peer. If this option is set, \n"
37 " the program will transmit RTP audio to the \n"
38 " specified address. (default: recv only) \n"
39 " --play-file=WAV Send audio from the WAV file instead of from \n"
40 " the sound device. \n"
41// " --record-file=WAV Record incoming audio to WAV file instead of \n"
42// " playing it to sound device. \n"
43 " --send-recv Set stream direction to bidirectional. \n"
44 " --send-only Set stream direction to send only \n"
45 " --recv-only Set stream direction to recv only (default) \n"
46 "\n"
47;
48
49
50
51#include <pjlib.h>
52#include <pjlib-util.h>
53#include <pjmedia.h>
54#include <pjmedia-codec.h>
55
56#include <stdlib.h> /* atoi() */
57#include <stdio.h>
58
59#include "util.h"
60
61
62#define THIS_FILE "stream.c"
63
64
Benny Prijonobf13fee2006-04-20 11:13:32 +000065
66/* Prototype */
67static void print_stream_stat(pjmedia_stream *stream);
68
69
70/*
71 * Register all codecs.
72 */
73static pj_status_t init_codecs(pjmedia_endpt *med_endpt)
74{
75 pj_status_t status;
76
Benny Prijonof9962132006-05-16 13:20:00 +000077#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +000078 status = pjmedia_codec_g711_init(med_endpt);
79 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +000080#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +000081
Benny Prijonof9962132006-05-16 13:20:00 +000082#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +000083 status = pjmedia_codec_gsm_init(med_endpt);
84 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +000085#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +000086
Benny Prijonof9962132006-05-16 13:20:00 +000087#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +000088 status = pjmedia_codec_speex_init(med_endpt, 0, -1, -1);
89 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +000090#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +000091
Benny Prijonof9962132006-05-16 13:20:00 +000092#if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC!=0
Benny Prijono15953012006-04-27 22:37:08 +000093 status = pjmedia_codec_l16_init(med_endpt, 0);
94 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +000095#endif
Benny Prijono15953012006-04-27 22:37:08 +000096
Benny Prijonobf13fee2006-04-20 11:13:32 +000097 return PJ_SUCCESS;
98}
99
100
101/*
102 * Create stream based on the codec, dir, remote address, etc.
103 */
104static pj_status_t create_stream( pj_pool_t *pool,
105 pjmedia_endpt *med_endpt,
Benny Prijono15953012006-04-27 22:37:08 +0000106 const pjmedia_codec_info *codec_info,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000107 pjmedia_dir dir,
108 pj_uint16_t local_port,
109 const pj_sockaddr_in *rem_addr,
110 pjmedia_stream **p_stream )
111{
112 pjmedia_stream_info info;
Benny Prijonob04c9e02006-05-17 17:17:39 +0000113 pjmedia_transport *transport;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000114 pj_status_t status;
115
116
117 /* Reset stream info. */
118 pj_memset(&info, 0, sizeof(info));
119
120
121 /* Initialize stream info formats */
122 info.type = PJMEDIA_TYPE_AUDIO;
123 info.dir = dir;
Benny Prijono15953012006-04-27 22:37:08 +0000124 pj_memcpy(&info.fmt, codec_info, sizeof(pjmedia_codec_info));
125 info.tx_pt = codec_info->pt;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000126 info.ssrc = pj_rand();
127
128
129 /* Copy remote address */
130 pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
131
132
Benny Prijonob04c9e02006-05-17 17:17:39 +0000133 /* Create media transport */
134 status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
135 &transport);
136 if (status != PJ_SUCCESS)
Benny Prijonobf13fee2006-04-20 11:13:32 +0000137 return status;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000138
139
140 /* Now that the stream info is initialized, we can create the
141 * stream.
142 */
143
Benny Prijonob04c9e02006-05-17 17:17:39 +0000144 status = pjmedia_stream_create( med_endpt, pool, &info,
145 transport, NULL, p_stream);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000146
147 if (status != PJ_SUCCESS) {
148 app_perror(THIS_FILE, "Error creating stream", status);
Benny Prijonob04c9e02006-05-17 17:17:39 +0000149 pjmedia_transport_udp_close(transport);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000150 return status;
151 }
152
153
154 return PJ_SUCCESS;
155}
156
157
158/*
Benny Prijono15953012006-04-27 22:37:08 +0000159 * usage()
160 */
161static void usage()
162{
163 puts(desc);
164}
165
166/*
Benny Prijonobf13fee2006-04-20 11:13:32 +0000167 * main()
168 */
169int main(int argc, char *argv[])
170{
171 pj_caching_pool cp;
172 pjmedia_endpt *med_endpt;
173 pj_pool_t *pool;
174 pjmedia_port *rec_file_port = NULL, *play_file_port = NULL;
175 pjmedia_master_port *master_port = NULL;
176 pjmedia_snd_port *snd_port = NULL;
177 pjmedia_stream *stream = NULL;
178 pjmedia_port *stream_port;
179 char tmp[10];
Benny Prijonob04c9e02006-05-17 17:17:39 +0000180 pj_status_t status;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000181
182
183 /* Default values */
Benny Prijono15953012006-04-27 22:37:08 +0000184 const pjmedia_codec_info *codec_info;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000185 pjmedia_dir dir = PJMEDIA_DIR_DECODING;
186 pj_sockaddr_in remote_addr;
187 pj_uint16_t local_port = 4000;
Benny Prijono15953012006-04-27 22:37:08 +0000188 char *codec_id = NULL;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000189 char *rec_file = NULL;
190 char *play_file = NULL;
191
192 enum {
193 OPT_CODEC = 'c',
194 OPT_LOCAL_PORT = 'p',
195 OPT_REMOTE = 'r',
196 OPT_PLAY_FILE = 'w',
197 OPT_RECORD_FILE = 'R',
198 OPT_SEND_RECV = 'b',
199 OPT_SEND_ONLY = 's',
200 OPT_RECV_ONLY = 'i',
Benny Prijono15953012006-04-27 22:37:08 +0000201 OPT_HELP = 'h',
Benny Prijonobf13fee2006-04-20 11:13:32 +0000202 };
203
204 struct pj_getopt_option long_options[] = {
205 { "codec", 1, 0, OPT_CODEC },
206 { "local-port", 1, 0, OPT_LOCAL_PORT },
207 { "remote", 1, 0, OPT_REMOTE },
208 { "play-file", 1, 0, OPT_PLAY_FILE },
209 { "record-file", 1, 0, OPT_RECORD_FILE },
210 { "send-recv", 0, 0, OPT_SEND_RECV },
211 { "send-only", 0, 0, OPT_SEND_ONLY },
212 { "recv-only", 0, 0, OPT_RECV_ONLY },
Benny Prijono15953012006-04-27 22:37:08 +0000213 { "help", 0, 0, OPT_HELP },
Benny Prijonobf13fee2006-04-20 11:13:32 +0000214 { NULL, 0, 0, 0 },
215 };
216
217 int c;
218 int option_index;
219
220
221 pj_memset(&remote_addr, 0, sizeof(remote_addr));
222
223
224 /* init PJLIB : */
225 status = pj_init();
226 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
227
228
229 /* Parse arguments */
230 pj_optind = 0;
Benny Prijono15953012006-04-27 22:37:08 +0000231 while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1) {
Benny Prijonobf13fee2006-04-20 11:13:32 +0000232
233 switch (c) {
234 case OPT_CODEC:
Benny Prijono15953012006-04-27 22:37:08 +0000235 codec_id = pj_optarg;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000236 break;
237
238 case OPT_LOCAL_PORT:
239 local_port = (pj_uint16_t) atoi(pj_optarg);
240 if (local_port < 1) {
241 printf("Error: invalid local port %s\n", pj_optarg);
242 return 1;
243 }
244 break;
245
246 case OPT_REMOTE:
247 {
248 pj_str_t ip = pj_str(strtok(pj_optarg, ":"));
249 pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":"));
250
251 status = pj_sockaddr_in_init(&remote_addr, &ip, port);
252 if (status != PJ_SUCCESS) {
253 app_perror(THIS_FILE, "Invalid remote address", status);
254 return 1;
255 }
256 }
257 break;
258
259 case OPT_PLAY_FILE:
260 play_file = pj_optarg;
261 break;
262
263 case OPT_RECORD_FILE:
264 rec_file = pj_optarg;
265 break;
266
267 case OPT_SEND_RECV:
268 dir = PJMEDIA_DIR_ENCODING_DECODING;
269 break;
270
271 case OPT_SEND_ONLY:
272 dir = PJMEDIA_DIR_ENCODING;
273 break;
274
275 case OPT_RECV_ONLY:
276 dir = PJMEDIA_DIR_DECODING;
277 break;
278
Benny Prijono15953012006-04-27 22:37:08 +0000279 case OPT_HELP:
280 usage();
281 return 1;
282
Benny Prijonobf13fee2006-04-20 11:13:32 +0000283 default:
284 printf("Invalid options %s\n", argv[pj_optind]);
285 return 1;
286 }
287
288 }
289
290
291 /* Verify arguments. */
292 if (dir & PJMEDIA_DIR_ENCODING) {
293 if (remote_addr.sin_addr.s_addr == 0) {
294 printf("Error: remote address must be set\n");
295 return 1;
296 }
297 }
298
299 if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) {
300 printf("Direction is set to --send-only because of --play-file\n");
301 dir = PJMEDIA_DIR_ENCODING;
302 }
303
304
305 /* Must create a pool factory before we can allocate any memory. */
306 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
307
308 /*
309 * Initialize media endpoint.
310 * This will implicitly initialize PJMEDIA too.
311 */
312 status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
313 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
314
315 /* Create memory pool for application purpose */
316 pool = pj_pool_create( &cp.factory, /* pool factory */
317 "app", /* pool name. */
318 4000, /* init size */
319 4000, /* increment size */
320 NULL /* callback on error */
321 );
322
323
324 /* Register all supported codecs */
325 status = init_codecs(med_endpt);
326 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
327
328
Benny Prijono15953012006-04-27 22:37:08 +0000329 /* Find which codec to use. */
330 if (codec_id) {
331 unsigned count = 1;
332 pj_str_t str_codec_id = pj_str(codec_id);
333 pjmedia_codec_mgr *codec_mgr = pjmedia_endpt_get_codec_mgr(med_endpt);
334 status = pjmedia_codec_mgr_find_codecs_by_id( codec_mgr,
335 &str_codec_id, &count,
336 &codec_info, NULL);
337 if (status != PJ_SUCCESS) {
338 printf("Error: unable to find codec %s\n", codec_id);
339 return 1;
340 }
341 } else {
342 /* Default to pcmu */
343 pjmedia_codec_mgr_get_codec_info( pjmedia_endpt_get_codec_mgr(med_endpt),
344 0, &codec_info);
345 }
346
Benny Prijonobf13fee2006-04-20 11:13:32 +0000347 /* Create stream based on program arguments */
Benny Prijono15953012006-04-27 22:37:08 +0000348 status = create_stream(pool, med_endpt, codec_info, dir, local_port,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000349 &remote_addr, &stream);
350 if (status != PJ_SUCCESS)
351 goto on_exit;
352
353
354 /* Get the port interface of the stream */
355 status = pjmedia_stream_get_port( stream, &stream_port);
356 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
357
358
359 if (play_file) {
Benny Prijono15953012006-04-27 22:37:08 +0000360 unsigned wav_ptime;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000361
Benny Prijono15953012006-04-27 22:37:08 +0000362 wav_ptime = stream_port->info.samples_per_frame * 1000 /
363 stream_port->info.clock_rate;
364 status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime,
365 0, -1, NULL, &play_file_port);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000366 if (status != PJ_SUCCESS) {
367 app_perror(THIS_FILE, "Unable to use file", status);
368 goto on_exit;
369 }
370
371 status = pjmedia_master_port_create(pool, play_file_port, stream_port,
372 0, &master_port);
373 if (status != PJ_SUCCESS) {
374 app_perror(THIS_FILE, "Unable to create master port", status);
375 goto on_exit;
376 }
377
378 status = pjmedia_master_port_start(master_port);
379 if (status != PJ_SUCCESS) {
380 app_perror(THIS_FILE, "Error starting master port", status);
381 goto on_exit;
382 }
383
384
385 } else {
386
387 /* Create sound device port. */
388 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
389 status = pjmedia_snd_port_create(pool, -1, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000390 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000391 stream_port->info.channel_count,
392 stream_port->info.samples_per_frame,
393 stream_port->info.bits_per_sample,
394 0, &snd_port);
395 else if (dir == PJMEDIA_DIR_ENCODING)
396 status = pjmedia_snd_port_create_rec(pool, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000397 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000398 stream_port->info.channel_count,
399 stream_port->info.samples_per_frame,
400 stream_port->info.bits_per_sample,
401 0, &snd_port);
402 else
403 status = pjmedia_snd_port_create_player(pool, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000404 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000405 stream_port->info.channel_count,
406 stream_port->info.samples_per_frame,
407 stream_port->info.bits_per_sample,
408 0, &snd_port);
409
410
411 if (status != PJ_SUCCESS) {
412 app_perror(THIS_FILE, "Unable to create sound port", status);
413 goto on_exit;
414 }
415
416 /* Connect sound port to stream */
417 status = pjmedia_snd_port_connect( snd_port, stream_port );
418 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
419
420 }
421
Benny Prijono69d9d192006-05-21 19:00:28 +0000422 /* Start streaming */
423 pjmedia_stream_start(stream);
424
Benny Prijonobf13fee2006-04-20 11:13:32 +0000425
426 /* Done */
427
428 if (dir == PJMEDIA_DIR_DECODING)
429 printf("Stream is active, dir is recv-only, local port is %d\n",
430 local_port);
431 else if (dir == PJMEDIA_DIR_ENCODING)
432 printf("Stream is active, dir is send-only, sending to %s:%d\n",
433 pj_inet_ntoa(remote_addr.sin_addr),
434 pj_ntohs(remote_addr.sin_port));
435 else
436 printf("Stream is active, send/recv, local port is %d, "
437 "sending to %s:%d\n",
438 local_port,
439 pj_inet_ntoa(remote_addr.sin_addr),
440 pj_ntohs(remote_addr.sin_port));
441
442
443 for (;;) {
444
445 puts("");
446 puts("Commands:");
447 puts(" s Display media statistics");
448 puts(" q Quit");
449 puts("");
450
451 printf("Command: "); fflush(stdout);
452
453 fgets(tmp, sizeof(tmp), stdin);
454
455 if (tmp[0] == 's')
456 print_stream_stat(stream);
457 else if (tmp[0] == 'q')
458 break;
459
460 }
461
462
463
464 /* Start deinitialization: */
465on_exit:
466
467 /* Destroy sound device */
468 if (snd_port) {
469 pjmedia_snd_port_destroy( snd_port );
470 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
471 }
472
473 /* If there is master port, then we just need to destroy master port
474 * (it will recursively destroy upstream and downstream ports, which
475 * in this case are file_port and stream_port).
476 */
477 if (master_port) {
478 pjmedia_master_port_destroy(master_port);
479 play_file_port = NULL;
480 stream = NULL;
481 }
482
483 /* Destroy stream */
484 if (stream) {
Benny Prijonob04c9e02006-05-17 17:17:39 +0000485 pjmedia_transport *tp;
486
487 tp = pjmedia_stream_get_transport(stream);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000488 pjmedia_stream_destroy(stream);
Benny Prijonob04c9e02006-05-17 17:17:39 +0000489 pjmedia_transport_udp_close(tp);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000490 }
491
492 /* Destroy file ports */
493 if (play_file_port)
494 pjmedia_port_destroy( play_file_port );
495 if (rec_file_port)
496 pjmedia_port_destroy( rec_file_port );
497
498
499 /* Release application pool */
500 pj_pool_release( pool );
501
502 /* Destroy media endpoint. */
503 pjmedia_endpt_destroy( med_endpt );
504
505 /* Destroy pool factory */
506 pj_caching_pool_destroy( &cp );
507
508
509 return (status == PJ_SUCCESS) ? 0 : 1;
510}
511
512
513
514
515static const char *good_number(char *buf, pj_int32_t val)
516{
517 if (val < 1000) {
518 pj_ansi_sprintf(buf, "%d", val);
519 } else if (val < 1000000) {
520 pj_ansi_sprintf(buf, "%d.%dK",
521 val / 1000,
522 (val % 1000) / 100);
523 } else {
524 pj_ansi_sprintf(buf, "%d.%02dM",
525 val / 1000000,
526 (val % 1000000) / 10000);
527 }
528
529 return buf;
530}
531
532
533
534/*
535 * Print stream statistics
536 */
537static void print_stream_stat(pjmedia_stream *stream)
538{
539 char duration[80], last_update[80];
540 char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16];
541 pjmedia_port *port;
542 pjmedia_rtcp_stat stat;
543 pj_time_val now;
544
545
546 pj_gettimeofday(&now);
547 pjmedia_stream_get_stat(stream, &stat);
548 pjmedia_stream_get_port(stream, &port);
549
550 puts("Stream statistics:");
551
552 /* Print duration */
553 PJ_TIME_VAL_SUB(now, stat.start);
554 sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld",
555 now.sec / 3600,
556 (now.sec % 3600) / 60,
557 (now.sec % 60),
558 now.msec);
559
560
561 printf(" Info: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n",
562 (int)port->info.encoding_name.slen,
563 port->info.encoding_name.ptr,
Benny Prijono15953012006-04-27 22:37:08 +0000564 port->info.clock_rate,
565 port->info.samples_per_frame * 1000 / port->info.clock_rate,
566 good_number(bps, port->info.bytes_per_frame * port->info.clock_rate /
567 port->info.samples_per_frame),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000568 good_number(ipbps, (port->info.bytes_per_frame+32) *
Benny Prijono15953012006-04-27 22:37:08 +0000569 port->info.clock_rate / port->info.clock_rate));
Benny Prijonobf13fee2006-04-20 11:13:32 +0000570
571 if (stat.rx.update_cnt == 0)
572 strcpy(last_update, "never");
573 else {
574 pj_gettimeofday(&now);
575 PJ_TIME_VAL_SUB(now, stat.rx.update);
576 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
577 now.sec / 3600,
578 (now.sec % 3600) / 60,
579 now.sec % 60,
580 now.msec);
581 }
582
583 printf(" RX stat last update: %s\n"
584 " total %s packets %sB received (%sB +IP hdr)%s\n"
585 " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
586 " (msec) min avg max last\n"
587 " loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
588 " jitter : %7.3f %7.3f %7.3f %7.3f%s\n",
589 last_update,
590 good_number(packets, stat.rx.pkt),
591 good_number(bytes, stat.rx.bytes),
592 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
593 "",
594 stat.rx.loss,
595 stat.rx.loss * 100.0 / stat.rx.pkt,
596 stat.rx.dup,
597 stat.rx.dup * 100.0 / stat.rx.pkt,
598 stat.rx.reorder,
599 stat.rx.reorder * 100.0 / stat.rx.pkt,
600 "",
601 stat.rx.loss_period.min / 1000.0,
602 stat.rx.loss_period.avg / 1000.0,
603 stat.rx.loss_period.max / 1000.0,
604 stat.rx.loss_period.last / 1000.0,
605 "",
606 stat.rx.jitter.min / 1000.0,
607 stat.rx.jitter.avg / 1000.0,
608 stat.rx.jitter.max / 1000.0,
609 stat.rx.jitter.last / 1000.0,
610 ""
611 );
612
613
614 if (stat.tx.update_cnt == 0)
615 strcpy(last_update, "never");
616 else {
617 pj_gettimeofday(&now);
618 PJ_TIME_VAL_SUB(now, stat.tx.update);
619 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
620 now.sec / 3600,
621 (now.sec % 3600) / 60,
622 now.sec % 60,
623 now.msec);
624 }
625
626 printf(" TX stat last update: %s\n"
627 " total %s packets %sB sent (%sB +IP hdr)%s\n"
628 " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
629 " (msec) min avg max last\n"
630 " loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
631 " jitter : %7.3f %7.3f %7.3f %7.3f%s\n",
632 last_update,
633 good_number(packets, stat.tx.pkt),
634 good_number(bytes, stat.tx.bytes),
635 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
636 "",
637 stat.tx.loss,
638 stat.tx.loss * 100.0 / stat.tx.pkt,
639 stat.tx.dup,
640 stat.tx.dup * 100.0 / stat.tx.pkt,
641 stat.tx.reorder,
642 stat.tx.reorder * 100.0 / stat.tx.pkt,
643 "",
644 stat.tx.loss_period.min / 1000.0,
645 stat.tx.loss_period.avg / 1000.0,
646 stat.tx.loss_period.max / 1000.0,
647 stat.tx.loss_period.last / 1000.0,
648 "",
649 stat.tx.jitter.min / 1000.0,
650 stat.tx.jitter.avg / 1000.0,
651 stat.tx.jitter.max / 1000.0,
652 stat.tx.jitter.last / 1000.0,
653 ""
654 );
655
656
657 printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f%s\n",
658 stat.rtt.min / 1000.0,
659 stat.rtt.avg / 1000.0,
660 stat.rtt.max / 1000.0,
661 stat.rtt.last / 1000.0,
662 ""
663 );
664
665}
666