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{
61const QHostAddress DISCOVERY_MULTICAST_GROUP("239.255.172.215");
62const quint16 DISCOVERY_PORT = 16571;
63}
64
65//=============================================================================================================
66// DEFINE FUNCTIONS
67//=============================================================================================================
68
69std::vector<LSLLIB::stream_info> LSLLIB::resolve_streams(double timeout)
70{
71 std::vector<stream_info> results;
72 std::set<std::string> seenUIDs; // track unique streams by UID
73
74 // Create a UDP socket and join the multicast group
75 QUdpSocket udpSocket;
76
77 // Bind to the discovery port, allow address reuse for multiple listeners
78 if (!udpSocket.bind(QHostAddress::AnyIPv4, DISCOVERY_PORT,
79 QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
80 qDebug() << "[lsl::resolve_streams] Failed to bind UDP socket:" << udpSocket.errorString();
81 return results;
82 }
83
84#ifndef Q_OS_WASM
85 // Join the multicast group (not available on WebAssembly)
86 if (!udpSocket.joinMulticastGroup(DISCOVERY_MULTICAST_GROUP)) {
87 qDebug() << "[lsl::resolve_streams] Failed to join multicast group:" << udpSocket.errorString();
88 // Continue anyway — broadcast messages may still be received on some platforms
89 }
90#endif
91
92 // Listen for the specified timeout
93 int timeoutMs = static_cast<int>(timeout * 1000.0);
94 QElapsedTimer timer;
95 timer.start();
96
97 while (timer.elapsed() < timeoutMs) {
98 // Wait for datagrams with remaining time budget
99 int remainingMs = timeoutMs - static_cast<int>(timer.elapsed());
100 if (remainingMs <= 0) {
101 break;
102 }
103
104 if (!udpSocket.waitForReadyRead(remainingMs)) {
105 continue;
106 }
107
108 // Process all pending datagrams
109 while (udpSocket.hasPendingDatagrams()) {
110 QNetworkDatagram datagram = udpSocket.receiveDatagram();
111 if (!datagram.isValid()) {
112 continue;
113 }
114
115 QByteArray data = datagram.data();
116 std::string payload(data.constData(), data.size());
117
118 // Try to parse as stream_info
120
121 // Validate the parsed info
122 if (info.uid().empty() || info.name().empty()) {
123 continue;
124 }
125
126 // Skip duplicates (same UID)
127 if (seenUIDs.count(info.uid()) > 0) {
128 continue;
129 }
130
131 // Set the data host from the UDP sender address (more reliable than self-reported hostname)
132 info.set_data_host(datagram.senderAddress().toString().toStdString());
133
134 seenUIDs.insert(info.uid());
135 results.push_back(info);
136 }
137 }
138
139#ifndef Q_OS_WASM
140 // Leave multicast group and close
141 udpSocket.leaveMulticastGroup(DISCOVERY_MULTICAST_GROUP);
142#endif
143 udpSocket.close();
144
145 return results;
146}
147
148//=============================================================================================================
149
150std::vector<LSLLIB::stream_info> LSLLIB::resolve_stream(const std::string& prop,
151 const std::string& value,
152 double timeout)
153{
154 // First get all streams
155 std::vector<stream_info> all = resolve_streams(timeout);
156
157 // Filter by the requested property
158 std::vector<stream_info> filtered;
159 for (const auto& info : all) {
160 std::string actual;
161 if (prop == "name") {
162 actual = info.name();
163 } else if (prop == "type") {
164 actual = info.type();
165 } else if (prop == "source_id") {
166 actual = info.source_id();
167 } else if (prop == "uid") {
168 actual = info.uid();
169 } else if (prop == "hostname") {
170 actual = info.hostname();
171 } else {
172 continue; // unknown property
173 }
174
175 if (actual == value) {
176 filtered.push_back(info);
177 }
178 }
179
180 return filtered;
181}
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.