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