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