1 /*
2 * This program only tracks the difference between system time
3 * and audio time, as reported in snd_pcm_status(). It should be
4 * helpful to verify the information reported by drivers.
5 */
6
7 #include <stdio.h>
8 #include <malloc.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <getopt.h>
13 #include <fcntl.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <limits.h>
17 #include <time.h>
18 #include <locale.h>
19 #include <math.h>
20 #include "../include/asoundlib.h"
21
22 static char *pcm_name = "hw:0";
23 snd_output_t *output = NULL;
24
usage(char * command)25 static void usage(char *command)
26 {
27 printf("Usage: %s [OPTION]... \n"
28 "\n"
29 "-h, --help help\n"
30 "-c, --capture capture tstamps \n"
31 "-d, --delay add delay \n"
32 "-D, --device=NAME select PCM by name \n"
33 "-p, --playback playback tstamps \n"
34 "-t, --ts_type=TYPE Compat(0),default(1),link(2),link_absolute(3),link_estimated(4),link_synchronized(5) \n"
35 "-r, --report show audio timestamp and accuracy validity\n"
36 , command);
37 }
38
39
timestamp2ns(snd_htimestamp_t t)40 long long timestamp2ns(snd_htimestamp_t t)
41 {
42 long long nsec;
43
44 nsec = t.tv_sec * 1000000000ULL;
45 nsec += t.tv_nsec;
46
47 return nsec;
48 }
49
timediff(snd_htimestamp_t t1,snd_htimestamp_t t2)50 long long timediff(snd_htimestamp_t t1, snd_htimestamp_t t2)
51 {
52 long long nsec1, nsec2;
53
54 nsec1 = timestamp2ns(t1);
55 nsec2 = timestamp2ns(t2);
56
57 return nsec1 - nsec2;
58 }
59
_gettimestamp(snd_pcm_t * handle,snd_htimestamp_t * timestamp,snd_htimestamp_t * trigger_timestamp,snd_htimestamp_t * audio_timestamp,snd_pcm_audio_tstamp_config_t * audio_tstamp_config,snd_pcm_audio_tstamp_report_t * audio_tstamp_report,snd_pcm_uframes_t * avail,snd_pcm_sframes_t * delay)60 void _gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp,
61 snd_htimestamp_t *trigger_timestamp,
62 snd_htimestamp_t *audio_timestamp,
63 snd_pcm_audio_tstamp_config_t *audio_tstamp_config,
64 snd_pcm_audio_tstamp_report_t *audio_tstamp_report,
65 snd_pcm_uframes_t *avail, snd_pcm_sframes_t *delay)
66 {
67 int err;
68 snd_pcm_status_t *status;
69
70 snd_pcm_status_alloca(&status);
71
72 snd_pcm_status_set_audio_htstamp_config(status, audio_tstamp_config);
73
74 if ((err = snd_pcm_status(handle, status)) < 0) {
75 printf("Stream status error: %s\n", snd_strerror(err));
76 exit(0);
77 }
78 snd_pcm_status_get_trigger_htstamp(status, trigger_timestamp);
79 snd_pcm_status_get_htstamp(status, timestamp);
80 snd_pcm_status_get_audio_htstamp(status, audio_timestamp);
81 snd_pcm_status_get_audio_htstamp_report(status, audio_tstamp_report);
82 *avail = snd_pcm_status_get_avail(status);
83 *delay = snd_pcm_status_get_delay(status);
84 }
85
86 #define TIMESTAMP_FREQ 8 /* Hz */
87 #define SAMPLE_FREQ 48000
88 #define PERIOD (SAMPLE_FREQ/TIMESTAMP_FREQ)
89 #define PCM_LINK /* sync start for playback and capture */
90 #define TRACK_CAPTURE /* dump capture timing info */
91 #define TRACK_PLAYBACK /* dump playback timing info */
92 /*#define TRACK_SAMPLE_COUNTS */ /* show difference between sample counters and audiotimestamps returned by driver */
93 #define PLAYBACK_BUFFERS 4
94 #define TSTAMP_TYPE SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW
95
96
main(int argc,char * argv[])97 int main(int argc, char *argv[])
98 {
99 int c;
100 int err;
101 unsigned int i;
102 snd_pcm_t *handle_p = NULL;
103 snd_pcm_t *handle_c = NULL;
104 snd_pcm_sframes_t frames;
105 snd_htimestamp_t tstamp_c, tstamp_p;
106 snd_htimestamp_t trigger_tstamp_c, trigger_tstamp_p;
107 snd_htimestamp_t audio_tstamp_c, audio_tstamp_p;
108 unsigned char buffer_p[PERIOD*4*4];
109 unsigned char buffer_c[PERIOD*4*4];
110
111 snd_pcm_hw_params_t *hwparams_p;
112 snd_pcm_hw_params_t *hwparams_c;
113
114 snd_pcm_sw_params_t *swparams_p;
115 snd_pcm_sw_params_t *swparams_c;
116
117 snd_pcm_uframes_t frame_count_c = 0;
118 snd_pcm_uframes_t frame_count_p = 0;
119
120 snd_pcm_sframes_t delay_p, delay_c;
121 snd_pcm_uframes_t avail_p, avail_c;
122
123 snd_pcm_audio_tstamp_config_t audio_tstamp_config_p;
124 snd_pcm_audio_tstamp_config_t audio_tstamp_config_c;
125 snd_pcm_audio_tstamp_report_t audio_tstamp_report_p;
126 snd_pcm_audio_tstamp_report_t audio_tstamp_report_c;
127
128 int option_index;
129 static const char short_options[] = "hcpdrD:t:";
130
131 static const struct option long_options[] = {
132 {"capture", 0, 0, 'c'},
133 {"delay", 0, 0, 'd'},
134 {"device", required_argument, 0, 'D'},
135 {"help", no_argument, 0, 'h'},
136 {"playback", 0, 0, 'p'},
137 {"ts_type", required_argument, 0, 't'},
138 {"report", 0, 0, 'r'},
139 {0, 0, 0, 0}
140 };
141
142 int do_delay = 0;
143 int do_playback = 0;
144 int do_capture = 0;
145 int type = 0;
146 int do_report = 0;
147
148 while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) {
149 switch (c) {
150 case 'h':
151 usage(argv[0]);
152 return 0;
153 case 'p':
154 do_playback = 1;
155 break;
156 case 'c':
157 do_capture = 1;
158 break;
159 case 'd':
160 do_delay = 1;
161 break;
162 case 'D':
163 pcm_name = optarg;
164 break;
165 case 't':
166 type = atoi(optarg);
167 break;
168 case 'r':
169 do_report = 1;
170 }
171 }
172
173 memset(&audio_tstamp_config_p, 0, sizeof(snd_pcm_audio_tstamp_config_t));
174 memset(&audio_tstamp_config_c, 0, sizeof(snd_pcm_audio_tstamp_config_t));
175 memset(&audio_tstamp_report_p, 0, sizeof(snd_pcm_audio_tstamp_report_t));
176 memset(&audio_tstamp_report_c, 0, sizeof(snd_pcm_audio_tstamp_report_t));
177
178 if (do_playback) {
179 if ((err = snd_pcm_open(&handle_p, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
180 printf("Playback open error: %s\n", snd_strerror(err));
181 goto _exit;
182 }
183
184 if ((err = snd_pcm_set_params(handle_p,
185 SND_PCM_FORMAT_S16,
186 SND_PCM_ACCESS_RW_INTERLEAVED,
187 2,
188 SAMPLE_FREQ,
189 0,
190 4*1000000/TIMESTAMP_FREQ)) < 0) {
191 printf("Playback open error: %s\n", snd_strerror(err));
192 goto _exit;
193 }
194
195 snd_pcm_hw_params_alloca(&hwparams_p);
196 /* get the current hwparams */
197 err = snd_pcm_hw_params_current(handle_p, hwparams_p);
198 if (err < 0) {
199 printf("Unable to determine current hwparams_p: %s\n", snd_strerror(err));
200 goto _exit;
201 }
202
203 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_COMPAT))
204 printf("Playback supports audio compat timestamps\n");
205 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_DEFAULT))
206 printf("Playback supports audio default timestamps\n");
207 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_LINK))
208 printf("Playback supports audio link timestamps\n");
209 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE))
210 printf("Playback supports audio link absolute timestamps\n");
211 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED))
212 printf("Playback supports audio link estimated timestamps\n");
213 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED))
214 printf("Playback supports audio link synchronized timestamps\n");
215
216 snd_pcm_sw_params_alloca(&swparams_p);
217 /* get the current swparams */
218 err = snd_pcm_sw_params_current(handle_p, swparams_p);
219 if (err < 0) {
220 printf("Unable to determine current swparams_p: %s\n", snd_strerror(err));
221 goto _exit;
222 }
223
224 /* enable tstamp */
225 err = snd_pcm_sw_params_set_tstamp_mode(handle_p, swparams_p, SND_PCM_TSTAMP_ENABLE);
226 if (err < 0) {
227 printf("Unable to set tstamp mode : %s\n", snd_strerror(err));
228 goto _exit;
229 }
230
231 err = snd_pcm_sw_params_set_tstamp_type(handle_p, swparams_p, TSTAMP_TYPE);
232 if (err < 0) {
233 printf("Unable to set tstamp type : %s\n", snd_strerror(err));
234 goto _exit;
235 }
236
237 /* write the sw parameters */
238 err = snd_pcm_sw_params(handle_p, swparams_p);
239 if (err < 0) {
240 printf("Unable to set swparams_p : %s\n", snd_strerror(err));
241 goto _exit;
242 }
243
244 }
245
246 if (do_capture) {
247
248 if ((err = snd_pcm_open(&handle_c, pcm_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
249 printf("Capture open error: %s\n", snd_strerror(err));
250 goto _exit;
251 }
252 if ((err = snd_pcm_set_params(handle_c,
253 SND_PCM_FORMAT_S16,
254 SND_PCM_ACCESS_RW_INTERLEAVED,
255 2,
256 SAMPLE_FREQ,
257 0,
258 4*1000000/TIMESTAMP_FREQ)) < 0) {
259 printf("Capture open error: %s\n", snd_strerror(err));
260 goto _exit;
261 }
262
263 snd_pcm_hw_params_alloca(&hwparams_c);
264 /* get the current hwparams */
265 err = snd_pcm_hw_params_current(handle_c, hwparams_c);
266 if (err < 0) {
267 printf("Unable to determine current hwparams_c: %s\n", snd_strerror(err));
268 goto _exit;
269 }
270
271 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_COMPAT))
272 printf("Capture supports audio compat timestamps\n");
273 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_DEFAULT))
274 printf("Capture supports audio default timestamps\n");
275 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_LINK))
276 printf("Capture supports audio link timestamps\n");
277 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE))
278 printf("Capture supports audio link absolute timestamps\n");
279 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED))
280 printf("Capture supports audio link estimated timestamps\n");
281 if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED))
282 printf("Capture supports audio link synchronized timestamps\n");
283
284 snd_pcm_sw_params_alloca(&swparams_c);
285 /* get the current swparams */
286 err = snd_pcm_sw_params_current(handle_c, swparams_c);
287 if (err < 0) {
288 printf("Unable to determine current swparams_c: %s\n", snd_strerror(err));
289 goto _exit;
290 }
291
292 /* enable tstamp */
293 err = snd_pcm_sw_params_set_tstamp_mode(handle_c, swparams_c, SND_PCM_TSTAMP_ENABLE);
294 if (err < 0) {
295 printf("Unable to set tstamp mode : %s\n", snd_strerror(err));
296 goto _exit;
297 }
298
299 err = snd_pcm_sw_params_set_tstamp_type(handle_c, swparams_c, TSTAMP_TYPE);
300 if (err < 0) {
301 printf("Unable to set tstamp type : %s\n", snd_strerror(err));
302 goto _exit;
303 }
304
305 /* write the sw parameters */
306 err = snd_pcm_sw_params(handle_c, swparams_c);
307 if (err < 0) {
308 printf("Unable to set swparams_c : %s\n", snd_strerror(err));
309 goto _exit;
310 }
311 }
312
313 if (do_playback && do_capture) {
314 #ifdef PCM_LINK
315 if ((err = snd_pcm_link(handle_c, handle_p)) < 0) {
316 printf("Streams link error: %s\n", snd_strerror(err));
317 exit(0);
318 }
319 #endif
320 }
321
322 if (do_playback) {
323 i = PLAYBACK_BUFFERS;
324 while (i--) {
325 frames = snd_pcm_writei(handle_p, buffer_p, PERIOD);
326 if (frames < 0) {
327 printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
328 goto _exit;
329 }
330 frame_count_p += frames;
331 }
332
333 if (PLAYBACK_BUFFERS != 4)
334 snd_pcm_start(handle_p);
335 }
336
337 if (do_capture) {
338 #ifndef PCM_LINK
339 /* need to start capture explicitly */
340 snd_pcm_start(handle_c);
341 #else
342 if (!do_playback)
343 /* need to start capture explicitly */
344 snd_pcm_start(handle_c);
345 #endif
346 }
347
348 while (1) {
349
350 if (do_capture) {
351
352 frames = snd_pcm_wait(handle_c, -1);
353 if (frames < 0) {
354 printf("snd_pcm_wait failed: %s\n", snd_strerror(frames));
355 goto _exit;
356 }
357
358 frames = snd_pcm_readi(handle_c, buffer_c, PERIOD);
359 if (frames < 0) {
360 printf("snd_pcm_readi failed: %s\n", snd_strerror(frames));
361 goto _exit;
362 }
363 frame_count_c += frames;
364
365 #if defined(TRACK_CAPTURE)
366 audio_tstamp_config_c.type_requested = type;
367 audio_tstamp_config_c.report_delay = do_delay;
368 _gettimestamp(handle_c, &tstamp_c, &trigger_tstamp_c,
369 &audio_tstamp_c, &audio_tstamp_config_c, &audio_tstamp_report_c,
370 &avail_c, &delay_c);
371 #if defined(TRACK_SAMPLE_COUNTS)
372 curr_count_c = frame_count_c + delay_c; /* read plus queued */
373
374
375 printf("capture: curr_count %lli driver count %lli, delta %lli\n",
376 (long long)curr_count_c * 1000000000LL / SAMPLE_FREQ ,
377 timestamp2ns(audio_tstamp_c),
378 (long long)curr_count_c * 1000000000LL / SAMPLE_FREQ - timestamp2ns(audio_tstamp_c)
379 );
380 #endif
381 if (do_report) {
382 if (audio_tstamp_report_c.valid == 0)
383 printf("Audio capture timestamp report invalid - ");
384 if (audio_tstamp_report_c.accuracy_report == 0)
385 printf("Audio capture timestamp accuracy report invalid");
386 printf("\n");
387 }
388
389
390 printf("\t capture: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli \t resolution %d ns \n",
391 timediff(tstamp_c, trigger_tstamp_c),
392 timestamp2ns(audio_tstamp_c),
393 timediff(tstamp_c, trigger_tstamp_c) - timestamp2ns(audio_tstamp_c), audio_tstamp_report_c.accuracy
394 );
395 #endif
396 }
397
398 if (do_playback) {
399 frames = snd_pcm_writei(handle_p, buffer_p, PERIOD);
400 if (frames < 0) {
401 printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
402 goto _exit;
403 }
404
405 frame_count_p += frames;
406
407 #if defined(TRACK_PLAYBACK)
408
409 audio_tstamp_config_p.type_requested = type;
410 audio_tstamp_config_p.report_delay = do_delay;
411 _gettimestamp(handle_p, &tstamp_p, &trigger_tstamp_p,
412 &audio_tstamp_p, &audio_tstamp_config_p, &audio_tstamp_report_p,
413 &avail_p, &delay_p);
414
415 #if defined(TRACK_SAMPLE_COUNTS)
416 curr_count_p = frame_count_p - delay_p; /* written minus queued */
417
418 printf("playback: curr_count %lli driver count %lli, delta %lli\n",
419 (long long)curr_count_p * 1000000000LL / SAMPLE_FREQ ,
420 timestamp2ns(audio_tstamp_p),
421 (long long)curr_count_p * 1000000000LL / SAMPLE_FREQ - timestamp2ns(audio_tstamp_p)
422 );
423 #endif
424 if (do_report) {
425 if (audio_tstamp_report_p.valid == 0)
426 printf("Audio playback timestamp report invalid - ");
427 if (audio_tstamp_report_p.accuracy_report == 0)
428 printf("Audio playback timestamp accuracy report invalid");
429 printf("\n");
430 }
431
432 printf("playback: systime: %lli nsec, audio time %lli nsec, \tsystime delta %lli resolution %d ns\n",
433 timediff(tstamp_p, trigger_tstamp_p),
434 timestamp2ns(audio_tstamp_p),
435 timediff(tstamp_p, trigger_tstamp_p) - timestamp2ns(audio_tstamp_p), audio_tstamp_report_p.accuracy
436 );
437 #endif
438 }
439
440
441 } /* while(1) */
442
443 _exit:
444 if (handle_p)
445 snd_pcm_close(handle_p);
446 if (handle_c)
447 snd_pcm_close(handle_c);
448
449 return 0;
450 }
451