Merge pull request 'docs: the macOS bundle was fine all along, and record the GUI verification' (#6) from docs/macos-packaging-and-gui-status into main

This commit was merged in pull request #6.
This commit is contained in:
2026-09-10 12:31:33 +00:00
2 changed files with 75 additions and 59 deletions
+14 -10
View File
@@ -25,7 +25,7 @@ set -euo pipefail
MACOS_BUNDLE_FOUND="${MACOS_BUNDLE_FOUND:-false}" MACOS_BUNDLE_FOUND="${MACOS_BUNDLE_FOUND:-false}"
if [ "${MACOS_BUNDLE_FOUND}" = "true" ]; then if [ "${MACOS_BUNDLE_FOUND}" = "true" ]; then
MACOS_NOTE="This archive contains a \`.plugin\` bundle." MACOS_NOTE="This archive contains a \`.plugin\` bundle (verified on v0.1.0: MH_BUNDLE + Info.plist, libobs via \`@rpath\` + \`@executable_path/../Frameworks\`, LiveKit dylibs bundled, all three binaries code-signed). **arm64 only -- no Intel slice**, macOS 13+. Never yet loaded in OBS.app by a human."
else else
MACOS_NOTE="This archive is packaged as a bare \`streamer-tools-camera.so\` (the layout \`build/package/\` currently produces on macOS), **not** an OBS.app-loadable \`.plugin\` bundle. It will not load in the OBS GUI as-is -- see the \"macOS packaging gap\" section of \`README.md\`." MACOS_NOTE="This archive is packaged as a bare \`streamer-tools-camera.so\` (the layout \`build/package/\` currently produces on macOS), **not** an OBS.app-loadable \`.plugin\` bundle. It will not load in the OBS GUI as-is -- see the \"macOS packaging gap\" section of \`README.md\`."
fi fi
@@ -36,18 +36,22 @@ cat > "${NOTES_FILE}" <<EOF
Built from commit \`${SHA}\`. Built from commit \`${SHA}\`.
**Read the per-platform notes below before relying on this.** The module has **Confirmed working on Linux and Windows, including a live show.** The plugin
been loaded in the OBS GUI exactly once -- Windows, OBS 32.2.2 on Windows 11, carried a real broadcast on 2026-09-07. Video and audio both arrive and hold
2026-09-09 -- and only "it loads and registers its source type" is confirmed up across a session: Linux verified by the project owner, Windows by two
there. Whether video renders correctly, A/V sync, end-to-end latency and directors independently.
mid-show publisher restart are all still unverified on every platform. See
"What is verified, and how" in \`README.md\` for exactly what is backed by Still unverified: **macOS in the OBS GUI** (nobody has opened it -- see the
automated tests versus a human watching OBS. table), **measured** A/V sync and end-to-end latency against the existing
egress path (no drift reported, but nothing measured), and whether a publisher
restarting mid-show recovers cleanly on screen. See "What is verified, and
how" in \`README.md\` for what is backed by automated tests versus a human
watching OBS.
| Platform | Archive | Notes | | Platform | Archive | Notes |
|---|---|---| |---|---|---|
| Linux (x64) | \`streamer-tools-camera-${TAG}-linux-x64.zip\` | Functionally complete and verified end to end against a real LiveKit server and a real libobs (see README); OBS GUI itself still unverified | | Linux (x64) | \`streamer-tools-camera-${TAG}-linux-x64.zip\` | Functionally complete, verified end to end against a real LiveKit server and a real libobs (see README), and confirmed working in the OBS GUI |
| Windows (x64) | \`streamer-tools-camera-${TAG}-windows-x64.zip\` | Built and tested by this workflow's Windows job. Confirmed to load in the OBS GUI (32.2.2 / Windows 11, 2026-09-09); nothing past module load is verified. The WinHTTP backend has never been exercised against a real streamer-tools server, only a loopback test server -- see README's Windows CI section | | Windows (x64) | \`streamer-tools-camera-${TAG}-windows-x64.zip\` | Built and tested by this workflow's Windows job, and confirmed working in the OBS GUI by two directors independently (first load: OBS 32.2.2 / Windows 11) -- which also exercises the WinHTTP backend against a real streamer-tools server |
| macOS | \`streamer-tools-camera-${TAG}-macos.zip\` | Built and tested by this workflow's macOS job. ${MACOS_NOTE} | | macOS | \`streamer-tools-camera-${TAG}-macos.zip\` | Built and tested by this workflow's macOS job. ${MACOS_NOTE} |
## Installing ## Installing
+61 -49
View File
@@ -24,15 +24,21 @@ The plugin is **functionally complete on Linux and verified end to end there**
real streamer-tools API shape, and pushes decoded frames into real streamer-tools API shape, and pushes decoded frames into
`obs_source_output_video`/`_audio`). `obs_source_output_video`/`_audio`).
**First confirmed OBS GUI load: Windows, 2026-09-09** — the v0.1.0 release **Confirmed working in the OBS GUI on Linux and Windows, including a live
artifact loaded into OBS 32.2.2 on Windows 11 (build 26200) on a director's show.** The plugin carried a real broadcast on 2026-09-07 and was reported to
machine, from work well. Video and audio both arrive and hold up across a session: Linux
`C:\ProgramData\obs-studio\plugins\streamer-tools-camera\bin\64bit\`. verified by the project owner, Windows by two directors independently
That retires "the module will not even load in a real OBS" for Windows. It (2026-09-09/10; the first Windows load was OBS 32.2.2 on Windows 11 build
does **not** yet cover whether video renders correctly, colours, A/V sync or 26200, from
latency — see "Not verified anywhere" below for what is still open. Linux and `C:\ProgramData\obs-studio\plugins\streamer-tools-camera\bin\64bit\`).
macOS have still never been opened in the GUI; macOS builds the real module in Because listing cameras requires an API call, that also retires "the WinHTTP
CI but its artifact is not yet loadable (see the macOS packaging gap under CI). backend has never run against a real streamer-tools server".
What that does **not** cover: measured A/V sync and end-to-end latency against
the existing egress path (no drift reported over a session, but nothing was
measured), mid-show publisher restart, and **macOS in the GUI — still never
opened by anyone**, though its artifact is now known to be correctly packaged
(see macOS packaging below). See "Not verified anywhere" for the current list.
⚠️ **The install directory is not the same on every platform, and getting it ⚠️ **The install directory is not the same on every platform, and getting it
wrong fails silently.** On Windows it is wrong fails silently.** On Windows it is
@@ -169,15 +175,15 @@ plugins. This bit a director on 2026-09-09: a correctly-shaped install under
`AppData\Roaming` produced a log with zero mention of the module. `AppData\Roaming` produced a log with zero mention of the module.
The module resolves the LiveKit libraries from `$ORIGIN` (verified: `ldd` on the staged copy resolves both The module resolves the LiveKit libraries from `$ORIGIN` (verified: `ldd` on the staged copy resolves both
to `bin/64bit/`), not from the build tree. macOS is not this shape; see the to `bin/64bit/`), not from the build tree. macOS is not this shape — it ships
macOS packaging gap under CI. a `.plugin` bundle; see macOS packaging under CI.
## Testing this by hand ## Testing this by hand
**The module has been loaded in the OBS GUI on Windows once (2026-09-09, OBS **Linux and Windows are confirmed working in the GUI — video and audio over a
32.2.2 / Windows 11 26200) — nothing beyond "it loads and registers its source" real session, Linux by the project owner and Windows by two directors
is confirmed there, and Linux and macOS have never been opened in the GUI at independently (2026-09-09/10). macOS has never been opened in the GUI by
all.** To do it on Linux: anyone.** To repeat the Linux run:
``` ```
mkdir -p ~/.config/obs-studio/plugins/streamer-tools-camera mkdir -p ~/.config/obs-studio/plugins/streamer-tools-camera
@@ -233,21 +239,17 @@ livekit-server 1.13.6 in dev mode):
| Two sources in one OBS process | same harness with a second source added: both connect with distinct nonce identities, both receive frames, both tear down cleanly | | Two sources in one OBS process | same harness with a second source added: both connect with distinct nonce identities, both receive frames, both tear down cleanly |
**Not verified anywhere:** **Not verified anywhere:**
- Anything past module load in the OBS GUI. Windows 2026-09-09 confirms the - **macOS in the OBS GUI.** Nobody has opened it. Its artifact is now known to
module loads and its source type appears; whether video actually renders be a correctly-formed, correctly-linked, code-signed `.plugin` bundle
(right way up, right colours), what the A/V sync and latency look like, and (verified by inspecting the shipped v0.1.0 zip — see macOS packaging under
whether a publisher restarting mid-show recovers on screen are all still CI), and it is arm64-only, so Intel Macs are out regardless. "The bundle is
unanswered. Linux and macOS have not been opened in the GUI at all. well formed" is not "OBS loaded it".
- macOS beyond "CI builds and links the real module and the core tests pass". - **Measured** A/V sync and end-to-end latency against the existing egress
Its artifact is a bare `.so` with a relative libobs install name and will path. A live show and several sessions on Linux and Windows produced no
not load in OBS.app — see the macOS packaging gap under CI. reported drift, which is not the same as a measurement — and the timestamp
- Windows beyond "the core library and the WinHTTP backend compile and their caveat above is the reason to want real numbers.
tests pass", from runs predating the current fixes. The WinHTTP backend has - Whether a publisher restarting mid-show recovers cleanly on screen.
never run against a real streamer-tools server, only against the loopback - Token expiry across a session longer than an hour (see below).
test server in `test_api_client`.
- A/V sync and end-to-end latency against the existing egress path.
- Behaviour against the real production streamer-tools server (only against a
stand-in serving the same shapes).
- Token expiry after an hour. Expiry is handled *reactively*: a fatal - Token expiry after an hour. Expiry is handled *reactively*: a fatal
disconnect makes the worker mint a fresh token and reconnect. The design disconnect makes the worker mint a fresh token and reconnect. The design
doc's "proactively refreshed before expiry" is **not** implemented — doc's "proactively refreshed before expiry" is **not** implemented —
@@ -261,8 +263,8 @@ runners available to this repo under the `CyberCoveLLC` org.
| Job | `runs-on` | Runner | State | | Job | `runs-on` | Runner | State |
|---|---|---|---| |---|---|---|---|
| `linux` | `ubuntu-24.04` | `localhost.localdomain` | **Green.** Builds the real adapter against Ubuntu's libobs-dev 30.0.2, runs all six test suites, uploads `build/package` as an artifact | | `linux` | `ubuntu-24.04` | `localhost.localdomain` | **Green.** Builds the real adapter against Ubuntu's libobs-dev 30.0.2, runs all six test suites, uploads `build/package` as an artifact |
| `macos` | `macos-latest` | `home-mac` (Global) | **Green.** Builds libobs 30.0.2 from source, then the real adapter; 6/6 tests; artifact uploaded. But see the macOS packaging gap below | | `macos` | `macos-latest` | `home-mac` (Global) | **Green.** Builds libobs 30.0.2 from source, then the real adapter; 6/6 tests; artifact uploaded as a `.plugin` bundle. Never loaded in OBS.app, and arm64-only — see macOS packaging below |
| `windows` | `windows-latest` | `winvm-builder` (org-scoped) | **Failing, fix pushed and awaiting a completed run.** Every completed run so far has failed; the latest got as far as building libobs and stopped on an OBS-side `OBS::w32-pthreads` target that its own modern CMake path never defines. A bootstrap patch for that gap has been pushed but not yet confirmed by a green run; see below | | `windows` | `windows-latest` | `winvm-builder` (org-scoped) | **Green.** Builds libobs 30.0.2 from source, then the real adapter; 6/6 tests; artifact staged. Was red twice more after the bootstrap was fixed, both times on `test_api_client`'s timeout probe — see "WinHTTP timeouts are not deadlines" below |
The Linux job is pinned to `ubuntu-24.04` rather than `ubuntu-latest`: this The Linux job is pinned to `ubuntu-24.04` rather than `ubuntu-latest`: this
instance's two Linux runners answer `ubuntu-latest` with different releases, instance's two Linux runners answer `ubuntu-latest` with different releases,
@@ -356,26 +358,36 @@ obs-studio, builds libobs from source, builds and links the real adapter,
passes 6/6 tests, and uploads its artifact. `otool -L` on the result shows it passes 6/6 tests, and uploads its artifact. `otool -L` on the result shows it
linked against libobs and `@rpath/liblivekit.dylib`. linked against libobs and `@rpath/liblivekit.dylib`.
### macOS packaging gap (known, unfixed) ### macOS packaging (was described here as broken; it is not)
**The macOS artifact will not load in OBS.app as it stands.** Two reasons, **This section used to claim the macOS artifact was a bare
neither of which CI can catch, because CI only proves it compiles and links: `streamer-tools-camera.so` with a relative libobs install name that "will not
load in OBS.app as it stands". That is wrong, and it contradicted the release
notes for the same build.** Corrected 2026-09-10 by inspecting the shipped
`streamer-tools-camera-v0.1.0-macos.zip` itself:
1. It is a bare `streamer-tools-camera.so`. OBS on macOS loads plugins as - It is a proper bundle: `streamer-tools-camera.plugin/Contents/MacOS/streamer-tools-camera`
`<name>.plugin` bundles (`Contents/MacOS/<name>`, `Contents/Resources/`, (Mach-O **`MH_BUNDLE`**, which is what OBS loads), plus `Info.plist`
an `Info.plist`), which is what obs-plugintemplate's (`CFBundlePackageType BNDL`, `CFBundleExecutable streamer-tools-camera`),
`cmake/macos/helpers.cmake` builds and which this project deliberately did `Contents/Resources/locale/en-US.ini`, and both LiveKit dylibs under
not vendor. `Contents/Frameworks/`.
2. `otool -L` shows the libobs dependency recorded as the relative path - The install names are right, which was the specific doubt. The module loads
`libobs/libobs.framework/Versions/A/libobs`, inherited from the `@rpath/libobs.framework/Versions/A/libobs` and carries
from-source libobs's own install name. A real plugin needs `LC_RPATH @executable_path/../Frameworks` — inside OBS.app that resolves to
`@rpath/libobs.framework/Versions/A/libobs` plus an `LC_RPATH` pointing at `OBS.app/Contents/Frameworks`, where libobs lives. `@rpath/liblivekit.dylib`
`OBS.app/Contents/Frameworks`. resolves through `LC_RPATH @loader_path/../Frameworks` to the bundle's own
copy, and `liblivekit.dylib` finds `liblivekit_ffi.dylib` through its own
`LC_RPATH @loader_path`. Nothing points into a build tree.
- All three binaries carry an `LC_CODE_SIGNATURE` (superblob `0xfade0cc0`),
which is not optional: arm64 macOS refuses to load unsigned code at all.
Fixing this means either vendoring the template's macOS bundle helpers or **The real macOS limitation is different: the bundle is arm64-only.** There is
adding an `install_name_tool` pass and a bundle layout — bounded work, but no x86_64 slice, so Intel Macs cannot load it, and `LSMinimumSystemVersion` is
work that has to be done and checked on an actual Mac. It is deliberately not `13.0`. Shipping a universal binary would mean building both slices and
attempted here rather than guessed at. `lipo`-ing them, on a Mac.
Everything above is static inspection of the artifact. **Nobody has yet opened
it in OBS.app** — well-formed and signed is a strong prior, not a load.
### Where the Windows bootstrap got to ### Where the Windows bootstrap got to