UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
swmix.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2023 CESNET z.s.p.o.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, is permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of CESNET nor the names of its contributors may be
17 * used to endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
22 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
23 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
24 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
46#include <assert.h>
47#include <math.h>
48#include <pthread.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52
53#include "audio/types.h"
54#include "debug.h"
55#include "gl_context.h"
56#include "host.h"
57#include "lib_common.h"
58#include "tv.h"
59#include "utils/config_file.h"
60#include "utils/fs.h"
61#include "utils/list.h"
62#include "utils/macros.h"
63#include "video.h"
64#include "video_capture.h"
65
66#define MAX_AUDIO_LEN (1024*1024)
67
72
73/*
74 * Bicubic interpolation taken from:
75 * http://www.codeproject.com/Articles/236394/Bi-Cubic-and-Bi-Linear-Interpolation-with-GLSL
76 */
77static const char * bicubic_template = STRINGIFY(
78uniform sampler2D textureSampler;
79uniform float fWidth;
80uniform float fHeight;
81float CatMullRom( float x )
82{
83 const float B = 0.0;
84 const float C = 0.5;
85 float f = x;
86 if( f < 0.0 )
87 {
88 f = -f;
89 }
90 if( f < 1.0 )
91 {
92 return ( ( 12.0 - 9.0 * B - 6.0 * C ) * ( f * f * f ) +
93 ( -18.0 + 12.0 * B + 6.0 *C ) * ( f * f ) +
94 ( 6.0 - 2.0 * B ) ) / 6.0;
95 }
96 else if( f >= 1.0 && f < 2.0 )
97 {
98 return ( ( -B - 6.0 * C ) * ( f * f * f )
99 + ( 6.0 * B + 30.0 * C ) * ( f *f ) +
100 ( - ( 12.0 * B ) - 48.0 * C ) * f +
101 8.0 * B + 24.0 * C)/ 6.0;
102 }
103 else
104 {
105 return 0.0;
106 }
107}
108float BSpline( float x )
109{
110 float f = x;
111 if( f < 0.0 )
112 {
113 f = -f;
114 }
115
116 if( f >= 0.0 && f <= 1.0 )
117 {
118 return ( 2.0 / 3.0 ) + ( 0.5 ) * ( f* f * f ) - (f*f);
119 }
120 else if( f > 1.0 && f <= 2.0 )
121 {
122 return 1.0 / 6.0 * pow( ( 2.0 - f ), 3.0 );
123 }
124 return 1.0;
125}
126float Triangular( float f )
127{
128 f = f / 2.0;
129 if( f < 0.0 )
130 {
131 return ( f + 1.0 );
132 }
133 else
134 {
135 return ( 1.0 - f );
136 }
137 return 0.0;
138}
139void main()
140{
141 float texelSizeX = 1.0 / fWidth;
142 float texelSizeY = 1.0 / fHeight;
143 vec4 nSum = vec4( 0.0, 0.0, 0.0, 0.0 );
144 vec4 nDenom = vec4( 0.0, 0.0, 0.0, 0.0 );
145 float a = fract( gl_TexCoord[0].x * fWidth );
146 float b = fract( gl_TexCoord[0].y * fHeight );
147 for( int m = -1; m <=2; m++ )
148 {
149 for( int n =-1; n<= 2; n++)
150 {
151 vec4 vecData = texture2D(textureSampler,
152 gl_TexCoord[0].xy + vec2(texelSizeX * float( m ),
153 texelSizeY * float( n )));
154 float f = INTERP_ALGORITHM_PLACEHOLDER( float( m ) - a );
155 vec4 vecCooef1 = vec4( f,f,f,f );
156 float f1 = INTERP_ALGORITHM_PLACEHOLDER( -( float( n ) - b ) );
157 vec4 vecCoeef2 = vec4( f1, f1, f1, f1 );
158 nSum = nSum + ( vecData * vecCoeef2 * vecCooef1 );
159 nDenom = nDenom + (( vecCoeef2 * vecCooef1 ));
160 }
161 }
162 gl_FragColor = nSum / nDenom;
163});
164
165/* prototypes of functions defined in this module */
166static void show_help(void);
167static void *master_worker(void *arg);
168static void *slave_worker(void *arg);
169static char *get_config_name(void);
170static bool get_slave_param_from_file(FILE* config, const char *slave_name, int *x, int *y,
171 int *width, int *height);
172static bool get_device_config_from_file(FILE* config_file, char *slave_name,
173 char *device_name_config) __attribute__((unused));
174static void vidcap_swmix_done(void *state);
175
176static char *get_config_name()
177{
178 const char *rc_suffix = "/.ug-swmix.rc";
179 static char buf[MAX_PATH_SIZE];
180 if(!getenv("HOME")) {
181 return NULL;
182 }
183
184 strncpy(buf, getenv("HOME"), sizeof(buf) - 1);
185 strncat(buf, rc_suffix, sizeof(buf) - 1);
186 return buf;
187}
188
189static void show_help()
190{
191 printf("SW Mix capture\n");
192 printf("Usage\n");
193 printf("\t-t swmix:<width>:<height>:<fps>[:<codec>[:interpolation=<i_type>[,<algo>]][:layout=<X>x<Y>]] "
194 "-t <dev1_config> -t <dev2_config>\n");
195 printf("\tor\n");
196 printf("\t-t swmix:file[=<file_path>] -t <dev1_config> -t <dev2_config> ...\n");
197 printf("\t\twhere <devn_config> is a configuration string of device as usual -\n"
198 "\t\t\tdevice config string or alias from UG config file.\n");
199 printf("\t\t<width> width of resulting video\n");
200 printf("\t\t<height> height of resulting video\n");
201 printf("\t\t<fps> FPS of resulting video, may be eg. 25 or 50i\n");
202 printf("\t\t<codec> codec of resulting video, may be one of RGBA, "
203 "RGB or UYVY (optional, default RGBA)\n");
204 printf("\t\t<i_type> can be one of 'bilinear' or 'bicubic' (default)\n");
205 printf("\t\t\t<algo> bicubic interpolation algorithm: CatMullRom, BSpline (default) or Triangular\n");
206 printf("\n");
207 printf("\t\tIn first variant, individual inputs are arranged automatically.\n");
208 printf("\t\tWith the second variant, you provide overall layout and layout for \n"
209 "\t\tindividual inputs in SW mix config file (%s).\n", get_config_name());
210}
211
224
265
266
267static void vidcap_swmix_probe(struct device_info **available_cards, int *count, void (**deleter)(void *))
268{
269 *deleter = free;
270 *available_cards = NULL;
271 *count = 0;
272}
273
277 float posX[4];
278 float posY[4];
279 GLuint texture[2]; // RGB(A), (UYVY)
280 GLuint fbo; // RGB(A)
281 double x, y, width, height; // in 1x1 unit space
282 double fb_aspect;
283
286};
287
288static struct slave_data *init_slave_data(struct vidcap_swmix_state *s, FILE *config) {
289 struct slave_data *slaves_data = (struct slave_data *)
290 calloc(s->devices_cnt, sizeof(struct slave_data));
291 double m;
292 int n;
293
294 if (s->grid_x != 0) {
295 m = s->grid_x;
296 n = s->grid_y;
297 } else {
298 // arrangement
299 // we want to have least MxN, where N <= M + 1
300 m = (-1.0 + sqrt(1.0 + 4.0 * s->devices_cnt)) / 2.0;
301 m = ceil(m);
302 n = (s->devices_cnt + m - 1) / ((int) m);
303 }
304
305 for(int i = 0; i < s->devices_cnt; ++i) {
306 glGenTextures(2, slaves_data[i].texture);
307 for(int j = 0; j < 2; ++j) {
308 glBindTexture(GL_TEXTURE_2D, slaves_data[i].texture[j]);
309 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
310 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
311 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
312 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
313 }
314
315 glGenFramebuffers(1, &slaves_data[i].fbo);
316
317 slaves_data[i].fb_aspect = (double) s->frame->tiles[0].width /
318 s->frame->tiles[0].height;
319
320 if(!s->use_config_file) {
321 slaves_data[i].width = 1.0 / (int) m;
322 slaves_data[i].height = 1.0 / n;
323 slaves_data[i].x = (i % (int) m) * slaves_data[i].width;
324 slaves_data[i].y = (i / (int) m) * slaves_data[i].height;
325 } else {
326 int x, y, width, height;
327 if(!get_slave_param_from_file(config,
329 &x, &y, &width, &height)) {
330 fprintf(stderr, "Cannot find config for device \"%s\"\n",
332 free(slaves_data);
333 return NULL;
334 }
335 slaves_data[i].width = (double) width /
336 s->frame->tiles[0].width;
337 slaves_data[i].height = (double) height /
338 s->frame->tiles[0].height;
339 slaves_data[i].x = (double) x /
340 s->frame->tiles[0].width;
341 slaves_data[i].y = (double) y /
342 s->frame->tiles[0].height;
343 }
344 }
345
346 return slaves_data;
347}
348
349static void destroy_slave_data(struct slave_data *data, int count) {
350 if (!data) {
351 return;
352 }
353 for(int i = 0; i < count; ++i) {
354 glDeleteTextures(2, data[i].texture);
355 glDeleteFramebuffers(1, &data[i].fbo);
356 }
357 free(data);
358}
359
361{
362 glBindTexture(GL_TEXTURE_2D, s->texture[0]);
363 switch (desc.color_spec) {
364 case UYVY:
365 case RGBA:
366 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, desc.width, desc.height,
367 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
368 break;
369 case RGB:
370 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, desc.width, desc.height,
371 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
372 break;
373 case BGR:
374 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, desc.width, desc.height,
375 0, GL_BGR, GL_UNSIGNED_BYTE, NULL);
376 break;
377 default:
378 fprintf(stderr, "SW mix: Unsupported color spec.\n");
379 exit_uv(1);
380 }
381
382 if(desc.color_spec == UYVY) {
383 glBindTexture(GL_TEXTURE_2D, s->texture[1]);
384 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, desc.width / 2, desc.height,
385 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
386 }
387 glBindTexture(GL_TEXTURE_2D, 0);
388
389 double video_aspect = (double) desc.width / desc.height;
390 double fb_aspect = (double) s->fb_aspect * s->width / s->height;
391 double width = s->width;
392 double height = s->height;
393 double x = s->x;
394 double y = s->y;
395
396 if(video_aspect > fb_aspect) {
397 height = width / video_aspect * s->fb_aspect;
398 y += (s->height - height) / 2;
399 } else {
400 width = height * video_aspect / s->fb_aspect;
401 x += (s->width - width) / 2;
402 }
403
404 // left top
405 s->posX[0] = -1.0 + 2.0 * x;
406 s->posY[0] = -1.0 + 2.0 * y;
407
408 // right top
409 s->posX[1] = -1.0 + 2.0 * x + 2.0 * width;
410 s->posY[1] = -1.0 + 2.0 * y;
411
412 // right bottom
413 s->posX[2] = -1.0 + 2.0 * x + 2.0 * width;
414 s->posY[2] = -1.0 + 2.0 * y + 2.0 * height;
415
416 // left bottom
417 s->posX[3] = -1.0 + 2.0 * x;
418 s->posY[3] = -1.0 + 2.0 * y + 2.0 * height;
419}
420
421// source code for a shader unit
422#define STRINGIFY(A) #A
423static const char * fprogram_from_uyvy = STRINGIFY(
424uniform sampler2D image;
425uniform float imageWidth;
426void main()
427{
428 vec4 yuv;
429 yuv.rgba = texture2D(image, gl_TexCoord[0].xy).grba;
430 if(gl_TexCoord[0].x * imageWidth / 2.0 - floor(gl_TexCoord[0].x * imageWidth / 2.0) > 0.5)
431 yuv.r = yuv.a;
432 yuv.r = 1.1643 * (yuv.r - 0.0625);
433 yuv.g = yuv.g - 0.5;
434 yuv.b = yuv.b - 0.5;
435 gl_FragColor.r = yuv.r + 1.7926 * yuv.b;
436 gl_FragColor.g = yuv.r - 0.2132 * yuv.g - 0.5328 * yuv.b;
437 gl_FragColor.b = yuv.r + 2.1124 * yuv.g;
438});
439
440static const char * fprogram_to_uyvy = STRINGIFY(
441uniform sampler2D image;
442uniform float imageWidth;
443void main()
444{
445 vec4 rgba1;
446 vec4 rgba2;
447 vec4 yuv1;
448 vec4 yuv2;
449 vec2 coor1;
450 vec2 coor2;
451 float U;
452 float V;
453 coor1 = gl_TexCoord[0].xy - vec2(1.0 / (imageWidth * 2.0), 0.0);
454 coor2 = gl_TexCoord[0].xy + vec2(1.0 / (imageWidth * 2.0), 0.0);
455 rgba1 = texture2D(image, coor1);
456 rgba2 = texture2D(image, coor2);
457 yuv1.x = 1.0/16.0 + (rgba1.r * 0.2126 + rgba1.g * 0.7152 + rgba1.b * 0.0722) * 0.8588;
458 yuv1.y = 0.5 + (-rgba1.r * 0.1145 - rgba1.g * 0.3854 + rgba1.b * 0.5) * 0.8784;
459 yuv1.z = 0.5 + (rgba1.r * 0.5 - rgba1.g * 0.4541 - rgba1.b * 0.0458) * 0.8784;
460 yuv2.x = 1.0/16.0 + (rgba2.r * 0.2126 + rgba2.g * 0.7152 + rgba2.b * 0.0722) * 0.8588;
461 yuv2.y = 0.5 + (-rgba2.r * 0.1145 - rgba2.g * 0.3854 + rgba2.b * 0.5) * 0.8784;
462 yuv2.z = 0.5 + (rgba2.r * 0.5 - rgba2.g * 0.4541 - rgba2.b * 0.0458) * 0.8784;
463 U = mix(yuv1.y, yuv2.y, 0.5);
464 V = mix(yuv1.z, yuv2.z, 0.5);
465 gl_FragColor = vec4(U,yuv1.x, V, yuv2.x);
466});
467
468static const char * vprogram = STRINGIFY(
469void main() {
470 gl_TexCoord[0] = gl_MultiTexCoord0;
471 gl_Position = ftransform();
472});
473
475{
477
478 if(!video_desc_eq(desc, s->saved_desc)) {
479 codec_t in_codec = s->current_frame->color_spec;
480 codec_t out_codec = in_codec;
481 codec_t natively_supported[] = { RGB, BGR, RGBA, UYVY, VIDEO_CODEC_NONE };
482
483 // prepare decoder
484 if (!codec_is_in_set(desc.color_spec, natively_supported)) {
485 codec_t c;
486 if ((s->decoder = get_best_decoder_from(out_codec, natively_supported, &c)) != NULL) {
488 desc.color_spec = s->decoder_to = c;
489 }
490 }
491
494 s->saved_desc = desc;
495 }
496}
497
498static void load_texture(struct slave_data *s, GLuint from_uyvy)
499{
500 glBindTexture(GL_TEXTURE_2D, s->texture[0]);
501 int width = s->current_frame->tiles[0].width;
502 GLenum format;
503
504 codec_t in_codec = s->current_frame->color_spec;
505 codec_t out_codec = in_codec;
506 codec_t natively_supported[] = { RGB, BGR, RGBA, UYVY, VIDEO_CODEC_NONE };
507 decoder_t decoder = NULL;
508 if (!codec_is_in_set(out_codec, natively_supported)) {
509 if (s->decoder_from == in_codec) {
510 out_codec = s->decoder_to;
511 decoder = s->decoder;
512 }
513 }
514
515 switch (out_codec) {
516 case UYVY:
517 width /= 2;
518 format = GL_RGBA;
519 break;
520 case RGB:
521 format = GL_RGB;
522 break;
523 case BGR:
524 format = GL_BGR;
525 break;
526 case RGBA:
527 format = GL_RGBA;
528 break;
529 default:
530 error_msg("Unexpected color space %s!",
531 get_codec_name(out_codec));
532 abort();
533 }
534 int src_height = s->current_frame->tiles[0].height;
535 int src_width = s->current_frame->tiles[0].width;
536 unsigned char *data = (unsigned char *) s->current_frame->tiles[0].data;
537 unsigned char *tmp = NULL, *in_gl_buffer = data;
538 if (decoder) {
539 tmp = in_gl_buffer = (unsigned char *) malloc(src_height *
540 vc_get_linesize(src_width, out_codec));
541 for (int i = 0; i < src_height; ++i) {
542 decoder(tmp + i * vc_get_linesize(src_width, out_codec),
543 data + i * vc_get_linesize(src_width, in_codec),
544 vc_get_linesize(src_width, out_codec), 0, 8, 16);
545 }
546 }
547
548 if(out_codec == UYVY) {
549 glBindTexture(GL_TEXTURE_2D, s->texture[1]);
550 } else {
551 glBindTexture(GL_TEXTURE_2D, s->texture[0]);
552 }
553 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
554 width, src_height, format, GL_UNSIGNED_BYTE, in_gl_buffer);
555 free(tmp);
556
557 if(out_codec == UYVY) {
558 glUseProgram(from_uyvy);
559 glUniform1i(glGetUniformLocation(from_uyvy, "image"), 0);
560 glUniform1f(glGetUniformLocation(from_uyvy, "imageWidth"),
561 (GLfloat) s->current_frame->tiles[0].width);
562
563 glBindFramebuffer(GL_FRAMEBUFFER, s->fbo);
564 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_EXT,
565 GL_TEXTURE_2D, s->texture[0], 0);
566
567 glViewport(0, 0, s->current_frame->tiles[0].width,
568 s->current_frame->tiles[0].height);
569 glBegin(GL_QUADS);
570 glTexCoord2f(0.0, 0.0); glVertex2f(-1.0, -1.0);
571 glTexCoord2f(1.0, 0.0); glVertex2f(1.0, -1.0);
572 glTexCoord2f(1.0, 1.0); glVertex2f(1.0, 1.0);
573 glTexCoord2f(0.0, 1.0); glVertex2f(-1.0, 1.0);
574 glEnd();
575 glUseProgram(0);
576 }
577}
578
579static void render_slave(struct slave_data *s, interpolation_t interpolation, GLuint bicubic_program)
580{
581 if(interpolation == BICUBIC) {
582 glUniform1f(glGetUniformLocation(bicubic_program, "fWidth"),
583 (GLfloat) s->current_frame->tiles[0].width);
584 glUniform1f(glGetUniformLocation(bicubic_program, "fHeight"),
585 (GLfloat) s->current_frame->tiles[0].height);
586 }
587 glBindTexture(GL_TEXTURE_2D, s->texture[0]);
588
589 glBegin(GL_QUADS);
590 glTexCoord2f(0.0, 0.0); glVertex2f(s->posX[0],
591 s->posY[0]);
592 glTexCoord2f(1.0, 0.0); glVertex2f(s->posX[1],
593 s->posY[1]);
594 glTexCoord2f(1.0, 1.0); glVertex2f(s->posX[2],
595 s->posY[2]);
596 glTexCoord2f(0.0, 1.0); glVertex2f(s->posX[3],
597 s->posY[3]);
598 glEnd();
599}
600
601static void *master_worker(void *arg)
602{
603 struct vidcap_swmix_state *s = (struct vidcap_swmix_state *) arg;
604 struct timeval t0;
605 GLuint from_uyvy, to_uyvy;
606
607 gettimeofday(&t0, NULL);
608
610 glEnable(GL_TEXTURE_2D);
613 assert(from_uyvy != 0);
614 assert(to_uyvy != 0);
615
616 glUseProgram(to_uyvy);
617 glUniform1i(glGetUniformLocation(to_uyvy, "image"), 0);
618 glUniform1f(glGetUniformLocation(to_uyvy, "imageWidth"),
619 (GLfloat) s->frame->tiles[0].width);
620 glUseProgram(0);
621
622 int field = 0;
623 char *tmp_buffer = (char *) malloc(s->frame->tiles[0].data_len);
624
625 char *current_buffer = NULL;
626
627 while(1) {
628 pthread_mutex_lock(&s->lock);
629 if(s->should_exit) {
630 pthread_mutex_unlock(&s->lock);
631 break;
632 }
633 pthread_mutex_unlock(&s->lock);
634
635 if(field == 0) {
636 pthread_mutex_lock(&s->lock);
638 pthread_cond_wait(&s->free_buffer_queue_not_empty_cv,
639 &s->lock);
640 }
641 current_buffer = simple_linked_list_pop(s->free_buffer_queue);
642 pthread_mutex_unlock(&s->lock);
643 }
644
645 // "capture" frames
646 for(int i = 0; i < s->devices_cnt; ++i) {
647 pthread_mutex_lock(&s->slaves[i].lock);
650 s->slaves_data[i].current_frame = NULL;
651 if(s->slaves[i].captured_frame) {
654 s->slaves[i].captured_frame = NULL;
655 } else if(s->slaves[i].done_frame) {
657 s->slaves[i].done_frame;
658 s->slaves[i].done_frame = NULL;
659 }
660 pthread_mutex_unlock(&s->slaves[i].lock);
661 }
662
663 // check for mode change
664 for(int i = 0; i < s->devices_cnt; ++i) {
665 if(s->slaves_data[i].current_frame) {
667 }
668 }
669
670 char *audio_data = NULL;
671 int audio_len = 0;
672
673 // load data
674 for(int i = 0; i < s->devices_cnt; ++i) {
675 if(s->slaves_data[i].current_frame) {
676 if(s->slaves[i].audio_captured && s->audio_device_index == -1) {
677 fprintf(stderr, "[swmix] Locking device #%d as an audio source.\n",
678 i);
679 s->audio_device_index = i;
680 }
681
682 if ((s->frame->interlacing != INTERLACED_MERGED || field == 1)
683 && s->audio_device_index == i) {
684 s->audio.bps = s->slaves[i].audio_frame.bps;
687 if(s->slaves[i].audio_frame.data_len) {
688 audio_data = (char *) malloc(s->slaves[i].audio_frame.data_len);
689 audio_len = s->slaves[i].audio_frame.data_len;
690 memcpy(audio_data, s->slaves[i].audio_frame.data,
691 audio_len);
692 s->slaves[i].audio_frame.data_len = 0;
693 }
694 }
695
696 load_texture(&s->slaves_data[i], from_uyvy);
697
698 }
699 }
700
701 // draw
702 glBindFramebuffer(GL_FRAMEBUFFER, s->fbo);
703 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_EXT,
704 GL_TEXTURE_2D, s->tex_output, 0);
705 glClearColor(0, 0, 0, 1);
706 glClear(GL_COLOR_BUFFER_BIT);
707
708 glViewport(0, 0, s->frame->tiles[0].width, s->frame->tiles[0].height);
709
710 if(s->interpolation == BICUBIC) {
711 glUseProgram(s->bicubic_program);
712 glUniform1i(glGetUniformLocation(s->bicubic_program, "image"), 0);
713 }
714
715 for(int i = 0; i < s->devices_cnt; ++i) {
716 if(s->slaves_data[i].current_frame) {
718 }
719 }
720 glUseProgram(0);
721
722 // read back
723 glBindTexture(GL_TEXTURE_2D, s->tex_output);
724 int width = s->frame->tiles[0].width;
725 GLenum format = GL_RGBA;
726 if(s->frame->color_spec == UYVY) {
727 glBindFramebuffer(GL_FRAMEBUFFER, s->fbo_uyvy);
728 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_EXT,
729 GL_TEXTURE_2D, s->tex_output_uyvy, 0);
730 glViewport(0, 0, s->frame->tiles[0].width / 2, s->frame->tiles[0].height);
731 glUseProgram(to_uyvy);
732 glBegin(GL_QUADS);
733 glTexCoord2f(0.0, 0.0); glVertex2f(-1.0, -1.0);
734 glTexCoord2f(1.0, 0.0); glVertex2f(1.0, -1.0);
735 glTexCoord2f(1.0, 1.0); glVertex2f(1.0, 1.0);
736 glTexCoord2f(0.0, 1.0); glVertex2f(-1.0, 1.0);
737 glEnd();
738 glUseProgram(0);
739 width /= 2;
740 glBindTexture(GL_TEXTURE_2D, s->tex_output_uyvy);
741 } else if (s->frame->color_spec == RGB) {
742 format = GL_RGB;
743 }
744
745 char *read_buf;
746 if(s->frame->interlacing == PROGRESSIVE) {
747 read_buf = current_buffer;
748 } else {
749 read_buf = tmp_buffer;
750 }
751 glReadPixels(0, 0, width,
752 s->frame->tiles[0].height,
753 format, GL_UNSIGNED_BYTE,
754 read_buf);
755 glBindFramebuffer(GL_FRAMEBUFFER, 0);
756 glBindTexture(GL_TEXTURE_2D, 0);
757
759 int linesize =
761 for(unsigned int i = field; i < s->frame->tiles[0].height; i += 2) {
762 memcpy(current_buffer + i * linesize, tmp_buffer + i * linesize,
763 linesize);
764 }
765 field = (field + 1) % 2;
766 }
767
768 if(field == 0) {
769 // wait until next frame time is due
770 double sec;
771 struct timeval t;
772 do {
773 gettimeofday(&t, NULL);
774 sec = tv_diff(t, t0);
775 } while(sec < 1.0 / s->frame->fps);
776 t0 = t;
777
778 pthread_mutex_lock(&s->lock);
779 while(s->completed_buffer != NULL) {
780 pthread_cond_wait(&s->frame_sent_cv, &s->lock);
781 }
782 s->completed_buffer = current_buffer;
783 s->completed_audio_buffer = audio_data;
784 s->completed_audio_buffer_len = audio_len;
785 current_buffer = NULL;
786 pthread_cond_signal(&s->frame_ready_cv);
787 pthread_mutex_unlock(&s->lock);
788 field = 0;
789 }
790
791 }
792
793 free(tmp_buffer);
794
795 glDeleteProgram(from_uyvy);
796 glDeleteProgram(to_uyvy);
797 glDisable(GL_TEXTURE_2D);
799
800 return NULL;
801}
802
803static void *slave_worker(void *arg)
804{
805 struct state_slave *s = (struct state_slave *) arg;
806
807 struct vidcap *device;
808 int ret =
809 initialize_video_capture(NULL, s->device_params, &device);
810 if(ret != 0) {
811 fprintf(stderr, "[swmix] Unable to initialize device %s (%s:%s).\n",
815 exit_uv(1);
816 return NULL;
817 }
818
819 while(!s->should_exit) {
820 struct video_frame *frame;
821 struct audio_frame *audio;
822
823 frame = vidcap_grab(device, &audio);
824 if (frame) {
825 struct video_frame *frame_local;
826 if (frame->callbacks.dispose) {
827 frame_local = frame;
828 } else {
829 frame_local = vf_get_copy(frame);
830 frame_local->callbacks.dispose = vf_free;
831 frame_local->callbacks.dispose_udata = NULL; // not needed
832 }
833 if (frame_local->interlacing == INTERLACED_MERGED) {
834 vc_deinterlace((unsigned char *) frame_local->tiles[0].data,
835 vc_get_linesize(frame_local->tiles[0].width, frame_local->color_spec),
836 frame_local->tiles[0].height);
837 }
838 pthread_mutex_lock(&s->lock);
839 // video frame was not processed, simply replace it
841 s->captured_frame = frame_local;
842 if(audio) {
843 s->audio_captured = true;
844 int len = audio->data_len;
845 if(len + s->audio_frame.data_len > (int) s->audio_frame.max_size) {
847 fprintf(stderr, "[SW Mix] Audio buffer overflow!\n");
848 }
850 len);
853 s->audio_frame.bps = audio->bps;
855 }
856 pthread_mutex_unlock(&s->lock);
857 }
858 }
859
860 vidcap_done(device);
861
862 return NULL;
863}
864
865static bool get_slave_param_from_file(FILE* config_file, const char *slave_name, int *x, int *y,
866 int *width, int *height)
867{
868 char *ret;
869 char line[1024];
870 fseek(config_file, 0, SEEK_SET); // rewind
871 ret = fgets(line, sizeof(line), config_file); // skip first line
872 if(!ret) return false;
873 while (fgets(line, sizeof(line), config_file)) {
874 char name[129];
875 int x_, y_, width_, height_;
876 if(sscanf(line, "%128s %d %d %d %d", name, &x_, &y_, &width_, &height_) != 5)
877 continue;
878 if(strcasecmp(name, slave_name) == 0) {
879 *x = x_;
880 *y = y_;
881 *width = width_;
882 *height = height_;
883 return true;
884 }
885 }
886 return false;
887}
888
889static bool get_device_config_from_file(FILE* config_file, char *slave_name,
890 char *device_name_config)
891{
892 char *ret;
893 char line[1024];
894 fseek(config_file, 0, SEEK_SET); // rewind
895 ret = fgets(line, sizeof(line), config_file); // skip first line
896 if(!ret) return false;
897 while (fgets(line, sizeof(line), config_file)) {
898 char name[129];
899 char dev_config[129];
900 int x_, y_, width_, height_;
901 if(sscanf(line, "%128s %d %d %d %d %128s", name, &x_, &y_, &width_, &height_, dev_config) != 6)
902 continue;
903 if(strcasecmp(name, slave_name) == 0) {
904 strcpy(device_name_config, dev_config);
905 return true;
906 }
907 }
908 return false;
909}
910
911#define PARSE_OK 0
912#define PARSE_ERROR 1
913#define PARSE_FILE 2
914static int parse_config_string(const char *fmt, unsigned int *width,
915 unsigned int *height, double *fps,
916 codec_t *color_spec, interpolation_t *interpolation, char **bicubic_algo, enum interlacing_t *interl, int *grid_x, int *grid_y, char **filepath)
917{
918 char *save_ptr = NULL;
919 char *item;
920 char *parse_string;
921 char *tmp;
922 int token_nr = 0;
923
924 *interl = PROGRESSIVE;
925
926 parse_string = (char *) alloca(strlen(fmt) + 1);
927 strcpy(parse_string, fmt);
928 tmp = parse_string;
929
930 while((item = strtok_r(tmp, ":", &save_ptr))) {
931 switch (token_nr) {
932 case 0:
933 if(strncasecmp(item, "file", strlen("file")) == 0) {
934 char *eq = strchr(item, '=');
935 if(filepath && eq){
936 *filepath = strdup(eq + 1);
937 }
938 return PARSE_FILE;
939 }
940 *width = atoi(item);
941 break;
942 case 1:
943 *height = atoi(item);
944 break;
945 case 2:
946 {
947 char *endptr;
948 *fps = strtod(item, &endptr);
949 if(tolower(*endptr) == 'i') {
950 *fps /= 2;
951 *interl = INTERLACED_MERGED;
952 }
953 }
954 break;
955 case 3:
958 fprintf(stderr, "Unrecognized color spec string: %s\n", item);
959 return PARSE_ERROR;
960 }
961 break;
962 default:
963 if(strncasecmp(item, "interpolation=", strlen("interpolation=")) == 0) {
964 if(strcasecmp(item + strlen("interpolation="), "bilinear") == 0) {
965 *interpolation = BILINEAR;
966 } else {
967 *interpolation = BICUBIC;
968 if(strchr(item, ',')) {
969 *bicubic_algo = strdup(strchr(item, ',') + 1);
970 }
971 }
972 } else if (strncasecmp(item, "layout=", strlen("layout=")) == 0) {
973 const char *l = item + strlen("layout=");
974 if (!strchr(l, 'x')) {
975 log_msg(LOG_LEVEL_ERROR, "Error parsing layout!\n");
976 return PARSE_ERROR;
977 }
978 *grid_x = atoi(l);
979 *grid_y = atoi(strchr(l, 'x') + 1);
980 if (*grid_x <= 0 || *grid_y <= 0) {
981 log_msg(LOG_LEVEL_ERROR, "Error parsing layout!\n");
982 return PARSE_ERROR;
983 }
984 } else {
985 log_msg(LOG_LEVEL_ERROR, "Unknown option: %s\n", item);
986 return PARSE_ERROR;
987 }
988 }
989 tmp = NULL;
990 token_nr += 1;
991 }
992
993 if(token_nr < 3)
994 return PARSE_ERROR;
995
996 return PARSE_OK;
997}
998
999static bool parse(struct vidcap_swmix_state *s, struct video_desc *desc, char *fmt,
1000 FILE **config_file, interpolation_t *interpolation,
1001 const struct vidcap_params *params)
1002{
1003 *config_file = NULL;
1004 char *config_path = NULL;
1005 int ret;
1006
1008 interpolation, &s->bicubic_algo, &desc->interlacing, &s->grid_x, &s->grid_y, &config_path);
1009 if(ret == PARSE_ERROR) {
1010 show_help();
1011 return false;
1012 }
1013
1014 if(ret == PARSE_FILE) {
1015 s->use_config_file = true;
1016 if(!config_path)
1017 config_path = strdup(get_config_name());
1018
1019 *config_file = fopen(config_path, "r");
1020 if(!*config_file) {
1021 fprintf(stderr, "Params not set and config file %s not found.\n",
1022 config_path);
1023 free(config_path);
1024 return false;
1025 }
1026 free(config_path);
1027
1028 char line[1024];
1029 if(!fgets(line, sizeof(line), *config_file)) {
1030 fprintf(stderr, "Input file is empty!\n");
1031 return false;
1032 }
1033 for(int i = strlen(line); i > 0 && isspace(line[i - 1]); i--) line[i - 1] = '\0'; // trim trailing spaces
1035 interpolation, &s->bicubic_algo, &desc->interlacing, &s->grid_x, &s->grid_y, NULL);
1036 if(ret != PARSE_OK) {
1037 fprintf(stderr, "Malformed input file! First line should contain config "
1038 "string same as for cmdline use (between first ':' and '#' "
1039 "exclusive):\n");
1040 show_help();
1041 return false;
1042 }
1043 } else {
1044 s->use_config_file = false;
1045 }
1046
1047
1048 if(desc->color_spec != RGBA && desc->color_spec != RGB && desc->color_spec != UYVY) {
1049 fprintf(stderr, "Unsupported output codec.\n");
1050 return false;
1051 }
1052
1053 s->devices_cnt = 0;
1054 const struct vidcap_params *tmp = params;
1055 while((tmp = vidcap_params_get_next(tmp))) {
1056 if (vidcap_params_get_driver(tmp) != NULL)
1057 s->devices_cnt++;
1058 else
1059 break;
1060 }
1061
1062 if (s->grid_x != 0 && s->devices_cnt > s->grid_x * s->grid_y) {
1063 log_msg(LOG_LEVEL_ERROR, "[swmix] Invalid layout! More devices given than layout size.\n");
1064 return false;
1065 }
1066
1067 s->slaves = (struct state_slave *) calloc(s->devices_cnt, sizeof(struct state_slave));
1068 for (int i = 0; i < s->devices_cnt; ++i) {
1070 s->slaves[i].audio_frame.data = (char *)
1071 malloc(s->slaves[i].audio_frame.max_size);
1072 s->slaves[i].audio_frame.data_len = 0;
1073 pthread_mutex_init(&(s->slaves[i].lock), NULL);
1074 }
1075
1076 tmp = params;
1077 for (int i = 0; i < s->devices_cnt; ++i) {
1078 tmp = vidcap_params_get_next(tmp);
1080 }
1081
1082 return true;
1083}
1084
1085static int
1087{
1088 GLenum format;
1089
1090 printf("vidcap_swmix_init\n");
1091
1093 strcmp(vidcap_params_get_fmt(params), "help") == 0) {
1094 show_help();
1095 return VIDCAP_INIT_NOERR;
1096 }
1097
1098 struct vidcap_swmix_state *s = calloc(1, sizeof *s);
1099 if(s == NULL) {
1100 printf("Unable to allocate swmix capture state\n");
1101 return VIDCAP_INIT_FAIL;
1102 }
1103
1104 s->completed_audio_buffer = NULL;
1105 s->network_audio_buffer = NULL;
1106
1107 s->frames = 0;
1108 s->slaves = NULL;
1109 s->audio_device_index = -1;
1110 s->bicubic_algo = strdup("BSpline");
1111 gettimeofday(&s->t0, NULL);
1113
1115
1116 s->should_exit = false;
1117 s->completed_buffer = NULL;
1118 s->network_buffer = NULL;
1119
1120 pthread_mutex_init(&s->lock, NULL);
1121 pthread_cond_init(&s->frame_ready_cv, NULL);
1122 pthread_cond_init(&s->frame_sent_cv, NULL);
1123 pthread_cond_init(&s->free_buffer_queue_not_empty_cv, NULL);
1124
1125 char *init_fmt = strdup(vidcap_params_get_fmt(params));
1126 struct video_desc desc = {.tile_count = 1, .color_spec = RGBA};
1127 FILE *config_file = NULL;
1128 if(!parse(s, &desc, init_fmt, &config_file, &s->interpolation, params)) {
1129 free(init_fmt);
1130 goto error;
1131 }
1132 free(init_fmt);
1133
1134 s->frame = vf_alloc_desc(desc);
1135
1137 fprintf(stderr, "[swmix] Unable to initialize OpenGL context.\n");
1138 goto error;
1139 }
1140
1141 if (s->gl_context.gl_major < 2) {
1142 fprintf(stderr, "[swmix] Unsufficient OpenGL version to run SWMix.\n");
1143 goto error;
1144 }
1145
1147
1148 {
1149 char *bicubic = strdup(bicubic_template);
1150 char *algo_pos;
1151 while((algo_pos = strstr(bicubic, "INTERP_ALGORITHM_PLACEHOLDER"))) {
1152 memset(algo_pos, ' ', strlen("INTERP_ALGORITHM_PLACEHOLDER"));
1153 memcpy(algo_pos, s->bicubic_algo, strlen(s->bicubic_algo));
1154 }
1155 printf("Using bicubic algorithm: %s\n", s->bicubic_algo);
1157 free(bicubic);
1158 }
1159
1161 if(!s->slaves_data) {
1162 goto error;
1163 }
1164
1165 if (config_file) {
1166 fclose(config_file);
1167 }
1168
1169 format = GL_RGBA;
1170 if(desc.color_spec == RGB) {
1171 format = GL_RGB;
1172 }
1173 glGenTextures(1, &s->tex_output);
1174 glBindTexture(GL_TEXTURE_2D, s->tex_output);
1175 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1176 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1177 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1178 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1179 glTexImage2D(GL_TEXTURE_2D, 0, format, desc.width, desc.height,
1180 0, format, GL_UNSIGNED_BYTE, NULL);
1181
1182 glGenTextures(1, &s->tex_output_uyvy);
1183 glBindTexture(GL_TEXTURE_2D, s->tex_output_uyvy);
1184 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1185 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1186 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1187 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1188 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, desc.width / 2, desc.height,
1189 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1190
1191 glGenFramebuffers(1, &s->fbo);
1192 glGenFramebuffers(1, &s->fbo_uyvy);
1193
1195
1197 s->frame->color_spec) * s->frame->tiles[0].height;
1198 for(int i = 0; i < 3; ++i) {
1199 char *buffer = (char *) malloc(s->frame->tiles[0].data_len);
1201 }
1202
1203 pthread_create(&s->master_thread_id, NULL, master_worker, (void *) s);
1204 for(int i = 0; i < s->devices_cnt; ++i) {
1205 pthread_create(&(s->slaves[i].thread_id), NULL, slave_worker, (void *) &s->slaves[i]);
1206 }
1207 s->threads_started = true;
1208
1209 *state = s;
1210 return VIDCAP_INIT_OK;
1211
1212error:
1213 if (config_file) {
1214 fclose(config_file);
1215 }
1217 return VIDCAP_INIT_FAIL;
1218}
1219
1220static void
1222{
1223 struct vidcap_swmix_state *s = (struct vidcap_swmix_state *) state;
1224
1225 assert(s != NULL);
1226
1227 if (s->threads_started) {
1228 for(int i = 0; i < s->devices_cnt; ++i) {
1229 s->slaves[i].should_exit = true;
1230 }
1231
1232 for(int i = 0; i < s->devices_cnt; ++i) {
1233 pthread_join(s->slaves[i].thread_id, NULL);
1234 }
1235 }
1236
1237 // wait for master thread to finish
1238 pthread_mutex_lock(&s->lock);
1239 s->should_exit = true;
1240 if(s->network_buffer) {
1242 s->network_buffer = NULL;
1243 pthread_cond_signal(&s->free_buffer_queue_not_empty_cv);
1244 }
1245 if(s->completed_buffer) {
1247 s->completed_buffer = NULL;
1248 pthread_cond_signal(&s->frame_sent_cv);
1249 }
1250 pthread_mutex_unlock(&s->lock);
1251 if (s->threads_started) {
1252 pthread_join(s->master_thread_id, NULL);
1253 }
1254
1255 if(s->completed_buffer)
1256 free(s->completed_buffer);
1257 char *buf = NULL;
1258 while ((buf = simple_linked_list_pop(s->free_buffer_queue)) != NULL) {
1259 free(buf);
1260 }
1261
1262 if (s->slaves) {
1263 for (int i = 0; i < s->devices_cnt; ++i) {
1264 pthread_mutex_destroy(&s->slaves[i].lock);
1267 free(s->slaves[i].audio_frame.data);
1269 }
1270 free(s->slaves);
1271 }
1272
1273 vf_free(s->frame);
1274
1276
1278
1279 if (s->tex_output) {
1280 glDeleteTextures(1, &s->tex_output);
1281 }
1282 if (s->tex_output_uyvy) {
1283 glDeleteTextures(1, &s->tex_output_uyvy);
1284 }
1285 if (s->fbo) {
1286 glDeleteFramebuffers(1, &s->fbo);
1287 }
1288 if (s->fbo_uyvy) {
1289 glDeleteFramebuffers(1, &s->fbo_uyvy);
1290 }
1291
1294
1295 pthread_mutex_destroy(&s->lock);
1296 pthread_cond_destroy(&s->frame_ready_cv);
1297 pthread_cond_destroy(&s->frame_sent_cv);
1298 pthread_cond_destroy(&s->free_buffer_queue_not_empty_cv);
1299
1300 free(s->bicubic_algo);
1302
1303 free(s);
1304}
1305
1306static struct video_frame *
1308{
1309 struct vidcap_swmix_state *s = (struct vidcap_swmix_state *) state;
1310
1311 *audio = NULL;
1312 if (s->network_audio_buffer) {
1313 free(s->network_audio_buffer);
1314 s->network_audio_buffer = NULL;
1315 }
1316
1317 pthread_mutex_lock(&s->lock);
1318 while(s->completed_buffer == NULL) {
1319 pthread_cond_wait(&s->frame_ready_cv, &s->lock);
1320 }
1321 if(s->network_buffer) {
1323 pthread_cond_signal(&s->free_buffer_queue_not_empty_cv);
1324 }
1326 s->completed_buffer = NULL;
1328 s->completed_audio_buffer = NULL;
1330 pthread_cond_signal(&s->frame_sent_cv);
1331 pthread_mutex_unlock(&s->lock);
1332
1333 s->frames++;
1334 gettimeofday(&s->t, NULL);
1335 double seconds = tv_diff(s->t, s->t0);
1336 if (seconds >= 5) {
1337 float fps = s->frames / seconds;
1338 log_msg(LOG_LEVEL_INFO, "[swmix cap.] %d frames in %g seconds = %g FPS\n", s->frames, seconds, fps);
1339 s->t0 = s->t;
1340 s->frames = 0;
1341 }
1342
1343 if(s->network_audio_buffer) {
1345 *audio = &s->audio;
1346 }
1347
1348 return s->frame;
1349}
1350
1358
1360
void ** state
struct video_desc desc
struct video_frame * frame
const char * fmt
int void size_t * len
struct audio_frame ** audio
int void(** deleter)(void *))
void vf_free(struct video_frame *buf)
Frees video_frame structure.
struct video_desc video_desc_from_frame(const struct video_frame *frame)
Returns struct video_desc from video frame.
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)
This file contains definition of some common audio types.
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define error_msg(...)
Definition debug.h:89
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define B
#define MAX_PATH_SIZE
Definition fs.h:48
bool init_gl_context(struct gl_context *context, int which)
initializes specified OpenGL context
Definition gl_context.c:69
void destroy_gl_context(struct gl_context *context)
Definition gl_context.c:223
void gl_context_make_current(struct gl_context *context)
Definition gl_context.c:236
GLuint glsl_compile_link(const char *vprogram, const char *fprogram)
Definition gl_context.c:147
#define GL_CONTEXT_LEGACY
Definition gl_context.h:73
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
struct video_frame * vidcap_grab(struct vidcap *state, struct audio_frame **audio)
Grabs video frame. This function may block for a short period if waiting for incoming frame....
#define VIDCAP_NO_GENERIC_FPS_INDICATOR
int initialize_video_capture(struct module *parent, struct vidcap_params *param, struct vidcap **state)
Initializes video capture.
void vidcap_done(struct vidcap *state)
Destroys vidap state.
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
int count
Definition hd-rum.c:44
void exit_uv(int status)
Definition host.cpp:625
static struct @19 params[100]
@ 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
void simple_linked_list_append(struct simple_linked_list *l, void *data)
Definition list.cpp:68
int simple_linked_list_size(struct simple_linked_list *l)
Definition list.cpp:93
struct simple_linked_list * simple_linked_list_init(void)
Definition list.cpp:53
void simple_linked_list_destroy(struct simple_linked_list *l)
Definition list.cpp:58
void * simple_linked_list_pop(struct simple_linked_list *l)
Definition list.cpp:82
decoder_t get_best_decoder_from(codec_t in, const codec_t *out_candidates, codec_t *out)
decoder_func_t * decoder_t
Definition pixfmt_conv.h:96
struct __attribute__((packed))
Definition rtp.h:88
const char * name
Definition settings.cpp:193
struct used to comunicate with audio capture/playback device
Definition types.h:99
int sample_rate
Definition types.h:101
char * data
Definition types.h:102
int ch_count
Definition types.h:104
int data_len
Definition types.h:103
int bps
Definition types.h:100
int max_size
Definition types.h:105
Definition hd-rum.c:27
double fb_aspect
Definition swmix.c:282
struct video_desc saved_desc
Definition swmix.c:276
double y
Definition swmix.c:281
codec_t decoder_from
Definition swmix.c:285
float posX[4]
Definition swmix.c:277
double height
Definition swmix.c:281
double x
Definition swmix.c:281
struct video_frame * current_frame
Definition swmix.c:275
GLuint texture[2]
Definition swmix.c:279
codec_t decoder_to
Definition swmix.c:285
GLuint fbo
Definition swmix.c:280
float posY[4]
Definition swmix.c:278
double width
Definition swmix.c:281
decoder_t decoder
Definition swmix.c:284
struct audio_frame audio_frame
Definition swmix.c:221
pthread_t thread_id
Definition swmix.c:213
pthread_mutex_t lock
Definition swmix.c:215
bool audio_captured
Definition swmix.c:222
struct video_frame * done_frame
Definition swmix.c:219
struct vidcap_params * device_params
Definition swmix.c:216
struct video_frame * captured_frame
Definition swmix.c:218
bool should_exit
Definition swmix.c:214
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
char * completed_buffer
Definition swmix.c:237
int completed_audio_buffer_len
Definition swmix.c:240
GLuint fbo_uyvy
Definition swmix.c:233
GLuint tex_output
Definition swmix.c:230
pthread_t master_thread_id
Definition swmix.c:253
char * bicubic_algo
Definition swmix.c:260
pthread_cond_t frame_ready_cv
Definition swmix.c:250
bool threads_started
Definition swmix.c:254
struct gl_context gl_context
Definition swmix.c:228
char * completed_audio_buffer
Definition swmix.c:239
int audio_device_index
index of video device from which to take audio
Definition swmix.c:242
interpolation_t interpolation
Definition swmix.c:262
struct slave_data * slaves_data
Definition swmix.c:257
struct simple_linked_list * free_buffer_queue
Definition swmix.c:243
GLuint bicubic_program
Definition swmix.c:261
struct state_slave * slaves
Definition swmix.c:226
struct audio_frame audio
Definition swmix.c:241
struct timeval t t0
Definition swmix.c:247
GLuint tex_output_uyvy
Definition swmix.c:231
bool use_config_file
Definition swmix.c:258
char * network_buffer
Definition swmix.c:236
char * network_audio_buffer
Definition swmix.c:238
pthread_cond_t frame_sent_cv
Definition swmix.c:251
struct video_frame * frame
Definition swmix.c:235
pthread_mutex_t lock
Definition swmix.c:249
pthread_cond_t free_buffer_queue_not_empty_cv
Definition swmix.c:244
This struct represents video capture state.
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
void(* dispose)(struct video_frame *)
Definition types.h:245
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
struct video_frame_callbacks callbacks
Definition types.h:324
codec_t color_spec
Definition types.h:306
static bool get_slave_param_from_file(FILE *config, const char *slave_name, int *x, int *y, int *width, int *height)
Definition swmix.c:865
static void vidcap_swmix_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
Definition swmix.c:267
static void reconfigure_slave_rendering(struct slave_data *s, struct video_desc desc)
Definition swmix.c:360
static void check_for_slave_format_change(struct slave_data *s)
Definition swmix.c:474
static void load_texture(struct slave_data *s, GLuint from_uyvy)
Definition swmix.c:498
static const char * bicubic_template
Definition swmix.c:77
static void * slave_worker(void *arg)
Definition swmix.c:803
static struct slave_data * init_slave_data(struct vidcap_swmix_state *s, FILE *config)
Definition swmix.c:288
static const char * fprogram_to_uyvy
Definition swmix.c:440
static void destroy_slave_data(struct slave_data *data, int count)
Definition swmix.c:349
static void vidcap_swmix_done(void *state)
Definition swmix.c:1221
static const char * vprogram
Definition swmix.c:468
#define MAX_AUDIO_LEN
Definition swmix.c:66
#define PARSE_OK
Definition swmix.c:911
#define PARSE_FILE
Definition swmix.c:913
#define PARSE_ERROR
Definition swmix.c:912
#define STRINGIFY(A)
Definition swmix.c:422
static const struct video_capture_info vidcap_swmix_info
Definition swmix.c:1351
static const char * fprogram_from_uyvy
Definition swmix.c:423
static bool get_device_config_from_file(FILE *config_file, char *slave_name, char *device_name_config) __attribute__((unused))
Definition swmix.c:889
static void render_slave(struct slave_data *s, interpolation_t interpolation, GLuint bicubic_program)
Definition swmix.c:579
static void * master_worker(void *arg)
Definition swmix.c:601
static bool parse(struct vidcap_swmix_state *s, struct video_desc *desc, char *fmt, FILE **config_file, interpolation_t *interpolation, const struct vidcap_params *params)
Definition swmix.c:999
static char * get_config_name(void)
Definition swmix.c:176
static int vidcap_swmix_init(struct vidcap_params *params, void **state)
Definition swmix.c:1086
interpolation_t
Definition swmix.c:68
@ BILINEAR
Definition swmix.c:70
@ BICUBIC
Definition swmix.c:69
static void show_help(void)
Definition swmix.c:189
static int parse_config_string(const char *fmt, unsigned int *width, unsigned int *height, double *fps, codec_t *color_spec, interpolation_t *interpolation, char **bicubic_algo, enum interlacing_t *interl, int *grid_x, int *grid_y, char **filepath)
Definition swmix.c:914
static struct video_frame * vidcap_swmix_grab(void *state, struct audio_frame **audio)
Definition swmix.c:1307
double tv_diff(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:97
interlacing_t
Definition types.h:163
@ PROGRESSIVE
progressive frame
Definition types.h:164
@ 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
@ BGR
8-bit BGR
Definition types.h:88
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
This is an umbrella header for video functions.
struct vidcap_params * vidcap_params_get_next(const struct vidcap_params *curr)
void vidcap_params_free_struct(struct vidcap_params *buf)
struct vidcap_params * vidcap_params_copy(const struct vidcap_params *params)
const char * vidcap_params_get_name(const struct vidcap_params *params)
const char * vidcap_params_get_driver(const struct vidcap_params *params)
const char * vidcap_params_get_fmt(const struct vidcap_params *params)
void vc_deinterlace(unsigned char *src, long src_linesize, int lines)
Deinterlaces framebuffer.
bool codec_is_in_set(codec_t codec, const codec_t *set)
struct video_frame * vf_get_copy(struct video_frame *original_frame)
Makes deep copy of the video frame.
bool video_desc_eq(struct video_desc a, struct video_desc b)
Compares two video descriptions.
#define VIDEO_FRAME_DISPOSE(frame)
Definition video_frame.h:76