OpenShot Audio Library | OpenShotAudio  0.6.0
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 
23 namespace 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 
45 }
46 
48 {
49  stopThread (2000);
50  socket.shutdown();
51 }
52 
53 void 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 
68 void 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);
96 }
97 
99 {
100  socket.shutdown();
101  stopThread (2000);
102 
103  #if JUCE_ANDROID
104  releaseMulticastLock();
105  #endif
106 }
107 
108 void 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 
128 std::vector<NetworkServiceDiscovery::Service> NetworkServiceDiscovery::AvailableServiceList::getServices() const
129 {
130  const ScopedLock sl (listLock);
131  auto listCopy = services;
132  return listCopy;
133 }
134 
135 void NetworkServiceDiscovery::AvailableServiceList::handleAsyncUpdate()
136 {
137  NullCheckedInvocation::invoke (onChange);
138 }
139 
140 void 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 
156 static 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 
167 void 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 
193 void 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
bool bindToPort(int localPortNumber)
static Array< IPAddress > getAllAddresses(bool includeIPv6=false)
static IPAddress getInterfaceBroadcastAddress(const IPAddress &interfaceAddress)
static RelativeTime seconds(double seconds) noexcept
bool startThread()
static Time JUCE_CALLTYPE getCurrentTime() noexcept
Definition: juce_Time.cpp:233
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
const String & getStringAttribute(StringRef attributeName) const noexcept
void setAttribute(const Identifier &attributeName, const String &newValue)
Advertiser(const String &serviceTypeUID, const String &serviceDescription, int broadcastPort, int connectionPort, RelativeTime minTimeBetweenBroadcasts=RelativeTime::seconds(1.5))
AvailableServiceList(const String &serviceTypeUID, int broadcastPort)