blob: 8b1f58919eb418f4c61cb98038ccb9a9b89c1218 [file] [log] [blame]
Benny Prijonobf13fee2006-04-20 11:13:32 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijonobf13fee2006-04-20 11:13:32 +00004 *
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"
Benny Prijono0d510782006-12-22 15:24:11 +000053 " --record-file=WAV Record incoming audio to WAV file instead of \n"
54 " playing it to sound device. \n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000055 " --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 Prijonofc24e692007-01-27 18:31:51 +000089 /* To suppress warning about unused var when all codecs are disabled */
90 PJ_UNUSED_ARG(status);
91
Benny Prijonof9962132006-05-16 13:20:00 +000092#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +000093 status = pjmedia_codec_g711_init(med_endpt);
94 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +000095#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +000096
Benny Prijonof9962132006-05-16 13:20:00 +000097#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +000098 status = pjmedia_codec_gsm_init(med_endpt);
99 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +0000100#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000101
Benny Prijonof9962132006-05-16 13:20:00 +0000102#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
Benny Prijonobf13fee2006-04-20 11:13:32 +0000103 status = pjmedia_codec_speex_init(med_endpt, 0, -1, -1);
104 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +0000105#endif
Benny Prijonobf13fee2006-04-20 11:13:32 +0000106
Benny Prijonof9962132006-05-16 13:20:00 +0000107#if defined(PJMEDIA_HAS_L16_CODEC) && PJMEDIA_HAS_L16_CODEC!=0
Benny Prijono15953012006-04-27 22:37:08 +0000108 status = pjmedia_codec_l16_init(med_endpt, 0);
109 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijonof9962132006-05-16 13:20:00 +0000110#endif
Benny Prijono15953012006-04-27 22:37:08 +0000111
Benny Prijonobf13fee2006-04-20 11:13:32 +0000112 return PJ_SUCCESS;
113}
114
115
116/*
117 * Create stream based on the codec, dir, remote address, etc.
118 */
119static pj_status_t create_stream( pj_pool_t *pool,
120 pjmedia_endpt *med_endpt,
Benny Prijono15953012006-04-27 22:37:08 +0000121 const pjmedia_codec_info *codec_info,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000122 pjmedia_dir dir,
123 pj_uint16_t local_port,
124 const pj_sockaddr_in *rem_addr,
125 pjmedia_stream **p_stream )
126{
127 pjmedia_stream_info info;
Benny Prijonob04c9e02006-05-17 17:17:39 +0000128 pjmedia_transport *transport;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000129 pj_status_t status;
130
131
132 /* Reset stream info. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000133 pj_bzero(&info, sizeof(info));
Benny Prijonobf13fee2006-04-20 11:13:32 +0000134
135
136 /* Initialize stream info formats */
137 info.type = PJMEDIA_TYPE_AUDIO;
138 info.dir = dir;
Benny Prijono15953012006-04-27 22:37:08 +0000139 pj_memcpy(&info.fmt, codec_info, sizeof(pjmedia_codec_info));
140 info.tx_pt = codec_info->pt;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000141 info.ssrc = pj_rand();
142
143
144 /* Copy remote address */
145 pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
146
147
Benny Prijonob04c9e02006-05-17 17:17:39 +0000148 /* Create media transport */
149 status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
Benny Prijono30c658b2006-06-01 11:37:30 +0000150 0, &transport);
Benny Prijonob04c9e02006-05-17 17:17:39 +0000151 if (status != PJ_SUCCESS)
Benny Prijonobf13fee2006-04-20 11:13:32 +0000152 return status;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000153
154
155 /* Now that the stream info is initialized, we can create the
156 * stream.
157 */
158
Benny Prijonob04c9e02006-05-17 17:17:39 +0000159 status = pjmedia_stream_create( med_endpt, pool, &info,
160 transport, NULL, p_stream);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000161
162 if (status != PJ_SUCCESS) {
163 app_perror(THIS_FILE, "Error creating stream", status);
Benny Prijonob04c9e02006-05-17 17:17:39 +0000164 pjmedia_transport_udp_close(transport);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000165 return status;
166 }
167
168
169 return PJ_SUCCESS;
170}
171
172
173/*
Benny Prijono15953012006-04-27 22:37:08 +0000174 * usage()
175 */
176static void usage()
177{
178 puts(desc);
179}
180
181/*
Benny Prijonobf13fee2006-04-20 11:13:32 +0000182 * main()
183 */
184int main(int argc, char *argv[])
185{
186 pj_caching_pool cp;
187 pjmedia_endpt *med_endpt;
188 pj_pool_t *pool;
189 pjmedia_port *rec_file_port = NULL, *play_file_port = NULL;
190 pjmedia_master_port *master_port = NULL;
191 pjmedia_snd_port *snd_port = NULL;
192 pjmedia_stream *stream = NULL;
193 pjmedia_port *stream_port;
194 char tmp[10];
Benny Prijonob04c9e02006-05-17 17:17:39 +0000195 pj_status_t status;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000196
197
198 /* Default values */
Benny Prijono15953012006-04-27 22:37:08 +0000199 const pjmedia_codec_info *codec_info;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000200 pjmedia_dir dir = PJMEDIA_DIR_DECODING;
201 pj_sockaddr_in remote_addr;
202 pj_uint16_t local_port = 4000;
Benny Prijono15953012006-04-27 22:37:08 +0000203 char *codec_id = NULL;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000204 char *rec_file = NULL;
205 char *play_file = NULL;
206
207 enum {
208 OPT_CODEC = 'c',
209 OPT_LOCAL_PORT = 'p',
210 OPT_REMOTE = 'r',
211 OPT_PLAY_FILE = 'w',
212 OPT_RECORD_FILE = 'R',
213 OPT_SEND_RECV = 'b',
214 OPT_SEND_ONLY = 's',
215 OPT_RECV_ONLY = 'i',
Benny Prijono15953012006-04-27 22:37:08 +0000216 OPT_HELP = 'h',
Benny Prijonobf13fee2006-04-20 11:13:32 +0000217 };
218
219 struct pj_getopt_option long_options[] = {
220 { "codec", 1, 0, OPT_CODEC },
221 { "local-port", 1, 0, OPT_LOCAL_PORT },
222 { "remote", 1, 0, OPT_REMOTE },
223 { "play-file", 1, 0, OPT_PLAY_FILE },
224 { "record-file", 1, 0, OPT_RECORD_FILE },
225 { "send-recv", 0, 0, OPT_SEND_RECV },
226 { "send-only", 0, 0, OPT_SEND_ONLY },
227 { "recv-only", 0, 0, OPT_RECV_ONLY },
Benny Prijono15953012006-04-27 22:37:08 +0000228 { "help", 0, 0, OPT_HELP },
Benny Prijonobf13fee2006-04-20 11:13:32 +0000229 { NULL, 0, 0, 0 },
230 };
231
232 int c;
233 int option_index;
234
235
Benny Prijonoac623b32006-07-03 15:19:31 +0000236 pj_bzero(&remote_addr, sizeof(remote_addr));
Benny Prijonobf13fee2006-04-20 11:13:32 +0000237
238
239 /* init PJLIB : */
240 status = pj_init();
241 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
242
243
244 /* Parse arguments */
245 pj_optind = 0;
Benny Prijono15953012006-04-27 22:37:08 +0000246 while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1) {
Benny Prijonobf13fee2006-04-20 11:13:32 +0000247
248 switch (c) {
249 case OPT_CODEC:
Benny Prijono15953012006-04-27 22:37:08 +0000250 codec_id = pj_optarg;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000251 break;
252
253 case OPT_LOCAL_PORT:
254 local_port = (pj_uint16_t) atoi(pj_optarg);
255 if (local_port < 1) {
256 printf("Error: invalid local port %s\n", pj_optarg);
257 return 1;
258 }
259 break;
260
261 case OPT_REMOTE:
262 {
263 pj_str_t ip = pj_str(strtok(pj_optarg, ":"));
264 pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":"));
265
266 status = pj_sockaddr_in_init(&remote_addr, &ip, port);
267 if (status != PJ_SUCCESS) {
268 app_perror(THIS_FILE, "Invalid remote address", status);
269 return 1;
270 }
271 }
272 break;
273
274 case OPT_PLAY_FILE:
275 play_file = pj_optarg;
276 break;
277
278 case OPT_RECORD_FILE:
279 rec_file = pj_optarg;
280 break;
281
282 case OPT_SEND_RECV:
283 dir = PJMEDIA_DIR_ENCODING_DECODING;
284 break;
285
286 case OPT_SEND_ONLY:
287 dir = PJMEDIA_DIR_ENCODING;
288 break;
289
290 case OPT_RECV_ONLY:
291 dir = PJMEDIA_DIR_DECODING;
292 break;
293
Benny Prijono15953012006-04-27 22:37:08 +0000294 case OPT_HELP:
295 usage();
296 return 1;
297
Benny Prijonobf13fee2006-04-20 11:13:32 +0000298 default:
299 printf("Invalid options %s\n", argv[pj_optind]);
300 return 1;
301 }
302
303 }
304
305
306 /* Verify arguments. */
307 if (dir & PJMEDIA_DIR_ENCODING) {
308 if (remote_addr.sin_addr.s_addr == 0) {
309 printf("Error: remote address must be set\n");
310 return 1;
311 }
312 }
313
314 if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) {
315 printf("Direction is set to --send-only because of --play-file\n");
316 dir = PJMEDIA_DIR_ENCODING;
317 }
318
319
320 /* Must create a pool factory before we can allocate any memory. */
321 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
322
323 /*
324 * Initialize media endpoint.
325 * This will implicitly initialize PJMEDIA too.
326 */
327 status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
328 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
329
330 /* Create memory pool for application purpose */
331 pool = pj_pool_create( &cp.factory, /* pool factory */
332 "app", /* pool name. */
333 4000, /* init size */
334 4000, /* increment size */
335 NULL /* callback on error */
336 );
337
338
339 /* Register all supported codecs */
340 status = init_codecs(med_endpt);
341 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
342
343
Benny Prijono15953012006-04-27 22:37:08 +0000344 /* Find which codec to use. */
345 if (codec_id) {
346 unsigned count = 1;
347 pj_str_t str_codec_id = pj_str(codec_id);
348 pjmedia_codec_mgr *codec_mgr = pjmedia_endpt_get_codec_mgr(med_endpt);
349 status = pjmedia_codec_mgr_find_codecs_by_id( codec_mgr,
350 &str_codec_id, &count,
351 &codec_info, NULL);
352 if (status != PJ_SUCCESS) {
353 printf("Error: unable to find codec %s\n", codec_id);
354 return 1;
355 }
356 } else {
357 /* Default to pcmu */
358 pjmedia_codec_mgr_get_codec_info( pjmedia_endpt_get_codec_mgr(med_endpt),
359 0, &codec_info);
360 }
361
Benny Prijonobf13fee2006-04-20 11:13:32 +0000362 /* Create stream based on program arguments */
Benny Prijono15953012006-04-27 22:37:08 +0000363 status = create_stream(pool, med_endpt, codec_info, dir, local_port,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000364 &remote_addr, &stream);
365 if (status != PJ_SUCCESS)
366 goto on_exit;
367
368
369 /* Get the port interface of the stream */
370 status = pjmedia_stream_get_port( stream, &stream_port);
371 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
372
373
374 if (play_file) {
Benny Prijono15953012006-04-27 22:37:08 +0000375 unsigned wav_ptime;
Benny Prijonobf13fee2006-04-20 11:13:32 +0000376
Benny Prijono15953012006-04-27 22:37:08 +0000377 wav_ptime = stream_port->info.samples_per_frame * 1000 /
378 stream_port->info.clock_rate;
379 status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime,
Benny Prijono6fd4b8f2006-06-22 18:51:50 +0000380 0, -1, &play_file_port);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000381 if (status != PJ_SUCCESS) {
382 app_perror(THIS_FILE, "Unable to use file", status);
383 goto on_exit;
384 }
385
386 status = pjmedia_master_port_create(pool, play_file_port, stream_port,
387 0, &master_port);
388 if (status != PJ_SUCCESS) {
389 app_perror(THIS_FILE, "Unable to create master port", status);
390 goto on_exit;
391 }
392
393 status = pjmedia_master_port_start(master_port);
394 if (status != PJ_SUCCESS) {
395 app_perror(THIS_FILE, "Error starting master port", status);
396 goto on_exit;
397 }
398
Benny Prijono0d510782006-12-22 15:24:11 +0000399 printf("Playing from WAV file %s..\n", play_file);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000400
Benny Prijono0d510782006-12-22 15:24:11 +0000401 } else if (rec_file) {
402
403 status = pjmedia_wav_writer_port_create(pool, rec_file,
404 stream_port->info.clock_rate,
405 stream_port->info.channel_count,
406 stream_port->info.samples_per_frame,
407 stream_port->info.bits_per_sample,
408 0, 0, &rec_file_port);
409 if (status != PJ_SUCCESS) {
410 app_perror(THIS_FILE, "Unable to use file", status);
411 goto on_exit;
412 }
413
414 status = pjmedia_master_port_create(pool, stream_port, rec_file_port,
415 0, &master_port);
416 if (status != PJ_SUCCESS) {
417 app_perror(THIS_FILE, "Unable to create master port", status);
418 goto on_exit;
419 }
420
421 status = pjmedia_master_port_start(master_port);
422 if (status != PJ_SUCCESS) {
423 app_perror(THIS_FILE, "Error starting master port", status);
424 goto on_exit;
425 }
426
427 printf("Recording to WAV file %s..\n", rec_file);
428
Benny Prijonobf13fee2006-04-20 11:13:32 +0000429 } else {
430
431 /* Create sound device port. */
432 if (dir == PJMEDIA_DIR_ENCODING_DECODING)
433 status = pjmedia_snd_port_create(pool, -1, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000434 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000435 stream_port->info.channel_count,
436 stream_port->info.samples_per_frame,
437 stream_port->info.bits_per_sample,
438 0, &snd_port);
439 else if (dir == PJMEDIA_DIR_ENCODING)
440 status = pjmedia_snd_port_create_rec(pool, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000441 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000442 stream_port->info.channel_count,
443 stream_port->info.samples_per_frame,
444 stream_port->info.bits_per_sample,
445 0, &snd_port);
446 else
447 status = pjmedia_snd_port_create_player(pool, -1,
Benny Prijono15953012006-04-27 22:37:08 +0000448 stream_port->info.clock_rate,
Benny Prijonobf13fee2006-04-20 11:13:32 +0000449 stream_port->info.channel_count,
450 stream_port->info.samples_per_frame,
451 stream_port->info.bits_per_sample,
452 0, &snd_port);
453
454
455 if (status != PJ_SUCCESS) {
456 app_perror(THIS_FILE, "Unable to create sound port", status);
457 goto on_exit;
458 }
459
460 /* Connect sound port to stream */
461 status = pjmedia_snd_port_connect( snd_port, stream_port );
462 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
463
464 }
465
Benny Prijono69d9d192006-05-21 19:00:28 +0000466 /* Start streaming */
467 pjmedia_stream_start(stream);
468
Benny Prijonobf13fee2006-04-20 11:13:32 +0000469
470 /* Done */
471
472 if (dir == PJMEDIA_DIR_DECODING)
473 printf("Stream is active, dir is recv-only, local port is %d\n",
474 local_port);
475 else if (dir == PJMEDIA_DIR_ENCODING)
476 printf("Stream is active, dir is send-only, sending to %s:%d\n",
477 pj_inet_ntoa(remote_addr.sin_addr),
478 pj_ntohs(remote_addr.sin_port));
479 else
480 printf("Stream is active, send/recv, local port is %d, "
481 "sending to %s:%d\n",
482 local_port,
483 pj_inet_ntoa(remote_addr.sin_addr),
484 pj_ntohs(remote_addr.sin_port));
485
486
487 for (;;) {
488
489 puts("");
490 puts("Commands:");
491 puts(" s Display media statistics");
492 puts(" q Quit");
493 puts("");
494
495 printf("Command: "); fflush(stdout);
496
497 fgets(tmp, sizeof(tmp), stdin);
498
499 if (tmp[0] == 's')
500 print_stream_stat(stream);
501 else if (tmp[0] == 'q')
502 break;
503
504 }
505
506
507
508 /* Start deinitialization: */
509on_exit:
510
511 /* Destroy sound device */
512 if (snd_port) {
513 pjmedia_snd_port_destroy( snd_port );
514 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
515 }
516
517 /* If there is master port, then we just need to destroy master port
518 * (it will recursively destroy upstream and downstream ports, which
519 * in this case are file_port and stream_port).
520 */
521 if (master_port) {
Benny Prijono22a300a2006-06-14 20:04:55 +0000522 pjmedia_master_port_destroy(master_port, PJ_TRUE);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000523 play_file_port = NULL;
524 stream = NULL;
525 }
526
527 /* Destroy stream */
528 if (stream) {
Benny Prijonob04c9e02006-05-17 17:17:39 +0000529 pjmedia_transport *tp;
530
531 tp = pjmedia_stream_get_transport(stream);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000532 pjmedia_stream_destroy(stream);
Benny Prijonob04c9e02006-05-17 17:17:39 +0000533 pjmedia_transport_udp_close(tp);
Benny Prijonobf13fee2006-04-20 11:13:32 +0000534 }
535
536 /* Destroy file ports */
537 if (play_file_port)
538 pjmedia_port_destroy( play_file_port );
539 if (rec_file_port)
540 pjmedia_port_destroy( rec_file_port );
541
542
543 /* Release application pool */
544 pj_pool_release( pool );
545
546 /* Destroy media endpoint. */
547 pjmedia_endpt_destroy( med_endpt );
548
549 /* Destroy pool factory */
550 pj_caching_pool_destroy( &cp );
551
Benny Prijonoaf1bb1e2006-11-21 12:39:31 +0000552 /* Shutdown PJLIB */
553 pj_shutdown();
554
Benny Prijonobf13fee2006-04-20 11:13:32 +0000555
556 return (status == PJ_SUCCESS) ? 0 : 1;
557}
558
559
560
561
562static const char *good_number(char *buf, pj_int32_t val)
563{
564 if (val < 1000) {
565 pj_ansi_sprintf(buf, "%d", val);
566 } else if (val < 1000000) {
567 pj_ansi_sprintf(buf, "%d.%dK",
568 val / 1000,
569 (val % 1000) / 100);
570 } else {
571 pj_ansi_sprintf(buf, "%d.%02dM",
572 val / 1000000,
573 (val % 1000000) / 10000);
574 }
575
576 return buf;
577}
578
579
580
581/*
582 * Print stream statistics
583 */
584static void print_stream_stat(pjmedia_stream *stream)
585{
586 char duration[80], last_update[80];
587 char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16];
588 pjmedia_port *port;
589 pjmedia_rtcp_stat stat;
590 pj_time_val now;
591
592
593 pj_gettimeofday(&now);
594 pjmedia_stream_get_stat(stream, &stat);
595 pjmedia_stream_get_port(stream, &port);
596
597 puts("Stream statistics:");
598
599 /* Print duration */
600 PJ_TIME_VAL_SUB(now, stat.start);
601 sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld",
602 now.sec / 3600,
603 (now.sec % 3600) / 60,
604 (now.sec % 60),
605 now.msec);
606
607
608 printf(" Info: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n",
609 (int)port->info.encoding_name.slen,
610 port->info.encoding_name.ptr,
Benny Prijono15953012006-04-27 22:37:08 +0000611 port->info.clock_rate,
612 port->info.samples_per_frame * 1000 / port->info.clock_rate,
613 good_number(bps, port->info.bytes_per_frame * port->info.clock_rate /
614 port->info.samples_per_frame),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000615 good_number(ipbps, (port->info.bytes_per_frame+32) *
Benny Prijono15953012006-04-27 22:37:08 +0000616 port->info.clock_rate / port->info.clock_rate));
Benny Prijonobf13fee2006-04-20 11:13:32 +0000617
618 if (stat.rx.update_cnt == 0)
619 strcpy(last_update, "never");
620 else {
621 pj_gettimeofday(&now);
622 PJ_TIME_VAL_SUB(now, stat.rx.update);
623 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
624 now.sec / 3600,
625 (now.sec % 3600) / 60,
626 now.sec % 60,
627 now.msec);
628 }
629
630 printf(" RX stat last update: %s\n"
631 " total %s packets %sB received (%sB +IP hdr)%s\n"
632 " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
633 " (msec) min avg max last\n"
634 " loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
635 " jitter : %7.3f %7.3f %7.3f %7.3f%s\n",
636 last_update,
637 good_number(packets, stat.rx.pkt),
638 good_number(bytes, stat.rx.bytes),
639 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
640 "",
641 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +0000642 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000643 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +0000644 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000645 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +0000646 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000647 "",
648 stat.rx.loss_period.min / 1000.0,
649 stat.rx.loss_period.avg / 1000.0,
650 stat.rx.loss_period.max / 1000.0,
651 stat.rx.loss_period.last / 1000.0,
652 "",
653 stat.rx.jitter.min / 1000.0,
654 stat.rx.jitter.avg / 1000.0,
655 stat.rx.jitter.max / 1000.0,
656 stat.rx.jitter.last / 1000.0,
657 ""
658 );
659
660
661 if (stat.tx.update_cnt == 0)
662 strcpy(last_update, "never");
663 else {
664 pj_gettimeofday(&now);
665 PJ_TIME_VAL_SUB(now, stat.tx.update);
666 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
667 now.sec / 3600,
668 (now.sec % 3600) / 60,
669 now.sec % 60,
670 now.msec);
671 }
672
673 printf(" TX stat last update: %s\n"
674 " total %s packets %sB sent (%sB +IP hdr)%s\n"
675 " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
676 " (msec) min avg max last\n"
677 " loss period: %7.3f %7.3f %7.3f %7.3f%s\n"
678 " jitter : %7.3f %7.3f %7.3f %7.3f%s\n",
679 last_update,
680 good_number(packets, stat.tx.pkt),
681 good_number(bytes, stat.tx.bytes),
682 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
683 "",
684 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +0000685 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000686 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +0000687 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000688 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +0000689 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonobf13fee2006-04-20 11:13:32 +0000690 "",
691 stat.tx.loss_period.min / 1000.0,
692 stat.tx.loss_period.avg / 1000.0,
693 stat.tx.loss_period.max / 1000.0,
694 stat.tx.loss_period.last / 1000.0,
695 "",
696 stat.tx.jitter.min / 1000.0,
697 stat.tx.jitter.avg / 1000.0,
698 stat.tx.jitter.max / 1000.0,
699 stat.tx.jitter.last / 1000.0,
700 ""
701 );
702
703
704 printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f%s\n",
705 stat.rtt.min / 1000.0,
706 stat.rtt.avg / 1000.0,
707 stat.rtt.max / 1000.0,
708 stat.rtt.last / 1000.0,
709 ""
710 );
711
712}
713