• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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