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
29struct 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
39static 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
69static 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	}
123end:
124	for (i = 0; i < cntr_count; ++i)
125		container_context_destroy(cntrs + i);
126
127	return err;
128}
129
130static 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
160static 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	}
213end:
214	for (i = 0; i < cntr_count; ++i)
215		container_context_destroy(cntrs + i);
216
217	return err;
218}
219
220static 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		off_t pos = lseek(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);
270end:
271	for (i = 0; i < cntr_count; ++i)
272		close(cntr_fds[i]);
273
274	free(cntr_fds);
275
276	return err;
277}
278
279static 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);
313end:
314	free(buf);
315
316	return err;
317}
318
319static 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	}
368end:
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
378static 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
406static 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
430int 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);
514end:
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