Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 23 additions & 29 deletions basic_room/capture_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
using namespace livekit;

// Test utils to run a capture loop to publish noisy audio frames to the room
void runNoiseCaptureLoop(const std::shared_ptr<AudioSource> &source,
std::atomic<bool> &running_flag) {
void runNoiseCaptureLoop(const std::shared_ptr<AudioSource>& source, std::atomic<bool>& running_flag) {
const int sample_rate = source->sample_rate();
const int num_channels = source->num_channels();
const int frame_ms = 10;
Expand All @@ -44,16 +43,15 @@ void runNoiseCaptureLoop(const std::shared_ptr<AudioSource> &source,
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) {
AudioFrame frame = AudioFrame::create(sample_rate, num_channels, samples_per_channel);
auto& pcm = frame.data();
for (auto& s : pcm) {
s = static_cast<int16_t>(dist(rng));
}

try {
source->captureFrame(frame);
} catch (const std::exception &e) {
} catch (const std::exception& e) {
std::cerr << "Error in captureFrame (noise): " << e.what() << std::endl;
break;
}
Expand All @@ -70,38 +68,35 @@ void runNoiseCaptureLoop(const std::shared_ptr<AudioSource> &source,
}

// Fake video source: solid color cycling
void runFakeVideoCaptureLoop(const std::shared_ptr<VideoSource> &source,
std::atomic<bool> &running_flag) {
void runFakeVideoCaptureLoop(const std::shared_ptr<VideoSource>& source, std::atomic<bool>& running_flag) {
auto frame = VideoFrame::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<float>(
std::chrono::high_resolution_clock::now() - start)
.count();
float t = std::chrono::duration<float>(std::chrono::high_resolution_clock::now() - start).count();
// Cycle every 4 seconds: 0=red, 1=green, 2=blue, 3=black
int stage = static_cast<int>(t) % 4;

std::array<uint8_t, 4> 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;
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();
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
Expand All @@ -114,9 +109,8 @@ void runFakeVideoCaptureLoop(const std::shared_ptr<VideoSource> &source,
// 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;
} catch (const std::exception& e) {
std::cerr << "Error in captureFrame (fake video): " << e.what() << std::endl;
break;
}

Expand Down
7 changes: 2 additions & 5 deletions basic_room/capture_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ class AudioSource;
class VideoSource;
} // namespace livekit

void runNoiseCaptureLoop(const std::shared_ptr<livekit::AudioSource> &source,
std::atomic<bool> &running_flag);
void runNoiseCaptureLoop(const std::shared_ptr<livekit::AudioSource>& source, std::atomic<bool>& running_flag);

void runFakeVideoCaptureLoop(
const std::shared_ptr<livekit::VideoSource> &source,
std::atomic<bool> &running_flag);
void runFakeVideoCaptureLoop(const std::shared_ptr<livekit::VideoSource>& source, std::atomic<bool>& running_flag);
57 changes: 21 additions & 36 deletions basic_room/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,66 +34,58 @@ std::atomic<bool> g_running{true};

void handleSignal(int) { g_running.store(false); }

void printUsage(const char *prog) {
void printUsage(const char* prog) {
std::cerr << "Usage:\n"
<< " " << prog << " --url <ws-url> --token <token>\n"
<< "Env fallbacks:\n"
<< " LIVEKIT_URL, LIVEKIT_TOKEN\n";
}

bool parseArgs(int argc, char *argv[], std::string &url, std::string &token,
bool &self_test) {
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 == "-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;
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;
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;
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 (const char* e = std::getenv("LIVEKIT_URL")) url = e;
}
if (token.empty()) {
if (const char *e = std::getenv("LIVEKIT_TOKEN"))
token = e;
if (const char* e = std::getenv("LIVEKIT_TOKEN")) token = e;
}

return !(url.empty() || token.empty());
}

void print_livekit_version() {
std::cout << "LiveKit version: " << LIVEKIT_BUILD_VERSION_FULL << " ("
<< LIVEKIT_BUILD_FLAVOR << ", commit " << LIVEKIT_BUILD_COMMIT
<< ", built " << LIVEKIT_BUILD_DATE << ")" << std::endl;
std::cout << "LiveKit version: " << LIVEKIT_BUILD_VERSION_FULL << " (" << LIVEKIT_BUILD_FLAVOR << ", commit "
<< LIVEKIT_BUILD_COMMIT << ", built " << LIVEKIT_BUILD_DATE << ")" << std::endl;
}

} // namespace

int main(int argc, char *argv[]) {
int main(int argc, char* argv[]) {
print_livekit_version();
std::string url, token;
bool self_test = false;
Expand Down Expand Up @@ -131,8 +123,7 @@ int main(int argc, char *argv[]) {
// ---- Create & publish AUDIO (noise) ----
// Match your runNoiseCaptureLoop pacing: it assumes frame_ms=10.
auto audioSource = std::make_shared<AudioSource>(48000, 1, 10);
auto audioTrack =
LocalAudioTrack::createLocalAudioTrack("noise", audioSource);
auto audioTrack = LocalAudioTrack::createLocalAudioTrack("noise", audioSource);

TrackPublishOptions audioOpts;
audioOpts.source = TrackSource::SOURCE_MICROPHONE;
Expand All @@ -144,7 +135,7 @@ int main(int argc, char *argv[]) {
room->localParticipant()->publishTrack(audioTrack, audioOpts);
audioPub = audioTrack->publication();
std::cout << "Published audio: sid=" << audioPub->sid() << "\n";
} catch (const std::exception &e) {
} catch (const std::exception& e) {
std::cerr << "Failed to publish audio: " << e.what() << "\n";
}

Expand All @@ -163,18 +154,16 @@ int main(int argc, char *argv[]) {
room->localParticipant()->publishTrack(videoTrack, videoOpts);
videoPub = videoTrack->publication();
std::cout << "Published video: sid=" << videoPub->sid() << "\n";
} catch (const std::exception &e) {
} catch (const std::exception& e) {
std::cerr << "Failed to publish video: " << e.what() << "\n";
}

// ---- Start synthetic capture loops ----
std::atomic<bool> audio_running{true};
std::atomic<bool> video_running{true};

std::thread audioThread(
[&] { runNoiseCaptureLoop(audioSource, audio_running); });
std::thread videoThread(
[&] { runFakeVideoCaptureLoop(videoSource, video_running); });
std::thread audioThread([&] { runNoiseCaptureLoop(audioSource, audio_running); });
std::thread videoThread([&] { runFakeVideoCaptureLoop(videoSource, video_running); });

// Keep alive until Ctrl-C
while (g_running.load()) {
Expand All @@ -185,17 +174,13 @@ int main(int argc, char *argv[]) {
audio_running.store(false);
video_running.store(false);

if (audioThread.joinable())
audioThread.join();
if (videoThread.joinable())
videoThread.join();
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());
if (audioPub) room->localParticipant()->unpublishTrack(audioPub->sid());
if (videoPub) room->localParticipant()->unpublishTrack(videoPub->sid());
} catch (...) {
}

Expand Down
47 changes: 24 additions & 23 deletions hello_livekit_receiver/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
/// Or via environment variables:
/// LIVEKIT_URL, LIVEKIT_RECEIVER_TOKEN, LIVEKIT_SENDER_IDENTITY

#include "livekit/livekit.h"

#include <atomic>
#include <cassert>
#include <chrono>
Expand All @@ -34,21 +32,23 @@
#include <iostream>
#include <thread>

#include "livekit/livekit.h"

using namespace livekit;

constexpr const char *kDataTrackName = "app-data";
constexpr const char *kVideoTrackName = "camera0";
constexpr const char* kDataTrackName = "app-data";
constexpr const char* kVideoTrackName = "camera0";

std::atomic<bool> g_running{true};

void handleSignal(int) { g_running.store(false); }

std::string getenvOrEmpty(const char *name) {
const char *v = std::getenv(name);
std::string getenvOrEmpty(const char* name) {
const char* v = std::getenv(name);
return v ? std::string(v) : std::string{};
}

int main(int argc, char *argv[]) {
int main(int argc, char* argv[]) {
std::string url = getenvOrEmpty("LIVEKIT_URL");
std::string receiver_token = getenvOrEmpty("LIVEKIT_RECEIVER_TOKEN");
std::string sender_identity = getenvOrEmpty("LIVEKIT_SENDER_IDENTITY");
Expand All @@ -60,7 +60,8 @@ int main(int argc, char *argv[]) {
}

if (url.empty() || receiver_token.empty() || sender_identity.empty()) {
std::cerr << "[error] Usage: HelloLivekitReceiver <ws-url> <receiver-token> <sender-identity>\n or set LIVEKIT_URL, LIVEKIT_RECEIVER_TOKEN, LIVEKIT_SENDER_IDENTITY\n";
std::cerr << "[error] Usage: HelloLivekitReceiver <ws-url> <receiver-token> <sender-identity>\n or set "
"LIVEKIT_URL, LIVEKIT_RECEIVER_TOKEN, LIVEKIT_SENDER_IDENTITY\n";
return 1;
}

Expand All @@ -82,35 +83,35 @@ int main(int argc, char *argv[]) {
return 1;
}

LocalParticipant *lp = room->localParticipant();
LocalParticipant* lp = room->localParticipant();
assert(lp);

std::cout << "[info] [receiver] Connected as identity='" << lp->identity() << "' room='" << room->room_info().name << "'; subscribing to sender identity='" << sender_identity << "'\n";
std::cout << "[info] [receiver] Connected as identity='" << lp->identity() << "' room='" << room->room_info().name
<< "'; subscribing to sender identity='" << sender_identity << "'\n";

int video_frame_count = 0;
room->setOnVideoFrameCallback(
sender_identity, kVideoTrackName,
[&video_frame_count](const VideoFrame &frame, std::int64_t timestamp_us) {
const auto ts_ms =
std::chrono::duration<double, std::milli>(timestamp_us).count();
const int n = video_frame_count++;
if (n % 10 == 0) {
std::cout << "[info] [receiver] Video frame #" << n << " " << frame.width() << "x" << frame.height() << " ts_ms=" << ts_ms << "\n";
}
});
room->setOnVideoFrameCallback(sender_identity, kVideoTrackName,
[&video_frame_count](const VideoFrame& frame, std::int64_t timestamp_us) {
const auto ts_ms = std::chrono::duration<double, std::milli>(timestamp_us).count();
const int n = video_frame_count++;
if (n % 10 == 0) {
std::cout << "[info] [receiver] Video frame #" << n << " " << frame.width() << "x"
<< frame.height() << " ts_ms=" << ts_ms << "\n";
}
});

int data_frame_count = 0;
room->addOnDataFrameCallback(
sender_identity, kDataTrackName,
[&data_frame_count](const std::vector<std::uint8_t> &payload,
std::optional<std::uint64_t> user_ts) {
[&data_frame_count](const std::vector<std::uint8_t>& payload, std::optional<std::uint64_t> user_ts) {
const int n = data_frame_count++;
if (n % 10 == 0) {
std::cout << "[info] [receiver] Data frame #" << n << "\n";
}
});

std::cout << "[info] [receiver] Listening for video track '" << kVideoTrackName << "' + data track '" << kDataTrackName << "'; Ctrl-C to exit\n";
std::cout << "[info] [receiver] Listening for video track '" << kVideoTrackName << "' + data track '"
<< kDataTrackName << "'; Ctrl-C to exit\n";

while (g_running.load()) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
Expand Down
Loading
Loading