From 703db4d3771adc11d97ed3c3784ca3771b8678cc Mon Sep 17 00:00:00 2001 From: Vladimir Sumarov Date: Tue, 23 Jun 2026 20:39:26 -0700 Subject: [PATCH] Report missing essential modules to the frontend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libobs silently skips a module whose file is missing or that fails to expose its OBS exports (MODULE_FILE_NOT_FOUND / MODULE_MISSING_EXPORTS), so such modules never appear in obs_module_failure_info::failures. The structured load-failure list therefore does not reflect a missing core plugin (e.g. obs-ffmpeg, which provides ffmpeg_aac) — the condition behind the startup crashes when the recording audio encoder was absent. Add a positive capability check after module load that verifies the encoders/output/service we depend on are actually registered (ffmpeg_aac, obs_x264, rtmp_output, rtmp_common) and surfaces any gaps through the same moduleLoadFailures list exposed by OBS_API_getModuleLoadFailures, so the frontend can react. Co-Authored-By: Claude Opus 4.8 (1M context) --- obs-studio-server/source/nodeobs_api.cpp | 49 ++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/obs-studio-server/source/nodeobs_api.cpp b/obs-studio-server/source/nodeobs_api.cpp index f00dce2b2..7a6397dce 100644 --- a/obs-studio-server/source/nodeobs_api.cpp +++ b/obs-studio-server/source/nodeobs_api.cpp @@ -163,6 +163,53 @@ static void copyModuleLoadFailures(const obs_module_failure_info &mfi) } } +static bool isServiceRegistered(const char *id) +{ + const char *type = nullptr; + for (size_t i = 0; obs_enum_service_types(i, &type); i++) { + if (type && strcmp(type, id) == 0) + return true; + } + return false; +} + +// A module that is missing on disk or fails to expose its OBS exports is skipped silently by libobs +// (MODULE_FILE_NOT_FOUND / MODULE_MISSING_EXPORTS), so it never appears in mfi.failures. Verify the +// capabilities we depend on are actually registered and surface the gaps through moduleLoadFailures. +static void reportMissingEssentialModules() +{ + struct EssentialModule { + const char *module; + const char *message; + bool present; + }; + + const EssentialModule essentials[] = { + {"obs-ffmpeg", "AAC audio encoder (ffmpeg_aac) is unavailable", obs_get_encoder_codec("ffmpeg_aac") != nullptr}, + {"obs-x264", "x264 video encoder (obs_x264) is unavailable", obs_get_encoder_codec("obs_x264") != nullptr}, + {"obs-outputs", "RTMP output (rtmp_output) is unavailable", obs_get_output_flags("rtmp_output") != 0}, + {"rtmp-services", "RTMP service (rtmp_common) is unavailable", isServiceRegistered("rtmp_common")}, + }; + + for (const EssentialModule &essential : essentials) { + if (essential.present) + continue; + + bool alreadyReported = false; + for (const ModuleLoadFailure &failure : moduleLoadFailures) { + if (failure.module == essential.module) { + alreadyReported = true; + break; + } + } + if (alreadyReported) + continue; + + blog(LOG_ERROR, "Essential module missing: %s - %s", essential.module, essential.message); + moduleLoadFailures.push_back({essential.module, "ESSENTIAL_MODULE_MISSING", essential.message}); + } +} + void OBS_API::Register(ipc::server &srv) { std::shared_ptr cls = std::make_shared("API"); @@ -1033,6 +1080,8 @@ void OBS_API::OBS_API_initAPI(void *data, const int64_t id, const std::vector