1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // mapper-io.c - a unit test for muxer/demuxer for PCM frames on buffer.
4 //
5 // Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
6 //
7 // Licensed under the terms of the GNU General Public License, version 2.
8
9 #include <aconfig.h>
10 #ifdef HAVE_MEMFD_CREATE
11 #define _GNU_SOURCE
12 #endif
13
14 #include "../mapper.h"
15 #include "../misc.h"
16
17 #include "generator.h"
18
19 #ifdef HAVE_MEMFD_CREATE
20 #include <sys/mman.h>
21 #endif
22
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <stdbool.h>
26
27 #include <assert.h>
28
29 struct mapper_trial {
30 enum container_format cntr_format;
31 struct container_context *cntrs;
32
33 char **paths;
34
35 struct mapper_context mapper;
36 bool verbose;
37 };
38
test_demuxer(struct mapper_context * mapper,snd_pcm_access_t access,unsigned int bytes_per_sample,unsigned int samples_per_frame,unsigned int frames_per_buffer,void * frame_buffer,unsigned int frame_count,struct container_context * cntrs,unsigned int cntr_count,bool verbose)39 static void test_demuxer(struct mapper_context *mapper, snd_pcm_access_t access,
40 unsigned int bytes_per_sample,
41 unsigned int samples_per_frame,
42 unsigned int frames_per_buffer,
43 void *frame_buffer, unsigned int frame_count,
44 struct container_context *cntrs,
45 unsigned int cntr_count, bool verbose)
46 {
47 unsigned int total_frame_count;
48 int err;
49
50 err = mapper_context_init(mapper, MAPPER_TYPE_DEMUXER, cntr_count,
51 verbose);
52 assert(err == 0);
53
54 err = mapper_context_pre_process(mapper, access, bytes_per_sample,
55 samples_per_frame, frames_per_buffer,
56 cntrs);
57 assert(err == 0);
58
59 total_frame_count = frame_count;
60 err = mapper_context_process_frames(mapper, frame_buffer,
61 &total_frame_count, cntrs);
62 assert(err == 0);
63 assert(total_frame_count == frame_count);
64
65 mapper_context_post_process(mapper);
66 mapper_context_destroy(mapper);
67 }
68
test_demux(struct mapper_trial * trial,snd_pcm_access_t access,snd_pcm_format_t sample_format,unsigned int samples_per_frame,unsigned int frames_per_second,unsigned int frames_per_buffer,void * frame_buffer,unsigned int frame_count,int * cntr_fds,unsigned int cntr_count)69 static int test_demux(struct mapper_trial *trial, snd_pcm_access_t access,
70 snd_pcm_format_t sample_format,
71 unsigned int samples_per_frame,
72 unsigned int frames_per_second,
73 unsigned int frames_per_buffer,
74 void *frame_buffer, unsigned int frame_count,
75 int *cntr_fds, unsigned int cntr_count)
76 {
77 struct container_context *cntrs = trial->cntrs;
78 enum container_format cntr_format = trial->cntr_format;
79 unsigned int bytes_per_sample;
80 uint64_t total_frame_count;
81 int i;
82 int err = 0;
83
84 for (i = 0; i < cntr_count; ++i) {
85 snd_pcm_format_t format;
86 unsigned int channels;
87 unsigned int rate;
88
89 err = container_builder_init(cntrs + i, cntr_fds[i], cntr_format, 0);
90 if (err < 0)
91 goto end;
92
93 format = sample_format;
94 rate = frames_per_second;
95 total_frame_count = frame_count;
96 if (cntr_count > 1)
97 channels = 1;
98 else
99 channels = samples_per_frame;
100 err = container_context_pre_process(cntrs + i, &format,
101 &channels, &rate,
102 &total_frame_count);
103 if (err < 0)
104 goto end;
105 assert(format == sample_format);
106 assert(rate == frames_per_second);
107 assert(total_frame_count >= 0);
108 if (cntr_count > 1)
109 assert(channels == 1);
110 else
111 assert(channels == samples_per_frame);
112 }
113
114 bytes_per_sample = snd_pcm_format_physical_width(sample_format) / 8;
115 test_demuxer(&trial->mapper, access, bytes_per_sample,
116 samples_per_frame, frames_per_buffer, frame_buffer,
117 frame_count, cntrs, cntr_count, trial->verbose);
118
119 for (i = 0; i < cntr_count; ++i) {
120 container_context_post_process(cntrs + i, &total_frame_count);
121 assert(total_frame_count == frame_count);
122 }
123 end:
124 for (i = 0; i < cntr_count; ++i)
125 container_context_destroy(cntrs + i);
126
127 return err;
128 }
129
test_muxer(struct mapper_context * mapper,snd_pcm_access_t access,unsigned int bytes_per_sample,unsigned int samples_per_frame,unsigned int frames_per_buffer,void * frame_buffer,unsigned int frame_count,struct container_context * cntrs,unsigned int cntr_count,bool verbose)130 static void test_muxer(struct mapper_context *mapper, snd_pcm_access_t access,
131 unsigned int bytes_per_sample,
132 unsigned int samples_per_frame,
133 unsigned int frames_per_buffer,
134 void *frame_buffer, unsigned int frame_count,
135 struct container_context *cntrs,
136 unsigned int cntr_count, bool verbose)
137 {
138 unsigned int total_frame_count;
139 int err;
140
141 err = mapper_context_init(mapper, MAPPER_TYPE_MUXER, cntr_count,
142 verbose);
143 assert(err == 0);
144
145 err = mapper_context_pre_process(mapper, access, bytes_per_sample,
146 samples_per_frame, frames_per_buffer,
147 cntrs);
148 assert(err == 0);
149
150 total_frame_count = frame_count;
151 err = mapper_context_process_frames(mapper, frame_buffer,
152 &total_frame_count, cntrs);
153 assert(err == 0);
154 assert(total_frame_count == frame_count);
155
156 mapper_context_post_process(mapper);
157 mapper_context_destroy(mapper);
158 }
159
test_mux(struct mapper_trial * trial,snd_pcm_access_t access,snd_pcm_format_t sample_format,unsigned int samples_per_frame,unsigned int frames_per_second,unsigned int frames_per_buffer,void * frame_buffer,unsigned int frame_count,int * cntr_fds,unsigned int cntr_count)160 static int test_mux(struct mapper_trial *trial, snd_pcm_access_t access,
161 snd_pcm_format_t sample_format,
162 unsigned int samples_per_frame,
163 unsigned int frames_per_second,
164 unsigned int frames_per_buffer,
165 void *frame_buffer, unsigned int frame_count,
166 int *cntr_fds, unsigned int cntr_count)
167 {
168 struct container_context *cntrs = trial->cntrs;
169 unsigned int bytes_per_sample;
170 uint64_t total_frame_count;
171 int i;
172 int err = 0;
173
174 for (i = 0; i < cntr_count; ++i) {
175 snd_pcm_format_t format;
176 unsigned int channels;
177 unsigned int rate;
178
179 err = container_parser_init(cntrs + i, cntr_fds[i], 0);
180 if (err < 0)
181 goto end;
182
183 format = sample_format;
184 rate = frames_per_second;
185 if (cntr_count > 1)
186 channels = 1;
187 else
188 channels = samples_per_frame;
189 err = container_context_pre_process(cntrs + i, &format,
190 &channels, &rate,
191 &total_frame_count);
192 if (err < 0)
193 goto end;
194
195 assert(format == sample_format);
196 assert(rate == frames_per_second);
197 assert(total_frame_count == frame_count);
198 if (cntr_count > 1)
199 assert(channels == 1);
200 else
201 assert(channels == samples_per_frame);
202 }
203
204 bytes_per_sample = snd_pcm_format_physical_width(sample_format) / 8;
205 test_muxer(&trial->mapper, access, bytes_per_sample, samples_per_frame,
206 frames_per_buffer, frame_buffer, frame_count, cntrs,
207 cntr_count, trial->verbose);
208
209 for (i = 0; i < cntr_count; ++i) {
210 container_context_post_process(cntrs + i, &total_frame_count);
211 assert(total_frame_count == frame_count);
212 }
213 end:
214 for (i = 0; i < cntr_count; ++i)
215 container_context_destroy(cntrs + i);
216
217 return err;
218 }
219
test_mapper(struct mapper_trial * trial,snd_pcm_access_t access,snd_pcm_format_t sample_format,unsigned int samples_per_frame,unsigned int frames_per_second,void * frame_buffer,void * check_buffer,unsigned int frame_count,unsigned int cntr_count)220 static int test_mapper(struct mapper_trial *trial, snd_pcm_access_t access,
221 snd_pcm_format_t sample_format,
222 unsigned int samples_per_frame,
223 unsigned int frames_per_second, void *frame_buffer,
224 void *check_buffer, unsigned int frame_count,
225 unsigned int cntr_count)
226 {
227 int *cntr_fds;
228 unsigned int frames_per_buffer;
229 int i;
230 int err;
231
232 // Use a buffer aligned by typical size of page frame.
233 frames_per_buffer = ((frame_count + 4096) / 4096) * 4096;
234
235 cntr_fds = calloc(cntr_count, sizeof(*cntr_fds));
236 if (cntr_fds == NULL)
237 return -ENOMEM;
238
239 for (i = 0; i < cntr_count; ++i) {
240 const char *path = trial->paths[i];
241
242 #ifdef HAVE_MEMFD_CREATE
243 cntr_fds[i] = memfd_create(path, 0);
244 #else
245 cntr_fds[i] = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
246 #endif
247 if (cntr_fds[i] < 0) {
248 err = -errno;
249 goto end;
250 }
251 }
252
253 err = test_demux(trial, access, sample_format, samples_per_frame,
254 frames_per_second, frames_per_buffer, frame_buffer,
255 frame_count, cntr_fds, cntr_count);
256 if (err < 0)
257 goto end;
258
259 for (i = 0; i < cntr_count; ++i) {
260 off64_t pos = lseek64(cntr_fds[i], 0, SEEK_SET);
261 if (pos != 0) {
262 err = -EIO;
263 goto end;
264 }
265 }
266
267 err = test_mux(trial, access, sample_format, samples_per_frame,
268 frames_per_second, frames_per_buffer, check_buffer,
269 frame_count, cntr_fds, cntr_count);
270 end:
271 for (i = 0; i < cntr_count; ++i)
272 close(cntr_fds[i]);
273
274 free(cntr_fds);
275
276 return err;
277 }
278
test_i_buf(struct mapper_trial * trial,snd_pcm_access_t access,snd_pcm_format_t sample_format,unsigned int samples_per_frame,unsigned int frames_per_second,void * frame_buffer,unsigned int frame_count,unsigned int cntr_count)279 static int test_i_buf(struct mapper_trial *trial, snd_pcm_access_t access,
280 snd_pcm_format_t sample_format,
281 unsigned int samples_per_frame,
282 unsigned int frames_per_second, void *frame_buffer,
283 unsigned int frame_count, unsigned int cntr_count)
284 {
285 unsigned int size;
286 char *buf;
287 int err;
288
289 size = frame_count * samples_per_frame *
290 snd_pcm_format_physical_width(sample_format) / 8;
291 buf = malloc(size);
292 if (buf == 0)
293 return -ENOMEM;
294 memset(buf, 0, size);
295
296 // Test multiple target.
297 err = test_mapper(trial, access, sample_format, samples_per_frame,
298 frames_per_second, frame_buffer, buf,
299 frame_count, cntr_count);
300 if (err < 0)
301 goto end;
302 err = memcmp(frame_buffer, buf, size);
303 assert(err == 0);
304
305 // Test single target.
306 err = test_mapper(trial, access, sample_format, samples_per_frame,
307 frames_per_second, frame_buffer, buf,
308 frame_count, 1);
309 if (err < 0)
310 goto end;
311 err = memcmp(frame_buffer, buf, size);
312 assert(err == 0);
313 end:
314 free(buf);
315
316 return err;
317 }
318
test_vector(struct mapper_trial * trial,snd_pcm_access_t access,snd_pcm_format_t sample_format,unsigned int samples_per_frame,unsigned int frames_per_second,void * frame_buffer,unsigned int frame_count,unsigned int cntr_count)319 static int test_vector(struct mapper_trial *trial, snd_pcm_access_t access,
320 snd_pcm_format_t sample_format,
321 unsigned int samples_per_frame,
322 unsigned int frames_per_second, void *frame_buffer,
323 unsigned int frame_count, unsigned int cntr_count)
324 {
325 unsigned int size;
326 char **bufs;
327 int i;
328 int err;
329
330 bufs = calloc(cntr_count, sizeof(*bufs));
331 if (bufs == NULL)
332 return -ENOMEM;
333
334 size = frame_count * snd_pcm_format_physical_width(sample_format) / 8;
335
336 for (i = 0; i < cntr_count; ++i) {
337 bufs[i] = malloc(size);
338 if (bufs[i] == NULL) {
339 err = -ENOMEM;
340 goto end;
341 }
342 memset(bufs[i], 0, size);
343 }
344
345 // Test multiple target.
346 err = test_mapper(trial, access, sample_format, samples_per_frame,
347 frames_per_second, frame_buffer, bufs,
348 frame_count, cntr_count);
349 if (err < 0)
350 goto end;
351 for (i = 0; i < cntr_count; ++i) {
352 char **target = frame_buffer;
353 err = memcmp(target[i], bufs[i], size);
354 assert(err == 0);
355 }
356
357 // Test single target.
358 err = test_mapper(trial, access, sample_format, samples_per_frame,
359 frames_per_second, frame_buffer, bufs,
360 frame_count, 1);
361 if (err < 0)
362 goto end;
363 for (i = 0; i < cntr_count; ++i) {
364 char **target = frame_buffer;
365 err = memcmp(target[i], bufs[i], size);
366 assert(err == 0);
367 }
368 end:
369 for (i = 0; i < cntr_count; ++i) {
370 if (bufs[i])
371 free(bufs[i]);
372 }
373 free(bufs);
374
375 return err;
376 }
377
test_n_buf(struct mapper_trial * trial,snd_pcm_access_t access,snd_pcm_format_t sample_format,unsigned int samples_per_frame,unsigned int frames_per_second,void * frame_buffer,unsigned int frame_count,unsigned int cntr_count)378 static int test_n_buf(struct mapper_trial *trial, snd_pcm_access_t access,
379 snd_pcm_format_t sample_format,
380 unsigned int samples_per_frame,
381 unsigned int frames_per_second, void *frame_buffer,
382 unsigned int frame_count, unsigned int cntr_count)
383 {
384 char *test_buf = frame_buffer;
385 unsigned int size;
386 char **test_vec;
387 int i;
388 int err;
389
390 size = frame_count * snd_pcm_format_physical_width(sample_format) / 8;
391
392 test_vec = calloc(cntr_count * 2, sizeof(*test_vec));
393 if (test_vec == NULL)
394 return -ENOMEM;
395
396 for (i = 0; i < cntr_count; ++i)
397 test_vec[i] = test_buf + size * i;
398
399 err = test_vector(trial, access, sample_format, samples_per_frame,
400 frames_per_second, test_vec, frame_count, cntr_count);
401 free(test_vec);
402
403 return err;
404 }
405
callback(struct test_generator * gen,snd_pcm_access_t access,snd_pcm_format_t sample_format,unsigned int samples_per_frame,void * frame_buffer,unsigned int frame_count)406 static int callback(struct test_generator *gen, snd_pcm_access_t access,
407 snd_pcm_format_t sample_format,
408 unsigned int samples_per_frame, void *frame_buffer,
409 unsigned int frame_count)
410 {
411
412 int (*handler)(struct mapper_trial *trial, snd_pcm_access_t access,
413 snd_pcm_format_t sample_format,
414 unsigned int samples_per_frame,
415 unsigned int frames_per_second, void *frame_buffer,
416 unsigned int frame_count, unsigned int cntr_count);
417 struct mapper_trial *trial = gen->private_data;
418
419 if (access == SND_PCM_ACCESS_RW_NONINTERLEAVED)
420 handler = test_vector;
421 else if (access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED)
422 handler = test_n_buf;
423 else
424 handler = test_i_buf;
425
426 return handler(trial, access, sample_format, samples_per_frame, 48000,
427 frame_buffer, frame_count, samples_per_frame);
428 };
429
main(int argc,const char * argv[])430 int main(int argc, const char *argv[])
431 {
432 // Test 8/16/18/20/24/32/64 bytes per sample.
433 static const uint64_t sample_format_mask =
434 (1ull << SND_PCM_FORMAT_U8) |
435 (1ull << SND_PCM_FORMAT_S16_LE) |
436 (1ull << SND_PCM_FORMAT_S18_3LE) |
437 (1ull << SND_PCM_FORMAT_S20_3LE) |
438 (1ull << SND_PCM_FORMAT_S24_LE) |
439 (1ull << SND_PCM_FORMAT_S32_LE) |
440 (1ull << SND_PCM_FORMAT_FLOAT64_LE);
441 uint64_t access_mask;
442 struct test_generator gen = {0};
443 struct mapper_trial *trial;
444 struct container_context *cntrs;
445 unsigned int samples_per_frame;
446 char **paths = NULL;
447 snd_pcm_access_t access;
448 bool verbose;
449 int i;
450 int err;
451
452 // Test up to 32 channels.
453 samples_per_frame = 32;
454 cntrs = calloc(samples_per_frame, sizeof(*cntrs));
455 if (cntrs == NULL)
456 return -ENOMEM;
457
458 paths = calloc(samples_per_frame, sizeof(*paths));
459 if (paths == NULL) {
460 err = -ENOMEM;
461 goto end;
462 }
463 for (i = 0; i < samples_per_frame; ++i) {
464 paths[i] = malloc(8);
465 if (paths[i] == NULL) {
466 err = -ENOMEM;
467 goto end;
468 }
469 snprintf(paths[i], 8, "hoge%d", i);
470 }
471
472 if (argc > 1) {
473 char *term;
474 access = strtol(argv[1], &term, 10);
475 if (errno != 0 || *term != '\0') {
476 err = -EINVAL;;
477 goto end;
478 }
479 if (access < SND_PCM_ACCESS_MMAP_INTERLEAVED &&
480 access > SND_PCM_ACCESS_RW_NONINTERLEAVED) {
481 err = -EINVAL;
482 goto end;
483 }
484 if (access == SND_PCM_ACCESS_MMAP_COMPLEX) {
485 err = -EINVAL;
486 goto end;
487 }
488
489 access_mask = 1ull << access;
490 verbose = true;
491 } else {
492 access_mask = (1ull << SND_PCM_ACCESS_MMAP_INTERLEAVED) |
493 (1ull << SND_PCM_ACCESS_MMAP_NONINTERLEAVED) |
494 (1ull << SND_PCM_ACCESS_RW_INTERLEAVED) |
495 (1ull << SND_PCM_ACCESS_RW_NONINTERLEAVED);
496 verbose = false;
497 }
498
499 err = generator_context_init(&gen, access_mask, sample_format_mask,
500 1, samples_per_frame,
501 23, 4500, 1024,
502 sizeof(struct mapper_trial));
503 if (err < 0)
504 goto end;
505
506 trial = gen.private_data;
507 trial->cntrs = cntrs;
508 trial->cntr_format = CONTAINER_FORMAT_RIFF_WAVE;
509 trial->paths = paths;
510 trial->verbose = verbose;
511 err = generator_context_run(&gen, callback);
512
513 generator_context_destroy(&gen);
514 end:
515 if (paths) {
516 for (i = 0; i < samples_per_frame; ++i)
517 free(paths[i]);
518 free(paths);
519 }
520 free(cntrs);
521
522 if (err < 0) {
523 printf("%s\n", strerror(-err));
524 return EXIT_FAILURE;
525 }
526
527 return EXIT_SUCCESS;
528 }
529