From 7dde7cbf82c9cc31286b711146c3243663463a7e Mon Sep 17 00:00:00 2001 From: shijing xian Date: Tue, 13 Jan 2026 09:58:47 +0800 Subject: [PATCH 01/10] added basic_room --- .gitignore | 26 +++++ CMakeLists.txt | 148 ++++++++++++++++++++++++++++ basic_room/CMakeLists.txt | 39 ++++++++ basic_room/capture_utils.cpp | 125 ++++++++++++++++++++++++ basic_room/capture_utils.h | 32 ++++++ basic_room/main.cpp | 184 +++++++++++++++++++++++++++++++++++ cmake/LiveKitSDK.cmake | 167 +++++++++++++++++++++++++++++++ 7 files changed, 721 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 basic_room/CMakeLists.txt create mode 100644 basic_room/capture_utils.cpp create mode 100644 basic_room/capture_utils.h create mode 100644 basic_room/main.cpp create mode 100644 cmake/LiveKitSDK.cmake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a0132a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +.cache/ +CMakeFiles/ +CMakeCache.txt +.DS_Store +Makefile +cmake_install.cmake +out +build/ +build-debug/ +build-release/ +vcpkg_installed/ +received_green.avif +docs/*.bak +docs/html/ +docs/latex/ +.vs/ +.vscode/ +# Compiled output +bin/ +lib/ +*.lib +*.a +*.so +*.dylib +*.dll +*.exe diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..574e398 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,148 @@ +cmake_minimum_required(VERSION 3.20) +project(livekit_cpp_example_collection LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# -------- User knobs -------- +# Default to the version you mentioned. Users can override: +# cmake -S . -B build -DLIVEKIT_SDK_VERSION=0.1.9 +set(LIVEKIT_SDK_VERSION "0.1.9" CACHE STRING "LiveKit C++ SDK version to download") + +# Optional: enforce integrity if you have the sha256 for that specific asset. +# Users can pass: +# -DLIVEKIT_SDK_SHA256= +set(LIVEKIT_SDK_SHA256 "" CACHE STRING "Optional sha256 for the downloaded SDK archive") + +# Where to unpack inside the build directory +set(LIVEKIT_SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk" CACHE PATH "Where to unpack the LiveKit SDK") + +# -------- Detect platform triple -------- +# We’re detecting the *host* because these examples are built natively. +# (If you later want cross-compile, this should key off CMAKE_SYSTEM_* instead.) +set(_host_os "") +if(WIN32) + set(_host_os "windows") +elseif(APPLE) + set(_host_os "macos") +elseif(UNIX) + set(_host_os "linux") +else() + message(FATAL_ERROR "Unsupported host OS") +endif() + +set(_host_arch "") +if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$") + set(_host_arch "x64") +elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)$") + set(_host_arch "arm64") +else() + message(FATAL_ERROR "Unsupported host arch: ${CMAKE_HOST_SYSTEM_PROCESSOR}") +endif() + +# Map to your release artifact naming +# Adjust if you use different naming (e.g., macos-x86_64 vs macos-x64) +if(_host_os STREQUAL "macos" AND _host_arch STREQUAL "x64") + # If your asset is named macos-x86_64 instead, change this to "macos-x86_64" + set(SDK_TRIPLE "macos-x64") +else() + set(SDK_TRIPLE "${_host_os}-${_host_arch}") +endif() + +# -------- Compute archive name + URL -------- +if(_host_os STREQUAL "windows") + set(SDK_EXT "zip") +else() + set(SDK_EXT "tar.gz") +endif() + +set(SDK_ARCHIVE "livekit-sdk-${SDK_TRIPLE}-${LIVEKIT_SDK_VERSION}.${SDK_EXT}") +set(SDK_URL "https://github.com/livekit/client-sdk-cpp/releases/download/v${LIVEKIT_SDK_VERSION}/${SDK_ARCHIVE}") + +# Where to download +set(SDK_DL_DIR "${CMAKE_BINARY_DIR}/_downloads") +set(SDK_ARCHIVE_PATH "${SDK_DL_DIR}/${SDK_ARCHIVE}") + +# The extracted SDK root will look like: +# /livekit-sdk--/ +set(SDK_EXTRACTED_ROOT "${LIVEKIT_SDK_DIR}/livekit-sdk-${SDK_TRIPLE}-${LIVEKIT_SDK_VERSION}") + +# -------- Download + extract (configure-time) -------- +file(MAKE_DIRECTORY "${SDK_DL_DIR}") +file(MAKE_DIRECTORY "${LIVEKIT_SDK_DIR}") + +if(NOT EXISTS "${SDK_EXTRACTED_ROOT}") + message(STATUS "LiveKit SDK not found at: ${SDK_EXTRACTED_ROOT}") + message(STATUS "Downloading: ${SDK_URL}") + + if(LIVEKIT_SDK_SHA256 STREQUAL "") + file(DOWNLOAD + "${SDK_URL}" + "${SDK_ARCHIVE_PATH}" + SHOW_PROGRESS + STATUS _dl_status + TLS_VERIFY ON + ) + else() + file(DOWNLOAD + "${SDK_URL}" + "${SDK_ARCHIVE_PATH}" + SHOW_PROGRESS + STATUS _dl_status + TLS_VERIFY ON + EXPECTED_HASH "SHA256=${LIVEKIT_SDK_SHA256}" + ) + endif() + + list(GET _dl_status 0 _dl_code) + list(GET _dl_status 1 _dl_msg) + if(NOT _dl_code EQUAL 0) + message(FATAL_ERROR "Failed to download ${SDK_URL}\nStatus: ${_dl_code}\nMessage: ${_dl_msg}") + endif() + + message(STATUS "Extracting: ${SDK_ARCHIVE_PATH}") + + # Clean any previous partial extraction + file(REMOVE_RECURSE "${SDK_EXTRACTED_ROOT}") + + if(_host_os STREQUAL "windows") + # CMake can extract zip with -E tar + execute_process( + COMMAND "${CMAKE_COMMAND}" -E tar xvf "${SDK_ARCHIVE_PATH}" + WORKING_DIRECTORY "${LIVEKIT_SDK_DIR}" + RESULT_VARIABLE _xret + ) + else() + execute_process( + COMMAND "${CMAKE_COMMAND}" -E tar xvf "${SDK_ARCHIVE_PATH}" + WORKING_DIRECTORY "${LIVEKIT_SDK_DIR}" + RESULT_VARIABLE _xret + ) + endif() + + if(NOT _xret EQUAL 0) + message(FATAL_ERROR "Failed to extract ${SDK_ARCHIVE_PATH} (code=${_xret})") + endif() +endif() + +if(NOT EXISTS "${SDK_EXTRACTED_ROOT}/lib/cmake") + message(FATAL_ERROR + "Extracted SDK does not look valid (missing lib/cmake).\n" + "Expected root: ${SDK_EXTRACTED_ROOT}\n" + "If the archive root folder name differs, adjust SDK_EXTRACTED_ROOT logic." + ) +endif() + +# -------- Make find_package(LiveKit) work -------- +# Your SDK installs a config package under /lib/cmake/... +# The most robust approach is to push onto CMAKE_PREFIX_PATH. +list(PREPEND CMAKE_PREFIX_PATH "${SDK_EXTRACTED_ROOT}") + +# Some packages also require explicit *_DIR; keep as a fallback option: +# set(LiveKit_DIR "${SDK_EXTRACTED_ROOT}/lib/cmake/LiveKit" CACHE PATH "" FORCE) + +find_package(LiveKit CONFIG REQUIRED) + +# -------- Build examples -------- +add_subdirectory(basic_room) + diff --git a/basic_room/CMakeLists.txt b/basic_room/CMakeLists.txt new file mode 100644 index 0000000..dbea5e7 --- /dev/null +++ b/basic_room/CMakeLists.txt @@ -0,0 +1,39 @@ +add_executable(basic_room main.cpp capture_utils.cpp capture_utils.h) + + find_package(Protobuf CONFIG REQUIRED) + + target_include_directories(basic_room PRIVATE ${ + CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries( + basic_room PRIVATE LiveKit::livekit protobuf::libprotobuf) + +#Make - llivekit_ffi resolvable + get_filename_component(_lk_cmake_dir + "${LiveKit_DIR}" DIRECTORY) #... / + lib / + cmake + get_filename_component(_lk_lib_dir "${_lk_cmake_dir}" DIRECTORY) #... / + lib target_link_directories(basic_room PRIVATE "${_lk_lib_dir}") + +#Nice - to - \ + have : copy runtime DLLs next to the exe on \ + Windows for "run from build dir". +#Only do this if your exported package provides these targets. + if (WIN32) +#livekit_ffi.dll + if (TARGET LiveKit::livekit_ffi) add_custom_command( + TARGET basic_room POST_BUILD COMMAND ${CMAKE_COMMAND} - + E copy_if_different "$" + "$") endif() + +#If you also export protobuf / abseil runtime targets, copy them too(optional). + if (TARGET protobuf::libprotobuf) add_custom_command( + TARGET basic_room POST_BUILD COMMAND ${CMAKE_COMMAND} - + E copy_if_different "$" + "$") endif() + + if (TARGET absl::abseil_dll) add_custom_command( + TARGET basic_room POST_BUILD COMMAND ${CMAKE_COMMAND} - + E copy_if_different "$" + "$") + endif() endif() diff --git a/basic_room/capture_utils.cpp b/basic_room/capture_utils.cpp new file mode 100644 index 0000000..8db8703 --- /dev/null +++ b/basic_room/capture_utils.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include "capture_utils.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "livekit/livekit.h" + +using namespace livekit; + +// Test utils to run a capture loop to publish noisy audio frames to the room +void runNoiseCaptureLoop(const std::shared_ptr &source, + std::atomic &running_flag) { + const int sample_rate = source->sample_rate(); + const int num_channels = source->num_channels(); + const int frame_ms = 10; + const int samples_per_channel = sample_rate * frame_ms / 1000; + + // White noise generator (keep amplitude conservative to avoid clipping) + std::mt19937 rng{std::random_device{}()}; + std::uniform_int_distribution dist(-3000, 3000); + + using Clock = std::chrono::steady_clock; + auto next_deadline = Clock::now(); + + while (running_flag.load(std::memory_order_relaxed)) { + AudioFrame frame = + AudioFrame::create(sample_rate, num_channels, samples_per_channel); + auto &pcm = frame.data(); + for (auto &s : pcm) { + s = static_cast(dist(rng)); + } + + try { + source->captureFrame(frame); + } catch (const std::exception &e) { + std::cerr << "Error in captureFrame (noise): " << e.what() << std::endl; + break; + } + + next_deadline += std::chrono::milliseconds(frame_ms); + std::this_thread::sleep_until(next_deadline); + } + + try { + source->clearQueue(); + } catch (...) { + std::cerr << "Error in clearQueue (noise)" << std::endl; + } +} + +// Fake video source: solid color cycling +void runFakeVideoCaptureLoop(const std::shared_ptr &source, + std::atomic &running_flag) { + auto frame = LKVideoFrame::create(1280, 720, VideoBufferType::RGBA); + const double framerate = 1.0 / 30.0; + + while (running_flag.load(std::memory_order_relaxed)) { + static auto start = std::chrono::high_resolution_clock::now(); + float t = std::chrono::duration( + std::chrono::high_resolution_clock::now() - start) + .count(); + // Cycle every 4 seconds: 0=red, 1=green, 2=blue, 3=black + int stage = static_cast(t) % 4; + + std::array rgb{}; + switch (stage) { + case 0: // red + rgb = {255, 0, 0, 0}; + break; + case 1: // green + rgb = {0, 255, 0, 0}; + break; + case 2: // blue + rgb = {0, 0, 255, 0}; + break; + case 3: // black + default: + rgb = {0, 0, 0, 0}; + break; + } + + // ARGB + uint8_t *data = frame.data(); + const size_t size = frame.dataSize(); + for (size_t i = 0; i < size; i += 4) { + data[i + 0] = 255; // A + data[i + 1] = rgb[0]; // R + data[i + 2] = rgb[1]; // G + data[i + 3] = rgb[2]; // B + } + + try { + // If VideoSource is ARGB-capable, pass frame. + // If it expects I420, pass i420 instead. + source->captureFrame(frame, 0, VideoRotation::VIDEO_ROTATION_0); + } catch (const std::exception &e) { + std::cerr << "Error in captureFrame (fake video): " << e.what() + << std::endl; + break; + } + + std::this_thread::sleep_for(std::chrono::duration(framerate)); + } +} diff --git a/basic_room/capture_utils.h b/basic_room/capture_utils.h new file mode 100644 index 0000000..e80b243 --- /dev/null +++ b/basic_room/capture_utils.h @@ -0,0 +1,32 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#pragma once + +#include +#include + +namespace livekit { +class AudioSource; +class VideoSource; +} // namespace livekit + +void runNoiseCaptureLoop(const std::shared_ptr &source, + std::atomic &running_flag); + +void runFakeVideoCaptureLoop( + const std::shared_ptr &source, + std::atomic &running_flag); diff --git a/basic_room/main.cpp b/basic_room/main.cpp new file mode 100644 index 0000000..26ca17d --- /dev/null +++ b/basic_room/main.cpp @@ -0,0 +1,184 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "capture_utils.h" +#include "livekit/livekit.h" + +using namespace livekit; + +namespace { + +std::atomic g_running{true}; + +void handleSignal(int) { g_running.store(false); } + +void printUsage(const char *prog) { + std::cerr << "Usage:\n" + << " " << prog << " --url --token \n" + << "Env fallbacks:\n" + << " LIVEKIT_URL, LIVEKIT_TOKEN\n"; +} + +bool parseArgs(int argc, char *argv[], std::string &url, std::string &token) { + for (int i = 1; i < argc; ++i) { + const std::string a = argv[i]; + if (a == "-h" || a == "--help") + return false; + + auto take = [&](std::string &out) -> bool { + if (i + 1 >= argc) + return false; + out = argv[++i]; + return true; + }; + + if (a == "--url") { + if (!take(url)) + return false; + } else if (a.rfind("--url=", 0) == 0) { + url = a.substr(std::string("--url=").size()); + } else if (a == "--token") { + if (!take(token)) + return false; + } else if (a.rfind("--token=", 0) == 0) { + token = a.substr(std::string("--token=").size()); + } + } + + if (url.empty()) { + if (const char *e = std::getenv("LIVEKIT_URL")) + url = e; + } + if (token.empty()) { + if (const char *e = std::getenv("LIVEKIT_TOKEN")) + token = e; + } + + return !(url.empty() || token.empty()); +} + +} // namespace + +int main(int argc, char *argv[]) { + std::string url, token; + if (!parseArgs(argc, argv, url, token)) { + printUsage(argv[0]); + return 1; + } + + std::signal(SIGINT, handleSignal); + + // Init LiveKit + livekit::initialize(livekit::LogSink::kConsole); + + auto room = std::make_unique(); + + RoomOptions options; + options.auto_subscribe = true; + options.dynacast = false; + + std::cout << "Connecting to: " << url << "\n"; + if (!room->Connect(url, token, options)) { + std::cerr << "Failed to connect\n"; + livekit::shutdown(); + return 1; + } + + std::cout << "Connected.\n"; + + // ---- Create & publish AUDIO (noise) ---- + // Match your runNoiseCaptureLoop pacing: it assumes frame_ms=10. + auto audioSource = std::make_shared(48000, 1, 10); + auto audioTrack = + LocalAudioTrack::createLocalAudioTrack("noise", audioSource); + + TrackPublishOptions audioOpts; + audioOpts.source = TrackSource::SOURCE_MICROPHONE; + audioOpts.dtx = false; + audioOpts.simulcast = false; + + std::shared_ptr audioPub; + try { + audioPub = room->localParticipant()->publishTrack(audioTrack, audioOpts); + std::cout << "Published audio: sid=" << audioPub->sid() << "\n"; + } catch (const std::exception &e) { + std::cerr << "Failed to publish audio: " << e.what() << "\n"; + } + + // ---- Create & publish VIDEO (fake RGB) ---- + // Your helper uses LKVideoFrame::create(1280, 720, BGRA), so match that. + auto videoSource = std::make_shared(1280, 720); + auto videoTrack = LocalVideoTrack::createLocalVideoTrack("rgb", videoSource); + + TrackPublishOptions videoOpts; + videoOpts.source = TrackSource::SOURCE_CAMERA; + videoOpts.dtx = false; + videoOpts.simulcast = false; + + std::shared_ptr videoPub; + try { + videoPub = room->localParticipant()->publishTrack(videoTrack, videoOpts); + std::cout << "Published video: sid=" << videoPub->sid() << "\n"; + } catch (const std::exception &e) { + std::cerr << "Failed to publish video: " << e.what() << "\n"; + } + + // ---- Start synthetic capture loops ---- + std::atomic audio_running{true}; + std::atomic video_running{true}; + + std::thread audioThread( + [&] { runNoiseCaptureLoop(audioSource, audio_running); }); + std::thread videoThread( + [&] { runFakeVideoCaptureLoop(videoSource, video_running); }); + + // Keep alive until Ctrl-C + while (g_running.load()) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + // Stop loops and join threads + audio_running.store(false); + video_running.store(false); + + if (audioThread.joinable()) + audioThread.join(); + if (videoThread.joinable()) + videoThread.join(); + + // Best-effort unpublish + try { + if (audioPub) + room->localParticipant()->unpublishTrack(audioPub->sid()); + if (videoPub) + room->localParticipant()->unpublishTrack(videoPub->sid()); + } catch (...) { + } + + room.reset(); + livekit::shutdown(); + std::cout << "Exiting.\n"; + return 0; +} diff --git a/cmake/LiveKitSDK.cmake b/cmake/LiveKitSDK.cmake new file mode 100644 index 0000000..55edb01 --- /dev/null +++ b/cmake/LiveKitSDK.cmake @@ -0,0 +1,167 @@ +# LiveKitSDK.cmake +# +# A small helper for example repos: +# - Downloads the appropriate prebuilt LiveKit C++ SDK release asset for the host OS/arch +# - Extracts it into a local directory (default: build/_deps/livekit-sdk) +# - Prepends the extracted prefix to CMAKE_PREFIX_PATH so find_package(LiveKit CONFIG REQUIRED) works +# +# Usage: +# list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +# include(LiveKitSDK) +# livekit_sdk_setup(VERSION "0.1.9" SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk") +# +# Optional: +# livekit_sdk_setup(VERSION "0.1.9" REPO "livekit/client-sdk-cpp" SHA256 "") + +include_guard(GLOBAL) + +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() + + set(_proc "${CMAKE_HOST_SYSTEM_PROCESSOR}") + if(_proc MATCHES "^(x86_64|AMD64)$") + set(_arch "x64") + elseif(_proc 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) + + set(_triple "${_os}-${_arch}") + set(${out_triple} "${_triple}" PARENT_SCOPE) +endfunction() + +function(_lk_archive_ext out_ext) + _lk_detect_host(_os _arch) + if(_os STREQUAL "windows") + set(${out_ext} "zip" PARENT_SCOPE) + else() + set(${out_ext} "tar.gz" PARENT_SCOPE) + endif() +endfunction() + +# Public: +# livekit_sdk_setup( +# VERSION +# SDK_DIR +# [REPO ] default: livekit/client-sdk-cpp +# [SHA256 ] optional: verify download +# [TRIPLE ] optional override +# [DOWNLOAD_DIR ] default: /_downloads +# [NO_DOWNLOAD] error if not already present +# ) +function(livekit_sdk_setup) + set(options NO_DOWNLOAD) + set(oneValueArgs VERSION SDK_DIR REPO SHA256 TRIPLE DOWNLOAD_DIR) + set(multiValueArgs) + cmake_parse_arguments(LK "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT LK_VERSION) + message(FATAL_ERROR "livekit_sdk_setup: VERSION is required") + 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_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}") + + if(NOT LK_DOWNLOAD_DIR) + set(LK_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/_downloads") + endif() + + set(_dl_dir "${LK_DOWNLOAD_DIR}") + set(_archive_path "${_dl_dir}/${_archive}") + + # Extracted root folder name (matches your bundle root) + set(_extracted_root "${LK_SDK_DIR}/livekit-sdk-${LK_TRIPLE}-${LK_VERSION}") + + file(MAKE_DIRECTORY "${_dl_dir}") + file(MAKE_DIRECTORY "${LK_SDK_DIR}") + + if(NOT EXISTS "${_extracted_root}") + if(LK_NO_DOWNLOAD) + message(FATAL_ERROR + "LiveKitSDK: SDK not found at:\n ${_extracted_root}\n" + "and 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 + ) + else() + file(DOWNLOAD "${_url}" "${_archive_path}" + SHOW_PROGRESS + TLS_VERIFY ON + STATUS _st + ) + endif() + + list(GET _st 0 _code) + list(GET _st 1 _msg) + if(NOT _code EQUAL 0) + message(FATAL_ERROR "LiveKitSDK: download failed\nURL: ${_url}\nStatus: ${_code}\nMessage: ${_msg}") + endif() + + message(STATUS "LiveKitSDK: extracting ${_archive_path}") + file(REMOVE_RECURSE "${_extracted_root}") + + execute_process( + COMMAND "${CMAKE_COMMAND}" -E tar xvf "${_archive_path}" + WORKING_DIRECTORY "${LK_SDK_DIR}" + RESULT_VARIABLE _xret + ) + if(NOT _xret EQUAL 0) + message(FATAL_ERROR "LiveKitSDK: extraction failed (${_xret}) for ${_archive_path}") + endif() + endif() + + if(NOT EXISTS "${_extracted_root}/lib/cmake") + message(FATAL_ERROR + "LiveKitSDK: extracted SDK does not look valid (missing lib/cmake)\n" + "Expected: ${_extracted_root}\n" + "If your archive root folder name differs, adjust _extracted_root logic." + ) + endif() + + # Make find_package(LiveKit CONFIG REQUIRED) work. + list(PREPEND CMAKE_PREFIX_PATH "${_extracted_root}") + + # Export a few useful variables for callers (optional). + set(LIVEKIT_SDK_EXTRACTED_ROOT "${_extracted_root}" CACHE PATH "LiveKit SDK extracted root" FORCE) + set(LIVEKIT_SDK_URL_USED "${_url}" CACHE STRING "LiveKit SDK URL used" FORCE) + + message(STATUS "LiveKitSDK: using SDK at ${_extracted_root}") +endfunction() + From fae8713cb7454a7888b8e1f747a72b007234b86e Mon Sep 17 00:00:00 2001 From: shijing xian Date: Wed, 14 Jan 2026 19:51:09 +0800 Subject: [PATCH 02/10] add the basic_room example, github workflow and README --- .github/workflow/builds.yml | 133 +++++++++++++++++++++++++++++++ CMakeLists.txt | 147 +++-------------------------------- README.md | 73 ++++++++++++++++- basic_room/CMakeLists.txt | 72 +++++++++-------- basic_room/capture_utils.cpp | 2 +- basic_room/main.cpp | 2 +- cmake/LiveKitSDK.cmake | 109 +++++++++++++++++++++----- 7 files changed, 345 insertions(+), 193 deletions(-) create mode 100644 .github/workflow/builds.yml diff --git a/.github/workflow/builds.yml b/.github/workflow/builds.yml new file mode 100644 index 0000000..4b29281 --- /dev/null +++ b/.github/workflow/builds.yml @@ -0,0 +1,133 @@ +name: Build examples against latest LiveKit SDK (via CMake) + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + workflow_dispatch: + +env: + # Used by LiveKitSDK.cmake when VERSION=latest (it looks for env GITHUB_TOKEN by default) + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + name: linux-x64 + - os: macos-latest + name: macos-arm64 + - os: windows-latest + name: windows-x64 + + name: Build (${{ matrix.name }}) + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + # ---------- deps ---------- + - name: Install deps (Ubuntu) + if: runner.os == 'Linux' + shell: bash + run: | + set -eux + sudo apt-get update + sudo apt-get install -y \ + cmake ninja-build pkg-config \ + protobuf-compiler libprotobuf-dev \ + libssl-dev \ + curl + + - name: Install deps (macOS) + if: runner.os == 'macOS' + shell: bash + run: | + set -eux + brew update + brew install cmake ninja protobuf + + - name: Install deps (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + choco install -y cmake --installargs 'ADD_CMAKE_TO_PATH=System' + choco install -y ninja + # curl is available on windows-latest; no need for jq/unzip since CMake does the download/extract. + + # ---------- configure + build ---------- + - name: Configure (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + set -eux + cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release + + - name: Build (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + set -eux + cmake --build build --config Release + + - name: Configure (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release + + - name: Build (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + cmake --build build --config Release + + # ---------- smoke test ---------- + # LiveKitSDK.cmake typically extracts into: build/_deps/livekit-sdk/ + # We’ll locate the extracted SDK root robustly and set runtime env vars from it. + - name: Smoke test (Linux/macOS) + if: runner.os != 'Windows' + shell: bash + run: | + set -eux + + sdk_root="$(ls -d build/_deps/livekit-sdk/* | head -n 1)" + echo "SDK root: ${sdk_root}" + ls -la "${sdk_root}/lib" || true + + if [[ "$RUNNER_OS" == "Linux" ]]; then + export LD_LIBRARY_PATH="${sdk_root}/lib:${LD_LIBRARY_PATH:-}" + else + export DYLD_LIBRARY_PATH="${sdk_root}/lib:${DYLD_LIBRARY_PATH:-}" + fi + + ./build/basic_room --help || true + + - name: Smoke test (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $sdkRoot = Get-ChildItem -Directory "build\_deps\livekit-sdk" | Select-Object -First 1 + if (-not $sdkRoot) { throw "SDK root not found under build\_deps\livekit-sdk" } + Write-Host "SDK root: $($sdkRoot.FullName)" + + # Prefer bin first; keep lib too (some packages put dlls in lib) + $env:PATH = "$($sdkRoot.FullName)\bin;$($sdkRoot.FullName)\lib;$env:PATH" + + .\build\basic_room.exe --help 2>$null + + # ---------- upload build output ---------- + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: basic_room-${{ matrix.name }} + path: | + build/basic_room* + build/basic_room.exe + retention-days: 7 diff --git a/CMakeLists.txt b/CMakeLists.txt index 574e398..7ba5e70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,148 +1,21 @@ cmake_minimum_required(VERSION 3.20) project(livekit_cpp_example_collection LANGUAGES CXX) - set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# -------- User knobs -------- -# Default to the version you mentioned. Users can override: -# cmake -S . -B build -DLIVEKIT_SDK_VERSION=0.1.9 -set(LIVEKIT_SDK_VERSION "0.1.9" CACHE STRING "LiveKit C++ SDK version to download") - -# Optional: enforce integrity if you have the sha256 for that specific asset. -# Users can pass: -# -DLIVEKIT_SDK_SHA256= -set(LIVEKIT_SDK_SHA256 "" CACHE STRING "Optional sha256 for the downloaded SDK archive") - -# Where to unpack inside the build directory -set(LIVEKIT_SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk" CACHE PATH "Where to unpack the LiveKit SDK") - -# -------- Detect platform triple -------- -# We’re detecting the *host* because these examples are built natively. -# (If you later want cross-compile, this should key off CMAKE_SYSTEM_* instead.) -set(_host_os "") -if(WIN32) - set(_host_os "windows") -elseif(APPLE) - set(_host_os "macos") -elseif(UNIX) - set(_host_os "linux") -else() - message(FATAL_ERROR "Unsupported host OS") -endif() - -set(_host_arch "") -if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$") - set(_host_arch "x64") -elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)$") - set(_host_arch "arm64") -else() - message(FATAL_ERROR "Unsupported host arch: ${CMAKE_HOST_SYSTEM_PROCESSOR}") -endif() - -# Map to your release artifact naming -# Adjust if you use different naming (e.g., macos-x86_64 vs macos-x64) -if(_host_os STREQUAL "macos" AND _host_arch STREQUAL "x64") - # If your asset is named macos-x86_64 instead, change this to "macos-x86_64" - set(SDK_TRIPLE "macos-x64") -else() - set(SDK_TRIPLE "${_host_os}-${_host_arch}") -endif() - -# -------- Compute archive name + URL -------- -if(_host_os STREQUAL "windows") - set(SDK_EXT "zip") -else() - set(SDK_EXT "tar.gz") -endif() - -set(SDK_ARCHIVE "livekit-sdk-${SDK_TRIPLE}-${LIVEKIT_SDK_VERSION}.${SDK_EXT}") -set(SDK_URL "https://github.com/livekit/client-sdk-cpp/releases/download/v${LIVEKIT_SDK_VERSION}/${SDK_ARCHIVE}") - -# Where to download -set(SDK_DL_DIR "${CMAKE_BINARY_DIR}/_downloads") -set(SDK_ARCHIVE_PATH "${SDK_DL_DIR}/${SDK_ARCHIVE}") - -# The extracted SDK root will look like: -# /livekit-sdk--/ -set(SDK_EXTRACTED_ROOT "${LIVEKIT_SDK_DIR}/livekit-sdk-${SDK_TRIPLE}-${LIVEKIT_SDK_VERSION}") +# Make "include(LiveKitSDK)" search in ./cmake +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -# -------- Download + extract (configure-time) -------- -file(MAKE_DIRECTORY "${SDK_DL_DIR}") -file(MAKE_DIRECTORY "${LIVEKIT_SDK_DIR}") +set(LIVEKIT_SDK_VERSION "latest" CACHE STRING "LiveKit C++ SDK version (e.g. 0.2.0 or latest)") -if(NOT EXISTS "${SDK_EXTRACTED_ROOT}") - message(STATUS "LiveKit SDK not found at: ${SDK_EXTRACTED_ROOT}") - message(STATUS "Downloading: ${SDK_URL}") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +include(LiveKitSDK) - if(LIVEKIT_SDK_SHA256 STREQUAL "") - file(DOWNLOAD - "${SDK_URL}" - "${SDK_ARCHIVE_PATH}" - SHOW_PROGRESS - STATUS _dl_status - TLS_VERIFY ON - ) - else() - file(DOWNLOAD - "${SDK_URL}" - "${SDK_ARCHIVE_PATH}" - SHOW_PROGRESS - STATUS _dl_status - TLS_VERIFY ON - EXPECTED_HASH "SHA256=${LIVEKIT_SDK_SHA256}" - ) - endif() - - list(GET _dl_status 0 _dl_code) - list(GET _dl_status 1 _dl_msg) - if(NOT _dl_code EQUAL 0) - message(FATAL_ERROR "Failed to download ${SDK_URL}\nStatus: ${_dl_code}\nMessage: ${_dl_msg}") - endif() - - message(STATUS "Extracting: ${SDK_ARCHIVE_PATH}") - - # Clean any previous partial extraction - file(REMOVE_RECURSE "${SDK_EXTRACTED_ROOT}") - - if(_host_os STREQUAL "windows") - # CMake can extract zip with -E tar - execute_process( - COMMAND "${CMAKE_COMMAND}" -E tar xvf "${SDK_ARCHIVE_PATH}" - WORKING_DIRECTORY "${LIVEKIT_SDK_DIR}" - RESULT_VARIABLE _xret - ) - else() - execute_process( - COMMAND "${CMAKE_COMMAND}" -E tar xvf "${SDK_ARCHIVE_PATH}" - WORKING_DIRECTORY "${LIVEKIT_SDK_DIR}" - RESULT_VARIABLE _xret - ) - endif() - - if(NOT _xret EQUAL 0) - message(FATAL_ERROR "Failed to extract ${SDK_ARCHIVE_PATH} (code=${_xret})") - endif() -endif() - -if(NOT EXISTS "${SDK_EXTRACTED_ROOT}/lib/cmake") - message(FATAL_ERROR - "Extracted SDK does not look valid (missing lib/cmake).\n" - "Expected root: ${SDK_EXTRACTED_ROOT}\n" - "If the archive root folder name differs, adjust SDK_EXTRACTED_ROOT logic." - ) -endif() - -# -------- Make find_package(LiveKit) work -------- -# Your SDK installs a config package under /lib/cmake/... -# The most robust approach is to push onto CMAKE_PREFIX_PATH. -list(PREPEND CMAKE_PREFIX_PATH "${SDK_EXTRACTED_ROOT}") - -# Some packages also require explicit *_DIR; keep as a fallback option: -# set(LiveKit_DIR "${SDK_EXTRACTED_ROOT}/lib/cmake/LiveKit" CACHE PATH "" FORCE) +livekit_sdk_setup( + VERSION "${LIVEKIT_SDK_VERSION}" + SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk" + GITHUB_TOKEN "$ENV{GITHUB_TOKEN}" +) find_package(LiveKit CONFIG REQUIRED) - -# -------- Build examples -------- add_subdirectory(basic_room) - diff --git a/README.md b/README.md index a8c50fa..4ddb858 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,73 @@ # cpp-example-collection -A collection of small examples for the LiveKit C++ SDK: https://github.com/livekit/client-sdk-cpp +This repository contains a collection of small, self-contained examples for the +[LiveKit C++ SDK](https://github.com/livekit/client-sdk-cpp). + +The goal of these examples is to demonstrate common usage patterns of the +LiveKit C++ SDK (connecting to a room, publishing tracks, RPC, data streams, +etc.) without requiring users to build the SDK from source. + + +## How the SDK is provided + +These examples **automatically download a prebuilt LiveKit C++ SDK release** +from GitHub at CMake configure time. + +This is handled by the cmake helper: [LiveKitSDK.cmake ](https://github.com/livekit-examples/cpp-example-collection/cmake/LiveKitSDK.cmake) + +## Selecting a LiveKit SDK version + +By default, the examples download the **latest released** LiveKit C++ SDK. + +You can pin a specific SDK version using the `LIVEKIT_SDK_VERSION` CMake option. + +### Examples + +Use the latest release: +```bash +cmake -S . -B build +# Or use a specific version (recommended for reproducibility): +cmake -S . -B build -DLIVEKIT_SDK_VERSION=0.2.0 +``` + +Reconfigure to change versions: +```bash +rm -rf build +cmake -S . -B build -DLIVEKIT_SDK_VERSION=0.3.1 +``` + + +### Building the examples +#### macOS / Linux +```bash +cmake -S . -B build # add -DLIVEKIT_SDK_VERSION=0.2.0 if using a specific version +cmake --build build +``` + +#### Windows (Visual Studio generator) +```powershell +cmake -S . -B build # add -DLIVEKIT_SDK_VERSION=0.2.0 if using a specific version +cmake --build build --config Release +``` + +The Livekit Release SDK is downloaded into **build/_deps/livekit-sdk/** + +### Running the examples + +After building, example binaries are located under: +```bash +build// +``` + +For example: +```bash +./build/basic_room/basic_room --url --token +``` + +### Supported platforms + +Prebuilt SDKs are downloaded automatically for: +* Windows: x64 +* macOS: x64, arm64 (Apple Silicon) +* Linux: x64 + +If no matching SDK is available for your platform, CMake configuration will fail with a clear error. \ No newline at end of file diff --git a/basic_room/CMakeLists.txt b/basic_room/CMakeLists.txt index dbea5e7..59f24f6 100644 --- a/basic_room/CMakeLists.txt +++ b/basic_room/CMakeLists.txt @@ -1,39 +1,45 @@ -add_executable(basic_room main.cpp capture_utils.cpp capture_utils.h) +add_executable(basic_room + main.cpp + capture_utils.cpp + capture_utils.h +) - find_package(Protobuf CONFIG REQUIRED) +target_include_directories(basic_room PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(basic_room PRIVATE LiveKit::livekit) - target_include_directories(basic_room PRIVATE ${ - CMAKE_CURRENT_SOURCE_DIR}) - target_link_libraries( - basic_room PRIVATE LiveKit::livekit protobuf::libprotobuf) +# Make -llivekit_ffi resolvable +get_filename_component(_lk_cmake_dir "${LiveKit_DIR}" DIRECTORY) # .../lib/cmake +get_filename_component(_lk_lib_dir "${_lk_cmake_dir}" DIRECTORY) # .../lib +target_link_directories(basic_room PRIVATE "${_lk_lib_dir}") -#Make - llivekit_ffi resolvable - get_filename_component(_lk_cmake_dir - "${LiveKit_DIR}" DIRECTORY) #... / - lib / - cmake - get_filename_component(_lk_lib_dir "${_lk_cmake_dir}" DIRECTORY) #... / - lib target_link_directories(basic_room PRIVATE "${_lk_lib_dir}") -#Nice - to - \ - have : copy runtime DLLs next to the exe on \ - Windows for "run from build dir". -#Only do this if your exported package provides these targets. - if (WIN32) -#livekit_ffi.dll - if (TARGET LiveKit::livekit_ffi) add_custom_command( - TARGET basic_room POST_BUILD COMMAND ${CMAKE_COMMAND} - - E copy_if_different "$" - "$") endif() -#If you also export protobuf / abseil runtime targets, copy them too(optional). - if (TARGET protobuf::libprotobuf) add_custom_command( - TARGET basic_room POST_BUILD COMMAND ${CMAKE_COMMAND} - - E copy_if_different "$" - "$") endif() +# Nice-to-have: copy runtime DLLs next to the exe on Windows for "run from build dir". +# Only do this if your exported package provides these targets. +if(WIN32) + # livekit_ffi.dll + if(TARGET LiveKit::livekit_ffi) + add_custom_command(TARGET basic_room POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$" + ) + endif() - if (TARGET absl::abseil_dll) add_custom_command( - TARGET basic_room POST_BUILD COMMAND ${CMAKE_COMMAND} - - E copy_if_different "$" - "$") - endif() endif() + # If you also export protobuf/abseil runtime targets, copy them too (optional). + if(TARGET protobuf::libprotobuf) + add_custom_command(TARGET basic_room POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$" + ) + endif() + + if(TARGET absl::abseil_dll) + add_custom_command(TARGET basic_room POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$" + ) + endif() +endif() diff --git a/basic_room/capture_utils.cpp b/basic_room/capture_utils.cpp index 8db8703..08497b0 100644 --- a/basic_room/capture_utils.cpp +++ b/basic_room/capture_utils.cpp @@ -72,7 +72,7 @@ void runNoiseCaptureLoop(const std::shared_ptr &source, // Fake video source: solid color cycling void runFakeVideoCaptureLoop(const std::shared_ptr &source, std::atomic &running_flag) { - auto frame = LKVideoFrame::create(1280, 720, VideoBufferType::RGBA); + auto frame = VideoFrame::create(1280, 720, VideoBufferType::RGBA); const double framerate = 1.0 / 30.0; while (running_flag.load(std::memory_order_relaxed)) { diff --git a/basic_room/main.cpp b/basic_room/main.cpp index 26ca17d..dbb7acc 100644 --- a/basic_room/main.cpp +++ b/basic_room/main.cpp @@ -128,7 +128,7 @@ int main(int argc, char *argv[]) { } // ---- Create & publish VIDEO (fake RGB) ---- - // Your helper uses LKVideoFrame::create(1280, 720, BGRA), so match that. + // Your helper uses VideoFrame::create(1280, 720, BGRA), so match that. auto videoSource = std::make_shared(1280, 720); auto videoTrack = LocalVideoTrack::createLocalVideoTrack("rgb", videoSource); diff --git a/cmake/LiveKitSDK.cmake b/cmake/LiveKitSDK.cmake index 55edb01..4f24142 100644 --- a/cmake/LiveKitSDK.cmake +++ b/cmake/LiveKitSDK.cmake @@ -10,8 +10,11 @@ # include(LiveKitSDK) # livekit_sdk_setup(VERSION "0.1.9" SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk") # +# Latest: +# livekit_sdk_setup(VERSION "latest" SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk") +# # Optional: -# livekit_sdk_setup(VERSION "0.1.9" REPO "livekit/client-sdk-cpp" SHA256 "") +# livekit_sdk_setup(VERSION "latest" REPO "livekit/client-sdk-cpp" GITHUB_TOKEN "$ENV{GITHUB_TOKEN}") include_guard(GLOBAL) @@ -41,7 +44,6 @@ endfunction() function(_lk_default_triple out_triple) _lk_detect_host(_os _arch) - set(_triple "${_os}-${_arch}") set(${out_triple} "${_triple}" PARENT_SCOPE) endfunction() @@ -55,24 +57,72 @@ function(_lk_archive_ext out_ext) endif() endfunction() +# Resolve VERSION="latest" via GitHub API, returning a version without leading "v". +function(_lk_resolve_latest_version out_version repo download_dir github_token) + if(NOT download_dir) + set(download_dir "${CMAKE_BINARY_DIR}/_downloads") + endif() + file(MAKE_DIRECTORY "${download_dir}") + + set(_api "https://api.github.com/repos/${repo}/releases/latest") + set(_json "${download_dir}/livekit_latest_release_${repo}.json") + string(REPLACE "/" "_" _json "${_json}") # sanitize filename + + set(_headers "User-Agent: cmake-livekit-sdk/1.0") + if(NOT "${github_token}" STREQUAL "") + list(APPEND _headers "Authorization: Bearer ${github_token}") + endif() + + file(DOWNLOAD + "${_api}" "${_json}" + TLS_VERIFY ON + HTTPHEADER ${_headers} + STATUS _st + ) + list(GET _st 0 _code) + list(GET _st 1 _msg) + if(NOT _code EQUAL 0) + message(FATAL_ERROR + "LiveKitSDK: failed to query latest release from GitHub API\n" + "API: ${_api}\n" + "Status: ${_code}\n" + "Message: ${_msg}\n" + "Tip: set GITHUB_TOKEN to avoid rate limits." + ) + endif() + + file(READ "${_json}" _content) + + # CMake >= 3.19 supports string(JSON ...) + string(JSON _tag GET "${_content}" tag_name) + if(_tag STREQUAL "") + message(FATAL_ERROR "LiveKitSDK: GitHub API response missing tag_name") + endif() + + # Strip leading "v" if present (v0.2.0 -> 0.2.0) + string(REGEX REPLACE "^v" "" _ver "${_tag}") + set(${out_version} "${_ver}" PARENT_SCOPE) +endfunction() + # Public: # livekit_sdk_setup( -# VERSION +# VERSION # SDK_DIR # [REPO ] default: livekit/client-sdk-cpp -# [SHA256 ] optional: verify download +# [SHA256 ] optional: verify download (only works for fixed VERSION) # [TRIPLE ] optional override # [DOWNLOAD_DIR ] default: /_downloads +# [GITHUB_TOKEN ] optional: auth for GitHub API when VERSION=latest # [NO_DOWNLOAD] error if not already present # ) function(livekit_sdk_setup) set(options NO_DOWNLOAD) - set(oneValueArgs VERSION SDK_DIR REPO SHA256 TRIPLE DOWNLOAD_DIR) + set(oneValueArgs VERSION SDK_DIR REPO SHA256 TRIPLE DOWNLOAD_DIR GITHUB_TOKEN) set(multiValueArgs) cmake_parse_arguments(LK "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if(NOT LK_VERSION) - message(FATAL_ERROR "livekit_sdk_setup: VERSION is required") + message(FATAL_ERROR "livekit_sdk_setup: VERSION is required (use \"latest\" if desired)") endif() if(NOT LK_SDK_DIR) @@ -83,23 +133,41 @@ function(livekit_sdk_setup) 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}") + # If VERSION is "latest", resolve it first. + set(_resolved_version "${LK_VERSION}") + if(LK_VERSION STREQUAL "latest") + # SHA256 cannot be reliably used with "latest" (changes over time). + if(LK_SHA256) + message(WARNING "LiveKitSDK: SHA256 was provided but VERSION=latest; ignoring SHA256.") + set(LK_SHA256 "") + endif() - if(NOT LK_DOWNLOAD_DIR) - set(LK_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/_downloads") + if(NOT LK_GITHUB_TOKEN) + # Try env var by default (common in GitHub Actions). + set(LK_GITHUB_TOKEN "$ENV{GITHUB_TOKEN}") + endif() + + _lk_resolve_latest_version(_resolved_version "${LK_REPO}" "${LK_DOWNLOAD_DIR}" "${LK_GITHUB_TOKEN}") + message(STATUS "LiveKitSDK: resolved latest version = ${_resolved_version}") endif() + _lk_archive_ext(_ext) + set(_archive "livekit-sdk-${LK_TRIPLE}-${_resolved_version}.${_ext}") + set(_url "https://github.com/${LK_REPO}/releases/download/v${_resolved_version}/${_archive}") + set(_dl_dir "${LK_DOWNLOAD_DIR}") set(_archive_path "${_dl_dir}/${_archive}") # Extracted root folder name (matches your bundle root) - set(_extracted_root "${LK_SDK_DIR}/livekit-sdk-${LK_TRIPLE}-${LK_VERSION}") + set(_extracted_root "${LK_SDK_DIR}/livekit-sdk-${LK_TRIPLE}-${_resolved_version}") file(MAKE_DIRECTORY "${_dl_dir}") file(MAKE_DIRECTORY "${LK_SDK_DIR}") @@ -137,14 +205,10 @@ function(livekit_sdk_setup) message(STATUS "LiveKitSDK: extracting ${_archive_path}") file(REMOVE_RECURSE "${_extracted_root}") - execute_process( - COMMAND "${CMAKE_COMMAND}" -E tar xvf "${_archive_path}" - WORKING_DIRECTORY "${LK_SDK_DIR}" - RESULT_VARIABLE _xret + file(ARCHIVE_EXTRACT + INPUT "${_archive_path}" + DESTINATION "${LK_SDK_DIR}" ) - if(NOT _xret EQUAL 0) - message(FATAL_ERROR "LiveKitSDK: extraction failed (${_xret}) for ${_archive_path}") - endif() endif() if(NOT EXISTS "${_extracted_root}/lib/cmake") @@ -157,11 +221,16 @@ function(livekit_sdk_setup) # Make find_package(LiveKit CONFIG REQUIRED) work. list(PREPEND CMAKE_PREFIX_PATH "${_extracted_root}") + set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH}" PARENT_SCOPE) + + # Direct hint to the package config dir + set(LiveKit_DIR "${_extracted_root}/lib/cmake/LiveKit" PARENT_SCOPE) # Export a few useful variables for callers (optional). set(LIVEKIT_SDK_EXTRACTED_ROOT "${_extracted_root}" CACHE PATH "LiveKit SDK extracted root" FORCE) set(LIVEKIT_SDK_URL_USED "${_url}" CACHE STRING "LiveKit SDK URL used" FORCE) + set(LIVEKIT_SDK_VERSION_RESOLVED "${_resolved_version}" CACHE STRING "LiveKit SDK resolved version" FORCE) + set(LIVEKIT_SDK_TRIPLE_USED "${LK_TRIPLE}" CACHE STRING "LiveKit SDK triple used" FORCE) message(STATUS "LiveKitSDK: using SDK at ${_extracted_root}") endfunction() - From a3642b0949a8e5fb9d3f4947cf7b78efe882e40e Mon Sep 17 00:00:00 2001 From: shijing xian Date: Wed, 14 Jan 2026 19:54:01 +0800 Subject: [PATCH 03/10] trigger CI From 7a47d5c1e08da2f232ef8e71799cacab52186766 Mon Sep 17 00:00:00 2001 From: shijing xian Date: Wed, 14 Jan 2026 19:57:12 +0800 Subject: [PATCH 04/10] workflow -> workflows --- .github/{workflow => workflows}/builds.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{workflow => workflows}/builds.yml (100%) diff --git a/.github/workflow/builds.yml b/.github/workflows/builds.yml similarity index 100% rename from .github/workflow/builds.yml rename to .github/workflows/builds.yml From b35dde36bd2a0274b3b24ddecfd591a5374cca99 Mon Sep 17 00:00:00 2001 From: shijing xian Date: Wed, 14 Jan 2026 20:15:13 +0800 Subject: [PATCH 05/10] fix the CI --- cmake/LiveKitSDK.cmake | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/cmake/LiveKitSDK.cmake b/cmake/LiveKitSDK.cmake index 4f24142..0c23ac2 100644 --- a/cmake/LiveKitSDK.cmake +++ b/cmake/LiveKitSDK.cmake @@ -65,20 +65,32 @@ function(_lk_resolve_latest_version out_version repo download_dir github_token) file(MAKE_DIRECTORY "${download_dir}") set(_api "https://api.github.com/repos/${repo}/releases/latest") - set(_json "${download_dir}/livekit_latest_release_${repo}.json") - string(REPLACE "/" "_" _json "${_json}") # sanitize filename - set(_headers "User-Agent: cmake-livekit-sdk/1.0") + # Sanitize only the repo part (NOT the whole path) + string(REPLACE "/" "_" _repo_sanitized "${repo}") + set(_json "${download_dir}/livekit_latest_release_${_repo_sanitized}.json") + + # Build headers as a proper argument list (quoted per header) + set(_headers + "User-Agent: cmake-livekit-sdk/1.0" + "Accept: application/vnd.github+json" + ) if(NOT "${github_token}" STREQUAL "") list(APPEND _headers "Authorization: Bearer ${github_token}") endif() - file(DOWNLOAD - "${_api}" "${_json}" + # Pass headers safely: keep each header as ONE argument + set(_download_args TLS_VERIFY ON - HTTPHEADER ${_headers} STATUS _st ) + list(APPEND _download_args HTTPHEADER) + foreach(h IN LISTS _headers) + list(APPEND _download_args "${h}") + endforeach() + + file(DOWNLOAD "${_api}" "${_json}" ${_download_args}) + list(GET _st 0 _code) list(GET _st 1 _msg) if(NOT _code EQUAL 0) @@ -104,6 +116,7 @@ function(_lk_resolve_latest_version out_version repo download_dir github_token) set(${out_version} "${_ver}" PARENT_SCOPE) endfunction() + # Public: # livekit_sdk_setup( # VERSION From 8add9f7333ff604ae3edeb5443e1f46b13856c96 Mon Sep 17 00:00:00 2001 From: shijing xian Date: Wed, 14 Jan 2026 20:26:07 +0800 Subject: [PATCH 06/10] fix windows bot --- .github/workflows/builds.yml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 4b29281..ab68aa8 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -8,7 +8,6 @@ on: workflow_dispatch: env: - # Used by LiveKitSDK.cmake when VERSION=latest (it looks for env GITHUB_TOKEN by default) GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: @@ -58,7 +57,12 @@ jobs: run: | choco install -y cmake --installargs 'ADD_CMAKE_TO_PATH=System' choco install -y ninja - # curl is available on windows-latest; no need for jq/unzip since CMake does the download/extract. + + - name: Setup MSVC (Windows) + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 # ---------- configure + build ---------- - name: Configure (Unix) @@ -66,8 +70,7 @@ jobs: shell: bash run: | set -eux - cmake -S . -B build -G Ninja \ - -DCMAKE_BUILD_TYPE=Release + cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release - name: Build (Unix) if: runner.os != 'Windows' @@ -80,7 +83,10 @@ jobs: if: runner.os == 'Windows' shell: pwsh run: | - cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release + cmake -S . -B build -G Ninja ` + -DCMAKE_BUILD_TYPE=Release ` + -DCMAKE_C_COMPILER=cl ` + -DCMAKE_CXX_COMPILER=cl - name: Build (Windows) if: runner.os == 'Windows' @@ -89,14 +95,11 @@ jobs: cmake --build build --config Release # ---------- smoke test ---------- - # LiveKitSDK.cmake typically extracts into: build/_deps/livekit-sdk/ - # We’ll locate the extracted SDK root robustly and set runtime env vars from it. - name: Smoke test (Linux/macOS) if: runner.os != 'Windows' shell: bash run: | set -eux - sdk_root="$(ls -d build/_deps/livekit-sdk/* | head -n 1)" echo "SDK root: ${sdk_root}" ls -la "${sdk_root}/lib" || true @@ -113,13 +116,11 @@ jobs: if: runner.os == 'Windows' shell: pwsh run: | - $sdkRoot = Get-ChildItem -Directory "build\_deps\livekit-sdk" | Select-Object -First 1 - if (-not $sdkRoot) { throw "SDK root not found under build\_deps\livekit-sdk" } + $sdkRoot = Get-ChildItem -Directory "build/_deps/livekit-sdk" | Select-Object -First 1 + if (-not $sdkRoot) { throw "SDK root not found under build/_deps/livekit-sdk" } Write-Host "SDK root: $($sdkRoot.FullName)" - # Prefer bin first; keep lib too (some packages put dlls in lib) $env:PATH = "$($sdkRoot.FullName)\bin;$($sdkRoot.FullName)\lib;$env:PATH" - .\build\basic_room.exe --help 2>$null # ---------- upload build output ---------- From 6725e92bd8f47a5ab71777b2e7c67afa128cf2fb Mon Sep 17 00:00:00 2001 From: shijing xian Date: Wed, 14 Jan 2026 21:32:47 +0800 Subject: [PATCH 07/10] another try fix the windows / macos CIs --- .github/workflows/builds.yml | 22 ++++++++- cmake/LiveKitSDK.cmake | 94 +++++++++++++++++++++--------------- 2 files changed, 76 insertions(+), 40 deletions(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index ab68aa8..8770372 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -116,12 +116,30 @@ jobs: if: runner.os == 'Windows' shell: pwsh run: | + # Locate SDK root $sdkRoot = Get-ChildItem -Directory "build/_deps/livekit-sdk" | Select-Object -First 1 - if (-not $sdkRoot) { throw "SDK root not found under build/_deps/livekit-sdk" } + if (-not $sdkRoot) { + throw "SDK root not found under build/_deps/livekit-sdk" + } Write-Host "SDK root: $($sdkRoot.FullName)" + # Make sure DLLs are found at runtime $env:PATH = "$($sdkRoot.FullName)\bin;$($sdkRoot.FullName)\lib;$env:PATH" - .\build\basic_room.exe --help 2>$null + + # Locate the built executable + $exe = Get-ChildItem -Recurse build -Filter basic_room.exe | Select-Object -First 1 + if (-not $exe) { + throw "basic_room.exe not found in build directory" + } + + Write-Host "Running $($exe.FullName) --help" + + # Try to execute it. We only care that it launches. + & $exe.FullName --help 2>$null + if ($LASTEXITCODE -ne 0) { + Write-Host "basic_room.exe exited with code $LASTEXITCODE (allowed for --help)" + } + # ---------- upload build output ---------- - name: Upload binary diff --git a/cmake/LiveKitSDK.cmake b/cmake/LiveKitSDK.cmake index 0c23ac2..fdf5db0 100644 --- a/cmake/LiveKitSDK.cmake +++ b/cmake/LiveKitSDK.cmake @@ -1,16 +1,15 @@ # LiveKitSDK.cmake # -# A small helper for example repos: +# Helper for example repos: # - Downloads the appropriate prebuilt LiveKit C++ SDK release asset for the host OS/arch -# - Extracts it into a local directory (default: build/_deps/livekit-sdk) -# - Prepends the extracted prefix to CMAKE_PREFIX_PATH so find_package(LiveKit CONFIG REQUIRED) works +# - Extracts it into a local directory (default: /_deps/livekit-sdk) +# - Prepends the extracted prefix to CMAKE_PREFIX_PATH so: +# find_package(LiveKit CONFIG REQUIRED) +# works out of the box. # # Usage: # list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # include(LiveKitSDK) -# livekit_sdk_setup(VERSION "0.1.9" SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk") -# -# Latest: # livekit_sdk_setup(VERSION "latest" SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk") # # Optional: @@ -18,6 +17,7 @@ include_guard(GLOBAL) +# -------------------- Host detection -------------------- function(_lk_detect_host out_os out_arch) if(WIN32) set(_os "windows") @@ -29,10 +29,13 @@ function(_lk_detect_host out_os out_arch) message(FATAL_ERROR "LiveKitSDK: unsupported host OS") endif() + # Use host processor; normalize common variants (case-insensitive) set(_proc "${CMAKE_HOST_SYSTEM_PROCESSOR}") - if(_proc MATCHES "^(x86_64|AMD64)$") + string(TOLOWER "${_proc}" _proc_l) + + if(_proc_l MATCHES "^(x86_64|amd64)$") set(_arch "x64") - elseif(_proc MATCHES "^(arm64|aarch64)$") + elseif(_proc_l MATCHES "^(arm64|aarch64)$") set(_arch "arm64") else() message(FATAL_ERROR "LiveKitSDK: unsupported host arch: ${_proc}") @@ -44,8 +47,7 @@ endfunction() function(_lk_default_triple out_triple) _lk_detect_host(_os _arch) - set(_triple "${_os}-${_arch}") - set(${out_triple} "${_triple}" PARENT_SCOPE) + set(${out_triple} "${_os}-${_arch}" PARENT_SCOPE) endfunction() function(_lk_archive_ext out_ext) @@ -57,7 +59,8 @@ function(_lk_archive_ext out_ext) endif() endfunction() -# Resolve VERSION="latest" via GitHub API, returning a version without leading "v". +# -------------------- GitHub API helpers -------------------- +# Resolve VERSION="latest" via GitHub API, returning version without leading "v". function(_lk_resolve_latest_version out_version repo download_dir github_token) if(NOT download_dir) set(download_dir "${CMAKE_BINARY_DIR}/_downloads") @@ -66,40 +69,56 @@ function(_lk_resolve_latest_version out_version repo download_dir github_token) set(_api "https://api.github.com/repos/${repo}/releases/latest") - # Sanitize only the repo part (NOT the whole path) + # Sanitize repo for filename string(REPLACE "/" "_" _repo_sanitized "${repo}") set(_json "${download_dir}/livekit_latest_release_${_repo_sanitized}.json") - # Build headers as a proper argument list (quoted per header) + # Build headers as a proper LIST (each element is one full header line) set(_headers "User-Agent: cmake-livekit-sdk/1.0" "Accept: application/vnd.github+json" + "X-GitHub-Api-Version: 2022-11-28" ) + + # Strip token (defensive: avoids accidental newline causing header splitting) + if(NOT "${github_token}" STREQUAL "") + string(STRIP "${github_token}" github_token) + endif() + + # Use Authorization only if token is non-empty if(NOT "${github_token}" STREQUAL "") - list(APPEND _headers "Authorization: Bearer ${github_token}") + # "token" is broadly compatible + list(APPEND _headers "Authorization: token ${github_token}") + else() + message(STATUS "LiveKitSDK: no GITHUB_TOKEN provided; GitHub API may rate-limit.") endif() - # Pass headers safely: keep each header as ONE argument - set(_download_args + # Capture LOG for actionable failure output + set(_dl_args TLS_VERIFY ON STATUS _st + LOG _log ) - list(APPEND _download_args HTTPHEADER) - foreach(h IN LISTS _headers) - list(APPEND _download_args "${h}") + list(APPEND _dl_args HTTPHEADER) + foreach(_h IN LISTS _headers) + list(APPEND _dl_args "${_h}") endforeach() - - file(DOWNLOAD "${_api}" "${_json}" ${_download_args}) + file(DOWNLOAD "${_api}" "${_json}" ${_dl_args}) list(GET _st 0 _code) list(GET _st 1 _msg) if(NOT _code EQUAL 0) + message(STATUS "LiveKitSDK: GitHub API download log:\n${_log}") + if(EXISTS "${_json}") + file(READ "${_json}" _body) + message(STATUS "LiveKitSDK: GitHub API response body:\n${_body}") + endif() message(FATAL_ERROR "LiveKitSDK: failed to query latest release from GitHub API\n" "API: ${_api}\n" "Status: ${_code}\n" "Message: ${_msg}\n" - "Tip: set GITHUB_TOKEN to avoid rate limits." + "Tip: set GITHUB_TOKEN to avoid rate limits, or use VERSION=." ) endif() @@ -111,13 +130,12 @@ function(_lk_resolve_latest_version out_version repo download_dir github_token) message(FATAL_ERROR "LiveKitSDK: GitHub API response missing tag_name") endif() - # Strip leading "v" if present (v0.2.0 -> 0.2.0) + # Strip leading "v" if present string(REGEX REPLACE "^v" "" _ver "${_tag}") set(${out_version} "${_ver}" PARENT_SCOPE) endfunction() - -# Public: +# -------------------- Public entrypoint -------------------- # livekit_sdk_setup( # VERSION # SDK_DIR @@ -131,13 +149,11 @@ endfunction() function(livekit_sdk_setup) set(options NO_DOWNLOAD) set(oneValueArgs VERSION SDK_DIR REPO SHA256 TRIPLE DOWNLOAD_DIR GITHUB_TOKEN) - set(multiValueArgs) - cmake_parse_arguments(LK "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + cmake_parse_arguments(LK "${options}" "${oneValueArgs}" "" ${ARGN}) if(NOT LK_VERSION) message(FATAL_ERROR "livekit_sdk_setup: VERSION is required (use \"latest\" if desired)") endif() - if(NOT LK_SDK_DIR) message(FATAL_ERROR "livekit_sdk_setup: SDK_DIR is required") endif() @@ -145,26 +161,23 @@ function(livekit_sdk_setup) 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() - # If VERSION is "latest", resolve it first. + # Resolve latest tag if requested set(_resolved_version "${LK_VERSION}") if(LK_VERSION STREQUAL "latest") - # SHA256 cannot be reliably used with "latest" (changes over time). if(LK_SHA256) message(WARNING "LiveKitSDK: SHA256 was provided but VERSION=latest; ignoring SHA256.") set(LK_SHA256 "") endif() if(NOT LK_GITHUB_TOKEN) - # Try env var by default (common in GitHub Actions). + # Common in CI if you set env: GITHUB_TOKEN: ${{ github.token }} set(LK_GITHUB_TOKEN "$ENV{GITHUB_TOKEN}") endif() @@ -176,13 +189,13 @@ function(livekit_sdk_setup) set(_archive "livekit-sdk-${LK_TRIPLE}-${_resolved_version}.${_ext}") set(_url "https://github.com/${LK_REPO}/releases/download/v${_resolved_version}/${_archive}") - set(_dl_dir "${LK_DOWNLOAD_DIR}") - set(_archive_path "${_dl_dir}/${_archive}") + set(_archive_path "${LK_DOWNLOAD_DIR}/${_archive}") - # Extracted root folder name (matches your bundle root) + # The archive is expected to contain a top-level folder named: + # livekit-sdk--/ set(_extracted_root "${LK_SDK_DIR}/livekit-sdk-${LK_TRIPLE}-${_resolved_version}") - file(MAKE_DIRECTORY "${_dl_dir}") + file(MAKE_DIRECTORY "${LK_DOWNLOAD_DIR}") file(MAKE_DIRECTORY "${LK_SDK_DIR}") if(NOT EXISTS "${_extracted_root}") @@ -194,30 +207,35 @@ function(livekit_sdk_setup) 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) + message(STATUS "LiveKitSDK: download log:\n${_log}") message(FATAL_ERROR "LiveKitSDK: download failed\nURL: ${_url}\nStatus: ${_code}\nMessage: ${_msg}") endif() - message(STATUS "LiveKitSDK: extracting ${_archive_path}") + # 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}" From e97914375bc5729bc18d68f942bc050d3ea9973d Mon Sep 17 00:00:00 2001 From: shijing xian Date: Wed, 14 Jan 2026 21:47:09 +0800 Subject: [PATCH 08/10] fix the smoke tests --- .github/workflows/builds.yml | 28 +++++++++++++++++----------- basic_room/main.cpp | 16 ++++++++++++++-- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 8770372..8cd8ae2 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -99,18 +99,26 @@ jobs: if: runner.os != 'Windows' shell: bash run: | - set -eux + set -euo pipefail + sdk_root="$(ls -d build/_deps/livekit-sdk/* | head -n 1)" echo "SDK root: ${sdk_root}" ls -la "${sdk_root}/lib" || true - + # Locate executable (it may not be directly under build/) + exe="$(find build -type f -name basic_room -perm -111 | head -n 1)" + if [[ -z "${exe}" ]]; then + echo "basic_room executable not found under build/" + find build -maxdepth 3 -type f -print + exit 1 + fi + echo "Running: ${exe} --self-test" if [[ "$RUNNER_OS" == "Linux" ]]; then export LD_LIBRARY_PATH="${sdk_root}/lib:${LD_LIBRARY_PATH:-}" else export DYLD_LIBRARY_PATH="${sdk_root}/lib:${DYLD_LIBRARY_PATH:-}" fi + "${exe}" --self-test - ./build/basic_room --help || true - name: Smoke test (Windows) if: runner.os == 'Windows' @@ -122,24 +130,22 @@ jobs: throw "SDK root not found under build/_deps/livekit-sdk" } Write-Host "SDK root: $($sdkRoot.FullName)" - # Make sure DLLs are found at runtime $env:PATH = "$($sdkRoot.FullName)\bin;$($sdkRoot.FullName)\lib;$env:PATH" - # Locate the built executable $exe = Get-ChildItem -Recurse build -Filter basic_room.exe | Select-Object -First 1 if (-not $exe) { throw "basic_room.exe not found in build directory" } - Write-Host "Running $($exe.FullName) --help" - # Try to execute it. We only care that it launches. - & $exe.FullName --help 2>$null - if ($LASTEXITCODE -ne 0) { - Write-Host "basic_room.exe exited with code $LASTEXITCODE (allowed for --help)" + $out = & $exe.FullName --self-test 2>&1 + $code = $LASTEXITCODE + if ($code -ne 0) { + Write-Host $out + throw "basic_room.exe --self-test failed with exit code $code" } - + Write-Host $out # ---------- upload build output ---------- - name: Upload binary diff --git a/basic_room/main.cpp b/basic_room/main.cpp index dbb7acc..daaa9ca 100644 --- a/basic_room/main.cpp +++ b/basic_room/main.cpp @@ -41,12 +41,17 @@ void printUsage(const char *prog) { << " LIVEKIT_URL, LIVEKIT_TOKEN\n"; } -bool parseArgs(int argc, char *argv[], std::string &url, std::string &token) { +bool parseArgs(int argc, char *argv[], std::string &url, std::string &token, bool &self_test) { for (int i = 1; i < argc; ++i) { const std::string a = argv[i]; if (a == "-h" || a == "--help") return false; + if (a == "--self-test") { + self_test = true; + return true; + } + auto take = [&](std::string &out) -> bool { if (i + 1 >= argc) return false; @@ -83,10 +88,17 @@ bool parseArgs(int argc, char *argv[], std::string &url, std::string &token) { int main(int argc, char *argv[]) { std::string url, token; - if (!parseArgs(argc, argv, url, token)) { + bool self_test = false; + if (!parseArgs(argc, argv, url, token, self_test)) { printUsage(argv[0]); return 1; } + if (self_test) { + livekit::initialize(livekit::LogSink::kConsole); + livekit::shutdown(); + std::cout << "self-test ok" << std::endl; + return 0; + } std::signal(SIGINT, handleSignal); From 554ff87ffe24bcfb7dcf8ffd531cd719a76da69a Mon Sep 17 00:00:00 2001 From: shijing xian Date: Wed, 14 Jan 2026 21:55:07 +0800 Subject: [PATCH 09/10] fix the github token issue --- .github/workflows/builds.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 8cd8ae2..3b83fb1 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -68,6 +68,8 @@ jobs: - name: Configure (Unix) if: runner.os != 'Windows' shell: bash + env: + GITHUB_TOKEN: ${{ github.token }} run: | set -eux cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release From 95fad02112e359cad56fef3d085ebc4065eb252e Mon Sep 17 00:00:00 2001 From: shijing xian Date: Wed, 14 Jan 2026 22:03:43 +0800 Subject: [PATCH 10/10] fix the flaky macos CI --- cmake/LiveKitSDK.cmake | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmake/LiveKitSDK.cmake b/cmake/LiveKitSDK.cmake index fdf5db0..02d36fa 100644 --- a/cmake/LiveKitSDK.cmake +++ b/cmake/LiveKitSDK.cmake @@ -88,7 +88,7 @@ function(_lk_resolve_latest_version out_version repo download_dir github_token) # Use Authorization only if token is non-empty if(NOT "${github_token}" STREQUAL "") # "token" is broadly compatible - list(APPEND _headers "Authorization: token ${github_token}") + list(APPEND _headers "Authorization: Bearer ${github_token}") else() message(STATUS "LiveKitSDK: no GITHUB_TOKEN provided; GitHub API may rate-limit.") endif() @@ -99,9 +99,8 @@ function(_lk_resolve_latest_version out_version repo download_dir github_token) STATUS _st LOG _log ) - list(APPEND _dl_args HTTPHEADER) foreach(_h IN LISTS _headers) - list(APPEND _dl_args "${_h}") + list(APPEND _dl_args HTTPHEADER "${_h}") endforeach() file(DOWNLOAD "${_api}" "${_json}" ${_dl_args})