Fix plugin failing to load on Windows (OBS 32.2+) - #125
Conversation
…ws build script The 1.18.1 MinGW MSI packages have been removed from gstreamer.freedesktop.org, breaking the docker/mingw image build. 1.18.6 is the closest still-available release in that series. Also add docker/build-windows.sh, which reproduces the .gitlab-ci.yml windows job's meson cross-compile steps for building the plugin locally via the mingw image.
…dows) obsproject/obs-studio#11569 dropped %PATH% from the DLL search order on Windows, breaking obs-gstreamer in two ways (issue fzwoch#124): - GStreamer element support DLLs, loaded later via g_module_open(), can no longer be found even with a system-wide GStreamer install. - If the GStreamer core DLLs are instead copied next to obs-gstreamer.dll to work around the load failure, GStreamer's own plugin-scan path guess (relative to libgstreamer-1.0-0.dll's location) resolves to a nonexistent lib\gstreamer-1.0 under the plugin directory, so the element registry comes up completely empty. Register the real GStreamer install's bin directory via AddDllDirectory() and point GST_PLUGIN_PATH at its lib\gstreamer-1.0, derived from the GSTREAMER_1_0_ROOT_* environment variables the official installer sets. Diagnosis and fix approach from kaymyst in the issue thread. Fixes fzwoch#124
…zwoch#124) The earlier AddDllDirectory()/GST_PLUGIN_PATH fix (b2fcb64) still required manually copying the GStreamer runtime's bin\ DLLs next to obs-gstreamer.dll, because those DLLs are obs-gstreamer.dll's own direct imports and the loader resolves them before any of the plugin's own code (including that fix) ever runs. Convert the direct links against libgstreamer-1.0-0.dll and the other 10 GStreamer/GLib DLLs to delay-imports (dlltool -y delay-import archives + mingw-w64-crt's bundled __delayLoadHelper2), so the loader doesn't need them until the first actual GStreamer call - by which point configure_gstreamer_windows_paths() has already registered the real install's directories. obs-gstreamer.dll now loads with nothing but the plugin file itself in the plugin's bin\64bit folder; no manual DLL copying needed. Confirmed working (base load and element registry, e.g. videoconvert) against a real OBS 32.2 + system-installed GStreamer setup. The known dlltool/ld.bfd delay-import relocation bug reported upstream is 32-bit only; this toolchain targets x86_64 exclusively, and a minimal delay-load proof of concept (plain DLL + delay-loaded call, no GStreamer involved) ran correctly under this exact toolchain before converting the real plugin. docker/gen-delayimp-libs.sh generates the delay-import archives from the real GStreamer install's DLLs at build time (gendef + dlltool -y); docker/build-windows.sh runs it before meson setup and passes the output dir via -Dgst_delayimp_dir. configure_gstreamer_windows_paths() now returns false (and obs_module_load logs a clear error and bails) when no GStreamer install is found at all, so a genuinely missing install still fails cleanly instead of hitting the delay-load runtime's default DebugBreak-on-failure path.
PATH-based setup is no longer needed - the plugin now locates the GStreamer run-time itself via GSTREAMER_1_0_ROOT_MINGW_X86_64.
|
That is a fair approach. It does add a lot of noise to the repo for some 'stupid' hack though. Also it specifically fights intentional behavior of the OBS team. So I'm kind of reluctant to merge it in. That being said, if it fixes the issue people are having it is worth pointing at your branch and binary for people to use in their setup. |
|
Whoops. Didn't mean to close this and wasn't finished typing either. Picking up the changes of this PR, another approach would be just a have a complete separate plugin to mitigate the changed behavior in OBS 32.2. I'll post the idea in #124 |
| L"GSTREAMER_1_0_ROOT_MSVC_X86_64", | ||
| }; | ||
|
|
||
| SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); |
There was a problem hiding this comment.
Plugins should not be calling this, this is owned by the process.
There was a problem hiding this comment.
Fixed in 4044627 - dropped the SetDefaultDllDirectories() call entirely. OBS 32.2 already makes that call itself (it's what actually dropped %PATH% from the process-wide search order), so the plugin doesn't need to re-assert it - AddDllDirectory() alone is enough to add to the search path OBS already established.
| if (GetFileAttributesW(core_dll) == INVALID_FILE_ATTRIBUTES) | ||
| continue; | ||
|
|
||
| if (!AddDllDirectory(bin_path)) |
There was a problem hiding this comment.
The return value from AddDllDirectory must be saved and passed to RemoveDllDirectory after the loading has completed, otherwise it permanently alters the DLL search path, which belongs to the process.
There was a problem hiding this comment.
Fixed in 4044627 - the cookie is now saved in a static DLL_DIRECTORY_COOKIE and released via RemoveDllDirectory() in a new obs_module_unload(). Kept it alive for the plugin's whole lifetime rather than removing it right after this function returns, since GStreamer element modules can be g_module_open()'d lazily at any later point (e.g. the first time a given codec is actually used), not just during the initial load.
Drop the plugin's own SetDefaultDllDirectories() call - OBS 32.2 already makes it, and a plugin shouldn't redecide that process-wide policy. Save the AddDllDirectory() cookie and release it in obs_module_unload() instead of leaking it for the process lifetime.
|
Pushed a fix for the review feedback above (4044627). Updated Windows test build if anyone wants to try it: https://github.com/kaymyst/obs-gstreamer/releases/tag/pr125-windows-test |
Summary
Fixes #124. OBS 32.2 (obsproject/obs-studio#11569) dropped
%PATH%from the DLL search order on Windows, which broke obs-gstreamer in two ways:obs-gstreamer.dll's own direct GStreamer/GLib imports could no longer be resolved atLoadLibrarytime, so the module failed to load at all ("Failed to load Module").g_module_open()) could no longer be found, and the plugin registry scan fell back to a path guess that doesn't exist under the OBS plugin layout, so elements likevideoconvert/rtspsrcwere unavailable even once the module did load.Fix, in two parts:
dlltool -ydelay-import archives + mingw-w64-crt's__delayLoadHelper2), so the loader doesn't need them resolved until the first actual GStreamer call.docker/gen-delayimp-libs.shgenerates the archives from the real GStreamer install at build time;meson.buildlinks against them on Windows instead of the normal eager import libs.bindirectory viaAddDllDirectory()and pointGST_PLUGIN_PATHat itslib\gstreamer-1.0, derived from theGSTREAMER_1_0_ROOT_MINGW_X86_64/_MSVC_X86_64environment variables the official installer sets. If no GStreamer install is found at all,obs_module_loadnow logs a clear error and bails out cleanly instead of hitting the delay-load runtime's default failure path.With this,
obs-gstreamer.dllloads with nothing but the plugin file itself inbin\64bit- noPATHchanges, no manually copying the GStreamer runtime next to the plugin.Test plan
docker/mingwimage (bumped to GStreamer 1.18.6 - 1.18.1 packages were removed upstream) and confirmedobjdumpshows the 11 direct GStreamer/GLib DLLs as delay-imports, not regular imports, with onlyKERNEL32.dll/msvcrt.dll/obs.dllremaining as regular imports.ld.bfd2.35.2), since there's a known dlltool/ld.bfd delay-import bug upstream - confirmed it's 32-bit only and doesn't affect this x86_64-only build.obs-gstreamer.dllin the plugin'sbin\64bit, andvideoconvert(and other elements) resolve correctly with no "no element" errors.