From bfb21af0c876a7d3034eb0c8419c74d6944d7236 Mon Sep 17 00:00:00 2001 From: L-Nafaryus Date: Thu, 25 Jul 2024 13:27:58 +0500 Subject: [PATCH] little cleanup, add space for mute feature --- .clangd | 2 + CMakeLists.txt | 32 +-- flake.nix | 91 ++++---- src/image-reaction.c | 499 +++++++++++++++++++++++++------------------ 4 files changed, 362 insertions(+), 262 deletions(-) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..ec0b890 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + CompilationDatabase: build diff --git a/CMakeLists.txt b/CMakeLists.txt index c2303d8..079155d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.16) project(image-reaction VERSION 1.3) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + set(PLUGIN_AUTHOR "scaled") set(PLUGIN_GIT ${PROJECT_NAME}) set(LINUX_MAINTAINER_EMAIL "scaled@scaledteam.ru") @@ -19,30 +21,32 @@ add_library(image-reaction MODULE src/image-reaction.c) target_link_libraries(image-reaction OBS::libobs) if(OS_WINDOWS) - # Enable Multicore Builds and disable FH4 (to not depend on VCRUNTIME140_1.DLL when building with VS2019) - if(MSVC) - add_definitions(/MP /d2FH4-) - endif() + # Enable Multicore Builds and disable FH4 (to not depend on VCRUNTIME140_1.DLL + # when building with VS2019) + if(MSVC) + add_definitions(/MP /d2FH4-) + endif() - target_link_libraries(${PROJECT_NAME} OBS::w32-pthreads) + target_link_libraries(${PROJECT_NAME} OBS::w32-pthreads) - configure_file(cmake/installer/installer-Windows.iss.in installer-Windows.generated.iss) + configure_file(cmake/installer/installer-Windows.iss.in + installer-Windows.generated.iss) endif() if(OS_LINUX) - target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra) + target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra) endif() if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -fvisibility=default") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -fvisibility=default") - set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") - set(MACOSX_PLUGIN_GUI_IDENTIFIER "${MACOS_BUNDLEID}") - set(MACOSX_PLUGIN_BUNDLE_VERSION "${PROJECT_VERSION}") - set(MACOSX_PLUGIN_SHORT_VERSION_STRING "1") + set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") + set(MACOSX_PLUGIN_GUI_IDENTIFIER "${MACOS_BUNDLEID}") + set(MACOSX_PLUGIN_BUNDLE_VERSION "${PROJECT_VERSION}") + set(MACOSX_PLUGIN_SHORT_VERSION_STRING "1") - configure_file(cmake/installer/installer-macOS.pkgproj.in installer-macOS.generated.pkgproj) + configure_file(cmake/installer/installer-macOS.pkgproj.in + installer-macOS.generated.pkgproj) endif() setup_plugin_target(${PROJECT_NAME}) - diff --git a/flake.nix b/flake.nix index 89c9314..bfcf2ae 100644 --- a/flake.nix +++ b/flake.nix @@ -1,52 +1,59 @@ { - description = "OBS Studio plugin with image that reacts to sound source"; + description = "OBS Studio plugin with image that reacts to sound source"; - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - }; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; - outputs = { self, nixpkgs, ... }: - let - forAllSystems = nixpkgs.lib.genAttrs [ "x86_64-linux" "i686-linux" ]; - nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; }); - inherit (nixpkgs) lib; + outputs = { + self, + nixpkgs, + ... + }: let + forAllSystems = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"]; + nixpkgsFor = forAllSystems (system: import nixpkgs {inherit system;}); + inherit (nixpkgs) lib; + in { + packages = forAllSystems (system: let + pkgs = nixpkgsFor.${system}; in { - packages = forAllSystems (system: - let pkgs = nixpkgsFor.${system}; - in { - obs-image-reaction = pkgs.stdenv.mkDerivation { - pname = "obs-image-reaction"; - version = "1.3"; + obs-image-reaction = pkgs.stdenv.mkDerivation { + pname = "obs-image-reaction"; + version = "1.3"; - src = ./.; - - nativeBuildInputs = with pkgs; [ cmake ]; - buildInputs = with pkgs; [ obs-studio ]; + src = ./.; - postInstall = '' - mkdir $out/lib $out/share - mv $out/obs-plugins/64bit $out/lib/obs-plugins - rm -rf $out/obs-plugins - mv $out/data $out/share/obs - ''; + nativeBuildInputs = with pkgs; [cmake]; + buildInputs = with pkgs; [obs-studio]; - meta = with lib; { - description = "OBS Studio plugin with image that reacts to sound source"; - homepage = "https://github.com/L-Nafaryus/obs-image-reaction"; - maintainers = []; - license = licenses.gpl2Plus; - platforms = [ "x86_64-linux" "i686-linux" ]; - }; - }; + postInstall = '' + mkdir $out/lib $out/share + mv $out/obs-plugins/64bit $out/lib/obs-plugins + rm -rf $out/obs-plugins + mv $out/data $out/share/obs + ''; - default = self.packages.${system}.obs-image-reaction; - }); + meta = with lib; { + description = "OBS Studio plugin with image that reacts to sound source"; + homepage = "https://github.com/L-Nafaryus/obs-image-reaction"; + maintainers = []; + license = licenses.gpl2Plus; + platforms = ["x86_64-linux" "i686-linux"]; + }; + }; - devShells = forAllSystems (system: let pkgs = nixpkgsFor.${system}; in { - default = pkgs.mkShell { - buildInputs = with pkgs; [ gcc cmake gnumake obs-studio ]; - }; - }); - }; + default = self.packages.${system}.obs-image-reaction; + }); + + devShells = forAllSystems (system: let + pkgs = nixpkgsFor.${system}; + in { + default = with pkgs; + mkShell rec { + nativeBuildInputs = [cmake gnumake]; + buildInputs = [obs-studio]; + LD_LIBRARY_PATH = lib.makeLibraryPath buildInputs; + }; + }); + }; } - diff --git a/src/image-reaction.c b/src/image-reaction.c index 1819358..0cf180a 100644 --- a/src/image-reaction.c +++ b/src/image-reaction.c @@ -20,101 +20,263 @@ #define info(format, ...) blog(LOG_INFO, format, ##__VA_ARGS__) #define warn(format, ...) blog(LOG_WARNING, format, ##__VA_ARGS__) +static const char* image_reaction_get_name(void* unused); +static void image_reaction_defaults(obs_data_t* data); +static void* image_reaction_create(obs_data_t* data, obs_source_t* source); +static void image_reaction_destroy(void* context); +static void image_reaction_show(void* context); +static void image_reaction_hide(void* context); +static uint32_t image_reaction_get_width(void* context); +static uint32_t image_reaction_get_height(void* context); +static void image_reaction_update(void* context, obs_data_t* data); +static void image_reaction_render(void* context, gs_effect_t* effect); +static void image_reaction_tick(void* context, float seconds); +static obs_properties_t* image_reaction_properties(void* context); + +static struct obs_source_info image_reaction_info = { + .id = "image_reaction", + .type = OBS_SOURCE_TYPE_INPUT, + .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_SRGB, + .get_name = image_reaction_get_name, + .get_defaults = image_reaction_defaults, + .create = image_reaction_create, + .destroy = image_reaction_destroy, + .show = image_reaction_show, + .hide = image_reaction_hide, + .get_width = image_reaction_get_width, + .get_height = image_reaction_get_height, + .update = image_reaction_update, + .video_render = image_reaction_render, + .video_tick = image_reaction_tick, + .get_properties = image_reaction_properties, + .icon_type = OBS_ICON_TYPE_IMAGE, +}; + +OBS_DECLARE_MODULE() +OBS_MODULE_USE_DEFAULT_LOCALE("image-reaction", "en-US") +MODULE_EXPORT const char* obs_module_description(void) { return "Image reaction"; } + +bool obs_module_load(void) { + obs_register_source(&image_reaction_info); + return true; +} + #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) -struct image_reaction_source { +enum volume_state { Silent, Loud, Muted }; + +struct image_reaction { obs_source_t* source; char source_name[255]; - char* file1; - char* file2; - bool persistent; - bool linear_alpha; - bool active; - - gs_image_file3_t if31; - gs_image_file3_t if32; - obs_weak_source_t* audio_source; - bool loud; - float threshold; - float smoothness; - float average; + bool persistent; + + bool active; + + char* idle_filename; + char* action_filename; + char* mute_filename; + gs_image_file3_t idle_image; + gs_image_file3_t action_image; + gs_image_file3_t mute_image; + + float average_volume; + enum volume_state volume_state; + bool is_volume_changed; uint64_t last_time; uint64_t capture_check_time; - bool animReset1; - bool animReset2; bool loudOld; bool animResetTrigger; + + // options + bool apply_alpha; + float volume_threshold; + float volume_smoothness; + + bool reset_idle_animation; + bool reset_action_animation; + bool reset_muted_animation; + + bool unload_images; }; -static const char* image_reaction_source_get_name(void* unused) { - UNUSED_PARAMETER(unused); - return obs_module_text("ImageReactionSource"); +static const char* image_filter = "All formats (*.bmp *.tga *.png *.jpeg *.jpg *.gif *.psd *.webp);;" + "BMP Files (*.bmp);;" + "Targa Files (*.tga);;" + "PNG Files (*.png);;" + "JPEG Files (*.jpeg *.jpg);;" + "GIF Files (*.gif);;" + "PSD Files (*.psd);;" + "WebP Files (*.webp);;" + "All Files (*.*)"; + +static const char* image_reaction_get_name(void*) { return obs_module_text("ImageReaction"); } + +static void image_reaction_defaults(obs_data_t* data) { + obs_data_set_default_bool(data, "unload", false); + obs_data_set_default_bool(data, "apply_alpha", false); + obs_data_set_default_string(data, "audio_source", ""); + obs_data_set_default_double(data, "threshold", -40.0f); + obs_data_set_default_double(data, "smoothness", 1.0f); } -static void image_reaction_source_load(struct image_reaction_source* context) { - for (int i = 0; i <= 1; i++) { - char* file = i == 0 ? context->file1 : context->file2; - gs_image_file3_t* if3 = i == 0 ? &context->if31 : &context->if32; +static void* image_reaction_create(obs_data_t* data, obs_source_t* source) { + struct image_reaction* ctx = bzalloc(sizeof(struct image_reaction)); + + ctx->source = source; + ctx->source_name[0] = '\0'; + ctx->loud = false; + + image_reaction_update(ctx, data); + + return ctx; +} + +static void image_reaction_destroy(void* context) { + struct image_reaction_source* ctx = context; + + image_reaction_unload(ctx); + + remove_audio_source(ctx); + + if (ctx->idle_image) { + bfree(ctx->idle_image); + } + + if (ctx->action_image) { + bfree(ctx->action_image); + } + + if (ctx->mute_image) { + bfree(ctx->mute_image); + } + + bfree(ctx); +} + +static void image_reaction_show(void* context) { + struct image_reaction_source* ctx = context; + + if (!ctx->persistent) { + image_reaction_load(context); + } +} + +static void image_reaction_hide(void* context) { + struct image_reaction_source* ctx = context; + + if (!cxt->persistent) { + image_reaction_unload(cxt); + } +} + +static uint32_t image_reaction_get_width(void* context) { + struct image_reaction_source* ctx = context; + uint32_t max = 0; + + if (ctx->idle_image.image2.image.loaded && ctx->idle_image.image2.image.cx > max) { + max = ctx->idle_image.image2.image.cx; + } + if (ctx->action_image.image2.image.loaded && ctx->action_image.image2.image.cx > max) { + max = ctx->action_image.image2.image.cx; + } + if (ctx->mute_image.image2.image.loaded && ctx->mute_image.image2.image.cx > max) { + max = ctx->mute_image.image2.image.cx; + } + + return max; +} + +static uint32_t image_reaction_get_height(void* context) { + struct image_reaction_source* ctx = context; + uint32_t max = 0; + + if (ctx->idle_image.image2.image.loaded && ctx->idle_image.image2.image.cy > max) { + max = ctx->idle_image.image2.image.cy; + } + if (ctx->action_image.image2.image.loaded && ctx->action_image.image2.image.cy > max) { + max = ctx->action_image.image2.image.cy; + } + if (ctx->mute_image.image2.image.loaded && ctx->mute_image.image2.image.cy > max) { + max = ctx->mute_image.image2.image.cy; + } + + return max; +} + +static void add_image(static image_reaction* ctx, char* filename, gs_image_file3_t* image) { + obs_enter_graphics(); + gs_image_file3_free(image); + obs_leave_graphics(); + + if (filename && *filename) { + debug("Loading image %s", filename); + + gs_image_file3_init(image, filename, ctx->apply_alpha ? GS_IMAGE_ALPHA_PREMULTIPLY_SRGB ? GS_IMAGE_ALPHA_PREMULTIPLY); obs_enter_graphics(); - gs_image_file3_free(if3); + gs_image_file3_init_texture(image); obs_leave_graphics(); - if (file && *file) { - debug("loading texture '%s'", file); - gs_image_file3_init(if3, file, - context->linear_alpha ? GS_IMAGE_ALPHA_PREMULTIPLY_SRGB : GS_IMAGE_ALPHA_PREMULTIPLY); - - obs_enter_graphics(); - gs_image_file3_init_texture(if3); - obs_leave_graphics(); - - if (!if3->image2.image.loaded) - warn("failed to load texture '%s'", file); + if (!image->image2.image.loaded) { + warn("Failed to load image %s", filename); } } } -static void image_reaction_source_unload(struct image_reaction_source* context) { - obs_enter_graphics(); - gs_image_file3_free(&context->if31); - gs_image_file3_free(&context->if32); +static void load(struct image_reaction* ctx) { + load_image(ctx->idle_filename, &ctx->idle_image); + load_image(ctx->action_filename, &ctx->action_image); + load_image(ctx->mute_filename, &ctx->mute_image); +} + +static void remove_image(struct image_reaction* ctx, gs_image_file3_t* image) { + if () + + obs_enter_graphics(); + gs_image_file3_free(&ctx->idle_image); + gs_image_file3_free(&ctx->action_image); + gs_image_file3_free(&ctx->mute_image); obs_leave_graphics(); } -static void audio_capture(void* param, obs_source_t* src, const struct audio_data* data, bool muted) { - struct image_reaction_source* context = param; +static void audio_source_capture(void* context, obs_source_t* source, const struct audio_data* adata, bool muted) { + struct image_reaction* ctx = context; + enum volume_state last_volume_state = ctx->volume_state; if (muted) { - context->average = 0; + ctx->volume_state = Muted; + ctx->average_volume = 0; } else { - uint32_t samplesCount = data->frames; + uint32_t samples_count = data->frames; float* samples = (float*)data->data[0]; + float average = 0.0f; - float averageLocal = 0.0f; - - for (uint32_t i = 0; i < samplesCount; i++) { - averageLocal += fabs(samples[i]) / samplesCount; + for (uint32_t n = 0; n < samples_count; n++) { + average += fabs(samples[n]) / samples_count; } - context->average += context->smoothness * (averageLocal - context->average); + ctx->average_volume += ctx->volume_smoothness * (average - ctx->average_volume); + + if (ctx->average_volume > ctx->volume_threshold) { + ctx->volume_state = Loud; + } else { + ctx->volume_state = Silent; + } } - context->loudOld = context->loud; - context->loud = context->average > context->threshold; - - if (context->loud != context->loudOld) - context->animResetTrigger = true; + if (ctx->volume_state != last_volume_state) { + ctx->is_volume_changed = true; + } } -static void image_reaction_source_update(void* data, obs_data_t* settings) { - struct image_reaction_source* context = data; +static void image_reaction_update(void* context, obs_data_t* data) { + struct image_reaction_source* ctx = context; const char* file1 = obs_data_get_string(settings, "file1"); const char* file2 = obs_data_get_string(settings, "file2"); const bool anim_reset_1 = obs_data_get_bool(settings, "anim_reset_1"); @@ -124,46 +286,46 @@ static void image_reaction_source_update(void* data, obs_data_t* settings) { const double threshold = obs_data_get_double(settings, "threshold"); const double smoothness = obs_data_get_double(settings, "smoothness"); - if (context->file1) - bfree(context->file1); - context->file1 = bstrdup(file1); + if (ctx->file1) + bfree(ctx->file1); + ctx->file1 = bstrdup(file1); - if (context->file2) - bfree(context->file2); - context->file2 = bstrdup(file2); + if (ctx->file2) + bfree(ctx->file2); + ctx->file2 = bstrdup(file2); - context->animReset1 = anim_reset_1; - context->animReset2 = anim_reset_2; + ctx->animReset1 = anim_reset_1; + ctx->animReset2 = anim_reset_2; - context->persistent = !unload; - context->linear_alpha = linear_alpha; - context->threshold = db_to_mul(threshold); - context->smoothness = pow(0.1, smoothness); + ctx->persistent = !unload; + ctx->linear_alpha = linear_alpha; + ctx->threshold = db_to_mul(threshold); + ctx->smoothness = pow(0.1, smoothness); /* Load the image if the source is persistent or showing */ - if (context->persistent || obs_source_showing(context->source)) - image_reaction_source_load(data); + if (ctx->persistent || obs_source_showing(ctx->source)) + image_reaction_load(context); else - image_reaction_source_unload(data); + image_reaction_unload(context); const char* cfg_source_name = obs_data_get_string(settings, "audio_source"); obs_weak_source_t* old = NULL; if (cfg_source_name[0] == '\0') { - if (context->audio_source) { - old = context->audio_source; - context->audio_source = NULL; + if (ctx->audio_source) { + old = ctx->audio_source; + ctx->audio_source = NULL; } - context->source_name[0] = '\0'; + ctx->source_name[0] = '\0'; } else { - if (context->source_name[0] == '\0' || strcmp(context->source_name, cfg_source_name) != 0) { - if (context->audio_source) { - old = context->audio_source; - context->audio_source = NULL; + if (ctx->source_name[0] == '\0' || strcmp(ctx->source_name, cfg_source_name) != 0) { + if (ctx->audio_source) { + old = ctx->audio_source; + ctx->audio_source = NULL; } - strcpy(context->source_name, cfg_source_name); - context->capture_check_time = os_gettime_ns() - 3000000000; + strcpy(ctx->source_name, cfg_source_name); + ctx->capture_check_time = os_gettime_ns() - 3000000000; } } @@ -171,99 +333,44 @@ static void image_reaction_source_update(void* data, obs_data_t* settings) { obs_source_t* old_source = obs_weak_source_get_source(old); if (old_source) { info("Removed audio capture from '%s'", obs_source_get_name(old_source)); - obs_source_remove_audio_capture_callback(old_source, audio_capture, context); + obs_source_remove_audio_capture_callback(old_source, audio_capture, ctx); obs_source_release(old_source); } obs_weak_source_release(old); } } -static void image_reaction_source_defaults(obs_data_t* settings) { - obs_data_set_default_bool(settings, "unload", false); - obs_data_set_default_bool(settings, "linear_alpha", false); - obs_data_set_default_string(settings, "audio_source", ""); - obs_data_set_default_double(settings, "threshold", -40.0f); - obs_data_set_default_double(settings, "smoothness", 1.0f); -} +static void render_image(gs_image_file3_t* image) { + if (image->image2.image.texture) { + gs_eparam_t* const param = gs_effect_get_param_by_name(effect, "image"); -static void image_reaction_source_show(void* data) { - struct image_reaction_source* context = data; - - if (!context->persistent) - image_reaction_source_load(context); -} - -static void image_reaction_source_hide(void* data) { - struct image_reaction_source* context = data; - - if (!context->persistent) - image_reaction_source_unload(context); -} - -static void* image_reaction_source_create(obs_data_t* settings, obs_source_t* source) { - struct image_reaction_source* context = bzalloc(sizeof(struct image_reaction_source)); - context->source = source; - - context->source_name[0] = '\0'; - context->loud = false; - - image_reaction_source_update(context, settings); - return context; -} - -static void image_reaction_source_destroy(void* data) { - struct image_reaction_source* context = data; - - image_reaction_source_unload(context); - - if (context->file1) - bfree(context->file1); - - if (context->file2) - bfree(context->file2); - - if (context->audio_source) { - obs_source_t* source = obs_weak_source_get_source(context->audio_source); - if (source) { - info("Removed audio capture from '%s'", obs_source_get_name(source)); - obs_source_remove_audio_capture_callback(source, audio_capture, context); - obs_source_release(source); - } - obs_weak_source_release(context->audio_source); + gs_effect_set_texture_srgb(param, image->image2.image.texture); + gs_draw_sprite(image->image2.image.texture, 0, image->image2.image.cx, image->image2.image.cy); } - - bfree(context); } -static uint32_t image_reaction_source_getwidth(void* data) { +static void image_reaction_render(void* data, gs_effect_t* effect) { struct image_reaction_source* context = data; - return MAX(context->if31.image2.image.cx, context->if32.image2.image.cx); -} + const bool is_enabled = gs_framebuffer_srgb_enabled(); -static uint32_t image_reaction_source_getheight(void* data) { - struct image_reaction_source* context = data; - return MAX(context->if31.image2.image.cy, context->if32.image2.image.cy); -} - -static void image_reaction_source_render(void* data, gs_effect_t* effect) { - struct image_reaction_source* context = data; - - const bool previous = gs_framebuffer_srgb_enabled(); gs_enable_framebuffer_srgb(true); gs_blend_state_push(); gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); - gs_image_file3_t* if3 = context->loud ? &context->if32 : &context->if31; - if (if3->image2.image.texture) { - gs_eparam_t* const param = gs_effect_get_param_by_name(effect, "image"); - gs_effect_set_texture_srgb(param, if3->image2.image.texture); - - gs_draw_sprite(if3->image2.image.texture, 0, if3->image2.image.cx, if3->image2.image.cy); + switch (ctx->volume_state) { + case Silent: + render_image(&ctx->idle_image); + break; + case Loud: + render_image(&ctx->action_image); + break; + case Muted: + render_image(&ctx->mute_image); + break; } gs_blend_state_pop(); - - gs_enable_framebuffer_srgb(previous); + gs_enable_framebuffer_srgb(is_enabled); } static void image_reaction_tick(void* data, float seconds) { @@ -354,35 +461,45 @@ static void image_reaction_tick(void* data, float seconds) { context->last_time = frame_time; } -static const char* image_filter = "All formats (*.bmp *.tga *.png *.jpeg *.jpg *.gif *.psd *.webp);;" - "BMP Files (*.bmp);;" - "Targa Files (*.tga);;" - "PNG Files (*.png);;" - "JPEG Files (*.jpeg *.jpg);;" - "GIF Files (*.gif);;" - "PSD Files (*.psd);;" - "WebP Files (*.webp);;" - "All Files (*.*)"; +static bool add_audio_source(void* property, obs_source_t* source) { + obs_property_t* prop = property; -static bool add_source(void* param, obs_source_t* src) { - obs_property_t* list = param; + uint32_t output_flags = obs_source_get_output_flags(source); - uint32_t caps = obs_source_get_output_flags(src); - - if ((caps & OBS_SOURCE_AUDIO) == 0) + if ((output_flags & OBS_SOURCE_AUDIO) == 0) return true; - const char* name = obs_source_get_name(src); - obs_property_list_add_string(list, name, name); + + const char* name = obs_source_get_name(source); + + obs_property_list_add_string(prop, name, name); + return true; } -static bool source_changed(obs_properties_t* props, obs_property_t* prop, obs_data_t* data) { +static void remove_audio_source(static image_reaction* ctx) { + if (ctx->audio_source) { + obs_source_t* audio_source = obs_weak_source_get_source(ctx->audio_source); + + if (audio_source) { + info("Remove audio capture %s", obs_source_get_name(audio_source)); + obs_source_remove_audio_capture_callback(source, audio_capture, ctx); + obs_source_release(source); + } + + obs_weak_source_release(ctx->audio_source); + + ctx->audio_source = NULL; + } +} + +static bool source_changed(obs_properties_t* properties, obs_property_t* property, obs_data_t* data) { obs_data_get_string(data, "audio_source"); + return true; } -static obs_properties_t* image_reaction_source_properties(void* data) { - struct image_reaction_source* s = data; +static obs_properties_t* image_reaction_properties(void* context) { + struct image_reaction* ctx = context; struct dstr path = {0}; obs_properties_t* props = obs_properties_create(); @@ -421,9 +538,9 @@ static obs_properties_t* image_reaction_source_properties(void* data) { return props; } -uint64_t image_reaction_source_get_memory_usage(void* data) { - struct image_reaction_source* s = data; - return s->if31.image2.mem_usage + s->if32.image2.mem_usage; +uint64_t image_reaction_get_memory_usage(void* context) { + struct image_reaction_source* ctx = context; + return ctx->idle_image.image2.mem_usage + ctx->action_image.image2.mem_usage + ctx->mute_image.image2.mem_usage; } static void missing_file_callback(void* src, const char* new_path, void* data) { @@ -437,33 +554,3 @@ static void missing_file_callback(void* src, const char* new_path, void* data) { UNUSED_PARAMETER(data); } - -static struct obs_source_info image_reaction_source_info = { - .id = "image_reaction_source", - .type = OBS_SOURCE_TYPE_INPUT, - .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_SRGB, - .get_name = image_reaction_source_get_name, - .create = image_reaction_source_create, - .destroy = image_reaction_source_destroy, - .update = image_reaction_source_update, - .get_defaults = image_reaction_source_defaults, - .show = image_reaction_source_show, - .hide = image_reaction_source_hide, - .get_width = image_reaction_source_getwidth, - .get_height = image_reaction_source_getheight, - .video_render = image_reaction_source_render, - .video_tick = image_reaction_tick, - .get_properties = image_reaction_source_properties, - .icon_type = OBS_ICON_TYPE_IMAGE, -}; - -OBS_DECLARE_MODULE() -OBS_MODULE_USE_DEFAULT_LOCALE("image-reaction", "en-US") -MODULE_EXPORT const char* obs_module_description(void) { return "Image reaction source"; } - -extern struct obs_source_info slideshow_info; - -bool obs_module_load(void) { - obs_register_source(&image_reaction_source_info); - return true; -}