blob: 7770430690e4531f2f765e30414ae80c8ebebe22 [file] [log] [blame]
Benny Prijonoe3ebd552009-02-18 20:14:15 +00001/* $Id$ */
2/*
3 * Copyright (C) 2008-2009 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#include <pjmedia-audiodev/audiotest.h>
21#include <pjmedia-audiodev/audiodev.h>
22#include <pjlib.h>
23#include <pjlib-util.h>
24
25#define THIS_FILE "audiotest.c"
26
27/* Test duration in msec */
28#define DURATION 10000
29
30/* Skip the first msec from the calculation */
31#define SKIP_DURATION 1000
32
Nanang Izzuddinfa923822009-03-11 11:28:44 +000033/* Division helper */
34#define DIV_ROUND_UP(a,b) (((a) + ((b) - 1)) / (b))
35#define DIV_ROUND(a,b) (((a) + ((b)/2 - 1)) / (b))
Benny Prijonoe3ebd552009-02-18 20:14:15 +000036
37struct stream_data
38{
39 pj_uint32_t first_timestamp;
40 pj_uint32_t last_timestamp;
41 pj_timestamp last_called;
Nanang Izzuddinfa923822009-03-11 11:28:44 +000042 pj_math_stat delay;
Benny Prijonoe3ebd552009-02-18 20:14:15 +000043};
44
45struct test_data
46{
47 pj_pool_t *pool;
Nanang Izzuddinfa923822009-03-11 11:28:44 +000048 const pjmedia_aud_param *param;
Benny Prijonoe3ebd552009-02-18 20:14:15 +000049 pjmedia_aud_test_results *result;
50 pj_bool_t running;
51 pj_bool_t has_error;
52 pj_mutex_t *mutex;
53
54 struct stream_data capture_data;
55 struct stream_data playback_data;
56};
57
58static pj_status_t play_cb(void *user_data, pjmedia_frame *frame)
59{
60 struct test_data *test_data = (struct test_data *)user_data;
61 struct stream_data *strm_data = &test_data->playback_data;
62
63 pj_mutex_lock(test_data->mutex);
64
65 /* Skip frames when test is not started or test has finished */
66 if (!test_data->running) {
67 pj_bzero(frame->buf, frame->size);
68 pj_mutex_unlock(test_data->mutex);
69 return PJ_SUCCESS;
70 }
71
72 /* Save last timestamp seen (to calculate drift) */
73 strm_data->last_timestamp = frame->timestamp.u32.lo;
74
75 if (strm_data->last_called.u64 == 0) {
Nanang Izzuddinfa923822009-03-11 11:28:44 +000076 /* Init vars. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +000077 pj_get_timestamp(&strm_data->last_called);
Nanang Izzuddinfa923822009-03-11 11:28:44 +000078 pj_math_stat_init(&strm_data->delay);
Benny Prijonoe3ebd552009-02-18 20:14:15 +000079 strm_data->first_timestamp = frame->timestamp.u32.lo;
Nanang Izzuddinfa923822009-03-11 11:28:44 +000080 } else {
Benny Prijonoe3ebd552009-02-18 20:14:15 +000081 pj_timestamp now;
82 unsigned delay;
83
Benny Prijonoe3ebd552009-02-18 20:14:15 +000084 /* Calculate frame interval */
Nanang Izzuddinfa923822009-03-11 11:28:44 +000085 pj_get_timestamp(&now);
Benny Prijonoe3ebd552009-02-18 20:14:15 +000086 delay = pj_elapsed_usec(&strm_data->last_called, &now);
Benny Prijonoe3ebd552009-02-18 20:14:15 +000087 strm_data->last_called = now;
88
Nanang Izzuddinfa923822009-03-11 11:28:44 +000089 /* Update frame interval statistic */
90 pj_math_stat_update(&strm_data->delay, delay);
Benny Prijonoe3ebd552009-02-18 20:14:15 +000091 }
92
93 pj_bzero(frame->buf, frame->size);
94
95 pj_mutex_unlock(test_data->mutex);
96
97 return PJ_SUCCESS;
98}
99
100static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
101{
102 struct test_data *test_data = (struct test_data*)user_data;
103 struct stream_data *strm_data = &test_data->capture_data;
104
105 pj_mutex_lock(test_data->mutex);
106
107 /* Skip frames when test is not started or test has finished */
108 if (!test_data->running) {
109 pj_mutex_unlock(test_data->mutex);
110 return PJ_SUCCESS;
111 }
112
113 /* Save last timestamp seen (to calculate drift) */
114 strm_data->last_timestamp = frame->timestamp.u32.lo;
115
116 if (strm_data->last_called.u64 == 0) {
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000117 /* Init vars. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000118 pj_get_timestamp(&strm_data->last_called);
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000119 pj_math_stat_init(&strm_data->delay);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000120 strm_data->first_timestamp = frame->timestamp.u32.lo;
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000121 } else {
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000122 pj_timestamp now;
123 unsigned delay;
124
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000125 /* Calculate frame interval */
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000126 pj_get_timestamp(&now);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000127 delay = pj_elapsed_usec(&strm_data->last_called, &now);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000128 strm_data->last_called = now;
129
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000130 /* Update frame interval statistic */
131 pj_math_stat_update(&strm_data->delay, delay);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000132 }
133
134 pj_mutex_unlock(test_data->mutex);
135 return PJ_SUCCESS;
136}
137
138static void app_perror(const char *title, pj_status_t status)
139{
140 char errmsg[PJ_ERR_MSG_SIZE];
141
142 pj_strerror(status, errmsg, sizeof(errmsg));
143 printf( "%s: %s (err=%d)\n",
144 title, errmsg, status);
145}
146
147
Benny Prijono10454dc2009-02-21 14:21:59 +0000148PJ_DEF(pj_status_t) pjmedia_aud_test( const pjmedia_aud_param *param,
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000149 pjmedia_aud_test_results *result)
150{
151 pj_status_t status = PJ_SUCCESS;
152 pjmedia_aud_stream *strm;
153 struct test_data test_data;
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000154 unsigned ptime, tmp;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000155
156 /*
157 * Init test parameters
158 */
159 pj_bzero(&test_data, sizeof(test_data));
160 test_data.param = param;
161 test_data.result = result;
162
163 test_data.pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(),
164 "audtest", 1000, 1000, NULL);
165 pj_mutex_create_simple(test_data.pool, "sndtest", &test_data.mutex);
166
167 /*
168 * Open device.
169 */
170 status = pjmedia_aud_stream_create(test_data.param, &rec_cb, &play_cb,
171 &test_data, &strm);
172 if (status != PJ_SUCCESS) {
173 app_perror("Unable to open device", status);
174 pj_pool_release(test_data.pool);
175 return status;
176 }
177
178
179 /* Sleep for a while to let sound device "settles" */
180 pj_thread_sleep(200);
181
182 /*
183 * Start the stream.
184 */
185 status = pjmedia_aud_stream_start(strm);
186 if (status != PJ_SUCCESS) {
187 app_perror("Unable to start capture stream", status);
188 pjmedia_aud_stream_destroy(strm);
189 pj_pool_release(test_data.pool);
190 return status;
191 }
192
193 PJ_LOG(3,(THIS_FILE,
194 " Please wait while test is in progress (~%d secs)..",
195 (DURATION+SKIP_DURATION)/1000));
196
197 /* Let the stream runs for few msec/sec to get stable result.
198 * (capture normally begins with frames available simultaneously).
199 */
200 pj_thread_sleep(SKIP_DURATION);
201
202
203 /* Begin gather data */
204 test_data.running = 1;
205
206 /*
207 * Let the test runs for a while.
208 */
209 pj_thread_sleep(DURATION);
210
211
212 /*
213 * Close stream.
214 */
215 test_data.running = 0;
216 pjmedia_aud_stream_destroy(strm);
217 pj_pool_release(test_data.pool);
218
219
220 /*
221 * Gather results
222 */
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000223 ptime = param->samples_per_frame * 1000 / param->clock_rate;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000224
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000225 tmp = pj_math_stat_get_stddev(&test_data.capture_data.delay);
226 result->rec.frame_cnt = test_data.capture_data.delay.n;
227 result->rec.min_interval = DIV_ROUND(test_data.capture_data.delay.min, 1000);
228 result->rec.max_interval = DIV_ROUND(test_data.capture_data.delay.max, 1000);
229 result->rec.avg_interval = DIV_ROUND(test_data.capture_data.delay.mean, 1000);
230 result->rec.dev_interval = DIV_ROUND(tmp, 1000);
231 result->rec.max_burst = DIV_ROUND_UP(result->rec.max_interval, ptime);
232
233 tmp = pj_math_stat_get_stddev(&test_data.playback_data.delay);
234 result->play.frame_cnt = test_data.playback_data.delay.n;
235 result->play.min_interval = DIV_ROUND(test_data.playback_data.delay.min, 1000);
236 result->play.max_interval = DIV_ROUND(test_data.playback_data.delay.max, 1000);
Nanang Izzuddin21286f22009-03-27 15:15:46 +0000237 result->play.avg_interval = DIV_ROUND(test_data.playback_data.delay.mean, 1000);
Nanang Izzuddinfa923822009-03-11 11:28:44 +0000238 result->play.dev_interval = DIV_ROUND(tmp, 1000);
239 result->play.max_burst = DIV_ROUND_UP(result->play.max_interval, ptime);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000240
241 /* Check drifting */
242 if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
Nanang Izzuddin21286f22009-03-27 15:15:46 +0000243 int play_diff, cap_diff, drift;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000244
Nanang Izzuddin21286f22009-03-27 15:15:46 +0000245 play_diff = test_data.playback_data.last_timestamp -
246 test_data.playback_data.first_timestamp;
247 cap_diff = test_data.capture_data.last_timestamp -
248 test_data.capture_data.first_timestamp;
249 drift = play_diff > cap_diff? play_diff - cap_diff :
250 cap_diff - play_diff;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000251
252 /* Allow one frame tolerance for clock drift detection */
253 if (drift < (int)param->samples_per_frame) {
254 result->rec_drift_per_sec = 0;
255 } else {
256 unsigned msec_dur;
257
258 msec_dur = (test_data.capture_data.last_timestamp -
259 test_data.capture_data.first_timestamp) * 1000 /
260 test_data.param->clock_rate;
261
262 result->rec_drift_per_sec = drift * 1000 / msec_dur;
263
264 }
265 }
266
267 return test_data.has_error? PJ_EUNKNOWN : PJ_SUCCESS;
268}
269