Skip to content

Instantly share code, notes, and snippets.

@patrickatkeylogic
Last active June 13, 2025 20:10
Show Gist options
  • Save patrickatkeylogic/715c0a9ce3ec44ceaa1947f2f59a3841 to your computer and use it in GitHub Desktop.
Save patrickatkeylogic/715c0a9ce3ec44ceaa1947f2f59a3841 to your computer and use it in GitHub Desktop.
Simple RTC camera test
#include <curl/curl.h>
#include <iostream>
#include <rtc/rtc.hpp>
#include <thread>
#include <nlohmann/json.hpp>
using nlohmann::json;
size_t writeCallback(void* contents, size_t size, size_t nmemb, std::string* output)
{
size_t totalSize = size * nmemb;
output->append((char*)contents, totalSize);
return totalSize;
}
json postOffer(const std::string& url, const std::string& offerSdp)
{
CURL* curl = curl_easy_init();
std::string response;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, offerSdp.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
struct curl_slist* headers = nullptr;
headers = curl_slist_append(headers, "Content-Type: text/plain");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
std::cout << "curl_easy_perform() failed: " << curl_easy_strerror(res) << "\n";
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
return json::parse(response);
}
int main()
{
rtc::InitLogger(rtc::LogLevel::None);
rtc::Configuration config;
config.disableAutoNegotiation = true;
config.iceServers.emplace_back("stun:stun.l.google.com:19302");
auto pc = std::make_shared<rtc::PeerConnection>(config);
auto session = std::make_shared<rtc::RtcpReceivingSession>();
// Audio track
auto audio = rtc::Description::Audio("0", rtc::Description::Direction::RecvOnly);
audio.addPCMACodec(8);
auto audioTrack = pc->addTrack(audio);
audioTrack->setMediaHandler(session);
// Video track
auto video = rtc::Description::Video("1", rtc::Description::Direction::RecvOnly);
video.addH264Codec(96);
video.setBitrate(2048);
auto videoTrack = pc->addTrack(video);
videoTrack->setMediaHandler(session);
// Data channel
auto dc = pc->createDataChannel("chat");
pc->onGatheringStateChange([&](rtc::PeerConnection::GatheringState state) {
std::cout << "ICE Gathering State: " << state << std::endl;
if (state == rtc::PeerConnection::GatheringState::Complete) {
std::cout << "ICE gathering complete. Sending offer..." << std::endl;
std::optional< rtc::Description> offer = pc->localDescription();
std::string offerStr = std::string(*offer);
json response = postOffer("http://192.168.10.14:1988/rtc/v1/play/", offerStr);
if (response.contains("sdp") && response.contains("type")) {
std::string sdp = response["sdp"];
std::string type = response["type"];
rtc::Description answer(sdp, type);
pc->setRemoteDescription(answer);
}
}
});
pc->onIceStateChange([&](rtc::PeerConnection::IceState state) {
std::cout << "peerconnection onIceStateChange: " << state << std::endl;
});
pc->onSignalingStateChange([&](rtc::PeerConnection::SignalingState state) {
std::cout << "peerconnection onSignalingStateChange: " << state << std::endl;
});
dc->onOpen([&]() {
std::cout << "dataChannel onOpen" << std::endl;
});
dc->onAvailable([&]() {
std::cout << "dataChannel onAvailable: is the datachannel open? " << dc->isOpen() << std::endl;
});
dc->onError([&](std::string error) {
std::cout << "dataChannel onError: " << error << std::endl;
});
dc->onClosed([&]() {
std::cout << "dataChannel onClosed" << std::endl;
});
dc->onMessage([&](rtc::message_variant message) {
std::cout << "dataChannel onMessage" << std::endl;
std::visit([&](auto&& data) {
std::cout << "Received datachannel message: ";
using T = std::decay_t<decltype(data)>;
if constexpr (std::is_same_v<T, std::string>) {
std::cout << data << std::endl;
if (data.rfind("interaction", 0) == 0) {
std::cout << "Responding to 'interaction' with 'OK\\0'." << std::endl;
dc->send("OK\0");
}
}
else if constexpr (std::is_same_v<T, rtc::binary>) {
std::cout << "[binary] size=" << data.size() << std::endl;
}
}, message);
});
audioTrack->onOpen([&]() {
std::cout << "audioTrack onOpen" << std::endl;
});
audioTrack->onAvailable([&]() {
std::cout << "audioTrack onAvailable" << std::endl;
});
audioTrack->onError([&](std::string error) {
std::cout << "audioTrack onError: " << error << std::endl;
});
audioTrack->onClosed([&]() {
std::cout << "audioTrack onClosed" << std::endl;
});
audioTrack->onMessage([&](rtc::message_variant data) {
std::cout << "audioTrack onMessage" << std::endl;
});
videoTrack->onOpen([&]() {
std::cout << "videoTrack onOpen" << std::endl;
});
videoTrack->onAvailable([&]() {
std::cout << "videoTrack onAvailable" << std::endl;
});
videoTrack->onClosed([&]() {
std::cout << "videoTrack onClosed" << std::endl;
});
videoTrack->onError([&](std::string error) {
std::cout << "videoTrack onError: " << error << std::endl;
});
videoTrack->onFrame([&](const rtc::binary& data, const rtc::FrameInfo& info) {
std::cout << "videoTrack onFrame: size=" << data.size() << ", ts=" << info.timestamp << std::endl;
});
videoTrack->onMessage([&](rtc::message_variant data) {
std::cout << "videoTrack onMessage" << std::endl;
});
pc->setLocalDescription();
std::cout << "Waiting for frames..." << std::endl;
while (true)
std::this_thread::sleep_for(std::chrono::seconds(10));
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment