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