Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjmedia / src / pjmedia-audiodev / audiotest.c
1 /* $Id$ */
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 #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 /* Division helper */
34 #define DIV_ROUND_UP(a,b) (((a) + ((b) - 1)) / (b))
35 #define DIV_ROUND(a,b) (((a) + ((b)/2 - 1)) / (b))
36
37 struct stream_data
38 {
39     pj_uint32_t     first_timestamp;
40     pj_uint32_t     last_timestamp;
41     pj_timestamp    last_called;
42     pj_math_stat    delay;
43 };
44
45 struct test_data 
46 {
47     pj_pool_t                      *pool;
48     const pjmedia_aud_param        *param;
49     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
58 static 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) {
76         /* Init vars. */
77         pj_get_timestamp(&strm_data->last_called);
78         pj_math_stat_init(&strm_data->delay);
79         strm_data->first_timestamp = frame->timestamp.u32.lo;
80     } else {
81         pj_timestamp now;
82         unsigned delay;
83
84         /* Calculate frame interval */
85         pj_get_timestamp(&now);
86         delay = pj_elapsed_usec(&strm_data->last_called, &now);
87         strm_data->last_called = now;
88
89         /* Update frame interval statistic */
90         pj_math_stat_update(&strm_data->delay, delay);
91     }
92
93     pj_bzero(frame->buf, frame->size);
94
95     pj_mutex_unlock(test_data->mutex);
96
97     return PJ_SUCCESS;
98 }
99
100 static 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) {
117         /* Init vars. */
118         pj_get_timestamp(&strm_data->last_called);
119         pj_math_stat_init(&strm_data->delay);
120         strm_data->first_timestamp = frame->timestamp.u32.lo;
121     } else {
122         pj_timestamp now;
123         unsigned delay;
124
125         /* Calculate frame interval */
126         pj_get_timestamp(&now);
127         delay = pj_elapsed_usec(&strm_data->last_called, &now);
128         strm_data->last_called = now;
129
130         /* Update frame interval statistic */
131         pj_math_stat_update(&strm_data->delay, delay);
132     }
133
134     pj_mutex_unlock(test_data->mutex);
135     return PJ_SUCCESS;
136 }
137
138 static 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
148 PJ_DEF(pj_status_t) pjmedia_aud_test( const pjmedia_aud_param *param,
149                                       pjmedia_aud_test_results *result)
150 {
151     pj_status_t status = PJ_SUCCESS;
152     pjmedia_aud_stream *strm;
153     struct test_data test_data;
154     unsigned ptime, tmp;
155     
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      */
223     ptime = param->samples_per_frame * 1000 / param->clock_rate;
224
225     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);
237     result->play.avg_interval = DIV_ROUND(test_data.playback_data.delay.mean, 1000);
238     result->play.dev_interval = DIV_ROUND(tmp, 1000);
239     result->play.max_burst    = DIV_ROUND_UP(result->play.max_interval, ptime);
240
241     /* Check drifting */
242     if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
243         int play_diff, cap_diff, drift;
244
245         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;
251
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