Skip to content

Commit b25a39a

Browse files
committed
Add WebRTCProvider command
1 parent b2a86bd commit b25a39a

File tree

10 files changed

+689
-6
lines changed

10 files changed

+689
-6
lines changed

examples/camera-app/linux/main.cpp

+147
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,157 @@
2020
#include <AppMain.h>
2121
#include <platform/CHIPDeviceConfig.h>
2222

23+
#include <rtc/rtc.hpp>
24+
#include <thread>
25+
2326
using namespace chip;
2427
using namespace chip::app;
2528
using namespace chip::app::Clusters;
2629
using namespace Camera;
30+
using namespace std::chrono_literals;
2731

2832
CameraDevice cameraDevice;
2933

34+
void RunCommandThread()
35+
{
36+
std::this_thread::sleep_for(1s);
37+
38+
rtc::InitLogger(rtc::LogLevel::Warning);
39+
40+
rtc::Configuration config;
41+
// config.iceServers.emplace_back("stun.l.google.com:19302");
42+
43+
auto pc = std::make_shared<rtc::PeerConnection>(config);
44+
45+
pc->onLocalDescription([](rtc::Description description) {
46+
std::cout << "Local Description (Paste this to the other peer):" << std::endl;
47+
std::cout << std::string(description) << std::endl;
48+
});
49+
50+
pc->onLocalCandidate([](rtc::Candidate candidate) {
51+
std::cout << "Local Candidate (Paste this to the other peer after the local description):" << std::endl;
52+
std::cout << std::string(candidate) << std::endl << std::endl;
53+
});
54+
55+
pc->onStateChange([](rtc::PeerConnection::State state) { std::cout << "[State: " << state << "]" << std::endl; });
56+
pc->onGatheringStateChange(
57+
[](rtc::PeerConnection::GatheringState state) { std::cout << "[Gathering State: " << state << "]" << std::endl; });
58+
59+
std::shared_ptr<rtc::DataChannel> dc;
60+
pc->onDataChannel([&](std::shared_ptr<rtc::DataChannel> _dc) {
61+
std::cout << "[Got a DataChannel with label: " << _dc->label() << "]" << std::endl;
62+
dc = _dc;
63+
64+
dc->onClosed([&]() { std::cout << "[DataChannel closed: " << dc->label() << "]" << std::endl; });
65+
66+
dc->onMessage([](auto data) {
67+
if (std::holds_alternative<std::string>(data))
68+
{
69+
std::cout << "[Received message: " << std::get<std::string>(data) << "]" << std::endl;
70+
}
71+
});
72+
});
73+
74+
bool exit = false;
75+
while (!exit)
76+
{
77+
std::cout << std::endl
78+
<< "**********************************************************************************"
79+
"*****"
80+
<< std::endl
81+
<< "* 0: Exit /"
82+
<< " 1: Enter remote description /"
83+
<< " 2: Enter remote candidate /"
84+
<< " 3: Send message /"
85+
<< " 4: Print Connection Info *" << std::endl
86+
<< "[Command]: ";
87+
88+
int command = -1;
89+
std::cin >> command;
90+
std::cin.ignore();
91+
92+
switch (command)
93+
{
94+
case 0: {
95+
exit = true;
96+
break;
97+
}
98+
case 1: {
99+
// Parse Description
100+
std::cout << "[Description]: ";
101+
std::string sdp, line;
102+
while (getline(std::cin, line) && !line.empty())
103+
{
104+
sdp += line;
105+
sdp += "\r\n";
106+
}
107+
pc->setRemoteDescription(sdp);
108+
std::this_thread::sleep_for(1s);
109+
break;
110+
}
111+
case 2: {
112+
// Parse Candidate
113+
std::cout << "[Candidate]: ";
114+
std::string candidate;
115+
getline(std::cin, candidate);
116+
pc->addRemoteCandidate(candidate);
117+
std::this_thread::sleep_for(2s);
118+
break;
119+
}
120+
case 3: {
121+
// Send Message
122+
if (!dc || !dc->isOpen())
123+
{
124+
std::cout << "** Channel is not Open ** ";
125+
break;
126+
}
127+
std::cout << "[Message]: ";
128+
std::string message;
129+
getline(std::cin, message);
130+
dc->send(message);
131+
break;
132+
}
133+
case 4: {
134+
// Connection Info
135+
if (!dc || !dc->isOpen())
136+
{
137+
std::cout << "** Channel is not Open ** ";
138+
break;
139+
}
140+
rtc::Candidate local, remote;
141+
std::optional<std::chrono::milliseconds> rtt = pc->rtt();
142+
if (pc->getSelectedCandidatePair(&local, &remote))
143+
{
144+
std::cout << "Local: " << local << std::endl;
145+
std::cout << "Remote: " << remote << std::endl;
146+
std::cout << "Bytes Sent:" << pc->bytesSent() << " / Bytes Received:" << pc->bytesReceived()
147+
<< " / Round-Trip Time:";
148+
if (rtt.has_value())
149+
std::cout << rtt.value().count();
150+
else
151+
std::cout << "null";
152+
std::cout << " ms";
153+
}
154+
else
155+
{
156+
std::cout << "Could not get Candidate Pair Info" << std::endl;
157+
}
158+
break;
159+
}
160+
default: {
161+
std::cout << "** Invalid Command ** " << std::endl;
162+
break;
163+
}
164+
}
165+
}
166+
167+
if (dc)
168+
dc->close();
169+
170+
if (pc)
171+
pc->close();
172+
}
173+
30174
void ApplicationInit()
31175
{
32176
ChipLogProgress(Zcl, "Matter Camera Linux App: ApplicationInit()");
@@ -42,6 +186,9 @@ int main(int argc, char * argv[])
42186
{
43187
VerifyOrDie(ChipLinuxAppInit(argc, argv) == 0);
44188

189+
std::thread runCommands(RunCommandThread);
190+
runCommands.detach();
191+
45192
ChipLinuxAppMainLoop();
46193

47194
return 0;

examples/camera-controller/BUILD.gn

+4
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ static_library("camera-controller-utils") {
6969
"commands/pairing/OpenCommissioningWindowCommand.h",
7070
"commands/pairing/PairingCommand.cpp",
7171
"commands/pairing/ToTLVCert.cpp",
72+
"commands/webrtc/AnswerCommand.cpp",
73+
"commands/webrtc/AnswerCommand.h",
74+
"commands/webrtc/ProviderCommand.cpp",
75+
"commands/webrtc/ProviderCommand.h",
7276
]
7377

7478
deps = [ "${chip_root}/src/app:events" ]

examples/camera-controller/commands/interactive/InteractiveCommands.cpp

+63-6
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,44 @@ std::queue<std::string> sCommandQueue;
4444
std::mutex sQueueMutex;
4545
std::condition_variable sQueueCondition;
4646

47+
// -----------------------------------------------------------------------------
48+
// READING THREAD SUPPORT
49+
// -----------------------------------------------------------------------------
50+
static std::thread sReadCommandsThread;
51+
static std::atomic<bool> sReadThreadQuit{ false };
52+
static bool sIsReadThreadRunning = false;
53+
54+
/**
55+
* The thread function that continuously reads commands from stdin.
56+
* We exit the loop when sReadThreadQuit is set to true.
57+
*/
4758
void ReadCommandThread()
4859
{
49-
char * command;
50-
while (true)
60+
while (!sReadThreadQuit.load())
5161
{
52-
command = readline(kInteractiveModePrompt);
53-
if (command != nullptr && *command)
62+
// readline() will block until user hits Enter or EOF.
63+
char * command = readline(kInteractiveModePrompt);
64+
if (command == nullptr)
65+
{
66+
// Usually means EOF or an error; if we want to keep going
67+
// after EOF, do so, otherwise break out here.
68+
// For now, break if command is nullptr to avoid infinite loop.
69+
break;
70+
}
71+
72+
if (*command)
5473
{
74+
// Non-empty line
5575
std::unique_lock<std::mutex> lock(sQueueMutex);
5676
sCommandQueue.push(command);
5777
free(command);
5878
sQueueCondition.notify_one();
5979
}
80+
else
81+
{
82+
// Empty line => skip
83+
free(command);
84+
}
6085
}
6186
}
6287

@@ -167,8 +192,7 @@ CHIP_ERROR InteractiveStartCommand::RunCommand()
167192
Logging::SetLogRedirectCallback(LoggingCallback);
168193
}
169194

170-
std::thread readCommands(ReadCommandThread);
171-
readCommands.detach();
195+
StartReadCommandThread();
172196

173197
char * command = nullptr;
174198
int status;
@@ -224,3 +248,36 @@ void PushCommand(const std::string & command)
224248
sCommandQueue.push(command);
225249
sQueueCondition.notify_one();
226250
}
251+
252+
void StartReadCommandThread()
253+
{
254+
// Only start if not already running
255+
if (!sIsReadThreadRunning)
256+
{
257+
sReadThreadQuit.store(false);
258+
sReadCommandsThread = std::thread(ReadCommandThread);
259+
sIsReadThreadRunning = true;
260+
}
261+
}
262+
263+
void StopReadCommandThread()
264+
{
265+
// Only stop if it's running
266+
if (sIsReadThreadRunning)
267+
{
268+
// Signal the thread to quit
269+
sReadThreadQuit.store(true);
270+
271+
// If readline() is currently blocking, it typically waits until ENTER or EOF.
272+
// In most real integrations, pressing ENTER (or sending EOF) is enough to
273+
// let the loop proceed and exit. If needed, you can forcibly break readline
274+
// by calling something like `rl_done = 1;`, etc.
275+
276+
// Join the thread so we know it's finished
277+
if (sReadCommandsThread.joinable())
278+
{
279+
sReadCommandsThread.join();
280+
}
281+
sIsReadThreadRunning = false;
282+
}
283+
}

examples/camera-controller/commands/interactive/InteractiveCommands.h

+12
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,15 @@ class InteractiveStartCommand : public InteractiveCommand
6666
};
6767

6868
void PushCommand(const std::string & command);
69+
70+
/**
71+
* Starts (or restarts) the ReadCommandThread, which continuously reads user input
72+
* from the console and pushes commands into the queue.
73+
*/
74+
void StartReadCommandThread();
75+
76+
/**
77+
* Stops the ReadCommandThread by signaling it to exit and then joining the thread.
78+
* Call this when you need to pause or stop reading from stdin to avoid conflicts.
79+
*/
80+
void StopReadCommandThread();

0 commit comments

Comments
 (0)