v2.0.0
Loading...
Searching...
No Matches
lsl_stream_discovery.cpp
Go to the documentation of this file.
1//=============================================================================================================
32
33//=============================================================================================================
34// INCLUDES
35//=============================================================================================================
36
38
39//=============================================================================================================
40// QT INCLUDES
41//=============================================================================================================
42
43#include <QUdpSocket>
44#include <QHostAddress>
45#include <QNetworkDatagram>
46#include <QElapsedTimer>
47#include <QDebug>
48
49//=============================================================================================================
50// STL INCLUDES
51//=============================================================================================================
52
53#include <set>
54
55//=============================================================================================================
56// CONSTANTS
57//=============================================================================================================
58
59namespace {
60 const QHostAddress DISCOVERY_MULTICAST_GROUP("239.255.172.215");
61 const quint16 DISCOVERY_PORT = 16571;
62}
63
64//=============================================================================================================
65// DEFINE FUNCTIONS
66//=============================================================================================================
67
68std::vector<LSLLIB::stream_info> LSLLIB::resolve_streams(double timeout)
69{
70 std::vector<stream_info> results;
71 std::set<std::string> seenUIDs; // track unique streams by UID
72
73 // Create a UDP socket and join the multicast group
74 QUdpSocket udpSocket;
75
76 // Bind to the discovery port, allow address reuse for multiple listeners
77 if (!udpSocket.bind(QHostAddress::AnyIPv4, DISCOVERY_PORT,
78 QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
79 qDebug() << "[lsl::resolve_streams] Failed to bind UDP socket:" << udpSocket.errorString();
80 return results;
81 }
82
83#ifndef Q_OS_WASM
84 // Join the multicast group (not available on WebAssembly)
85 if (!udpSocket.joinMulticastGroup(DISCOVERY_MULTICAST_GROUP)) {
86 qDebug() << "[lsl::resolve_streams] Failed to join multicast group:" << udpSocket.errorString();
87 // Continue anyway — broadcast messages may still be received on some platforms
88 }
89#endif
90
91 // Listen for the specified timeout
92 int timeoutMs = static_cast<int>(timeout * 1000.0);
93 QElapsedTimer timer;
94 timer.start();
95
96 while (timer.elapsed() < timeoutMs) {
97 // Wait for datagrams with remaining time budget
98 int remainingMs = timeoutMs - static_cast<int>(timer.elapsed());
99 if (remainingMs <= 0) {
100 break;
101 }
102
103 if (!udpSocket.waitForReadyRead(remainingMs)) {
104 continue;
105 }
106
107 // Process all pending datagrams
108 while (udpSocket.hasPendingDatagrams()) {
109 QNetworkDatagram datagram = udpSocket.receiveDatagram();
110 if (!datagram.isValid()) {
111 continue;
112 }
113
114 QByteArray data = datagram.data();
115 std::string payload(data.constData(), data.size());
116
117 // Try to parse as stream_info
119
120 // Validate the parsed info
121 if (info.uid().empty() || info.name().empty()) {
122 continue;
123 }
124
125 // Skip duplicates (same UID)
126 if (seenUIDs.count(info.uid()) > 0) {
127 continue;
128 }
129
130 // Set the data host from the UDP sender address (more reliable than self-reported hostname)
131 info.set_data_host(datagram.senderAddress().toString().toStdString());
132
133 seenUIDs.insert(info.uid());
134 results.push_back(info);
135 }
136 }
137
138#ifndef Q_OS_WASM
139 // Leave multicast group and close
140 udpSocket.leaveMulticastGroup(DISCOVERY_MULTICAST_GROUP);
141#endif
142 udpSocket.close();
143
144 return results;
145}
146
147//=============================================================================================================
148
149std::vector<LSLLIB::stream_info> LSLLIB::resolve_stream(const std::string& prop,
150 const std::string& value,
151 double timeout)
152{
153 // First get all streams
154 std::vector<stream_info> all = resolve_streams(timeout);
155
156 // Filter by the requested property
157 std::vector<stream_info> filtered;
158 for (const auto& info : all) {
159 std::string actual;
160 if (prop == "name") {
161 actual = info.name();
162 } else if (prop == "type") {
163 actual = info.type();
164 } else if (prop == "source_id") {
165 actual = info.source_id();
166 } else if (prop == "uid") {
167 actual = info.uid();
168 } else if (prop == "hostname") {
169 actual = info.hostname();
170 } else {
171 continue; // unknown property
172 }
173
174 if (actual == value) {
175 filtered.push_back(info);
176 }
177 }
178
179 return filtered;
180}
Declares the resolve_streams / resolve_stream free functions used to enumerate LSL outlets currently ...
std::vector< stream_info > resolve_streams(double timeout=1.0)
Resolve all streams currently available on the network.
std::vector< stream_info > resolve_stream(const std::string &prop, const std::string &value, double timeout=1.0)
Resolve streams by a specific property value.
Value-type descriptor of a single LSL stream: semantic metadata plus transport endpoint,...
void set_data_host(const std::string &host)
Set the data host (used internally during discovery).
std::string uid() const noexcept
A unique identifier for this particular stream instance (auto-generated).
std::string name() const noexcept
Name of the stream.
static stream_info from_string(const std::string &data)
Deserialize a stream_info from a network transport string.