diff --git a/Makefile.dev b/Makefile.dev index 5a93677..713dbf3 100644 --- a/Makefile.dev +++ b/Makefile.dev @@ -1,6 +1,6 @@ TARGET ?= forge INSTALL_DIR ?= $(HOME)/.local/bin -TEST_ARGS ?= --frag=./shaders --frag-config=./config/shaders.cfg --tempo=30 +TEST_ARGS ?= --frag=./shaders --frag-config=./config/shaders.cfg --video-in=/dev/video0 --tempo=30 SHELL := /bin/bash .PHONY: build diff --git a/README.md b/README.md index 8dc1ad4..321777c 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,8 @@ make -f Makefile.dev release-arch - [ ] Tap-tempo feature - [ ] Clean code and fix things - [ ] Video input - - [ ] Fixed camera video + - [x] Fixed camera video + - [ ] Pass video info to shaders - [ ] Sub process video reading - [ ] Shader based format mapping - [ ] Video mapping config file diff --git a/config/shaders.cfg b/config/shaders.cfg index 51a5c2f..c35a3fd 100644 --- a/config/shaders.cfg +++ b/config/shaders.cfg @@ -12,13 +12,19 @@ TEX_COUNT=8 FRAG_COUNT=8 FRAG_OUTPUT=7 FRAG_MONITOR=8 + FRAG_1_OUT=2 -FRAG_2_OUT=5 FRAG_3_OUT=3 +FRAG_2_OUT=5 FRAG_4_OUT=6 FRAG_5_OUT=7 FRAG_6_OUT=0 +IN_COUNT=2 + +IN_1_OUT=1 +IN_2_OUT=4 + SUB_TYPE_COUNT=3 SUB_1_PREFIX=src_ SUB_2_PREFIX=fx_ diff --git a/src/args.c b/src/args.c index 37e9b81..3203e42 100644 --- a/src/args.c +++ b/src/args.c @@ -23,6 +23,7 @@ static void print_help(int status_code) { "[-mo] " "[-f=DIR_PATH] " "[-fc=CFG_PATH] " + "[-v=FILE] " "[-is=SIZE] " "[-t=TEMPO] " "[--demo] " @@ -39,6 +40,7 @@ static void print_help(int status_code) { " -f, --frag fragment shaders directory (default: TODO)\n" " -fc, --frag-config fragment shaders config file (default: " "TODO)\n" + " -v, --video-in path to video device (multiple allowed)\n" " -is, --internal-size internal texture height (default: 720)\n" "(default: " "3)\n" @@ -93,6 +95,7 @@ Parameters args_parse(int argc, char **argv) { params.base_tempo = 60.0f; params.demo = false; params.windowed = false; + params.video_count = 0; for (i = 1; i < argc; i++) { arg = argv[i]; @@ -110,6 +113,8 @@ Parameters args_parse(int argc, char **argv) { params.frag_path = value; } else if (is_arg(arg, "-fc") || is_arg(arg, "--frag-config")) { params.frag_config_path = value; + } else if (is_arg(arg, "-v") || is_arg(arg, "--video-in")) { + params.video_in[params.video_count++] = value; } else if (is_arg(arg, "-t") || is_arg(arg, "--tempo")) { params.base_tempo = (float)parse_uint(arg, value); } else if (is_arg(arg, "-is") || is_arg(arg, "--internal-size")) { diff --git a/src/config.h b/src/config.h index 931b02d..cbf934f 100644 --- a/src/config.h +++ b/src/config.h @@ -9,4 +9,8 @@ #define VERSION "(dev)" #endif /* VERSION */ +#ifndef MAX_VIDEO +#define MAX_VIDEO 256 +#endif + #endif /* CONFIG_H */ \ No newline at end of file diff --git a/src/forge.c b/src/forge.c index 12974b8..2aba87d 100644 --- a/src/forge.c +++ b/src/forge.c @@ -12,19 +12,25 @@ #include "shaders.h" #include "timer.h" #include "types.h" +#include "video.h" #include "window.h" static Context context; static ShaderProgram program; static Window *window_output; static Window *window_monitor; +static VideoDevice *devices; +static File *fragment_shaders; +static File common_shader_code; +static Timer timer; +static ConfigFile shader_config; -static unsigned int compute_fps(Timer *timer) { +static unsigned int compute_fps() { static double fps; char title[100]; - if (timer_inc(timer)) { - fps = timer_reset(timer); + if (timer_inc(&timer)) { + fps = timer_reset(&timer); if (window_output != NULL) { sprintf(title, PACKAGE " " VERSION " - %.0ffps", fps); @@ -76,56 +82,27 @@ static void free_context() { free(context.seeds); } -static void hot_reload(File *common_shader_code, File *fragment_shaders) { +static void hot_reload() { unsigned int i; bool force_update; force_update = false; - if (file_should_update(*common_shader_code)) { - file_update(common_shader_code); + if (file_should_update(common_shader_code)) { + file_update(&common_shader_code); force_update = true; } for (i = 0; i < program.frag_count; i++) { if (force_update || file_should_update(fragment_shaders[i])) { file_update(&fragment_shaders[i]); - file_prepend(&fragment_shaders[i], *common_shader_code); + file_prepend(&fragment_shaders[i], common_shader_code); shaders_update(program, fragment_shaders, i); } } } -static void loop(bool hr, File *common_shader_code, File *fragment_shaders, - Timer *timer) { - if (hr) { - hot_reload(common_shader_code, fragment_shaders); - } - - context.fps = compute_fps(timer); - - context.time = window_get_time(); - - if (window_output != NULL) { - window_use(window_output, &context); - - shaders_compute(program, context, false, false); - - window_refresh(window_output); - } - - if (window_monitor != NULL) { - window_use(window_monitor, &context); - - shaders_compute(program, context, true, window_output != NULL); - - window_refresh(window_monitor); - } - - window_events(); -} - File read_fragment_shader_file(char *frag_path, unsigned int i) { File fragment_shader; char *file_path; @@ -141,30 +118,64 @@ File read_fragment_shader_file(char *frag_path, unsigned int i) { return fragment_shader; } -static void init_files(char *frag_path, File *common_shader_code, - File *fragment_shaders, unsigned int frag_count) { +static void init_files(char *frag_path, unsigned int frag_count) { unsigned int i; + fragment_shaders = malloc(frag_count * sizeof(File)); + for (i = 0; i < frag_count + 1; i++) { if (i == 0) { - (*common_shader_code) = read_fragment_shader_file(frag_path, i); + common_shader_code = read_fragment_shader_file(frag_path, i); } else { fragment_shaders[i - 1] = read_fragment_shader_file(frag_path, i); - file_prepend(&fragment_shaders[i - 1], *common_shader_code); + file_prepend(&fragment_shaders[i - 1], common_shader_code); } } } -static void free_files(File *common_shader_code, File *fragment_shaders, - unsigned int frag_count) { +static void free_files(unsigned int frag_count) { unsigned int i; for (i = 0; i < frag_count; i++) { file_free(&fragment_shaders[i], true); } - file_free(common_shader_code, true); + file_free(&common_shader_code, true); +} + +static void init_devices(char *video_in[MAX_VIDEO], unsigned int video_count) { + unsigned int i; + + devices = malloc(video_count * sizeof(VideoDevice)); + + for (i = 0; i < video_count; i++) { + devices[i] = video_init(video_in[i], 640, 480); // TODO define in args + } +} + +static void update_devices(unsigned int video_count) { + unsigned int i; + + for (i = 0; i < video_count; i++) { + if (!devices[i].error) { + if (!video_read(&devices[i])) { + video_free(devices[i]); // TODO hot reload of video + } + } + } +} + +static void free_devices(unsigned int video_count) { + unsigned int i; + + for (i = 0; i < video_count; i++) { + shaders_free_video(program, devices[i]); + + video_free(devices[i]); + } + + free(devices); } static void error_callback(int error, const char *description) { @@ -188,33 +199,59 @@ static void key_callback(Window *window, int key, } } +static void loop(bool hr, unsigned int video_count) { + if (hr) { + hot_reload(); + } + + context.fps = compute_fps(); + + context.time = window_get_time(); + + update_devices(video_count); + + if (window_output != NULL) { + window_use(window_output, &context); + + shaders_compute(program, context, false, false); + + window_refresh(window_output); + } + + if (window_monitor != NULL) { + window_use(window_monitor, &context); + + shaders_compute(program, context, true, window_output != NULL); + + window_refresh(window_monitor); + } + + window_events(); +} + void forge_run(Parameters params) { unsigned int frag_count; - File *fragment_shaders; - File common_shader_code; - Timer timer; - ConfigFile shader_config; shader_config = config_file_read(params.frag_config_path, false); frag_count = config_file_get_int(shader_config, "FRAG_COUNT", 6); - fragment_shaders = malloc(frag_count * sizeof(File)); - - init_files(params.frag_path, &common_shader_code, fragment_shaders, - frag_count); + init_files(params.frag_path, frag_count); window_startup(error_callback); context.internal_size = params.internal_size; + init_devices(params.video_in, params.video_count); + if (params.output) { window_output = window_init(PACKAGE " " VERSION, params.output_screen, params.windowed, NULL, key_callback); window_use(window_output, &context); - program = shaders_init(fragment_shaders, shader_config, context, NULL); + program = shaders_init(fragment_shaders, shader_config, context, devices, + params.video_count, NULL); } else { window_output = NULL; } @@ -226,7 +263,8 @@ void forge_run(Parameters params) { window_use(window_monitor, &context); - program = shaders_init(fragment_shaders, shader_config, context, + program = shaders_init(fragment_shaders, shader_config, context, devices, + params.video_count, window_output != NULL ? &program : NULL); } else { window_monitor = NULL; @@ -245,7 +283,7 @@ void forge_run(Parameters params) { while ((window_output == NULL || !window_should_close(window_output)) && (window_monitor == NULL || !window_should_close(window_monitor))) { - loop(params.hot_reload, &common_shader_code, fragment_shaders, &timer); + loop(params.hot_reload, params.video_count); } shaders_free(program); @@ -262,11 +300,13 @@ void forge_run(Parameters params) { shaders_free_window(program, params.output); } + free_devices(params.video_count); + window_terminate(); free_context(); - free_files(&common_shader_code, fragment_shaders, frag_count); + free_files(frag_count); config_file_free(shader_config); } \ No newline at end of file diff --git a/src/shaders.c b/src/shaders.c index 89ccf53..6749e08 100644 --- a/src/shaders.c +++ b/src/shaders.c @@ -99,6 +99,13 @@ static void link_video_to_texture(ShaderProgram *program, VideoDevice *device, return; } + glActiveTexture(GL_TEXTURE0 + texture_index); + + glBindTexture(GL_TEXTURE_2D, program->textures[texture_index]); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, device->width, device->height, 0, + GL_RGB, GL_UNSIGNED_BYTE, 0); + // https://registry.khronos.org/OpenGL/extensions/EXT/EXT_EGL_image_storage.txt glEGLImageTargetTextureStorageEXT(program->textures[texture_index], (GLeglImageOES)device->dma_image, NULL); @@ -106,6 +113,23 @@ static void link_video_to_texture(ShaderProgram *program, VideoDevice *device, log_success("Texture %d linked to %s", texture_index, device->name); } +static void init_videos(ShaderProgram *program, ConfigFile shader_config, + VideoDevice *devices, unsigned int device_count) { + unsigned int i; + unsigned tex_i; + char name[32]; + + for (i = 0; i < program->in_count; i++) { + if (i < device_count && !devices[i].error) { + sprintf(name, "IN_%d_OUT", i + 1); + tex_i = config_file_get_int(shader_config, name, 0); + link_video_to_texture(program, &devices[i], tex_i); + } else { + log_warn("Cannot link input %d", i + 1); + } + } +} + static void init_framebuffers(ShaderProgram *program, ConfigFile shader_config) { unsigned int i; @@ -320,7 +344,8 @@ static void init_programs(ShaderProgram *program, ConfigFile shader_config) { } ShaderProgram shaders_init(File *fragment_shaders, ConfigFile shader_config, - Context context, ShaderProgram *previous) { + Context context, VideoDevice *devices, + unsigned int device_count, ShaderProgram *previous) { ShaderProgram program; if (previous == NULL) { @@ -335,8 +360,7 @@ ShaderProgram shaders_init(File *fragment_shaders, ConfigFile shader_config, config_file_get_int(shader_config, "FRAG_MONITOR", 1) - 1; program.sub_type_count = config_file_get_int(shader_config, "SUB_TYPE_COUNT", 0); - program.sub_variant_count = - config_file_get_int(shader_config, "SUB_VARIANT_COUNT", 1); + program.in_count = config_file_get_int(shader_config, "IN_COUNT", 0); init_gl(&program); @@ -348,6 +372,8 @@ ShaderProgram shaders_init(File *fragment_shaders, ConfigFile shader_config, init_textures(&program, context); + init_videos(&program, shader_config, devices, device_count); + init_framebuffers(&program, shader_config); init_programs(&program, shader_config); diff --git a/src/shaders.h b/src/shaders.h index 053c170..66c73c3 100644 --- a/src/shaders.h +++ b/src/shaders.h @@ -4,7 +4,8 @@ #define SHADERS_H ShaderProgram shaders_init(File *fragment_shaders, ConfigFile shader_config, - Context context, ShaderProgram *shared_program); + Context context, VideoDevice *devices, + unsigned int device_count, ShaderProgram *previous); void shaders_update(ShaderProgram program, File *fragment_shaders, unsigned int i); diff --git a/src/types.h b/src/types.h index 1a3303d..30dc711 100644 --- a/src/types.h +++ b/src/types.h @@ -8,21 +8,25 @@ #include #include +#include "config.h" + #ifndef TYPES_H #define TYPES_H typedef struct Parameters { bool hot_reload; bool output; - unsigned char output_screen; + unsigned int output_screen; bool monitor; - unsigned char monitor_screen; + unsigned int monitor_screen; char *frag_path; char *frag_config_path; unsigned int internal_size; float base_tempo; bool demo; bool windowed; + char *video_in[MAX_VIDEO]; + unsigned int video_count; } Parameters; typedef struct Vertex { @@ -75,6 +79,7 @@ typedef struct ShaderProgram { unsigned int sub_variant_count; GLuint *sub_locations; + unsigned int in_count; EGLDisplay egl_display; } ShaderProgram; diff --git a/src/video.c b/src/video.c index 2d54306..69d3cd5 100644 --- a/src/video.c +++ b/src/video.c @@ -194,14 +194,30 @@ VideoDevice video_init(char *name, unsigned int preferred_width, return device; } -void video_read(VideoDevice device) { - ioctl(device.fd, VIDIOC_DQBUF, &device.buf); - ioctl(device.fd, VIDIOC_QBUF, &device.buf); - // TODO check for error and close +bool video_read(VideoDevice *device) { + if (ioctl(device->fd, VIDIOC_DQBUF, &device->buf) == -1) { + log_warn("(%s) Video error", device->name); + device->error = true; + return false; + } + + if (ioctl(device->fd, VIDIOC_QBUF, &device->buf) == -1) { + log_warn("(%s) Video error", device->name); + device->error = true; + return false; + } + + return true; } void video_free(VideoDevice device) { - close_stream(device); - close(device.exp_fd); - close(device.fd); + if (!device.error) { + close_stream(device); + } + if (device.exp_fd != -1) { + close(device.exp_fd); + } + if (device.fd != -1) { + close(device.fd); + } } \ No newline at end of file diff --git a/src/video.h b/src/video.h index 4d7d38c..c04784a 100644 --- a/src/video.h +++ b/src/video.h @@ -6,7 +6,7 @@ VideoDevice video_init(char *name, unsigned int preferred_width, unsigned int preferred_height); -void video_read(VideoDevice device); +bool video_read(VideoDevice *device); void video_free(VideoDevice device);