Skip to content

Commit 7dde7cb

Browse files
added basic_room
1 parent 164d69d commit 7dde7cb

7 files changed

Lines changed: 721 additions & 0 deletions

File tree

.gitignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.cache/
2+
CMakeFiles/
3+
CMakeCache.txt
4+
.DS_Store
5+
Makefile
6+
cmake_install.cmake
7+
out
8+
build/
9+
build-debug/
10+
build-release/
11+
vcpkg_installed/
12+
received_green.avif
13+
docs/*.bak
14+
docs/html/
15+
docs/latex/
16+
.vs/
17+
.vscode/
18+
# Compiled output
19+
bin/
20+
lib/
21+
*.lib
22+
*.a
23+
*.so
24+
*.dylib
25+
*.dll
26+
*.exe

CMakeLists.txt

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
cmake_minimum_required(VERSION 3.20)
2+
project(livekit_cpp_example_collection LANGUAGES CXX)
3+
4+
set(CMAKE_CXX_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
7+
# -------- User knobs --------
8+
# Default to the version you mentioned. Users can override:
9+
# cmake -S . -B build -DLIVEKIT_SDK_VERSION=0.1.9
10+
set(LIVEKIT_SDK_VERSION "0.1.9" CACHE STRING "LiveKit C++ SDK version to download")
11+
12+
# Optional: enforce integrity if you have the sha256 for that specific asset.
13+
# Users can pass:
14+
# -DLIVEKIT_SDK_SHA256=<hash>
15+
set(LIVEKIT_SDK_SHA256 "" CACHE STRING "Optional sha256 for the downloaded SDK archive")
16+
17+
# Where to unpack inside the build directory
18+
set(LIVEKIT_SDK_DIR "${CMAKE_BINARY_DIR}/_deps/livekit-sdk" CACHE PATH "Where to unpack the LiveKit SDK")
19+
20+
# -------- Detect platform triple --------
21+
# We’re detecting the *host* because these examples are built natively.
22+
# (If you later want cross-compile, this should key off CMAKE_SYSTEM_* instead.)
23+
set(_host_os "")
24+
if(WIN32)
25+
set(_host_os "windows")
26+
elseif(APPLE)
27+
set(_host_os "macos")
28+
elseif(UNIX)
29+
set(_host_os "linux")
30+
else()
31+
message(FATAL_ERROR "Unsupported host OS")
32+
endif()
33+
34+
set(_host_arch "")
35+
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$")
36+
set(_host_arch "x64")
37+
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)$")
38+
set(_host_arch "arm64")
39+
else()
40+
message(FATAL_ERROR "Unsupported host arch: ${CMAKE_HOST_SYSTEM_PROCESSOR}")
41+
endif()
42+
43+
# Map to your release artifact naming
44+
# Adjust if you use different naming (e.g., macos-x86_64 vs macos-x64)
45+
if(_host_os STREQUAL "macos" AND _host_arch STREQUAL "x64")
46+
# If your asset is named macos-x86_64 instead, change this to "macos-x86_64"
47+
set(SDK_TRIPLE "macos-x64")
48+
else()
49+
set(SDK_TRIPLE "${_host_os}-${_host_arch}")
50+
endif()
51+
52+
# -------- Compute archive name + URL --------
53+
if(_host_os STREQUAL "windows")
54+
set(SDK_EXT "zip")
55+
else()
56+
set(SDK_EXT "tar.gz")
57+
endif()
58+
59+
set(SDK_ARCHIVE "livekit-sdk-${SDK_TRIPLE}-${LIVEKIT_SDK_VERSION}.${SDK_EXT}")
60+
set(SDK_URL "https://github.com/livekit/client-sdk-cpp/releases/download/v${LIVEKIT_SDK_VERSION}/${SDK_ARCHIVE}")
61+
62+
# Where to download
63+
set(SDK_DL_DIR "${CMAKE_BINARY_DIR}/_downloads")
64+
set(SDK_ARCHIVE_PATH "${SDK_DL_DIR}/${SDK_ARCHIVE}")
65+
66+
# The extracted SDK root will look like:
67+
# <LIVEKIT_SDK_DIR>/livekit-sdk-<triple>-<ver>/
68+
set(SDK_EXTRACTED_ROOT "${LIVEKIT_SDK_DIR}/livekit-sdk-${SDK_TRIPLE}-${LIVEKIT_SDK_VERSION}")
69+
70+
# -------- Download + extract (configure-time) --------
71+
file(MAKE_DIRECTORY "${SDK_DL_DIR}")
72+
file(MAKE_DIRECTORY "${LIVEKIT_SDK_DIR}")
73+
74+
if(NOT EXISTS "${SDK_EXTRACTED_ROOT}")
75+
message(STATUS "LiveKit SDK not found at: ${SDK_EXTRACTED_ROOT}")
76+
message(STATUS "Downloading: ${SDK_URL}")
77+
78+
if(LIVEKIT_SDK_SHA256 STREQUAL "")
79+
file(DOWNLOAD
80+
"${SDK_URL}"
81+
"${SDK_ARCHIVE_PATH}"
82+
SHOW_PROGRESS
83+
STATUS _dl_status
84+
TLS_VERIFY ON
85+
)
86+
else()
87+
file(DOWNLOAD
88+
"${SDK_URL}"
89+
"${SDK_ARCHIVE_PATH}"
90+
SHOW_PROGRESS
91+
STATUS _dl_status
92+
TLS_VERIFY ON
93+
EXPECTED_HASH "SHA256=${LIVEKIT_SDK_SHA256}"
94+
)
95+
endif()
96+
97+
list(GET _dl_status 0 _dl_code)
98+
list(GET _dl_status 1 _dl_msg)
99+
if(NOT _dl_code EQUAL 0)
100+
message(FATAL_ERROR "Failed to download ${SDK_URL}\nStatus: ${_dl_code}\nMessage: ${_dl_msg}")
101+
endif()
102+
103+
message(STATUS "Extracting: ${SDK_ARCHIVE_PATH}")
104+
105+
# Clean any previous partial extraction
106+
file(REMOVE_RECURSE "${SDK_EXTRACTED_ROOT}")
107+
108+
if(_host_os STREQUAL "windows")
109+
# CMake can extract zip with -E tar
110+
execute_process(
111+
COMMAND "${CMAKE_COMMAND}" -E tar xvf "${SDK_ARCHIVE_PATH}"
112+
WORKING_DIRECTORY "${LIVEKIT_SDK_DIR}"
113+
RESULT_VARIABLE _xret
114+
)
115+
else()
116+
execute_process(
117+
COMMAND "${CMAKE_COMMAND}" -E tar xvf "${SDK_ARCHIVE_PATH}"
118+
WORKING_DIRECTORY "${LIVEKIT_SDK_DIR}"
119+
RESULT_VARIABLE _xret
120+
)
121+
endif()
122+
123+
if(NOT _xret EQUAL 0)
124+
message(FATAL_ERROR "Failed to extract ${SDK_ARCHIVE_PATH} (code=${_xret})")
125+
endif()
126+
endif()
127+
128+
if(NOT EXISTS "${SDK_EXTRACTED_ROOT}/lib/cmake")
129+
message(FATAL_ERROR
130+
"Extracted SDK does not look valid (missing lib/cmake).\n"
131+
"Expected root: ${SDK_EXTRACTED_ROOT}\n"
132+
"If the archive root folder name differs, adjust SDK_EXTRACTED_ROOT logic."
133+
)
134+
endif()
135+
136+
# -------- Make find_package(LiveKit) work --------
137+
# Your SDK installs a config package under <prefix>/lib/cmake/...
138+
# The most robust approach is to push <prefix> onto CMAKE_PREFIX_PATH.
139+
list(PREPEND CMAKE_PREFIX_PATH "${SDK_EXTRACTED_ROOT}")
140+
141+
# Some packages also require explicit *_DIR; keep as a fallback option:
142+
# set(LiveKit_DIR "${SDK_EXTRACTED_ROOT}/lib/cmake/LiveKit" CACHE PATH "" FORCE)
143+
144+
find_package(LiveKit CONFIG REQUIRED)
145+
146+
# -------- Build examples --------
147+
add_subdirectory(basic_room)
148+

basic_room/CMakeLists.txt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
add_executable(basic_room main.cpp capture_utils.cpp capture_utils.h)
2+
3+
find_package(Protobuf CONFIG REQUIRED)
4+
5+
target_include_directories(basic_room PRIVATE ${
6+
CMAKE_CURRENT_SOURCE_DIR})
7+
target_link_libraries(
8+
basic_room PRIVATE LiveKit::livekit protobuf::libprotobuf)
9+
10+
#Make - llivekit_ffi resolvable
11+
get_filename_component(_lk_cmake_dir
12+
"${LiveKit_DIR}" DIRECTORY) #... /
13+
lib /
14+
cmake
15+
get_filename_component(_lk_lib_dir "${_lk_cmake_dir}" DIRECTORY) #... /
16+
lib target_link_directories(basic_room PRIVATE "${_lk_lib_dir}")
17+
18+
#Nice - to - \
19+
have : copy runtime DLLs next to the exe on \
20+
Windows for "run from build dir".
21+
#Only do this if your exported package provides these targets.
22+
if (WIN32)
23+
#livekit_ffi.dll
24+
if (TARGET LiveKit::livekit_ffi) add_custom_command(
25+
TARGET basic_room POST_BUILD COMMAND ${CMAKE_COMMAND} -
26+
E copy_if_different "$<TARGET_FILE:LiveKit::livekit_ffi>"
27+
"$<TARGET_FILE_DIR:basic_room>") endif()
28+
29+
#If you also export protobuf / abseil runtime targets, copy them too(optional).
30+
if (TARGET protobuf::libprotobuf) add_custom_command(
31+
TARGET basic_room POST_BUILD COMMAND ${CMAKE_COMMAND} -
32+
E copy_if_different "$<TARGET_FILE:protobuf::libprotobuf>"
33+
"$<TARGET_FILE_DIR:basic_room>") endif()
34+
35+
if (TARGET absl::abseil_dll) add_custom_command(
36+
TARGET basic_room POST_BUILD COMMAND ${CMAKE_COMMAND} -
37+
E copy_if_different "$<TARGET_FILE:absl::abseil_dll>"
38+
"$<TARGET_FILE_DIR:basic_room>")
39+
endif() endif()

basic_room/capture_utils.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
* Copyright 2025 LiveKit, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "capture_utils.h"
18+
19+
#include <array>
20+
#include <atomic>
21+
#include <chrono>
22+
#include <cstdint>
23+
#include <iostream>
24+
#include <random>
25+
#include <thread>
26+
27+
#include "livekit/livekit.h"
28+
29+
using namespace livekit;
30+
31+
// Test utils to run a capture loop to publish noisy audio frames to the room
32+
void runNoiseCaptureLoop(const std::shared_ptr<AudioSource> &source,
33+
std::atomic<bool> &running_flag) {
34+
const int sample_rate = source->sample_rate();
35+
const int num_channels = source->num_channels();
36+
const int frame_ms = 10;
37+
const int samples_per_channel = sample_rate * frame_ms / 1000;
38+
39+
// White noise generator (keep amplitude conservative to avoid clipping)
40+
std::mt19937 rng{std::random_device{}()};
41+
std::uniform_int_distribution<int> dist(-3000, 3000);
42+
43+
using Clock = std::chrono::steady_clock;
44+
auto next_deadline = Clock::now();
45+
46+
while (running_flag.load(std::memory_order_relaxed)) {
47+
AudioFrame frame =
48+
AudioFrame::create(sample_rate, num_channels, samples_per_channel);
49+
auto &pcm = frame.data();
50+
for (auto &s : pcm) {
51+
s = static_cast<int16_t>(dist(rng));
52+
}
53+
54+
try {
55+
source->captureFrame(frame);
56+
} catch (const std::exception &e) {
57+
std::cerr << "Error in captureFrame (noise): " << e.what() << std::endl;
58+
break;
59+
}
60+
61+
next_deadline += std::chrono::milliseconds(frame_ms);
62+
std::this_thread::sleep_until(next_deadline);
63+
}
64+
65+
try {
66+
source->clearQueue();
67+
} catch (...) {
68+
std::cerr << "Error in clearQueue (noise)" << std::endl;
69+
}
70+
}
71+
72+
// Fake video source: solid color cycling
73+
void runFakeVideoCaptureLoop(const std::shared_ptr<VideoSource> &source,
74+
std::atomic<bool> &running_flag) {
75+
auto frame = LKVideoFrame::create(1280, 720, VideoBufferType::RGBA);
76+
const double framerate = 1.0 / 30.0;
77+
78+
while (running_flag.load(std::memory_order_relaxed)) {
79+
static auto start = std::chrono::high_resolution_clock::now();
80+
float t = std::chrono::duration<float>(
81+
std::chrono::high_resolution_clock::now() - start)
82+
.count();
83+
// Cycle every 4 seconds: 0=red, 1=green, 2=blue, 3=black
84+
int stage = static_cast<int>(t) % 4;
85+
86+
std::array<uint8_t, 4> rgb{};
87+
switch (stage) {
88+
case 0: // red
89+
rgb = {255, 0, 0, 0};
90+
break;
91+
case 1: // green
92+
rgb = {0, 255, 0, 0};
93+
break;
94+
case 2: // blue
95+
rgb = {0, 0, 255, 0};
96+
break;
97+
case 3: // black
98+
default:
99+
rgb = {0, 0, 0, 0};
100+
break;
101+
}
102+
103+
// ARGB
104+
uint8_t *data = frame.data();
105+
const size_t size = frame.dataSize();
106+
for (size_t i = 0; i < size; i += 4) {
107+
data[i + 0] = 255; // A
108+
data[i + 1] = rgb[0]; // R
109+
data[i + 2] = rgb[1]; // G
110+
data[i + 3] = rgb[2]; // B
111+
}
112+
113+
try {
114+
// If VideoSource is ARGB-capable, pass frame.
115+
// If it expects I420, pass i420 instead.
116+
source->captureFrame(frame, 0, VideoRotation::VIDEO_ROTATION_0);
117+
} catch (const std::exception &e) {
118+
std::cerr << "Error in captureFrame (fake video): " << e.what()
119+
<< std::endl;
120+
break;
121+
}
122+
123+
std::this_thread::sleep_for(std::chrono::duration<double>(framerate));
124+
}
125+
}

basic_room/capture_utils.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2025 LiveKit, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <chrono>
20+
#include <memory>
21+
22+
namespace livekit {
23+
class AudioSource;
24+
class VideoSource;
25+
} // namespace livekit
26+
27+
void runNoiseCaptureLoop(const std::shared_ptr<livekit::AudioSource> &source,
28+
std::atomic<bool> &running_flag);
29+
30+
void runFakeVideoCaptureLoop(
31+
const std::shared_ptr<livekit::VideoSource> &source,
32+
std::atomic<bool> &running_flag);

0 commit comments

Comments
 (0)