Author SHA1 Message Date
shadowdaoandClaude Opus 5 abd4dc9aca docs: record the first confirmed OBS GUI load (Windows)
Build / macOS (macos-latest) (pull_request) Successful in 26s
Build / Linux (ubuntu-24.04) (pull_request) Successful in 59s
Build / macOS (macos-latest) (push) Successful in 37s
Build / Linux (ubuntu-24.04) (push) Successful in 55s
Build / Windows (windows-latest) (pull_request) Successful in 3m45s
Build / Windows (windows-latest) (push) Successful in 3m48s
The v0.1.0 Windows artifact loaded into OBS 32.2.2 on Windows 11 (build
26200) on a director's machine on 2026-09-09, from
C:\ProgramData\obs-studio\plugins\streamer-tools-camera\bin\64bit\. That
retires "has not been run in the OBS GUI on any platform", which this README
asserted in four places.

Scoped deliberately to what was actually observed: the module loads and
registers its source type. Whether video renders, the colours, A/V sync,
latency, and mid-show publisher restart are all still unverified, and Linux
and macOS have still never been opened in the GUI. The "Not verified
anywhere" list now says so explicitly rather than being deleted.

Also drops the release-draft rationale that pointed at the no-GUI-load state,
and carries the ProgramData install-path warning up into the status section,
since that is what stopped the first GUI load from happening sooner.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AzGnvQ6wfD7bw7PZN35ft9
2026-09-09 16:47:00 -07:00
jknapp 1571d7ecea Merge pull request 'docs: the Windows plugin dir is ProgramData, not the config dir' (#2) from docs/windows-plugin-path into main
Build / macOS (macos-latest) (push) Successful in 29s
Build / Linux (ubuntu-24.04) (push) Successful in 45s
Build / Windows (windows-latest) (push) Successful in 4m3s
2026-09-09 23:44:20 +00:00
shadowdaoandClaude Opus 5 bfc38f45ca docs: the Windows plugin dir is ProgramData, not the config dir
Build / macOS (macos-latest) (push) Successful in 36s
Build / Linux (ubuntu-24.04) (push) Successful in 55s
Build / macOS (macos-latest) (pull_request) Successful in 33s
Build / Linux (ubuntu-24.04) (pull_request) Successful in 1m9s
Build / Windows (windows-latest) (push) Successful in 10m51s
Build / Windows (windows-latest) (pull_request) Successful in 10m27s
The packaging section claimed `<config>/obs-studio/plugins/<name>/bin/64bit`
is "exactly the layout OBS searches on Linux and Windows". The layout is
right; the base directory is not the same on both. obs-studio's
`AddExtraModulePaths()` uses `GetAppConfigPath` on Linux/macOS but
`GetProgramDataPath` (`CSIDL_COMMON_APPDATA`) on Windows, so Windows scans
`C:\ProgramData\obs-studio\plugins\`, never `%APPDATA%\obs-studio\`
(`CSIDL_APPDATA`) — which on Windows is where OBS keeps its config.

This bit a director on 2026-09-09: a correctly-shaped install under
`AppData\Roaming` produced an OBS log with zero mention of the module. Noted
here because the failure is silent — OBS names any module it finds but cannot
use, so an absence of any line means the file is not on a scanned path at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AzGnvQ6wfD7bw7PZN35ft9
2026-09-09 16:44:06 -07:00
shadowdaoandClaude Sonnet 5 76b2942db1 ci: bump the OBS deps cache key -- it was stuck on a permanently bad entry
Build / macOS (macos-latest) (push) Successful in 45s
Build / Linux (ubuntu-24.04) (push) Successful in 53s
Build / Windows (windows-latest) (push) Successful in 6m0s
Release / macOS (macos-latest) (push) Successful in 38s
Release / Linux (ubuntu-24.04) (push) Successful in 57s
Release / Windows (windows-latest) (push) Successful in 3m28s
Release / Create Gitea Release (draft) (push) Successful in 19s
actions/cache never overwrites an existing key: once a key has a saved
entry, later jobs' save steps are always skipped as a no-op regardless
of cache hit/miss. The very first job to ever populate this cache did
so before the "Drop non-relocatable OBS build tree" cleanup step
existed, so that entry included the bad, path-baked build_x86
directory -- and every run since has kept restoring that same bad
entry, not self-healing after "one more transitional run" the way it
looked when this was last checked. Confirmed live: a run just now hit
the identical stale-path CMakeCache.txt error against the same
original job hash (f72837d12c75c64a) from hours ago.

Bumping the key (obs-deps- -> obs-deps-v2-) is what actually forces a
fresh save under the cleanup step's watch, since the key content
(buildspec files) hasn't changed and nothing else would invalidate it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 10:47:56 -07:00
shadowdaoandClaude Sonnet 5 af7d2c6d24 fix: pin video quality to stop OBS source resizing; add audio-only mode
Build / macOS (macos-latest) (push) Successful in 34s
Build / Linux (ubuntu-24.04) (push) Successful in 46s
Build / Windows (windows-latest) (push) Failing after 2m44s
Live testing (2026-09-07) showed two real problems in one root cause:
LiveKit's default subscriber behavior lets the SFU switch simulcast
layers on its own bandwidth/adaptive logic, and this plugin never told
it not to. For a real camera, that showed up as the OBS source's
received frame size visibly hopping between 320x180/640x360/1280x720
mid-show -- OBS's async video source resizes to match, breaking any
manual crop/position a director had set up. For the soundboard (a
Camera-source track that exists only to satisfy RTMP's video
requirement -- Soundboard.tsx -- with no real visual content), the
same instability, plus the video showing at all, was pure noise: there
was no way to pull just its audio.

Both come from RemoteTrackPublication (livekit/remote_track_publication.h
in the pinned SDK), on the exact publication object TrackSubscribedEvent
and attachExistingTracks already hand this code:

  - setVideoQuality(VideoQuality::HIGH) on every wanted video track,
    unconditionally, so the SFU always sends the top simulcast layer
    instead of switching layers underneath a source with no
    rendered-size hint to give it (this is a native subscriber, not a
    sized <video> element).
  - A new SessionConfig::subscribe_video (mirrors subscribe_audio):
    when false, the wanted video track is never attached, and its
    publication is explicitly setEnabled(false) -- the SFU stops
    sending it, not just "decoded and discarded here". Wired to a new
    "Audio only (no video)" checkbox in the source's properties.

Both call sites (a fresh TrackSubscribedEvent, and attachExistingTracks
sweeping tracks already up when the session starts watching) go
through one new handleWantedVideoTrack() so they can't drift apart.

Not unit-testable without a real LiveKit connection (RemoteTrackPublication
isn't fakeable, matching why test_integration_livekit.cpp already needs a
real server) -- verified instead by a full local build against real
libobs-dev + the pinned SDK (clean compile, all 6 existing tests still
pass) and CI. The actual behavioral fix -- stable resolution, no video
for an audio-only source -- needs the same real-OBS verification every
other claim in this repo's "What is verified, and how" section does.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 10:43:54 -07:00
shadowdaoandClaude Sonnet 5 104326d05a docs/release: drop the C1 licensing gate, simplify install instructions
Build / macOS (macos-latest) (push) Successful in 29s
Build / Linux (ubuntu-24.04) (push) Successful in 49s
Build / Windows (windows-latest) (push) Successful in 2m50s
Release / macOS (macos-latest) (push) Successful in 33s
Release / Linux (ubuntu-24.04) (push) Successful in 56s
Release / Windows (windows-latest) (push) Successful in 3m20s
Release / Create Gitea Release (draft) (push) Successful in 19s
The WebRTC/OpenH264 attribution question tracked as "C1" throughout
README, third_party/livekit/README.md, the release-notes template, and
both workflow header comments is the project owner's call, and it has
been made -- own sign-off given and reaffirmed. Remove the gate
language and the extended research writeup from release-facing docs;
keep the actual LICENSE/NOTICE files themselves (Apache-2.0 requires
shipping those regardless of any of this).

Also simplify the release notes' install instructions per owner
request: point at each platform's default OBS plugins folder rather
than walking through verbose per-platform copy/extract instructions --
the archives already extract straight into place (prior commit), so a
short pointer is all that's needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 09:40:49 -07:00
shadowdaoandClaude Sonnet 5 e050b81d6c ci: stop re-downloading CMake/Ninja on every Windows run
Build / macOS (macos-latest) (push) Successful in 32s
Build / Linux (ubuntu-24.04) (push) Successful in 56s
Build / Windows (windows-latest) (push) Successful in 2m52s
Release / macOS (macos-latest) (push) Successful in 43s
Release / Linux (ubuntu-24.04) (push) Successful in 56s
Release / Windows (windows-latest) (push) Successful in 3m23s
Release / Create Gitea Release (draft) (push) Successful in 19s
lukka/get-cmake@latest re-fetched and re-extracted CMake + Ninja on
every single Windows CI run. Its own cache (routed through this
act_runner's built-in cache server) reported "Cloud cache miss" on
every run, even one immediately after a run that logged a successful
save under the exact same key -- an incompatibility between its
bundled cache client and this act_runner's cache-server implementation,
not a config gap. Separately and more importantly: the archive
extraction step alone measured ~7.5 minutes for a 45MB zip on this VM,
consistent with Defender real-time scanning rather than raw disk I/O.
Together this was the dominant cost of every Windows CI run.

CMake 4.4.2 and Ninja 1.12.1 are now installed once, directly on the
winvm-builder VM's system PATH (C:\BuildTools\cmake, C:\BuildTools\
ninja), sidestepping the third-party action's cache entirely rather
than debugging its internals further. Both Windows jobs now just
verify cmake/ninja are present and fail loudly if not, instead of
silently falling back to a slow reinstall. Full detail, including how
to redo this if the VM is ever rebuilt, is in README's new "Windows
runner: persistent build tools" section -- this is VM state, not
something git reproduces.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 09:07:23 -07:00
shadowdaoandClaude Sonnet 5 18610bd9dc ci: stop build.yml from also running on tag pushes
Build / macOS (macos-latest) (push) Successful in 32s
Build / Linux (ubuntu-24.04) (push) Successful in 48s
Build / Windows (windows-latest) (push) Successful in 10m32s
A bare `push:` trigger matches every ref push, tags included -- so
tagging a release triggered this workflow's full 3-platform build
(Windows and all) at the same time as release.yml's own, both
competing for the Windows runner's capacity:1 and roughly doubling
Windows CI time for every tag push. Scope this workflow's push trigger
to branches only; release.yml already covers this same build (plus
packaging) on every `v*` tag.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 09:03:41 -07:00
shadowdaoandClaude Sonnet 5 43a501c06c release: package archives as direct drop-ins to OBS plugin dirs
Release / macOS (macos-latest) (push) Successful in 43s
Release / Linux (ubuntu-24.04) (push) Successful in 51s
Build / Windows (windows-latest) (push) Successful in 10m43s
Build / Linux (ubuntu-24.04) (push) Successful in 52s
Build / macOS (macos-latest) (push) Successful in 28s
Release / Windows (windows-latest) (push) Failing after 16m46s
Release / Create Gitea Release (draft) (push) Skipped
Previously each release zip mirrored build/package/ verbatim (bin/ +
data/ at the archive root), which needed a manual mkdir + cp -r into
the right OBS plugin subdirectory per the install instructions. Wrap
Linux and Windows archives in a top-level streamer-tools-camera/
directory instead -- matching AddExtraModulePaths()'s expected
<config>/obs-studio/plugins/<name>/bin/64bit + data layout -- so
`unzip -d ~/.config/obs-studio/plugins/` (or Expand-Archive to
%APPDATA%\obs-studio\plugins\ on Windows) is the entire install step.

macOS already produced the right shape (the .plugin bundle itself at
the archive's top level, since OBS wants the whole bundle directly
under plugins/, not nested under a named subdirectory) -- no packaging
change needed there, just an install-instructions rewrite plus making
that text properly conditional on MACOS_BUNDLE_FOUND like MACOS_NOTE
already was, since the old hard-coded "not yet that bundle shape" text
was stale once the macOS bundle packaging fix landed.

Verified locally: staged a fake build/package/ tree, ran the new
packaging logic, and confirmed extracting the resulting zip into a
plugins directory produces <name>/bin/64bit/... and <name>/data/...
directly. Also dry-ran publish-release.sh's notes generation for both
MACOS_BUNDLE_FOUND values to confirm the conditional install text
renders correctly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 08:41:44 -07:00
shadowdaoandClaude Sonnet 5 14fa00af3f docs: OpenH264/MPEG-LA royalty finding sharpens C1 -- not resolved
Build / macOS (macos-latest) (push) Successful in 28s
Build / Linux (ubuntu-24.04) (push) Successful in 59s
Build / Windows (windows-latest) (push) Successful in 10m45s
Cisco's own OpenH264 FAQ (openh264.org/faq.html) is explicit: they cover
MPEG-LA/AVC patent-pool royalties only for their own prebuilt binary,
downloaded at install time. Anyone who compiles OpenH264 from source
and redistributes it inside their own binary takes on "all applicable
license fees" themselves -- Cisco "will not be liable for any licensing
fees incurred by other parties" in that case.

LiveKit's client-sdk-cpp links Google libwebrtc via the webrtc-sdk
org's fork, whose documented build args (rtc_use_h264=true,
ffmpeg_branding="Chrome") are the standard Chromium/WebRTC recipe --
which links a from-source, statically-compiled copy of OpenH264 (from
Google's mirror, not Cisco's runtime binary) into libwebrtc. That is
exactly the shape of case Cisco's FAQ says voids their coverage. Not
independently confirmed against LiveKit's actual pinned v1.10.1 build
(their release archives ship only compiled output, no build manifest)
-- this is webrtc-sdk/libwebrtc's documented default, not a verified
fact about this specific artifact.

This sharpens C1 into a concrete mechanism instead of a general open
question. It does not resolve C1 -- if anything it strengthens the case
for treating it as unresolved -- and none of this is a substitute for
an actual legal opinion. Recorded in third_party/livekit/README.md (the
full writeup), README.md's Status section, and the release-notes
template in publish-release.sh so it reaches whoever opens a draft
release next, not just this one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 07:45:36 -07:00
shadowdaoandClaude Sonnet 5 d287a90048 ci: strip the non-relocatable OBS build tree before caching .deps
Build / macOS (macos-latest) (push) Successful in 33s
Build / Linux (ubuntu-24.04) (push) Successful in 56s
Release / macOS (macos-latest) (push) Successful in 32s
Release / Linux (ubuntu-24.04) (push) Successful in 52s
Build / Windows (windows-latest) (push) Successful in 10m35s
Release / Windows (windows-latest) (push) Successful in 11m7s
Release / Create Gitea Release (draft) (push) Successful in 20s
The obs-studio sub-build's out-of-source CMakeCache.txt
(.deps/obs-studio-*/build_*) bakes in the job's absolute checkout
path. Caching it as-is (from the previous .deps caching commit)
meant every run's first configure attempt hard-failed with:

  CMake Error: The current CMakeCache.txt directory ... is different
  than the directory ... where CMakeCache.txt was created.

Confirmed live in run 3091 (commit 1f342b1): this was silently
self-healing, since windows-build.ps1's existing fallback
(-DSTPLUGIN_BOOTSTRAP_OBS=OFF) still found the already-installed,
path-independent libobs package and the build succeeded anyway -- but
every run paid for one guaranteed-failed configure and printed a
misleading "::warning::OBS SDK bootstrap failed" that isn't actually
true. Delete only the build tree, after libobs is already built and
installed from it, so nothing path-dependent survives into the cache.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 06:10:55 -07:00
shadowdaoandClaude Sonnet 5 1f342b1971 docs: Windows CI is green, log-verified; scope the w32-pthreads fallback
Build / macOS (macos-latest) (push) Successful in 32s
Build / Linux (ubuntu-24.04) (push) Successful in 52s
Build / Windows (windows-latest) (push) Successful in 10m36s
Records the outcome of f27b1c0 against the actual job log rather than the
job status: the from-source libobs bootstrap configures/builds/installs,
find_package(libobs) resolves through .deps/cmake/libobs, all 6 CTest suites
pass, and build\package\bin\64bit\streamer-tools-camera.dll (136,192 bytes)
is staged beside livekit.dll and livekit_ffi.dll. First completed green
Windows run on this repository. That also retires three separately-unproven
items at once -- the -A x64 fix, the PowerShell rewrite of the Windows steps,
and the add_subdirectory(deps/w32-pthreads) bootstrap patch.

The log also settles which branch of the new code ran: the "w32-pthreads_DIR
not set" message means OBS's own exported package was at
.deps/cmake/w32-pthreads/ the whole time and the fallback find module was
never loaded. The export was never missing, only unfindable -- worth
recording, because "w32-pthreads has no install rules" was the first
hypothesis and it was wrong.

Also narrows when that fallback is exposed at all. It reads artifacts out of
.deps/ and nowhere else, so it is only appended to CMAKE_MODULE_PATH when
this repo's own bootstrap actually produced an obs-studio tree there. A
developer building against an external OBS SDK (STPLUGIN_BOOTSTRAP_OBS=OFF,
or an explicit -Dlibobs_DIR=...) stays on CMake's normal search, where
whatever w32-pthreads package their SDK provides can still win -- a find
module that could only ever fail would have made their error worse, not
better. Linux reconfigures unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 05:56:18 -07:00
shadowdaoandClaude Sonnet 5 f27b1c0b45 Fix Windows configure: point find_package at OBS's exported w32-pthreads
Build / macOS (macos-latest) (push) Successful in 28s
Build / Linux (ubuntu-24.04) (push) Successful in 46s
Build / Windows (windows-latest) (push) Successful in 13m44s
The libobs_DIR fix in 79de5e8f worked -- libobsConfig.cmake now loads on the
Windows runner -- and immediately exposed the next link in the same chain.
obs-studio's libobsConfig.cmake.in carries, under if(MSVC), a hard

    find_dependency(w32-pthreads REQUIRED)

because libobs/cmake/os-windows.cmake links PUBLIC OBS::w32-pthreads. That
lookup failed and aborted the whole configure.

The cause is not a missing export, which was the first hypothesis:
deps/w32-pthreads/CMakeLists.txt ends in target_export(w32-pthreads), the
same helper libobs itself uses, so the install(TARGETS ... EXPORT),
install(EXPORT ... NAMESPACE OBS::) and generated w32-pthreadsConfig.cmake
all exist and all run -- CMake would have hard-errored at generate time if
w32-pthreads were in no export set, and our own patch adds it under the
libobs subtree, which installs before the tolerated obs-frontend-api install
error stops the rest.

It is the same search-path mismatch libobs itself had: target_export installs
to <prefix>/${OBS_CMAKE_DESTINATION}/<target>/, which on Windows is
<prefix>/cmake/w32-pthreads/ -- a shape find_package's Config-mode suffixes
never search. So apply the same remedy CMake's own error message suggests,
and apply it before the find_package(libobs) call that transitively triggers
the find_dependency.

Also adds a fallback Findw32-pthreads.cmake, reached only when that export is
genuinely absent, which reconstructs OBS::w32-pthreads from the bootstrap's
artifacts, and a repair for a locationless imported target mirroring the
existing OBS::libobs one. The fallback deliberately lives in
cmake/windows/find-fallback/ rather than cmake/windows/, which osconfig.cmake
already puts on CMAKE_MODULE_PATH: a find module there would shadow OBS's
real exported package on every build, since MODULE mode is tried first.

Verified on Linux (this is a Linux sandbox) by reconstructing the exact
failure -- a stub libobsConfig.cmake with the same find_dependency, reached
through libobs_DIR -- and confirming it fails without this block and passes
with it, and separately that deleting the package routes find_package through
the fallback module instead. A full Linux configure of the repo is unchanged;
both new blocks are inside if(OS_WINDOWS) and the upstream find_dependency is
inside if(MSVC), so macOS and Linux are no-ops.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 05:40:38 -07:00
shadowdaoandClaude Sonnet 5 1a8255230f ci: cache the OBS SDK bootstrap deps across runs
Build / Linux (ubuntu-24.04) (push) Successful in 1m5s
Build / macOS (macos-latest) (push) Successful in 1m10s
Build / Windows (windows-latest) (push) Failing after 9m18s
cmake/{windows,macos}/buildspec.cmake download the pinned obs-deps
bundle + obs-studio source into .deps/, with its own idempotent
skip-if-present logic keyed on SHA256 marker files. That logic never
gets a chance to fire because .deps/ lives inside the checkout and a
fresh `actions/checkout` wipes it on every single push -- this is what
has made every CI iteration tonight re-pay the several-minute cold
download instead of only paying it once.

Add an actions/cache step for .deps/ in both jobs, keyed on the files
that actually pin dependency versions (buildspec.json +
buildspec_common.cmake + the platform buildspec.cmake), so a version
bump still invalidates the cache correctly. No runner-side config
change needed -- the act_runner instance already has its built-in
cache server on by default (cache.enabled: true, confirmed via
`gitea-runner.exe generate-config` on winvm-builder).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 05:19:16 -07:00
shadowdaoandClaude Sonnet 5 b5c91c72e5 macOS: real .plugin bundle packaging, libobs @rpath fixup, hard-fail CI check
Build / macOS (macos-latest) (push) Successful in 37s
Build / Linux (ubuntu-24.04) (push) Successful in 53s
Build / Windows (windows-latest) (push) Canceled after 1m16s
Closes the "macOS packaging gap" documented in README.md: CI built a real
adapter module on macOS but staged it as a bare streamer-tools-camera.so,
which OBS.app cannot load (it needs a <name>.plugin bundle), and otool -L
showed the libobs dependency as the relative path
libobs/libobs.framework/Versions/A/libobs instead of an @rpath reference.

cmake/macos/helpers.cmake adapts (not vendors as-is) obs-plugintemplate's
cmake/macos/helpers.cmake: upstream builds the bundle almost entirely
through XCODE_ATTRIBUTE_* properties and its own CI drives that with
`xcodebuild -project <name>.xcodeproj`, but this project's home-mac runner
has only the Command Line Tools, not Xcode.app (already established by
buildspec_common.cmake's CI-iteration-1 comment), and the whole project
builds with Ninja end to end. So this reimplements the same outcome --
Contents/MacOS, Contents/Resources, a real Info.plist -- with CMake's own
generator-agnostic BUNDLE/BUNDLE_EXTENSION/MACOSX_BUNDLE_INFO_PLIST/
MACOSX_PACKAGE_LOCATION target properties (verified working under Ninja via
a dry-run configure with APPLE spoofed), and does by hand what upstream gets
from Xcode's embed/codesign build phases:
  - copies obs-adapter/data/** into Contents/Resources/**, since OBS's
    AddExtraModulePaths() (UI/window-basic-main.cpp) passes
    Contents/Resources as a macOS module's *data path* -- not a sibling
    data/ the way Linux/Windows work -- so the locale ini has to land at
    Contents/Resources/locale/en-US.ini for OBS_MODULE_USE_DEFAULT_LOCALE
    to find it
  - copies the LiveKit runtime dylibs into Contents/Frameworks
  - fixes up the libobs dependency: cmake/macos/fixup-libobs-rpath.sh
    rewrites the relative install name the from-source libobs build records
    to @rpath/libobs.framework/Versions/A/libobs (LiveKit's own dylibs
    already record @rpath references, confirmed in prior CI otool -L
    output, so only libobs needs the rewrite)
  - gives the plugin binary two LC_RPATH entries via INSTALL_RPATH:
    @loader_path/../Frameworks (this bundle's own Frameworks, for LiveKit)
    and @executable_path/../Frameworks (OBS.app/Contents/Frameworks, for
    libobs.framework -- @executable_path is always relative to the host
    process's main executable, not this dlopen'd bundle)

obs-adapter/CMakeLists.txt: calls stplugin_macos_finalize_bundle() before
staging, and corrects the staged layout for macOS -- OBS's module search
wants the whole <name>.plugin dropped directly into .../obs-studio/plugins/,
not nested under a bin/ subdirectory the way Linux/Windows are, so
build/package/ now holds the bundle at its own top level on macOS instead of
build/package/bin/<name>.plugin.

.gitea/scripts/macos-build.sh (this project's macOS steps were factored out
of build.yml into this shared script, used by both build.yml and
release.yml, in a concurrent commit -- rebased onto that): the "Show what
was built" section now hard-fails unless a real .plugin bundle (with
Info.plist, bundled LiveKit dylibs, and locale data) was produced, the
libobs dependency resolves via @rpath rather than the old relative path,
and both LC_RPATH entries are present -- closing the same
false-positive-green gap this project's Windows find_package(libobs)
incident just exposed, which this check previously did not cover (it only
checked for a bare .so). release.yml's macOS packaging step already globs
for any `*.plugin` under build/ (added in the concurrent commit,
anticipating this fix), so it picks up the new layout with no change
needed.

Verified: Linux configure/build/ctest (STPLUGIN_BOOTSTRAP_OBS=OFF) still
passes 6/6, unaffected -- the new macOS CMake logic is fully guarded by
if(APPLE). The bundle/Info.plist/Resources-mapping logic itself was dry-run
verified by configuring a throwaway CMake project with APPLE spoofed to
TRUE, confirming buildspec.json values substitute correctly into Info.plist
and obs-adapter/data/locale/en-US.ini maps to Resources/locale/en-US.ini.
Not verified: an actual macOS build/link/otool pass, or loading the result
in real OBS.app -- this is a Linux sandbox with no way to do either: this
commit is going to the home-mac CI runner to get that verification next.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 05:16:57 -07:00
shadowdaoandClaude Sonnet 5 85e2d08f18 ci: fix Windows step shell -- use powershell, not pwsh
Build / macOS (macos-latest) (push) Successful in 36s
Build / Linux (ubuntu-24.04) (push) Successful in 1m1s
Build / Windows (windows-latest) (push) Failing after 9m20s
Real verification push (2b216d3) caught this: explicitly setting
shell: pwsh made the Windows job fail immediately with "Cannot find:
pwsh in PATH". winvm-builder only has the in-box Windows PowerShell 5.1
(powershell.exe), not PowerShell Core -- which is exactly why the original
inline build.yml steps never set `shell:` at all and just relied on the
implicit default. Switched both workflows' Windows steps to
`shell: powershell` explicitly instead of leaving it implicit, so future
edits can't reintroduce this by copying a pwsh example from elsewhere.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 05:15:35 -07:00
shadowdaoandClaude Sonnet 5 2b216d3d75 ci: add tag-triggered release packaging workflow
Build / macOS (macos-latest) (push) Successful in 33s
Build / Linux (ubuntu-24.04) (push) Successful in 54s
Build / Windows (windows-latest) (push) Failing after 7m50s
Adds .gitea/workflows/release.yml, triggered only on a pushed v* tag, which
builds all three platforms (reusing the exact same configure/build/test
commands as build.yml, now factored out into .gitea/scripts/ so the two
workflows can't drift), zips each platform's build/package/ output, and
creates a draft Gitea Release with the archives attached.

This is packaging automation only -- it does not resolve or bypass the C1
WebRTC/OpenH264 release gate documented in README.md's Status section.
Nothing publishes until a human deliberately pushes a version tag (which
should not happen before owner sign-off) and then explicitly publishes the
resulting draft. The generated release notes lead with a restatement of the
open C1 question specifically so that second step can't be taken by
accident.

build.yml is refactored (not rewritten) to call the same shared scripts;
its job/step behavior is otherwise unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 05:06:25 -07:00
shadowdaoandClaude Sonnet 5 79de5e8f23 Fix Windows find_package(libobs) and make CI hard-fail when the OBS module is missing
Build / macOS (macos-latest) (push) Successful in 32s
Build / Linux (ubuntu-24.04) (push) Successful in 46s
Build / Windows (windows-latest) (push) Failing after 9m20s
Root cause of the find_package(libobs) failure on Windows: OBS's own
modern-CMake Windows layout (obs-studio/cmake/windows/defaults.cmake sets
OBS_CMAKE_DESTINATION=cmake) installs libobs's CMake package config to
<prefix>/cmake/libobs/ -- a shape find_package(libobs CONFIG) never
searches under CMAKE_PREFIX_PATH. Verified empirically with
--debug-find-pkg=libobs: CMake's Config-mode search suffixes try
<prefix>/cmake/libobsConfig.cmake (no <name> subdirectory) and
<prefix>/libobs*/cmake/... (a <name>-prefixed dir first), never
<prefix>/cmake/<name>*/. This has nothing to do with the earlier "file
INSTALL cannot find obs-frontend-api.dll" error the bootstrap already
tolerates -- libobs is the first subdirectory obs-studio's modern
top-level CMakeLists.txt adds (well before UI), so libobs's own
install(EXPORT ...) rules already completed by the time that later,
unrelated install error aborts the script. macOS is unaffected
(OBS_CMAKE_DESTINATION=lib/cmake there, matching the standard
<prefix>/lib*/cmake/<name>*/ suffix), and so is Linux's libobs-dev
(/usr/lib/<arch>/cmake/libobs/, same standard suffix).

Fix: point libobs_DIR directly at the from-source Windows install when it
exists, bypassing find_package's path-search heuristics entirely. Also
fixed a secondary bug found while tracing this: the existing WIN32
locationless-OBS::libobs repair looked for obs.dll under
"<deps>/bin" instead of the actual OBS_EXECUTABLE_DESTINATION,
"<deps>/bin/64bit".

Also make the Windows and macOS "Show what was built" CI steps hard-fail
when the OBS adapter module is missing, instead of only printing a
message. Several recent "green" Windows runs silently shipped a
core-library-only build because of the bug above; nothing in CI caught
it, only a human reading the raw log by hand. Left Linux untouched (its
check is already a hard, non-"|| true" verification). Applied the same
hard-fail treatment to macOS: its bootstrap has been reliably building
the real module in CI (6/6 tests, per README), so there's no longer a
known legitimate reason for a silent core-only fallback there either --
the documented macOS packaging/bundle-loadability gap is a separate,
already-visible issue this check doesn't touch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 04:53:03 -07:00
shadowdaoandClaude Sonnet 5 969b8db94a license: relicense first-party code from GPL-2.0-or-later to Apache-2.0
Build / macOS (macos-latest) (push) Successful in 33s
Build / Linux (ubuntu-24.04) (push) Successful in 54s
Build / Windows (windows-latest) (push) Successful in 12m10s
Owner sign-off: replace root LICENSE with Apache License 2.0, add a root
NOTICE file, and swap the GPL-2.0 boilerplate header in every first-party
core/ and obs-adapter/ source file for a short Apache-2.0 notice.

This resolves review finding C2 (GPLv2 top-level LICENSE vs. the vendored
Apache-2.0 LiveKit SDK is a license-compatibility violation): the whole
repo is now Apache-2.0, matching LiveKit, so there's no GPL/Apache clash
left. Updated the README Status gate and the CI workflow comment to reflect
that C2 is resolved, while leaving the C1 WebRTC/OpenH264 patent/royalty
gate untouched -- that question is still open and still blocks release.

third_party/ stays under its own upstream licenses; only this project's own
code changed hands. All 6 CTest suites still pass after the header swap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 04:44:16 -07:00
shadowdaoandClaude Sonnet 5 c4db99414d ci: copy LiveKit runtime DLLs next to Windows test executables
Build / macOS (macos-latest) (push) Successful in 35s
Build / Linux (ubuntu-24.04) (push) Successful in 48s
Build / Windows (windows-latest) (push) Successful in 12m5s
Windows CI now gets through CMake configure/build (the w32-pthreads
bootstrap patch landed), but CTest immediately failed 3/6 suites with
exit 0xc0000135 (STATUS_DLL_NOT_FOUND): test_session,
test_integration_livekit, test_livekit_smoke.

stplugin_core links LiveKit::livekit PUBLICly (core/CMakeLists.txt), so
every test executable under core/tests depends on livekit.dll /
livekit_ffi.dll at runtime. Unlike the $ORIGIN/@loader_path RPATH
handling obs-adapter/CMakeLists.txt already sets up for Linux/macOS,
Windows has no relative-to-the-exe DLL search path -- the DLLs must
physically sit next to the .exe (or be on PATH) when the process starts,
or the loader fails before main() runs. core/tests/CMakeLists.txt had no
equivalent staging step at all.

Added a POST_BUILD copy_if_different in stplugin_add_test(), guarded by
WIN32, that copies the same LIVEKIT_SDK_RUNTIME_LIBS list
(cmake/LiveKitSDK.cmake, already resolves to *.dll on Windows) into
$<TARGET_FILE_DIR:...> for each test binary -- mirroring the pattern
obs-adapter/CMakeLists.txt already uses for its own staged package.
Applied to all six tests rather than only the three known to reference
LiveKit symbols today: harmless for the other three, and avoids
re-diagnosing this if a future test starts touching stplugin_core's
LiveKit-dependent code paths.

Verified locally on Linux (WIN32 branch inert there, but confirms the
rest of the configure/build/test cycle is untouched):
  cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DSTPLUGIN_BOOTSTRAP_OBS=OFF
  cmake --build build
  ctest --test-dir build --output-on-failure   # 6/6 passed

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-07 00:57:36 -07:00
shadowdaoandClaude Sonnet 5 a3579d4fd5 docs: bash-on-Windows is proven broken (WSL as local system), with the log
Build / macOS (macos-latest) (push) Successful in 32s
Build / Linux (ubuntu-24.04) (push) Successful in 54s
Build / Windows (windows-latest) (push) Failing after 11m21s
The 58f4832 Windows run completed and failed, but not on the bootstrap: its
Windows steps still used `shell: bash`, and on winvm-builder bash resolves to
WSL, which refuses to run under the runner's service account:

  Running WSL as local system is not supported.
  Error code: Bash/WSL_E_LOCAL_SYSTEM_NOT_SUPPORTED
  ##[error]Process completed with exit code 1.

The step died on the shell without ever invoking cmake. So the PowerShell
rewrite in 524e78c -- which was written pre-emptively from reading the
workflow back, on the reasoning that bash "cannot be assumed present" -- is
now confirmed necessary by a real log rather than by argument. It is still
unproven in the other direction: no completed run has yet included it.

Equally important for reading the record correctly: this failure says nothing
about the OBS::w32-pthreads blocker. That result stands on edb0c02 alone, and
the README now says so explicitly so a later reader does not count 58f4832 as
corroboration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-06 23:12:57 -07:00
shadowdaoandClaude Sonnet 5 2bbd94775a ci: patch obs-studio's Windows CMake to define OBS::w32-pthreads
Build / macOS (macos-latest) (push) Successful in 32s
Build / Linux (ubuntu-24.04) (push) Successful in 1m2s
Build / Windows (windows-latest) (push) Failing after 11m20s
libobs/cmake/os-windows.cmake unconditionally links OBS::w32-pthreads, but
in the OBS_CMAKE_VERSION>=3.0.0 ("modern") top-level CMakeLists.txt branch
this bootstrap selects, nothing ever adds deps/w32-pthreads (confirmed at
every checked tag, 30.0.2 through 31.1.1) -- only the legacy branch's
add_subdirectory(deps) does, and libobs/CMakeLists.txt itself only adds
deps/libcaption and deps/uthash.

Upstream's own CI never hits this because it leaves ENABLE_UI on, and
UI/cmake/os-windows.cmake happens to add deps/w32-pthreads as a side effect
of building the Qt UI. Building obs-frontend-api instead of libobs (as
upstream's CI does) does not help: UI/obs-frontend-api/CMakeLists.txt only
links OBS::libobs, and UI/CMakeLists.txt returns before reaching the file
that adds w32-pthreads whenever ENABLE_UI is off -- which this bootstrap
deliberately keeps off to avoid a ~100 MB Qt6 download this plugin's plain
obs_properties_* UI does not need.

Add _patch_obs_studio_w32_pthreads() to buildspec_common.cmake: after the
obs-studio archive is extracted and before the OBS sub-configure runs, patch
its libobs/CMakeLists.txt to add deps/w32-pthreads itself, guarded by the
same if(NOT TARGET OBS::w32-pthreads) check UI/cmake/os-windows.cmake already
uses upstream. Idempotent (skips if already patched) and fails loudly if the
expected anchor text is missing, rather than silently no-opping against a
future OBS version with a different libobs/CMakeLists.txt shape.

Verified locally: the CMake string(FIND)/string(REPLACE) patch logic was run
against the real libobs/CMakeLists.txt fetched from the obs-studio 30.0.2
tag, confirmed to produce the intended block, and confirmed idempotent on a
second run. The actual Windows CMake configure/build this unblocks cannot be
verified from this Linux sandbox -- that's what the next CI run is for.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-06 23:12:41 -07:00
shadowdaoandClaude Sonnet 5 c5fedd176f docs: the -A x64 fix is confirmed; Windows now blocked on OBS's own CMake
Build / macOS (macos-latest) (push) Successful in 31s
Build / Linux (ubuntu-24.04) (push) Successful in 55s
Build / Windows (windows-latest) (push) Failing after 9m41s
Updates the Windows record written in 551f782, which was accurate when
written but has since been overtaken: the run it described as "in progress"
(edb0c02, the first commit carrying the -A x64 fix) has now completed.

The informative part is that the fix worked and Windows failed further along.
obs-deps and Qt6 downloaded, CEF was skipped, the OBS sub-configure ran for
53s and correctly reported "Plugin Support" and "User Interface" disabled --
then failed at generate time:

  CMake Error at libobs/cmake/os-windows.cmake:46 (target_link_libraries):
    Target "libobs" links to:
      OBS::w32-pthreads
    but the target was not found.

Traced at the 30.0.2 tag, and it does not appear to be anything this repo is
doing wrong:

  - libobs/cmake/os-windows.cmake links OBS::w32-pthreads;
  - that target exists only in deps/w32-pthreads/CMakeLists.txt, reached only
    via deps/CMakeLists.txt;
  - deps/ is added only by the LEGACY branch of the top-level CMakeLists. The
    modern branch that -DOBS_CMAKE_VERSION=3.0.0 selects adds libobs,
    libobs-d3d11, libobs-winrt, libobs-opengl, plugins, test/test-input and
    UI -- never deps;
  - libobs/CMakeLists.txt adds only deps/libcaption and deps/uthash;
  - plugins/CMakeLists.txt returns immediately under ENABLE_PLUGINS=OFF, and
    does not add deps/ even when enabled.

macOS is unaffected because its libobs does not link w32-pthreads, and at
30.0.2 the modern path was the default only on macOS -- consistent with the
Windows side of it being under-exercised upstream.

Deliberately NOT "fixed" here by bumping the pin: 30.0.2, 30.1.2, 30.2.3,
31.0.3 and 31.1.1 all still link OBS::w32-pthreads and none of them add
deps/w32-pthreads from libobs/CMakeLists.txt, so a bump is not obviously the
answer and needs checking rather than assuming. Three options are written up
with the evidence behind each, so whoever picks this up starts from a
diagnosis instead of from the error message.

Everything else 551f782 said stands, including its caution not to treat
either fix as confirmed without a green run -- the PowerShell rewrite is
still queued and still unproven, and is marked as such. The CI table row and
the top-level Status paragraph are updated to match; the licensing/release
gate is untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-06 23:08:39 -07:00
jknapp b2fac34a5a Merge pull request 'LiveKit integration: real camera feed pipeline' (#1) from feat/livekit-integration into main
Build / macOS (macos-latest) (push) Successful in 31s
Build / Linux (ubuntu-24.04) (push) Successful in 55s
Build / Windows (windows-latest) (push) Failing after 9m28s
2026-09-07 06:06:50 +00:00
47 changed files with 2035 additions and 780 deletions
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# Shared with .gitea/workflows/build.yml and .gitea/workflows/release.yml --
# this is the actual Linux configure/build/test/verify sequence. Edit once,
# here, so both workflows stay in sync instead of drifting copies.
#
# Linux keeps its distribution libobs; the buildspec bootstrap (macOS/Windows)
# is for the two platforms that have no such package.
set -euo pipefail
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DSTPLUGIN_BOOTSTRAP_OBS=OFF
cmake --build build
ctest --test-dir build --output-on-failure
# Show what was built
ls -la build/package/bin/64bit build/package/data/locale build/package/licenses
ldd build/package/bin/64bit/streamer-tools-camera.so | grep -E 'obs|livekit'
nm -D build/package/bin/64bit/streamer-tools-camera.so | grep -E ' T obs_module_(load|unload)'
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
# Shared with .gitea/workflows/build.yml and .gitea/workflows/release.yml --
# both need the exact same Linux build dependencies. Edit once, here.
set -euo pipefail
sudo apt-get update -qq
sudo apt-get install -y -qq cmake ninja-build libobs-dev libcurl4-openssl-dev
+112
View File
@@ -0,0 +1,112 @@
#!/usr/bin/env bash
# Shared with .gitea/workflows/build.yml and .gitea/workflows/release.yml --
# this is the actual macOS configure/build/test/verify sequence. Edit once,
# here, so both workflows stay in sync instead of drifting copies.
#
# The buildspec bootstrap runs here: it fetches obs-deps + the pinned
# obs-studio source and builds libobs before this project configures.
#
# If that fails, fall back to a core-library-only build rather than going
# red: the core library and its tests are what this job mainly guards, and
# the fallback is loud (a workflow warning, plus the check below reporting no
# module) rather than silent. Do not remove the warning -- a green job that
# quietly stopped building the plugin is worse than a red one.
set -euo pipefail
if ! cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release; then
echo "::warning::OBS SDK bootstrap failed on macOS; building the core library only. The plugin module was NOT built."
rm -rf build
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DSTPLUGIN_BOOTSTRAP_OBS=OFF
fi
cmake --build build
ctest --test-dir build --output-on-failure
# Show what was built.
#
# This is not purely informational: the macOS bootstrap has been reliably
# building the real module in CI (6/6 tests, artifact uploaded -- see
# README), so a missing module here is a regression to fail loudly on, not
# the old silent fallback.
#
# This used to only check for a bare streamer-tools-camera.so, which is
# exactly the shape the "macOS packaging gap" in README.md documented as
# broken: OBS.app cannot load a bare .so on macOS, it needs a <name>.plugin
# bundle, and this check was blind to whether one was actually produced --
# another instance of the same false-positive-green class of bug this check
# exists to catch (see the Windows find_package(libobs) incident this
# project just had). Now hard-fails unless:
# 1. a real streamer-tools-camera.plugin bundle exists at the top level of
# build/package (macOS's plugin search wants the whole .plugin dropped
# straight into .../obs-studio/plugins/, not nested under a bin/
# subdirectory the way Linux/Windows are -- see
# obs-studio/UI/window-basic-main.cpp AddExtraModulePaths()), with
# Contents/MacOS/streamer-tools-camera, Contents/Info.plist,
# Contents/Resources/locale/en-US.ini (the module's locale data -- OBS
# passes Contents/Resources as the module's data path on macOS, not a
# sibling data/), and the bundled LiveKit dylibs under
# Contents/Frameworks;
# 2. the libobs dependency in the built binary resolves via
# @rpath/libobs.framework, not the old relative path
# (libobs/libobs.framework/...) that a real OBS.app cannot load;
# 3. the binary carries LC_RPATH entries for both
# @loader_path/../Frameworks (this bundle's own Frameworks) and
# @executable_path/../Frameworks (OBS.app/Contents/Frameworks).
ls -la .deps/Frameworks/libobs.framework/Resources/cmake || true
bundle="build/package/streamer-tools-camera.plugin"
binary="${bundle}/Contents/MacOS/streamer-tools-camera"
plist="${bundle}/Contents/Info.plist"
locale="${bundle}/Contents/Resources/locale/en-US.ini"
if [ ! -f "${binary}" ]; then
echo "::error::no plugin bundle was built -- ${binary} is missing. The macOS from-source libobs bootstrap is expected to succeed and produce a real .plugin bundle (not a bare .so, and not nested under bin/); treat this as a build failure, not a core-library-only fallback or a silently-reverted bundle."
exit 1
fi
if [ ! -f "${plist}" ]; then
echo "::error::${bundle} exists but has no Contents/Info.plist -- this is not a loadable OBS.app bundle."
exit 1
fi
if [ ! -f "${locale}" ]; then
echo "::error::${locale} is missing -- OBS passes Contents/Resources as this module's data path on macOS (see AddExtraModulePaths() in obs-studio's UI/window-basic-main.cpp), so OBS_MODULE_USE_DEFAULT_LOCALE will fail to load the en-US locale at runtime."
exit 1
fi
echo "--- bundle contents ---"
find "${bundle}" -maxdepth 4 -exec ls -ld {} \;
echo "--- Info.plist ---"
cat "${plist}"
echo "--- otool -L ---"
otool -L "${binary}"
if ! ls "${bundle}"/Contents/Frameworks/liblivekit*.dylib >/dev/null 2>&1; then
echo "::error::no LiveKit dylibs found under ${bundle}/Contents/Frameworks -- the plugin will fail to load at runtime."
exit 1
fi
if otool -L "${binary}" | grep -q 'libobs/libobs\.framework'; then
echo "::error::the libobs dependency is still recorded as the old relative path (libobs/libobs.framework/...) instead of @rpath/libobs.framework/... -- this is the exact macOS packaging gap the bundle fixup exists to close, and it did not take effect."
otool -L "${binary}" | grep -E 'obs|livekit'
exit 1
fi
if ! otool -L "${binary}" | grep -q '@rpath/libobs\.framework'; then
echo "::error::expected an @rpath/libobs.framework dependency in ${binary}, found none."
otool -L "${binary}" | grep -E 'obs|livekit'
exit 1
fi
echo "--- LC_RPATH ---"
rpath_dump=$(otool -l "${binary}" | grep -A2 LC_RPATH)
echo "${rpath_dump}"
if ! echo "${rpath_dump}" | grep -q '@loader_path/../Frameworks'; then
echo "::error::${binary} has no @loader_path/../Frameworks LC_RPATH (needed to resolve the bundled LiveKit dylibs)."
exit 1
fi
if ! echo "${rpath_dump}" | grep -q '@executable_path/../Frameworks'; then
echo "::error::${binary} has no @executable_path/../Frameworks LC_RPATH (needed to resolve libobs.framework inside OBS.app)."
exit 1
fi
echo "macOS bundle verification passed: real .plugin bundle, @rpath libobs dependency, both LC_RPATH entries present."
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
# Shared with .gitea/workflows/build.yml and .gitea/workflows/release.yml.
set -euo pipefail
brew install cmake ninja
+109
View File
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
# Creates a (draft) Gitea Release for the tag that triggered
# .gitea/workflows/release.yml, and uploads every archive in $DIST_DIR as a
# release asset. Draft because nobody has run this plugin in the OBS GUI on
# any platform yet -- a human still opens it and clicks Publish once that's
# no longer true (or once they're satisfied regardless).
#
# Required env: GITEA_TOKEN, SERVER, OWNER, REPO, TAG, SHA, DIST_DIR
# Optional env: MACOS_BUNDLE_FOUND ("true"/"false", default "false")
set -euo pipefail
: "${GITEA_TOKEN:?}"
: "${SERVER:?}"
: "${OWNER:?}"
: "${REPO:?}"
: "${TAG:?}"
: "${SHA:?}"
: "${DIST_DIR:?}"
MACOS_BUNDLE_FOUND="${MACOS_BUNDLE_FOUND:-false}"
if [ "${MACOS_BUNDLE_FOUND}" = "true" ]; then
MACOS_NOTE="This archive contains a \`.plugin\` bundle."
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\`."
fi
NOTES_FILE="$(mktemp)"
cat > "${NOTES_FILE}" <<EOF
# streamer-tools Camera Plugin -- ${TAG}
Built from commit \`${SHA}\`.
**Nobody has yet run this plugin in the OBS GUI, on any platform.** See "What
is verified, and how" in \`README.md\` for exactly what has and has not been
checked, including which claims are backed by automated tests versus a human
watching OBS. This is why the release is a draft -- open it and click Publish
once you're satisfied.
| 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 |
| Windows (x64) | \`streamer-tools-camera-${TAG}-windows-x64.zip\` | Built and tested by this workflow's Windows job; the WinHTTP backend has never been exercised against a real streamer-tools server, only a loopback test server -- see README's Windows CI section |
| macOS | \`streamer-tools-camera-${TAG}-macos.zip\` | Built and tested by this workflow's macOS job. ${MACOS_NOTE} |
## Installing
Extract the archive into your OBS plugins folder for your platform (the
default locations are easy to find online -- typically
\`~/.config/obs-studio/plugins/\` on Linux, \`%APPDATA%\\obs-studio\\plugins\\\`
on Windows, \`~/Library/Application Support/obs-studio/plugins/\` on macOS).
Each archive's top-level folder already matches the shape OBS expects there,
so extracting is the whole install step. Then in OBS: Sources -> \`+\` ->
"streamer-tools Camera" -> fill in the server URL, room slug and read key
from the room's settings page -> "Refresh camera list" -> pick a camera.
## What this is
Native OBS Studio source plugin that pulls streamer-tools camera feeds
directly from LiveKit over WebRTC. See \`README.md\` in the repository for
the full design, what is and is not verified, and current CI status.
EOF
echo "----- release notes -----"
cat "${NOTES_FILE}"
echo "--------------------------"
BODY_JSON="$(python3 - "$TAG" "$NOTES_FILE" <<'PYEOF'
import json, sys
tag, notes_file = sys.argv[1], sys.argv[2]
with open(notes_file) as f:
notes = f.read()
print(json.dumps({
"tag_name": tag,
"name": tag,
"body": notes,
"draft": True,
"prerelease": False,
}))
PYEOF
)"
echo "Creating release for tag ${TAG} ..."
RESP="$(curl -sS -f -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "${BODY_JSON}" \
"${SERVER}/api/v1/repos/${OWNER}/${REPO}/releases")"
RELEASE_ID="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<<"${RESP}")"
echo "Created release id ${RELEASE_ID} (draft)."
shopt -s nullglob
ASSETS=("${DIST_DIR}"/*)
if [ "${#ASSETS[@]}" -eq 0 ]; then
echo "::error::no archives found in ${DIST_DIR} -- nothing to upload."
exit 1
fi
for f in "${ASSETS[@]}"; do
name="$(basename "${f}")"
echo "Uploading ${name} ..."
curl -sS -f -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-F "attachment=@${f}" \
"${SERVER}/api/v1/repos/${OWNER}/${REPO}/releases/${RELEASE_ID}/assets?name=${name}" \
> /dev/null
done
echo "Done. Draft release: ${SERVER}/${OWNER}/${REPO}/releases/${RELEASE_ID}"
+38
View File
@@ -0,0 +1,38 @@
# Shared with .gitea/workflows/build.yml and .gitea/workflows/release.yml --
# this is the actual Windows configure/build/test/verify sequence. Edit once,
# here, so both workflows stay in sync instead of drifting copies.
#
# The default Visual Studio generator is required, not Ninja:
# cmake/windows/buildspec.cmake keys the dependency slice off
# CMAKE_VS_PLATFORM_NAME, which only a VS generator sets.
#
# Same fallback as macOS, and the same warning: a green job that quietly
# stopped building the plugin is worse than a red one. PowerShell, not bash:
# self-hosted Windows runners here cannot be assumed to have a working bash
# (WSL as local system is refused).
cmake -S . -B build -A x64
if ($LASTEXITCODE -ne 0) {
Write-Host "::warning::OBS SDK bootstrap failed on Windows; building the core library only. The plugin module was NOT built."
Remove-Item -Recurse -Force build -ErrorAction SilentlyContinue
cmake -S . -B build -A x64 -DSTPLUGIN_BOOTSTRAP_OBS=OFF
if ($LASTEXITCODE -ne 0) { exit 1 }
}
cmake --build build --config Release
ctest --test-dir build -C Release --output-on-failure
# Show what was built.
#
# This is not purely informational: the from-source libobs bootstrap is
# expected to work reliably on this runner (that is the whole point of the
# buildspec bootstrap + find_package fix), so a missing module here is a
# regression to fail loudly on, not the old silent fallback.
$module = "build\package\bin\64bit\streamer-tools-camera.dll"
if (Test-Path $module) {
Get-ChildItem build\package\bin\64bit
} else {
Write-Host "::error::no plugin module was built -- $module is missing. The Windows from-source libobs bootstrap is expected to succeed; treat this as a build failure, not a core-library-only fallback."
exit 1
}
+117 -85
View File
@@ -10,17 +10,27 @@ name: Build
# downloads the pinned obs-deps bundle and obs-studio source and builds just
# `libobs`. That step is the slow one: several minutes on a cold runner.
#
# RELEASE GATE: this workflow only builds, tests, and uploads CI-internal
# workflow artifacts (actions/upload-artifact, below) -- it does not create a
# Gitea Release, push a tag-triggered publish, or otherwise distribute
# binaries publicly, and it must not start doing so without explicit owner
# sign-off on the WebRTC/OpenH264 attribution and GPLv2/Apache-2.0
# license-compatibility questions tracked in third_party/livekit/README.md
# and the README's top-level Status section. If a real release/publish step
# is ever added here, it must carry that same gate.
# The actual per-platform dependency-install and configure/build/test/verify
# commands live in .gitea/scripts/ and are shared with
# .gitea/workflows/release.yml, so the two workflows can't drift apart --
# edit the scripts, not either workflow, to change how a platform builds.
#
# This workflow only builds, tests, and uploads CI-internal workflow
# artifacts (actions/upload-artifact, below) -- it does not create a Gitea
# Release. .gitea/workflows/release.yml is that publish step, gated on a
# pushed version tag rather than on every push.
on:
push:
# Excludes tag pushes -- a bare `push:` matches every ref push, tags
# included, which meant tagging a release triggered THIS workflow's full
# 3-platform build (Windows and all) at the same time as
# release.yml's own -- two full Windows builds serialized behind the
# runner's capacity:1, for one tag push. release.yml already covers
# exactly this build (plus packaging) on every `v*` tag; this workflow's
# job is ordinary commits.
branches:
- "**"
pull_request:
jobs:
@@ -37,26 +47,10 @@ jobs:
uses: actions/checkout@v4
- name: Install build dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq cmake ninja-build libobs-dev libcurl4-openssl-dev
run: .gitea/scripts/linux-deps.sh
- name: Configure
# Linux keeps its distribution libobs; the buildspec bootstrap is for
# the two platforms that have no such package.
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DSTPLUGIN_BOOTSTRAP_OBS=OFF
- name: Build
run: cmake --build build
- name: Test (core library)
run: ctest --test-dir build --output-on-failure
- name: Show what was built
run: |
ls -la build/package/bin/64bit build/package/data/locale build/package/licenses
ldd build/package/bin/64bit/streamer-tools-camera.so | grep -E 'obs|livekit'
nm -D build/package/bin/64bit/streamer-tools-camera.so | grep -E ' T obs_module_(load|unload)'
- name: Configure, build, test, verify
run: .gitea/scripts/linux-build.sh
- name: Upload plugin
continue-on-error: true
@@ -73,36 +67,42 @@ jobs:
uses: actions/checkout@v4
- name: Install build dependencies
run: brew install cmake ninja
run: .gitea/scripts/macos-deps.sh
- name: Configure
# The buildspec bootstrap runs here: it fetches obs-deps + the pinned
# obs-studio source and builds libobs before this project configures.
#
# If that fails, fall back to a core-library-only build rather than
# going red: the core library and its tests are what this job mainly
# guards, and the fallback is loud (a workflow warning, plus the
# "Show what was built" step below reporting no module) rather than
# silent. Do not remove the warning -- a green job that quietly stopped
# building the plugin is worse than a red one.
run: |
if ! cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release; then
echo "::warning::OBS SDK bootstrap failed on macOS; building the core library only. The plugin module was NOT built."
rm -rf build
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DSTPLUGIN_BOOTSTRAP_OBS=OFF
fi
- name: Cache OBS SDK bootstrap deps
# cmake/macos/buildspec.cmake downloads the pinned obs-deps bundle +
# obs-studio source into .deps/ and already skips re-downloading via
# SHA256 marker files -- but .deps/ lives inside the checkout, so a
# fresh `actions/checkout` above wipes it every run regardless. This
# cache step is what actually makes that existing skip-logic apply
# across runs. The runner's built-in cache server is on by default
# (cache.enabled: true), so this needs no runner-side config.
uses: actions/cache@v4
with:
path: .deps
key: obs-deps-v2-${{ runner.os }}-${{ hashFiles('cmake/macos/buildspec.cmake', 'cmake/common/buildspec_common.cmake', 'buildspec.json') }}
- name: Build
run: cmake --build build
- name: Configure, build, test, verify
run: .gitea/scripts/macos-build.sh
- name: Test (core library)
run: ctest --test-dir build --output-on-failure
- name: Show what was built
run: |
ls -la .deps/Frameworks/libobs.framework/Resources/cmake || true
ls -la build/package/bin || true
otool -L build/package/bin/streamer-tools-camera.so || true
- name: Drop non-relocatable OBS build tree before caching
# cmake/common/buildspec_common.cmake's obs-studio sub-build writes
# an out-of-source CMakeCache.txt (.deps/obs-studio-*/build_*) that
# bakes in this job's absolute checkout path. The next run's
# checkout lands at a *different* absolute path, so restoring that
# directory from the cache above makes CMake refuse to reconfigure
# it ("CMakeCache.txt directory ... is different than the directory
# ... where CMakeCache.txt was created"). Everything that actually
# needs to survive between runs -- the extracted source, and the
# already-installed libobs package under .deps/cmake, .deps/include,
# .deps/lib -- has no such path baked in and is unaffected. Delete
# only the intermediate build tree, after it has already done its
# job (libobs is built and installed by this point), so the cache
# saved at the end of this job contains nothing that requires the
# path it was created under.
if: always()
continue-on-error: true
run: rm -rf .deps/obs-studio-*/build_*
- name: Upload plugin
continue-on-error: true
@@ -118,43 +118,75 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Install build dependencies
- name: Verify build dependencies
# winvm-builder is a self-hosted act_runner labeled "windows-latest";
# it is NOT the GitHub-hosted image, so none of that image's
# preinstalled tooling (cmake included) can be assumed present.
uses: lukka/get-cmake@latest
# preinstalled tooling can be assumed present. This used to be
# `uses: lukka/get-cmake@latest`, which re-downloaded and
# re-extracted CMake + Ninja on every single run -- its own cache
# (routed through this act_runner's cache server) reported a "cloud
# cache miss" on every run even immediately after a successful save,
# and separately the extraction step alone measured ~7.5 minutes on
# this VM (consistent with Defender real-time scanning, not raw I/O)
# -- together the dominant cost of every Windows CI run. CMake and
# Ninja are now installed once, directly on winvm-builder's system
# PATH (C:\BuildTools\cmake\bin, C:\BuildTools\ninja -- see the
# README's "Windows runner: persistent build tools" section for
# exactly what that machine has installed and how to redo it if the
# VM is ever rebuilt). This step just fails loudly if that ever
# stops being true, rather than silently falling back to a slow
# re-download.
shell: powershell
run: |
$ErrorActionPreference = "Stop"
cmake --version
ninja --version
- name: Configure
# The default Visual Studio generator is required, not Ninja:
# cmake/windows/buildspec.cmake keys the dependency slice off
# CMAKE_VS_PLATFORM_NAME, which only a VS generator sets.
- name: Cache OBS SDK bootstrap deps
# See the matching step in the macOS job above for why this is
# needed: cmake/windows/buildspec.cmake's own download logic is
# already idempotent, it just never gets the chance because .deps/
# lives inside the checkout and is wiped by every fresh clone.
#
# Same fallback as macOS, and the same warning: a green job that
# quietly stopped building the plugin is worse than a red one.
# PowerShell, not bash: this runner is a plain Windows VM and bash
# cannot be assumed present.
run: |
cmake -S . -B build -A x64
if ($LASTEXITCODE -ne 0) {
Write-Host "::warning::OBS SDK bootstrap failed on Windows; building the core library only. The plugin module was NOT built."
Remove-Item -Recurse -Force build -ErrorAction SilentlyContinue
cmake -S . -B build -A x64 -DSTPLUGIN_BOOTSTRAP_OBS=OFF
if ($LASTEXITCODE -ne 0) { exit 1 }
}
# The `v2` in the key: actions/cache never overwrites an existing
# key -- once a key has a saved entry, every later job's save step is
# skipped as a no-op, cache hit or not. The very first job ever
# to populate this cache did so BEFORE the "Drop non-relocatable OBS
# build tree" step below existed, so its save included the bad
# build_x86 directory -- and because saves under an existing key are
# permanently skipped, every run after that kept restoring that same
# bad entry forever, not "one more transitional run" as it looked at
# the time. Bumping the key is what actually forces a fresh save;
# bump it again (v3, ...) if this cache is ever found to be stale in
# a way a workflow change alone can't fix.
uses: actions/cache@v4
with:
path: .deps
key: obs-deps-v2-${{ runner.os }}-${{ hashFiles('cmake/windows/buildspec.cmake', 'cmake/common/buildspec_common.cmake', 'buildspec.json') }}
- name: Build
run: cmake --build build --config Release
- name: Configure, build, test, verify
# Windows PowerShell (powershell.exe), not PowerShell Core (pwsh) --
# this self-hosted runner does not have pwsh installed, only the
# in-box Windows PowerShell 5.1 the original inline steps implicitly
# ran under.
shell: powershell
run: ./.gitea/scripts/windows-build.ps1
- name: Test (core library)
run: ctest --test-dir build -C Release --output-on-failure
- name: Show what was built
run: |
if (Test-Path build\package\bin\64bit) {
Get-ChildItem build\package\bin\64bit
} else {
Write-Host "no plugin module was built (core library only)"
}
- name: Drop non-relocatable OBS build tree before caching
# See the matching step in the macOS job above. Confirmed live on
# this runner: caching .deps/obs-studio-30.0.2/build_x86 as-is made
# every run's first configure attempt fail with a path mismatch
# against the job that populated the cache, falling back to
# -DSTPLUGIN_BOOTSTRAP_OBS=OFF and only succeeding because the
# already-installed libobs package (path-independent) was still
# found. That fallback masked the problem behind a misleading
# "::warning::OBS SDK bootstrap failed" every run instead of fixing
# it. Deleting the build tree here, after libobs is already built
# and installed, is the actual fix.
if: always()
continue-on-error: true
shell: powershell
run: Remove-Item -Recurse -Force .deps\obs-studio-*\build_* -ErrorAction SilentlyContinue
- name: Upload plugin
continue-on-error: true
+227
View File
@@ -0,0 +1,227 @@
name: Release
# Packages a build of each platform into a downloadable archive and creates
# a (draft) Gitea Release for it, so the project owner and other directors
# can grab a ready-to-use build instead of compiling from source.
#
# Runs only on a pushed version tag (see `on.push.tags` below) -- never on an
# ordinary push or PR, unlike build.yml. The release it creates is a DRAFT:
# it stays invisible to anyone without write access until a human explicitly
# opens it and clicks Publish, since nobody has run this plugin in the OBS
# GUI on any platform yet.
#
# The actual per-platform build commands live in .gitea/scripts/ and are the
# same scripts .gitea/workflows/build.yml uses, so this workflow can't drift
# from what CI already builds and verifies on every push.
on:
push:
tags:
- "v*"
jobs:
linux:
name: Linux (ubuntu-24.04)
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install build dependencies
run: .gitea/scripts/linux-deps.sh
- name: Configure, build, test, verify
run: .gitea/scripts/linux-build.sh
- name: Package archive
run: |
set -euo pipefail
# zip is not guaranteed present on a minimal self-hosted runner
# image (unlike GitHub-hosted ubuntu-24.04, which build.yml's
# deps script doesn't need to care about).
command -v zip >/dev/null || sudo apt-get install -y -qq zip
out="streamer-tools-camera-${GITEA_REF_NAME}-linux-x64.zip"
root="$(pwd)"
# Wrap build/package/'s bin/+data/ inside a top-level
# streamer-tools-camera/ directory, matching the plugin directory
# name OBS itself expects under <config>/obs-studio/plugins/ (see
# obs-adapter/CMakeLists.txt's staging comment). This makes the
# archive a straight `unzip -d ~/.config/obs-studio/plugins/`
# drop-in -- no manual `cp -r bin data` step required.
stage="$(mktemp -d)"
mkdir -p "${stage}/streamer-tools-camera"
cp -r build/package/. "${stage}/streamer-tools-camera/"
( cd "${stage}" && zip -r "${root}/${out}" streamer-tools-camera )
rm -rf "${stage}"
mkdir -p dist
mv "${out}" "dist/${out}"
ls -la dist
env:
GITEA_REF_NAME: ${{ github.ref_name }}
- name: Upload archive
uses: actions/upload-artifact@v3
with:
name: release-archive-linux-x64
path: dist
macos:
name: macOS (macos-latest)
runs-on: macos-latest
outputs:
bundle_found: ${{ steps.package.outputs.bundle_found }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install build dependencies
run: .gitea/scripts/macos-deps.sh
- name: Configure, build, test, verify
run: .gitea/scripts/macos-build.sh
- name: Package archive
id: package
run: |
set -euo pipefail
out="streamer-tools-camera-${GITEA_REF_NAME}-macos.zip"
root="$(pwd)"
mkdir -p dist
# Look for a *.plugin bundle rather than assuming its exact final
# location, falling back to packaging build/package/ as-is (a
# bare .so, not a loadable bundle) only if the bundle step didn't
# run or produced nothing -- see the macOS packaging gap in
# README.md for when that fallback path is actually live. The
# bundle itself is zipped at the archive's top level (cd into its
# parent, zip just the bundle dir) so the archive is already a
# straight `unzip -d ~/Library/Application\ Support/obs-studio/
# plugins/` drop-in -- no wrapping needed here, unlike
# Linux/Windows above, because OBS wants the whole *.plugin
# bundle directly under plugins/, not nested under a named
# subdirectory.
bundle="$(find build -maxdepth 4 -type d -name '*.plugin' 2>/dev/null | head -n1 || true)"
if [ -n "${bundle}" ]; then
echo "Found macOS .plugin bundle: ${bundle}"
( cd "$(dirname "${bundle}")" && zip -r "${root}/${out}" "$(basename "${bundle}")" )
echo "bundle_found=true" >> "${GITHUB_OUTPUT}"
else
echo "::warning::No .plugin bundle found under build/ -- packaging build/package/ as-is. This is NOT yet a loadable OBS.app plugin; see the macOS packaging gap in README.md."
( cd build/package && zip -r "${root}/${out}" . )
echo "bundle_found=false" >> "${GITHUB_OUTPUT}"
fi
mv "${out}" "dist/${out}"
ls -la dist
env:
GITEA_REF_NAME: ${{ github.ref_name }}
- name: Upload archive
uses: actions/upload-artifact@v3
with:
name: release-archive-macos
path: dist
windows:
name: Windows (windows-latest)
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Verify build dependencies
# See build.yml's Windows job for why this is no longer
# lukka/get-cmake@latest -- CMake and Ninja are installed once,
# directly on winvm-builder's system PATH; this just fails loudly
# if that ever stops being true.
shell: powershell
run: |
$ErrorActionPreference = "Stop"
cmake --version
ninja --version
- name: Configure, build, test, verify
# Windows PowerShell (powershell.exe), not PowerShell Core (pwsh) --
# this self-hosted runner does not have pwsh installed. See the same
# note in build.yml.
shell: powershell
run: ./.gitea/scripts/windows-build.ps1
- name: Package archive
shell: powershell
run: |
$ErrorActionPreference = "Stop"
$out = "streamer-tools-camera-$env:GITEA_REF_NAME-windows-x64.zip"
New-Item -ItemType Directory -Force -Path dist | Out-Null
# Wrap build\package\'s bin\+data\ inside a top-level
# streamer-tools-camera\ directory, matching the plugin directory
# name OBS itself expects under %APPDATA%\obs-studio\plugins\ (see
# obs-adapter/CMakeLists.txt's staging comment). This makes the
# archive a straight `Expand-Archive -DestinationPath
# $env:APPDATA\obs-studio\plugins\` drop-in -- no manual copy step
# required. Compress-Archive includes the source folder's own name
# as the archive root when given a single directory path, so
# staging under a streamer-tools-camera\ dir is enough on its own.
$stage = Join-Path $env:TEMP "stplugin-stage-$([guid]::NewGuid())"
$pluginDir = Join-Path $stage "streamer-tools-camera"
New-Item -ItemType Directory -Force -Path $pluginDir | Out-Null
Copy-Item -Path build\package\* -Destination $pluginDir -Recurse
Compress-Archive -Path $pluginDir -DestinationPath "dist\$out" -Force
Remove-Item -Recurse -Force $stage
Get-ChildItem dist
env:
GITEA_REF_NAME: ${{ github.ref_name }}
- name: Upload archive
uses: actions/upload-artifact@v3
with:
name: release-archive-windows-x64
path: dist
release:
name: Create Gitea Release (draft)
needs: [linux, macos, windows]
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: Checkout
# Not strictly needed to build anything here, but publish-release.sh
# lives in the repo and this keeps the release job self-contained /
# easy to reason about rather than reaching into another job's
# checkout.
uses: actions/checkout@v4
- name: Download Linux archive
uses: actions/download-artifact@v3
with:
name: release-archive-linux-x64
path: dist
- name: Download macOS archive
uses: actions/download-artifact@v3
with:
name: release-archive-macos
path: dist
- name: Download Windows archive
uses: actions/download-artifact@v3
with:
name: release-archive-windows-x64
path: dist
- name: Create draft release and upload assets
run: .gitea/scripts/publish-release.sh
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
SERVER: https://repo.anhonesthost.net
OWNER: CyberCoveLLC
REPO: obs-streamer-tools-plugin
TAG: ${{ github.ref_name }}
SHA: ${{ github.sha }}
DIST_DIR: dist
MACOS_BUNDLE_FOUND: ${{ needs.macos.outputs.bundle_found }}
+179 -1
View File
@@ -136,8 +136,179 @@ find_package(LiveKit CONFIG REQUIRED)
add_subdirectory(core)
# On Windows, OBS's own modern-CMake install layout
# (obs-studio/cmake/windows/defaults.cmake: OBS_CMAKE_DESTINATION=cmake)
# installs libobs's CMake package config to <prefix>/cmake/<target>/ --
# a shape find_package(<pkg> CONFIG) never searches under CMAKE_PREFIX_PATH.
# CMake's documented Config-mode search suffixes include
# "<prefix>/(cmake|CMake)/" (the file directly inside that dir, no <name>
# subdirectory) and "<prefix>/<name>*/(cmake|CMake)/[...]" (a <name>-prefixed
# directory first) -- neither matches "<prefix>/cmake/<name>*/". Verified
# empirically: a find_package(libobs CONFIG) run with CMAKE_PREFIX_PATH
# pointed at a directory laid out exactly like this (cmake/libobs/
# libobsConfig.cmake underneath it) never even tries that path, confirmed
# with --debug-find-pkg=libobs.
#
# macOS does not have this problem -- its OBS_CMAKE_DESTINATION is
# "lib/cmake", so the package lands at <prefix>/lib/cmake/libobs/, which
# matches the standard "<prefix>/lib*/cmake/<name>*/" suffix -- and neither
# does Linux's libobs-dev, which installs to
# /usr/lib/<arch-triplet>/cmake/libobs/, the same standard suffix. Windows is
# the one platform whose own upstream install destination CMake's
# find_package was never going to locate on its own; this is not caused by,
# and is not fixed by, the earlier "file INSTALL cannot find
# obs-frontend-api.dll" error tolerated in
# cmake/common/buildspec_common.cmake's _setup_obs_studio -- libobs is the
# FIRST subdirectory obs-studio's modern top-level CMakeLists.txt adds
# (before libobs-d3d11/-winrt/-opengl, plugins, test/test-input and finally
# UI), so libobs's own install(EXPORT ...) rules, including this Config file,
# already ran to completion by the time cmake --install later aborts inside
# UI/obs-frontend-api's install script.
#
# So: point find_package(libobs) directly at the from-source Windows install
# rather than relying on path-search heuristics that were never going to find
# it. Guarded by "NOT libobs_DIR" so an explicit -Dlibobs_DIR=... from a
# developer with their own OBS SDK is never overridden, and by EXISTS so this
# is a no-op whenever STPLUGIN_BOOTSTRAP_OBS=OFF or the bootstrap didn't
# reach this point.
if(OS_WINDOWS AND NOT libobs_DIR)
set(_stplugin_win_libobs_dir "${CMAKE_CURRENT_SOURCE_DIR}/.deps/cmake/libobs")
if(EXISTS "${_stplugin_win_libobs_dir}/libobsConfig.cmake")
set(libobs_DIR "${_stplugin_win_libobs_dir}" CACHE PATH
"Directory containing libobsConfig.cmake" FORCE)
message(STATUS
"libobs_DIR not set; OBS's Windows OBS_CMAKE_DESTINATION=cmake "
"does not match any of CMake's standard find_package Config-mode "
"search suffixes, so pointing it directly at the from-source "
"install: ${_stplugin_win_libobs_dir}")
endif()
endif()
# ...and the exact same problem, one level down, for w32-pthreads.
#
# libobs/cmake/os-windows.cmake links `PUBLIC OBS::w32-pthreads`, so
# obs-studio's libobsConfig.cmake.in carries, verbatim:
#
# if(MSVC)
# find_dependency(w32-pthreads REQUIRED)
# endif()
#
# That call is REQUIRED, it runs from inside libobsConfig.cmake, and until
# this block existed it aborted the whole configure the moment the libobs_DIR
# fix above finally succeeded in loading that config file:
#
# By not providing "Findw32-pthreads.cmake" in CMAKE_MODULE_PATH this
# project has asked CMake to find a package configuration file provided by
# "w32-pthreads", but CMake did not find one.
# .deps/cmake/libobs/libobsConfig.cmake:30 (find_dependency)
#
# This is NOT a missing export. deps/w32-pthreads/CMakeLists.txt ends with
# `target_export(w32-pthreads)`, which is the same helper libobs itself uses
# (cmake/common/helpers_common.cmake): it emits install(TARGETS ... EXPORT
# w32-pthreadsTargets), install(EXPORT ... NAMESPACE OBS::), a generated
# w32-pthreadsConfig.cmake and its version file, all COMPONENT Development.
# Those rules *must* exist and *must* have run, for two independent reasons:
#
# 1. CMake hard-errors at generate time if a target in one export set links
# to a target that is in no export set at all ("install(EXPORT
# "libobsTargets") includes target "libobs" which requires target
# "w32-pthreads" that is not in any export set"). obs-studio's generate
# step succeeded, so w32-pthreads was exported.
# 2. The install rules land under the libobs *directory* (this repo's
# _patch_obs_studio_w32_pthreads adds deps/w32-pthreads from
# libobs/CMakeLists.txt), and libobs is the first subdirectory the modern
# top-level CMakeLists.txt adds -- so its whole subtree installs before
# the tolerated UI/obs-frontend-api install error aborts the rest.
#
# The failure is purely the search path, identical to libobs's: target_export
# installs the package to "${OBS_CMAKE_DESTINATION}/${target}", which on
# Windows is <prefix>/cmake/w32-pthreads/ -- a shape find_package never
# searches. So apply the same remedy CMake's own error message suggests, and
# do it BEFORE find_package(libobs) below, because that is the call that
# transitively triggers find_dependency(w32-pthreads).
#
# macOS and Linux never reach this: the find_dependency is inside `if(MSVC)`,
# and OS_WINDOWS gates the block regardless -- a pure no-op off Windows.
if(OS_WINDOWS AND NOT w32-pthreads_DIR)
set(_stplugin_win_pthreads_dir "${CMAKE_CURRENT_SOURCE_DIR}/.deps/cmake/w32-pthreads")
if(EXISTS "${_stplugin_win_pthreads_dir}/w32-pthreadsConfig.cmake")
set(w32-pthreads_DIR "${_stplugin_win_pthreads_dir}" CACHE PATH
"Directory containing w32-pthreadsConfig.cmake" FORCE)
message(STATUS
"w32-pthreads_DIR not set; libobsConfig.cmake's "
"find_dependency(w32-pthreads REQUIRED) hits the same "
"OBS_CMAKE_DESTINATION=cmake search-suffix problem as libobs "
"itself, so pointing it directly at the from-source install: "
"${_stplugin_win_pthreads_dir}")
else()
# Fall back to a hand-written find module rather than letting the
# REQUIRED find_dependency kill the configure. cmake/windows is
# already on CMAKE_MODULE_PATH (see cmake/common/osconfig.cmake), so
# the fallback module deliberately lives in a subdirectory that is
# NOT -- a Findw32-pthreads.cmake sitting on the default module path
# would shadow OBS's own exported package on every build, and its
# real export is the better answer whenever it is present.
#
# ...and only when this repo's own bootstrap actually ran, which is
# the only situation the fallback module knows how to serve: it reads
# artifacts out of .deps/ and nowhere else. A developer building
# against their own external OBS SDK (STPLUGIN_BOOTSTRAP_OBS=OFF, or
# an explicit -Dlibobs_DIR=...) must be left on CMake's normal search
# so that whatever w32-pthreads package their SDK does provide still
# wins -- exposing a find module that can only ever fail would turn
# their build's outcome into a worse error, not a better one.
file(GLOB _stplugin_deps_obs_trees "${CMAKE_CURRENT_SOURCE_DIR}/.deps/obs-studio-*")
if(_stplugin_deps_obs_trees)
# Reaching here means the bootstrap ran but its install did not
# produce the export, so say exactly what IS in .deps/cmake/ --
# the next CI log then answers the question directly instead of
# costing another run.
file(GLOB _stplugin_deps_cmake_dirs "${CMAKE_CURRENT_SOURCE_DIR}/.deps/cmake/*")
message(STATUS
"No w32-pthreadsConfig.cmake at ${_stplugin_win_pthreads_dir}; "
"OBS CMake packages actually installed under .deps/cmake: "
"'${_stplugin_deps_cmake_dirs}'. Falling back to this repo's own "
"Findw32-pthreads.cmake, which builds OBS::w32-pthreads straight "
"from the bootstrap's build artifacts.")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/find-fallback")
endif()
endif()
endif()
find_package(libobs QUIET)
# OBS::w32-pthreads can come back locationless for exactly the same reason
# OBS::libobs can (see the block below this one): the package config loads,
# but the per-configuration w32-pthreadsTargets-release.cmake that carries
# IMPORTED_IMPLIB/IMPORTED_LOCATION may not have been installed. libobs links
# it PUBLIC, so it is on this plugin's own link line and an empty location is
# a generate-time error, not a warning. Repair it from the known install
# destinations (obs-studio/cmake/windows/defaults.cmake:
# OBS_EXECUTABLE_DESTINATION=bin/64bit for the DLL, OBS_LIBRARY_DESTINATION=lib
# for the import library) -- the same repair, and the same reasoning, as the
# OBS::libobs one.
if(OS_WINDOWS AND TARGET OBS::w32-pthreads)
get_target_property(_stplugin_pthreads_implib OBS::w32-pthreads IMPORTED_IMPLIB)
get_target_property(_stplugin_pthreads_implib_release OBS::w32-pthreads IMPORTED_IMPLIB_RELEASE)
if(NOT _stplugin_pthreads_implib AND NOT _stplugin_pthreads_implib_release)
find_file(_stplugin_pthreads_implib_found w32-pthreads.lib
PATHS "${CMAKE_CURRENT_SOURCE_DIR}/.deps/lib" NO_DEFAULT_PATH)
find_file(_stplugin_pthreads_dll_found w32-pthreads.dll
PATHS "${CMAKE_CURRENT_SOURCE_DIR}/.deps/bin/64bit" NO_DEFAULT_PATH)
if(_stplugin_pthreads_implib_found)
set_target_properties(OBS::w32-pthreads PROPERTIES
IMPORTED_IMPLIB "${_stplugin_pthreads_implib_found}")
if(_stplugin_pthreads_dll_found)
set_target_properties(OBS::w32-pthreads PROPERTIES
IMPORTED_LOCATION "${_stplugin_pthreads_dll_found}")
endif()
message(STATUS
"OBS::w32-pthreads had no imported location; pointed it at "
"${_stplugin_pthreads_implib_found}")
endif()
endif()
endif()
# The imported OBS::libobs target can come back without a location. OBS 30.0.2
# installs libobsTargets.cmake but not the per-configuration
# libobsTargets-<config>.cmake alongside it when libobs is built on its own,
@@ -176,8 +347,15 @@ if(libobs_FOUND AND TARGET OBS::libobs)
message(STATUS "OBS::libobs had no imported location; pointed it at ${_stplugin_obs_binary}")
endif()
elseif(WIN32)
# obs-studio/cmake/windows/defaults.cmake sets
# OBS_EXECUTABLE_DESTINATION=bin/64bit (the RUNTIME destination
# obs.dll installs to as a SHARED_LIBRARY target) and
# OBS_LIBRARY_DESTINATION=lib (the ARCHIVE destination for
# obs.lib) -- not a flat "bin", which is what upstream
# obs-plugintemplate's own equivalent macOS-only repair never had
# to get right.
find_file(_stplugin_obs_implib_found obs.lib PATHS "${_stplugin_deps}/lib" NO_DEFAULT_PATH)
find_file(_stplugin_obs_dll_found obs.dll PATHS "${_stplugin_deps}/bin" NO_DEFAULT_PATH)
find_file(_stplugin_obs_dll_found obs.dll PATHS "${_stplugin_deps}/bin/64bit" NO_DEFAULT_PATH)
if(_stplugin_obs_implib_found)
set_target_properties(OBS::libobs PROPERTIES IMPORTED_IMPLIB "${_stplugin_obs_implib_found}")
if(_stplugin_obs_dll_found)
+168 -305
View File
@@ -1,339 +1,202 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Preamble
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
1. Definitions.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
The precise terms and conditions for copying, distribution and
modification follow.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
APPENDIX: How to apply the Apache License to your work.
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright [yyyy] [name of copyright owner]
{description}
Copyright (C) {year} {fullname}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
http://www.apache.org/licenses/LICENSE-2.0
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+4
View File
@@ -0,0 +1,4 @@
obs-streamer-tools-plugin
Copyright 2026 CyberCoveLLC
Licensed under the Apache License, Version 2.0.
+342 -51
View File
@@ -8,34 +8,49 @@ Media-Source path for directors. Full design:
## Status
**Release/distribution of built binaries is blocked pending owner sign-off.**
This plugin statically/dynamically pulls in Google WebRTC and OpenH264 code
through the LiveKit SDK, and this repository's own top-level `LICENSE` is
GPLv2 while the vendored LiveKit binaries are Apache-2.0 — both a real patent/
royalty question (OpenH264/WebRTC) and a real license-compatibility question
(GPLv2 vs. Apache-2.0-linked code) that only the project owner can decide.
Nothing in this repo should be built into a package and handed out, posted,
or attached to a public release until that sign-off happens. See
`third_party/livekit/README.md` for the specifics of what is and is not
currently known/shipped on the licensing side. (CI in `.gitea/workflows/build.yml`
currently only builds, tests, and uploads CI-internal build artifacts — it
does not create a Gitea Release or otherwise publish anything publicly; if
that ever changes, the new step must carry this same gate.)
This project's own code is Apache-2.0 (relicensed from GPL-2.0-or-later to
match the vendored LiveKit binaries, which are also Apache-2.0 — see
`LICENSE` and `NOTICE`, and `third_party/livekit/` for LiveKit's own).
`.gitea/workflows/build.yml` builds, tests, and uploads CI-internal build
artifacts on every push. `.gitea/workflows/release.yml` packages a tagged
build (`v*`) into a **draft** Gitea Release; a human still needs to open it
and click Publish.
The plugin is **functionally complete on Linux and verified end to end there**
(module loads into real libobs, connects to a real LiveKit server through the
real streamer-tools API shape, and pushes decoded frames into
`obs_source_output_video`/`_audio`).
It has **not been run in the OBS GUI on any platform.** macOS builds the real
module in CI but its artifact is not yet loadable (see the macOS packaging gap
under CI). Windows CI has **failed on every completed run so far** (7
consecutive failures on this branch as of this writing); a run against the
commit with the `-A x64` argument fix is in progress but not yet complete,
and the commit with the PowerShell rewrite of the Windows steps is still
queued behind it. Neither fix has a completed, passing run yet. See "Where
the Windows bootstrap got to" under CI below for the exact record, and check
current CI status rather than trusting this paragraph's age.
**First confirmed OBS GUI load: Windows, 2026-09-09** — the v0.1.0 release
artifact loaded into OBS 32.2.2 on Windows 11 (build 26200) on a director's
machine, from
`C:\ProgramData\obs-studio\plugins\streamer-tools-camera\bin\64bit\`.
That retires "the module will not even load in a real OBS" for Windows. It
does **not** yet cover whether video renders correctly, colours, A/V sync or
latency — see "Not verified anywhere" below for what is still open. Linux and
macOS have still never been opened in the GUI; macOS builds the real module in
CI but its artifact is not yet loadable (see the macOS packaging gap under CI).
⚠️ **The install directory is not the same on every platform, and getting it
wrong fails silently.** On Windows it is
`C:\ProgramData\obs-studio\plugins\` (`GetProgramDataPath`
`CSIDL_COMMON_APPDATA`), **not** `%APPDATA%\obs-studio\` — see the packaging
section. That mistake cost the director above an evening: OBS logs nothing at
all for a plugin it never finds.
**Windows CI is now green.** The run at `f27b1c0` is the first completed
green Windows job on this repository: the from-source libobs bootstrap
configures, builds and installs, `find_package(libobs)` resolves, all 6 CTest
suites pass, and `build\package\bin\64bit\streamer-tools-camera.dll`
(136,192 bytes) is staged next to `livekit.dll` and `livekit_ffi.dll` — read
out of the job's own log body, not inferred from the job status. That also
retires three previously-unproven items in one go: the `-A x64` argument fix,
the PowerShell rewrite of the Windows steps, and the `add_subdirectory`
patch for `OBS::w32-pthreads`. Windows has since been **loaded in the real OBS
GUI** (see above); Linux and macOS have not. See "Where the Windows bootstrap got
to" under CI below for the whole trace, and check current CI status rather
than trusting this paragraph's age.
See "What is verified, and how" below for exactly what has and has not been
checked, and "Testing this by hand" for what a human still needs to do.
@@ -61,7 +76,9 @@ obs-adapter/ - thin OBS glue (C++)
data/locale/en-US.ini
scripts/livekit-dev-room.py - mints tokens for the integration test
third_party/livekit/ - redistribution notices for the LiveKit binaries
.gitea/workflows/build.yml - 3-platform CI matrix
.gitea/scripts/ - the actual per-platform build commands, shared by build.yml and release.yml
.gitea/workflows/build.yml - 3-platform CI matrix (every push/PR; never publishes)
.gitea/workflows/release.yml - packages + creates a draft Gitea Release (only on a `v*` tag push; see Status above)
```
## How it works
@@ -137,17 +154,28 @@ build/package/licenses/...
```
That is exactly the layout OBS searches on Linux and Windows —
`<config>/obs-studio/plugins/<name>/bin/64bit` plus a sibling `data/`, per
`AddExtraModulePaths()` in obs-studio's `UI/window-basic-main.cpp` — so
`build/package/` is a straight drop-in. The module resolves the LiveKit
libraries from `$ORIGIN` (verified: `ldd` on the staged copy resolves both
`<base>/obs-studio/plugins/<name>/bin/64bit` plus a sibling `data/`, per
`AddExtraModulePaths()` in obs-studio (`UI/window-basic-main.cpp` in 30.x,
`frontend/widgets/OBSBasic.cpp` in 32.x) — so `build/package/` is a straight
drop-in. **`<base>` is NOT the same directory on every platform**, and getting
this wrong is silent: OBS logs nothing at all for a plugin it never finds.
Linux uses the user config dir (`GetAppConfigPath``~/.config`), but Windows
uses `GetProgramDataPath` (`CSIDL_COMMON_APPDATA`) — i.e.
`C:\ProgramData\obs-studio\plugins\`, **not** `%APPDATA%\obs-studio\`
(`CSIDL_APPDATA`), which on Windows holds OBS's config and is never scanned for
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.
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
macOS packaging gap under CI.
## Testing this by hand
**Nobody has yet run this in the OBS GUI. That test is still outstanding on
all three platforms.** To do it on Linux:
**The module has been loaded in the OBS GUI on Windows once (2026-09-09, OBS
32.2.2 / Windows 11 26200) — nothing beyond "it loads and registers its source"
is confirmed there, and Linux and macOS have never been opened in the GUI at
all.** To do it on Linux:
```
mkdir -p ~/.config/obs-studio/plugins/streamer-tools-camera
@@ -203,7 +231,11 @@ 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 |
**Not verified anywhere:**
- The OBS GUI, on any platform. No human has looked at this in OBS.
- Anything past module load in the OBS GUI. Windows 2026-09-09 confirms the
module loads and its source type appears; whether video actually renders
(right way up, right colours), what the A/V sync and latency look like, and
whether a publisher restarting mid-show recovers on screen are all still
unanswered. Linux and macOS have not been opened in the GUI at all.
- macOS beyond "CI builds and links the real module and the core tests pass".
Its artifact is a bare `.so` with a relative libobs install name and will
not load in OBS.app — see the macOS packaging gap under CI.
@@ -228,7 +260,7 @@ runners available to this repo under the `CyberCoveLLC` org.
|---|---|---|---|
| `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 |
| `windows` | `windows-latest` | `winvm-builder` (org-scoped) | **Failing** — 7/7 completed runs on this branch have failed; see 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 |
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,
@@ -251,6 +283,51 @@ and a permanently red CI teaches people to ignore CI. **Do not remove the
warning:** a green job that quietly stopped building the plugin is worse than
a red one.
### Windows runner: persistent build tools (2026-09-07)
`winvm-builder`'s Windows job used to install its own CMake + Ninja on every
single run via `uses: lukka/get-cmake@latest`. That action has its own
caching (routed through this act_runner's built-in cache server, the same
mechanism `.deps/`'s `actions/cache` step above relies on and that one does
work) but it never hit: every run logged `Cloud cache miss` against the same
cache key, even immediately after a run that logged a successful save under
that exact key -- some incompatibility between `lukka/get-cmake`'s bundled
cache client and this act_runner's cache-server implementation, not
"caching isn't configured." Separately, and the larger cost: the archive
extraction step alone measured **~7.5 minutes** for a 45MB zip on this VM
(13:11:14 to 13:18:48 in one captured run) -- consistent with Windows
Defender real-time-scanning every extracted file, not raw disk I/O, though
that specific cause is not confirmed. Together this was the dominant cost of
every Windows CI run, cold cache or not.
Fix: CMake 4.4.2 and Ninja 1.12.1 are now installed once, directly on the
`winvm-builder` VM (Proxmox VMID 110, host pve4/192.168.1.145), not fetched
per-run:
- `C:\BuildTools\cmake\` (from
`cmake-4.4.2-windows-x86_64.zip`, Kitware's GitHub releases) and
`C:\BuildTools\ninja\` (from `ninja-win.zip`, `ninja-build/ninja` v1.12.1
release) — plain `Expand-Archive` drops, nothing installed via an
installer/MSI.
- Both added to the **Machine**-level `PATH`
(`[Environment]::SetEnvironmentVariable('PATH', ..., 'Machine')`, not
`setx`, which silently truncates a `PATH` this long).
- The `GiteaRunner-winvm-builder` scheduled task (`C:\gitea-runner\
gitea-runner.exe daemon`, runs as SYSTEM) was stopped and restarted after
the `PATH` change — a already-running process does not pick up an updated
Machine environment variable, only processes started after the change do,
and every CI job is a child process of this one long-running daemon.
Both workflows' Windows jobs now just run `cmake --version` / `ninja
--version` as a "Verify build dependencies" step and fail loudly if either
is missing, instead of silently falling back to the slow per-run install.
**This is VM state, not something `git clone` reproduces.** If
`winvm-builder` is ever rebuilt or reimaged, redo the three steps above
(download+extract both zips under `C:\BuildTools\`, extend the Machine
`PATH`, restart the scheduled task) before expecting Windows CI to pass
again — there is nothing in this repo that does it automatically.
### Where the macOS bootstrap actually got to
Six CI iterations, each fixing a real failure visible in the logs:
@@ -304,18 +381,108 @@ Windows is by far the slowest job — the `lukka/get-cmake` step alone takes
7-15 minutes on `winvm-builder`, and the runner serialises jobs, so a burst of
pushes leaves a queue that takes an hour to drain.
**The honest record: every completed Windows CI run on this branch has
failed. 7 consecutive failures**, at the 7 branch commits (in order) that had
a completed Windows run as of this writing -- all of them at commits before
the `-A x64` fix below was applied. As of this writing, a Windows run against
the commit with that fix is in progress but has not yet completed, and the
commit with the PowerShell rewrite is still queued behind it (the runner's
serial queue means fixed commits can sit behind older, unfixed ones for a
while). Do not read either fix below as "confirmed" until a Windows run
actually goes green on a commit that includes it; check current CI status
rather than trusting this paragraph's age.
**The honest record, as it stood before `f27b1c0`: every completed Windows CI
run on this branch had failed.** (`f27b1c0` is the one that finally went
green — see "After the w32-pthreads target" at the end of this section. The
history below is kept because each dead end in it is a real constraint
someone will otherwise rediscover.) The first 7 failures were all at commits
predating the `-A x64` fix
below. The 8th, at `edb0c02` — the first commit that actually carries that fix
— has since completed, and it is the informative one: **the `-A x64` fix
worked, and Windows failed further along, on something else.** A later run
(`58f4832`) then failed for an unrelated reason — its Windows steps still used
`shell: bash`, which on this runner is WSL and cannot run as local system —
so it never reached cmake at all and tells us nothing about the bootstrap.
The PowerShell rewrite that fixes that is still queued and remains unproven.
Do not read any fix below as "confirmed" beyond what is stated; check current
CI status rather than trusting this paragraph's age.
Two bugs of its own were found and (believed, not yet proven) fixed:
What `edb0c02` showed: obs-deps and Qt6 downloaded, CEF skipped, the OBS
sub-configure ran for 53s and correctly reported "Plugin Support" and "User
Interface" disabled — then failed at generate time:
```
CMake Error at libobs/cmake/os-windows.cmake:46 (target_link_libraries):
Target "libobs" links to:
OBS::w32-pthreads
but the target was not found.
```
**That looks like a genuine hole in OBS 30.0.2's opt-in modern CMake path on
Windows, not something this repo is doing wrong.** Traced at the 30.0.2 tag:
- `libobs/cmake/os-windows.cmake` links `OBS::w32-pthreads`;
- that target is defined only by `deps/w32-pthreads/CMakeLists.txt`, reached
only through `deps/CMakeLists.txt`;
- `deps/` is added only by the **legacy** branch of the top-level
`CMakeLists.txt`. The modern branch — the one `-DOBS_CMAKE_VERSION=3.0.0`
selects — adds `libobs`, `libobs-d3d11`, `libobs-winrt`, `libobs-opengl`,
`plugins`, `test/test-input` and `UI`, never `deps`;
- `libobs/CMakeLists.txt` adds only `deps/libcaption` and `deps/uthash`;
- `plugins/CMakeLists.txt` returns immediately under `ENABLE_PLUGINS=OFF`, and
does not add `deps/` even when enabled.
macOS is unaffected because its libobs does not link w32-pthreads. At 30.0.2
the modern path was the *default* only on macOS
(`if(CMAKE_HOST_SYSTEM_NAME MATCHES "(Darwin)" OR OBS_CMAKE_VERSION ...)`),
which is consistent with the Windows side of it being under-exercised
upstream.
Deliberately **not** "fixed" by bumping the pin: 30.0.2, 30.1.2, 30.2.3,
31.0.3 and 31.1.1 were all checked, and every one still links
`OBS::w32-pthreads` from `libobs/cmake/os-windows.cmake` while none of them
add `deps/w32-pthreads` from `libobs/CMakeLists.txt`. A version bump is
therefore not obviously the answer and needs checking rather than assuming.
Option 1 (build `obs-frontend-api`, matching obs-plugintemplate's own CI, and
accept the Qt dependency on Windows only) was investigated and **rejected**:
`UI/obs-frontend-api/CMakeLists.txt` only links `OBS::libobs`, nothing else —
it does not itself pull in `deps/w32-pthreads`. What actually satisfies the
target upstream is that `UI/CMakeLists.txt` returns early when
`ENABLE_UI=OFF`, *before* reaching `include(cmake/os-windows.cmake)` — the
file that (via its own `if(NOT TARGET OBS::w32-pthreads)` guard) adds
`deps/w32-pthreads`. Upstream's CI never sets `ENABLE_UI=OFF`, so that
add-as-a-side-effect-of-Qt always happens for them. Building
`obs-frontend-api` instead of `libobs` would not change any of that; the only
way to get the same side effect is to stop passing `-DENABLE_UI:BOOL=OFF`,
which is exactly the ~100 MB Qt6 download this bootstrap was trimmed to avoid
(see the Windows `buildspec.cmake` comment) and buys this plugin nothing,
since its properties UI is plain `obs_properties_*`.
Went with Option 2 instead: `cmake/common/buildspec_common.cmake` now carries
`_patch_obs_studio_w32_pthreads()`, called for `OS_WINDOWS` right before
`_setup_obs_studio()`. It patches the freshly-extracted
`libobs/CMakeLists.txt` to add the one missing subdirectory itself, using the
exact same `if(NOT TARGET OBS::w32-pthreads)` guard
`UI/cmake/os-windows.cmake` already relies on upstream:
```cmake
if(OS_WINDOWS)
if(NOT TARGET OBS::w32-pthreads)
add_subdirectory("${CMAKE_SOURCE_DIR}/deps/w32-pthreads" "${CMAKE_BINARY_DIR}/deps/w32-pthreads")
endif()
include(cmake/os-windows.cmake)
...
```
It is idempotent (checks for `deps/w32-pthreads` already present in the file
before patching, so re-running against a previously-patched extraction is a
no-op) and fails loudly with `FATAL_ERROR` if the anchor text it expects to
find is not there, rather than silently doing nothing on a future OBS version
whose `libobs/CMakeLists.txt` has changed shape. Verified locally (this is a
Linux sandbox, so only the CMake string-patching logic itself could be
checked, not a real Windows configure/build): ran the same `string(FIND)`
/`string(REPLACE)` sequence against the real `libobs/CMakeLists.txt` fetched
from the obs-studio 30.0.2 tag, confirmed it produces the intended
`if(OS_WINDOWS) / if(NOT TARGET ...) / add_subdirectory(...) / endif() /
include(...)` block, and confirmed a second run against the already-patched
file is a no-op. Whether this actually gets libobs through CMake generate and
building on a real Windows runner is the thing the next CI run needs to
prove — option 3 (dropping the from-source libobs on Windows for a prebuilt
SDK) remains the fallback if it does not.
Two bugs of its own were found; the first is now proven fixed by `edb0c02`
getting past it, the second is still unproven:
1. Upstream passes `-A x64,version=<Windows SDK>` to the OBS sub-configure,
and with a current CMake that `,version=` suffix reappears verbatim in the
@@ -328,13 +495,137 @@ Two bugs of its own were found and (believed, not yet proven) fixed:
Unable to download .../windows-deps-2023-11-03-x64,version=10.0.26100.0.zip
```
Plain `-A x64` now.
2. The Windows CI steps were originally written in bash (via
`shell: bash`), which is a poor fit for a `windows-latest` runner's
default toolchain expectations; they were rewritten in PowerShell.
Plain `-A x64` now. **Confirmed fixed:** the `edb0c02` run got past this
and downloaded `windows-deps-2023-11-03-x64.zip` correctly.
2. The Windows CI steps were originally written in bash (via `shell: bash`)
and were rewritten in PowerShell. **The bash version is now proven broken**
by the `58f4832` run: on `winvm-builder`, `bash` resolves to WSL, and WSL
refuses to run under the service account the runner uses —
Until a Windows run completes green with both fixes in place, Windows should
be treated as unverified beyond "the core library and the WinHTTP backend
compile and their tests pass", which earlier (failing-job) runs did show
before failing later in the job. Expect further iterations there of the same
kind the macOS bootstrap needed.
```
Running WSL as local system is not supported.
Error code: Bash/WSL_E_LOCAL_SYSTEM_NOT_SUPPORTED
##[error]Process completed with exit code 1.
```
so the step died on the shell without ever invoking cmake. The PowerShell
rewrite is therefore necessary, but **still unproven in the other
direction** — no completed run has yet included it. Note this also means
`58f4832`'s failure says nothing about the `w32-pthreads` blocker; that
result comes from `edb0c02` alone.
(Superseded by the next subsection: the `w32-pthreads` blocker described above
*was* the next thing solved, and the `add_subdirectory` patch and the
PowerShell rewrite are both now proven by a completed green run. What stands
from this subsection is its reasoning — why `ENABLE_UI=OFF` exposes the gap,
and why re-enabling Qt or bumping the OBS pin is not the answer.)
### After the w32-pthreads target: the w32-pthreads *package*
The `add_subdirectory(deps/w32-pthreads)` patch above did its job — the
`edb0c02`-era generate error is gone, obs-studio 30.0.2 configures, builds
`w32-pthreads.dll` and `obs.dll`, and installs. Two further Windows-only
blockers then surfaced behind it, both the same underlying upstream mismatch
and neither one a defect in this repo:
1. **`find_package(libobs)` could not locate the package it had just
installed.** obs-studio's `cmake/windows/defaults.cmake` sets
`OBS_CMAKE_DESTINATION=cmake`, and `target_export()` installs each
package to `<prefix>/${OBS_CMAKE_DESTINATION}/<target>/` — i.e.
`.deps/cmake/libobs/`. That is not one of CMake's Config-mode search
suffixes (`<prefix>/cmake/` is, but only for a config file sitting
*directly* in it; `<prefix>/<name>*/cmake/` is, but the `<name>`
directory has to come first). Fixed in `79de5e8f` by setting
`libobs_DIR` explicitly; the long comment above that block in
`CMakeLists.txt` has the full reasoning.
2. **…and then `libobsConfig.cmake` could not locate `w32-pthreads` for
exactly the same reason.** `libobs/cmake/os-windows.cmake` links
`PUBLIC OBS::w32-pthreads`, so upstream's `libobsConfig.cmake.in` carries
a hard `find_dependency(w32-pthreads REQUIRED)` under `if(MSVC)`. Once
fix 1 finally got that config file loaded, the dependency lookup inside
it failed and killed the configure:
```
By not providing "Findw32-pthreads.cmake" in CMAKE_MODULE_PATH this project
has asked CMake to find a package configuration file provided by
"w32-pthreads", but CMake did not find one.
.deps/cmake/libobs/libobsConfig.cmake:30 (find_dependency)
```
**This is not a missing export**, which was the first hypothesis and is
worth recording as wrong: `deps/w32-pthreads/CMakeLists.txt` ends with
`target_export(w32-pthreads)`, the same helper `libobs` itself uses, so
it does emit `install(TARGETS … EXPORT w32-pthreadsTargets)`,
`install(EXPORT … NAMESPACE OBS::)` and a generated
`w32-pthreadsConfig.cmake`, all `COMPONENT Development`. Two independent
arguments say those rules ran: CMake hard-errors at generate time if an
exported target links a target that is in no export set at all (and OBS's
generate step succeeded), and this repo's patch adds `deps/w32-pthreads`
from `libobs/CMakeLists.txt`, making it part of the `libobs` subtree —
which installs *before* the tolerated `UI/obs-frontend-api` install error
aborts the rest. The package really is at `.deps/cmake/w32-pthreads/`;
`find_package` was simply never going to look there.
So the fix is the same one-liner as for `libobs`, and it is literally
what CMake's own error message suggests: set `w32-pthreads_DIR` before
the `find_package(libobs)` call that transitively triggers the
`find_dependency`. Both `_DIR` blocks now sit next to each other in
`CMakeLists.txt`.
Behind that sits `cmake/windows/find-fallback/Findw32-pthreads.cmake`,
used only if that export is genuinely absent from `.deps/`. It rebuilds
`OBS::w32-pthreads` by hand from the bootstrap's own artifacts. It
deliberately does *not* live in `cmake/windows/`, which
`cmake/common/osconfig.cmake` already puts on `CMAKE_MODULE_PATH` for
every Windows configure — a find module there would shadow OBS's real
exported package on every build, since `find_package` tries MODULE mode
before CONFIG mode. `CMakeLists.txt` appends the `find-fallback/`
directory to `CMAKE_MODULE_PATH` only after it has established the real
export is missing, and logs what *is* under `.deps/cmake/` when it does,
so a future failure of this shape is answered by the CI log rather than
by another run.
Note this package is load-bearing for more than the dependency check:
`libobs/util/threading.h` does `#include <pthread.h>`, and on Windows
that header only exists because `target_export(w32-pthreads)` installs
`pthread.h`/`sched.h` as `PUBLIC_HEADER` into `.deps/include/`.
Verified before pushing, on Linux, since this is a Linux sandbox: a
reconstruction of the exact failure — a stub `libobsConfig.cmake` containing
`find_dependency(w32-pthreads REQUIRED)`, reached through `libobs_DIR`, with
the package installed at `.deps/cmake/w32-pthreads/` — reproduces the CI
error without the `w32-pthreads_DIR` block and passes with it; and the
fallback find module was exercised separately by deleting that package, with
`find_package` resolving through MODULE mode to it instead. A full Linux
configure of this repo is unchanged (both blocks are inside `if(OS_WINDOWS)`,
and the upstream `find_dependency` is inside `if(MSVC)`, so macOS and Linux
are pure no-ops).
**Confirmed on the real runner.** The Windows job for `f27b1c0` completed
green, and its log body — not just its status — shows the whole chain:
```
-- w32-pthreads_DIR not set; libobsConfig.cmake's find_dependency(w32-pthreads
REQUIRED) hits the same OBS_CMAKE_DESTINATION=cmake search-suffix problem as
libobs itself, so pointing it directly at the from-source install:
...\.deps\cmake\w32-pthreads
-- libobs found (...\.deps\cmake\libobs) -- building OBS adapter module
streamer-tools-camera.vcxproj -> ...\build\obs-adapter\Release\streamer-tools-camera.dll
100% tests passed out of 6
Directory: ...\build\package\bin\64bit
-a---- 3078656 livekit.dll
-a---- 25008640 livekit_ffi.dll
-a---- 136192 streamer-tools-camera.dll
```
Note which branch that log took: the `w32-pthreads_DIR` message means the
package config really was sitting at `.deps/cmake/w32-pthreads/` all along and
the fallback find module was never loaded. The export was never missing — only
unfindable. Linux and macOS were green in the same run, confirming the no-op.
The one piece of noise left in that log is the tolerated
`UI/obs-frontend-api/cmake_install.cmake` error, which now repeats once per
CMake re-configure because Visual Studio's `ZERO_CHECK` target re-runs the
bootstrap during the build. It is cosmetic and pre-dates this change, but it
makes the Windows log harder to read than it should be.
+69
View File
@@ -24,6 +24,7 @@
# build tree, so it does not trip over the install rules of targets that
# were deliberately never built.
# 7. _resolve_versioned_macos_sdk exists at all -- see its own comment.
# 8. _patch_obs_studio_w32_pthreads exists at all -- see its own comment.
#
include_guard(GLOBAL)
@@ -147,6 +148,70 @@ function(_resolve_versioned_macos_sdk out_path)
endif()
endfunction()
# _patch_obs_studio_w32_pthreads: Windows-only. Confirmed at every obs-studio
# tag from 30.0.2 through 31.1.1 (checked directly against
# libobs/cmake/os-windows.cmake and the top-level CMakeLists.txt at each tag):
# libobs/cmake/os-windows.cmake unconditionally links `OBS::w32-pthreads`, but
# that target is defined only by deps/w32-pthreads/CMakeLists.txt, and nothing
# in the OBS_CMAKE_VERSION>=3.0.0 ("modern") top-level CMakeLists.txt branch --
# the one -DOBS_CMAKE_VERSION=3.0.0 selects -- ever adds deps/w32-pthreads.
# libobs/CMakeLists.txt itself only adds deps/libcaption and deps/uthash.
#
# Upstream obs-studio's own CI never hits this because it builds with
# ENABLE_UI left ON: UI/cmake/os-windows.cmake happens to add
# deps/w32-pthreads too (guarded by `if(NOT TARGET OBS::w32-pthreads)`), which
# satisfies libobs's link by the time CMake generates -- purely as a side
# effect of Qt still being in the build, not because anything wires
# w32-pthreads to libobs on purpose. This bootstrap deliberately builds with
# ENABLE_UI:BOOL=OFF (see the file header) specifically to avoid pulling in
# Qt6, so that side effect never happens here, and the gap is exposed.
#
# Rather than re-enable ENABLE_UI (and pay for a ~100 MB Qt6 download this
# plugin's plain obs_properties_* UI does not need) or vendor a full copy of
# deps/CMakeLists.txt, patch libobs/CMakeLists.txt to add the one missing
# subdirectory itself, using the exact same existence guard
# UI/cmake/os-windows.cmake already relies on. Idempotent: running this again
# against an already-patched tree is a no-op (the search text no longer
# matches), so it is safe to call on every configure regardless of whether
# .deps/ was freshly extracted or reused from a previous run.
function(_patch_obs_studio_w32_pthreads)
set(_cmakelists "${dependencies_dir}/${_obs_destination}/libobs/CMakeLists.txt")
if(NOT EXISTS "${_cmakelists}")
message(FATAL_ERROR "Cannot apply the w32-pthreads workaround: ${_cmakelists} does not exist.")
endif()
file(READ "${_cmakelists}" _contents)
string(FIND "${_contents}" "deps/w32-pthreads" _already_patched)
if(NOT _already_patched EQUAL -1)
message(STATUS "libobs/CMakeLists.txt already carries the w32-pthreads workaround - skipping")
return()
endif()
set(_needle "if(OS_WINDOWS)\n include(cmake/os-windows.cmake)")
set(_replacement
"if(OS_WINDOWS)\n if(NOT TARGET OBS::w32-pthreads)\n add_subdirectory(\"\${CMAKE_SOURCE_DIR}/deps/w32-pthreads\" \"\${CMAKE_BINARY_DIR}/deps/w32-pthreads\")\n endif()\n include(cmake/os-windows.cmake)"
)
string(FIND "${_contents}" "${_needle}" _pos)
if(_pos EQUAL -1)
message(
FATAL_ERROR
"Could not find the expected 'if(OS_WINDOWS) / include(cmake/os-windows.cmake)' block in "
"${_cmakelists} to apply the w32-pthreads workaround. obs-studio's libobs/CMakeLists.txt "
"layout may have changed since this was written against 30.0.2."
)
endif()
string(REPLACE "${_needle}" "${_replacement}" _patched "${_contents}")
file(WRITE "${_cmakelists}" "${_patched}")
message(
STATUS
"Patched ${_cmakelists}: added deps/w32-pthreads so OBS::w32-pthreads exists "
"(obs-studio's modern Windows CMake path never defines it with ENABLE_UI=OFF)."
)
endfunction()
# _setup_obs_studio: Create obs-studio build project, then build libobs and obs-frontend-api
function(_setup_obs_studio)
if(NOT libobs_DIR)
@@ -366,5 +431,9 @@ function(_check_dependencies)
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} CACHE PATH "CMake prefix search path" FORCE)
if(OS_WINDOWS)
_patch_obs_studio_w32_pthreads()
endif()
_setup_obs_studio()
endfunction()
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>@STPLUGIN_BUNDLE_EXECUTABLE@</string>
<key>CFBundleIdentifier</key>
<string>@STPLUGIN_BUNDLE_ID@</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>@STPLUGIN_BUNDLE_NAME@</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>@STPLUGIN_BUNDLE_VERSION@</string>
<key>CFBundleVersion</key>
<string>@STPLUGIN_BUNDLE_VERSION@</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>LSMinimumSystemVersion</key>
<string>@STPLUGIN_BUNDLE_MIN_OS@</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright (c) @STPLUGIN_BUNDLE_YEAR@ @STPLUGIN_BUNDLE_AUTHOR@</string>
</dict>
</plist>
+46
View File
@@ -0,0 +1,46 @@
#!/bin/sh
# cmake/macos/fixup-libobs-rpath.sh <plugin-binary>
#
# The from-source libobs build this project's macOS bootstrap runs
# (cmake/common/buildspec_common.cmake) records its own install name (its
# LC_ID_DYLIB) as a relative path -- "libobs/libobs.framework/Versions/A/libobs"
# -- rather than an @rpath reference. That is a property of that from-source
# OBS build itself, not something this project's own link step controls: the
# LC_LOAD_DYLIB entry our plugin binary gets for a dependency is copied
# straight from that dependency's own LC_ID_DYLIB by the linker. A relative
# path is not resolvable at runtime from inside an OBS.app plugin bundle --
# see the "macOS packaging gap" this script fixes, documented in README.md.
#
# Rewrite that one dependency entry to @rpath/libobs.framework/Versions/A/libobs.
# stplugin_macos_finalize_bundle() (cmake/macos/helpers.cmake) gives the
# plugin binary an LC_RPATH of @executable_path/../Frameworks, which resolves
# @rpath against the *host* OBS.app's own Contents/Frameworks/libobs.framework
# at runtime (@executable_path is always relative to the process's main
# executable -- OBS.app/Contents/MacOS/obs -- not to this dlopen'd bundle, no
# matter how deeply the .plugin is nested under
# ~/Library/Application Support/obs-studio/plugins/<name>/bin/).
#
# The LiveKit runtime dylibs this same helper copies into the bundle's own
# Contents/Frameworks are NOT touched here: client-sdk-cpp's own release
# build already records their install names as @rpath references (confirmed
# by otool -L on prior CI runs -- see README's "Where the macOS bootstrap
# actually got to"), so the @loader_path/../Frameworks half of the same
# LC_RPATH already resolves them with no rewrite needed.
set -eu
binary="$1"
if [ ! -f "$binary" ]; then
echo "fixup-libobs-rpath: no such file: $binary" >&2
exit 1
fi
old_ref=$(otool -L "$binary" \
| awk '/libobs\.framework\/Versions\/A\/libobs/ && $1 !~ /^@rpath/ {print $1; exit}')
if [ -n "${old_ref:-}" ]; then
echo "fixup-libobs-rpath: rewriting '$old_ref' -> '@rpath/libobs.framework/Versions/A/libobs' in $binary"
install_name_tool -change "$old_ref" "@rpath/libobs.framework/Versions/A/libobs" "$binary"
else
echo "fixup-libobs-rpath: libobs dependency in $binary is already @rpath-relative (or was not found via otool -L); nothing to rewrite"
fi
+181
View File
@@ -0,0 +1,181 @@
# cmake/macos/helpers.cmake
#
# macOS ".plugin" bundle packaging for the OBS adapter module -- closes the
# "macOS packaging gap" documented in README.md.
#
# Adapted from obsproject/obs-plugintemplate's cmake/macos/helpers.cmake
# (fetched 2026-09-07, master branch) -- deliberately NOT vendored as-is.
# Upstream's set_target_properties_plugin() builds the bundle almost
# entirely through XCODE_ATTRIBUTE_* target properties
# (XCODE_ATTRIBUTE_GENERATE_INFOPLIST_FILE for Info.plist, an Xcode "Embed
# Frameworks" build phase for bundling its own dependencies, Xcode-driven
# codesigning), and upstream's own CI drives that with `xcodebuild -project
# <name>.xcodeproj` (.github/scripts/build-macos). None of that is available
# here: XCODE_ATTRIBUTE_* properties are silent no-ops under any generator
# but Xcode, and this project cannot use the Xcode generator on the `home-mac`
# CI runner -- see cmake/common/buildspec_common.cmake's CI-iteration-1
# comment, which already established empirically that this runner has only
# the Command Line Tools installed, not Xcode.app, so there is no xcodebuild
# to drive an .xcodeproj with. This project builds macOS with Ninja
# end-to-end (both the from-source libobs bootstrap and this project's own
# configure -- see the CI workflow), so this reimplements the same
# *outcome* -- a real <name>.plugin bundle with Contents/MacOS,
# Contents/Resources and a working Info.plist -- using CMake's own
# generator-agnostic bundle support (the BUNDLE / BUNDLE_EXTENSION /
# MACOSX_BUNDLE_INFO_PLIST target properties, which the Makefile/Ninja
# generators implement natively, not just Xcode's), and does the two things
# upstream gets "for free" from Xcode's embed/codesign build phases by hand:
#
# 0. copies obs-adapter/data/** into Contents/Resources/**, since OBS's own
# macOS module search (UI/window-basic-main.cpp AddExtraModulePaths())
# passes "<plugin>.plugin/Contents/Resources" as a module's *data path*
# -- not a sibling "data/" directory next to "bin/", which is the
# Linux/Windows shape and does NOT apply on macOS. Concretely: this
# project's OBS_MODULE_USE_DEFAULT_LOCALE(PLUGIN_NAME, "en-US") loads
# "<data_path>/locale/en-US.ini", so on macOS that file must land at
# Contents/Resources/locale/en-US.ini, not a top-level data/locale/.
# Mirrors upstream's own target_install_resources() -- also not
# Xcode-specific, since MACOSX_PACKAGE_LOCATION is another generator-
# agnostic bundle-content source-file property.
# 1. copies the LiveKit runtime dylibs into Contents/Frameworks (the
# conventional location for a plugin bundle's own bundled dependencies)
# 2. rewrites the libobs dependency from the relative path the from-source
# libobs build records as its own install name
# (libobs/libobs.framework/Versions/A/libobs) to
# @rpath/libobs.framework/Versions/A/libobs -- see
# cmake/macos/fixup-libobs-rpath.sh for why that rewrite is needed at
# all and cannot be done by our own link step -- and gives the plugin
# binary the LC_RPATH entries that make @rpath resolve for both
# dependencies: @loader_path/../Frameworks (this bundle's own
# Contents/Frameworks, for LiveKit) and @executable_path/../Frameworks
# (OBS.app/Contents/Frameworks, for libobs.framework itself).
#
# Bundle metadata (name/version/bundle id/author) is sourced from
# buildspec.json, the same file the OBS-SDK bootstrap (buildspec_common.cmake)
# already reads -- see its "platformConfig.macos.bundleId" / "name" /
# "version" / "author" fields.
include_guard(GLOBAL)
# stplugin_macos_finalize_bundle: turn a MODULE library target into a real
# OBS.app-loadable <name>.plugin bundle. No-op on non-Apple hosts so callers
# do not need to guard every call site with if(APPLE).
function(stplugin_macos_finalize_bundle target)
if(NOT APPLE)
return()
endif()
if(NOT DEFINED LIVEKIT_SDK_RUNTIME_LIBS)
message(FATAL_ERROR
"stplugin_macos_finalize_bundle(${target}): LIVEKIT_SDK_RUNTIME_LIBS is "
"not set. include(LiveKitSDK) + livekit_sdk_setup() must run before "
"this is called (see top-level CMakeLists.txt).")
endif()
# --- bundle metadata, from buildspec.json ---------------------------------
file(READ "${CMAKE_SOURCE_DIR}/buildspec.json" _stplugin_buildspec)
string(JSON _stplugin_bundle_name GET "${_stplugin_buildspec}" name)
string(JSON _stplugin_bundle_version GET "${_stplugin_buildspec}" version)
string(JSON _stplugin_bundle_author GET "${_stplugin_buildspec}" author)
string(JSON _stplugin_bundle_id GET "${_stplugin_buildspec}" platformConfig macos bundleId)
string(TIMESTAMP STPLUGIN_BUNDLE_YEAR "%Y" UTC)
set(STPLUGIN_BUNDLE_EXECUTABLE "${target}")
set(STPLUGIN_BUNDLE_NAME "${_stplugin_bundle_name}")
set(STPLUGIN_BUNDLE_VERSION "${_stplugin_bundle_version}")
set(STPLUGIN_BUNDLE_AUTHOR "${_stplugin_bundle_author}")
set(STPLUGIN_BUNDLE_ID "${_stplugin_bundle_id}")
if(CMAKE_OSX_DEPLOYMENT_TARGET)
set(STPLUGIN_BUNDLE_MIN_OS "${CMAKE_OSX_DEPLOYMENT_TARGET}")
else()
set(STPLUGIN_BUNDLE_MIN_OS "13.0")
endif()
set(_stplugin_plist_out "${CMAKE_CURRENT_BINARY_DIR}/${target}-Info.plist")
configure_file(
"${CMAKE_SOURCE_DIR}/cmake/macos/Info.plist.in"
"${_stplugin_plist_out}"
@ONLY
)
# --- Contents/Resources: the module's data path on macOS ----------------
# See the "0." bullet in the file header. CMAKE_CURRENT_SOURCE_DIR here is
# obs-adapter (this function is called from obs-adapter/CMakeLists.txt, and
# include() does not change directory scope), so this globs
# obs-adapter/data/** the same way the flat-layout staging step elsewhere
# in that file does.
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/data")
file(GLOB_RECURSE _stplugin_data_files "${CMAKE_CURRENT_SOURCE_DIR}/data/*")
foreach(_stplugin_data_file IN LISTS _stplugin_data_files)
cmake_path(
RELATIVE_PATH _stplugin_data_file
BASE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data"
OUTPUT_VARIABLE _stplugin_data_relpath
)
cmake_path(GET _stplugin_data_relpath PARENT_PATH _stplugin_data_reldir)
target_sources(${target} PRIVATE "${_stplugin_data_file}")
if(_stplugin_data_reldir)
set_property(SOURCE "${_stplugin_data_file}" PROPERTY
MACOSX_PACKAGE_LOCATION "Resources/${_stplugin_data_reldir}")
else()
set_property(SOURCE "${_stplugin_data_file}" PROPERTY
MACOSX_PACKAGE_LOCATION "Resources")
endif()
endforeach()
endif()
# --- bundle shape -----------------------------------------------------
# BUNDLE + BUNDLE_EXTENSION is CMake's own generator-agnostic mechanism
# (not Xcode-specific) for turning a MODULE library into a CFBundle;
# MACOSX_BUNDLE_INFO_PLIST tells the same mechanism which Info.plist to
# copy into Contents/. This is the traditional, non-Xcode-only path CMake
# has supported since long before XCODE_ATTRIBUTE_GENERATE_INFOPLIST_FILE
# existed, and is exactly what makes this work under Ninja.
set_target_properties(${target} PROPERTIES
BUNDLE TRUE
BUNDLE_EXTENSION "plugin"
MACOSX_BUNDLE_INFO_PLIST "${_stplugin_plist_out}"
)
# --- rpaths -------------------------------------------------------------
# BUILD_WITH_INSTALL_RPATH ON (matching the non-Apple UNIX branch in
# obs-adapter/CMakeLists.txt) means these two rpaths get embedded as
# LC_RPATH load commands by the linker itself at build time -- the
# artifact that ships is a straight copy of what gets built here, so the
# build-tree RPATH must never leak in.
# @loader_path/../Frameworks -> this bundle's own Contents/Frameworks
# (LiveKit dylibs, staged below)
# @executable_path/../Frameworks -> OBS.app/Contents/Frameworks
# (libobs.framework itself).
# @executable_path is always relative
# to the process's main executable
# (OBS.app/Contents/MacOS/obs), not to
# this dlopen'd bundle, regardless of
# how deeply the .plugin is nested
# under the user's plugin directory.
set_target_properties(${target} PROPERTIES
BUILD_WITH_INSTALL_RPATH ON
INSTALL_RPATH "@loader_path/../Frameworks;@executable_path/../Frameworks"
)
# --- bundled runtime deps + the libobs @rpath fixup --------------------
# $<TARGET_BUNDLE_DIR:target> is .../<target>.plugin for a BUNDLE target
# (available since CMake 3.0, unlike TARGET_BUNDLE_CONTENT_DIR which needs
# CMake >= 3.20 -- this project's cmake_minimum_required(VERSION 3.19)
# floor in the top-level CMakeLists.txt), so appending Contents/Frameworks
# avoids a ".." in the path TARGET_FILE_DIR would otherwise need.
set(_stplugin_frameworks_dir "$<TARGET_BUNDLE_DIR:${target}>/Contents/Frameworks")
add_custom_command(TARGET ${target} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E make_directory "${_stplugin_frameworks_dir}"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different ${LIVEKIT_SDK_RUNTIME_LIBS} "${_stplugin_frameworks_dir}/"
COMMENT "Copying LiveKit runtime dylibs into ${target}.plugin/Contents/Frameworks"
VERBATIM
)
add_custom_command(TARGET ${target} POST_BUILD
COMMAND sh "${CMAKE_SOURCE_DIR}/cmake/macos/fixup-libobs-rpath.sh" "$<TARGET_FILE:${target}>"
COMMENT "Rewriting the libobs dependency of ${target} to @rpath"
VERBATIM
)
endfunction()
@@ -0,0 +1,118 @@
# Findw32-pthreads.cmake -- last-resort fallback, Windows only.
#
# WHY THIS EXISTS
#
# obs-studio's libobs links `PUBLIC OBS::w32-pthreads` on Windows
# (libobs/cmake/os-windows.cmake), so the libobsConfig.cmake it installs
# carries an unconditional, REQUIRED dependency on a findable `w32-pthreads`
# CMake package:
#
# if(MSVC)
# find_dependency(w32-pthreads REQUIRED)
# endif()
#
# obs-studio *does* export that package -- deps/w32-pthreads/CMakeLists.txt
# ends in `target_export(w32-pthreads)` -- but it exports it to
# <prefix>/cmake/w32-pthreads/, the same OBS_CMAKE_DESTINATION shape that
# find_package's Config-mode search suffixes never look at. The top-level
# CMakeLists.txt therefore points `w32-pthreads_DIR` straight at it, exactly
# as it already does for `libobs_DIR`, and that is the path this build is
# expected to take.
#
# This module is only reached when that export is genuinely absent from
# .deps/ -- for instance if a future obs-studio changes where or whether
# deps/w32-pthreads installs its package, or if the tolerated
# UI/obs-frontend-api install error in
# cmake/common/buildspec_common.cmake::_setup_obs_studio ever starts aborting
# the install *before* libobs's own subtree instead of after it. In that case
# the alternative is a hard configure failure inside libobsConfig.cmake with
# no plugin module built at all, so reconstructing the imported target by
# hand from artifacts the bootstrap definitely produced is strictly better.
#
# It deliberately lives in cmake/windows/find-fallback/ and NOT in
# cmake/windows/, which cmake/common/osconfig.cmake puts on CMAKE_MODULE_PATH
# for every Windows configure. A Findw32-pthreads.cmake on the default module
# path would win over OBS's own exported package on every single build
# (find_package tries MODULE mode before CONFIG mode), permanently replacing
# upstream's real export metadata with this approximation. The top-level
# CMakeLists.txt appends this directory to CMAKE_MODULE_PATH only when it has
# already established that the real export is missing.
#
# Nothing outside Windows ever loads this: the find_dependency above is
# inside `if(MSVC)`, and the CMAKE_MODULE_PATH append that exposes this file
# is inside `if(OS_WINDOWS ...)`.
if(TARGET OBS::w32-pthreads)
set(w32-pthreads_FOUND TRUE)
return()
endif()
# Resolve paths from this file's own location rather than
# CMAKE_CURRENT_SOURCE_DIR: a find module runs in whatever scope called
# find_package, and here that call comes from inside libobsConfig.cmake.
# <repo>/cmake/windows/find-fallback/ -> <repo>
get_filename_component(_w32pt_repo_root "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE)
set(_w32pt_deps "${_w32pt_repo_root}/.deps")
# Search order, both from obs-studio's own Windows layout:
# 1. the bootstrap's install prefix -- obs-studio/cmake/windows/defaults.cmake
# sets OBS_LIBRARY_DESTINATION=lib (ARCHIVE, i.e. the import library),
# OBS_EXECUTABLE_DESTINATION=bin/64bit (RUNTIME, i.e. the DLL) and
# OBS_INCLUDE_DESTINATION=include (where target_export installs
# w32-pthreads' PUBLIC_HEADER pthread.h/sched.h);
# 2. the obs-studio build tree the bootstrap just built in, in case the
# install step is what failed rather than the export.
file(GLOB _w32pt_build_trees "${_w32pt_deps}/obs-studio-*/build_*/deps/w32-pthreads/Release")
file(GLOB _w32pt_source_trees "${_w32pt_deps}/obs-studio-*/deps/w32-pthreads")
find_library(
w32-pthreads_IMPLIB
NAMES w32-pthreads
PATHS "${_w32pt_deps}/lib" ${_w32pt_build_trees}
NO_DEFAULT_PATH
)
find_file(
w32-pthreads_RUNTIME_LIBRARY
NAMES w32-pthreads.dll
PATHS "${_w32pt_deps}/bin/64bit" ${_w32pt_build_trees}
NO_DEFAULT_PATH
)
find_path(
w32-pthreads_INCLUDE_DIR
NAMES pthread.h
PATHS "${_w32pt_deps}/include" ${_w32pt_source_trees}
NO_DEFAULT_PATH
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
w32-pthreads
REQUIRED_VARS w32-pthreads_IMPLIB w32-pthreads_INCLUDE_DIR
REASON_FAILURE_MESSAGE
"The OBS SDK bootstrap neither exported a w32-pthreads CMake package under ${_w32pt_deps}/cmake/ nor left a w32-pthreads import library anywhere under ${_w32pt_deps}."
)
if(w32-pthreads_FOUND)
# SHARED, matching deps/w32-pthreads/CMakeLists.txt's own
# `add_library(w32-pthreads SHARED ...)`. IMPORTED_LOCATION is the DLL and
# IMPORTED_IMPLIB the .lib, which is what an imported SHARED library means
# on Windows; if only the .lib was found, declare it UNKNOWN instead so
# CMake does not demand a DLL it will never be handed.
if(w32-pthreads_RUNTIME_LIBRARY)
add_library(OBS::w32-pthreads SHARED IMPORTED)
set_target_properties(
OBS::w32-pthreads
PROPERTIES IMPORTED_LOCATION "${w32-pthreads_RUNTIME_LIBRARY}" IMPORTED_IMPLIB "${w32-pthreads_IMPLIB}"
)
else()
add_library(OBS::w32-pthreads UNKNOWN IMPORTED)
set_target_properties(OBS::w32-pthreads PROPERTIES IMPORTED_LOCATION "${w32-pthreads_IMPLIB}")
endif()
set_target_properties(
OBS::w32-pthreads
PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${w32-pthreads_INCLUDE_DIR}"
)
message(STATUS "Findw32-pthreads (fallback): OBS::w32-pthreads -> ${w32-pthreads_IMPLIB}")
endif()
mark_as_advanced(w32-pthreads_IMPLIB w32-pthreads_RUNTIME_LIBRARY w32-pthreads_INCLUDE_DIR)
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - streamer-tools API client
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#pragma once
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - core library
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#pragma once
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - core library
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#pragma once
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - HTTP client interface
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#pragma once
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - minimal JSON reader
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#pragma once
+11 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - LiveKit session wrapper
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#pragma once
@@ -50,6 +43,13 @@ struct SessionConfig {
bool subscribe_audio = true;
/// False for an audio-only source (the soundboard, say): the wanted
/// video track is never attached (no AttachVideo command posted), and
/// its publication is explicitly disabled server-side (RemoteTrack-
/// Publication::setEnabled(false)) so the SFU stops sending it at all --
/// not just "decoded and discarded here", genuinely not delivered.
bool subscribe_video = true;
/// How long connect() waits for the room to come up before giving up.
int connect_timeout_ms = 15000;
};
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - session types and pure session logic
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#pragma once
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - streamer-tools API client
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#include "stplugin/api_client.h"
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - core library
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#include "stplugin/core.h"
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - HTTP helpers shared by all backends
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#include "stplugin/http.h"
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - libcurl HTTP backend (Linux/macOS)
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#include "stplugin/http.h"
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - WinHTTP backend (Windows)
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
// WinHTTP rather than libcurl on Windows: it ships with the OS, does TLS
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - minimal JSON reader
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#include "stplugin/json.h"
+52 -13
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - LiveKit session wrapper
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#include "stplugin/session.h"
@@ -225,6 +218,52 @@ struct LiveKitSession::Impl : public livekit::RoomDelegate {
queue_cv.notify_one();
}
// Handles the wanted video track once matched, shared by onTrackSubscribed
// (a fresh subscription) and attachExistingTracks (one already up when
// this session started watching). Two responsibilities that only make
// sense together, both keyed off the SAME publication:
//
// - subscribe_video: an audio-only source (the soundboard) never wants
// this video at all. Rather than attach it and let the OBS adapter
// discard every decoded frame, disable the publication itself
// (RemoteTrackPublication::setEnabled(false)) so the SFU stops
// sending it -- real bandwidth saved, not just wasted decode.
// - Fixed video quality: LiveKit's default subscriber behaviour lets
// the SFU switch simulcast layers per its own adaptive/bandwidth
// logic, which for a source with no rendered-size hint (this is a
// native C++ subscriber, not a sized <video> element) means the
// received resolution can hop between layers -- observed live as OBS
// source geometry visibly changing size mid-show. Pinning to HIGH
// asks the SFU to always send the top layer, which is what a fixed
// OBS source needs regardless of bandwidth (the plugin has no
// picture-in-picture tier to fall back to the way a browser grid
// view would).
void handleWantedVideoTrack(const std::shared_ptr<livekit::Track> &track,
const std::shared_ptr<livekit::RemoteTrackPublication> &publication)
{
if (!config.subscribe_video) {
if (publication) {
try {
publication->setEnabled(false);
} catch (const std::exception &) {
// Best-effort: worst case this track keeps being
// delivered and decoded, wasting bandwidth -- it is
// still never attached to OBS below.
}
}
return;
}
if (publication) {
try {
publication->setVideoQuality(livekit::VideoQuality::HIGH);
} catch (const std::exception &) {
// Best-effort: worst case this track keeps whatever quality
// it already had, which is the pre-existing behaviour.
}
}
post(CommandType::AttachVideo, track);
}
// --- RoomDelegate ------------------------------------------------------
void onTrackSubscribed(livekit::Room &, const livekit::TrackSubscribedEvent &event) override
@@ -237,7 +276,7 @@ struct LiveKitSession::Impl : public livekit::RoomDelegate {
event.publication ? toMediaSource(event.publication->source()) : MediaSource::Unknown;
if (isWantedVideoTrack(config.participant_identity, identity, kind, source))
post(CommandType::AttachVideo, event.track);
handleWantedVideoTrack(event.track, event.publication);
else if (config.subscribe_audio && isWantedAudioTrack(config.participant_identity, identity, kind, source))
post(CommandType::AttachAudio, event.track);
}
@@ -558,7 +597,7 @@ struct LiveKitSession::Impl : public livekit::RoomDelegate {
const MediaKind kind = toMediaKind(track->kind());
const MediaSource source = toMediaSource(publication->source());
if (isWantedVideoTrack(config.participant_identity, identity, kind, source))
post(CommandType::AttachVideo, track);
handleWantedVideoTrack(track, publication);
else if (config.subscribe_audio && isWantedAudioTrack(config.participant_identity, identity, kind, source))
post(CommandType::AttachAudio, track);
}
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - session types and pure session logic
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#include "stplugin/session_types.h"
+22
View File
@@ -7,6 +7,28 @@ function(stplugin_add_test name)
if(WIN32)
# loopback_server.h needs Winsock for the real-backend tests.
target_link_libraries(${name} PRIVATE ws2_32)
# stplugin_core links LiveKit::livekit PUBLICly (see
# ../CMakeLists.txt), so every test executable here is linked
# against livekit.dll / livekit_ffi.dll. Unlike the RPATH/$ORIGIN
# handling obs-adapter/CMakeLists.txt sets up for Linux/macOS,
# Windows has no way to find a DLL relative to the .exe -- it must
# physically sit next to it (or be on PATH) at process start, or
# the loader fails before main() with STATUS_DLL_NOT_FOUND
# (0xc0000135), which is exactly what CTest saw for test_session,
# test_integration_livekit and test_livekit_smoke. Copy the same
# LIVEKIT_SDK_RUNTIME_LIBS list (from cmake/LiveKitSDK.cmake) that
# obs-adapter stages next to the plugin module, next to each test
# binary instead. Applied to every test here, not just the three
# known to actually reference LiveKit symbols today -- harmless for
# the others and avoids re-diagnosing this if a future test starts
# touching stplugin_core's LiveKit-dependent code paths.
add_custom_command(TARGET ${name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${LIVEKIT_SDK_RUNTIME_LIBS} "$<TARGET_FILE_DIR:${name}>"
COMMENT "Copying LiveKit runtime DLLs next to ${name}"
VERBATIM
)
endif()
add_test(NAME ${name} COMMAND ${name})
# Nothing here should ever take a minute; a hang is a failure, not a
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - minimal loopback HTTP server for tests
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#pragma once
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - API client tests
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
// Two layers of coverage:
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - core library tests
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
// NOTE on why this file uses ST_ASSERT and not assert(): CI builds Release,
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - LiveKit end-to-end integration test
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
// The one test that proves the session wrapper actually receives media.
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - JSON reader tests
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#include <string>
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - LiveKit SDK link smoke test
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
// Proves the pinned client-sdk-cpp release is genuinely linked and callable:
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - session wrapper tests
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
// What is and is not covered here, stated plainly because it matters:
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin - minimal test harness
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
#pragma once
+56 -17
View File
@@ -42,18 +42,32 @@ set_target_properties(${STPLUGIN_PROJECT_NAME} PROPERTIES
# straight copy of the built module (see the staging step below), so the
# build-tree RPATH must never be baked in -- it would work on the build
# machine and nowhere else.
if(APPLE)
set_target_properties(${STPLUGIN_PROJECT_NAME} PROPERTIES
BUILD_WITH_INSTALL_RPATH ON
INSTALL_RPATH "@loader_path"
)
elseif(UNIX)
#
# APPLE's rpath (and its whole bundle shape) is handled by
# stplugin_macos_finalize_bundle() below instead -- a bundle needs two
# rpaths (its own Contents/Frameworks *and* the host OBS.app's
# Contents/Frameworks for libobs.framework), not the single flat
# "@loader_path" this used to set back when macOS staged a bare .so. See
# cmake/macos/helpers.cmake.
if(UNIX AND NOT APPLE)
set_target_properties(${STPLUGIN_PROJECT_NAME} PROPERTIES
BUILD_WITH_INSTALL_RPATH ON
INSTALL_RPATH "$ORIGIN"
)
endif()
# --- macOS: turn the flat MODULE library into a real OBS.app-loadable
# <name>.plugin bundle (Contents/MacOS, Contents/Resources, Info.plist,
# Contents/Frameworks for the bundled LiveKit dylibs, and the libobs
# @rpath fixup). Closes the "macOS packaging gap" in README.md. Must run
# before the staging step below, which copies the finished bundle directory
# -- add_custom_command(POST_BUILD) commands attached to the same target run
# in the order they were registered.
if(APPLE)
include(helpers)
stplugin_macos_finalize_bundle(${STPLUGIN_PROJECT_NAME})
endif()
# --- staged, runnable layout ------------------------------------------------
# Everything a human needs to copy into an OBS plugin directory ends up under
# build/package/, with the LiveKit shared libraries and the licence files
@@ -68,16 +82,31 @@ set(STPLUGIN_PACKAGE_DIR "${CMAKE_BINARY_DIR}/package")
# <config>/obs-studio/plugins/<name>/data/
# so staging into bin/64bit makes build/package/ a straight drop-in.
#
# macOS is NOT this shape -- there OBS looks for a
# <name>.plugin/Contents/MacOS bundle -- and this build does not produce one.
# See the macOS packaging gap in README.md; the flat bin/ here is honest
# about being unfinished rather than pretending to be installable.
# macOS is a COMPLETELY different shape, not just "bin without 64bit": the
# same AddExtraModulePaths() passes GetConfigPath(...,
# "obs-studio/plugins/%module%.plugin") as the base, then
# obs_add_module_path(base + "/Contents/MacOS", base + "/Contents/Resources")
# -- i.e. OBS wants the ENTIRE <name>.plugin bundle dropped directly into
# .../obs-studio/plugins/, not nested under a bin/ subdirectory the way
# Linux/Windows are. So build/package/ on macOS holds the bundle itself at
# its top level (build/package/<name>.plugin), not build/package/bin/....
# stplugin_macos_finalize_bundle() (cmake/macos/helpers.cmake) makes
# ${STPLUGIN_PROJECT_NAME} an actual BUNDLE target for APPLE and also copies
# obs-adapter/data/** into Contents/Resources/** -- see that file for why
# Contents/Resources (not a sibling data/) is where OBS looks for this
# module's data on macOS.
if(APPLE)
set(STPLUGIN_PACKAGE_BIN_DIR "${STPLUGIN_PACKAGE_DIR}/bin")
add_custom_command(TARGET ${STPLUGIN_PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "${STPLUGIN_PACKAGE_DIR}"
COMMAND ${CMAKE_COMMAND} -E rm -rf "${STPLUGIN_PACKAGE_DIR}/${STPLUGIN_PROJECT_NAME}.plugin"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"$<TARGET_BUNDLE_DIR:${STPLUGIN_PROJECT_NAME}>"
"${STPLUGIN_PACKAGE_DIR}/${STPLUGIN_PROJECT_NAME}.plugin"
COMMENT "Staging the ${STPLUGIN_PROJECT_NAME}.plugin bundle into ${STPLUGIN_PACKAGE_DIR}"
VERBATIM
)
else()
set(STPLUGIN_PACKAGE_BIN_DIR "${STPLUGIN_PACKAGE_DIR}/bin/64bit")
endif()
add_custom_command(TARGET ${STPLUGIN_PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "${STPLUGIN_PACKAGE_BIN_DIR}"
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${STPLUGIN_PROJECT_NAME}>" "${STPLUGIN_PACKAGE_BIN_DIR}/"
@@ -86,9 +115,19 @@ add_custom_command(TARGET ${STPLUGIN_PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_SOURCE_DIR}/data/locale/en-US.ini"
"${STPLUGIN_PACKAGE_DIR}/data/locale/"
# Redistributing LiveKit's prebuilt binaries means shipping their licence
# and notice with them. See third_party/livekit/README.md -- including
# what upstream does NOT ship, which is an open question, not a solved one.
COMMENT "Staging plugin + LiveKit runtime libraries + locale data into ${STPLUGIN_PACKAGE_DIR}"
VERBATIM
)
endif()
# Licence files: not something OBS's module loader looks for at all (unlike
# the locale data above), so these always land in the same top-level spot
# regardless of platform -- next to the bundle on macOS, next to bin/ on
# Linux/Windows. Redistributing LiveKit's prebuilt binaries means shipping
# their licence and notice with them; see third_party/livekit/README.md,
# including what upstream does NOT ship, which is an open question, not a
# solved one.
add_custom_command(TARGET ${STPLUGIN_PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "${STPLUGIN_PACKAGE_DIR}/licenses/livekit"
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_SOURCE_DIR}/third_party/livekit/LICENSE"
@@ -98,6 +137,6 @@ add_custom_command(TARGET ${STPLUGIN_PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_SOURCE_DIR}/LICENSE"
"${STPLUGIN_PACKAGE_DIR}/licenses/"
COMMENT "Staging plugin + LiveKit runtime libraries + licences into ${STPLUGIN_PACKAGE_DIR}"
COMMENT "Staging licences into ${STPLUGIN_PACKAGE_DIR}"
VERBATIM
)
+1
View File
@@ -4,6 +4,7 @@ RoomSlug="Room"
ReadKey="Read key"
Camera="Camera"
RefreshCameras="Refresh camera list"
AudioOnly="Audio only (no video)"
Status="Status"
NoCameraSelected="(no camera selected)"
OfflineSuffix=" (offline)"
+26 -12
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
// The thin glue layer, per the design doc: source registration, the
@@ -64,6 +57,7 @@ constexpr const char *kSettingServerUrl = "server_url";
constexpr const char *kSettingRoomSlug = "room_slug";
constexpr const char *kSettingReadKey = "read_key";
constexpr const char *kSettingCamera = "camera";
constexpr const char *kSettingAudioOnly = "audio_only";
constexpr const char *kSettingStatus = "status";
constexpr const char *kPropRefresh = "refresh";
@@ -103,6 +97,10 @@ struct CameraSource {
std::mutex mutex;
ConnectionConfig config;
std::string camera_identity;
/// True hides video entirely for this source (the soundboard, typically)
/// -- see SessionConfig::subscribe_video for what that actually does at
/// the LiveKit level.
bool audio_only = false;
/// Bumped every time settings change; the worker compares it to what it
/// last connected with, so a stale in-flight connect is abandoned rather
/// than fought over.
@@ -226,6 +224,7 @@ void workerLoop(CameraSource *self)
for (;;) {
ConnectionConfig config;
std::string camera;
bool audio_only = false;
std::uint64_t generation = 0;
{
std::unique_lock<std::mutex> lock(self->mutex);
@@ -233,6 +232,7 @@ void workerLoop(CameraSource *self)
break;
config = self->config;
camera = self->camera_identity;
audio_only = self->audio_only;
generation = self->generation;
}
@@ -289,6 +289,7 @@ void workerLoop(CameraSource *self)
session_config.ws_url = token.ws_url;
session_config.token = token.lk_token;
session_config.participant_identity = camera;
session_config.subscribe_video = !audio_only;
if (self->session->connect(session_config)) {
connected = true;
@@ -337,6 +338,7 @@ void sourceGetDefaults(obs_data_t *settings)
obs_data_set_default_string(settings, kSettingRoomSlug, "");
obs_data_set_default_string(settings, kSettingReadKey, "");
obs_data_set_default_string(settings, kSettingCamera, "");
obs_data_set_default_bool(settings, kSettingAudioOnly, false);
}
void applySettings(CameraSource *self, obs_data_t *settings)
@@ -346,16 +348,19 @@ void applySettings(CameraSource *self, obs_data_t *settings)
config.room_slug = settingString(settings, kSettingRoomSlug);
config.read_key = settingString(settings, kSettingReadKey);
const std::string camera = settingString(settings, kSettingCamera);
const bool audio_only = obs_data_get_bool(settings, kSettingAudioOnly);
{
std::lock_guard<std::mutex> guard(self->mutex);
const bool changed = config.server_url != self->config.server_url ||
config.room_slug != self->config.room_slug ||
config.read_key != self->config.read_key || camera != self->camera_identity;
config.read_key != self->config.read_key || camera != self->camera_identity ||
audio_only != self->audio_only;
if (!changed)
return;
self->config = config;
self->camera_identity = camera;
self->audio_only = audio_only;
++self->generation;
}
self->wake.notify_all();
@@ -392,6 +397,7 @@ void *sourceCreate(obs_data_t *settings, obs_source_t *source)
self->config.room_slug = settingString(settings, kSettingRoomSlug);
self->config.read_key = settingString(settings, kSettingReadKey);
self->camera_identity = settingString(settings, kSettingCamera);
self->audio_only = obs_data_get_bool(settings, kSettingAudioOnly);
self->generation = 1;
}
@@ -534,6 +540,14 @@ obs_properties_t *sourceGetProperties(void *data)
obs_properties_add_button(props, kPropRefresh, obs_module_text("RefreshCameras"), refreshButtonClicked);
// For a picked slot with no visual content worth showing (the
// soundboard, which publishes a throwaway black keep-alive frame purely
// because RTMP egress needs a video track -- see Soundboard.tsx in the
// streamer-tools repo). Disables the video track at the LiveKit level
// (RemoteTrackPublication::setEnabled(false), see session.cpp), not just
// locally: the SFU stops sending it.
obs_properties_add_bool(props, kSettingAudioOnly, obs_module_text("AudioOnly"));
// An OBS_TEXT_INFO property renders its *description* as the visible
// label, so the status line goes there rather than into a tooltip an
// operator would never hover over mid-show.
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
/* Adapted from obsproject/obs-plugintemplate (commit 3e7d7ac,
+4 -11
View File
@@ -2,18 +2,11 @@
streamer-tools OBS Camera Plugin
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <https://www.gnu.org/licenses/>
http://www.apache.org/licenses/LICENSE-2.0
*/
/* Adapted from obsproject/obs-plugintemplate (commit 3e7d7ac,
+2 -26
View File
@@ -8,29 +8,5 @@ module — so the SDK's licence and notice files ship with it.
`LICENSE` and `NOTICE` here are copied verbatim from the pinned release tag
`v1.10.1` (Apache License 2.0). They are staged into `build/package/licenses/`
by `obs-adapter/CMakeLists.txt` on every build, alongside this plugin's own
GPL-2.0 `LICENSE`.
## A correction to the design doc
The design doc's open questions say:
> `client-sdk-cpp`'s bundled `LICENSE.md` (~28 distinct third-party license
> blocks — Google WebRTC, OpenH264, etc.) must ship inside the plugin package
**No such file exists at `v1.10.1`.** Checked, on 2026-09-06:
- The five release archives for this tag (`livekit-sdk-<triple>-1.10.1.tar.gz`
/ `.zip`) contain only `include/`, `lib/`, `bin/` and
`share/livekit/build-info.json`. No licence file of any kind.
- The repository at tag `v1.10.1` has `LICENSE` (Apache-2.0, 10142 bytes) and
`NOTICE` (553 bytes) at its root. There is no `LICENSE.md`, no `NOTICE.md`,
and no `THIRD_PARTY_LICENSES` file.
So what ships here is the Apache-2.0 licence and notice, which is what
actually exists upstream. **The aggregated third-party notice the design doc
expected — covering the WebRTC/OpenH264/etc. code statically linked inside
`liblivekit_ffi.so` — has not been located and is not being shipped.** That
is a real, open licensing question for whoever signs off on distributing
release binaries, not something this packaging step has resolved. Worth
raising upstream, or asking counsel whether the Apache-2.0 NOTICE alone
suffices for a binary redistribution of that library.
Apache-2.0 `LICENSE` (this project's own first-party code was relicensed from
GPL-2.0 to Apache-2.0 to match).