blob: 9e8dbb5ccae7408be0b449de95a7f202051be9f0 [file] [log] [blame]
Chinyue Chen2ff17232016-07-07 14:56:06 +08001/*
2 * Copyright 2016 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include <pthread.h>
8#include <getopt.h>
9#include <stdio.h>
10#include <stdlib.h>
11
12#include "cras_client.h"
13
14#define CAPTURE_PERIOD_SIZE 256
15#define CAPTURE_PERIOD_COUNT 4
16
17int num_retries = 3;
18int wait_sec = 10;
19
20struct stream_in {
21 uint32_t sample_rate;
22 uint32_t channels;
23 uint32_t period_size;
Earl Ou7ae955b2016-11-30 16:41:22 +080024 struct cras_audio_format *cras_format;
25 struct cras_stream_params *cras_params;
Chinyue Chen2ff17232016-07-07 14:56:06 +080026 cras_stream_id_t stream_id;
27
28 uint32_t frames_to_capture;
29 uint32_t frames_captured;
30 pthread_mutex_t lock;
31 pthread_cond_t cond;
32};
33
34static uint32_t get_input_period_size(uint32_t sample_rate)
35{
36 /*
37 * The supported capture sample rates range from 8000 to 48000.
38 * We need to use different buffer size when creating CRAS stream
39 * so that appropriate latency is maintained.
40 */
41 if (sample_rate <= 12000)
42 return (CAPTURE_PERIOD_SIZE) / 4;
43 else if (sample_rate <= 24000)
44 return (CAPTURE_PERIOD_SIZE) / 2;
45 else
46 return CAPTURE_PERIOD_SIZE;
47}
48
Fletcher Woodruff0d548562019-09-23 13:42:10 -060049void cras_connection_status_cb(struct cras_client *client,
50 cras_connection_status_t status, void *user_arg);
Chinyue Chen2ff17232016-07-07 14:56:06 +080051
52int cras_open(struct cras_client **client)
53{
54 int rc;
55
56 rc = cras_client_create(client);
57 if (rc < 0) {
58 fprintf(stderr, "%s: Failed to create CRAS client.\n", __func__);
59 return rc;
60 }
61
62 rc = cras_client_connect_timeout(*client, 3000);
63 if (rc) {
64 fprintf(stderr, "%s: Failed to connect to CRAS server, rc %d.\n",
65 __func__, rc);
66 goto fail;
67 }
68
69 rc = cras_client_run_thread(*client);
70 if (rc) {
71 fprintf(stderr, "%s: Failed to start CRAS client.\n", __func__);
72 goto fail;
73 }
74
75 rc = cras_client_connected_wait(*client);
76 if (rc) {
77 fprintf(stderr, "%s: Failed to wait for connected.\n", __func__);
78 goto fail;
79 }
80
Fletcher Woodruff0d548562019-09-23 13:42:10 -060081 cras_client_set_connection_status_cb(*client, cras_connection_status_cb,
82 NULL);
Chinyue Chen2ff17232016-07-07 14:56:06 +080083
84 return 0;
85
86fail:
87 cras_client_destroy(*client);
88 return rc;
89}
90
91int cras_close(struct cras_client *client)
92{
93 cras_client_stop(client);
94 cras_client_destroy(client);
95 return 0;
96}
97
Fletcher Woodruff0d548562019-09-23 13:42:10 -060098void cras_connection_status_cb(struct cras_client *client,
99 cras_connection_status_t status,
100 void *user_arg) {
101 if (status == CRAS_CONN_STATUS_FAILED) {
102 fprintf(stderr, "Server connection failed!\n");
103 cras_client_stop(client);
104 }
Chinyue Chen2ff17232016-07-07 14:56:06 +0800105}
106
107static int in_read_cb(struct cras_client *client,
108 cras_stream_id_t stream_id,
109 uint8_t *captured_samples,
110 uint8_t *playback_samples,
111 unsigned int frames,
112 const struct timespec *captured_time,
113 const struct timespec *playback_time,
114 void *user_arg)
115{
116 struct stream_in *in = (struct stream_in *)user_arg;
117
118 pthread_mutex_lock(&in->lock);
119
120 in->frames_captured += frames;
121 if (in->frames_captured >= in->frames_to_capture)
122 pthread_cond_signal(&in->cond);
123
124 pthread_mutex_unlock(&in->lock);
125
126 return frames;
127}
128
129static int in_err_cb(struct cras_client *client,
130 cras_stream_id_t stream_id,
131 int error,
132 void *user_arg)
133{
134 fprintf(stderr, "%s: enter\n", __func__);
135 return 0;
136}
137
138struct stream_in *open_input_stream(struct cras_client *client,
139 uint32_t sample_rate, uint32_t channels)
140{
141 struct stream_in *in;
142
143 printf("%s: sample_rate %d channels %d\n", __func__, sample_rate, channels);
144
145 in = (struct stream_in *)calloc(1, sizeof(struct stream_in));
146
147 in->sample_rate = sample_rate;
148 in->channels = channels;
149 in->period_size = get_input_period_size(in->sample_rate);
150
151 in->cras_format = cras_audio_format_create(
152 SND_PCM_FORMAT_S16_LE,
153 in->sample_rate,
154 in->channels);
155 if (!in->cras_format) {
156 fprintf(stderr, "%s: Failed to create CRAS audio format.\n", __func__);
157 goto cleanup;
158 }
159
160 in->cras_params = cras_client_unified_params_create(
161 CRAS_STREAM_INPUT,
162 in->period_size,
163 CRAS_STREAM_TYPE_DEFAULT,
164 0,
165 (void *)in,
166 in_read_cb,
167 in_err_cb,
168 in->cras_format);
169 if (!in->cras_params) {
170 fprintf(stderr, "%s: Failed to create stream params.\n", __func__);
171 goto cleanup;
172 }
173
174 printf("%s: stream created.\n", __func__);
175 return in;
176
177cleanup:
178 if (in->cras_format)
179 cras_audio_format_destroy(in->cras_format);
180 if (in->cras_params)
181 cras_client_stream_params_destroy(in->cras_params);
182 free(in);
183 return NULL;
184}
185
186int start_input_stream(struct cras_client *client, struct stream_in *in)
187{
188 return cras_client_add_stream(client, &in->stream_id, in->cras_params);
189}
190
191void close_input_stream(struct cras_client *client, struct stream_in *in)
192{
193 if (in->stream_id)
194 cras_client_rm_stream(client, in->stream_id);
195 cras_audio_format_destroy(in->cras_format);
196 cras_client_stream_params_destroy(in->cras_params);
197 free(in);
198}
199
200int cras_test_capture(struct cras_client *client, uint32_t sample_rate,
201 uint32_t channels, uint32_t segment_ms, uint32_t segments)
202{
203 struct stream_in *in;
204 int i, ret = 0, fail_count = 0;
205 pthread_condattr_t attr;
206 struct timespec abs_ts, wait_ts;
207
208 in = open_input_stream(client, sample_rate, channels);
209 if (!in) {
210 fprintf(stderr, "%s: Failed to open input stream.\n", __func__);
211 return -1;
212 }
213
214 in->frames_to_capture = in->sample_rate * segment_ms / 1000;
215
216 pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *)NULL);
217 pthread_condattr_init(&attr);
218 pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
219 pthread_cond_init(&in->cond, &attr);
220
221 pthread_mutex_lock(&in->lock);
222
223 ret = start_input_stream(client, in);
224 if (ret) {
225 pthread_mutex_unlock(&in->lock);
226 fprintf(stderr, "%s: Failed to start input stream.\n", __func__);
227 goto cleanup;
228 }
229
230 for (i = 0; i < segments; ++i) {
231 in->frames_captured = 0;
232
233 // Wait for captured data.
234 if (wait_sec > 0) {
235 clock_gettime(CLOCK_MONOTONIC, &abs_ts);
236 wait_ts.tv_sec = wait_sec;
237 wait_ts.tv_nsec = 0;
238 add_timespecs(&abs_ts, &wait_ts);
239 ret = pthread_cond_timedwait(&in->cond, &in->lock, &abs_ts);
240 } else {
241 ret = pthread_cond_wait(&in->cond, &in->lock);
242 }
243 if (ret) {
244 if (ret == ETIMEDOUT) {
245 fprintf(stderr, "%s: Failed to receive captured data.\n",
246 __func__);
247 } else {
248 fprintf(stderr, "%s: Failed to wait for data.\n", __func__);
249 }
250 if (++fail_count >= num_retries)
251 break;
252 else
253 continue;
254 }
255
256 printf("%s: Captured %d frames for segment %d.\n", __func__,
257 in->frames_captured, i);
258 }
259
260 pthread_mutex_unlock(&in->lock);
261
262cleanup:
263 close_input_stream(client, in);
264 return ret;
265}
266
267int cras_stress_capture(struct cras_client *client)
268{
269 uint32_t test_sample_rate[] = {
270 12345,
271 44100,
272 48000,
273 96000,
274 192000,
275 };
276 uint32_t test_channels[] = {
277 1,
278 2,
279 3,
280 4,
281 5,
282 6,
283 7,
284 8,
285 };
286 int i, j;
287 int ret;
288
289 for (i = 0; i < ARRAY_SIZE(test_sample_rate); ++i) {
290 for (j = 0; j < ARRAY_SIZE(test_channels); ++j) {
291 ret = cras_test_capture(client, test_sample_rate[i],
292 test_channels[j], 20, 10);
293 if (ret) {
294 fprintf(stderr,
295 "%s: Failed to capture sample_rate %d channels %d.\n",
296 __func__, test_sample_rate[i], test_channels[j]);
297 return ret;
298 }
299 }
300 }
301
302 return 0;
303}
304
305int main (int argc, char *argv[])
306{
307 struct cras_client *client;
308 int c, i, rc;
309 int num_tests = 1;
310 struct option long_opt[] =
311 {
312 { "help", no_argument, NULL, 'h' },
313 { "num_tests", required_argument, NULL, 'n' },
314 { "num_retries", required_argument, NULL, 'r' },
315 { "wait_sec", required_argument, NULL, 'w' },
316 { NULL, 0, NULL, 0 }
317 };
318
319 while (1) {
320 c = getopt_long(argc, argv, "hn:r:w:", long_opt, NULL);
321 if (c == -1)
322 break;
323 switch (c) {
324 case 'n':
325 num_tests = atoi(optarg);
326 break;
327
328 case 'r':
329 num_retries = atoi(optarg);
330 break;
331
332 case 'w':
333 wait_sec = atoi(optarg);
334 break;
335
336 case 'h':
337 printf("Usage: %s [OPTIONS]\n", argv[0]);
338 printf(" --num_tests <num> Number of test runs, "
339 "default: %d\n", num_tests);
340 printf(" --num_retries <num> Number of retries, "
341 "default: %d\n", num_retries);
342 printf(" --wait_sec <num> Wait seconds for captured data, "
343 "default: %d, use 0 to wait infinitely\n", wait_sec);
344 printf(" -h, --help Print this help and exit\n");
345 printf("\n");
346 exit(EXIT_SUCCESS);
347 }
348 }
349
350 rc = cras_open(&client);
351 if (rc) {
352 fprintf(stderr, "%s: Failed to open CRAS, rc %d.\n", __func__, rc);
353 exit(EXIT_FAILURE);
354 }
355
356 // Run tests.
357 for (i = 0; i < num_tests; ++i) {
358 printf("%s: ============\n", __func__);
359 printf("%s: TEST PASS %d\n", __func__, i);
360 printf("%s: ============\n", __func__);
361 rc = cras_stress_capture(client);
362 if (rc) {
363 fprintf(stderr, "%s: Failed in test pass %d.\n", __func__, i);
364 break;
365 }
366 }
367
368 if (client)
369 cras_close(client);
370
371 exit(EXIT_SUCCESS);
372}