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 5747 2018-02-26 10:14:27Z 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 stream based on program arguments */
643  status = create_stream(pool, med_endpt, codec_info, dir, local_port,
644  &remote_addr, mcast, &mcast_addr,
645 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
646  use_srtp, &srtp_crypto_suite,
647  &srtp_tx_key, &srtp_rx_key,
648  is_dtls_client, is_dtls_server,
649 #endif
650  &stream);
651  if (status != PJ_SUCCESS)
652  goto on_exit;
653 
654  /* Get codec default param for info */
656  pjmedia_endpt_get_codec_mgr(med_endpt),
657  codec_info,
658  &codec_param);
659  /* Should be ok, as create_stream() above succeeded */
660  pj_assert(status == PJ_SUCCESS);
661 
662  /* Get the port interface of the stream */
663  status = pjmedia_stream_get_port( stream, &stream_port);
664  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
665 
666 
667  if (play_file) {
668  unsigned wav_ptime;
669 
670  wav_ptime = PJMEDIA_PIA_PTIME(&stream_port->info);
671  status = pjmedia_wav_player_port_create(pool, play_file, wav_ptime,
672  0, -1, &play_file_port);
673  if (status != PJ_SUCCESS) {
674  app_perror(THIS_FILE, "Unable to use file", status);
675  goto on_exit;
676  }
677 
678  status = pjmedia_master_port_create(pool, play_file_port, stream_port,
679  0, &master_port);
680  if (status != PJ_SUCCESS) {
681  app_perror(THIS_FILE, "Unable to create master port", status);
682  goto on_exit;
683  }
684 
685  status = pjmedia_master_port_start(master_port);
686  if (status != PJ_SUCCESS) {
687  app_perror(THIS_FILE, "Error starting master port", status);
688  goto on_exit;
689  }
690 
691  printf("Playing from WAV file %s..\n", play_file);
692 
693  } else if (rec_file) {
694 
695  status = pjmedia_wav_writer_port_create(pool, rec_file,
696  PJMEDIA_PIA_SRATE(&stream_port->info),
697  PJMEDIA_PIA_CCNT(&stream_port->info),
698  PJMEDIA_PIA_SPF(&stream_port->info),
699  PJMEDIA_PIA_BITS(&stream_port->info),
700  0, 0, &rec_file_port);
701  if (status != PJ_SUCCESS) {
702  app_perror(THIS_FILE, "Unable to use file", status);
703  goto on_exit;
704  }
705 
706  status = pjmedia_master_port_create(pool, stream_port, rec_file_port,
707  0, &master_port);
708  if (status != PJ_SUCCESS) {
709  app_perror(THIS_FILE, "Unable to create master port", status);
710  goto on_exit;
711  }
712 
713  status = pjmedia_master_port_start(master_port);
714  if (status != PJ_SUCCESS) {
715  app_perror(THIS_FILE, "Error starting master port", status);
716  goto on_exit;
717  }
718 
719  printf("Recording to WAV file %s..\n", rec_file);
720 
721  } else {
722 
723  /* Create sound device port. */
725  status = pjmedia_snd_port_create(pool, -1, -1,
726  PJMEDIA_PIA_SRATE(&stream_port->info),
727  PJMEDIA_PIA_CCNT(&stream_port->info),
728  PJMEDIA_PIA_SPF(&stream_port->info),
729  PJMEDIA_PIA_BITS(&stream_port->info),
730  0, &snd_port);
731  else if (dir == PJMEDIA_DIR_ENCODING)
732  status = pjmedia_snd_port_create_rec(pool, -1,
733  PJMEDIA_PIA_SRATE(&stream_port->info),
734  PJMEDIA_PIA_CCNT(&stream_port->info),
735  PJMEDIA_PIA_SPF(&stream_port->info),
736  PJMEDIA_PIA_BITS(&stream_port->info),
737  0, &snd_port);
738  else
739  status = pjmedia_snd_port_create_player(pool, -1,
740  PJMEDIA_PIA_SRATE(&stream_port->info),
741  PJMEDIA_PIA_CCNT(&stream_port->info),
742  PJMEDIA_PIA_SPF(&stream_port->info),
743  PJMEDIA_PIA_BITS(&stream_port->info),
744  0, &snd_port);
745 
746 
747  if (status != PJ_SUCCESS) {
748  app_perror(THIS_FILE, "Unable to create sound port", status);
749  goto on_exit;
750  }
751 
752  /* Connect sound port to stream */
753  status = pjmedia_snd_port_connect( snd_port, stream_port );
754  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
755 
756  }
757 
758  /* Start streaming */
759  pjmedia_stream_start(stream);
760 
761 
762  /* Done */
763 
764  if (dir == PJMEDIA_DIR_DECODING)
765  printf("Stream is active, dir is recv-only, local port is %d\n",
766  local_port);
767  else if (dir == PJMEDIA_DIR_ENCODING)
768  printf("Stream is active, dir is send-only, sending to %s:%d\n",
769  pj_inet_ntop2(pj_AF_INET(), &remote_addr.sin_addr, addr,
770  sizeof(addr)),
771  pj_ntohs(remote_addr.sin_port));
772  else
773  printf("Stream is active, send/recv, local port is %d, "
774  "sending to %s:%d\n",
775  local_port,
776  pj_inet_ntop2(pj_AF_INET(), &remote_addr.sin_addr, addr,
777  sizeof(addr)),
778  pj_ntohs(remote_addr.sin_port));
779 
780 
781  for (;;) {
782 
783  puts("");
784  puts("Commands:");
785  puts(" s Display media statistics");
786  puts(" q Quit");
787  puts("");
788 
789  printf("Command: "); fflush(stdout);
790 
791  if (fgets(tmp, sizeof(tmp), stdin) == NULL) {
792  puts("EOF while reading stdin, will quit now..");
793  break;
794  }
795 
796  if (tmp[0] == 's')
797  print_stream_stat(stream, &codec_param);
798  else if (tmp[0] == 'q')
799  break;
800 
801  }
802 
803 
804 
805  /* Start deinitialization: */
806 on_exit:
807 
808  /* Destroy sound device */
809  if (snd_port) {
810  pjmedia_snd_port_destroy( snd_port );
811  PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
812  }
813 
814  /* If there is master port, then we just need to destroy master port
815  * (it will recursively destroy upstream and downstream ports, which
816  * in this case are file_port and stream_port).
817  */
818  if (master_port) {
819  pjmedia_master_port_destroy(master_port, PJ_TRUE);
820  play_file_port = NULL;
821  stream = NULL;
822  }
823 
824  /* Destroy stream */
825  if (stream) {
826  pjmedia_transport *tp;
827 
828  tp = pjmedia_stream_get_transport(stream);
829  pjmedia_stream_destroy(stream);
830 
832  }
833 
834  /* Destroy file ports */
835  if (play_file_port)
836  pjmedia_port_destroy( play_file_port );
837  if (rec_file_port)
838  pjmedia_port_destroy( rec_file_port );
839 
840 
841  /* Release application pool */
842  pj_pool_release( pool );
843 
844  /* Destroy media endpoint. */
845  pjmedia_endpt_destroy( med_endpt );
846 
847  /* Destroy pool factory */
849 
850  /* Shutdown PJLIB */
851  pj_shutdown();
852 
853 
854  return (status == PJ_SUCCESS) ? 0 : 1;
855 }
856 
857 
858 
859 
860 static const char *good_number(char *buf, pj_int32_t val)
861 {
862  if (val < 1000) {
863  pj_ansi_sprintf(buf, "%d", val);
864  } else if (val < 1000000) {
865  pj_ansi_sprintf(buf, "%d.%dK",
866  val / 1000,
867  (val % 1000) / 100);
868  } else {
869  pj_ansi_sprintf(buf, "%d.%02dM",
870  val / 1000000,
871  (val % 1000000) / 10000);
872  }
873 
874  return buf;
875 }
876 
877 
878 #define SAMPLES_TO_USEC(usec, samples, clock_rate) \
879  do { \
880  if (samples <= 4294) \
881  usec = samples * 1000000 / clock_rate; \
882  else { \
883  usec = samples * 1000 / clock_rate; \
884  usec *= 1000; \
885  } \
886  } while(0)
887 
888 #define PRINT_VOIP_MTC_VAL(s, v) \
889  if (v == 127) \
890  sprintf(s, "(na)"); \
891  else \
892  sprintf(s, "%d", v)
893 
894 
895 /*
896  * Print stream statistics
897  */
898 static void print_stream_stat(pjmedia_stream *stream,
899  const pjmedia_codec_param *codec_param)
900 {
901  char duration[80], last_update[80];
902  char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16];
903  pjmedia_port *port;
904  pjmedia_rtcp_stat stat;
905  pj_time_val now;
906 
907 
908  pj_gettimeofday(&now);
909  pjmedia_stream_get_stat(stream, &stat);
910  pjmedia_stream_get_port(stream, &port);
911 
912  puts("Stream statistics:");
913 
914  /* Print duration */
915  PJ_TIME_VAL_SUB(now, stat.start);
916  sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld",
917  now.sec / 3600,
918  (now.sec % 3600) / 60,
919  (now.sec % 60),
920  now.msec);
921 
922 
923  printf(" Info: audio %dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n",
924  PJMEDIA_PIA_SRATE(&port->info),
925  PJMEDIA_PIA_PTIME(&port->info),
926  good_number(bps, (codec_param->info.avg_bps+7)/8),
927  good_number(ipbps, ((codec_param->info.avg_bps+7)/8) +
928  (40 * 1000 /
929  codec_param->setting.frm_per_pkt /
930  codec_param->info.frm_ptime)));
931 
932  if (stat.rx.update_cnt == 0)
933  strcpy(last_update, "never");
934  else {
935  pj_gettimeofday(&now);
936  PJ_TIME_VAL_SUB(now, stat.rx.update);
937  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
938  now.sec / 3600,
939  (now.sec % 3600) / 60,
940  now.sec % 60,
941  now.msec);
942  }
943 
944  printf(" RX stat last update: %s\n"
945  " total %s packets %sB received (%sB +IP hdr)%s\n"
946  " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
947  " (msec) min avg max last dev\n"
948  " loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n"
949  " jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
950  last_update,
951  good_number(packets, stat.rx.pkt),
952  good_number(bytes, stat.rx.bytes),
953  good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32),
954  "",
955  stat.rx.loss,
956  stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
957  stat.rx.dup,
958  stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
959  stat.rx.reorder,
960  stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
961  "",
962  stat.rx.loss_period.min / 1000.0,
963  stat.rx.loss_period.mean / 1000.0,
964  stat.rx.loss_period.max / 1000.0,
965  stat.rx.loss_period.last / 1000.0,
966  pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
967  "",
968  stat.rx.jitter.min / 1000.0,
969  stat.rx.jitter.mean / 1000.0,
970  stat.rx.jitter.max / 1000.0,
971  stat.rx.jitter.last / 1000.0,
972  pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
973  ""
974  );
975 
976 
977  if (stat.tx.update_cnt == 0)
978  strcpy(last_update, "never");
979  else {
980  pj_gettimeofday(&now);
981  PJ_TIME_VAL_SUB(now, stat.tx.update);
982  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
983  now.sec / 3600,
984  (now.sec % 3600) / 60,
985  now.sec % 60,
986  now.msec);
987  }
988 
989  printf(" TX stat last update: %s\n"
990  " total %s packets %sB sent (%sB +IP hdr)%s\n"
991  " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n"
992  " (msec) min avg max last dev\n"
993  " loss period: %7.3f %7.3f %7.3f %7.3f %7.3f%s\n"
994  " jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
995  last_update,
996  good_number(packets, stat.tx.pkt),
997  good_number(bytes, stat.tx.bytes),
998  good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32),
999  "",
1000  stat.tx.loss,
1001  stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
1002  stat.tx.dup,
1003  stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
1004  stat.tx.reorder,
1005  stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
1006  "",
1007  stat.tx.loss_period.min / 1000.0,
1008  stat.tx.loss_period.mean / 1000.0,
1009  stat.tx.loss_period.max / 1000.0,
1010  stat.tx.loss_period.last / 1000.0,
1011  pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
1012  "",
1013  stat.tx.jitter.min / 1000.0,
1014  stat.tx.jitter.mean / 1000.0,
1015  stat.tx.jitter.max / 1000.0,
1016  stat.tx.jitter.last / 1000.0,
1017  pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
1018  ""
1019  );
1020 
1021 
1022  printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
1023  stat.rtt.min / 1000.0,
1024  stat.rtt.mean / 1000.0,
1025  stat.rtt.max / 1000.0,
1026  stat.rtt.last / 1000.0,
1027  pj_math_stat_get_stddev(&stat.rtt) / 1000.0,
1028  ""
1029  );
1030 
1031 #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
1032  /* RTCP XR Reports */
1033  do {
1034  char loss[16], dup[16];
1035  char jitter[80];
1036  char toh[80];
1037  char plc[16], jba[16], jbr[16];
1038  char signal_lvl[16], noise_lvl[16], rerl[16];
1039  char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
1040  pjmedia_rtcp_xr_stat xr_stat;
1041 
1042  if (pjmedia_stream_get_stat_xr(stream, &xr_stat) != PJ_SUCCESS)
1043  break;
1044 
1045  puts("\nExtended reports:");
1046 
1047  /* Statistics Summary */
1048  puts(" Statistics Summary");
1049 
1050  if (xr_stat.rx.stat_sum.l)
1051  sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
1052  else
1053  sprintf(loss, "(na)");
1054 
1055  if (xr_stat.rx.stat_sum.d)
1056  sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
1057  else
1058  sprintf(dup, "(na)");
1059 
1060  if (xr_stat.rx.stat_sum.j) {
1061  unsigned jmin, jmax, jmean, jdev;
1062 
1063  SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
1064  port->info.fmt.det.aud.clock_rate);
1065  SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
1066  port->info.fmt.det.aud.clock_rate);
1067  SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
1068  port->info.fmt.det.aud.clock_rate);
1069  SAMPLES_TO_USEC(jdev,
1070  pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
1071  port->info.fmt.det.aud.clock_rate);
1072  sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
1073  jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
1074  } else
1075  sprintf(jitter, "(report not available)");
1076 
1077  if (xr_stat.rx.stat_sum.t) {
1078  sprintf(toh, "%11d %11d %11d %11d",
1079  xr_stat.rx.stat_sum.toh.min,
1080  xr_stat.rx.stat_sum.toh.mean,
1081  xr_stat.rx.stat_sum.toh.max,
1082  pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
1083  } else
1084  sprintf(toh, "(report not available)");
1085 
1086  if (xr_stat.rx.stat_sum.update.sec == 0)
1087  strcpy(last_update, "never");
1088  else {
1089  pj_gettimeofday(&now);
1090  PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
1091  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1092  now.sec / 3600,
1093  (now.sec % 3600) / 60,
1094  now.sec % 60,
1095  now.msec);
1096  }
1097 
1098  printf(" RX last update: %s\n"
1099  " begin seq=%d, end seq=%d%s\n"
1100  " pkt loss=%s, dup=%s%s\n"
1101  " (msec) min avg max dev\n"
1102  " jitter : %s\n"
1103  " toh : %s\n",
1104  last_update,
1105  xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
1106  "",
1107  loss, dup,
1108  "",
1109  jitter,
1110  toh
1111  );
1112 
1113  if (xr_stat.tx.stat_sum.l)
1114  sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
1115  else
1116  sprintf(loss, "(na)");
1117 
1118  if (xr_stat.tx.stat_sum.d)
1119  sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
1120  else
1121  sprintf(dup, "(na)");
1122 
1123  if (xr_stat.tx.stat_sum.j) {
1124  unsigned jmin, jmax, jmean, jdev;
1125 
1126  SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
1127  port->info.fmt.det.aud.clock_rate);
1128  SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
1129  port->info.fmt.det.aud.clock_rate);
1130  SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
1131  port->info.fmt.det.aud.clock_rate);
1132  SAMPLES_TO_USEC(jdev,
1133  pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
1134  port->info.fmt.det.aud.clock_rate);
1135  sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
1136  jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
1137  } else
1138  sprintf(jitter, "(report not available)");
1139 
1140  if (xr_stat.tx.stat_sum.t) {
1141  sprintf(toh, "%11d %11d %11d %11d",
1142  xr_stat.tx.stat_sum.toh.min,
1143  xr_stat.tx.stat_sum.toh.mean,
1144  xr_stat.tx.stat_sum.toh.max,
1145  pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
1146  } else
1147  sprintf(toh, "(report not available)");
1148 
1149  if (xr_stat.tx.stat_sum.update.sec == 0)
1150  strcpy(last_update, "never");
1151  else {
1152  pj_gettimeofday(&now);
1153  PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
1154  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1155  now.sec / 3600,
1156  (now.sec % 3600) / 60,
1157  now.sec % 60,
1158  now.msec);
1159  }
1160 
1161  printf(" TX last update: %s\n"
1162  " begin seq=%d, end seq=%d%s\n"
1163  " pkt loss=%s, dup=%s%s\n"
1164  " (msec) min avg max dev\n"
1165  " jitter : %s\n"
1166  " toh : %s\n",
1167  last_update,
1168  xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
1169  "",
1170  loss, dup,
1171  "",
1172  jitter,
1173  toh
1174  );
1175 
1176  /* VoIP Metrics */
1177  puts(" VoIP Metrics");
1178 
1179  PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
1180  PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
1181  PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
1182  PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
1183  PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
1184  PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
1185  PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
1186 
1187  switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
1188  case PJMEDIA_RTCP_XR_PLC_DIS:
1189  sprintf(plc, "DISABLED");
1190  break;
1191  case PJMEDIA_RTCP_XR_PLC_ENH:
1192  sprintf(plc, "ENHANCED");
1193  break;
1194  case PJMEDIA_RTCP_XR_PLC_STD:
1195  sprintf(plc, "STANDARD");
1196  break;
1197  case PJMEDIA_RTCP_XR_PLC_UNK:
1198  default:
1199  sprintf(plc, "UNKNOWN");
1200  break;
1201  }
1202 
1203  switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
1204  case PJMEDIA_RTCP_XR_JB_FIXED:
1205  sprintf(jba, "FIXED");
1206  break;
1207  case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
1208  sprintf(jba, "ADAPTIVE");
1209  break;
1210  default:
1211  sprintf(jba, "UNKNOWN");
1212  break;
1213  }
1214 
1215  sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
1216 
1217  if (xr_stat.rx.voip_mtc.update.sec == 0)
1218  strcpy(last_update, "never");
1219  else {
1220  pj_gettimeofday(&now);
1221  PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
1222  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1223  now.sec / 3600,
1224  (now.sec % 3600) / 60,
1225  now.sec % 60,
1226  now.msec);
1227  }
1228 
1229  printf(" RX last update: %s\n"
1230  " packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
1231  " burst : density=%d (%.2f%%), duration=%d%s\n"
1232  " gap : density=%d (%.2f%%), duration=%d%s\n"
1233  " delay : round trip=%d%s, end system=%d%s\n"
1234  " level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
1235  " quality : R factor=%s, ext R factor=%s\n"
1236  " MOS LQ=%s, MOS CQ=%s\n"
1237  " config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
1238  " JB delay : cur=%d%s, max=%d%s, abs max=%d%s\n",
1239  last_update,
1240  /* pakcets */
1241  xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
1242  xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
1243  /* burst */
1244  xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
1245  xr_stat.rx.voip_mtc.burst_dur, "ms",
1246  /* gap */
1247  xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
1248  xr_stat.rx.voip_mtc.gap_dur, "ms",
1249  /* delay */
1250  xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
1251  xr_stat.rx.voip_mtc.end_sys_delay, "ms",
1252  /* level */
1253  signal_lvl, "dB",
1254  noise_lvl, "dB",
1255  rerl, "",
1256  /* quality */
1257  r_factor, ext_r_factor, mos_lq, mos_cq,
1258  /* config */
1259  plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
1260  /* JB delay */
1261  xr_stat.rx.voip_mtc.jb_nom, "ms",
1262  xr_stat.rx.voip_mtc.jb_max, "ms",
1263  xr_stat.rx.voip_mtc.jb_abs_max, "ms"
1264  );
1265 
1266  PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
1267  PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
1268  PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
1269  PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
1270  PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
1271  PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
1272  PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
1273 
1274  switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
1275  case PJMEDIA_RTCP_XR_PLC_DIS:
1276  sprintf(plc, "DISABLED");
1277  break;
1278  case PJMEDIA_RTCP_XR_PLC_ENH:
1279  sprintf(plc, "ENHANCED");
1280  break;
1281  case PJMEDIA_RTCP_XR_PLC_STD:
1282  sprintf(plc, "STANDARD");
1283  break;
1284  case PJMEDIA_RTCP_XR_PLC_UNK:
1285  default:
1286  sprintf(plc, "unknown");
1287  break;
1288  }
1289 
1290  switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
1291  case PJMEDIA_RTCP_XR_JB_FIXED:
1292  sprintf(jba, "FIXED");
1293  break;
1294  case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
1295  sprintf(jba, "ADAPTIVE");
1296  break;
1297  default:
1298  sprintf(jba, "unknown");
1299  break;
1300  }
1301 
1302  sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
1303 
1304  if (xr_stat.tx.voip_mtc.update.sec == 0)
1305  strcpy(last_update, "never");
1306  else {
1307  pj_gettimeofday(&now);
1308  PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
1309  sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
1310  now.sec / 3600,
1311  (now.sec % 3600) / 60,
1312  now.sec % 60,
1313  now.msec);
1314  }
1315 
1316  printf(" TX last update: %s\n"
1317  " packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
1318  " burst : density=%d (%.2f%%), duration=%d%s\n"
1319  " gap : density=%d (%.2f%%), duration=%d%s\n"
1320  " delay : round trip=%d%s, end system=%d%s\n"
1321  " level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
1322  " quality : R factor=%s, ext R factor=%s\n"
1323  " MOS LQ=%s, MOS CQ=%s\n"
1324  " config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
1325  " JB delay : cur=%d%s, max=%d%s, abs max=%d%s\n",
1326  last_update,
1327  /* pakcets */
1328  xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
1329  xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
1330  /* burst */
1331  xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
1332  xr_stat.tx.voip_mtc.burst_dur, "ms",
1333  /* gap */
1334  xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
1335  xr_stat.tx.voip_mtc.gap_dur, "ms",
1336  /* delay */
1337  xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
1338  xr_stat.tx.voip_mtc.end_sys_delay, "ms",
1339  /* level */
1340  signal_lvl, "dB",
1341  noise_lvl, "dB",
1342  rerl, "",
1343  /* quality */
1344  r_factor, ext_r_factor, mos_lq, mos_cq,
1345  /* config */
1346  plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
1347  /* JB delay */
1348  xr_stat.tx.voip_mtc.jb_nom, "ms",
1349  xr_stat.tx.voip_mtc.jb_max, "ms",
1350  xr_stat.tx.voip_mtc.jb_abs_max, "ms"
1351  );
1352 
1353 
1354  /* RTT delay (by receiver side) */
1355  printf(" (msec) min avg max last dev\n");
1356  printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f %7.3f%s\n",
1357  xr_stat.rtt.min / 1000.0,
1358  xr_stat.rtt.mean / 1000.0,
1359  xr_stat.rtt.max / 1000.0,
1360  xr_stat.rtt.last / 1000.0,
1361  pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0,
1362  ""
1363  );
1364  } while (0);
1365 #endif /* PJMEDIA_HAS_RTCP_XR */
1366 
1367 }
1368 
pj_status_t pjmedia_transport_close(pjmedia_transport *tp)
Definition: transport.h:993
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
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
#define PJ_ASSERT_RETURN(expr, retval)
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_TIME_VAL_SUB(t1, t2)
#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
struct pjmedia_endpt pjmedia_endpt
Definition: types.h:187
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:501
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)
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
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)
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
#define pj_assert(expr)
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_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)
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)
#define pj_SOL_IP()
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:957
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
#define pj_AF_INET()

 


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