1 | /* $Id:$ */
|
---|
2 | /*
|
---|
3 | * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
|
---|
4 | *
|
---|
5 | * This program is free software; you can redistribute it and/or modify
|
---|
6 | * it under the terms of the GNU General Public License as published by
|
---|
7 | * the Free Software Foundation; either version 2 of the License, or
|
---|
8 | * (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This program is distributed in the hope that it will be useful,
|
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
13 | * GNU General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU General Public License
|
---|
16 | * along with this program; if not, write to the Free Software
|
---|
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
---|
18 | */
|
---|
19 |
|
---|
20 |
|
---|
21 | #include <pjmedia.h>
|
---|
22 | #include <pjlib.h>
|
---|
23 |
|
---|
24 | #include <stdio.h>
|
---|
25 |
|
---|
26 | #define THIS_FILE "lacency.c"
|
---|
27 |
|
---|
28 |
|
---|
29 | /* Util to display the error message for the specified error code */
|
---|
30 | static int app_perror( const char *sender, const char *title,
|
---|
31 | pj_status_t status)
|
---|
32 | {
|
---|
33 | char errmsg[PJ_ERR_MSG_SIZE];
|
---|
34 |
|
---|
35 | PJ_UNUSED_ARG(sender);
|
---|
36 |
|
---|
37 | pj_strerror(status, errmsg, sizeof(errmsg));
|
---|
38 |
|
---|
39 | printf("%s: %s [code=%d]\n", title, errmsg, status);
|
---|
40 | return 1;
|
---|
41 | }
|
---|
42 |
|
---|
43 | /*
|
---|
44 | * Find out latency
|
---|
45 | */
|
---|
46 | static int calculate_latency(pj_pool_t *pool, pjmedia_port *wav)
|
---|
47 | {
|
---|
48 | pjmedia_frame frm;
|
---|
49 | short *buf;
|
---|
50 | unsigned i, samples_per_frame, read, len;
|
---|
51 | unsigned start_pos;
|
---|
52 | pj_status_t status;
|
---|
53 |
|
---|
54 | unsigned lat_sum = 0,
|
---|
55 | lat_cnt = 0,
|
---|
56 | lat_min = 10000,
|
---|
57 | lat_max = 0;
|
---|
58 |
|
---|
59 | samples_per_frame = wav->info.samples_per_frame;
|
---|
60 | frm.buf = pj_pool_alloc(pool, samples_per_frame * 2);
|
---|
61 | frm.size = samples_per_frame * 2;
|
---|
62 | len = pjmedia_wav_player_get_len(wav);
|
---|
63 | buf = pj_pool_alloc(pool, len + samples_per_frame);
|
---|
64 |
|
---|
65 | read = 0;
|
---|
66 | while (read < len/2) {
|
---|
67 | status = pjmedia_port_get_frame(wav, &frm);
|
---|
68 | if (status != PJ_SUCCESS)
|
---|
69 | break;
|
---|
70 |
|
---|
71 | pjmedia_copy_samples(buf+read, (short*)frm.buf, samples_per_frame);
|
---|
72 | read += samples_per_frame;
|
---|
73 | }
|
---|
74 |
|
---|
75 | if (read < 2 * wav->info.clock_rate) {
|
---|
76 | puts("Error: too short");
|
---|
77 | return -1;
|
---|
78 | }
|
---|
79 |
|
---|
80 | start_pos = 0;
|
---|
81 | while (start_pos < len/2 - wav->info.clock_rate) {
|
---|
82 | int max_signal = 0;
|
---|
83 | unsigned max_signal_pos = start_pos;
|
---|
84 | unsigned max_echo_pos = 0;
|
---|
85 | unsigned pos;
|
---|
86 | unsigned lat;
|
---|
87 |
|
---|
88 | /* Get the largest signal in the next 0.7s */
|
---|
89 | for (i=start_pos; i<start_pos + wav->info.clock_rate * 700 / 1000; ++i) {
|
---|
90 | if (abs(buf[i]) > max_signal) {
|
---|
91 | max_signal = abs(buf[i]);
|
---|
92 | max_signal_pos = i;
|
---|
93 | }
|
---|
94 | }
|
---|
95 |
|
---|
96 | /* Advance 10ms from max_signal_pos */
|
---|
97 | pos = max_signal_pos + 10 * wav->info.clock_rate / 1000;
|
---|
98 |
|
---|
99 | /* Get the largest signal in the next 500ms */
|
---|
100 | max_signal = 0;
|
---|
101 | max_echo_pos = pos;
|
---|
102 | for (i=pos; i<pos+wav->info.clock_rate/2; ++i) {
|
---|
103 | if (abs(buf[i]) > max_signal) {
|
---|
104 | max_signal = abs(buf[i]);
|
---|
105 | max_echo_pos = i;
|
---|
106 | }
|
---|
107 | }
|
---|
108 |
|
---|
109 | lat = (max_echo_pos - max_signal_pos) * 1000 / wav->info.clock_rate;
|
---|
110 |
|
---|
111 | #if 0
|
---|
112 | printf("Latency = %u\n", lat);
|
---|
113 | #endif
|
---|
114 |
|
---|
115 | lat_sum += lat;
|
---|
116 | lat_cnt++;
|
---|
117 | if (lat < lat_min)
|
---|
118 | lat_min = lat;
|
---|
119 | if (lat > lat_max)
|
---|
120 | lat_max = lat;
|
---|
121 |
|
---|
122 | /* Advance next loop */
|
---|
123 | start_pos += wav->info.clock_rate;
|
---|
124 | }
|
---|
125 |
|
---|
126 | printf("Latency average = %u\n", lat_sum / lat_cnt);
|
---|
127 | printf("Latency minimum = %u\n", lat_min);
|
---|
128 | printf("Latency maximum = %u\n", lat_max);
|
---|
129 | printf("Number of data = %u\n", lat_cnt);
|
---|
130 | return 0;
|
---|
131 | }
|
---|
132 |
|
---|
133 | /*
|
---|
134 | To calculate sound latency:
|
---|
135 | pjsua_vc6 --ec-tail 0 --rec-file rec.wav --play-file tock.wav
|
---|
136 |
|
---|
137 | cc 0 0
|
---|
138 | cc 1 0
|
---|
139 | cc 1 2
|
---|
140 | cc 0 2
|
---|
141 | sleep 30000
|
---|
142 | cd 0 0
|
---|
143 | cd 1 0
|
---|
144 | cd 1 2
|
---|
145 | cd 0 2
|
---|
146 | q
|
---|
147 |
|
---|
148 |
|
---|
149 | To calculate network latency:
|
---|
150 | pjsua_vc6 --ec-tail 0 --rec-file rec.wav --play-file tock.wav --null-audio
|
---|
151 |
|
---|
152 | m
|
---|
153 | sip:192.168.0.2:5090
|
---|
154 | sleep 200
|
---|
155 | cd 0 4
|
---|
156 | cd 4 0
|
---|
157 | cc 1 2
|
---|
158 | cc 4 2
|
---|
159 | cc 1 4
|
---|
160 | sleep 30000
|
---|
161 | cd 1 2
|
---|
162 | cd 4 2
|
---|
163 | cd 1 4
|
---|
164 | h
|
---|
165 | sleep 500
|
---|
166 | q
|
---|
167 |
|
---|
168 |
|
---|
169 | */
|
---|
170 |
|
---|
171 | /*
|
---|
172 | * main()
|
---|
173 | */
|
---|
174 | int main(int argc, char *argv[])
|
---|
175 | {
|
---|
176 | enum { NSAMPLES = 160, COUNT=100 };
|
---|
177 | pj_caching_pool cp;
|
---|
178 | pj_pool_t *pool;
|
---|
179 | pjmedia_port *wav;
|
---|
180 | pj_status_t status;
|
---|
181 |
|
---|
182 |
|
---|
183 | /* Verify cmd line arguments. */
|
---|
184 | if (argc != 2) {
|
---|
185 | puts("Error: missing argument(s)");
|
---|
186 | puts("Usage: latency REV.WAV");
|
---|
187 | return 1;
|
---|
188 | }
|
---|
189 |
|
---|
190 | pj_log_set_level(0);
|
---|
191 |
|
---|
192 | status = pj_init();
|
---|
193 | PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
|
---|
194 |
|
---|
195 | pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
|
---|
196 |
|
---|
197 | pool = pj_pool_create( &cp.factory, /* pool factory */
|
---|
198 | "wav", /* pool name. */
|
---|
199 | 4000, /* init size */
|
---|
200 | 4000, /* increment size */
|
---|
201 | NULL /* callback on error */
|
---|
202 | );
|
---|
203 |
|
---|
204 | pj_register_strerror(PJMEDIA_ERRNO_START, PJ_ERRNO_SPACE_SIZE,
|
---|
205 | &pjmedia_strerror);
|
---|
206 |
|
---|
207 | /* Wav */
|
---|
208 | status = pjmedia_wav_player_port_create( pool, /* memory pool */
|
---|
209 | argv[1], /* file to play */
|
---|
210 | 0, /* use default ptime*/
|
---|
211 | 0, /* flags */
|
---|
212 | 0, /* default buffer */
|
---|
213 | &wav /* returned port */
|
---|
214 | );
|
---|
215 | if (status != PJ_SUCCESS) {
|
---|
216 | app_perror(THIS_FILE, argv[1], status);
|
---|
217 | return 1;
|
---|
218 | }
|
---|
219 |
|
---|
220 | status = calculate_latency(pool, wav);
|
---|
221 | if (status != PJ_SUCCESS)
|
---|
222 | return 1;
|
---|
223 |
|
---|
224 | status = pjmedia_port_destroy( wav );
|
---|
225 | PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
|
---|
226 |
|
---|
227 | pj_pool_release( pool );
|
---|
228 | pj_caching_pool_destroy( &cp );
|
---|
229 | pj_shutdown();
|
---|
230 |
|
---|
231 | /* Done. */
|
---|
232 | return 0;
|
---|
233 | }
|
---|
234 |
|
---|