• 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 <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