-
-
Notifications
You must be signed in to change notification settings - Fork 40
Fix plugin failing to load on Windows (OBS 32.2+) #125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
928d69c
b2fcb64
17d672f
9da2f13
4044627
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| /linux/ | ||
| /macos/ | ||
| /macos-aarch64/ | ||
| /windows/ | ||
| /gst-delayimp/ | ||
| /cross.txt |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| #!/bin/bash | ||
| set -e | ||
|
|
||
| export PKG_CONFIG_PATH=/c/gstreamer/1.0/x86_64/lib/pkgconfig/ | ||
|
|
||
| rm -f cross.txt | ||
| { | ||
| echo "[binaries]" | ||
| echo "c = 'x86_64-w64-mingw32-gcc'" | ||
| echo "cpp = 'x86_64-w64-mingw32-g++'" | ||
| echo "ar = 'x86_64-w64-mingw32-ar'" | ||
| echo "strip = 'x86_64-w64-mingw32-strip'" | ||
| echo "pkgconfig = 'x86_64-w64-mingw32-pkg-config'" | ||
| echo "windres = 'x86_64-w64-mingw32-windres'" | ||
| echo "" | ||
| echo "[properties]" | ||
| echo "c_link_args = ['-static-libgcc', '-L/bin/64bit']" | ||
| echo "pkg_config_libdir = '/c/gstreamer/1.0/mingw_x86_64/lib/pkgconfig'" | ||
| echo "" | ||
| echo "[host_machine]" | ||
| echo "system = 'windows'" | ||
| echo "cpu_family = 'x86_64'" | ||
| echo "cpu = 'x86_64'" | ||
| echo "endian = 'little'" | ||
| } >> cross.txt | ||
|
|
||
| export C_INCLUDE_PATH=/ | ||
|
|
||
| rm -rf gst-delayimp | ||
| bash docker/gen-delayimp-libs.sh /c/gstreamer/1.0/mingw_x86_64/bin gst-delayimp | ||
|
|
||
| meson --buildtype release --cross-file cross.txt -Dpkg_config_path=/c/gstreamer/1.0/x86_64/lib/pkgconfig/ \ | ||
| -Dgst_delayimp_dir="$(pwd)/gst-delayimp" windows | ||
| ln -sf /c/ "windows/c:" | ||
| ninja -C windows | ||
|
|
||
| echo "Build complete: windows/obs-gstreamer.dll" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #!/bin/bash | ||
| # Generates delay-import libraries for the GStreamer/GLib DLLs obs-gstreamer | ||
| # directly links against on Windows, so obs-gstreamer.dll itself doesn't need | ||
| # them resolvable at LoadLibrary time (see gstreamer.c's | ||
| # configure_gstreamer_windows_paths()). | ||
| set -e | ||
|
|
||
| BIN_DIR="$1" | ||
| OUT_DIR="$2" | ||
|
|
||
| if [ -z "$BIN_DIR" ] || [ -z "$OUT_DIR" ]; then | ||
| echo "usage: $0 <gstreamer-bin-dir> <output-dir>" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| mkdir -p "$OUT_DIR" | ||
|
|
||
| DLLS="libgstreamer-1.0-0 libgobject-2.0-0 libglib-2.0-0 libintl-8 libgstvideo-1.0-0 libgstbase-1.0-0 libgstaudio-1.0-0 libgsttag-1.0-0 libgstapp-1.0-0 libgstnet-1.0-0 libgio-2.0-0" | ||
|
|
||
| for name in $DLLS; do | ||
| dll="$BIN_DIR/$name.dll" | ||
| if [ ! -f "$dll" ]; then | ||
| echo "missing $dll" >&2 | ||
| exit 1 | ||
| fi | ||
| ( cd "$OUT_DIR" && gendef "$dll" ) | ||
| x86_64-w64-mingw32-dlltool \ | ||
| -d "$OUT_DIR/$name.def" \ | ||
| --dllname "$name.dll" \ | ||
| --output-delaylib "$OUT_DIR/lib${name}_delay.a" | ||
| done |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,70 @@ | |
| #include <obs/obs-module.h> | ||
| #include <gst/gst.h> | ||
|
|
||
| #ifdef _WIN32 | ||
| #include <windows.h> | ||
|
|
||
| // OBS 32.2 (obsproject/obs-studio#11569) dropped %PATH% from the DLL search | ||
| // order, breaking obs-gstreamer on Windows in two ways (issue #124): | ||
| // - obs-gstreamer.dll's own direct GStreamer/GLib imports could no longer | ||
| // be resolved at LoadLibrary time at all, so the module failed to load. | ||
| // Fixed by delay-loading those imports (see meson.build) so the loader | ||
| // doesn't need them until first use, by which point this has run. | ||
| // - element support DLLs pulled in later via g_module_open() could no | ||
| // longer be found, so register the GStreamer install's bin directory | ||
| // explicitly, and point GST_PLUGIN_PATH at its lib\gstreamer-1.0 so the | ||
| // element registry scan doesn't fall back to a guess relative to wherever | ||
| // the delay-loaded core DLLs happened to resolve from. | ||
| // | ||
| // Returns false if no usable GStreamer install was found, so the caller can | ||
| // bail out before making any delay-loaded GStreamer call - a call that fails | ||
| // to resolve hits the delay-load runtime's default failure handling, which | ||
| // is not a controlled error path (see mingw-w64-crt/misc/delayimp.c). | ||
| static bool configure_gstreamer_windows_paths(void) | ||
| { | ||
| static const wchar_t *const roots[] = { | ||
| L"GSTREAMER_1_0_ROOT_MINGW_X86_64", | ||
| L"GSTREAMER_1_0_ROOT_MSVC_X86_64", | ||
| }; | ||
|
|
||
| SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); | ||
|
|
||
| for (size_t i = 0; i < sizeof(roots) / sizeof(roots[0]); i++) { | ||
| wchar_t root[MAX_PATH]; | ||
| DWORD len = GetEnvironmentVariableW(roots[i], root, MAX_PATH); | ||
| if (len == 0 || len >= MAX_PATH - 32) | ||
| continue; | ||
| if (root[len - 1] != L'\\') { | ||
| wcscat(root, L"\\"); | ||
| len++; | ||
| } | ||
|
|
||
| wchar_t bin_path[MAX_PATH]; | ||
| wcscpy(bin_path, root); | ||
| wcscat(bin_path, L"bin"); | ||
|
|
||
| wchar_t core_dll[MAX_PATH]; | ||
| wcscpy(core_dll, bin_path); | ||
| wcscat(core_dll, L"\\libgstreamer-1.0-0.dll"); | ||
| if (GetFileAttributesW(core_dll) == INVALID_FILE_ATTRIBUTES) | ||
| continue; | ||
|
|
||
| if (!AddDllDirectory(bin_path)) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The return value from
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 4044627 - the cookie is now saved in a static |
||
| continue; | ||
|
|
||
| if (GetEnvironmentVariableW(L"GST_PLUGIN_PATH", NULL, 0) == 0) { | ||
| wchar_t plugin_path[MAX_PATH]; | ||
| wcscpy(plugin_path, root); | ||
| wcscat(plugin_path, L"lib\\gstreamer-1.0"); | ||
| SetEnvironmentVariableW(L"GST_PLUGIN_PATH", plugin_path); | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
| #endif | ||
|
|
||
| extern const char *obs_gstreamer_version; | ||
|
|
||
| OBS_DECLARE_MODULE() | ||
|
|
@@ -82,6 +146,16 @@ bool obs_module_load(void) | |
| { | ||
| guint major, minor, micro, nano; | ||
|
|
||
| #ifdef _WIN32 | ||
| if (!configure_gstreamer_windows_paths()) { | ||
| blog(LOG_ERROR, | ||
| "[obs-gstreamer] Could not locate a GStreamer runtime install (checked " | ||
| "GSTREAMER_1_0_ROOT_MINGW_X86_64 and GSTREAMER_1_0_ROOT_MSVC_X86_64). Install the official " | ||
| "GStreamer Windows runtime: https://gstreamer.freedesktop.org/download/"); | ||
| return false; | ||
| } | ||
| #endif | ||
|
|
||
| gst_version(&major, &minor, µ, &nano); | ||
|
|
||
| blog(LOG_INFO, "[obs-gstreamer] build: %s, gst-runtime: %u.%u.%u", obs_gstreamer_version, major, minor, micro); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| option('gst_delayimp_dir', type : 'string', value : '', | ||
| description : 'Directory containing delay-import archives generated by docker/gen-delayimp-libs.sh (Windows only)') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plugins should not be calling this, this is owned by the process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.