Files
obs-streamer-tools-plugin/cmake/LiveKitSDK.cmake
T
shadowdaoandClaude Sonnet 5 7146f78831
Build / macOS (macos-latest) (push) Failing after 10s
Build / Linux (ubuntu-24.04) (push) Successful in 3m21s
Build / Windows (windows-latest) (push) Failing after 8m18s
ci: fix the three failures the first real three-platform run exposed
Linux, glibc. The LiveKit SDK's "linux-x64" asset is not actually generic:
it is built on Ubuntu 24.04 and needs GLIBC_2.38 and GLIBCXX_3.4.32, so
linking it on a 22.04 runner fails outright ("undefined reference to
std::ios_base_library_init()@GLIBCXX_3.4.32", "__isoc23_strtol@GLIBC_2.38").
That is exactly what happened when this repo's CI landed on the 22.04 Linux
runner instead of the 24.04 one. LiveKitSDK.cmake now defaults Linux to the
ubuntu-22.04 asset, which needs at most GLIBC_2.35 / GLIBCXX_3.4.30 (checked
with objdump against both archives) and therefore links and runs on 22.04 and
on everything newer -- the right floor for a plugin handed to directors as a
binary.

Linux, libobs version. The Linux job is pinned to ubuntu-24.04 rather than
ubuntu-latest, which this instance's two Linux runners answer with different
releases. 24.04's libobs-dev is 30.0.2 -- exactly the OBS version
buildspec.json pins for macOS/Windows -- so all three platforms build against
the same libobs. A 22.04 runner would have given OBS 27, a different API
surface.

macOS, no Xcode. The OBS sub-build failed its configure with "No
CMAKE_C_COMPILER could be found": the template hardcodes the Xcode generator,
and the `home-mac` runner has the Command Line Tools but no xcodebuild. The
sub-build now uses Ninja (with an explicit CMAKE_BUILD_TYPE, since Ninja is
single-config) and builds a single architecture rather than upstream's forced
universal -- this plugin is single-arch anyway, because client-sdk-cpp ships
single-arch dylibs, so a universal libobs would double the slowest step in CI
for a slice nothing links against.

While in there, generator flags are built as proper CMake lists so each
becomes its own argv entry. Upstream packs several into one space-separated
string and passes it unquoted, which execute_process hands to cmake as a
single argument; it happens not to matter for the optional flags upstream
passes, but it would silently swallow -DCMAKE_BUILD_TYPE.

Also lowers the libobs API floor in the adapter: video_format_get_parameters
instead of video_format_get_parameters_for_format. The _for_format variant
only exists from libobs 30 onwards and only differs for the 10-bit formats
(I010/P010) this source never receives, so using the older entry point keeps
the module loadable on an older OBS -- the direction that matters, since OBS
refuses modules built against a NEWER libobs than the one running.

Re-verified locally on Ubuntu 24.04 with the ubuntu-22.04 SDK asset:
ctest 6/6; the real-LiveKit integration test still reports "36 video frames,
323 audio frames, 10 state changes / 32 checks passed"; and the headless
libobs harness still logs "connected to ws://127.0.0.1:7880 ... watching
cam-test" followed by "video frame 640x360 I420" with the camera dropdown
populated from the live slot list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RL8abRmgFXkVASHkkqiJbE
2026-09-06 22:08:03 -07:00

222 lines
9.0 KiB
CMake

# LiveKitSDK.cmake
#
# Downloads the prebuilt LiveKit C++ SDK (livekit/client-sdk-cpp) release
# asset for the host OS/arch, extracts it, and points
# `find_package(LiveKit CONFIG REQUIRED)` at it.
#
# Adapted from livekit-examples/cpp-example-collection's cmake/LiveKitSDK.cmake
# (fetched 2026-09-06). Deliberate changes from the upstream reference:
#
# 1. VERSION must be an exact release number. The upstream helper accepted
# VERSION "latest" and resolved it through the GitHub releases API at
# configure time. That is exactly what this project must not do: the
# design doc calls for a pinned release tag, treating version bumps as
# deliberate work (the SDK is young and ships roughly weekly). Dropping
# the "latest" path also removes a GitHub-API call -- and its rate
# limiting / GITHUB_TOKEN plumbing -- from every CI configure.
# 2. Exports LIVEKIT_SDK_RUNTIME_LIBS: the shared libraries that must be
# staged next to the built OBS plugin module for it to load at runtime
# (liblivekit + liblivekit_ffi). Upstream examples run out of the build
# tree and never needed this; a redistributable OBS plugin does.
# 3. Exports LIVEKIT_SDK_INCLUDE_DIR / _LIB_DIR / _BIN_DIR for packaging.
#
# Usage:
# list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# include(LiveKitSDK)
# livekit_sdk_setup(VERSION "1.10.1" SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk")
# find_package(LiveKit CONFIG REQUIRED)
include_guard(GLOBAL)
# -------------------- Host detection --------------------
function(_lk_detect_host out_os out_arch)
if(WIN32)
set(_os "windows")
elseif(APPLE)
set(_os "macos")
elseif(UNIX)
set(_os "linux")
else()
message(FATAL_ERROR "LiveKitSDK: unsupported host OS")
endif()
# Prefer the *target* processor when cross-compiling is expressed through
# CMAKE_OSX_ARCHITECTURES (macOS CI runners are arm64 but may target x64).
set(_proc "${CMAKE_HOST_SYSTEM_PROCESSOR}")
if(APPLE AND CMAKE_OSX_ARCHITECTURES)
list(LENGTH CMAKE_OSX_ARCHITECTURES _n_arch)
if(_n_arch GREATER 1)
message(FATAL_ERROR
"LiveKitSDK: CMAKE_OSX_ARCHITECTURES lists ${_n_arch} architectures "
"(${CMAKE_OSX_ARCHITECTURES}). client-sdk-cpp ships single-arch "
"dylibs only, so universal binaries are not supported. Build one "
"architecture at a time.")
endif()
list(GET CMAKE_OSX_ARCHITECTURES 0 _proc)
endif()
string(TOLOWER "${_proc}" _proc_l)
if(_proc_l MATCHES "^(x86_64|amd64)$")
set(_arch "x64")
elseif(_proc_l MATCHES "^(arm64|aarch64)$")
set(_arch "arm64")
else()
message(FATAL_ERROR "LiveKitSDK: unsupported host arch: ${_proc}")
endif()
set(${out_os} "${_os}" PARENT_SCOPE)
set(${out_arch} "${_arch}" PARENT_SCOPE)
endfunction()
function(_lk_default_triple out_triple)
_lk_detect_host(_os _arch)
if(_os STREQUAL "linux")
# NOT the generic "linux-<arch>" asset, despite the name. That one is
# built on Ubuntu 24.04 and needs GLIBC_2.38 and GLIBCXX_3.4.32: linking
# it on Ubuntu 22.04 fails outright ("undefined reference to
# std::ios_base_library_init()@GLIBCXX_3.4.32", "__isoc23_strtol@GLIBC_2.38"),
# which is exactly what happened when CI landed on a 22.04 runner.
# The ubuntu-22.04 asset needs at most GLIBC_2.35 / GLIBCXX_3.4.30, so it
# links and runs on 22.04 AND on everything newer -- the right floor for a
# plugin that gets handed to directors as a binary.
set(${out_triple} "ubuntu-22.04-${_arch}" PARENT_SCOPE)
else()
set(${out_triple} "${_os}-${_arch}" PARENT_SCOPE)
endif()
endfunction()
function(_lk_archive_ext out_ext)
if(WIN32)
set(${out_ext} "zip" PARENT_SCOPE)
else()
set(${out_ext} "tar.gz" PARENT_SCOPE)
endif()
endfunction()
# -------------------- Public entrypoint --------------------
# livekit_sdk_setup(
# VERSION <x.y.z> REQUIRED, exact -- "latest" is rejected
# SDK_DIR <dir> REQUIRED, where the archive is extracted
# [REPO <org/repo>] default: livekit/client-sdk-cpp
# [SHA256 <hash>] optional: verify the downloaded archive
# [TRIPLE <os-arch>] optional override (e.g. ubuntu-24.04-x64)
# [DOWNLOAD_DIR <dir>] default: <build>/_downloads
# [NO_DOWNLOAD] fail instead of downloading if absent
# )
function(livekit_sdk_setup)
set(options NO_DOWNLOAD)
set(oneValueArgs VERSION SDK_DIR REPO SHA256 TRIPLE DOWNLOAD_DIR)
cmake_parse_arguments(LK "${options}" "${oneValueArgs}" "" ${ARGN})
if(NOT LK_VERSION)
message(FATAL_ERROR "livekit_sdk_setup: VERSION is required")
endif()
if(LK_VERSION STREQUAL "latest")
message(FATAL_ERROR
"livekit_sdk_setup: VERSION=\"latest\" is deliberately not supported. "
"This project pins an exact client-sdk-cpp release; see the comment at "
"the top of cmake/LiveKitSDK.cmake.")
endif()
if(NOT LK_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+$")
message(FATAL_ERROR "livekit_sdk_setup: VERSION must be x.y.z, got '${LK_VERSION}'")
endif()
if(NOT LK_SDK_DIR)
message(FATAL_ERROR "livekit_sdk_setup: SDK_DIR is required")
endif()
if(NOT LK_REPO)
set(LK_REPO "livekit/client-sdk-cpp")
endif()
if(NOT LK_DOWNLOAD_DIR)
set(LK_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/_downloads")
endif()
if(NOT LK_TRIPLE)
_lk_default_triple(LK_TRIPLE)
endif()
_lk_archive_ext(_ext)
set(_archive "livekit-sdk-${LK_TRIPLE}-${LK_VERSION}.${_ext}")
set(_url "https://github.com/${LK_REPO}/releases/download/v${LK_VERSION}/${_archive}")
set(_archive_path "${LK_DOWNLOAD_DIR}/${_archive}")
# The archive contains a single top-level folder named after the asset.
set(_extracted_root "${LK_SDK_DIR}/livekit-sdk-${LK_TRIPLE}-${LK_VERSION}")
file(MAKE_DIRECTORY "${LK_DOWNLOAD_DIR}")
file(MAKE_DIRECTORY "${LK_SDK_DIR}")
if(NOT EXISTS "${_extracted_root}/lib/cmake")
if(LK_NO_DOWNLOAD)
message(FATAL_ERROR
"LiveKitSDK: SDK not found at:\n ${_extracted_root}\nand NO_DOWNLOAD was set.")
endif()
message(STATUS "LiveKitSDK: downloading ${_url}")
if(LK_SHA256)
file(DOWNLOAD "${_url}" "${_archive_path}"
SHOW_PROGRESS TLS_VERIFY ON
EXPECTED_HASH "SHA256=${LK_SHA256}"
STATUS _st LOG _log)
else()
file(DOWNLOAD "${_url}" "${_archive_path}"
SHOW_PROGRESS TLS_VERIFY ON
STATUS _st LOG _log)
endif()
list(GET _st 0 _code)
list(GET _st 1 _msg)
if(NOT _code EQUAL 0)
file(REMOVE "${_archive_path}")
message(STATUS "LiveKitSDK: download log:\n${_log}")
message(FATAL_ERROR
"LiveKitSDK: download failed\nURL: ${_url}\nStatus: ${_code}\nMessage: ${_msg}\n"
"If this triple has no release asset, pass TRIPLE explicitly.")
endif()
# Remove any previous partial extraction.
file(REMOVE_RECURSE "${_extracted_root}")
message(STATUS "LiveKitSDK: extracting ${_archive_path}")
file(ARCHIVE_EXTRACT INPUT "${_archive_path}" DESTINATION "${LK_SDK_DIR}")
endif()
if(NOT EXISTS "${_extracted_root}/lib/cmake/LiveKit/LiveKitConfig.cmake")
message(FATAL_ERROR
"LiveKitSDK: extracted SDK does not look valid (missing "
"lib/cmake/LiveKit/LiveKitConfig.cmake)\nExpected under: ${_extracted_root}")
endif()
# Make find_package(LiveKit CONFIG REQUIRED) work in the caller's scope.
list(PREPEND CMAKE_PREFIX_PATH "${_extracted_root}")
set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH}" PARENT_SCOPE)
set(LiveKit_DIR "${_extracted_root}/lib/cmake/LiveKit" PARENT_SCOPE)
# --- Runtime libraries, for staging next to the plugin module ------------
# Windows keeps the DLLs in bin/ and the import libs in lib/; the Unix
# platforms put the shared objects directly in lib/.
if(WIN32)
file(GLOB _runtime_libs "${_extracted_root}/bin/*.dll")
elseif(APPLE)
file(GLOB _runtime_libs "${_extracted_root}/lib/*.dylib")
else()
file(GLOB _runtime_libs "${_extracted_root}/lib/*.so" "${_extracted_root}/lib/*.so.*")
endif()
if(NOT _runtime_libs)
message(FATAL_ERROR
"LiveKitSDK: found no runtime shared libraries under ${_extracted_root}. "
"The release layout may have changed for version ${LK_VERSION}.")
endif()
set(LIVEKIT_SDK_EXTRACTED_ROOT "${_extracted_root}" CACHE PATH "LiveKit SDK extracted root" FORCE)
set(LIVEKIT_SDK_INCLUDE_DIR "${_extracted_root}/include" CACHE PATH "LiveKit SDK include dir" FORCE)
set(LIVEKIT_SDK_LIB_DIR "${_extracted_root}/lib" CACHE PATH "LiveKit SDK lib dir" FORCE)
set(LIVEKIT_SDK_BIN_DIR "${_extracted_root}/bin" CACHE PATH "LiveKit SDK bin dir" FORCE)
set(LIVEKIT_SDK_RUNTIME_LIBS "${_runtime_libs}" CACHE STRING "LiveKit SDK runtime shared libraries" FORCE)
set(LIVEKIT_SDK_URL_USED "${_url}" CACHE STRING "LiveKit SDK URL used" FORCE)
set(LIVEKIT_SDK_VERSION_RESOLVED "${LK_VERSION}" CACHE STRING "LiveKit SDK version" FORCE)
set(LIVEKIT_SDK_TRIPLE_USED "${LK_TRIPLE}" CACHE STRING "LiveKit SDK triple" FORCE)
message(STATUS "LiveKitSDK: using SDK ${LK_VERSION} (${LK_TRIPLE}) at ${_extracted_root}")
endfunction()