diff options
Diffstat (limited to 'pjsip-apps/src')
-rw-r--r-- | pjsip-apps/src/samples/debug.c | 2 | ||||
-rw-r--r-- | pjsip-apps/src/samples/playfile.c | 1 | ||||
-rw-r--r-- | pjsip-apps/src/samples/siprtp.c | 66 | ||||
-rw-r--r-- | pjsip-apps/src/samples/stream.c | 681 | ||||
-rw-r--r-- | pjsip-apps/src/samples/util.h | 18 |
5 files changed, 729 insertions, 39 deletions
diff --git a/pjsip-apps/src/samples/debug.c b/pjsip-apps/src/samples/debug.c index 259a7def..b6c10055 100644 --- a/pjsip-apps/src/samples/debug.c +++ b/pjsip-apps/src/samples/debug.c @@ -27,5 +27,5 @@ * E.g.: * #include "playfile.c" */ -#include "siprtp.c" +#include "stream.c" diff --git a/pjsip-apps/src/samples/playfile.c b/pjsip-apps/src/samples/playfile.c index 215ff415..1ae9a6cf 100644 --- a/pjsip-apps/src/samples/playfile.c +++ b/pjsip-apps/src/samples/playfile.c @@ -176,3 +176,4 @@ int main(int argc, char *argv[]) /* Done. */ return 0; } + diff --git a/pjsip-apps/src/samples/siprtp.c b/pjsip-apps/src/samples/siprtp.c index 91cc93d6..ca7f07d5 100644 --- a/pjsip-apps/src/samples/siprtp.c +++ b/pjsip-apps/src/samples/siprtp.c @@ -18,6 +18,43 @@ */ + + +/* Usage */ +static const char *USAGE = +" PURPOSE: \n" +" This program establishes SIP INVITE session and media, and calculate \n" +" the media quality (packet lost, jitter, rtt, etc.). Unlike normal \n" +" pjmedia applications, this program bypasses all pjmedia stream \n" +" framework and transmit encoded RTP packets manually using own thread. \n" +"\n" +" USAGE:\n" +" siprtp [options] => to start in server mode\n" +" siprtp [options] URL => to start in client mode\n" +"\n" +" Program options:\n" +" --count=N, -c Set number of calls to create (default:1) \n" +"\n" +" Address and ports options:\n" +" --local-port=PORT,-p Set local SIP port (default: 5060)\n" +" --rtp-port=PORT, -r Set start of RTP port (default: 4000)\n" +" --ip-addr=IP, -i Set local IP address to use (otherwise it will\n" +" try to determine local IP address from hostname)\n" +"\n" +" Logging Options:\n" +" --log-level=N, -l Set log verbosity level (default=5)\n" +" --app-log-level=N Set app screen log verbosity (default=3)\n" +" --log-file=FILE Write log to file FILE\n" +"\n" +" Codec Options:\n" +" --a-pt=PT Set audio payload type to PT (default=0)\n" +" --a-name=NAME Set audio codec name to NAME (default=pcmu)\n" +" --a-clock=RATE Set audio codec rate to RATE Hz (default=8000Hz)\n" +" --a-bitrate=BPS Set audio codec bitrate to BPS (default=64000bps)\n" +" --a-ptime=MS Set audio frame time to MS msec (default=20ms)\n" +; + + /* Include all headers. */ #include <pjsip.h> #include <pjmedia.h> @@ -725,35 +762,6 @@ static int worker_thread(void *arg) } -/* Usage */ -static const char *USAGE = -"Usage:\n" -" siprtp [options] => to start in server mode\n" -" siprtp [options] URL => to start in client mode\n" -"\n" -"Program options:\n" -" --count=N, -c Set number of calls to create (default:1) \n" -"\n" -"Address and ports options:\n" -" --local-port=PORT,-p Set local SIP port (default: 5060)\n" -" --rtp-port=PORT, -r Set start of RTP port (default: 4000)\n" -" --ip-addr=IP, -i Set local IP address to use (otherwise it will\n" -" try to determine local IP address from hostname)\n" -"\n" -"Logging Options:\n" -" --log-level=N, -l Set log verbosity level (default=5)\n" -" --app-log-level=N Set app screen log verbosity (default=3)\n" -" --log-file=FILE Write log to file FILE\n" -"\n" -"Codec Options:\n" -" --a-pt=PT Set audio payload type to PT (default=0)\n" -" --a-name=NAME Set audio codec name to NAME (default=pcmu)\n" -" --a-clock=RATE Set audio codec rate to RATE Hz (default=8000 Hz)\n" -" --a-bitrate=BPS Set audio codec bitrate to BPS (default=64000 bps)\n" -" --a-ptime=MS Set audio frame time to MS msec (default=20 msec)\n" -; - - /* Init application options */ static pj_status_t init_options(int argc, char *argv[]) { diff --git a/pjsip-apps/src/samples/stream.c b/pjsip-apps/src/samples/stream.c new file mode 100644 index 00000000..8503c1b8 --- /dev/null +++ b/pjsip-apps/src/samples/stream.c @@ -0,0 +1,681 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +static const char *desc = + " stream.c \n" + " \n" + " PURPOSE: \n" + " Demonstrate how to use pjmedia stream component to transmit/receive \n" + " RTP packets to/from sound device. \n" + "\n" + "\n" + " USAGE: \n" + " stream [options] \n" + "\n" + "\n" + " Options:\n" + " --codec=CODEC Set the codec name. Valid codec names are: \n" + " pcma, pcmu, gsm, speexnb, speexwb, speexuwb. \n" + " (default: pcma). \n" + " --local-port=PORT Set local RTP port (default=4000) \n" + " --remote=IP:PORT Set the remote peer. If this option is set, \n" + " the program will transmit RTP audio to the \n" + " specified address. (default: recv only) \n" + " --play-file=WAV Send audio from the WAV file instead of from \n" + " the sound device. \n" +// " --record-file=WAV Record incoming audio to WAV file instead of \n" +// " playing it to sound device. \n" + " --send-recv Set stream direction to bidirectional. \n" + " --send-only Set stream direction to send only \n" + " --recv-only Set stream direction to recv only (default) \n" + "\n" +; + + + +#include <pjlib.h> +#include <pjlib-util.h> +#include <pjmedia.h> +#include <pjmedia-codec.h> + +#include <stdlib.h> /* atoi() */ +#include <stdio.h> + +#include "util.h" + + +#define THIS_FILE "stream.c" + + +struct codec +{ + char *name; + char *encoding_name; + int pt; + int clock_rate; +} codec[] = +{ + { "pcma", "pcma", PJMEDIA_RTP_PT_PCMA, 8000 }, + { "pcmu", "pcmu", PJMEDIA_RTP_PT_PCMU, 8000 }, + { "gsm", "gsm", PJMEDIA_RTP_PT_GSM, 8000 }, + { "speexnb", "speex", 120, 8000 }, + { "speexwb", "speex", 121, 16000 }, + { "speexuwb", "speex", 122, 32000 }, +}; + + +/* Prototype */ +static void print_stream_stat(pjmedia_stream *stream); + + +/* + * Register all codecs. + */ +static pj_status_t init_codecs(pjmedia_endpt *med_endpt) +{ + pj_status_t status; + + status = pjmedia_codec_g711_init(med_endpt); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjmedia_codec_gsm_init(med_endpt); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + status = pjmedia_codec_speex_init(med_endpt, 0, -1, -1); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + return PJ_SUCCESS; +} + + +/* + * Create stream based on the codec, dir, remote address, etc. + */ +static pj_status_t create_stream( pj_pool_t *pool, + pjmedia_endpt *med_endpt, + unsigned codec_index, + pjmedia_dir dir, + pj_uint16_t local_port, + const pj_sockaddr_in *rem_addr, + pjmedia_stream **p_stream ) +{ + pjmedia_stream_info info; + pj_status_t status; + + + /* Reset stream info. */ + pj_memset(&info, 0, sizeof(info)); + + + /* Initialize stream info formats */ + info.type = PJMEDIA_TYPE_AUDIO; + info.dir = dir; + info.fmt.encoding_name = pj_str(codec[codec_index].encoding_name); + info.fmt.type = PJMEDIA_TYPE_AUDIO; + info.fmt.sample_rate = codec[codec_index].clock_rate; + info.fmt.pt = codec[codec_index].pt; + info.tx_pt = codec[codec_index].pt; + info.ssrc = pj_rand(); + + + /* Copy remote address */ + pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in)); + + + /* Create RTP socket */ + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, + &info.sock_info.rtp_sock); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + + /* Bind RTP socket to local port */ + info.sock_info.rtp_addr_name.sin_family = PJ_AF_INET; + info.sock_info.rtp_addr_name.sin_port = pj_htons(local_port); + + status = pj_sock_bind(info.sock_info.rtp_sock, + &info.sock_info.rtp_addr_name, + sizeof(pj_sockaddr_in)); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to bind RTP socket", status); + pj_sock_close(info.sock_info.rtp_sock); + return status; + } + + + /* Create RTCP socket */ + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, + &info.sock_info.rtcp_sock); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); + + + /* Bind RTP socket to local port + 1 */ + ++local_port; + info.sock_info.rtcp_addr_name.sin_family = PJ_AF_INET; + info.sock_info.rtcp_addr_name.sin_port = pj_htons(local_port); + + status = pj_sock_bind(info.sock_info.rtcp_sock, + &info.sock_info.rtcp_addr_name, + sizeof(pj_sockaddr_in)); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to bind RTCP socket", status); + pj_sock_close(info.sock_info.rtp_sock); + pj_sock_close(info.sock_info.rtcp_sock); + return status; + } + + + /* Now that the stream info is initialized, we can create the + * stream. + */ + + status = pjmedia_stream_create( med_endpt, pool, &info, NULL, p_stream); + + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Error creating stream", status); + pj_sock_close(info.sock_info.rtp_sock); + pj_sock_close(info.sock_info.rtcp_sock); + return status; + } + + + return PJ_SUCCESS; +} + + +/* + * main() + */ +int main(int argc, char *argv[]) +{ + pj_caching_pool cp; + pjmedia_endpt *med_endpt; + pj_pool_t *pool; + pjmedia_port *rec_file_port = NULL, *play_file_port = NULL; + pjmedia_master_port *master_port = NULL; + pjmedia_snd_port *snd_port = NULL; + pjmedia_stream *stream = NULL; + pjmedia_port *stream_port; + char tmp[10]; + pj_status_t status; + + + /* Default values */ + int codec_index = 0; + pjmedia_dir dir = PJMEDIA_DIR_DECODING; + pj_sockaddr_in remote_addr; + pj_uint16_t local_port = 4000; + char *rec_file = NULL; + char *play_file = NULL; + + enum { + OPT_CODEC = 'c', + OPT_LOCAL_PORT = 'p', + OPT_REMOTE = 'r', + OPT_PLAY_FILE = 'w', + OPT_RECORD_FILE = 'R', + OPT_SEND_RECV = 'b', + OPT_SEND_ONLY = 's', + OPT_RECV_ONLY = 'i', + }; + + struct pj_getopt_option long_options[] = { + { "codec", 1, 0, OPT_CODEC }, + { "local-port", 1, 0, OPT_LOCAL_PORT }, + { "remote", 1, 0, OPT_REMOTE }, + { "play-file", 1, 0, OPT_PLAY_FILE }, + { "record-file", 1, 0, OPT_RECORD_FILE }, + { "send-recv", 0, 0, OPT_SEND_RECV }, + { "send-only", 0, 0, OPT_SEND_ONLY }, + { "recv-only", 0, 0, OPT_RECV_ONLY }, + { NULL, 0, 0, 0 }, + }; + + int c; + int option_index; + + + pj_memset(&remote_addr, 0, sizeof(remote_addr)); + + + /* init PJLIB : */ + status = pj_init(); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + + /* Parse arguments */ + pj_optind = 0; + while((c=pj_getopt_long(argc,argv, "", long_options, &option_index))!=-1) { + + switch (c) { + case OPT_CODEC: + { + unsigned i; + for (i=0; i<PJ_ARRAY_SIZE(codec); ++i) { + if (pj_ansi_stricmp(pj_optarg, codec[i].name)==0) { + break; + } + } + + if (i == PJ_ARRAY_SIZE(codec)) { + printf("Error: unknown codec %s\n", pj_optarg); + return 1; + } + + codec_index = i; + } + break; + + case OPT_LOCAL_PORT: + local_port = (pj_uint16_t) atoi(pj_optarg); + if (local_port < 1) { + printf("Error: invalid local port %s\n", pj_optarg); + return 1; + } + break; + + case OPT_REMOTE: + { + pj_str_t ip = pj_str(strtok(pj_optarg, ":")); + pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":")); + + status = pj_sockaddr_in_init(&remote_addr, &ip, port); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Invalid remote address", status); + return 1; + } + } + break; + + case OPT_PLAY_FILE: + play_file = pj_optarg; + break; + + case OPT_RECORD_FILE: + rec_file = pj_optarg; + break; + + case OPT_SEND_RECV: + dir = PJMEDIA_DIR_ENCODING_DECODING; + break; + + case OPT_SEND_ONLY: + dir = PJMEDIA_DIR_ENCODING; + break; + + case OPT_RECV_ONLY: + dir = PJMEDIA_DIR_DECODING; + break; + + default: + printf("Invalid options %s\n", argv[pj_optind]); + return 1; + } + + } + + + /* Verify arguments. */ + if (dir & PJMEDIA_DIR_ENCODING) { + if (remote_addr.sin_addr.s_addr == 0) { + printf("Error: remote address must be set\n"); + return 1; + } + } + + if (play_file != NULL && dir != PJMEDIA_DIR_ENCODING) { + printf("Direction is set to --send-only because of --play-file\n"); + dir = PJMEDIA_DIR_ENCODING; + } + + + /* Must create a pool factory before we can allocate any memory. */ + pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); + + /* + * Initialize media endpoint. + * This will implicitly initialize PJMEDIA too. + */ + status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + /* Create memory pool for application purpose */ + pool = pj_pool_create( &cp.factory, /* pool factory */ + "app", /* pool name. */ + 4000, /* init size */ + 4000, /* increment size */ + NULL /* callback on error */ + ); + + + /* Register all supported codecs */ + status = init_codecs(med_endpt); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + + /* Create stream based on program arguments */ + status = create_stream(pool, med_endpt, codec_index, dir, local_port, + &remote_addr, &stream); + if (status != PJ_SUCCESS) + goto on_exit; + + + /* Get the port interface of the stream */ + status = pjmedia_stream_get_port( stream, &stream_port); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + + if (play_file) { + + status = pjmedia_file_player_port_create(pool, play_file, 0, + -1, NULL, &play_file_port); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to use file", status); + goto on_exit; + } + + status = pjmedia_master_port_create(pool, play_file_port, stream_port, + 0, &master_port); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to create master port", status); + goto on_exit; + } + + status = pjmedia_master_port_start(master_port); + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Error starting master port", status); + goto on_exit; + } + + + } else { + + /* Create sound device port. */ + if (dir == PJMEDIA_DIR_ENCODING_DECODING) + status = pjmedia_snd_port_create(pool, -1, -1, + stream_port->info.sample_rate, + stream_port->info.channel_count, + stream_port->info.samples_per_frame, + stream_port->info.bits_per_sample, + 0, &snd_port); + else if (dir == PJMEDIA_DIR_ENCODING) + status = pjmedia_snd_port_create_rec(pool, -1, + stream_port->info.sample_rate, + stream_port->info.channel_count, + stream_port->info.samples_per_frame, + stream_port->info.bits_per_sample, + 0, &snd_port); + else + status = pjmedia_snd_port_create_player(pool, -1, + stream_port->info.sample_rate, + stream_port->info.channel_count, + stream_port->info.samples_per_frame, + stream_port->info.bits_per_sample, + 0, &snd_port); + + + if (status != PJ_SUCCESS) { + app_perror(THIS_FILE, "Unable to create sound port", status); + goto on_exit; + } + + /* Connect sound port to stream */ + status = pjmedia_snd_port_connect( snd_port, stream_port ); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + } + + + /* Done */ + + if (dir == PJMEDIA_DIR_DECODING) + printf("Stream is active, dir is recv-only, local port is %d\n", + local_port); + else if (dir == PJMEDIA_DIR_ENCODING) + printf("Stream is active, dir is send-only, sending to %s:%d\n", + pj_inet_ntoa(remote_addr.sin_addr), + pj_ntohs(remote_addr.sin_port)); + else + printf("Stream is active, send/recv, local port is %d, " + "sending to %s:%d\n", + local_port, + pj_inet_ntoa(remote_addr.sin_addr), + pj_ntohs(remote_addr.sin_port)); + + + for (;;) { + + puts(""); + puts("Commands:"); + puts(" s Display media statistics"); + puts(" q Quit"); + puts(""); + + printf("Command: "); fflush(stdout); + + fgets(tmp, sizeof(tmp), stdin); + + if (tmp[0] == 's') + print_stream_stat(stream); + else if (tmp[0] == 'q') + break; + + } + + + + /* Start deinitialization: */ +on_exit: + + /* Destroy sound device */ + if (snd_port) { + pjmedia_snd_port_destroy( snd_port ); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + } + + /* If there is master port, then we just need to destroy master port + * (it will recursively destroy upstream and downstream ports, which + * in this case are file_port and stream_port). + */ + if (master_port) { + pjmedia_master_port_destroy(master_port); + play_file_port = NULL; + stream = NULL; + } + + /* Destroy stream */ + if (stream) { + pjmedia_stream_destroy(stream); + } + + /* Destroy file ports */ + if (play_file_port) + pjmedia_port_destroy( play_file_port ); + if (rec_file_port) + pjmedia_port_destroy( rec_file_port ); + + + /* Release application pool */ + pj_pool_release( pool ); + + /* Destroy media endpoint. */ + pjmedia_endpt_destroy( med_endpt ); + + /* Destroy pool factory */ + pj_caching_pool_destroy( &cp ); + + + return (status == PJ_SUCCESS) ? 0 : 1; +} + + + + +static const char *good_number(char *buf, pj_int32_t val) +{ + if (val < 1000) { + pj_ansi_sprintf(buf, "%d", val); + } else if (val < 1000000) { + pj_ansi_sprintf(buf, "%d.%dK", + val / 1000, + (val % 1000) / 100); + } else { + pj_ansi_sprintf(buf, "%d.%02dM", + val / 1000000, + (val % 1000000) / 10000); + } + + return buf; +} + + + +/* + * Print stream statistics + */ +static void print_stream_stat(pjmedia_stream *stream) +{ + char duration[80], last_update[80]; + char bps[16], ipbps[16], packets[16], bytes[16], ipbytes[16]; + pjmedia_port *port; + pjmedia_rtcp_stat stat; + pj_time_val now; + + + pj_gettimeofday(&now); + pjmedia_stream_get_stat(stream, &stat); + pjmedia_stream_get_port(stream, &port); + + puts("Stream statistics:"); + + /* Print duration */ + PJ_TIME_VAL_SUB(now, stat.start); + sprintf(duration, " Duration: %02ld:%02ld:%02ld.%03ld", + now.sec / 3600, + (now.sec % 3600) / 60, + (now.sec % 60), + now.msec); + + + printf(" Info: audio %.*s@%dHz, %dms/frame, %sB/s (%sB/s +IP hdr)\n", + (int)port->info.encoding_name.slen, + port->info.encoding_name.ptr, + port->info.sample_rate, + port->info.samples_per_frame * 1000 / port->info.sample_rate, + good_number(bps, port->info.bytes_per_frame * port->info.sample_rate / + port->info.sample_rate), + good_number(ipbps, (port->info.bytes_per_frame+32) * + port->info.sample_rate / port->info.sample_rate)); + + if (stat.rx.update_cnt == 0) + strcpy(last_update, "never"); + else { + pj_gettimeofday(&now); + PJ_TIME_VAL_SUB(now, stat.rx.update); + sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", + now.sec / 3600, + (now.sec % 3600) / 60, + now.sec % 60, + now.msec); + } + + printf(" RX stat last update: %s\n" + " total %s packets %sB received (%sB +IP hdr)%s\n" + " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n" + " (msec) min avg max last\n" + " loss period: %7.3f %7.3f %7.3f %7.3f%s\n" + " jitter : %7.3f %7.3f %7.3f %7.3f%s\n", + last_update, + good_number(packets, stat.rx.pkt), + good_number(bytes, stat.rx.bytes), + good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32), + "", + stat.rx.loss, + stat.rx.loss * 100.0 / stat.rx.pkt, + stat.rx.dup, + stat.rx.dup * 100.0 / stat.rx.pkt, + stat.rx.reorder, + stat.rx.reorder * 100.0 / stat.rx.pkt, + "", + stat.rx.loss_period.min / 1000.0, + stat.rx.loss_period.avg / 1000.0, + stat.rx.loss_period.max / 1000.0, + stat.rx.loss_period.last / 1000.0, + "", + stat.rx.jitter.min / 1000.0, + stat.rx.jitter.avg / 1000.0, + stat.rx.jitter.max / 1000.0, + stat.rx.jitter.last / 1000.0, + "" + ); + + + if (stat.tx.update_cnt == 0) + strcpy(last_update, "never"); + else { + pj_gettimeofday(&now); + PJ_TIME_VAL_SUB(now, stat.tx.update); + sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", + now.sec / 3600, + (now.sec % 3600) / 60, + now.sec % 60, + now.msec); + } + + printf(" TX stat last update: %s\n" + " total %s packets %sB sent (%sB +IP hdr)%s\n" + " pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)%s\n" + " (msec) min avg max last\n" + " loss period: %7.3f %7.3f %7.3f %7.3f%s\n" + " jitter : %7.3f %7.3f %7.3f %7.3f%s\n", + last_update, + good_number(packets, stat.tx.pkt), + good_number(bytes, stat.tx.bytes), + good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32), + "", + stat.tx.loss, + stat.tx.loss * 100.0 / stat.tx.pkt, + stat.tx.dup, + stat.tx.dup * 100.0 / stat.tx.pkt, + stat.tx.reorder, + stat.tx.reorder * 100.0 / stat.tx.pkt, + "", + stat.tx.loss_period.min / 1000.0, + stat.tx.loss_period.avg / 1000.0, + stat.tx.loss_period.max / 1000.0, + stat.tx.loss_period.last / 1000.0, + "", + stat.tx.jitter.min / 1000.0, + stat.tx.jitter.avg / 1000.0, + stat.tx.jitter.max / 1000.0, + stat.tx.jitter.last / 1000.0, + "" + ); + + + printf(" RTT delay : %7.3f %7.3f %7.3f %7.3f%s\n", + stat.rtt.min / 1000.0, + stat.rtt.avg / 1000.0, + stat.rtt.max / 1000.0, + stat.rtt.last / 1000.0, + "" + ); + +} + diff --git a/pjsip-apps/src/samples/util.h b/pjsip-apps/src/samples/util.h index 2e2a32c8..02ff7024 100644 --- a/pjsip-apps/src/samples/util.h +++ b/pjsip-apps/src/samples/util.h @@ -36,14 +36,14 @@ static int app_perror( const char *sender, const char *title, * This utility function parses the command line and look for * common sound options. */ -static pj_status_t get_snd_options( const char *app_name, - int argc, - char *argv[], - int *dev_id, - int *clock_rate, - int *channel_count, - int *samples_per_frame, - int *bits_per_sample) +pj_status_t get_snd_options(const char *app_name, + int argc, + char *argv[], + int *dev_id, + int *clock_rate, + int *channel_count, + int *samples_per_frame, + int *bits_per_sample) { struct pj_getopt_option long_options[] = { { "dev", 1, 0, 'd' }, @@ -133,7 +133,7 @@ static pj_status_t get_snd_options( const char *app_name, /* Dump memory pool usage. */ -static void dump_pool_usage( const char *app_name, pj_caching_pool *cp ) +void dump_pool_usage( const char *app_name, pj_caching_pool *cp ) { #if !defined(PJ_HAS_POOL_ALT_API) || PJ_HAS_POOL_ALT_API==0 pj_pool_t *p; |