OpenShot Library | OpenShotAudio  0.2.2
juce_NetworkServiceDiscovery.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
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 
44  startThread (2);
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  auto localAddress = IPAddress::getLocalAddress();
71  message.setAttribute ("address", localAddress.toString());
72  auto broadcastAddress = IPAddress::getInterfaceBroadcastAddress (localAddress);
73  auto data = message.toString (XmlElement::TextFormat().singleLine().withoutHeader());
74  socket.write (broadcastAddress.toString(), broadcastPort, data.toRawUTF8(), (int) data.getNumBytesAsUTF8());
75 }
76 
77 //==============================================================================
79  : Thread ("Discovery_listen"), serviceTypeUID (serviceType)
80 {
81  #if JUCE_ANDROID
82  acquireMulticastLock();
83  #endif
84 
85  socket.bindToPort (broadcastPort);
86  startThread (2);
87 }
88 
90 {
91  socket.shutdown();
92  stopThread (2000);
93 
94  #if JUCE_ANDROID
95  releaseMulticastLock();
96  #endif
97 }
98 
99 void NetworkServiceDiscovery::AvailableServiceList::run()
100 {
101  while (! threadShouldExit())
102  {
103  if (socket.waitUntilReady (true, 200) == 1)
104  {
105  char buffer[1024];
106  auto bytesRead = socket.read (buffer, sizeof (buffer) - 1, false);
107 
108  if (bytesRead > 10)
109  if (auto xml = parseXML (String (CharPointer_UTF8 (buffer),
110  CharPointer_UTF8 (buffer + bytesRead))))
111  if (xml->hasTagName (serviceTypeUID))
112  handleMessage (*xml);
113  }
114 
115  removeTimedOutServices();
116  }
117 }
118 
119 std::vector<NetworkServiceDiscovery::Service> NetworkServiceDiscovery::AvailableServiceList::getServices() const
120 {
121  const ScopedLock sl (listLock);
122  auto listCopy = services;
123  return listCopy;
124 }
125 
126 void NetworkServiceDiscovery::AvailableServiceList::handleAsyncUpdate()
127 {
128  if (onChange != nullptr)
129  onChange();
130 }
131 
132 void NetworkServiceDiscovery::AvailableServiceList::handleMessage (const XmlElement& xml)
133 {
134  Service service;
135  service.instanceID = xml.getStringAttribute ("id");
136 
137  if (service.instanceID.trim().isNotEmpty())
138  {
139  service.description = xml.getStringAttribute ("name");
140  service.address = IPAddress (xml.getStringAttribute ("address"));
141  service.port = xml.getIntAttribute ("port");
142  service.lastSeen = Time::getCurrentTime();
143 
144  handleMessage (service);
145  }
146 }
147 
148 static void sortServiceList (std::vector<NetworkServiceDiscovery::Service>& services)
149 {
150  auto compareServices = [] (const NetworkServiceDiscovery::Service& s1,
151  const NetworkServiceDiscovery::Service& s2)
152  {
153  return s1.instanceID < s2.instanceID;
154  };
155 
156  std::sort (services.begin(), services.end(), compareServices);
157 }
158 
159 void NetworkServiceDiscovery::AvailableServiceList::handleMessage (const Service& service)
160 {
161  const ScopedLock sl (listLock);
162 
163  for (auto& s : services)
164  {
165  if (s.instanceID == service.instanceID)
166  {
167  if (s.description != service.description
168  || s.address != service.address
169  || s.port != service.port)
170  {
171  s = service;
172  triggerAsyncUpdate();
173  }
174 
175  s.lastSeen = service.lastSeen;
176  return;
177  }
178  }
179 
180  services.push_back (service);
181  sortServiceList (services);
182  triggerAsyncUpdate();
183 }
184 
185 void NetworkServiceDiscovery::AvailableServiceList::removeTimedOutServices()
186 {
187  const double timeoutSeconds = 5.0;
188  auto oldestAllowedTime = Time::getCurrentTime() - RelativeTime::seconds (timeoutSeconds);
189 
190  const ScopedLock sl (listLock);
191 
192  auto oldEnd = std::end (services);
193  auto newEnd = std::remove_if (std::begin (services), oldEnd,
194  [=] (const Service& s) { return s.lastSeen < oldestAllowedTime; });
195 
196  if (newEnd != oldEnd)
197  {
198  services.erase (newEnd, oldEnd);
199  triggerAsyncUpdate();
200  }
201 }
202 
203 } // namespace juce
Wraps a pointer to a null-terminated UTF-8 character string, and provides various methods to operate ...
bool bindToPort(int localPortNumber)
Binds the socket to the specified local port.
Automatically locks and unlocks a mutex object.
Represents an IP address.
static IPAddress getLocalAddress(bool includeIPv6=false)
Returns the first 'real' address for the local machine.
static IPAddress getInterfaceBroadcastAddress(const IPAddress &interfaceAddress)
If the IPAdress is the address of an interface on the machine, returns the associated broadcast addre...
A relative measure of time.
static RelativeTime seconds(double seconds) noexcept
Creates a new RelativeTime object representing a number of seconds.
The JUCE String class!
Definition: juce_String.h:43
Encapsulates a thread.
Definition: juce_Thread.h:47
void startThread()
Starts the thread running.
static Time JUCE_CALLTYPE getCurrentTime() noexcept
Returns a Time object that is set to the current system time.
Definition: juce_Time.cpp:218
A universally unique 128-bit identifier.
Definition: juce_Uuid.h:43
Used to build a tree of elements representing an XML document.
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
Returns the value of a named attribute as an integer.
const String & getStringAttribute(StringRef attributeName) const noexcept
Returns the value of a named attribute.
void setAttribute(const Identifier &attributeName, const String &newValue)
Adds a named attribute to the element.
Advertiser(const String &serviceTypeUID, const String &serviceDescription, int broadcastPort, int connectionPort, RelativeTime minTimeBetweenBroadcasts=RelativeTime::seconds(1.5))
Creates and starts an Advertiser thread, broadcasting with the given properties.
AvailableServiceList(const String &serviceTypeUID, int broadcastPort)
Creates an AvailableServiceList that will bind to the given port number and watch the network for Adv...
std::vector< Service > getServices() const
Returns a list of the currently known services.