BLOG | DOCUMENTATION | TRAC

Home --> Documentations --> PJMEDIA Reference

Samples: Remote Streaming

This example mainly demonstrates how to stream media file to remote peer using RTP.

This file is pjsip-apps/src/samples/streamutil.c

1 /* $Id: streamutil.c 5910 2018-11-21 08:39:45Z nanang $ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
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 
33 #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 
44 /* An experimental feature to add multicast option to this app for providing
45  * the capability to send IP datagrams from a single source to more than
46  * one receivers.
47  */
48 #define HAVE_MULTICAST 1
49 
50 static const char *desc =
51  " streamutil \n"
52  " \n"
53  " PURPOSE: \n"
54  " Demonstrate how to use pjmedia stream component to transmit/receive \n"
55  " RTP packets to/from sound device. \n"
56  "\n"
57  "\n"
58  " USAGE: \n"
59  " streamutil [options] \n"
60  "\n"
61  "\n"
62  " Options:\n"
63  " --codec=CODEC Set the codec name. \n"
64  " --local-port=PORT Set local RTP port (default=4000) \n"
65  " --remote=IP:PORT Set the remote peer. If this option is set, \n"
66  " the program will transmit RTP audio to the \n"
67  " specified address. (default: recv only) \n"
68 #if HAVE_MULTICAST
69  " --mcast-add=IP Joins the multicast group as specified by \n"
70  " the address. Sample usage: \n"
71  " Sender: \n"
72  " streamutil --remote=[multicast_addr]:[port] \n"
73  " Receivers: \n"
74  " streamutil --local-port=[port] \n"
75  " --mcast-add=[multicast_addr] \n"
76 #endif
77  " --play-file=WAV Send audio from the WAV file instead of from \n"
78  " the sound device. \n"
79  " --record-file=WAV Record incoming audio to WAV file instead of \n"
80  " playing it to sound device. \n"
81  " --send-recv Set stream direction to bidirectional. \n"
82  " --send-only Set stream direction to send only \n"
83  " --recv-only Set stream direction to recv only (default) \n"
84 
85 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
86  " --use-srtp[=NAME] Enable SRTP with crypto suite NAME \n"
87  " e.g: AES_CM_128_HMAC_SHA1_80 (default), \n"
88  " AES_CM_128_HMAC_SHA1_32 \n"
89  " Use this option along with the TX & RX keys, \n"
90  " formated of 60 hex digits (e.g: E148DA..) \n"
91  " --srtp-tx-key SRTP key for transmiting \n"
92  " --srtp-rx-key SRTP key for receiving \n"
93  " --srtp-dtls-client Use DTLS for SRTP keying, as DTLS client \n"
94  " --srtp-dtls-server Use DTLS for SRTP keying, as DTLS server \n"
95 #endif
96 
97  "\n"
98 ;
99 
100 
101 
102 
103 #define THIS_FILE "stream.c"
104 
105 
106 
107 /* Prototype */
108 static void print_stream_stat(pjmedia_stream *stream,
109  const pjmedia_codec_param *codec_param);
110 
111 /* Hexa string to octet array */
112 int my_hex_string_to_octet_string(char *raw, char *hex, int len)
113 {
114  int i;
115  for (i = 0; i < len; i+=2) {
116  int tmp;
117  if (i+1 >= len || !pj_isxdigit(hex[i]) || !pj_isxdigit(hex[i+1]))
118  return i;
119  tmp = pj_hex_digit_to_val((unsigned char)hex[i]) << 4;
120  tmp |= pj_hex_digit_to_val((unsigned char)hex[i+1]);
121  raw[i/2] = (char)(tmp & 0xFF);
122  }
123  return len;
124 }
125 
126 /*
127  * Register all codecs.
128  */
129 static pj_status_t init_codecs(pjmedia_endpt *med_endpt)
130 {
131  return pjmedia_codec_register_audio_codecs(med_endpt, NULL);
132 }
133 
134 
135 /*
136  * Create stream based on the codec, dir, remote address, etc.
137  */
138 static pj_status_t create_stream( pj_pool_t *pool,
139  pjmedia_endpt *med_endpt,
140  const pjmedia_codec_info *codec_info,
141  pjmedia_dir dir,
142  pj_uint16_t local_port,
143  const pj_sockaddr_in *rem_addr,
144  pj_bool_t mcast,
145  const pj_sockaddr_in *mcast_addr,
146 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
147  pj_bool_t use_srtp,
148  const pj_str_t *crypto_suite,
149  const pj_str_t *srtp_tx_key,
150  const pj_str_t *srtp_rx_key,
151  pj_bool_t is_dtls_client,
152  pj_bool_t is_dtls_server,
153 #endif
154  pjmedia_stream **p_stream )
155 {
157  pjmedia_transport *transport = NULL;
158  pj_status_t status;
159 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
160  pjmedia_transport *srtp_tp = NULL;
161 #endif
162 
163 
164  /* Reset stream info. */
165  pj_bzero(&info, sizeof(info));
166 
167 
168  /* Initialize stream info formats */
169  info.type = PJMEDIA_TYPE_AUDIO;
170  info.dir = dir;
171  pj_memcpy(&info.fmt, codec_info, sizeof(pjmedia_codec_info));
172  info.tx_pt = codec_info->pt;
173  info.rx_pt = codec_info->pt;
174  info.ssrc = pj_rand();
175 
176 #if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
177  /* Set default RTCP XR enabled/disabled */
178  info.rtcp_xr_enabled = PJ_TRUE;
179 #endif
180 
181  /* Copy remote address */
182  pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
183 
184  /* If remote address is not set, set to an arbitrary address
185  * (otherwise stream will assert).
186  */
187  if (info.rem_addr.addr.sa_family == 0) {
188  const pj_str_t addr = pj_str("127.0.0.1");
189  pj_sockaddr_in_init(&info.rem_addr.ipv4, &addr, 0);
190  }
191 
192  pj_sockaddr_cp(&info.rem_rtcp, &info.rem_addr);
194  pj_sockaddr_get_port(&info.rem_rtcp)+1);
195 
196  if (mcast) {
198  int reuse = 1;
199 
200  pj_bzero(&si, sizeof(pjmedia_sock_info));
202 
203  /* Create RTP socket */
204  status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0,
205  &si.rtp_sock);
206  if (status != PJ_SUCCESS)
207  return status;
208 
210  pj_SO_REUSEADDR(), &reuse, sizeof(reuse));
211  if (status != PJ_SUCCESS)
212  return status;
213 
214  /* Bind RTP socket */
215  status = pj_sockaddr_init(pj_AF_INET(), &si.rtp_addr_name,
216  NULL, local_port);
217  if (status != PJ_SUCCESS)
218  return status;
219 
220  status = pj_sock_bind(si.rtp_sock, &si.rtp_addr_name,
222  if (status != PJ_SUCCESS)
223  return status;
224 
225  /* Create RTCP socket */
226  status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0,
227  &si.rtcp_sock);
228  if (status != PJ_SUCCESS)
229  return status;
230 
232  pj_SO_REUSEADDR(), &reuse, sizeof(reuse));
233  if (status != PJ_SUCCESS)
234  return status;
235 
236  /* Bind RTCP socket */
238  NULL, local_port+1);
239  if (status != PJ_SUCCESS)
240  return status;
241 
242  status = pj_sock_bind(si.rtcp_sock, &si.rtcp_addr_name,
244  if (status != PJ_SUCCESS)
245  return status;
246 
247 #ifdef HAVE_MULTICAST
248  {
249  unsigned char loop;
250  struct pj_ip_mreq imr;
251 
252  pj_memset(&imr, 0, sizeof(struct pj_ip_mreq));
253  imr.imr_multiaddr.s_addr = mcast_addr->sin_addr.s_addr;
254  imr.imr_interface.s_addr = pj_htonl(PJ_INADDR_ANY);
255  status = pj_sock_setsockopt(si.rtp_sock, pj_SOL_IP(),
257  &imr, sizeof(struct pj_ip_mreq));
258  if (status != PJ_SUCCESS)
259  return status;
260 
261  status = pj_sock_setsockopt(si.rtcp_sock, pj_SOL_IP(),
263  &imr, sizeof(struct pj_ip_mreq));
264  if (status != PJ_SUCCESS)
265  return status;
266 
267  /* Disable local reception of local sent packets */
268  loop = 0;
270  pj_IP_MULTICAST_LOOP(), &loop, sizeof(loop));
272  pj_IP_MULTICAST_LOOP(), &loop, sizeof(loop));
273  }
274 #endif
275 
276  /* Create media transport from existing sockets */
277  status = pjmedia_transport_udp_attach( med_endpt, NULL, &si,
279  if (status != PJ_SUCCESS)
280  return status;
281 
282  } else {
283  /* Create media transport */
284  status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
285  0, &transport);
286  if (status != PJ_SUCCESS)
287  return status;
288  }
289 
290 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
291  /* Check if SRTP enabled */
292  if (use_srtp) {
293  status = pjmedia_transport_srtp_create(med_endpt, transport,
294  NULL, &srtp_tp);
295  if (status != PJ_SUCCESS)
296  return status;
297 
298  if (is_dtls_client || is_dtls_server) {
299  char fp[128];
300  pj_size_t fp_len = sizeof(fp);
301  pjmedia_srtp_dtls_nego_param dtls_param;
302 
303  pjmedia_transport_srtp_dtls_get_fingerprint(srtp_tp, "SHA-256", fp, &fp_len);
304  PJ_LOG(3, (THIS_FILE, "Local cert fingerprint: %s", fp));
305 
306  pj_bzero(&dtls_param, sizeof(dtls_param));
307  pj_sockaddr_cp(&dtls_param.rem_addr, rem_addr);
308  pj_sockaddr_cp(&dtls_param.rem_rtcp, rem_addr);
309  dtls_param.is_role_active = is_dtls_client;
310 
311  status = pjmedia_transport_srtp_dtls_start_nego(srtp_tp, &dtls_param);
312  if (status != PJ_SUCCESS)
313  return status;
314  } else {
315  pjmedia_srtp_crypto tx_plc, rx_plc;
316 
317  pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto));
318  pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto));
319 
320  tx_plc.key = *srtp_tx_key;
321  tx_plc.name = *crypto_suite;
322  rx_plc.key = *srtp_rx_key;
323  rx_plc.name = *crypto_suite;
324 
325  status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc);
326  if (status != PJ_SUCCESS)
327  return status;
328  }
329 
330  transport = srtp_tp;
331  }
332 #endif
333 
334  /* Now that the stream info is initialized, we can create the
335  * stream.
336  */
337 
338  status = pjmedia_stream_create( med_endpt, pool, &info,
339  transport,
340  NULL, p_stream);
341 
342  if (status != PJ_SUCCESS) {
343  app_perror(THIS_FILE, "Error creating stream", status);
344  pjmedia_transport_close(transport);
345  return status;
346  }
347 
348  /* Start media transport */
349  pjmedia_transport_media_start(transport, 0, 0, 0, 0);
350 
351 
352  return PJ_SUCCESS;
353 }
354 
355 
356 /*
357  * usage()
358  */
359 static void usage()
360 {
361  puts(desc);
362 }
363 
364 /*
365  * main()
366  */
367 int main(int argc, char *argv[])
368 {
369  pj_caching_pool cp;
370  pjmedia_endpt *med_endpt;
371  pj_pool_t *pool;
372  pjmedia_port *rec_file_port = NULL, *play_file_port = NULL;
373  pjmedia_master_port *master_port = NULL;
374  pjmedia_snd_port *snd_port = NULL;
375  pjmedia_stream *stream = NULL;
376  pjmedia_port *stream_port;
377  char tmp[10];
378  char addr[PJ_INET_ADDRSTRLEN];
379  pj_status_t status;
380 
381 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
382  /* SRTP variables */
383  pj_bool_t use_srtp = PJ_FALSE;
384  char tmp_tx_key[64];
385  char tmp_rx_key[64];
386  pj_str_t srtp_tx_key = {NULL, 0};
387  pj_str_t srtp_rx_key = {NULL, 0};
388  pj_str_t srtp_crypto_suite = {NULL, 0};
389  pj_bool_t is_dtls_client = PJ_FALSE;
390  pj_bool_t is_dtls_server = PJ_FALSE;
391  int tmp_key_len;
392 #endif
393 
394  /* Default values */
395  const pjmedia_codec_info *codec_info;
396  pjmedia_codec_param codec_param;
398  pj_sockaddr_in remote_addr;
399  pj_sockaddr_in mcast_addr;
400  pj_uint16_t local_port = 4000;
401  char *codec_id = NULL;
402  char *rec_file = NULL;
403  char *play_file = NULL;
404  int mcast = 0;
405 
406  enum {
407  OPT_CODEC = 'c',
408  OPT_LOCAL_PORT = 'p',
409  OPT_REMOTE = 'r',
410  OPT_MCAST = 'm',
411  OPT_PLAY_FILE = 'w',
412  OPT_RECORD_FILE = 'R',
413  OPT_SEND_RECV = 'b',
414  OPT_SEND_ONLY = 's',
415  OPT_RECV_ONLY = 'i',
416 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
417  OPT_USE_SRTP = 'S',
418 #endif
419  OPT_SRTP_TX_KEY = 'x',
420  OPT_SRTP_RX_KEY = 'y',
421  OPT_SRTP_DTLS_CLIENT = 'd',
422  OPT_SRTP_DTLS_SERVER = 'D',
423  OPT_HELP = 'h',
424  };
425 
426  struct pj_getopt_option long_options[] = {
427  { "codec", 1, 0, OPT_CODEC },
428  { "local-port", 1, 0, OPT_LOCAL_PORT },
429  { "remote", 1, 0, OPT_REMOTE },
430  { "mcast-add", 1, 0, OPT_MCAST },
431  { "play-file", 1, 0, OPT_PLAY_FILE },
432  { "record-file", 1, 0, OPT_RECORD_FILE },
433  { "send-recv", 0, 0, OPT_SEND_RECV },
434  { "send-only", 0, 0, OPT_SEND_ONLY },
435  { "recv-only", 0, 0, OPT_RECV_ONLY },
436 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
437  { "use-srtp", 2, 0, OPT_USE_SRTP },
438  { "srtp-tx-key", 1, 0, OPT_SRTP_TX_KEY },
439  { "srtp-rx-key", 1, 0, OPT_SRTP_RX_KEY },
440  { "srtp-dtls-client", 0, 0, OPT_SRTP_DTLS_CLIENT },
441  { "srtp-dtls-server", 0, 0, OPT_SRTP_DTLS_SERVER },
442 #endif
443  { "help", 0, 0, OPT_HELP },
444  { NULL, 0, 0, 0 },
445  };
446 
447  int c;
448  int option_index;
449 
450 
451  pj_bzero(&remote_addr, sizeof(remote_addr));
452 
453 
454  /* init PJLIB : */
455  status = pj_init();
456  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
457 
458 
459  /* Parse arguments */
460  pj_optind = 0;
461  while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1) {
462 
463  switch (c) {
464  case OPT_CODEC:
465  codec_id = pj_optarg;
466  break;
467 
468  case OPT_LOCAL_PORT:
469  local_port = (pj_uint16_t) atoi(pj_optarg);
470  if (local_port < 1) {
471  printf("Error: invalid local port %s\n", pj_optarg);
472  return 1;
473  }
474  break;
475 
476  case OPT_REMOTE:
477  {
478  pj_str_t ip = pj_str(strtok(pj_optarg, ":"));
479  pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":"));
480 
481  status = pj_sockaddr_in_init(&remote_addr, &ip, port);
482  if (status != PJ_SUCCESS) {
483  app_perror(THIS_FILE, "Invalid remote address", status);
484  return 1;
485  }
486  }
487  break;
488 
489  case OPT_MCAST:
490  {
491  pj_str_t ip = pj_str(pj_optarg);
492 
493  mcast = 1;
494  status = pj_sockaddr_in_init(&mcast_addr, &ip, 0);
495  if (status != PJ_SUCCESS) {
496  app_perror(THIS_FILE, "Invalid mcast address", status);
497  return 1;
498  }
499  }
500  break;
501 
502  case OPT_PLAY_FILE:
503  play_file = pj_optarg;
504  break;
505 
506  case OPT_RECORD_FILE:
507  rec_file = pj_optarg;
508  break;
509 
510  case OPT_SEND_RECV:
512  break;
513 
514  case OPT_SEND_ONLY:
515  dir = PJMEDIA_DIR_ENCODING;
516  break;
517 
518  case OPT_RECV_ONLY:
519  dir = PJMEDIA_DIR_DECODING;
520  break;
521 
522 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
523  case OPT_USE_SRTP:
524  use_srtp = PJ_TRUE;
525  if (pj_optarg) {
526  pj_strset(&srtp_crypto_suite, pj_optarg, strlen(pj_optarg));
527  } else {
528  srtp_crypto_suite = pj_str("AES_CM_128_HMAC_SHA1_80");
529  }
530  break;
531 
532  case OPT_SRTP_TX_KEY:
533  tmp_key_len = my_hex_string_to_octet_string(tmp_tx_key, pj_optarg,
534  (int)strlen(pj_optarg));
535  pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2);
536  break;
537 
538  case OPT_SRTP_RX_KEY:
539  tmp_key_len = my_hex_string_to_octet_string(tmp_rx_key, pj_optarg,
540  (int)strlen(pj_optarg));
541  pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2);
542  break;
543  case OPT_SRTP_DTLS_CLIENT:
544  is_dtls_client = PJ_TRUE;
545  if (is_dtls_server) {
546  printf("Error: Cannot be as both DTLS server & client\n");
547  return 1;
548  }
549  break;
550  case OPT_SRTP_DTLS_SERVER:
551  is_dtls_server = PJ_TRUE;
552  if (is_dtls_client) {
553  printf("Error: Cannot be as both DTLS server & client\n");
554  return 1;
555  }
556  break;
557 #endif
558 
559  case OPT_HELP:
560  usage();
561  return 1;
562 
563  default:
564  printf("Invalid options %s\n", argv[pj_optind]);
565  return 1;
566  }
567 
568  }
569 
570 
571  /* Verify arguments. */
572  if (dir & PJMEDIA_DIR_ENCODING
573 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
574  || is_dtls_client || is_dtls_server
575 #endif
576  ) {
577  if (remote_addr.sin_addr.s_addr == 0) {
578  printf("Error: remote address must be set\n");
579  return 1;
580  }
581  }
582 
583  if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) {
584  printf("Direction is set to --send-only because of --play-file\n");
585  dir = PJMEDIA_DIR_ENCODING;
586  }
587 
588 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
589  /* SRTP validation */
590  if (use_srtp) {
591  if (!is_dtls_client && !is_dtls_server &&
592  (!srtp_tx_key.slen || !srtp_rx_key.slen))
593  {
594  printf("Error: Key for each SRTP stream direction must be set\n");
595  return 1;
596  }
597  }
598 #endif
599 
600  /* Must create a pool factory before we can allocate any memory. */
602 
603  /*
604  * Initialize media endpoint.
605  * This will implicitly initialize PJMEDIA too.
606  */
607  status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
608  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
609 
610  /* Create memory pool for application purpose */
611  pool = pj_pool_create( &cp.factory, /* pool factory */
612  "app", /* pool name. */
613  4000, /* init size */
614  4000, /* increment size */
615  NULL /* callback on error */
616  );
617 
618 
619  /* Register all supported codecs */
620  status = init_codecs(med_endpt);
621  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
622 
623 
624  /* Find which codec to use. */
625  if (codec_id) {
626  unsigned count = 1;
627  pj_str_t str_codec_id = pj_str(codec_id);
628  pjmedia_codec_mgr *codec_mgr = pjmedia_endpt_get_codec_mgr(med_endpt);
629  status = pjmedia_codec_mgr_find_codecs_by_id( codec_mgr,
630  &str_codec_id, &count,
631  &codec_info, NULL);
632  if (status != PJ_SUCCESS) {
633  printf("Error: unable to find codec %s\n", codec_id);
634  return 1;
635  }
636  } else {
637  /* Default to pcmu */
639  0, &codec_info);
640  }
641 
642  /* Create event manager */
643  status = pjmedia_event_mgr_create(pool, 0, NULL);
644  if (status != PJ_SUCCESS)
645  goto on_exit;
646 
647  /* Create stream based on program arguments */
648  status = create_stream(pool, med_endpt, codec_info, dir, local_port,
649  &remote_addr, mcast, &mcast_addr,
650 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
651  use_srtp, &srtp_crypto_suite,
652  &srtp_tx_key, &srtp_rx_key,
653  is_dtls_client, is_dtls_server,
654 #endif
655  &stream);
656  if (status != PJ_SUCCESS)
657  goto on_exit;
658 
659  /* Get codec default param for info */
661  pjmedia_endpt_get_codec_mgr(med_endpt),
662  codec_info,
663  &codec_param);
664  /* Should be ok, as create_stream() above succeeded */
665  pj_assert(status == PJ_SUCCESS);
666 
667  /* Get the port interface of the stream */
668  status = pjmedia_stream_get_port( stream, &stream_port);
669  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
670 
671 
672  if (play_file) {
673  unsigned wav_ptime;
674 
675  wav_ptime = PJMEDIA_PIA_PTIME(&stream_port->info);
676  status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime,
677  0, -1, &play_file_port);
678  if (status != PJ_SUCCESS) {
679  app_perror(THIS_FILE, "Unable to use file", status);
680  goto on_exit;
681  }
682 
683  status = pjmedia_master_port_create(pool, play_file_port, stream_port,
684  0, &master_port);
685  if (status != PJ_SUCCESS) {
686  app_perror(THIS_FILE, "Unable to create master port", status);
687  goto on_exit;
688  }
689 
690  status = pjmedia_master_port_start(master_port);
691  if (status != PJ_SUCCESS) {
692  app_perror(THIS_FILE, "Error starting master port", status);
693  goto on_exit;
694  }
695 
696  printf("Playing from WAV file %s..\n", play_file);
697 
698  } else if (rec_file) {
699 
700  status = pjmedia_wav_writer_port_create(pool, rec_file,
701  PJMEDIA_PIA_SRATE(&stream_port->info),
702  PJMEDIA_PIA_CCNT(&stream_port->info),
703  PJMEDIA_PIA_SPF(&stream_port->info),
704  PJMEDIA_PIA_BITS(&stream_port->info),
705  0, 0, &rec_file_port);
706  if (status != PJ_SUCCESS) {
707  app_perror(THIS_FILE, "Unable to use file", status);
708  goto on_exit;
709  }
710 
711  status = pjmedia_master_port_create(pool, stream_port, rec_file_port,
712  0, &master_port);
713  if (status != PJ_SUCCESS) {
714  app_perror(THIS_FILE, "Unable to create master port", status);
715  goto on_exit;
716  }
717 
718  status = pjmedia_master_port_start(master_port);
719  if (status != PJ_SUCCESS) {
720  app_perror(THIS_FILE, "Error starting master port", status);
721  goto on_exit;
722  }
723 
724  printf("Recording to WAV file %s..\n", rec_file);
725 
726  } else {
727 
728  /* Create sound device port. */
730  status = pjmedia_snd_port_create(pool, -1, -1,
731  PJMEDIA_PIA_SRATE(&stream_port->info),
732  PJMEDIA_PIA_CCNT(&stream_port->info),
733  PJMEDIA_PIA_SPF(&stream_port->info),
734  PJMEDIA_PIA_BITS(&stream_port->info),
735  0, &snd_port);
736  else if (dir == PJMEDIA_DIR_ENCODING)
737  status = pjmedia_snd_port_create_rec(pool, -1,
738  PJMEDIA_PIA_SRATE(&stream_port->info),
739  PJMEDIA_PIA_CCNT(&stream_port->info),
740  PJMEDIA_PIA_SPF(&stream_port->info),
741  PJMEDIA_PIA_BITS(&stream_port->info),
742  0, &snd_port);
743  else
744  status = pjmedia_snd_port_create_player(pool, -1,
745  PJMEDIA_PIA_SRATE(&stream_port->info),
746  PJMEDIA_PIA_CCNT(&stream_port->info),
747  PJMEDIA_PIA_SPF(&stream_port->info),
748  PJMEDIA_PIA_BITS(&stream_port->info),
749  0, &snd_port);
750 
751 
752  if (status != PJ_SUCCESS) {
753  app_perror(THIS_FILE, "Unable to create sound port", status);
754  goto on_exit;
755  }
756 
757  /* Connect sound port to stream */
758  status = pjmedia_snd_port_connect( snd_port, stream_port );
759  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
760 
761  }
762 
763  /* Start streaming */
764  pjmedia_stream_start(stream);
765 
766 
767  /* Done */
768 
769  if (dir == PJMEDIA_DIR_DECODING)
770  printf("Stream is active, dir is recv-only, local port is %d\n",
771  local_port);
772  else if (dir == PJMEDIA_DIR_ENCODING)
773  printf("Stream is active, dir is send-only, sending to %s:%d\n",
774  pj_inet_ntop2(pj_AF_INET(), &remote_addr.sin_addr, addr,
775  sizeof(addr)),
776  pj_ntohs(remote_addr.sin_port));
777  else
778  printf("Stream is active, send/recv, local port is %d, "
779  "sending to %s:%d\n",
780  local_port,
781  pj_inet_ntop2(pj_AF_INET(), &remote_addr.sin_addr, addr,
782  sizeof(addr)),
783  pj_ntohs(remote_addr.sin_port));
784 
785 
786  for (;;) {
787 
788  puts("");
789  puts("Commands:");
790  puts(" s Display media statistics");
791  puts(" q Quit");
792  puts("");
793 
794  printf("Command: "); fflush(stdout);
795 
796  if (fgets(tmp, sizeof(tmp), stdin) == NULL) {
797  puts("EOF while reading stdin, will quit now..");
798  break;
799  }
800 
801  if (tmp[0] == 's')
802  print_stream_stat(stream, &codec_param);
803  else if (tmp[0] == 'q')
804  break;
805 
806  }
807 
808 
809 
810  /* Start deinitialization: */
811 on_exit:
812 
813  /* Destroy sound device */
814  if (snd_port) {
815  pjmedia_snd_port_destroy( snd_port );
816  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
817  }
818 
819  /* If there is master port, then we just need to destroy master port
820  * (it will recursively destroy upstream and downstream ports, which
821  * in this case are file_port and stream_port).
822  */
823  if (master_port) {
824  pjmedia_master_port_destroy(master_port, PJ_TRUE);
825  play_file_port = NULL;
826  stream = NULL;
827  }
828 
829  /* Destroy stream */
830  if (stream) {
831  pjmedia_transport *tp;
832 
833  tp = pjmedia_stream_get_transport(stream);
834  pjmedia_stream_destroy(stream);
835 
838  }
839 
840  /* Destroy file ports */
841  if (play_file_port)
842  pjmedia_port_destroy( play_file_port );
843  if (rec_file_port)
844  pjmedia_port_destroy( rec_file_port );
845 
846  /* Destroy event manager */
848 
849  /* Release application pool */
850  pj_pool_release( pool );
851 
852  /* Destroy media endpoint. */
853  pjmedia_endpt_destroy( med_endpt );
854 
855  /* Destroy pool factory */
857 
858  /* Shutdown PJLIB */
859  pj_shutdown();
860 
861 
862  return (status == PJ_SUCCESS) ? 0 : 1;
863 }
864 
865 
866 
867 
868 static const char *good_number(char *buf, pj_int32_t val)
869 {
870  if (val < 1000) {
871  pj_ansi_sprintf(buf, "%d", val);
872  } else if (val < 1000000) {
873  pj_ansi_sprintf(buf, "%d.%dK",
874  val / 1000,
875  (val % 1000) / 100);
876  } else {
877  pj_ansi_sprintf(buf, "%d.%02dM",
878  val / 1000000,
879  (val % 1000000) / 10000);
880  }
881 
882  return buf;
883 }
884 
885 
886 #define SAMPLES_TO_USEC(usec, samples, clock_rate) \
887  do { \
888  if (samples <= 4294) \
889  usec = samples * 1000000 / clock_rate; \
890  else { \
891  usec = samples * 1000 / clock_rate; \
892  usec *= 1000; \
893  } \
894  } while(0)
895 
896 #define PRINT_VOIP_MTC_VAL(s, v) \
897  if (v == 127) \
898  sprintf(s, "(na)"); \
899  else \
900  sprintf(s, "%d", v)
901 
902 
903 /*
904  * Print stream statistics
905  */
906 static void print_stream_stat(pjmedia_stream *stream,
907  const pjmedia_codec_param *codec_param)
908 {
909  char duration[80], last_update[80];
910  char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16];
911  pjmedia_port *port;
912  pjmedia_rtcp_stat stat;
913  pj_time_val now;
914 
915 
916  pj_gettimeofday(&now);
917  pjmedia_stream_get_stat(stream, &stat);
918  pjmedia_stream_get_port(stream, &port);
919 
920  puts("Stream statistics:");
921 
922  /* Print duration */
923  PJ_TIME_VAL_SUB(now, stat.start);
924  sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld",
925  now.sec / 3600,
926  (now.sec % 3600) / 60,
927  (now.sec % 60),
928  now.msec);
929 
930 
931  printf(" Info: audio %dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n",
932  PJMEDIA_PIA_SRATE(&port->info),
933  PJMEDIA_PIA_PTIME(&port->info),
934  good_number(bps, (codec_param->info.avg_bps+7)/8),
935  good_number(ipbps, ((codec_param->info.avg_bps+7)/8) +
936  (40 * 1000 /
937  codec_param->setting.frm_per_pkt /
938  codec_param->info.frm_ptime)));
939 
940  if (stat.rx.update_cnt == 0)
941  strcpy(last_update, "never");
942  else {
943  pj_gettimeofday(&now);
944  PJ_TIME_VAL_SUB(now, stat.rx.update);
945  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
946  now.sec / 3600,
947  (now.sec % 3600) / 60,
948  now.sec % 60,
949  now.msec);
950  }
951 
952  printf(" RX stat last update: %s\n"
953  " total %s packets %sB received (%sB +IP hdr)%s\n"
954  " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
955  " (msec) min avg max last dev\n"
956  " loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n"
957  " jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
958  last_update,
959  good_number(packets, stat.rx.pkt),
960  good_number(bytes, stat.rx.bytes),
961  good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
962  "",
963  stat.rx.loss,
964  stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
965  stat.rx.dup,
966  stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
967  stat.rx.reorder,
968  stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
969  "",
970  stat.rx.loss_period.min / 1000.0,
971  stat.rx.loss_period.mean / 1000.0,
972  stat.rx.loss_period.max / 1000.0,
973  stat.rx.loss_period.last / 1000.0,
974  pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
975  "",
976  stat.rx.jitter.min / 1000.0,
977  stat.rx.jitter.mean / 1000.0,
978  stat.rx.jitter.max / 1000.0,
979  stat.rx.jitter.last / 1000.0,
980  pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
981  ""
982  );
983 
984 
985  if (stat.tx.update_cnt == 0)
986  strcpy(last_update, "never");
987  else {
988  pj_gettimeofday(&now);
989  PJ_TIME_VAL_SUB(now, stat.tx.update);
990  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
991  now.sec / 3600,
992  (now.sec % 3600) / 60,
993  now.sec % 60,
994  now.msec);
995  }
996 
997  printf(" TX stat last update: %s\n"
998  " total %s packets %sB sent (%sB +IP hdr)%s\n"
999  " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
1000  " (msec) min avg max last dev\n"
1001  " loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n"
1002  " jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
1003  last_update,
1004  good_number(packets, stat.tx.pkt),
1005  good_number(bytes, stat.tx.bytes),
1006  good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
1007  "",
1008  stat.tx.loss,
1009  stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
1010  stat.tx.dup,
1011  stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
1012  stat.tx.reorder,
1013  stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
1014  "",
1015  stat.tx.loss_period.min / 1000.0,
1016  stat.tx.loss_period.mean / 1000.0,
1017  stat.tx.loss_period.max / 1000.0,
1018  stat.tx.loss_period.last / 1000.0,
1019  pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
1020  "",
1021  stat.tx.jitter.min / 1000.0,
1022  stat.tx.jitter.mean / 1000.0,
1023  stat.tx.jitter.max / 1000.0,
1024  stat.tx.jitter.last / 1000.0,
1025  pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
1026  ""
1027  );
1028 
1029 
1030  printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
1031  stat.rtt.min / 1000.0,
1032  stat.rtt.mean / 1000.0,
1033  stat.rtt.max / 1000.0,
1034  stat.rtt.last / 1000.0,
1035  pj_math_stat_get_stddev(&stat.rtt) / 1000.0,
1036  ""
1037  );
1038 
1039 #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
1040  /* RTCP XR Reports */
1041  do {
1042  char loss[16], dup[16];
1043  char jitter[80];
1044  char toh[80];
1045  char plc[16], jba[16], jbr[16];
1046  char signal_lvl[16], noise_lvl[16], rerl[16];
1047  char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
1048  pjmedia_rtcp_xr_stat xr_stat;
1049 
1050  if (pjmedia_stream_get_stat_xr(stream, &xr_stat) != PJ_SUCCESS)
1051  break;
1052 
1053  puts("\nExtended reports:");
1054 
1055  /* Statistics Summary */
1056  puts(" Statistics Summary");
1057 
1058  if (xr_stat.rx.stat_sum.l)
1059  sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
1060  else
1061  sprintf(loss, "(na)");
1062 
1063  if (xr_stat.rx.stat_sum.d)
1064  sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
1065  else
1066  sprintf(dup, "(na)");
1067 
1068  if (xr_stat.rx.stat_sum.j) {
1069  unsigned jmin, jmax, jmean, jdev;
1070 
1071  SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
1072  port->info.fmt.det.aud.clock_rate);
1073  SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
1074  port->info.fmt.det.aud.clock_rate);
1075  SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
1076  port->info.fmt.det.aud.clock_rate);
1077  SAMPLES_TO_USEC(jdev,
1078  pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
1079  port->info.fmt.det.aud.clock_rate);
1080  sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
1081  jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
1082  } else
1083  sprintf(jitter, "(report not available)");
1084 
1085  if (xr_stat.rx.stat_sum.t) {
1086  sprintf(toh, "%11d %11d %11d %11d",
1087  xr_stat.rx.stat_sum.toh.min,
1088  xr_stat.rx.stat_sum.toh.mean,
1089  xr_stat.rx.stat_sum.toh.max,
1090  pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
1091  } else
1092  sprintf(toh, "(report not available)");
1093 
1094  if (xr_stat.rx.stat_sum.update.sec == 0)
1095  strcpy(last_update, "never");
1096  else {
1097  pj_gettimeofday(&now);
1098  PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
1099  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1100  now.sec / 3600,
1101  (now.sec % 3600) / 60,
1102  now.sec % 60,
1103  now.msec);
1104  }
1105 
1106  printf(" RX last update: %s\n"
1107  " begin seq=%d, end seq=%d%s\n"
1108  " pkt loss=%s, dup=%s%s\n"
1109  " (msec) min avg max dev\n"
1110  " jitter : %s\n"
1111  " toh : %s\n",
1112  last_update,
1113  xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
1114  "",
1115  loss, dup,
1116  "",
1117  jitter,
1118  toh
1119  );
1120 
1121  if (xr_stat.tx.stat_sum.l)
1122  sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
1123  else
1124  sprintf(loss, "(na)");
1125 
1126  if (xr_stat.tx.stat_sum.d)
1127  sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
1128  else
1129  sprintf(dup, "(na)");
1130 
1131  if (xr_stat.tx.stat_sum.j) {
1132  unsigned jmin, jmax, jmean, jdev;
1133 
1134  SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
1135  port->info.fmt.det.aud.clock_rate);
1136  SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
1137  port->info.fmt.det.aud.clock_rate);
1138  SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
1139  port->info.fmt.det.aud.clock_rate);
1140  SAMPLES_TO_USEC(jdev,
1141  pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
1142  port->info.fmt.det.aud.clock_rate);
1143  sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
1144  jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
1145  } else
1146  sprintf(jitter, "(report not available)");
1147 
1148  if (xr_stat.tx.stat_sum.t) {
1149  sprintf(toh, "%11d %11d %11d %11d",
1150  xr_stat.tx.stat_sum.toh.min,
1151  xr_stat.tx.stat_sum.toh.mean,
1152  xr_stat.tx.stat_sum.toh.max,
1153  pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
1154  } else
1155  sprintf(toh, "(report not available)");
1156 
1157  if (xr_stat.tx.stat_sum.update.sec == 0)
1158  strcpy(last_update, "never");
1159  else {
1160  pj_gettimeofday(&now);
1161  PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
1162  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1163  now.sec / 3600,
1164  (now.sec % 3600) / 60,
1165  now.sec % 60,
1166  now.msec);
1167  }
1168 
1169  printf(" TX last update: %s\n"
1170  " begin seq=%d, end seq=%d%s\n"
1171  " pkt loss=%s, dup=%s%s\n"
1172  " (msec) min avg max dev\n"
1173  " jitter : %s\n"
1174  " toh : %s\n",
1175  last_update,
1176  xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
1177  "",
1178  loss, dup,
1179  "",
1180  jitter,
1181  toh
1182  );
1183 
1184  /* VoIP Metrics */
1185  puts(" VoIP Metrics");
1186 
1187  PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
1188  PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
1189  PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
1190  PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
1191  PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
1192  PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
1193  PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
1194 
1195  switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
1196  case PJMEDIA_RTCP_XR_PLC_DIS:
1197  sprintf(plc, "DISABLED");
1198  break;
1199  case PJMEDIA_RTCP_XR_PLC_ENH:
1200  sprintf(plc, "ENHANCED");
1201  break;
1202  case PJMEDIA_RTCP_XR_PLC_STD:
1203  sprintf(plc, "STANDARD");
1204  break;
1205  case PJMEDIA_RTCP_XR_PLC_UNK:
1206  default:
1207  sprintf(plc, "UNKNOWN");
1208  break;
1209  }
1210 
1211  switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
1212  case PJMEDIA_RTCP_XR_JB_FIXED:
1213  sprintf(jba, "FIXED");
1214  break;
1215  case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
1216  sprintf(jba, "ADAPTIVE");
1217  break;
1218  default:
1219  sprintf(jba, "UNKNOWN");
1220  break;
1221  }
1222 
1223  sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
1224 
1225  if (xr_stat.rx.voip_mtc.update.sec == 0)
1226  strcpy(last_update, "never");
1227  else {
1228  pj_gettimeofday(&now);
1229  PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
1230  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1231  now.sec / 3600,
1232  (now.sec % 3600) / 60,
1233  now.sec % 60,
1234  now.msec);
1235  }
1236 
1237  printf(" RX last update: %s\n"
1238  " packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
1239  " burst : density=%d (%.2f%%), duration=%d%s\n"
1240  " gap : density=%d (%.2f%%), duration=%d%s\n"
1241  " delay : round trip=%d%s, end system=%d%s\n"
1242  " level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
1243  " quality : R factor=%s, ext R factor=%s\n"
1244  " MOS LQ=%s, MOS CQ=%s\n"
1245  " config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
1246  " JB delay : cur=%d%s, max=%d%s, abs max=%d%s\n",
1247  last_update,
1248  /* pakcets */
1249  xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
1250  xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
1251  /* burst */
1252  xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
1253  xr_stat.rx.voip_mtc.burst_dur, "ms",
1254  /* gap */
1255  xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
1256  xr_stat.rx.voip_mtc.gap_dur, "ms",
1257  /* delay */
1258  xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
1259  xr_stat.rx.voip_mtc.end_sys_delay, "ms",
1260  /* level */
1261  signal_lvl, "dB",
1262  noise_lvl, "dB",
1263  rerl, "",
1264  /* quality */
1265  r_factor, ext_r_factor, mos_lq, mos_cq,
1266  /* config */
1267  plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
1268  /* JB delay */
1269  xr_stat.rx.voip_mtc.jb_nom, "ms",
1270  xr_stat.rx.voip_mtc.jb_max, "ms",
1271  xr_stat.rx.voip_mtc.jb_abs_max, "ms"
1272  );
1273 
1274  PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
1275  PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
1276  PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
1277  PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
1278  PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
1279  PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
1280  PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
1281 
1282  switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
1283  case PJMEDIA_RTCP_XR_PLC_DIS:
1284  sprintf(plc, "DISABLED");
1285  break;
1286  case PJMEDIA_RTCP_XR_PLC_ENH:
1287  sprintf(plc, "ENHANCED");
1288  break;
1289  case PJMEDIA_RTCP_XR_PLC_STD:
1290  sprintf(plc, "STANDARD");
1291  break;
1292  case PJMEDIA_RTCP_XR_PLC_UNK:
1293  default:
1294  sprintf(plc, "unknown");
1295  break;
1296  }
1297 
1298  switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
1299  case PJMEDIA_RTCP_XR_JB_FIXED:
1300  sprintf(jba, "FIXED");
1301  break;
1302  case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
1303  sprintf(jba, "ADAPTIVE");
1304  break;
1305  default:
1306  sprintf(jba, "unknown");
1307  break;
1308  }
1309 
1310  sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
1311 
1312  if (xr_stat.tx.voip_mtc.update.sec == 0)
1313  strcpy(last_update, "never");
1314  else {
1315  pj_gettimeofday(&now);
1316  PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
1317  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1318  now.sec / 3600,
1319  (now.sec % 3600) / 60,
1320  now.sec % 60,
1321  now.msec);
1322  }
1323 
1324  printf(" TX last update: %s\n"
1325  " packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
1326  " burst : density=%d (%.2f%%), duration=%d%s\n"
1327  " gap : density=%d (%.2f%%), duration=%d%s\n"
1328  " delay : round trip=%d%s, end system=%d%s\n"
1329  " level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
1330  " quality : R factor=%s, ext R factor=%s\n"
1331  " MOS LQ=%s, MOS CQ=%s\n"
1332  " config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
1333  " JB delay : cur=%d%s, max=%d%s, abs max=%d%s\n",
1334  last_update,
1335  /* pakcets */
1336  xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
1337  xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
1338  /* burst */
1339  xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
1340  xr_stat.tx.voip_mtc.burst_dur, "ms",
1341  /* gap */
1342  xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
1343  xr_stat.tx.voip_mtc.gap_dur, "ms",
1344  /* delay */
1345  xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
1346  xr_stat.tx.voip_mtc.end_sys_delay, "ms",
1347  /* level */
1348  signal_lvl, "dB",
1349  noise_lvl, "dB",
1350  rerl, "",
1351  /* quality */
1352  r_factor, ext_r_factor, mos_lq, mos_cq,
1353  /* config */
1354  plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
1355  /* JB delay */
1356  xr_stat.tx.voip_mtc.jb_nom, "ms",
1357  xr_stat.tx.voip_mtc.jb_max, "ms",
1358  xr_stat.tx.voip_mtc.jb_abs_max, "ms"
1359  );
1360 
1361 
1362  /* RTT delay (by receiver side) */
1363  printf(" (msec) min avg max last dev\n");
1364  printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
1365  xr_stat.rtt.min / 1000.0,
1366  xr_stat.rtt.mean / 1000.0,
1367  xr_stat.rtt.max / 1000.0,
1368  xr_stat.rtt.last / 1000.0,
1369  pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0,
1370  ""
1371  );
1372  } while (0);
1373 #endif /* PJMEDIA_HAS_RTCP_XR */
1374 
1375 }
1376 
pj_status_t pjmedia_transport_close(pjmedia_transport *tp)
Definition: transport.h:1003
pj_uint32_t begin_seq
Definition: rtcp_xr.h:250
PJMEDIA main header file.
pjmedia_audio_format_detail aud
Definition: format.h:311
pj_sockaddr rtcp_addr_name
Definition: transport.h:296
pj_str_t key
Definition: transport_srtp.h:94
pj_ssize_t slen
unsigned t
Definition: rtcp_xr.h:260
#define PJ_ASSERT_RETURN(expr, retval)
pj_uint8_t ext_r_factor
Definition: rtcp_xr.h:286
pj_in_addr sin_addr
pj_uint16_t pj_ntohs(pj_uint16_t netshort)
unsigned tx_pt
Definition: stream.h:117
pj_int8_t signal_lvl
Definition: rtcp_xr.h:280
Definition: codec.h:235
pj_uint32_t pkt
Definition: rtcp.h:175
pj_math_stat jitter
Definition: rtcp_xr.h:265
pj_status_t pjmedia_transport_srtp_start(pjmedia_transport *srtp, const pjmedia_srtp_crypto *tx, const pjmedia_srtp_crypto *rx)
PJ_BEGIN_DECL typedef int pj_int32_t
pjmedia_port_info info
Definition: port.h:366
pj_time_val update
Definition: rtcp.h:173
void pj_pool_release(pj_pool_t *pool)
pj_addr_hdr addr
unsigned lost
Definition: rtcp_xr.h:263
pj_status_t pjmedia_transport_srtp_create(pjmedia_endpt *endpt, pjmedia_transport *tp, const pjmedia_srtp_setting *opt, pjmedia_transport **p_tp)
pjmedia_format fmt
Definition: port.h:234
#define PJ_INVALID_SOCKET
int pj_bool_t
pj_uint16_t gap_dur
Definition: rtcp_xr.h:277
pj_uint16_t jb_max
Definition: rtcp_xr.h:295
void * pj_memcpy(void *dst, const void *src, pj_size_t size)
pj_uint16_t sin_port
Definition: transport_srtp.h:91
pjmedia_type type
Definition: stream.h:95
pj_time_val update
Definition: rtcp_xr.h:248
unsigned loss
Definition: rtcp.h:178
pj_sockaddr rem_addr
Definition: transport_srtp.h:287
pjmedia_codec_info fmt
Definition: stream.h:115
pj_status_t pjmedia_port_destroy(pjmedia_port *port)
Definition: codec.h:268
#define PJ_TIME_VAL_SUB(t1, t2)
struct pjmedia_endpt pjmedia_endpt
Definition: types.h:187
#define pj_AF_INET()
pj_uint8_t frm_per_pkt
Definition: codec.h:300
pj_uint16_t rnd_trip_delay
Definition: rtcp_xr.h:278
pj_status_t pjmedia_stream_start(pjmedia_stream *stream)
unsigned clock_rate
Definition: format.h:247
unsigned update_cnt
Definition: rtcp.h:174
pj_status_t pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port)
pj_status_t pjmedia_master_port_create(pj_pool_t *pool, pjmedia_port *u_port, pjmedia_port *d_port, unsigned options, pjmedia_master_port **p_m)
unsigned pj_math_stat_get_stddev(const pj_math_stat *stat)
Definition: types.h:60
pj_uint8_t mos_cq
Definition: rtcp_xr.h:290
unsigned pt
Definition: codec.h:238
Definition: codec.h:704
pj_status_t pjmedia_wav_writer_port_create(pj_pool_t *pool, const char *filename, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned flags, pj_ssize_t buff_size, pjmedia_port **p_port)
pjmedia_transport * pjmedia_stream_get_transport(pjmedia_stream *st)
Definition: transport.h:504
pj_uint8_t gap_den
Definition: rtcp_xr.h:275
pj_str_t name
Definition: transport_srtp.h:97
#define PJ_LOG(level, arg)
void * pj_memset(void *dst, int c, pj_size_t size)
struct pjmedia_snd_port pjmedia_snd_port
Definition: sound_port.h:145
unsigned l
Definition: rtcp_xr.h:257
int pj_status_t
pj_uint8_t rerl
Definition: rtcp_xr.h:282
pj_pool_factory_policy pj_pool_factory_default_policy
unsigned j
Definition: rtcp_xr.h:259
pj_uint16_t sa_family
unsigned pj_hex_digit_to_val(unsigned char c)
pj_status_t pjmedia_event_mgr_create(pj_pool_t *pool, unsigned options, pjmedia_event_mgr **mgr)
Definition: types.h:158
pj_int8_t noise_lvl
Definition: rtcp_xr.h:281
Definition: types.h:164
Secure RTP (SRTP) transport.
pj_uint8_t rx_config
Definition: rtcp_xr.h:292
int pj_isxdigit(unsigned char c)
pj_status_t pjmedia_stream_create(pjmedia_endpt *endpt, pj_pool_t *pool, const pjmedia_stream_info *info, pjmedia_transport *tp, void *user_data, pjmedia_stream **p_stream)
pj_status_t pj_sockaddr_in_init(pj_sockaddr_in *addr, const pj_str_t *cp, pj_uint16_t port)
Definition: port.h:364
pj_status_t pjmedia_master_port_destroy(pjmedia_master_port *m, pj_bool_t destroy_ports)
unsigned d
Definition: rtcp_xr.h:258
#define pj_SOCK_DGRAM()
pj_str_t info
#define pj_SOL_IP()
unsigned rx_pt
Definition: stream.h:118
pj_pool_factory factory
pj_status_t pjmedia_transport_srtp_dtls_get_fingerprint(pjmedia_transport *srtp, const char *hash, char *buf, pj_size_t *len)
void pjmedia_event_mgr_destroy(pjmedia_event_mgr *mgr)
pj_status_t pjmedia_transport_srtp_dtls_start_nego(pjmedia_transport *srtp, const pjmedia_srtp_dtls_nego_param *param)
pj_uint32_t ssrc
Definition: stream.h:122
pjmedia_codec_mgr * pjmedia_endpt_get_codec_mgr(pjmedia_endpt *endpt)
pj_uint16_t jb_abs_max
Definition: rtcp_xr.h:297
Include all codecs API in PJMEDIA-CODEC.
pj_sock_t rtcp_sock
Definition: transport.h:289
pj_status_t pjmedia_master_port_start(pjmedia_master_port *m)
pj_status_t pjmedia_snd_port_create(pj_pool_t *pool, int rec_id, int play_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port)
pj_status_t pjmedia_transport_udp_create(pjmedia_endpt *endpt, const char *name, int port, unsigned options, pjmedia_transport **p_tp)
PJ_TRUE
Definition: stream.h:93
pj_uint16_t end_sys_delay
Definition: rtcp_xr.h:279
pj_uint32_t bytes
Definition: rtcp.h:176
unsigned PJMEDIA_PIA_CCNT(const pjmedia_port_info *pia)
Definition: port.h:257
pj_status_t pj_sock_setsockopt(pj_sock_t sockfd, pj_uint16_t level, pj_uint16_t optname, const void *optval, int optlen)
pj_status_t pjmedia_endpt_create(pj_pool_factory *pf, pj_ioqueue_t *ioqueue, unsigned worker_cnt, pjmedia_endpt **p_endpt)
Definition: endpoint.h:105
#define pj_IP_ADD_MEMBERSHIP()
pj_uint8_t discard_rate
Definition: rtcp_xr.h:273
unsigned dup
Definition: rtcp_xr.h:264
pj_status_t pjmedia_transport_media_stop(pjmedia_transport *tp)
Definition: transport.h:989
pj_sockaddr rem_rtcp
Definition: stream.h:99
pj_str_t pj_str(char *str)
unsigned PJMEDIA_PIA_PTIME(const pjmedia_port_info *pia)
Definition: port.h:284
pj_status_t pjmedia_codec_mgr_get_codec_info(pjmedia_codec_mgr *mgr, unsigned pt, const pjmedia_codec_info **inf)
pj_sock_t rtp_sock
Definition: transport.h:279
void pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src)
pj_status_t pj_sockaddr_set_port(pj_sockaddr *addr, pj_uint16_t hostport)
void pj_caching_pool_init(pj_caching_pool *ch_pool, const pj_pool_factory_policy *policy, pj_size_t max_capacity)
#define pj_SOL_SOCKET()
Definition: rtcp.h:197
unsigned reorder
Definition: rtcp.h:179
pj_status_t pj_sockaddr_init(int af, pj_sockaddr *addr, const pj_str_t *cp, pj_uint16_t port)
pj_status_t pjmedia_snd_port_create_player(pj_pool_t *pool, int index, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port)
pj_status_t pjmedia_codec_mgr_get_default_param(pjmedia_codec_mgr *mgr, const pjmedia_codec_info *info, pjmedia_codec_param *param)
void pj_bzero(void *dst, pj_size_t size)
pj_status_t pj_sock_bind(pj_sock_t sockfd, const pj_sockaddr_t *my_addr, int addrlen)
Definition: jbsim.c:72
pj_status_t pj_gettimeofday(pj_time_val *tv)
pj_uint8_t gmin
Definition: rtcp_xr.h:283
pj_status_t pjmedia_stream_get_stat(const pjmedia_stream *stream, pjmedia_rtcp_stat *stat)
#define pj_assert(expr)
Definition: transport_srtp.h:275
pj_math_stat jitter
Definition: rtcp.h:189
#define PJ_INET_ADDRSTRLEN
pj_uint8_t mos_lq
Definition: rtcp_xr.h:288
pj_status_t pj_sock_socket(int family, int type, int protocol, pj_sock_t *sock)
pj_uint16_t jb_nom
Definition: rtcp_xr.h:293
pj_status_t pjmedia_codec_register_audio_codecs(pjmedia_endpt *endpt, const pjmedia_audio_codec_config *c)
pjmedia_rtcp_stream_stat rx
Definition: rtcp.h:202
pj_status_t pjmedia_wav_player_port_create(pj_pool_t *pool, const char *filename, unsigned ptime, unsigned flags, pj_ssize_t buff_size, pjmedia_port **p_port)
char * pj_inet_ntop2(int af, const void *src, char *dst, int size)
pj_uint16_t pj_sockaddr_get_port(const pj_sockaddr_t *addr)
pj_status_t pjmedia_stream_destroy(pjmedia_stream *stream)
pj_uint32_t s_addr
pj_sockaddr rem_addr
Definition: stream.h:98
unsigned PJMEDIA_PIA_SPF(const pjmedia_port_info *pia)
Definition: port.h:298
union pjmedia_format::@4 det
pj_status_t pjmedia_snd_port_connect(pjmedia_snd_port *snd_port, pjmedia_port *port)
unsigned pj_sockaddr_get_len(const pj_sockaddr_t *addr)
pj_math_stat toh
Definition: rtcp_xr.h:266
pj_pool_t * pj_pool_create(pj_pool_factory *factory, const char *name, pj_size_t initial_size, pj_size_t increment_size, pj_pool_callback *callback)
pjmedia_rtcp_xr_stream_stat rx
Definition: rtcp_xr.h:305
unsigned PJMEDIA_PIA_SRATE(const pjmedia_port_info *pia)
Definition: port.h:244
pj_uint16_t burst_dur
Definition: rtcp_xr.h:276
pj_status_t pjmedia_codec_mgr_find_codecs_by_id(pjmedia_codec_mgr *mgr, const pj_str_t *codec_id, unsigned *count, const pjmedia_codec_info *p_info[], unsigned prio[])
struct pjmedia_codec_param::@1 info
pj_status_t pjmedia_transport_media_start(pjmedia_transport *tp, pj_pool_t *tmp_pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *sdp_remote, unsigned media_index)
Definition: transport.h:967
pj_status_t pjmedia_transport_udp_attach(pjmedia_endpt *endpt, const char *name, const pjmedia_sock_info *si, unsigned options, pjmedia_transport **p_tp)
pj_sockaddr_in ipv4
unsigned PJMEDIA_PIA_BITS(const pjmedia_port_info *pia)
Definition: port.h:270
void pj_shutdown(void)
pj_sockaddr rtp_addr_name
Definition: transport.h:286
pj_uint32_t avg_bps
Definition: codec.h:277
pj_uint8_t loss_rate
Definition: rtcp_xr.h:272
pj_uint16_t frm_ptime
Definition: codec.h:280
struct pjmedia_codec_param::@2 setting
pj_bool_t is_role_active
Definition: transport_srtp.h:299
#define pj_IP_MULTICAST_LOOP()
pjmedia_dir dir
Definition: stream.h:97
pj_sockaddr rem_rtcp
Definition: transport_srtp.h:292
typedefPJ_BEGIN_DECL struct pjmedia_master_port pjmedia_master_port
Definition: master_port.h:66
pj_uint32_t end_seq
Definition: rtcp_xr.h:251
pj_status_t pjmedia_stream_get_port(pjmedia_stream *stream, pjmedia_port **p_port)
#define pj_SO_REUSEADDR()
pj_uint8_t r_factor
Definition: rtcp_xr.h:284
PJ_SUCCESS
pj_time_val start
Definition: rtcp.h:199
pjmedia_dir
Definition: types.h:152
unsigned short pj_uint16_t
pj_uint32_t pj_htonl(pj_uint32_t hostlong)
pj_math_stat rtt
Definition: rtcp.h:204
pj_uint8_t burst_den
Definition: rtcp_xr.h:274
pjmedia_rtcp_xr_stream_stat tx
Definition: rtcp_xr.h:306
pj_status_t pj_init(void)
pj_math_stat loss_period
Definition: rtcp.h:182
Definition: types.h:173
unsigned dup
Definition: rtcp.h:180
pj_status_t pjmedia_endpt_destroy(pjmedia_endpt *endpt)
Definition: endpoint.h:146
size_t pj_size_t
pj_str_t * pj_strset(pj_str_t *str, char *ptr, pj_size_t length)
PJ_FALSE
int pj_rand(void)
pj_status_t pjmedia_snd_port_create_rec(pj_pool_t *pool, int index, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port)
Definition: rtcp_xr.h:303
pj_math_stat rtt
Definition: rtcp_xr.h:307
pjmedia_rtcp_stream_stat tx
Definition: rtcp.h:201
void pj_caching_pool_destroy(pj_caching_pool *ch_pool)
Definition: transport.h:276
Definition: transport_udp.h:59

 


PJMEDIA small footprint Open Source media stack
Copyright (C) 2006-2008 Teluu Inc.