blob: 120f710c6ff7634abdd6e54839fa58f5b7568f96 [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
33/* Max frames per sec (to calculate number of delays to keep). */
34#define MAX_FRAMES_PER_SEC 100
35
36/* Number of frame durations to keep */
37#define MAX_DELAY_COUNTER (((DURATION/1000)+1)*MAX_FRAMES_PER_SEC)
38
39
40struct stream_data
41{
42 pj_uint32_t first_timestamp;
43 pj_uint32_t last_timestamp;
44 pj_timestamp last_called;
45 unsigned counter;
46 unsigned min_delay;
47 unsigned max_delay;
48 unsigned delay[MAX_DELAY_COUNTER];
49};
50
51struct test_data
52{
53 pj_pool_t *pool;
Benny Prijono10454dc2009-02-21 14:21:59 +000054 const pjmedia_aud_param *param;
Benny Prijonoe3ebd552009-02-18 20:14:15 +000055 pjmedia_aud_test_results *result;
56 pj_bool_t running;
57 pj_bool_t has_error;
58 pj_mutex_t *mutex;
59
60 struct stream_data capture_data;
61 struct stream_data playback_data;
62};
63
64static pj_status_t play_cb(void *user_data, pjmedia_frame *frame)
65{
66 struct test_data *test_data = (struct test_data *)user_data;
67 struct stream_data *strm_data = &test_data->playback_data;
68
69 pj_mutex_lock(test_data->mutex);
70
71 /* Skip frames when test is not started or test has finished */
72 if (!test_data->running) {
73 pj_bzero(frame->buf, frame->size);
74 pj_mutex_unlock(test_data->mutex);
75 return PJ_SUCCESS;
76 }
77
78 /* Save last timestamp seen (to calculate drift) */
79 strm_data->last_timestamp = frame->timestamp.u32.lo;
80
81 if (strm_data->last_called.u64 == 0) {
82 pj_get_timestamp(&strm_data->last_called);
83 /* Init min_delay to one frame */
84 strm_data->min_delay = test_data->param->samples_per_frame * 1000000 /
85 test_data->param->clock_rate;
86 strm_data->first_timestamp = frame->timestamp.u32.lo;
87
88 } else if (strm_data->counter <= MAX_DELAY_COUNTER) {
89 pj_timestamp now;
90 unsigned delay;
91
92 pj_get_timestamp(&now);
93
94 /* Calculate frame interval */
95 delay = pj_elapsed_usec(&strm_data->last_called, &now);
96 if (delay < strm_data->min_delay)
97 strm_data->min_delay = delay;
98 if (delay > strm_data->max_delay)
99 strm_data->max_delay = delay;
100
101 strm_data->last_called = now;
102
103 /* Save the frame interval for later calculation */
104 strm_data->delay[strm_data->counter] = delay;
105 ++strm_data->counter;
106
107 } else {
108
109 /* No space, can't take anymore frames */
110 test_data->running = 0;
111
112 }
113
114 pj_bzero(frame->buf, frame->size);
115
116 pj_mutex_unlock(test_data->mutex);
117
118 return PJ_SUCCESS;
119}
120
121static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
122{
123 struct test_data *test_data = (struct test_data*)user_data;
124 struct stream_data *strm_data = &test_data->capture_data;
125
126 pj_mutex_lock(test_data->mutex);
127
128 /* Skip frames when test is not started or test has finished */
129 if (!test_data->running) {
130 pj_mutex_unlock(test_data->mutex);
131 return PJ_SUCCESS;
132 }
133
134 /* Save last timestamp seen (to calculate drift) */
135 strm_data->last_timestamp = frame->timestamp.u32.lo;
136
137 if (strm_data->last_called.u64 == 0) {
138 pj_get_timestamp(&strm_data->last_called);
139 /* Init min_delay to one frame */
140 strm_data->min_delay = test_data->param->samples_per_frame * 1000000 /
141 test_data->param->clock_rate;
142 strm_data->first_timestamp = frame->timestamp.u32.lo;
143
144 } else if (strm_data->counter <= MAX_DELAY_COUNTER) {
145 pj_timestamp now;
146 unsigned delay;
147
148 pj_get_timestamp(&now);
149
150 /* Calculate frame interval */
151 delay = pj_elapsed_usec(&strm_data->last_called, &now);
152 if (delay < strm_data->min_delay)
153 strm_data->min_delay = delay;
154 if (delay > strm_data->max_delay)
155 strm_data->max_delay = delay;
156
157 strm_data->last_called = now;
158
159 /* Save the frame interval for later calculation */
160 strm_data->delay[strm_data->counter] = delay;
161 ++strm_data->counter;
162
163 } else {
164
165 /* No space, can't take anymore frames */
166 test_data->running = 0;
167
168 }
169
170 pj_mutex_unlock(test_data->mutex);
171 return PJ_SUCCESS;
172}
173
174static void app_perror(const char *title, pj_status_t status)
175{
176 char errmsg[PJ_ERR_MSG_SIZE];
177
178 pj_strerror(status, errmsg, sizeof(errmsg));
179 printf( "%s: %s (err=%d)\n",
180 title, errmsg, status);
181}
182
183
Benny Prijono10454dc2009-02-21 14:21:59 +0000184PJ_DEF(pj_status_t) pjmedia_aud_test( const pjmedia_aud_param *param,
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000185 pjmedia_aud_test_results *result)
186{
187 pj_status_t status = PJ_SUCCESS;
188 pjmedia_aud_stream *strm;
189 struct test_data test_data;
190
191 /*
192 * Init test parameters
193 */
194 pj_bzero(&test_data, sizeof(test_data));
195 test_data.param = param;
196 test_data.result = result;
197
198 test_data.pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(),
199 "audtest", 1000, 1000, NULL);
200 pj_mutex_create_simple(test_data.pool, "sndtest", &test_data.mutex);
201
202 /*
203 * Open device.
204 */
205 status = pjmedia_aud_stream_create(test_data.param, &rec_cb, &play_cb,
206 &test_data, &strm);
207 if (status != PJ_SUCCESS) {
208 app_perror("Unable to open device", status);
209 pj_pool_release(test_data.pool);
210 return status;
211 }
212
213
214 /* Sleep for a while to let sound device "settles" */
215 pj_thread_sleep(200);
216
217 /*
218 * Start the stream.
219 */
220 status = pjmedia_aud_stream_start(strm);
221 if (status != PJ_SUCCESS) {
222 app_perror("Unable to start capture stream", status);
223 pjmedia_aud_stream_destroy(strm);
224 pj_pool_release(test_data.pool);
225 return status;
226 }
227
228 PJ_LOG(3,(THIS_FILE,
229 " Please wait while test is in progress (~%d secs)..",
230 (DURATION+SKIP_DURATION)/1000));
231
232 /* Let the stream runs for few msec/sec to get stable result.
233 * (capture normally begins with frames available simultaneously).
234 */
235 pj_thread_sleep(SKIP_DURATION);
236
237
238 /* Begin gather data */
239 test_data.running = 1;
240
241 /*
242 * Let the test runs for a while.
243 */
244 pj_thread_sleep(DURATION);
245
246
247 /*
248 * Close stream.
249 */
250 test_data.running = 0;
251 pjmedia_aud_stream_destroy(strm);
252 pj_pool_release(test_data.pool);
253
254
255 /*
256 * Gather results
257 */
258 result->rec.frame_cnt = test_data.capture_data.counter;
259 result->rec.min_interval = test_data.capture_data.min_delay / 1000;
260 result->rec.max_interval = test_data.capture_data.max_delay / 1000;
261 result->rec.max_burst = test_data.capture_data.max_delay / 1000 /
262 (param->samples_per_frame * 1000 / param->clock_rate);
263
264 result->play.frame_cnt = test_data.playback_data.counter;
265 result->play.min_interval = test_data.playback_data.min_delay / 1000;
266 result->play.max_interval = test_data.playback_data.max_delay / 1000;
267 result->play.max_burst = test_data.playback_data.max_delay / 1000 /
268 (param->samples_per_frame * 1000 / param->clock_rate);
269
270 /* Check drifting */
271 if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
272 int end_diff, start_diff, drift;
273
274 end_diff = test_data.capture_data.last_timestamp -
275 test_data.playback_data.last_timestamp;
276 start_diff = test_data.capture_data.first_timestamp-
277 test_data.playback_data.first_timestamp;
278 drift = end_diff > start_diff? end_diff - start_diff :
279 start_diff - end_diff;
280
281 /* Allow one frame tolerance for clock drift detection */
282 if (drift < (int)param->samples_per_frame) {
283 result->rec_drift_per_sec = 0;
284 } else {
285 unsigned msec_dur;
286
287 msec_dur = (test_data.capture_data.last_timestamp -
288 test_data.capture_data.first_timestamp) * 1000 /
289 test_data.param->clock_rate;
290
291 result->rec_drift_per_sec = drift * 1000 / msec_dur;
292
293 }
294 }
295
296 return test_data.has_error? PJ_EUNKNOWN : PJ_SUCCESS;
297}
298