cmake_minimum_required(VERSION 3.19) project(obs-streamer-tools-plugin VERSION 0.1.0 DESCRIPTION "OBS Studio source plugin for streamer-tools camera feeds" LANGUAGES C CXX ) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE) endif() enable_testing() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/common") # --- OBS SDK --------------------------------------------------------------- # Linux gets libobs from the distribution (Ubuntu's libobs-dev ships real # libobsConfig.cmake), and that path is left exactly as it was. # # macOS and Windows have no such package, so they use obs-plugintemplate's # buildspec bootstrap, trimmed: it downloads the pinned obs-deps bundle and # the pinned obs-studio source, then builds and installs just `libobs`. See # buildspec.json for why the OBS pin is deliberately low, and # cmake/common/buildspec_common.cmake for every change from upstream. # # STPLUGIN_BOOTSTRAP_OBS=OFF falls back to plain find_package(libobs), for a # developer who already has an OBS SDK on their prefix path and does not want # a from-source libobs build. option(STPLUGIN_BOOTSTRAP_OBS "Download and build libobs from source (macOS/Windows)" ON) # Fallbacks for imported targets that were exported under a different # configuration name than the one being built, lifted from # obs-plugintemplate's cmake/common/bootstrap.cmake. Without these, an # imported libobs exported as (say) RelWithDebInfo fails a Release build with # "IMPORTED_LOCATION or IMPORTED_IMPLIB not set for imported target # OBS::libobs configuration Release". set(CMAKE_MAP_IMPORTED_CONFIG_RELEASE Release RelWithDebInfo MinSizeRel None "") set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release MinSizeRel None "") set(CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL MinSizeRel Release RelWithDebInfo None "") set(CMAKE_MAP_IMPORTED_CONFIG_DEBUG Debug RelWithDebInfo Release MinSizeRel None "") include(osconfig) if(STPLUGIN_BOOTSTRAP_OBS AND (OS_MACOS OR OS_WINDOWS)) if(OS_MACOS) # client-sdk-cpp ships single-arch dylibs, so this plugin is built for # one architecture even though the libobs it links is universal. if(NOT CMAKE_OSX_ARCHITECTURES) set(CMAKE_OSX_ARCHITECTURES "${CMAKE_HOST_SYSTEM_PROCESSOR}" CACHE STRING "" FORCE) endif() if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0" CACHE STRING "" FORCE) endif() endif() include(buildspec) endif() # --- LiveKit C++ client SDK ------------------------------------------------- # Pinned, prebuilt release of livekit/client-sdk-cpp, downloaded and unpacked # by cmake/LiveKitSDK.cmake, then consumed through its own CMake package # config as the LiveKit::livekit imported target. See the design doc's # "Resolved (2026-09-07)" section: an exact pin, never "latest". set(STPLUGIN_LIVEKIT_SDK_VERSION "1.10.1" CACHE STRING "Pinned livekit/client-sdk-cpp release version") set(STPLUGIN_LIVEKIT_SDK_TRIPLE "" CACHE STRING "Override the client-sdk-cpp release triple (e.g. ubuntu-24.04-x64); empty = autodetect") set(STPLUGIN_LIVEKIT_SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk" CACHE PATH "Directory the client-sdk-cpp release archive is extracted into (point at a persistent path to cache it across CI builds)") # Pinned SHA256 checksums for the client-sdk-cpp v1.10.1 release archives, # so the download in cmake/LiveKitSDK.cmake is verified the same way the # obs-deps bootstrap next to it already is (see # cmake/common/buildspec_common.cmake ~line 324). Each hash below was # computed by downloading the real GitHub release asset and running # `sha256sum` on it (2026-09-06/07) -- none of these were guessed or copied # from an unverified source. To add a hash for a new version or triple: # curl -LO https://github.com/livekit/client-sdk-cpp/releases/download/v/livekit-sdk--. # sha256sum livekit-sdk--.* # Covers every triple _lk_default_triple() can resolve to for this pinned # version: Linux (ubuntu-22.04-x64/arm64), macOS (macos-x64/arm64) and # Windows (windows-x64). Verified by extracting each archive # (tar tzf / unzip -l) and confirming a real LiveKitConfig.cmake inside -- # only Linux was also verified by an actual local CMake configure+build in # this environment; macOS and Windows were downloaded and hashed but not # build-tested here. set(_stplugin_livekit_sha256_1.10.1_ubuntu-22.04-x64 "6f4fc8143f36952d42bfd5ff8d1782cf6211ba8fd6b055877e9ef85441d66324") set(_stplugin_livekit_sha256_1.10.1_ubuntu-22.04-arm64 "399677167b474b7f107c6937904ea01898c9ec8da648cbed669387e599c6ea45") set(_stplugin_livekit_sha256_1.10.1_macos-x64 "7102655c1f2947be4b06a95f9fafa1a11379219328e82ad875e5ebccfc9ac7e3") set(_stplugin_livekit_sha256_1.10.1_macos-arm64 "0822af7014519a473c5b5cd019bde58c26cc2bfe5b78e4a790395ead232dc55b") set(_stplugin_livekit_sha256_1.10.1_windows-x64 "b9fc6b2865298d7e3d032205d7e74fb9628cfe55a2ed28cb657db0a481cd518c") include(LiveKitSDK) if(STPLUGIN_LIVEKIT_SDK_TRIPLE) set(_stplugin_livekit_triple "${STPLUGIN_LIVEKIT_SDK_TRIPLE}") else() # Mirrors LiveKitSDK.cmake's own autodetection so the checksum lookup # below matches whatever triple livekit_sdk_setup() will actually # resolve to and download. _lk_default_triple(_stplugin_livekit_triple) endif() set(_stplugin_livekit_sha256_var "_stplugin_livekit_sha256_${STPLUGIN_LIVEKIT_SDK_VERSION}_${_stplugin_livekit_triple}") if(DEFINED ${_stplugin_livekit_sha256_var}) set(_stplugin_livekit_sha256 "${${_stplugin_livekit_sha256_var}}") else() set(_stplugin_livekit_sha256 "") message(WARNING "LiveKitSDK: no pinned SHA256 for triple '${_stplugin_livekit_triple}' " "at version ${STPLUGIN_LIVEKIT_SDK_VERSION} -- the downloaded archive " "will NOT be integrity-checked. Compute one (see the comment above " "this block) and add it to CMakeLists.txt.") endif() if(STPLUGIN_LIVEKIT_SDK_TRIPLE) livekit_sdk_setup( VERSION "${STPLUGIN_LIVEKIT_SDK_VERSION}" SDK_DIR "${STPLUGIN_LIVEKIT_SDK_DIR}" TRIPLE "${STPLUGIN_LIVEKIT_SDK_TRIPLE}" SHA256 "${_stplugin_livekit_sha256}" ) else() livekit_sdk_setup( VERSION "${STPLUGIN_LIVEKIT_SDK_VERSION}" SDK_DIR "${STPLUGIN_LIVEKIT_SDK_DIR}" SHA256 "${_stplugin_livekit_sha256}" ) endif() 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 /cmake// -- # a shape find_package( CONFIG) never searches under CMAKE_PREFIX_PATH. # CMake's documented Config-mode search suffixes include # "/(cmake|CMake)/" (the file directly inside that dir, no # subdirectory) and "/*/(cmake|CMake)/[...]" (a -prefixed # directory first) -- neither matches "/cmake/*/". 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 /lib/cmake/libobs/, which # matches the standard "/lib*/cmake/*/" suffix -- and neither # does Linux's libobs-dev, which installs to # /usr/lib//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 /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. # # Reaching here means the bootstrap 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() 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-.cmake alongside it when libobs is built on its own, # and CMake then fails at generate time with: # # IMPORTED_LOCATION or IMPORTED_IMPLIB not set for imported target # "OBS::libobs" configuration "Release". # # Repair it here rather than fighting OBS's export machinery: the library the # bootstrap just built is in a known place, and pointing the imported target # at it is exactly what the missing file would have done. Distribution # packages (Ubuntu's libobs-dev) export a complete target and never take this # path. if(libobs_FOUND AND TARGET OBS::libobs) get_target_property(_stplugin_obs_location OBS::libobs IMPORTED_LOCATION) get_target_property(_stplugin_obs_location_release OBS::libobs IMPORTED_LOCATION_RELEASE) get_target_property(_stplugin_obs_implib OBS::libobs IMPORTED_IMPLIB) get_target_property(_stplugin_obs_implib_release OBS::libobs IMPORTED_IMPLIB_RELEASE) if(NOT _stplugin_obs_location AND NOT _stplugin_obs_location_release AND NOT _stplugin_obs_implib AND NOT _stplugin_obs_implib_release) set(_stplugin_deps "${CMAKE_CURRENT_SOURCE_DIR}/.deps") if(APPLE) set(_stplugin_obs_binary "${_stplugin_deps}/Frameworks/libobs.framework/Versions/A/libobs") if(NOT EXISTS "${_stplugin_obs_binary}") set(_stplugin_obs_binary "${_stplugin_deps}/Frameworks/libobs.framework/libobs") endif() if(EXISTS "${_stplugin_obs_binary}") set_target_properties( OBS::libobs PROPERTIES IMPORTED_LOCATION "${_stplugin_obs_binary}" INTERFACE_INCLUDE_DIRECTORIES "${_stplugin_deps}/Frameworks/libobs.framework/Headers" ) 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/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) set_target_properties(OBS::libobs PROPERTIES IMPORTED_LOCATION "${_stplugin_obs_dll_found}") endif() message(STATUS "OBS::libobs had no imported location; pointed it at ${_stplugin_obs_implib_found}") endif() endif() endif() endif() if(libobs_FOUND) message(STATUS "libobs found (${libobs_DIR}) -- building OBS adapter module") add_subdirectory(obs-adapter) else() message(WARNING "libobs NOT found -- skipping OBS adapter module; core library still builds/tests") endif()