blob: ed615eb63111bd92cd41376e305a0b867bad678b [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
Benny Prijono1ec70b32006-06-20 15:39:07 +000021/**
22 * \page page_pjmedia_samples_streamutil_c Samples: Remote Streaming
23 *
24 * This example mainly demonstrates how to stream media file to remote
25 * peer using RTP.
26 *
27 * This file is pjsip-apps/src/samples/streamutil.c
28 *
29 * \includelineno streamutil.c
30 */
31
32
Benny Prijonobf13fee2006-04-20 11:13:32 +000033static const char *desc =
Benny Prijono6d7a45f2006-04-24 23:13:00 +000034 " streamutil \n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000035 " \n"
36 " PURPOSE: \n"
37 " Demonstrate how to use pjmedia stream component to transmit/receive \n"
38 " RTP packets to/from sound device. \n"
39 "\n"
40 "\n"
41 " USAGE: \n"
Benny Prijono6d7a45f2006-04-24 23:13:00 +000042 " streamutil [options] \n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000043 "\n"
44 "\n"
45 " Options:\n"
Benny Prijono15953012006-04-27 22:37:08 +000046 " --codec=CODEC Set the codec name. \n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000047 " --local-port=PORT Set local RTP port (default=4000) \n"
48 " --remote=IP:PORT Set the remote peer. If this option is set, \n"
49 " the program will transmit RTP audio to the \n"
50 " specified address. (default: recv only) \n"
51 " --play-file=WAV Send audio from the WAV file instead of from \n"
52 " the sound device. \n"
53// " --record-file=WAV Record incoming audio to WAV file instead of \n"
54// " playing it to sound device. \n"
55 " --send-recv Set stream direction to bidirectional. \n"
56 " --send-only Set stream direction to send only \n"
57 " --recv-only Set stream direction to recv only (default) \n"
58 "\n"
59;
60
61
62
63#include <pjlib.h>
64#include <pjlib-util.h>
65#include <pjmedia.h>
66#include <pjmedia-codec.h>
67
68#include <stdlib.h> /* atoi() */
69#include <stdio.h>
70
71#include "util.h"
72
73
74#define THIS_FILE "stream.c"
75
76
Benny Prijonobf13fee2006-04-20 11:13:32 +000077
78/* Prototype */
79static void print_stream_stat(pjmedia_stream *stream);
80
81
82/*
83 * Register all codecs.
84 */
85static pj_status_t init_codecs(pjmedia_endpt *med_endpt)
86{
87 pj_status_t status;
88
Benny Prijonof9962132006-05-16 13:20:00 +000089#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +000090 status = pjmedia_codec_g711_init(med_endpt);
91 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +000092#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +000093
Benny Prijonof9962132006-05-16 13:20:00 +000094#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +000095 status = pjmedia_codec_gsm_init(med_endpt);
96 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +000097#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +000098
Benny Prijonof9962132006-05-16 13:20:00 +000099#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +0000100 status = pjmedia_codec_speex_init(med_endpt, 0, -1, -1);
101 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +0000102#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000103
Benny Prijonof9962132006-05-16 13:20:00 +0000104#if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC!=0
Benny Prijono15953012006-04-27 22:37:08 +0000105 status = pjmedia_codec_l16_init(med_endpt, 0);
106 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +0000107#endif
Benny Prijono15953012006-04-27 22:37:08 +0000108
Benny Prijonobf13fee2006-04-20 11:13:32 +0000109 return PJ_SUCCESS;
110}
111
112
113/*
114 * Create stream based on the codec, dir, remote address, etc.
115 */
116static pj_status_t create_stream( pj_pool_t *pool,
117 pjmedia_endpt *med_endpt,
Benny Prijono15953012006-04-27 22:37:08 +0000118 const pjmedia_codec_info *codec_info,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000119 pjmedia_dir dir,
120 pj_uint16_t local_port,
121 const pj_sockaddr_in *rem_addr,
122 pjmedia_stream **p_stream )
123{
124 pjmedia_stream_info info;
Benny Prijonob04c9e02006-05-17 17:17:39 +0000125 pjmedia_transport *transport;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000126 pj_status_t status;
127
128
129 /* Reset stream info. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000130 pj_bzero(&info, sizeof(info));
Benny Prijonobf13fee2006-04-20 11:13:32 +0000131
132
133 /* Initialize stream info formats */
134 info.type = PJMEDIA_TYPE_AUDIO;
135 info.dir = dir;
Benny Prijono15953012006-04-27 22:37:08 +0000136 pj_memcpy(&info.fmt, codec_info, sizeof(pjmedia_codec_info));
137 info.tx_pt = codec_info->pt;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000138 info.ssrc = pj_rand();
139
140
141 /* Copy remote address */
142 pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
143
144
Benny Prijonob04c9e02006-05-17 17:17:39 +0000145 /* Create media transport */
146 status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
Benny Prijono30c658b2006-06-01 11:37:30 +0000147 0, &transport);
Benny Prijonob04c9e02006-05-17 17:17:39 +0000148 if (status != PJ_SUCCESS)
Benny Prijonobf13fee2006-04-20 11:13:32 +0000149 return status;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000150
151
152 /* Now that the stream info is initialized, we can create the
153 * stream.
154 */
155
Benny Prijonob04c9e02006-05-17 17:17:39 +0000156 status = pjmedia_stream_create( med_endpt, pool, &info,
157 transport, NULL, p_stream);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000158
159 if (status != PJ_SUCCESS) {
160 app_perror(THIS_FILE, "Error creating stream", status);
Benny Prijonob04c9e02006-05-17 17:17:39 +0000161 pjmedia_transport_udp_close(transport);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000162 return status;
163 }
164
165
166 return PJ_SUCCESS;
167}
168
169
170/*
Benny Prijono15953012006-04-27 22:37:08 +0000171 * usage()
172 */
173static void usage()
174{
175 puts(desc);
176}
177
178/*
Benny Prijonobf13fee2006-04-20 11:13:32 +0000179 * main()
180 */
181int main(int argc, char *argv[])
182{
183 pj_caching_pool cp;
184 pjmedia_endpt *med_endpt;
185 pj_pool_t *pool;
186 pjmedia_port *rec_file_port = NULL, *play_file_port = NULL;
187 pjmedia_master_port *master_port = NULL;
188 pjmedia_snd_port *snd_port = NULL;
189 pjmedia_stream *stream = NULL;
190 pjmedia_port *stream_port;
191 char tmp[10];
Benny Prijonob04c9e02006-05-17 17:17:39 +0000192 pj_status_t status;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000193
194
195 /* Default values */
Benny Prijono15953012006-04-27 22:37:08 +0000196 const pjmedia_codec_info *codec_info;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000197 pjmedia_dir dir = PJMEDIA_DIR_DECODING;
198 pj_sockaddr_in remote_addr;
199 pj_uint16_t local_port = 4000;
Benny Prijono15953012006-04-27 22:37:08 +0000200 char *codec_id = NULL;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000201 char *rec_file = NULL;
202 char *play_file = NULL;
203
204 enum {
205 OPT_CODEC = 'c',
206 OPT_LOCAL_PORT = 'p',
207 OPT_REMOTE = 'r',
208 OPT_PLAY_FILE = 'w',
209 OPT_RECORD_FILE = 'R',
210 OPT_SEND_RECV = 'b',
211 OPT_SEND_ONLY = 's',
212 OPT_RECV_ONLY = 'i',
Benny Prijono15953012006-04-27 22:37:08 +0000213 OPT_HELP = 'h',
Benny Prijonobf13fee2006-04-20 11:13:32 +0000214 };
215
216 struct pj_getopt_option long_options[] = {
217 { "codec", 1, 0, OPT_CODEC },
218 { "local-port", 1, 0, OPT_LOCAL_PORT },
219 { "remote", 1, 0, OPT_REMOTE },
220 { "play-file", 1, 0, OPT_PLAY_FILE },
221 { "record-file", 1, 0, OPT_RECORD_FILE },
222 { "send-recv", 0, 0, OPT_SEND_RECV },
223 { "send-only", 0, 0, OPT_SEND_ONLY },
224 { "recv-only", 0, 0, OPT_RECV_ONLY },
Benny Prijono15953012006-04-27 22:37:08 +0000225 { "help", 0, 0, OPT_HELP },
Benny Prijonobf13fee2006-04-20 11:13:32 +0000226 { NULL, 0, 0, 0 },
227 };
228
229 int c;
230 int option_index;
231
232
Benny Prijonoac623b32006-07-03 15:19:31 +0000233 pj_bzero(&remote_addr, sizeof(remote_addr));
Benny Prijonobf13fee2006-04-20 11:13:32 +0000234
235
236 /* init PJLIB : */
237 status = pj_init();
238 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
239
240
241 /* Parse arguments */
242 pj_optind = 0;
Benny Prijono15953012006-04-27 22:37:08 +0000243 while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1) {
Benny Prijonobf13fee2006-04-20 11:13:32 +0000244
245 switch (c) {
246 case OPT_CODEC:
Benny Prijono15953012006-04-27 22:37:08 +0000247 codec_id = pj_optarg;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000248 break;
249
250 case OPT_LOCAL_PORT:
251 local_port = (pj_uint16_t) atoi(pj_optarg);
252 if (local_port < 1) {
253 printf("Error: invalid local port %s\n", pj_optarg);
254 return 1;
255 }
256 break;
257
258 case OPT_REMOTE:
259 {
260 pj_str_t ip = pj_str(strtok(pj_optarg, ":"));
261 pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":"));
262
263 status = pj_sockaddr_in_init(&remote_addr, &ip, port);
264 if (status != PJ_SUCCESS) {
265 app_perror(THIS_FILE, "Invalid remote address", status);
266 return 1;
267 }
268 }
269 break;
270
271 case OPT_PLAY_FILE:
272 play_file = pj_optarg;
273 break;
274
275 case OPT_RECORD_FILE:
276 rec_file = pj_optarg;
277 break;
278
279 case OPT_SEND_RECV:
280 dir = PJMEDIA_DIR_ENCODING_DECODING;
281 break;
282
283 case OPT_SEND_ONLY:
284 dir = PJMEDIA_DIR_ENCODING;
285 break;
286
287 case OPT_RECV_ONLY:
288 dir = PJMEDIA_DIR_DECODING;
289 break;
290
Benny Prijono15953012006-04-27 22:37:08 +0000291 case OPT_HELP:
292 usage();
293 return 1;
294
Benny Prijonobf13fee2006-04-20 11:13:32 +0000295 default:
296 printf("Invalid options %s\n", argv[pj_optind]);
297 return 1;
298 }
299
300 }
301
302
303 /* Verify arguments. */
304 if (dir & PJMEDIA_DIR_ENCODING) {
305 if (remote_addr.sin_addr.s_addr == 0) {
306 printf("Error: remote address must be set\n");
307 return 1;
308 }
309 }
310
311 if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) {
312 printf("Direction is set to --send-only because of --play-file\n");
313 dir = PJMEDIA_DIR_ENCODING;
314 }
315
316
317 /* Must create a pool factory before we can allocate any memory. */
318 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
319
320 /*
321 * Initialize media endpoint.
322 * This will implicitly initialize PJMEDIA too.
323 */
324 status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
325 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
326
327 /* Create memory pool for application purpose */
328 pool = pj_pool_create( &cp.factory, /* pool factory */
329 "app", /* pool name. */
330 4000, /* init size */
331 4000, /* increment size */
332 NULL /* callback on error */
333 );
334
335
336 /* Register all supported codecs */
337 status = init_codecs(med_endpt);
338 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
339
340
Benny Prijono15953012006-04-27 22:37:08 +0000341 /* Find which codec to use. */
342 if (codec_id) {
343 unsigned count = 1;
344 pj_str_t str_codec_id = pj_str(codec_id);
345 pjmedia_codec_mgr *codec_mgr = pjmedia_endpt_get_codec_mgr(med_endpt);
346 status = pjmedia_codec_mgr_find_codecs_by_id( codec_mgr,
347 &str_codec_id, &count,
348 &codec_info, NULL);
349 if (status != PJ_SUCCESS) {
350 printf("Error: unable to find codec %s\n", codec_id);
351 return 1;
352 }
353 } else {
354 /* Default to pcmu */
355 pjmedia_codec_mgr_get_codec_info( pjmedia_endpt_get_codec_mgr(med_endpt),
356 0, &codec_info);
357 }
358
Benny Prijonobf13fee2006-04-20 11:13:32 +0000359 /* Create stream based on program arguments */
Benny Prijono15953012006-04-27 22:37:08 +0000360 status = create_stream(pool, med_endpt, codec_info, dir, local_port,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000361 &remote_addr, &stream);
362 if (status != PJ_SUCCESS)
363 goto on_exit;
364
365
366 /* Get the port interface of the stream */
367 status = pjmedia_stream_get_port( stream, &stream_port);
368 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
369
370
371 if (play_file) {
Benny Prijono15953012006-04-27 22:37:08 +0000372 unsigned wav_ptime;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000373
Benny Prijono15953012006-04-27 22:37:08 +0000374 wav_ptime = stream_port->info.samples_per_frame * 1000 /
375 stream_port->info.clock_rate;
376 status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime,
Benny Prijono6fd4b8f2006-06-22 18:51:50 +0000377 0, -1, &play_file_port);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000378 if (status != PJ_SUCCESS) {
379 app_perror(THIS_FILE, "Unable to use file", status);
380 goto on_exit;
381 }
382
383 status = pjmedia_master_port_create(pool, play_file_port, stream_port,
384 0, &master_port);
385 if (status != PJ_SUCCESS) {
386 app_perror(THIS_FILE, "Unable to create master port", status);
387 goto on_exit;
388 }
389
390 status = pjmedia_master_port_start(master_port);
391 if (status != PJ_SUCCESS) {
392 app_perror(THIS_FILE, "Error starting master port", status);
393 goto on_exit;
394 }
395
396
397 } else {
398
399 /* Create sound device port. */
400 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
401 status = pjmedia_snd_port_create(pool, -1, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000402 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000403 stream_port->info.channel_count,
404 stream_port->info.samples_per_frame,
405 stream_port->info.bits_per_sample,
406 0, &snd_port);
407 else if (dir == PJMEDIA_DIR_ENCODING)
408 status = pjmedia_snd_port_create_rec(pool, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000409 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000410 stream_port->info.channel_count,
411 stream_port->info.samples_per_frame,
412 stream_port->info.bits_per_sample,
413 0, &snd_port);
414 else
415 status = pjmedia_snd_port_create_player(pool, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000416 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000417 stream_port->info.channel_count,
418 stream_port->info.samples_per_frame,
419 stream_port->info.bits_per_sample,
420 0, &snd_port);
421
422
423 if (status != PJ_SUCCESS) {
424 app_perror(THIS_FILE, "Unable to create sound port", status);
425 goto on_exit;
426 }
427
428 /* Connect sound port to stream */
429 status = pjmedia_snd_port_connect( snd_port, stream_port );
430 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
431
432 }
433
Benny Prijono69d9d192006-05-21 19:00:28 +0000434 /* Start streaming */
435 pjmedia_stream_start(stream);
436
Benny Prijonobf13fee2006-04-20 11:13:32 +0000437
438 /* Done */
439
440 if (dir == PJMEDIA_DIR_DECODING)
441 printf("Stream is active, dir is recv-only, local port is %d\n",
442 local_port);
443 else if (dir == PJMEDIA_DIR_ENCODING)
444 printf("Stream is active, dir is send-only, sending to %s:%d\n",
445 pj_inet_ntoa(remote_addr.sin_addr),
446 pj_ntohs(remote_addr.sin_port));
447 else
448 printf("Stream is active, send/recv, local port is %d, "
449 "sending to %s:%d\n",
450 local_port,
451 pj_inet_ntoa(remote_addr.sin_addr),
452 pj_ntohs(remote_addr.sin_port));
453
454
455 for (;;) {
456
457 puts("");
458 puts("Commands:");
459 puts(" s Display media statistics");
460 puts(" q Quit");
461 puts("");
462
463 printf("Command: "); fflush(stdout);
464
465 fgets(tmp, sizeof(tmp), stdin);
466
467 if (tmp[0] == 's')
468 print_stream_stat(stream);
469 else if (tmp[0] == 'q')
470 break;
471
472 }
473
474
475
476 /* Start deinitialization: */
477on_exit:
478
479 /* Destroy sound device */
480 if (snd_port) {
481 pjmedia_snd_port_destroy( snd_port );
482 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
483 }
484
485 /* If there is master port, then we just need to destroy master port
486 * (it will recursively destroy upstream and downstream ports, which
487 * in this case are file_port and stream_port).
488 */
489 if (master_port) {
Benny Prijono22a300a2006-06-14 20:04:55 +0000490 pjmedia_master_port_destroy(master_port, PJ_TRUE);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000491 play_file_port = NULL;
492 stream = NULL;
493 }
494
495 /* Destroy stream */
496 if (stream) {
Benny Prijonob04c9e02006-05-17 17:17:39 +0000497 pjmedia_transport *tp;
498
499 tp = pjmedia_stream_get_transport(stream);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000500 pjmedia_stream_destroy(stream);
Benny Prijonob04c9e02006-05-17 17:17:39 +0000501 pjmedia_transport_udp_close(tp);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000502 }
503
504 /* Destroy file ports */
505 if (play_file_port)
506 pjmedia_port_destroy( play_file_port );
507 if (rec_file_port)
508 pjmedia_port_destroy( rec_file_port );
509
510
511 /* Release application pool */
512 pj_pool_release( pool );
513
514 /* Destroy media endpoint. */
515 pjmedia_endpt_destroy( med_endpt );
516
517 /* Destroy pool factory */
518 pj_caching_pool_destroy( &cp );
519
Benny Prijonoaf1bb1e2006-11-21 12:39:31 +0000520 /* Shutdown PJLIB */
521 pj_shutdown();
522
Benny Prijonobf13fee2006-04-20 11:13:32 +0000523
524 return (status == PJ_SUCCESS) ? 0 : 1;
525}
526
527
528
529
530static const char *good_number(char *buf, pj_int32_t val)
531{
532 if (val < 1000) {
533 pj_ansi_sprintf(buf, "%d", val);
534 } else if (val < 1000000) {
535 pj_ansi_sprintf(buf, "%d.%dK",
536 val / 1000,
537 (val % 1000) / 100);
538 } else {
539 pj_ansi_sprintf(buf, "%d.%02dM",
540 val / 1000000,
541 (val % 1000000) / 10000);
542 }
543
544 return buf;
545}
546
547
548
549/*
550 * Print stream statistics
551 */
552static void print_stream_stat(pjmedia_stream *stream)
553{
554 char duration[80], last_update[80];
555 char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16];
556 pjmedia_port *port;
557 pjmedia_rtcp_stat stat;
558 pj_time_val now;
559
560
561 pj_gettimeofday(&now);
562 pjmedia_stream_get_stat(stream, &stat);
563 pjmedia_stream_get_port(stream, &port);
564
565 puts("Stream statistics:");
566
567 /* Print duration */
568 PJ_TIME_VAL_SUB(now, stat.start);
569 sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld",
570 now.sec / 3600,
571 (now.sec % 3600) / 60,
572 (now.sec % 60),
573 now.msec);
574
575
576 printf(" Info: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n",
577 (int)port->info.encoding_name.slen,
578 port->info.encoding_name.ptr,
Benny Prijono15953012006-04-27 22:37:08 +0000579 port->info.clock_rate,
580 port->info.samples_per_frame * 1000 / port->info.clock_rate,
581 good_number(bps, port->info.bytes_per_frame * port->info.clock_rate /
582 port->info.samples_per_frame),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000583 good_number(ipbps, (port->info.bytes_per_frame+32) *
Benny Prijono15953012006-04-27 22:37:08 +0000584 port->info.clock_rate / port->info.clock_rate));
Benny Prijonobf13fee2006-04-20 11:13:32 +0000585
586 if (stat.rx.update_cnt == 0)
587 strcpy(last_update, "never");
588 else {
589 pj_gettimeofday(&now);
590 PJ_TIME_VAL_SUB(now, stat.rx.update);
591 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
592 now.sec / 3600,
593 (now.sec % 3600) / 60,
594 now.sec % 60,
595 now.msec);
596 }
597
598 printf(" RX stat last update: %s\n"
599 " total %s packets %sB received (%sB +IP hdr)%s\n"
600 " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
601 " (msec) min avg max last\n"
602 " loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
603 " jitter : %7.3f %7.3f %7.3f %7.3f%s\n",
604 last_update,
605 good_number(packets, stat.rx.pkt),
606 good_number(bytes, stat.rx.bytes),
607 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
608 "",
609 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +0000610 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000611 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +0000612 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000613 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +0000614 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000615 "",
616 stat.rx.loss_period.min / 1000.0,
617 stat.rx.loss_period.avg / 1000.0,
618 stat.rx.loss_period.max / 1000.0,
619 stat.rx.loss_period.last / 1000.0,
620 "",
621 stat.rx.jitter.min / 1000.0,
622 stat.rx.jitter.avg / 1000.0,
623 stat.rx.jitter.max / 1000.0,
624 stat.rx.jitter.last / 1000.0,
625 ""
626 );
627
628
629 if (stat.tx.update_cnt == 0)
630 strcpy(last_update, "never");
631 else {
632 pj_gettimeofday(&now);
633 PJ_TIME_VAL_SUB(now, stat.tx.update);
634 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
635 now.sec / 3600,
636 (now.sec % 3600) / 60,
637 now.sec % 60,
638 now.msec);
639 }
640
641 printf(" TX stat last update: %s\n"
642 " total %s packets %sB sent (%sB +IP hdr)%s\n"
643 " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
644 " (msec) min avg max last\n"
645 " loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
646 " jitter : %7.3f %7.3f %7.3f %7.3f%s\n",
647 last_update,
648 good_number(packets, stat.tx.pkt),
649 good_number(bytes, stat.tx.bytes),
650 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
651 "",
652 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +0000653 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000654 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +0000655 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000656 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +0000657 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000658 "",
659 stat.tx.loss_period.min / 1000.0,
660 stat.tx.loss_period.avg / 1000.0,
661 stat.tx.loss_period.max / 1000.0,
662 stat.tx.loss_period.last / 1000.0,
663 "",
664 stat.tx.jitter.min / 1000.0,
665 stat.tx.jitter.avg / 1000.0,
666 stat.tx.jitter.max / 1000.0,
667 stat.tx.jitter.last / 1000.0,
668 ""
669 );
670
671
672 printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f%s\n",
673 stat.rtt.min / 1000.0,
674 stat.rtt.avg / 1000.0,
675 stat.rtt.max / 1000.0,
676 stat.rtt.last / 1000.0,
677 ""
678 );
679
680}
681