blob: fdade1f6dbfde06a2bbdd92e8d8986dda6e42fcc [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id: latency.c 4537 2013-06-19 06:47:43Z riza $ */
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/* See http://trac.pjsip.org/repos/wiki/MeasuringSoundLatency on
22 * how to use this program.
23 */
24
25#include <pjmedia.h>
26#include <pjlib.h>
27
28#include <stdio.h>
29
30#define THIS_FILE "lacency.c"
31
32
33/* Util to display the error message for the specified error code */
34static int app_perror( const char *sender, const char *title,
35 pj_status_t status)
36{
37 char errmsg[PJ_ERR_MSG_SIZE];
38
39 PJ_UNUSED_ARG(sender);
40
41 pj_strerror(status, errmsg, sizeof(errmsg));
42
43 printf("%s: %s [code=%d]\n", title, errmsg, status);
44 return 1;
45}
46
47/*
48 * Find out latency
49 */
50static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav)
51{
52 pjmedia_frame frm;
53 short *buf;
54 unsigned i, samples_per_frame;
55 pj_size_t read, len;
56 unsigned start_pos;
57 pj_status_t status;
58
59 unsigned lat_sum = 0,
60 lat_cnt = 0,
61 lat_min = 10000,
62 lat_max = 0;
63
64 samples_per_frame = PJMEDIA_PIA_SPF(&wav->info);
65 frm.buf = pj_pool_alloc(pool, samples_per_frame * 2);
66 frm.size = samples_per_frame * 2;
67 len = pjmedia_wav_player_get_len(wav);
68 buf = pj_pool_alloc(pool, len + samples_per_frame);
69
70 read = 0;
71 while (read < len/2) {
72 status = pjmedia_port_get_frame(wav, &frm);
73 if (status != PJ_SUCCESS)
74 break;
75
76 pjmedia_copy_samples(buf+read, (short*)frm.buf, samples_per_frame);
77 read += samples_per_frame;
78 }
79
80 if (read < 2 * PJMEDIA_PIA_SRATE(&wav->info)) {
81 puts("Error: too short");
82 return -1;
83 }
84
85 start_pos = 0;
86 while (start_pos < len/2 - PJMEDIA_PIA_SRATE(&wav->info)) {
87 int max_signal = 0;
88 unsigned max_signal_pos = start_pos;
89 unsigned max_echo_pos = 0;
90 unsigned pos;
91 unsigned lat;
92
93 /* Get the largest signal in the next 0.7s */
94 for (i=start_pos; i<start_pos + PJMEDIA_PIA_SRATE(&wav->info) * 700 / 1000; ++i) {
95 if (abs(buf[i]) > max_signal) {
96 max_signal = abs(buf[i]);
97 max_signal_pos = i;
98 }
99 }
100
101 /* Advance 10ms from max_signal_pos */
102 pos = max_signal_pos + 10 * PJMEDIA_PIA_SRATE(&wav->info) / 1000;
103
104 /* Get the largest signal in the next 500ms */
105 max_signal = 0;
106 max_echo_pos = pos;
107 for (i=pos; i<pos+PJMEDIA_PIA_SRATE(&wav->info)/2; ++i) {
108 if (abs(buf[i]) > max_signal) {
109 max_signal = abs(buf[i]);
110 max_echo_pos = i;
111 }
112 }
113
114 lat = (max_echo_pos - max_signal_pos) * 1000 / PJMEDIA_PIA_SRATE(&wav->info);
115
116#if 0
117 printf("Latency = %u\n", lat);
118#endif
119
120 lat_sum += lat;
121 lat_cnt++;
122 if (lat < lat_min)
123 lat_min = lat;
124 if (lat > lat_max)
125 lat_max = lat;
126
127 /* Advance next loop */
128 start_pos += PJMEDIA_PIA_SRATE(&wav->info);
129 }
130
131 printf("Latency average = %u\n", lat_sum / lat_cnt);
132 printf("Latency minimum = %u\n", lat_min);
133 printf("Latency maximum = %u\n", lat_max);
134 printf("Number of data = %u\n", lat_cnt);
135 return 0;
136}
137
138
139/*
140 * main()
141 */
142int main(int argc, char *argv[])
143{
144 enum { NSAMPLES = 160, COUNT=100 };
145 pj_caching_pool cp;
146 pj_pool_t *pool;
147 pjmedia_port *wav;
148 pj_status_t status;
149
150
151 /* Verify cmd line arguments. */
152 if (argc != 2) {
153 puts("Error: missing argument(s)");
154 puts("Usage: latency REV.WAV");
155 return 1;
156 }
157
158 pj_log_set_level(0);
159
160 status = pj_init();
161 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
162
163 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
164
165 pool = pj_pool_create( &cp.factory, /* pool factory */
166 "wav", /* pool name. */
167 4000, /* init size */
168 4000, /* increment size */
169 NULL /* callback on error */
170 );
171
172 status = pj_register_strerror(PJMEDIA_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
173 &pjmedia_strerror);
174 pj_assert(status == PJ_SUCCESS);
175
176 /* Wav */
177 status = pjmedia_wav_player_port_create( pool, /* memory pool */
178 argv[1], /* file to play */
179 0, /* use default ptime*/
180 0, /* flags */
181 0, /* default buffer */
182 &wav /* returned port */
183 );
184 if (status != PJ_SUCCESS) {
185 app_perror(THIS_FILE, argv[1], status);
186 return 1;
187 }
188
189 status = calculate_latency(pool, wav);
190 if (status != PJ_SUCCESS)
191 return 1;
192
193 status = pjmedia_port_destroy( wav );
194 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
195
196 pj_pool_release( pool );
197 pj_caching_pool_destroy( &cp );
198 pj_shutdown();
199
200 /* Done. */
201 return 0;
202}
203