UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
testcard.c
Go to the documentation of this file.
1
13/*
14 * Copyright (c) 2005-2006 University of Glasgow
15 * Copyright (c) 2005-2024 CESNET z.s.p.o.
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, is permitted provided that the following conditions
19 * are met:
20 *
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 *
24 * 2. Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the distribution.
27 *
28 * 3. All advertising materials mentioning features or use of this software
29 * must display the following acknowledgement:
30 *
31 * This product includes software developed by the University of Southern
32 * California Information Sciences Institute. This product also includes
33 * software developed by CESNET z.s.p.o.
34 *
35 * 4. Neither the name of the University, Institute, CESNET nor the names of
36 * its contributors may be used to endorse or promote products derived from
37 * this software without specific prior written permission.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
40 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
41 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
42 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
43 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
44 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
45 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
46 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
48 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
49 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
50 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 */
58#include <ctype.h> // for isdigit
59#include <errno.h> // for errno
60#include <math.h> // for round, sin, M_PI
61#include <stdbool.h> // for false, bool, true
62#include <stddef.h> // for ptrdiff_t
63#include <stdint.h>
64#include <stdio.h>
65#include <stdlib.h>
66#include <string.h>
67
68#include "audio/types.h"
69#include "audio/utils.h"
70#include "compat/htonl.h"
71#include "debug.h"
72#include "host.h"
73#include "lib_common.h"
74#include "tv.h"
75#include "types.h"
76#include "utils/color_out.h"
77#include "utils/macros.h"
78#include "utils/misc.h"
79#include "utils/pam.h"
80#include "utils/string.h"
81#include "utils/vf_split.h"
83#include "utils/y4m.h"
84#include "video.h"
85#include "video_capture.h"
87
88enum {
93};
94#define MOD_NAME "[testcard] "
95#define AUDIO_BUFFER_SAMPLES (AUDIO_SAMPLE_RATE * BUFFER_SEC)
96#define DEFAULT_FORMAT ((struct video_desc) { 1920, 1080, UYVY, 25.0, INTERLACED_MERGED, 1 })
97#define DEFAULT_PATTERN "bars"
98
100 int count;
101 int samples[5];
103};
104static const int alen_pattern_2997[] = { 1602, 1601, 1602, 1601, 1602 };
105static const int alen_pattern_5994[] = { 801, 801, 800, 801, 801 };
106static const int alen_pattern_11988[] = { 400, 401, 400, 401, 400 };
107_Static_assert(sizeof alen_pattern_2997 <= sizeof ((struct audio_len_pattern *) 0)->samples && sizeof alen_pattern_5994 <= sizeof ((struct audio_len_pattern *) 0)->samples, "insufficient length");
108
133
134static void
136{
137 const double scale = 0.1;
138 char *out = s->audio_data;
139
140 for (int i = 0; i < AUDIO_BUFFER_SAMPLES; i += 1) {
141 const int32_t val =
142 round(sin(((double) i / ((double) AUDIO_SAMPLE_RATE /
143 s->audio_frequency)) *
144 M_PI * 2.) *
145 INT32_MAX * scale);
146 for (int j = 0; j < s->audio.ch_count; ++j) {
147 STORE_I32_SAMPLE(out, val, s->audio.bps);
148 out += s->audio.bps;
149 }
150 }
151}
152
153static bool configure_audio(struct testcard_state *s)
154{
155 if (audio_capture_sample_rate != 0 &&
158 MOD_NAME "Requested %d Hz audio but only %d HZ is "
159 "currently supported by "
160 "this module!\n",
162 }
167 s->audio.data = s->audio_data = (char *) realloc(s->audio.data, 2 * s->audio.max_size);
169 if ((AUDIO_SAMPLE_RATE * s->fps_den) % s->fps_num == 0) {
170 s->apattern.count = 1;
172 } else if (s->fps_den == 1001 && s->fps_num == 30000) {
173 s->apattern.count = sizeof alen_pattern_2997 / sizeof alen_pattern_2997[0];
175 } else if (s->fps_den == 1001 && s->fps_num == 60000) {
176 s->apattern.count = sizeof alen_pattern_5994 / sizeof alen_pattern_5994[0];
178 } else if (s->fps_den == 1001 && s->fps_num == 120000) {
179 s->apattern.count = sizeof alen_pattern_11988 / sizeof alen_pattern_11988[0];
181 } else {
182 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Audio not implemented for %f FPS! Please report a bug if it is a common frame rate.\n", s->frame->fps);
183 return false;
184 }
185
187 memcpy(s->audio.data + s->audio.max_size, s->audio.data, s->audio.max_size);
188 s->grab_audio = true;
189
190 return true;
191}
192
193#if 0
194static int configure_tiling(struct testcard_state *s, const char *fmt)
195{
196 char *tmp, *token, *saveptr = NULL;
197 int tile_cnt;
198 int x;
199
200 int grid_w, grid_h;
201
202 if(fmt[1] != '=') return 1;
203
204 tmp = strdup(&fmt[2]);
205 token = strtok_r(tmp, "x", &saveptr);
206 grid_w = atoi(token);
207 token = strtok_r(NULL, "x", &saveptr);
208 grid_h = atoi(token);
209 free(tmp);
210
211 s->tiled = vf_alloc(grid_w * grid_h);
212 s->tiles_cnt_horizontal = grid_w;
213 s->tiles_cnt_vertical = grid_h;
215 s->tiled->fps = s->frame->fps;
217
218 tile_cnt = grid_w *
219 grid_h;
220 assert(tile_cnt >= 1);
221
222 s->tiles_data = (char **) malloc(tile_cnt *
223 sizeof(char *));
224 /* split only horizontally!!!!!! */
225 vf_split(s->tiled, s->frame, grid_w,
226 1, 1 /*prealloc*/);
227 /* for each row, make the tile data correct.
228 * .data pointers of same row point to same block,
229 * but different row */
230 for(x = 0; x < grid_w; ++x) {
231 int y;
232
233 s->tiles_data[x] = s->tiled->tiles[x].data;
234
235 s->tiled->tiles[x].width = s->frame->tiles[0].width/ grid_w;
236 s->tiled->tiles[x].height = s->frame->tiles[0].height / grid_h;
237 s->tiled->tiles[x].data_len = s->frame->tiles[0].data_len / (grid_w * grid_h);
238
239 s->tiled->tiles[x].data =
240 s->tiles_data[x] = (char *) realloc(s->tiled->tiles[x].data,
241 s->tiled->tiles[x].data_len * grid_h * 2);
242
243
244 memcpy(s->tiled->tiles[x].data + s->tiled->tiles[x].data_len * grid_h,
245 s->tiled->tiles[x].data, s->tiled->tiles[x].data_len * grid_h);
246 /* recopy tiles vertically */
247 for(y = 1; y < grid_h; ++y) {
248 memcpy(&s->tiled->tiles[y * grid_w + x],
249 &s->tiled->tiles[x], sizeof(struct tile));
250 /* make the pointers correct */
251 s->tiles_data[y * grid_w + x] =
252 s->tiles_data[x] +
253 y * s->tiled->tiles[x].height *
255
256 s->tiled->tiles[y * grid_w + x].data =
257 s->tiles_data[x] +
258 y * s->tiled->tiles[x].height *
260 }
261 }
262
263 return 0;
264}
265#endif
266
267static struct video_desc parse_format(char **fmt, char **save_ptr) {
268 struct video_desc desc = { 0 };
269 desc.tile_count = 1;
270 char *tmp = strtok_r(*fmt, ":", save_ptr);
271 if (!tmp) {
272 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Missing width!\n");
273 return (struct video_desc) { 0 };
274 }
275 desc.width = MAX(strtol(tmp, NULL, 0), 0);
276
277 if ((tmp = strtok_r(NULL, ":", save_ptr)) == NULL) {
278 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Missing height!\n");
279 return (struct video_desc) { 0 };
280 }
281 desc.height = MAX(strtol(tmp, NULL, 0), 0);
282
283 if (desc.width * desc.height == 0) {
284 fprintf(stderr, "Wrong dimensions for testcard.\n");
285 return (struct video_desc) { 0 };
286 }
287
288 if ((tmp = strtok_r(NULL, ":", save_ptr)) == NULL) {
289 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Missing FPS!\n");
290 return (struct video_desc) { 0 };
291 }
292 if (!parse_fps(tmp, &desc)) {
293 return (struct video_desc) { 0 };
294 }
295
296 if ((tmp = strtok_r(NULL, ":", save_ptr)) == NULL) {
297 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Missing pixel format!\n");
298 return (struct video_desc) { 0 };
299 }
302 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unknown codec '%s'\n", tmp);
303 return (struct video_desc) { 0 };
304 }
306 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unsupported codec '%s'\n", tmp);
307 return (struct video_desc) { 0 };
308 }
309
310 *fmt = NULL;
311 return desc;
312}
313
314static size_t testcard_load_from_file_pam(const char *filename, struct video_desc *desc, char **in_file_contents) {
315 struct pam_metadata info;
316 unsigned char *data = NULL;
317 if (pam_read(filename, &info, &data, malloc) == 0) {
318 return false;
319 }
320 switch (info.depth) {
321 case 3:
322 desc->color_spec = info.maxval == 255 ? RGB : RG48;
323 break;
324 case 4:
326 break;
327 default:
328 log_msg(LOG_LEVEL_ERROR, "Unsupported PAM/PNM channel count %d!\n", info.depth);
329 return 0;
330 }
331 desc->width = info.width;
332 desc->height = info.height;
333 size_t data_len = vc_get_datalen(desc->width, desc->height, desc->color_spec);
334 *in_file_contents = (char *) malloc(data_len);
335 if (desc->color_spec == RG48) {
336 uint16_t *in = (uint16_t *)(void *) data;
337 uint16_t *out = (uint16_t *)(void *) *in_file_contents;
338 for (size_t i = 0; i < (size_t) info.width * info.height * 3; ++i) {
339 *out++ = ntohs(*in++) * ((1<<16U) / (info.maxval + 1));
340 }
341 } else {
342 memcpy(*in_file_contents, data, data_len);
343 }
344 free(data);
345 return data_len;
346}
347
348static size_t testcard_load_from_file_y4m(const char *filename, struct video_desc *desc, char **in_file_contents) {
349 struct y4m_metadata info;
350 unsigned char *data = NULL;
351 if (y4m_read(filename, &info, &data, malloc) == 0) {
352 return 0;
353 }
354 if (info.bitdepth < DEPTH8 ||
355 (info.subsampling < Y4M_SUBS_420 ||
356 info.subsampling > Y4M_SUBS_444)) {
357 MSG(ERROR, "Only 3-channel 8+ bit Y4M is supported.\n");
358 log_msg(LOG_LEVEL_INFO, MOD_NAME "Provided Y4M picture has subsampling %d and bit depth %d bits.\n", info.subsampling, info.bitdepth);
359 return 0;
360 }
361 desc->width = info.width;
362 desc->height = info.height;
363 desc->color_spec = info.bitdepth == DEPTH8 ? UYVY : Y416;
364 size_t data_len = vc_get_datalen(desc->width, desc->height, desc->color_spec);
365 unsigned char *converted = malloc(data_len);
366
367 switch (info.subsampling) {
368 case Y4M_SUBS_420:
369 if (info.bitdepth == DEPTH8) {
371 converted);
372 } else {
373 i420_16_to_y416((int) desc->width, (int) desc->height,
374 data, converted, info.bitdepth);
375 }
376 break;
377 case Y4M_SUBS_422:
378 if (info.bitdepth == DEPTH8) {
380 converted);
381 } else {
382 i422_16_to_y416((int) desc->width, (int) desc->height,
383 data, converted, info.bitdepth);
384 }
385 break;
386 case Y4M_SUBS_444:
387 if (info.bitdepth == DEPTH8) {
389 converted);
390 } else {
391 i444_16_to_y416((int) desc->width, (int) desc->height,
392 data, converted, info.bitdepth);
393 }
394 break;
395 default:
396 MSG(ERROR, "Wrong Y4M subsampling: %d", info.subsampling);
397 free(converted);
398 return 0;
399 }
400 *in_file_contents = (char *) converted;
401 free(data);
402 return data_len;
403}
404
405static size_t testcard_load_from_file(const char *filename, struct video_desc *desc, char **in_file_contents, bool deduce_pixfmt) {
406 if (ends_with(filename, ".pam") || ends_with(filename, ".pnm") || ends_with(filename, ".ppm")) {
407 return testcard_load_from_file_pam(filename, desc, in_file_contents);
408 } else if (ends_with(filename, ".y4m")) {
409 return testcard_load_from_file_y4m(filename, desc, in_file_contents);
410 }
411
412 if (deduce_pixfmt && strchr(filename, '.') != NULL && get_codec_from_file_extension(strrchr(filename, '.') + 1)) {
413 desc->color_spec = get_codec_from_file_extension(strrchr(filename, '.') + 1);
414 }
415 long data_len = vc_get_datalen(desc->width, desc->height, desc->color_spec);
416 *in_file_contents = (char *) malloc(data_len);
417 FILE *in = fopen(filename, "r");
418 if (in == NULL) {
419 log_msg(LOG_LEVEL_WARNING, MOD_NAME "%s fopen: %s\n", filename, ug_strerror(errno));
420 return 0;
421 }
422 fseek(in, 0L, SEEK_END);
423 long filesize = ftell(in);
424 if (filesize == -1) {
425 log_msg(LOG_LEVEL_WARNING, MOD_NAME "ftell: %s\n", ug_strerror(errno));
426 filesize = data_len;
427 }
428 fseek(in, 0L, SEEK_SET);
429
430 do {
431 if (data_len != filesize) {
432 int level = data_len < filesize ? LOG_LEVEL_WARNING : LOG_LEVEL_ERROR;
433 log_msg(level, MOD_NAME "Wrong file size for selected resolution"
434 "and codec. File size %ld, computed size %ld\n", filesize, data_len);
435 filesize = data_len;
436 if (level == LOG_LEVEL_ERROR) {
437 data_len = 0; break;
438 }
439 }
440
441 if (fread(*in_file_contents, filesize, 1, in) != 1) {
442 log_msg(LOG_LEVEL_ERROR, "Cannot read file %s\n", filename);
443 data_len = 0; break;
444 }
445 } while (false);
446
447 fclose(in);
448 if (data_len == 0) {
449 free(*in_file_contents);
450 *in_file_contents = NULL;
451 }
452 return data_len;
453}
454
455static void show_help(bool full) {
456 printf("testcard options:\n");
457 color_printf(TBOLD(TRED("\t-t testcard") "[:size=<width>x<height>][:fps=<fps>][:codec=<codec>]") "[:file=<filename>][:p][:s=<X>x<Y>][:i|:sf][:still][:pattern=<pattern>] " TBOLD("| -t testcard:[full]help\n"));
458 color_printf("or\n");
459 color_printf(TBOLD(TRED("\t-t testcard") ":<width>:<height>:<fps>:<codec>") "[:other_opts]\n");
460 color_printf("where\n");
461 color_printf(TBOLD("\t file ") " - use file for input data instead of predefined pattern\n"
462 "\t (raw or PAM/PNM/Y4M)\n");
463 color_printf(TBOLD("\t fps ") " - frames per second (with optional 'i' suffix for interlaced)\n");
464 color_printf(TBOLD("\t i|sf ") " - send as interlaced or segmented frame\n");
465 color_printf(TBOLD("\t mode ") " - use specified mode (use 'mode=help' for list)\n");
466 color_printf(TBOLD("\t p ") " - pan with frame\n");
467 color_printf(TBOLD("\tpattern") " - pattern to use, use \"" TBOLD("pattern=help") "\" for options\n");
468 color_printf(TBOLD("\t s ") " - split the frames into XxY separate tiles (currently defunct)\n");
469 color_printf(TBOLD("\t still ") " - send still image\n");
470 if (full) {
471 color_printf(TBOLD(" afrequency") " - embedded audio frequency\n");
472 color_printf(TBOLD("\t frames") " - total number of video "
473 "frames to capture\n");
474 }
475 color_printf("\n");
476 testcard_show_codec_help("testcard", false);
477 color_printf("\n");
478 color_printf("Examples:\n");
479 color_printf(TBOLD("\t%s -t testcard:file=picture.pam\n"), uv_argv[0]);
480 color_printf(TBOLD("\t%s -t testcard:mode=VGA\n"), uv_argv[0]);
481 color_printf(TBOLD("\t%s -t testcard:s=1920x1080:f=59.94i\n"), uv_argv[0]);
482 color_printf("\n");
483 color_printf("Default mode: %s\n", video_desc_to_string(DEFAULT_FORMAT));
484 color_printf(TBOLD("Note:") " only certain codec and generator combinations produce full-depth samples (not up-sampled 8-bit), use " TBOLD("pattern=help") " for details.\n");
485}
486
487static bool
489{
490 if (desc.width <= 0 || desc.height <= 0) {
491 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong video size, given: %dx%d\n",
493 return false;
494 }
495 if (s->audio_frequency <= 0) {
498 "Audio frequency must be positive, given: %d\n",
499 s->audio_frequency);
500 return false;
501 }
502 return true;
503}
504
506{
507 struct testcard_state *s = NULL;
508 char *filename = NULL;
509 const char *strip_fmt = NULL;
510 char *save_ptr = NULL;
511 int ret = VIDCAP_INIT_FAIL;
512 char *tmp;
513 char *in_file_contents = NULL;
514 size_t in_file_contents_size = 0;
515
516 if (strcmp(vidcap_params_get_fmt(params), "help") == 0 || strcmp(vidcap_params_get_fmt(params), "fullhelp") == 0) {
517 show_help(strcmp(vidcap_params_get_fmt(params), "fullhelp") == 0);
518 return VIDCAP_INIT_NOERR;
519 }
520
521 if ((s = calloc(1, sizeof *s)) == NULL) {
522 return VIDCAP_INIT_FAIL;
523 }
524 strncat(s->pattern, DEFAULT_PATTERN, sizeof s->pattern - 1);
526
527 char *fmt = strdup(vidcap_params_get_fmt(params));
528 char *ptr = fmt;
529
532 desc.fps = 0;
533 if (strlen(ptr) > 0 && isdigit(ptr[0])) {
534 desc = parse_format(&ptr, &save_ptr);
535 }
536
537 tmp = strtok_r(ptr, ":", &save_ptr);
538 while (tmp) {
539 if (strcmp(tmp, "p") == 0) {
540 s->pan = 48;
541 } else if (strcmp(tmp, "i") == 0) {
543 log_msg(LOG_LEVEL_WARNING, "[testcard] Deprecated 'i' option. Use format testcard:1920:1080:50i:UYVY instead!\n");
544 } else if (strcmp(tmp, "sf") == 0) {
546 log_msg(LOG_LEVEL_WARNING, "[testcard] Deprecated 'sf' option. Use format testcard:1920:1080:25sf:UYVY instead!\n");
547 } else if (strcmp(tmp, "still") == 0) {
548 s->still_image = true;
549 } else if (IS_KEY_PREFIX(tmp, "pattern")) {
550 const char *pattern = strchr(tmp, '=') + 1;
551 strncpy(s->pattern, pattern, sizeof s->pattern - 1);
552 } else if (IS_KEY_PREFIX(tmp, "codec")) {
553 desc.color_spec = get_codec_from_name(strchr(tmp, '=') + 1);
555 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong color spec: %s\n", strchr(tmp, '=') + 1);
556 goto error;
557 }
558 } else if (IS_KEY_PREFIX(tmp, "mode")) {
559 codec_t saved_codec = desc.color_spec;
560 desc = get_video_desc_from_string(strchr(tmp, '=') + 1);
561 desc.color_spec = saved_codec;
562 } else if (IS_KEY_PREFIX(tmp, "size")) {
563 if (!strncmp(tmp, "s=", 2)) {
564 MSG(WARNING, "parameter s= denotes now size "
565 "(not strip)\n");
566 }
567 tmp = strchr(tmp, '=') + 1;
568 if (isdigit(tmp[0]) && strchr(tmp, 'x') != NULL) {
569 desc.width = atoi(tmp);
570 desc.height = atoi(strchr(tmp, 'x') + 1);
571 } else {
572 struct video_desc size_dsc =
574 desc.width = size_dsc.width;
575 desc.height = size_dsc.height;
576 }
577 } else if (IS_KEY_PREFIX(tmp, "strip")) {
578 strip_fmt = strchr(tmp, '=') + 1;
579 } else if (IS_KEY_PREFIX(tmp, "fps")) {
580 if (!parse_fps(strchr(tmp, '=') + 1, &desc)) {
581 goto error;
582 }
583 } else if (IS_KEY_PREFIX(tmp, "file")) {
584 filename = strchr(tmp, '=') + 1;
585 } else if (strstr(tmp, "afrequency=") == tmp) {
586 s->audio_frequency = atoi(strchr(tmp, '=') + 1);
587 } else if (IS_KEY_PREFIX(tmp, "frames")) {
588 s->capture_frames =
589 strtoll(strchr(tmp, '=') + 1, NULL, 0);
590 } else {
591 fprintf(stderr, "[testcard] Unknown option: %s\n", tmp);
592 goto error;
593 }
594 tmp = strtok_r(NULL, ":", &save_ptr);
595 }
596
597 if (!validate_settings(s, desc)) {
598 goto error;
599 }
600
601 if (filename) {
602 if ((in_file_contents_size = testcard_load_from_file(filename, &desc, &in_file_contents, desc.color_spec == VIDEO_CODEC_NONE)) == 0) {
603 goto error;
604 }
605 }
608
610 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Planar pixel format '%s', using still picture.\n", get_codec_name(desc.color_spec));
611 s->still_image = true;
612 }
613
616
619 if (!s->generator) {
620 ret = strstr(s->pattern, "help") != NULL ? VIDCAP_INIT_NOERR : VIDCAP_INIT_FAIL;
621 goto error;
622 }
623 if (in_file_contents_size > 0) {
624 video_pattern_generator_fill_data(s->generator, in_file_contents);
625 }
626
628
629 log_msg(LOG_LEVEL_INFO, MOD_NAME "capture set to %s, bpc %d, pattern: %s, audio %s\n", video_desc_to_string(desc),
630 get_bits_per_component(s->frame->color_spec), s->pattern, (s->grab_audio ? "on" : "off"));
631
632 if (strip_fmt != NULL) {
633 log_msg(LOG_LEVEL_ERROR, "Multi-tile testcard (stripping) is currently broken, you can use eg. \"-t aggregate -t testcard[args] -t testcard[args]\" instead!\n");
634 goto error;
635#if 0
636 if(configure_tiling(s, strip_fmt) != 0) {
637 goto error;
638 }
639#endif
640 }
641
644
646 if (!configure_audio(s)) {
647 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot initialize audio!\n");
648 goto error;
649 }
650 }
651
652 free(fmt);
653 free(in_file_contents);
654
655 *state = s;
656 return VIDCAP_INIT_OK;
657
658error:
659 free(fmt);
660 vf_free(s->frame);
661 free(in_file_contents);
662 free(s);
663 return ret;
664}
665
666static void vidcap_testcard_done(void *state)
667{
668 struct testcard_state *s = (struct testcard_state *) state;
669 if (s->tiled) {
670 int i;
671 for (i = 0; i < s->tiles_cnt_horizontal; ++i) {
672 free(s->tiles_data[i]);
673 }
674 vf_free(s->tiled);
675 }
676 vf_free(s->frame);
678 free(s->audio_data);
679 free(s);
680}
681
683{
684 if (!s->grab_audio) {
685 return NULL;
686 }
687
688 s->audio.data += (ptrdiff_t)s->audio.ch_count * s->audio.bps *
691 (s->apattern.current_idx + 1) % s->apattern.count;
692 s->audio.data_len = s->audio.ch_count * s->audio.bps *
694 if (s->audio.data >=
695 s->audio_data + s->audio.max_size) {
696 s->audio.data -= s->audio.max_size;
697 }
698 s->audio.timestamp =
699 ((int64_t)s->audio_frames * 90000 + AUDIO_SAMPLE_RATE - 1) /
701 s->audio_frames +=
702 s->audio.data_len / (s->audio.ch_count * s->audio.bps);
703 return &s->audio;
704}
705
706static struct video_frame *vidcap_testcard_grab(void *arg, struct audio_frame **audio)
707{
708 struct testcard_state *state = arg;
709
710 if (state->video_frames + 1 == state->capture_frames) {
711 return NULL;
712 }
713 time_ns_t curr_time = 0;
714 do {
715 curr_time = get_time_in_ns();
716 } while ((double) (curr_time - state->last_frame_time) / NS_IN_SEC <
717 1.0 / state->frame->fps);
718 state->last_frame_time = curr_time;
719 state->frame->timestamp =
720 (state->video_frames * state->fps_den * 90000 + state->fps_num - 1) /
721 state->fps_num;
722
724
726
727 if (state->tiled) {
728 /* update tile data instead */
729 int i;
730 int count = state->tiled->tile_count;
731
732 for (i = 0; i < count; ++i) {
733 /* shift - for semantics of vars refer to configure_tiling*/
734 state->tiled->tiles[i].data += vc_get_linesize(
735 state->tiled->tiles[i].width, state->tiled->color_spec);
736 /* if out of data, move to beginning
737 * keep in mind that we have two "pictures" for
738 * every tile stored sequentially */
739 if(state->tiled->tiles[i].data >= state->tiles_data[i] +
740 state->tiled->tiles[i].data_len * state->tiles_cnt_vertical) {
741 state->tiled->tiles[i].data = state->tiles_data[i];
742 }
743 }
744
745 return state->tiled;
746 }
747
748 state->video_frames += 1;
749 return state->frame;
750}
751
752static void vidcap_testcard_probe(struct device_info **available_devices, int *count, void (**deleter)(void *))
753{
754 *deleter = free;
755
756 *count = 1;
757 *available_devices = (struct device_info *) calloc(*count, sizeof(struct device_info));
758 struct device_info *card = *available_devices;
759 snprintf(card->name, sizeof card->name, "Testing signal");
760
761 struct size {
762 int width;
763 int height;
764 } sizes[] = {
765 {1280, 720},
766 {1920, 1080},
767 {3840, 2160},
768 };
769 int framerates[] = {24, 30, 60};
770 const char * const pix_fmts[] = {"UYVY", "RGB"};
771
772 snprintf(card->modes[0].name,
773 sizeof card->modes[0].name, "Default");
774 snprintf(card->modes[0].id,
775 sizeof card->modes[0].id,
776 "{\"width\":\"\", "
777 "\"height\":\"\", "
778 "\"format\":\"\", "
779 "\"fps\":\"\"}");
780
781 int i = 1;
782 for (const char * const *pix_fmt = pix_fmts; pix_fmt != pix_fmts + sizeof pix_fmts / sizeof pix_fmts[0]; pix_fmt++) {
783 for (const struct size *size = sizes; size != sizes + sizeof sizes / sizeof sizes[0]; size++) {
784 for (const int *fps = framerates; fps != framerates + sizeof framerates / sizeof framerates[0]; fps++) {
785 snprintf(card->modes[i].name,
786 sizeof card->name,
787 "%dx%d@%d %s",
788 size->width, size->height,
789 *fps, *pix_fmt);
790 snprintf(card->modes[i].id,
791 sizeof card->modes[0].id,
792 "{\"width\":\"%d\", "
793 "\"height\":\"%d\", "
794 "\"format\":\"%s\", "
795 "\"fps\":\"%d\"}",
796 size->width, size->height,
797 *pix_fmt, *fps);
798 i++;
799 }
800 }
801 }
802 dev_add_option(card, "Still", "Send still image", "still", ":still", true);
803 dev_add_option(card, "Pattern", "Pattern to use", "pattern", ":pattern=", false);
804}
805
813
815
816/* vim: set expandtab sw=8: */
void ** state
int void * val
struct video_desc desc
const char * fmt
struct audio_frame ** audio
int void(** deleter)(void *))
struct video_frame * vf_alloc(int count)
Allocates blank video frame.
void vf_free(struct video_frame *buf)
Frees video_frame structure.
int vc_get_linesize(unsigned int width, codec_t codec)
struct video_frame * vf_alloc_desc(struct video_desc desc)
Allocates video frame accordig given desc.
const char * get_codec_name(codec_t codec)
codec_t get_codec_from_name(const char *name)
pattern
Definition testcard.c:94
#define MOD_NAME
Definition testcard.c:38
#define DEFAULT_AUDIO_BPS
Definition testcard.c:66
This file contains definition of some common audio types.
#define STORE_I32_SAMPLE(ptr, val, out_bps)
stores given int32_t sample at required out_bps
Definition utils.h:143
static void show_help()
Definition wasapi.cpp:193
int color_printf(const char *format,...)
#define TRED(x)
Definition color_out.h:75
#define TBOLD(x)
Definition color_out.h:70
static __device__ vec3 round(const vec3 &v)
Definition cuda_dxt.cu:122
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define MSG(l, fmt,...)
Definition debug.h:96
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
int count
Definition hd-rum.c:44
unsigned int audio_capture_bps
Definition host.cpp:164
unsigned int audio_capture_sample_rate
Definition host.cpp:165
char ** uv_argv
Definition host.cpp:174
unsigned int audio_capture_channels
Definition host.cpp:163
#define DEFAULT_AUDIO_CAPTURE_CHANNELS
Definition host.h:89
@ LIBRARY_CLASS_VIDEO_CAPTURE
Definition lib_common.h:78
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
#define IS_KEY_PREFIX(tok, key)
expands to true value if <k> from tok in format <k>=<v> is prefix of key
Definition macros.h:138
#define IF_NOT_NULL_ELSE(val, default_val)
Definition macros.h:51
#define MAX(a, b)
Definition macros.h:65
int get_framerate_d(double fps)
Definition misc.cpp:190
int get_framerate_n(double fps)
Definition misc.cpp:184
const char * ug_strerror(int errnum)
Definition misc.cpp:203
bool pam_read(const char *filename, struct pam_metadata *info, unsigned char **data, void *(*allocator)(size_t))
Definition pam.c:141
bool ends_with(const char *haystick, const char *needle)
Definition string.c:109
struct used to comunicate with audio capture/playback device
Definition types.h:99
int sample_rate
Definition types.h:101
int64_t timestamp
Definition types.h:109
char * data
Definition types.h:102
int ch_count
Definition types.h:104
int data_len
Definition types.h:103
int flags
Definition types.h:111
int bps
Definition types.h:100
int max_size
Definition types.h:105
char name[1024]
human readable name of the mode
Definition types.h:395
char id[1024]
< optional zero-terminated array of available modes
Definition types.h:392
char name[1024]
human readable name of the device
Definition types.h:385
struct device_info::mode modes[512]
int tiles_cnt_horizontal
Definition testcard.c:125
struct audio_frame audio
Definition testcard.c:119
char * audio_data
Definition testcard.c:128
long long video_frames
Definition testcard.c:111
struct video_frame * tiled
Definition testcard.c:116
char pattern[128]
Definition testcard.c:131
video_pattern_generator_t generator
Definition testcard.c:114
int tiles_cnt_vertical
Definition testcard.c:126
time_ns_t last_frame_time
Definition testcard.c:112
long long capture_frames
Definition testcard.c:122
char ** tiles_data
Definition testcard.c:124
struct audio_len_pattern apattern
Definition testcard.c:120
int audio_frequency
Definition testcard.c:121
struct video_frame * frame
Definition testcard.c:115
long long audio_frames
Definition testcard.c:110
Struct tile is an area of video_frame.
Definition types.h:279
unsigned int width
Definition types.h:280
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
unsigned int height
Definition types.h:281
unsigned int tile_count
Definition types.h:185
codec_t color_spec
Definition types.h:182
enum interlacing_t interlacing
Definition types.h:184
double fps
Definition types.h:183
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
enum interlacing_t interlacing
Definition types.h:307
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
double fps
Definition types.h:308
int flags
Definition types.h:329
codec_t color_spec
Definition types.h:306
void testcard_show_codec_help(const char *name, bool src_8b_only)
bool testcard_has_conversion(codec_t c)
long long time_ns_t
Definition tv.h:81
#define NS_IN_SEC
Definition tv.h:92
static time_ns_t get_time_in_ns()
Definition tv.h:101
This file contains some common data types.
@ DEPTH8
Definition types.h:126
@ TIMESTAMP_VALID
timestamp set by source (in 90 kHz clock)
Definition types.h:223
void dev_add_option(struct device_info *dev, const char *name, const char *desc, const char *key, const char *opt_str, bool is_boolean)
@ SEGMENTED_FRAME
Segmented frame. Contains the same data as progressive frame.
Definition types.h:168
@ INTERLACED_MERGED
Columngs of both fields are interlaced together.
Definition types.h:167
codec_t
Definition types.h:65
@ RGB
RGB 8-bit (packed into 24-bit word)
Definition types.h:80
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
Definition types.h:71
@ VIDEO_CODEC_NONE
dummy color spec
Definition types.h:66
@ RG48
16-bit RGB little-endian (RlRhGl...)
Definition types.h:95
@ Y416
interleaved little-endian YCbCr 4444 16-bit - UYVA
Definition types.h:99
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
void vf_split(struct video_frame *out, struct video_frame *src, unsigned int x_count, unsigned int y_count, int preallocate)
Definition vf_split.cpp:51
const char * video_desc_to_string(struct video_desc d)
Definition video.cpp:241
static double parse_fps(const char *string, bool interlaced)
Definition video.cpp:253
struct video_desc get_video_desc_from_string(const char *string)
Definition video.cpp:291
This is an umbrella header for video functions.
static const struct video_capture_info vidcap_testcard_info
Definition testcard.c:806
static void generate_audio_sine(struct testcard_state *s)
Definition testcard.c:135
static const int alen_pattern_5994[]
Definition testcard.c:105
static struct video_desc parse_format(char **fmt, char **save_ptr)
Definition testcard.c:267
static bool configure_audio(struct testcard_state *s)
Definition testcard.c:153
static int vidcap_testcard_init(struct vidcap_params *params, void **state)
Definition testcard.c:505
#define DEFAULT_FORMAT
Definition testcard.c:96
#define AUDIO_BUFFER_SAMPLES
Definition testcard.c:95
static size_t testcard_load_from_file_y4m(const char *filename, struct video_desc *desc, char **in_file_contents)
Definition testcard.c:348
static const int alen_pattern_2997[]
Definition testcard.c:104
static size_t testcard_load_from_file(const char *filename, struct video_desc *desc, char **in_file_contents, bool deduce_pixfmt)
Definition testcard.c:405
#define DEFAULT_PATTERN
Definition testcard.c:97
static size_t testcard_load_from_file_pam(const char *filename, struct video_desc *desc, char **in_file_contents)
Definition testcard.c:314
@ DEFAULT_AUIDIO_FREQUENCY
Definition testcard.c:92
@ BUFFER_SEC
Definition testcard.c:90
@ AUDIO_SAMPLE_RATE
Definition testcard.c:89
static void vidcap_testcard_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
Definition testcard.c:752
static bool validate_settings(struct testcard_state *s, struct video_desc desc)
Definition testcard.c:488
static const int alen_pattern_11988[]
Definition testcard.c:106
static audio_frame * vidcap_testcard_get_audio(struct testcard_state *s)
Definition testcard.c:682
static struct video_frame * vidcap_testcard_grab(void *arg, struct audio_frame **audio)
Definition testcard.c:706
static void vidcap_testcard_done(void *state)
Definition testcard.c:666
const char * vidcap_params_get_fmt(const struct vidcap_params *params)
unsigned int vidcap_params_get_flags(const struct vidcap_params *params)
#define VIDCAP_FLAG_AUDIO_ANY
void i420_8_to_uyvy(int width, int height, const unsigned char *in, unsigned char *out)
void i444_8_to_uyvy(int width, int height, const unsigned char *in, unsigned char *out)
void i422_16_to_y416(int width, int height, const unsigned char *in, unsigned char *out, int in_depth)
void i420_16_to_y416(int width, int height, const unsigned char *in, unsigned char *out, int in_depth)
void i422_8_to_uyvy(int width, int height, const unsigned char *in, unsigned char *out)
bool codec_is_planar(codec_t codec)
size_t vc_get_datalen(unsigned int width, unsigned int height, codec_t codec)
int get_bits_per_component(codec_t codec)
codec_t get_codec_from_file_extension(const char *ext)
void i444_16_to_y416(int width, int height, const unsigned char *in, unsigned char *out, int in_depth)
struct tile * vf_get_tile(struct video_frame *buf, int pos)
Returns pointer to n-th tile from video frame Equivalent to &video_frame::tiles[pos].
void video_pattern_generator_fill_data(video_pattern_generator_t s, const char *data)
char * video_pattern_generator_next_frame(video_pattern_generator_t s)
video_pattern_generator_t video_pattern_generator_create(const char *config, int width, int height, codec_t color_spec, int offset)
void video_pattern_generator_destroy(video_pattern_generator_t s)
size_t y4m_read(const char *filename, struct y4m_metadata *info, unsigned char **data, void *(*allocator)(size_t))
Definition y4m.c:77
@ Y4M_SUBS_444
Definition y4m.h:53
@ Y4M_SUBS_422
Definition y4m.h:52
@ Y4M_SUBS_420
Definition y4m.h:51