feat: video auto reconnect
Clang Lint CI / lint-no-video (push) Failing after 58s
Clang Build CI / run-no-video (push) Successful in 58s
Clang Build CI / run-video (push) Failing after 58s
Clang Build CI / build-release (push) Failing after 1m13s
Clang Lint CI / lint-video (push) Has been cancelled

This commit is contained in:
2026-05-14 13:16:55 +02:00
parent 25b7134a43
commit e60d53bac4
17 changed files with 303 additions and 325 deletions
+2 -2
View File
@@ -1,9 +1,9 @@
AUTOMAKE_OPTIONS = foreign subdir-objects -Wall AUTOMAKE_OPTIONS = foreign subdir-objects -Wall
bin_PROGRAMS = forge bin_PROGRAMS = forge
forge_SOURCES = src/args.c src/arr.c src/config_file.c src/file.c src/forge.c src/main.c src/midi.c src/project.c src/rand.c src/shaders.c src/shared.c src/state.c src/string.c src/tempo.c src/timer.c src/video.c src/window.c $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/hashmap.c/hashmap.c $(top_srcdir)/log.c/src/log.c forge_SOURCES = src/args.c src/arr.c src/config_file.c src/file.c src/forge.c src/main.c src/midi.c src/project.c src/rand.c src/shaders.c src/state.c src/string.c src/tempo.c src/timer.c src/video.c src/window.c $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/hashmap.c/hashmap.c $(top_srcdir)/log.c/src/log.c
forge_CFLAGS = -Ofast -march=native -flto -funroll-loops -fprefetch-loop-arrays -fno-exceptions -fopenmp -I$(top_srcdir)/include -DGLFW_INCLUDE_NONE -DGLFW_EXPOSE_NATIVE_EGL -DGLFW_NATIVE_INCLUDE_NONE -DLOG_USE_COLOR -DVIDEO_IN -DDATADIR=\"$(datadir)/$(PACKAGE)\" forge_CFLAGS = -Ofast -march=native -flto -funroll-loops -fprefetch-loop-arrays -fno-exceptions -fopenmp -I$(top_srcdir)/include -DGLFW_INCLUDE_NONE -DGLFW_EXPOSE_NATIVE_EGL -DGLFW_NATIVE_INCLUDE_NONE -DLOG_USE_COLOR -DVIDEO_IN -DDATADIR=\"$(datadir)/$(PACKAGE)\"
forge_LDADD = -lm -lGL -lglfw -lasound forge_LDADD = -lm -lGL -lglfw -lasound
include_HEADERS = src/args.h src/arr.h src/config.h src/config_file.h src/constants.h src/file.h src/forge.h src/main.h src/midi.h src/project.h src/rand.h src/shaders.h src/shared.h src/state.h src/string.h src/tempo.h src/timer.h src/types.h src/video.h src/window.h $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/include/linmath.h $(top_srcdir)/include/hashmap.h $(top_srcdir)/include/log.h include_HEADERS = src/args.h src/arr.h src/config.h src/config_file.h src/constants.h src/file.h src/forge.h src/main.h src/midi.h src/project.h src/rand.h src/shaders.h src/state.h src/string.h src/tempo.h src/timer.h src/types.h src/video.h src/window.h $(top_srcdir)/include/glad/gl.h $(top_srcdir)/include/glad/egl.h $(top_srcdir)/include/linmath.h $(top_srcdir)/include/hashmap.h $(top_srcdir)/include/log.h
EXTRA_DIST = default/forge_project.cfg default/frag1.glsl default/frag10.glsl default/frag2.glsl default/frag3.glsl default/frag4.glsl default/frag5.glsl default/frag6.glsl default/frag7.glsl default/frag8.glsl default/frag9.glsl default/inc_cp437.glsl default/inc_debug.glsl default/inc_functions.glsl default/inc_fx.glsl default/inc_magic.glsl default/inc_rand.glsl default/inc_res.glsl default/inc_sentences.glsl default/inc_src.glsl default/inc_template.glsl default/inc_time.glsl default/inc_yuyv.glsl EXTRA_DIST = default/forge_project.cfg default/frag1.glsl default/frag10.glsl default/frag2.glsl default/frag3.glsl default/frag4.glsl default/frag5.glsl default/frag6.glsl default/frag7.glsl default/frag8.glsl default/frag9.glsl default/inc_cp437.glsl default/inc_debug.glsl default/inc_functions.glsl default/inc_fx.glsl default/inc_magic.glsl default/inc_rand.glsl default/inc_res.glsl default/inc_sentences.glsl default/inc_src.glsl default/inc_template.glsl default/inc_time.glsl default/inc_yuyv.glsl
confdir = $(prefix)/share/$(PACKAGE) confdir = $(prefix)/share/$(PACKAGE)
+3 -8
View File
@@ -1,6 +1,6 @@
TARGET ?= forge TARGET ?= forge
INSTALL_DIR ?= $(HOME)/.local/bin INSTALL_DIR ?= $(HOME)/.local/bin
TEST_ARGS ?= --video-in=/dev/video0 --video-in=/dev/video1 --video-in=/dev/video2 --video-in=/dev/video3 --video-in=/dev/video4 --video-in=/dev/video5 --video-in=/dev/video6 --video-in=/dev/video7 --video-in=/dev/video8 --video-in=/dev/video9 RUN_ARGS ?= --help
SHELL := /bin/bash SHELL := /bin/bash
.PHONY: build .PHONY: build
@@ -22,7 +22,6 @@ build:
-DGLFW_NATIVE_INCLUDE_NONE \ -DGLFW_NATIVE_INCLUDE_NONE \
-DLOG_USE_COLOR \ -DLOG_USE_COLOR \
-DVIDEO_IN \ -DVIDEO_IN \
-DEGL_DEBUG \
-o build/$(TARGET) \ -o build/$(TARGET) \
-g -Og -g -Og
@@ -50,11 +49,7 @@ format:
.PHONY: run .PHONY: run
run: build run: build
./build/$(TARGET) $(TEST_ARGS) --monitor-only --internal-size=480 --video-size=240 --hot-reload ./build/$(TARGET) $(RUN_ARGS)
.PHONY: demo
demo: build
./build/$(TARGET) $(TEST_ARGS) --demo
.PHONY: sample .PHONY: sample
sample: build sample: build
@@ -65,7 +60,7 @@ valgrind: build
valgrind \ valgrind \
--show-realloc-size-zero=no \ --show-realloc-size-zero=no \
--undef-value-errors=no \ --undef-value-errors=no \
./build/$(TARGET) $(TEST_ARGS) ./build/$(TARGET) $(RUN_ARGS)
.PHONY: clean-release .PHONY: clean-release
clean-release: clean-release:
+1
View File
@@ -22,6 +22,7 @@ AC_CHECK_HEADERS([math.h])
AC_CHECK_HEADERS([string.h]) AC_CHECK_HEADERS([string.h])
AC_CHECK_HEADERS([time.h]) AC_CHECK_HEADERS([time.h])
AC_CHECK_HEADERS([unistd.h]) AC_CHECK_HEADERS([unistd.h])
AC_CHECK_HEADERS([pthread.h])
AC_CHECK_HEADERS([linux/videodev2.h]) AC_CHECK_HEADERS([linux/videodev2.h])
+126 -102
View File
@@ -1,5 +1,6 @@
#include <log.h> #include <log.h>
#include <math.h> #include <math.h>
#include <pthread.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -14,7 +15,6 @@
#include "midi.h" #include "midi.h"
#include "project.h" #include "project.h"
#include "shaders.h" #include "shaders.h"
#include "shared.h"
#include "state.h" #include "state.h"
#include "tempo.h" #include "tempo.h"
#include "timer.h" #include "timer.h"
@@ -22,15 +22,18 @@
#include "window.h" #include "window.h"
static Parameters init_params; static Parameters init_params;
static SharedContext *context; static Context context;
static ShaderProgram program; static ShaderProgram program;
static Window *window_output; static Window *window_output;
static Window *window_monitor; static Window *window_monitor;
static Timer timer; static Timer timer;
static MidiDevice midi; static MidiDevice midi;
static bool trace_midi;
static Project project; static Project project;
#ifdef VIDEO_IN
static VideoCaptureArray video_captures;
#endif /* VIDEO_IN */
static void compute_fps() { static void compute_fps() {
double fps; double fps;
char title[STR_LEN]; char title[STR_LEN];
@@ -52,103 +55,108 @@ static void compute_fps() {
window_update_title(window_monitor, title); window_update_title(window_monitor, title);
} }
context->fps = (unsigned int)round(fps); context.fps = (unsigned int)round(fps);
} }
} }
static void init_context() { static void init_context() {
context = shared_init_context("/" PACKAGE "_context"); context.stop = false;
context->stop = false; state_init(&context, &project.state_config, init_params.demo,
state_init(context, &project.state_config, init_params.demo,
init_params.auto_random, init_params.auto_random_cycle, init_params.auto_random, init_params.auto_random_cycle,
init_params.base_tempo, init_params.load_state); init_params.base_tempo, init_params.load_state);
memset(context->input_resolutions, 0, sizeof(context->input_resolutions)); #ifdef VIDEO_IN
memset(context.input_resolutions, 0, sizeof(context.input_resolutions));
memset(context.input_formats, 0, sizeof(context.input_formats));
memset(context.input_fps, 0, sizeof(context.input_fps));
memset(context.input_swap, 0, sizeof(context.input_swap));
#endif /* VIDEO_IN */
} }
static void free_context() { shared_close_context(context); }
static void reload_shader(unsigned int i) { static void reload_shader(unsigned int i) {
shaders_update(&program, &project.fragment_shaders[i][0], i, &project); shaders_update(&program, &project.fragment_shaders[i][0], i, &project);
} }
#ifdef VIDEO_IN #ifdef VIDEO_IN
static void init_inputs() { static void init_inputs() {
context->inputs.length = init_params.video_in.length; video_captures.length = init_params.video_in.length;
for (unsigned int i = 0; i < init_params.video_in.length; i++) { for (unsigned int i = 0; i < init_params.video_in.length; i++) {
video_init(&context->inputs.values[i], init_params.video_in.values[i], video_init(&video_captures.values[i], init_params.video_in.values[i],
init_params.video_size); init_params.video_size);
if (!context->inputs.values[i].error) { if (!video_captures.values[i].error) {
context->input_resolutions[i][0] = context->inputs.values[i].width; context.input_resolutions[i][0] = video_captures.values[i].width;
context->input_resolutions[i][1] = context->inputs.values[i].height; context.input_resolutions[i][1] = video_captures.values[i].height;
context->inputs.values[i].needs_reload = false; context.input_formats[i] = video_captures.values[i].pixelformat;
context->inputs.values[i].disconnected = false; video_captures.values[i].needs_reload = false;
video_captures.values[i].disconnected = false;
} }
} }
} }
static bool reconnect_video_captures() { static void start_video_background_read(VideoCapture *video_capture,
for (unsigned int i = 0; i < init_params.video_in.length; i++) { Context *context, int input_index,
if (context->inputs.values[i].disconnected) { bool trace_fps) {
video_init(&context->inputs.values[i], init_params.video_in.values[i], pthread_t thread;
init_params.video_size); struct VideoBackgroundReadArgs *process_args =
(struct VideoBackgroundReadArgs *)malloc(
if (!context->inputs.values[i].error) { sizeof(struct VideoBackgroundReadArgs));
context->input_resolutions[i][0] = context->inputs.values[i].width; process_args->capture = video_capture;
context->input_resolutions[i][1] = context->inputs.values[i].height; process_args->context = context;
process_args->input_index = input_index;
if (!video_background_read(&context->inputs.values[i], context, i, process_args->trace_fps = trace_fps;
init_params.trace_fps)) { pthread_create(&thread, NULL, video_background_read, (void *)process_args);
return false; pthread_detach(thread);
}
context->inputs.values[i].needs_reload = true;
context->inputs.values[i].disconnected = false;
}
}
}
return true;
} }
static bool start_video_captures() { static void *
pid_t pid; background_reconnect_video_captures(__attribute__((unused)) void *args) {
for (unsigned int i = 0; i < context->inputs.length; i++) { log_info("background video capture reconnect started");
if (!context->inputs.values[i].error && while (!context.stop) {
!video_background_read(&context->inputs.values[i], context, i,
init_params.trace_fps)) {
return false;
}
}
if (!init_params.video_reconnect) {
return true;
}
pid = fork();
if (pid < 0) {
log_error("Could not create subprocess");
return false;
}
if (pid == 0) {
return true;
}
log_info("background reconnect acquisition started (pid: %d)", pid);
while (!context->stop) {
sleep(1); sleep(1);
if (!reconnect_video_captures()) { for (unsigned int i = 0; i < init_params.video_in.length; i++) {
return false; if (video_captures.values[i].disconnected) {
video_free(&video_captures.values[i]);
video_init(&video_captures.values[i], init_params.video_in.values[i],
init_params.video_size);
if (!video_captures.values[i].error) {
context.input_resolutions[i][0] = video_captures.values[i].width;
context.input_resolutions[i][1] = video_captures.values[i].height;
context.input_formats[i] = video_captures.values[i].pixelformat;
video_captures.values[i].needs_reload = true;
video_captures.values[i].disconnected = false;
start_video_background_read(&video_captures.values[i], &context, i,
init_params.trace_fps);
} }
} }
exit(EXIT_SUCCESS); }
}
pthread_exit(NULL);
}
static void start_video_captures() {
pthread_t thread;
for (unsigned int i = 0; i < video_captures.length; i++) {
if (!video_captures.values[i].error) {
start_video_background_read(&video_captures.values[i], &context, i,
init_params.trace_fps);
}
}
if (init_params.video_reconnect) {
pthread_create(&thread, NULL, background_reconnect_video_captures, NULL);
pthread_detach(thread);
}
} }
static void free_video_captures() { static void free_video_captures() {
for (unsigned int i = 0; i < context->inputs.length; i++) { for (unsigned int i = 0; i < video_captures.length; i++) {
shaders_free_input(&program, i); shaders_free_input(&program, i);
video_free(&context->inputs.values[i]); video_free(&video_captures.values[i]);
} }
} }
@@ -157,7 +165,7 @@ static void free_video_captures() {
static void error_callback(int error, const char *description) { static void error_callback(int error, const char *description) {
log_error("[GLFW] %d: %s", error, description); log_error("[GLFW] %d: %s", error, description);
window_terminate(); window_terminate();
context->stop = true; context.stop = true;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -173,13 +181,37 @@ static void key_callback(Window *window, int key,
log_info("[ESC] Closing..."); log_info("[ESC] Closing...");
window_close(window); window_close(window);
} else if (event > 0) { } else if (event > 0) {
state_key_event(context, &project.state_config, event, &midi); state_key_event(&context, &project.state_config, event, &midi);
} }
} }
static void midi_callback(unsigned char code, unsigned char value) { static void midi_callback(unsigned char code, unsigned char value) {
state_midi_event(context, &project.state_config, &midi, code, value, state_midi_event(&context, &project.state_config, &midi, code, value,
trace_midi); init_params.trace_midi);
}
static void start_state_background_write() {
pthread_t thread;
struct StateBackgroundWriteArgs *process_args =
(struct StateBackgroundWriteArgs *)malloc(
sizeof(struct StateBackgroundWriteArgs));
process_args->context = &context;
process_args->state_config = project.state_config;
process_args->midi = &midi;
pthread_create(&thread, NULL, state_background_write, process_args);
pthread_detach(thread);
}
static void start_midi_background_listen() {
pthread_t thread;
struct MidiBackgroundListenArgs *process_args =
(struct MidiBackgroundListenArgs *)malloc(
sizeof(struct MidiBackgroundListenArgs));
process_args->device = &midi;
process_args->context = &context;
process_args->event_callback = midi_callback;
pthread_create(&thread, NULL, midi_background_listen, process_args);
pthread_detach(thread);
} }
static bool init(const Parameters *params) { static bool init(const Parameters *params) {
@@ -204,30 +236,24 @@ static bool init(const Parameters *params) {
midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw")); midi_open(&midi, config_file_get_str(&project.config, "MIDI_HW", "hw"));
if (midi.error) { if (midi.error) {
context->demo = true; context.demo = true;
} else { } else {
trace_midi = params->trace_midi; start_midi_background_listen();
if (!midi_background_listen(&midi, context, midi_callback)) {
return false;
}
} }
if (!state_background_write(context, &project.state_config, &midi)) { start_state_background_write();
return false;
}
window_startup(error_callback); window_startup(error_callback);
context->tex_resolution[1] = params->internal_size; context.tex_resolution[1] = params->internal_size;
if (params->output) { if (params->output) {
window_output = window_init(PACKAGE " " VERSION, params->output_screen, window_output = window_init(PACKAGE " " VERSION, params->output_screen,
params->windowed, NULL, key_callback); params->windowed, NULL, key_callback);
window_use(window_output, context); window_use(window_output, &context);
shaders_init(&program, &project, context, false); shaders_init(&program, &project, &context, false);
} else { } else {
window_output = NULL; window_output = NULL;
} }
@@ -237,19 +263,19 @@ static bool init(const Parameters *params) {
window_init(PACKAGE " " VERSION " (monitor)", params->monitor_screen, window_init(PACKAGE " " VERSION " (monitor)", params->monitor_screen,
params->windowed, window_output, key_callback); params->windowed, window_output, key_callback);
window_use(window_monitor, context); window_use(window_monitor, &context);
shaders_init(&program, &project, context, window_output != NULL); shaders_init(&program, &project, &context, window_output != NULL);
} else { } else {
window_monitor = NULL; window_monitor = NULL;
} }
#ifdef VIDEO_IN #ifdef VIDEO_IN
shaders_link_inputs(&program, &project, &context->inputs); shaders_link_inputs(&program, &project, &video_captures);
#endif /* VIDEO_IN */ #endif /* VIDEO_IN */
if (program.error) { if (program.error) {
context->stop = true; context.stop = true;
window_terminate(); window_terminate();
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -273,10 +299,10 @@ static bool loop() {
#ifdef VIDEO_IN #ifdef VIDEO_IN
if (init_params.video_reconnect) { if (init_params.video_reconnect) {
for (unsigned int i = 0; i < context->inputs.length; i++) { for (unsigned int i = 0; i < video_captures.length; i++) {
if (context->inputs.values[i].needs_reload) { if (video_captures.values[i].needs_reload) {
shaders_relink_input(&program, &project, &context->inputs, i); shaders_relink_input(&program, &project, &video_captures, i);
context->inputs.values[i].needs_reload = false; video_captures.values[i].needs_reload = false;
} }
} }
} }
@@ -284,21 +310,21 @@ static bool loop() {
compute_fps(); compute_fps();
context->time = window_get_time(); context.time = window_get_time();
context->tempo_total = (float)tempo_total(&context->tempo); context.tempo_total = (float)tempo_total(&context.tempo);
if (window_output != NULL) { if (window_output != NULL) {
window_use(window_output, context); window_use(window_output, &context);
shaders_compute(&program, context, false, false); shaders_compute(&program, &context, false, false);
window_refresh(window_output); window_refresh(window_output);
} }
if (window_monitor != NULL) { if (window_monitor != NULL) {
window_use(window_monitor, context); window_use(window_monitor, &context);
shaders_compute(&program, context, true, window_output != NULL); shaders_compute(&program, &context, true, window_output != NULL);
window_refresh(window_monitor); window_refresh(window_monitor);
} }
@@ -309,22 +335,22 @@ static bool loop() {
} }
static void shutdown() { static void shutdown() {
context->stop = true; context.stop = true;
if (init_params.save_state) { if (init_params.save_state) {
state_save(context, &project.state_config); state_save(&context, &project.state_config);
} }
shaders_free(&program); shaders_free(&program);
if (window_output != NULL) { if (window_output != NULL) {
window_use(window_output, context); window_use(window_output, &context);
shaders_free_window(&program, false); shaders_free_window(&program, false);
} }
if (window_monitor != NULL) { if (window_monitor != NULL) {
window_use(window_monitor, context); window_use(window_monitor, &context);
shaders_free_window(&program, init_params.output); shaders_free_window(&program, init_params.output);
} }
@@ -333,8 +359,6 @@ static void shutdown() {
free_video_captures(); free_video_captures();
#endif /* VIDEO_IN */ #endif /* VIDEO_IN */
free_context();
project_free(&project); project_free(&project);
window_terminate(); window_terminate();
+10 -19
View File
@@ -1,6 +1,6 @@
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include <log.h> #include <log.h>
#include <stdlib.h> #include <pthread.h>
#include "types.h" #include "types.h"
@@ -33,32 +33,23 @@ void midi_write(const MidiDevice *device, unsigned char code,
snd_rawmidi_write(device->output, buffer, 3); snd_rawmidi_write(device->output, buffer, 3);
} }
bool midi_background_listen(const MidiDevice *device, void *midi_background_listen(void *args) {
const SharedContext *context, MidiBackgroundReadArgs *process_args = (MidiBackgroundReadArgs *)args;
void (*event_callback)(unsigned char code, MidiDevice *device = process_args->device;
unsigned char value)) { Context *context = process_args->context;
pid_t pid;
int bytes_read; int bytes_read;
unsigned char buffer[3]; unsigned char buffer[3];
pid = fork(); log_info("(%s) background acquisition started", device->name);
if (pid < 0) {
log_error("Could not create subprocess");
return false;
}
if (pid == 0) {
return true;
}
log_info("(%s) background acquisition started (pid: %d)", device->name, pid);
while (!context->stop) { while (!context->stop) {
bytes_read = snd_rawmidi_read(device->input, buffer, 3); bytes_read = snd_rawmidi_read(device->input, buffer, 3);
if (bytes_read == 3) { if (bytes_read == 3) {
event_callback(buffer[1], buffer[2]); process_args->event_callback(buffer[1], buffer[2]);
} }
} }
log_info("(%s) background acquisition stopped by main thread (pid: %d)", log_info("(%s) background acquisition stopped by main thread", device->name);
device->name, pid); pthread_exit(NULL);
exit(EXIT_SUCCESS);
} }
+1 -4
View File
@@ -6,9 +6,6 @@
void midi_open(MidiDevice *device, const char *name); void midi_open(MidiDevice *device, const char *name);
void midi_write(const MidiDevice *device, unsigned char code, void midi_write(const MidiDevice *device, unsigned char code,
unsigned char value); unsigned char value);
bool midi_background_listen(const MidiDevice *device, void *midi_background_listen(void *args);
const SharedContext *context,
void (*event_callback)(unsigned char code,
unsigned char value));
#endif /* MIDI_H */ #endif /* MIDI_H */
+29 -29
View File
@@ -134,8 +134,7 @@ static bool init_gl(ShaderProgram *program) {
!check_eglerror(program, "init_gl"); !check_eglerror(program, "init_gl");
} }
static bool init_textures(ShaderProgram *program, static bool init_textures(ShaderProgram *program, const Context *context) {
const SharedContext *context) {
glGenTextures(program->tex_count, program->textures); glGenTextures(program->tex_count, program->textures);
if (check_glerror(program, "init_textures/glGenTextures")) { if (check_glerror(program, "init_textures/glGenTextures")) {
return false; return false;
@@ -639,8 +638,7 @@ static bool init_programs(ShaderProgram *program, const ConfigFile *config,
return true; return true;
} }
static void update_viewport(ShaderProgram *program, static void update_viewport(ShaderProgram *program, const Context *context) {
const SharedContext *context) {
// viewport changed // viewport changed
if (context->resolution[0] != program->last_resolution[0] || if (context->resolution[0] != program->last_resolution[0] ||
context->resolution[1] != program->last_resolution[1]) { context->resolution[1] != program->last_resolution[1]) {
@@ -685,7 +683,7 @@ static void write_uniform_multi_3f(GLuint location, unsigned int count,
} }
static void use_program(const ShaderProgram *program, int i, bool output, static void use_program(const ShaderProgram *program, int i, bool output,
const SharedContext *context) { const Context *context) {
unsigned int k; unsigned int k;
unsigned int offset; unsigned int offset;
unsigned int subcount; unsigned int subcount;
@@ -728,17 +726,21 @@ static void use_program(const ShaderProgram *program, int i, bool output,
context->active[j] + 1); context->active[j] + 1);
} }
#ifdef VIDEO_IN
for (unsigned int j = 0; j < program->in_count; j++) { for (unsigned int j = 0; j < program->in_count; j++) {
write_uniform_2f(program->iinres_locations[i * program->in_count + j], write_uniform_2f(program->iinres_locations[i * program->in_count + j],
&context->input_resolutions[j]); &context->input_resolutions[j]);
write_uniform_1i(program->iinfmt_locations[i * program->in_count + j], write_uniform_1i(program->iinfmt_locations[i * program->in_count + j],
context->inputs.values[j].pixelformat); context->input_formats[j]);
write_uniform_1i(program->iinfps_locations[i * program->in_count + j], write_uniform_1i(program->iinfps_locations[i * program->in_count + j],
context->inputs.values[j].fps); context->input_fps[j]);
write_uniform_1i(program->iinswap_locations[i * program->in_count + j], write_uniform_1i(program->iinswap_locations[i * program->in_count + j],
context->inputs.values[j].swap ? 1 : 0); context->input_swap[j] ? 1 : 0);
} }
#endif /* VIDEO_IN */
// set seeds uniforms // set seeds uniforms
for (unsigned int j = 0; j < program->frag_count; j++) { for (unsigned int j = 0; j < program->frag_count; j++) {
write_uniform_1i(program->iseed_locations[i * program->frag_count + j], write_uniform_1i(program->iseed_locations[i * program->frag_count + j],
@@ -788,7 +790,7 @@ static void use_program(const ShaderProgram *program, int i, bool output,
} }
void shaders_init(ShaderProgram *program, const Project *project, void shaders_init(ShaderProgram *program, const Project *project,
const SharedContext *context, bool rebind) { const Context *context, bool rebind) {
if (!rebind) { if (!rebind) {
program->error = false; program->error = false;
program->last_resolution[0] = context->resolution[0]; program->last_resolution[0] = context->resolution[0];
@@ -841,23 +843,36 @@ void shaders_init(ShaderProgram *program, const Project *project,
} }
} }
#ifdef VIDEO_IN
void shaders_relink_input(ShaderProgram *program, const Project *project, void shaders_relink_input(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs, unsigned int i) { VideoCaptureArray *inputs, unsigned int i) {
#ifdef VIDEO_IN
// shaders_free_input(program, i); // shaders_free_input(program, i);
init_input(program, &project->config, inputs, i, true); init_input(program, &project->config, inputs, i, true);
#endif /* VIDEO_IN */
} }
void shaders_link_inputs(ShaderProgram *program, const Project *project, void shaders_link_inputs(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs) { VideoCaptureArray *inputs) {
#ifdef VIDEO_IN
for (unsigned int i = 0; i < program->in_count; i++) { for (unsigned int i = 0; i < program->in_count; i++) {
init_input(program, &project->config, inputs, i, false); init_input(program, &project->config, inputs, i, false);
} }
#endif /* VIDEO_IN */
} }
void shaders_free_input(ShaderProgram *program, unsigned int input_index) {
if (program->dma_images[input_index] != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(program->egl_display, program->dma_images[input_index]);
program->dma_images[input_index] = EGL_NO_IMAGE_KHR;
}
if (program->dma_images_swap[input_index] != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(program->egl_display,
program->dma_images_swap[input_index]);
program->dma_images_swap[input_index] = EGL_NO_IMAGE_KHR;
}
check_eglerror_ro("shaders_free_input/eglDestroyImageKHR");
}
#endif /* VIDEO_IN */
void shaders_update(ShaderProgram *program, const File *fragment_shader, void shaders_update(ShaderProgram *program, const File *fragment_shader,
unsigned int i, const Project *project) { unsigned int i, const Project *project) {
if (compile_shader(program->fragment_shaders[i], fragment_shader->path, if (compile_shader(program->fragment_shaders[i], fragment_shader->path,
@@ -868,7 +883,7 @@ void shaders_update(ShaderProgram *program, const File *fragment_shader,
} }
} }
void shaders_compute(ShaderProgram *program, const SharedContext *context, void shaders_compute(ShaderProgram *program, const Context *context,
bool monitor, bool output_only) { bool monitor, bool output_only) {
if (!output_only) { if (!output_only) {
glBindVertexArray(program->vertex_array[0]); glBindVertexArray(program->vertex_array[0]);
@@ -914,18 +929,3 @@ void shaders_free_window(const ShaderProgram *program, bool secondary) {
glDeleteVertexArrays(1, &program->vertex_array[secondary ? 1 : 0]); glDeleteVertexArrays(1, &program->vertex_array[secondary ? 1 : 0]);
check_glerror_ro("shaders_free_window/glDeleteVertexArrays"); check_glerror_ro("shaders_free_window/glDeleteVertexArrays");
} }
void shaders_free_input(ShaderProgram *program, unsigned int input_index) {
#ifdef VIDEO_IN
if (program->dma_images[input_index] != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(program->egl_display, program->dma_images[input_index]);
program->dma_images[input_index] = EGL_NO_IMAGE_KHR;
}
if (program->dma_images_swap[input_index] != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(program->egl_display,
program->dma_images_swap[input_index]);
program->dma_images_swap[input_index] = EGL_NO_IMAGE_KHR;
}
check_eglerror_ro("shaders_free_input/eglDestroyImageKHR");
#endif /* VIDEO_IN */
}
+8 -4
View File
@@ -4,7 +4,9 @@
#define SHADERS_H #define SHADERS_H
void shaders_init(ShaderProgram *program, const Project *project, void shaders_init(ShaderProgram *program, const Project *project,
const SharedContext *context, bool rebind); const Context *context, bool rebind);
#ifdef VIDEO_IN
void shaders_relink_input(ShaderProgram *program, const Project *project, void shaders_relink_input(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs, unsigned int i); VideoCaptureArray *inputs, unsigned int i);
@@ -12,16 +14,18 @@ void shaders_relink_input(ShaderProgram *program, const Project *project,
void shaders_link_inputs(ShaderProgram *program, const Project *project, void shaders_link_inputs(ShaderProgram *program, const Project *project,
VideoCaptureArray *inputs); VideoCaptureArray *inputs);
void shaders_free_input(ShaderProgram *program, unsigned int input_index);
#endif /* VIDEO_IN */
void shaders_update(ShaderProgram *program, const File *fragment_shader, void shaders_update(ShaderProgram *program, const File *fragment_shader,
unsigned int i, const Project *project); unsigned int i, const Project *project);
void shaders_compute(ShaderProgram *program, const SharedContext *context, void shaders_compute(ShaderProgram *program, const Context *context,
bool monitor, bool output_only); bool monitor, bool output_only);
void shaders_free(const ShaderProgram *program); void shaders_free(const ShaderProgram *program);
void shaders_free_window(const ShaderProgram *program, bool secondary); void shaders_free_window(const ShaderProgram *program, bool secondary);
void shaders_free_input(ShaderProgram *program, unsigned int input_index);
#endif /* SHADERS_H */ #endif /* SHADERS_H */
-35
View File
@@ -1,35 +0,0 @@
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "types.h"
#include "shared.h"
static void *open_shared(const char *key, size_t size, int *fd) {
*fd = shm_open(key, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ftruncate(*fd, size);
return mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
}
static void close_shared(void *shared, size_t size, int fd) {
munmap(shared, size);
close(fd);
}
SharedContext *shared_init_context(const char *key) {
int shared_fd;
SharedContext *shared;
shared = open_shared(key, sizeof(SharedContext), &shared_fd);
shared->fd = shared_fd;
return shared;
}
void shared_close_context(SharedContext *shared) {
close_shared(shared, sizeof(SharedContext), shared->fd);
}
-9
View File
@@ -1,9 +0,0 @@
#include "types.h"
#ifndef SHARED_H
#define SHARED_H
SharedContext *shared_init_context(const char *key);
void shared_close_context(SharedContext *shared);
#endif /* SHARED_H */
+33 -42
View File
@@ -1,6 +1,6 @@
#include <log.h> #include <log.h>
#include <pthread.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include "types.h" #include "types.h"
@@ -20,8 +20,7 @@ static void safe_midi_write(const MidiDevice *midi, unsigned int code,
} }
} }
static void update_page(const SharedContext *context, static void update_page(const Context *context, const StateConfig *state_config,
const StateConfig *state_config,
const MidiDevice *midi) { const MidiDevice *midi) {
unsigned int page_item_min; unsigned int page_item_min;
unsigned int page_item_max; unsigned int page_item_max;
@@ -51,7 +50,7 @@ static void update_page(const SharedContext *context,
} }
} }
static void update_active(const SharedContext *context, static void update_active(const Context *context,
const StateConfig *state_config, const StateConfig *state_config,
const MidiDevice *midi) { const MidiDevice *midi) {
unsigned int k; unsigned int k;
@@ -66,8 +65,7 @@ static void update_active(const SharedContext *context,
} }
} }
static void update_values(const SharedContext *context, static void update_values(const Context *context, StateConfig *state_config,
const StateConfig *state_config,
const MidiDevice *midi) { const MidiDevice *midi) {
unsigned int j; unsigned int j;
unsigned int k; unsigned int k;
@@ -83,12 +81,12 @@ static void update_values(const SharedContext *context,
} }
} }
static void reset(SharedContext *context) { static void reset(Context *context) {
memset(context->values, 0, sizeof(context->values)); memset(context->values, 0, sizeof(context->values));
memset(context->state.values, 0, sizeof(context->state.values)); memset(context->state.values, 0, sizeof(context->state.values));
} }
static void randomize(SharedContext *context, const StateConfig *state_config) { static void randomize(Context *context, const StateConfig *state_config) {
unsigned int j; unsigned int j;
unsigned int l; unsigned int l;
unsigned int part; unsigned int part;
@@ -116,8 +114,7 @@ static void randomize(SharedContext *context, const StateConfig *state_config) {
} }
} }
static void load_from_file(SharedContext *context, static void load_from_file(Context *context, const StateConfig *state_config,
const StateConfig *state_config,
const char *state_file) { const char *state_file) {
ConfigFile saved_state; ConfigFile saved_state;
char key[STR_LEN]; char key[STR_LEN];
@@ -161,7 +158,7 @@ static void load_from_file(SharedContext *context,
config_file_free(&saved_state); config_file_free(&saved_state);
} }
static void load_from_default_file(SharedContext *context, static void load_from_default_file(Context *context,
const StateConfig *state_config) { const StateConfig *state_config) {
char state_file[STR_LEN]; char state_file[STR_LEN];
@@ -170,7 +167,7 @@ static void load_from_default_file(SharedContext *context,
load_from_file(context, state_config, state_file); load_from_file(context, state_config, state_file);
} }
static void load_from_index_file(SharedContext *context, static void load_from_index_file(Context *context,
const StateConfig *state_config, const StateConfig *state_config,
unsigned int index) { unsigned int index) {
char state_file[STR_LEN]; char state_file[STR_LEN];
@@ -181,7 +178,7 @@ static void load_from_index_file(SharedContext *context,
load_from_file(context, state_config, state_file); load_from_file(context, state_config, state_file);
} }
static void save_to_file(const SharedContext *context, static void save_to_file(const Context *context,
const StateConfig *state_config, const StateConfig *state_config,
const char *state_file) { const char *state_file) {
StringArray lines; StringArray lines;
@@ -220,7 +217,7 @@ static void save_to_file(const SharedContext *context,
file_write(state_file, &lines); file_write(state_file, &lines);
} }
static void save_to_default_file(const SharedContext *context, static void save_to_default_file(const Context *context,
const StateConfig *state_config) { const StateConfig *state_config) {
char state_file[STR_LEN]; char state_file[STR_LEN];
@@ -229,7 +226,7 @@ static void save_to_default_file(const SharedContext *context,
save_to_file(context, state_config, state_file); save_to_file(context, state_config, state_file);
} }
static void save_to_index_file(const SharedContext *context, static void save_to_index_file(const Context *context,
const StateConfig *state_config, const StateConfig *state_config,
unsigned int index) { unsigned int index) {
char state_file[STR_LEN]; char state_file[STR_LEN];
@@ -414,7 +411,7 @@ void state_parse_config(StateConfig *state_config, const ConfigFile *config) {
} }
} }
void state_midi_event(SharedContext *context, const StateConfig *state_config, void state_midi_event(Context *context, const StateConfig *state_config,
const MidiDevice *midi, unsigned char code, const MidiDevice *midi, unsigned char code,
unsigned char value, bool trace_midi) { unsigned char value, bool trace_midi) {
unsigned int i; unsigned int i;
@@ -508,7 +505,7 @@ void state_midi_event(SharedContext *context, const StateConfig *state_config,
} }
} }
void state_key_event(SharedContext *context, const StateConfig *state_config, void state_key_event(Context *context, const StateConfig *state_config,
unsigned int code, const MidiDevice *midi) { unsigned int code, const MidiDevice *midi) {
unsigned int index; unsigned int index;
@@ -565,29 +562,23 @@ void state_key_event(SharedContext *context, const StateConfig *state_config,
} }
} }
bool state_background_write(SharedContext *context, void *state_background_write(void *args) {
const StateConfig *state_config, StateBackgroundWriteArgs *process_args = (StateBackgroundWriteArgs *)args;
const MidiDevice *midi) { Context *context = process_args->context;
pid_t pid; StateConfig state_config = process_args->state_config;
MidiDevice *midi = process_args->midi;
bool beat_active; bool beat_active;
bool last_active; bool last_active;
bool change; bool change;
bool last_change; bool last_change;
pid = fork(); log_info("(state) background writing started");
if (pid < 0) {
log_error("Could not create subprocess");
return false;
}
if (pid == 0) {
return true;
}
log_info("(state) background writing started (pid: %d)", pid);
if (!midi->error) { if (!midi->error) {
update_page(context, state_config, midi); update_page(context, &state_config, midi);
update_active(context, state_config, midi); update_active(context, &state_config, midi);
update_values(context, state_config, midi); update_values(context, &state_config, midi);
} }
last_active = false; last_active = false;
@@ -597,11 +588,11 @@ bool state_background_write(SharedContext *context,
beat_active = tempo_progress(&context->tempo, 1.0) < 0.5; beat_active = tempo_progress(&context->tempo, 1.0) < 0.5;
if (!midi->error && beat_active != last_active) { if (!midi->error && beat_active != last_active) {
safe_midi_write(midi, state_config->tap_tempo_code, safe_midi_write(midi, state_config.tap_tempo_code,
beat_active ? MIDI_MAX : 0); beat_active ? MIDI_MAX : 0);
safe_midi_write(midi, safe_midi_write(midi,
state_config->select_frag_codes.values[context->selected], state_config.select_frag_codes.values[context->selected],
beat_active ? MIDI_MAX : 0); beat_active ? MIDI_MAX : 0);
} }
@@ -611,20 +602,20 @@ bool state_background_write(SharedContext *context,
(double)context->auto_random_cycle) < 0.5; (double)context->auto_random_cycle) < 0.5;
if (context->auto_random && change && !last_change) { if (context->auto_random && change && !last_change) {
randomize(context, state_config); randomize(context, &state_config);
update_values(context, state_config, midi); update_values(context, &state_config, midi);
} }
last_change = change; last_change = change;
} }
log_info("(state) background writing stopped by main thread (pid: %d)", pid); log_info("(state) background writing stopped by main thread");
exit(EXIT_SUCCESS); pthread_exit(NULL);
} }
void state_init(SharedContext *context, const StateConfig *state_config, void state_init(Context *context, const StateConfig *state_config, bool demo,
bool demo, bool auto_random, unsigned int auto_random_cycles, bool auto_random, unsigned int auto_random_cycles,
unsigned int base_tempo, bool load_state) { unsigned int base_tempo, bool load_state) {
tempo_init(&context->tempo, base_tempo); tempo_init(&context->tempo, base_tempo);
context->demo = demo; context->demo = demo;
@@ -655,6 +646,6 @@ void state_init(SharedContext *context, const StateConfig *state_config,
} }
} }
void state_save(const SharedContext *context, const StateConfig *state_config) { void state_save(const Context *context, const StateConfig *state_config) {
save_to_default_file(context, state_config); save_to_default_file(context, state_config);
} }
+6 -8
View File
@@ -5,21 +5,19 @@
void state_parse_config(StateConfig *state_config, const ConfigFile *config); void state_parse_config(StateConfig *state_config, const ConfigFile *config);
void state_midi_event(SharedContext *context, const StateConfig *state_config, void state_midi_event(Context *context, const StateConfig *state_config,
const MidiDevice *midi, unsigned char code, const MidiDevice *midi, unsigned char code,
unsigned char value, bool trace_midi); unsigned char value, bool trace_midi);
void state_key_event(SharedContext *context, const StateConfig *state_config, void state_key_event(Context *context, const StateConfig *state_config,
unsigned int code, const MidiDevice *midi); unsigned int code, const MidiDevice *midi);
bool state_background_write(SharedContext *context, void *state_background_write(void *args);
const StateConfig *state_config,
const MidiDevice *midi);
void state_init(SharedContext *context, const StateConfig *state_config, void state_init(Context *context, const StateConfig *state_config, bool demo,
bool demo, bool auto_random, unsigned int auto_random_cycles, bool auto_random, unsigned int auto_random_cycles,
unsigned int base_tempo, bool load_state); unsigned int base_tempo, bool load_state);
void state_save(const SharedContext *context, const StateConfig *state_config); void state_save(const Context *context, const StateConfig *state_config);
#endif /* STATE_H */ #endif /* STATE_H */
+65 -41
View File
@@ -135,34 +135,6 @@ typedef struct ShaderProgram {
#endif /* VIDEO_IN */ #endif /* VIDEO_IN */
} ShaderProgram; } ShaderProgram;
// video.c
typedef struct VideoCapture {
char name[STR_LEN];
bool error;
bool disconnected;
bool needs_reload;
bool with_swap;
bool swap;
unsigned int fps;
int fd;
int exp_fd;
int exp_fd_swap;
unsigned int width;
unsigned int height;
unsigned int pixelformat;
unsigned int bytesperline;
#ifdef VIDEO_IN
struct v4l2_buffer buf;
struct v4l2_buffer buf_swap;
#endif /* VIDEO_IN */
} VideoCapture;
typedef struct VideoCaptureArray {
VideoCapture values[MAX_VIDEO];
unsigned int length;
} VideoCaptureArray;
// window.c // window.c
typedef GLFWwindow Window; typedef GLFWwindow Window;
@@ -182,11 +154,16 @@ typedef struct Tempo {
// context.c // context.c
typedef struct SharedContext { typedef struct Context {
int fd; int fd;
vec2 resolution; vec2 resolution;
vec2 tex_resolution; vec2 tex_resolution;
#ifdef VIDEO_IN
vec2 input_resolutions[MAX_VIDEO]; vec2 input_resolutions[MAX_VIDEO];
unsigned int input_formats[MAX_VIDEO];
unsigned int input_fps[MAX_VIDEO];
bool input_swap[MAX_VIDEO];
#endif /* VIDEO_IN */
double time; double time;
Tempo tempo; Tempo tempo;
double tempo_total; double tempo_total;
@@ -200,9 +177,59 @@ typedef struct SharedContext {
unsigned int auto_random_cycle; unsigned int auto_random_cycle;
unsigned int seeds[MAX_FRAG]; unsigned int seeds[MAX_FRAG];
unsigned int fps; unsigned int fps;
VideoCaptureArray inputs; _Atomic bool stop;
bool stop; } Context;
} SharedContext;
// video.c
#ifdef VIDEO_IN
typedef struct VideoCapture {
char name[STR_LEN];
_Atomic bool error;
_Atomic bool disconnected;
_Atomic bool needs_reload;
bool with_swap;
_Atomic bool swap;
int fd;
int exp_fd;
int exp_fd_swap;
unsigned int width;
unsigned int height;
unsigned int pixelformat;
unsigned int bytesperline;
struct v4l2_buffer buf;
struct v4l2_buffer buf_swap;
} VideoCapture;
typedef struct VideoCaptureArray {
VideoCapture values[MAX_VIDEO];
unsigned int length;
} VideoCaptureArray;
typedef struct VideoBackgroundReadArgs {
VideoCapture *capture;
Context *context;
int input_index;
bool trace_fps;
} VideoBackgroundReadArgs;
#endif /* VIDEO_IN */
// midi.c
typedef struct MidiDevice {
bool error;
char name[STR_LEN];
snd_rawmidi_t *input;
snd_rawmidi_t *output;
} MidiDevice;
typedef struct MidiBackgroundListenArgs {
MidiDevice *device;
Context *context;
void (*event_callback)(unsigned char code, unsigned char value);
} MidiBackgroundReadArgs;
// state.c // state.c
@@ -240,6 +267,12 @@ typedef struct StateConfig {
UintArray hotkey_save; UintArray hotkey_save;
} StateConfig; } StateConfig;
typedef struct StateBackgroundWriteArgs {
Context *context;
StateConfig state_config;
MidiDevice *midi;
} StateBackgroundWriteArgs;
// timer.c // timer.c
typedef struct Timer { typedef struct Timer {
@@ -260,15 +293,6 @@ typedef struct ConfigFileItem {
char value[STR_LEN]; char value[STR_LEN];
} ConfigFileItem; } ConfigFileItem;
// midi.c
typedef struct MidiDevice {
bool error;
char name[STR_LEN];
snd_rawmidi_t *input;
snd_rawmidi_t *output;
} MidiDevice;
// project.c // project.c
typedef struct Project { typedef struct Project {
+18 -24
View File
@@ -4,8 +4,8 @@
#include <fcntl.h> #include <fcntl.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <log.h> #include <log.h>
#include <pthread.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
@@ -344,23 +344,17 @@ void video_init(VideoCapture *video_capture, const char *name,
create_image_buffers(video_capture); create_image_buffers(video_capture);
} }
bool video_background_read(VideoCapture *video_capture, SharedContext *context, void *video_background_read(void *args) {
int input_index, bool trace_fps) { VideoBackgroundReadArgs *process_args = (VideoBackgroundReadArgs *)args;
pid_t pid; VideoCapture *video_capture = process_args->capture;
Context *context = process_args->context;
int input_index = process_args->input_index;
bool trace_fps = process_args->trace_fps;
Timer timer; Timer timer;
double fps; double fps;
unsigned int video_result; unsigned int video_result;
pid = fork(); log_info("(%s) background acquisition started", video_capture->name);
if (pid < 0) {
log_error("Could not create subprocess");
return false;
}
if (pid == 0) {
return true;
}
log_info("(%s) background acquisition started (pid: %d)", video_capture->name,
pid);
timer_init(&timer, 30); timer_init(&timer, 30);
while (!context->stop && !video_capture->error) { while (!context->stop && !video_capture->error) {
@@ -368,26 +362,26 @@ bool video_background_read(VideoCapture *video_capture, SharedContext *context,
if (video_result > 0 && timer_inc(&timer)) { if (video_result > 0 && timer_inc(&timer)) {
fps = timer_reset(&timer); fps = timer_reset(&timer);
context->inputs.values[input_index].fps = (unsigned int)round(fps); context->input_fps[input_index] = (unsigned int)round(fps);
if (trace_fps) { if (trace_fps) {
log_trace("(%s) %.2ffps", video_capture->name, fps); log_trace("(%s) %.2ffps", video_capture->name, fps);
} }
} }
if (video_result > 0) { if (video_result > 0) {
video_capture->swap = video_result == 2; context->input_swap[input_index] = video_capture->swap =
video_result == 2;
} }
} }
if (context->stop) { if (context->stop) {
log_info("(%s) background acquisition stopped by main thread (pid: %d)", log_info("(%s) background acquisition stopped by main thread",
video_capture->name, pid); video_capture->name);
} else { } else {
log_info("(%s) background acquisition stopped after error (pid: %d)", log_info("(%s) background acquisition stopped after error",
video_capture->name, pid); video_capture->name);
video_capture->disconnected = true; video_capture->disconnected = true;
video_capture->pixelformat = 0; context->input_formats[input_index] = 0;
video_free(video_capture);
} }
exit(context->stop ? EXIT_SUCCESS : EXIT_FAILURE); pthread_exit(NULL);
} }
void video_free(const VideoCapture *video_capture) { void video_free(const VideoCapture *video_capture) {
@@ -396,7 +390,7 @@ void video_free(const VideoCapture *video_capture) {
close(video_capture->exp_fd); close(video_capture->exp_fd);
} }
if (video_capture->exp_fd_swap != -1) { if (video_capture->exp_fd_swap != -1) {
close(video_capture->exp_fd); close(video_capture->exp_fd_swap);
} }
if (video_capture->fd != -1) { if (video_capture->fd != -1) {
close(video_capture->fd); close(video_capture->fd);
+5 -2
View File
@@ -3,12 +3,15 @@
#ifndef VIDEO_H #ifndef VIDEO_H
#define VIDEO_H #define VIDEO_H
#ifdef VIDEO_IN
void video_init(VideoCapture *video_capture, const char *name, void video_init(VideoCapture *video_capture, const char *name,
unsigned int preferred_height); unsigned int preferred_height);
bool video_background_read(VideoCapture *video_capture, SharedContext *context, void *video_background_read(void *args);
int input_index, bool trace_fps);
void video_free(const VideoCapture *video_capture); void video_free(const VideoCapture *video_capture);
#endif /* VIDEO_IN */
#endif /* VIDEO_H */ #endif /* VIDEO_H */
+1 -1
View File
@@ -123,7 +123,7 @@ void window_events() { glfwPollEvents(); }
double window_get_time() { return glfwGetTime(); } double window_get_time() { return glfwGetTime(); }
void window_use(Window *window, SharedContext *context) { void window_use(Window *window, Context *context) {
int width; int width;
int height; int height;
+1 -1
View File
@@ -15,7 +15,7 @@ void window_update_title(Window *window, const char *title);
double window_get_time(); double window_get_time();
void window_use(Window *window, SharedContext *context); void window_use(Window *window, Context *context);
void window_refresh(Window *window); void window_refresh(Window *window);