Last active
June 13, 2025 20:10
-
-
Save patrickatkeylogic/715c0a9ce3ec44ceaa1947f2f59a3841 to your computer and use it in GitHub Desktop.
Simple RTC camera test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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