OpenShot Audio Library | OpenShotAudio 0.4.0
 
Loading...
Searching...
No Matches
juce_NetworkServiceDiscovery.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26#if JUCE_ANDROID
27 extern void acquireMulticastLock();
28 extern void releaseMulticastLock();
29#endif
30
32 const String& serviceDescription,
33 int broadcastPortToUse, int connectionPort,
34 RelativeTime minTimeBetweenBroadcasts)
35 : Thread ("Discovery_broadcast"),
36 message (serviceTypeUID), broadcastPort (broadcastPortToUse),
37 minInterval (minTimeBetweenBroadcasts)
38{
39 message.setAttribute ("id", Uuid().toString());
40 message.setAttribute ("name", serviceDescription);
41 message.setAttribute ("address", String());
42 message.setAttribute ("port", connectionPort);
43
44 startThread (Priority::background);
45}
46
48{
49 stopThread (2000);
50 socket.shutdown();
51}
52
53void NetworkServiceDiscovery::Advertiser::run()
54{
55 if (! socket.bindToPort (0))
56 {
57 jassertfalse;
58 return;
59 }
60
61 while (! threadShouldExit())
62 {
63 sendBroadcast();
64 wait ((int) minInterval.inMilliseconds());
65 }
66}
67
68void NetworkServiceDiscovery::Advertiser::sendBroadcast()
69{
70 static IPAddress local = IPAddress::local();
71
72 for (auto& address : IPAddress::getAllAddresses())
73 {
74 if (address == local)
75 continue;
76
77 message.setAttribute ("address", address.toString());
78
79 auto broadcastAddress = IPAddress::getInterfaceBroadcastAddress (address);
80 auto data = message.toString (XmlElement::TextFormat().singleLine().withoutHeader());
81
82 socket.write (broadcastAddress.toString(), broadcastPort, data.toRawUTF8(), (int) data.getNumBytesAsUTF8());
83 }
84}
85
86//==============================================================================
88 : Thread ("Discovery_listen"), serviceTypeUID (serviceType)
89{
90 #if JUCE_ANDROID
91 acquireMulticastLock();
92 #endif
93
94 socket.bindToPort (broadcastPort);
95 startThread (Priority::background);
96}
97
99{
100 socket.shutdown();
101 stopThread (2000);
102
103 #if JUCE_ANDROID
104 releaseMulticastLock();
105 #endif
106}
107
108void NetworkServiceDiscovery::AvailableServiceList::run()
109{
110 while (! threadShouldExit())
111 {
112 if (socket.waitUntilReady (true, 200) == 1)
113 {
114 char buffer[1024];
115 auto bytesRead = socket.read (buffer, sizeof (buffer) - 1, false);
116
117 if (bytesRead > 10)
118 if (auto xml = parseXML (String (CharPointer_UTF8 (buffer),
119 CharPointer_UTF8 (buffer + bytesRead))))
120 if (xml->hasTagName (serviceTypeUID))
121 handleMessage (*xml);
122 }
123
124 removeTimedOutServices();
125 }
126}
127
128std::vector<NetworkServiceDiscovery::Service> NetworkServiceDiscovery::AvailableServiceList::getServices() const
129{
130 const ScopedLock sl (listLock);
131 auto listCopy = services;
132 return listCopy;
133}
134
135void NetworkServiceDiscovery::AvailableServiceList::handleAsyncUpdate()
136{
137 NullCheckedInvocation::invoke (onChange);
138}
139
140void NetworkServiceDiscovery::AvailableServiceList::handleMessage (const XmlElement& xml)
141{
142 Service service;
143 service.instanceID = xml.getStringAttribute ("id");
144
145 if (service.instanceID.trim().isNotEmpty())
146 {
147 service.description = xml.getStringAttribute ("name");
148 service.address = IPAddress (xml.getStringAttribute ("address"));
149 service.port = xml.getIntAttribute ("port");
150 service.lastSeen = Time::getCurrentTime();
151
152 handleMessage (service);
153 }
154}
155
156static void sortServiceList (std::vector<NetworkServiceDiscovery::Service>& services)
157{
158 auto compareServices = [] (const NetworkServiceDiscovery::Service& s1,
159 const NetworkServiceDiscovery::Service& s2)
160 {
161 return s1.instanceID < s2.instanceID;
162 };
163
164 std::sort (services.begin(), services.end(), compareServices);
165}
166
167void NetworkServiceDiscovery::AvailableServiceList::handleMessage (const Service& service)
168{
169 const ScopedLock sl (listLock);
170
171 for (auto& s : services)
172 {
173 if (s.instanceID == service.instanceID)
174 {
175 if (s.description != service.description
176 || s.address != service.address
177 || s.port != service.port)
178 {
179 s = service;
180 triggerAsyncUpdate();
181 }
182
183 s.lastSeen = service.lastSeen;
184 return;
185 }
186 }
187
188 services.push_back (service);
189 sortServiceList (services);
190 triggerAsyncUpdate();
191}
192
193void NetworkServiceDiscovery::AvailableServiceList::removeTimedOutServices()
194{
195 const double timeoutSeconds = 5.0;
196 auto oldestAllowedTime = Time::getCurrentTime() - RelativeTime::seconds (timeoutSeconds);
197
198 const ScopedLock sl (listLock);
199
200 auto oldEnd = std::end (services);
201 auto newEnd = std::remove_if (std::begin (services), oldEnd,
202 [=] (const Service& s) { return s.lastSeen < oldestAllowedTime; });
203
204 if (newEnd != oldEnd)
205 {
206 services.erase (newEnd, oldEnd);
207 triggerAsyncUpdate();
208 }
209}
210
211} // namespace juce
static Array< IPAddress > getAllAddresses(bool includeIPv6=false)
static IPAddress local(bool IPv6=false) noexcept
static IPAddress getInterfaceBroadcastAddress(const IPAddress &interfaceAddress)
static RelativeTime seconds(double seconds) noexcept
Thread(const String &threadName, size_t threadStackSize=osDefaultStackSize)
bool stopThread(int timeOutMilliseconds)
static Time JUCE_CALLTYPE getCurrentTime() noexcept
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
const String & getStringAttribute(StringRef attributeName) const noexcept
Advertiser(const String &serviceTypeUID, const String &serviceDescription, int broadcastPort, int connectionPort, RelativeTime minTimeBetweenBroadcasts=RelativeTime::seconds(1.5))
AvailableServiceList(const String &serviceTypeUID, int broadcastPort)