Vuo  2.3.2
VuoSyphonListener.m
Go to the documentation of this file.
1 
10 #import "VuoSyphon.h"
11 #import "VuoSyphonListener.h"
12 #include "VuoImageRenderer.h"
13 #include <OpenGL/OpenGL.h>
14 #include <OpenGL/CGLMacro.h>
15 #include "VuoGlContext.h"
16 #include "module.h"
17 #include "node.h"
18 
19 #ifdef VUO_COMPILER
21  "title" : "VuoSyphonListener",
22  "dependencies" : [
23  "VuoSyphonServerNotifier",
24  "AppKit.framework",
25  "VuoGlContext"
26  ]
27  });
28 #endif
29 
30 #include <pthread.h>
31 static pthread_t VuoSyphonListener_mainThread = NULL;
32 
36 static void __attribute__((constructor)) VuoSyphonListener_init()
37 {
38  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
39  VUOLOG_PROFILE_BEGIN(mainQueue);
40  dispatch_sync(dispatch_get_main_queue(), ^{
41  VUOLOG_PROFILE_END(mainQueue);
42  VuoSyphonListener_mainThread = pthread_self();
43  });
44  });
45 }
46 
51 {
52  return VuoSyphonListener_mainThread == pthread_self();
53 }
54 
55 
60 {
61  SyphonImage *frame = (SyphonImage *)image->freeCallbackContext;
62  [frame release];
63 }
64 
65 @implementation VuoSyphonListener
66 
67 @synthesize syphonClient;
68 
72 -(id) init
73 {
74  if (self = [super init])
75  {
76  // Wait for VuoSyphonListener_init() to complete.
77  VUOLOG_PROFILE_BEGIN(mainQueue);
78  dispatch_sync(dispatch_get_main_queue(), ^{
79  VUOLOG_PROFILE_END(mainQueue);
80  });
81 
82  callback = NULL;
83  serverNotifier = NULL;
86  refreshQueue = dispatch_queue_create("vuo.syphon.VuoSyphonListener", 0);
87  }
88  return self;
89 }
90 
94 -(void) startListeningWithServerDescription:(VuoSyphonServerDescription)description callback:(void(*)(VuoImage))receivedFrame
95 {
96  dispatch_sync(refreshQueue, ^{
97 
100  desiredServer = description;
101 
102  callback = receivedFrame;
103 
105  VuoRetain(allServerDescriptions);
106  [self refreshSyphonClientThreadUnsafe:allServerDescriptions];
107  VuoRelease(allServerDescriptions);
108 
109  if (!serverNotifier)
110  {
113  VuoSyphonServerNotifier_setNotificationMethod(serverNotifier, self, @selector(refreshSyphonClient:));
115  }
116  });
117 }
118 
124 -(void) refreshSyphonClientThreadUnsafe:(VuoList_VuoSyphonServerDescription)serverDescriptions
125 {
126  if (syphonClient)
127  {
128  // Already connected to a server — make sure it's still desired and available.
129 
130  __block NSDictionary *currentDict;
132  currentDict = [[syphonClient serverDescription] retain];
133  else
134  {
135  VUOLOG_PROFILE_BEGIN(mainQueue);
136  dispatch_sync(dispatch_get_main_queue(), ^{
137  VUOLOG_PROFILE_END(mainQueue);
138  currentDict = [[syphonClient serverDescription] retain];
139  });
140  }
141 
142  bool isCurrentStillDesired;
143  {
144  VuoText currentName = VuoText_make([[currentDict objectForKey:SyphonServerDescriptionUUIDKey] UTF8String]);
145  VuoText currentAppName = VuoText_make([[currentDict objectForKey:SyphonServerDescriptionUUIDKey] UTF8String]);
146  VuoSyphonServerDescription current = VuoSyphonServerDescription_make(VuoText_make("*"), currentName, currentAppName, true);
148  VuoListAppendValue_VuoSyphonServerDescription(currentAsList, current);
150  isCurrentStillDesired = (VuoListGetCount_VuoSyphonServerDescription(currentMatchingDesired) > 0);
151  }
152 
153  bool isCurrentStillAvailable;
154  {
155  VuoText currentUUID = VuoText_make([[currentDict objectForKey:SyphonServerDescriptionUUIDKey] UTF8String]);
157  VuoList_VuoSyphonServerDescription availableMatchingCurrent = VuoSyphon_filterServerDescriptions(serverDescriptions, current);
158  isCurrentStillAvailable = (VuoListGetCount_VuoSyphonServerDescription(availableMatchingCurrent) > 0);
159  }
160  [currentDict release];
161 
162  if (isCurrentStillDesired && isCurrentStillAvailable)
163  {
164  return;
165  }
166  else
167  {
169  [syphonClient stop];
170  else
171  {
172  VUOLOG_PROFILE_BEGIN(mainQueue);
173  dispatch_sync(dispatch_get_main_queue(), ^{
174  VUOLOG_PROFILE_END(mainQueue);
175  [syphonClient stop];
176  });
177  }
178  self.syphonClient = nil;
179  }
180  }
181 
182  // Not connected to a server — see if a desired one is available.
183 
185  VuoRetain(matchingServers);
186 
187  if (VuoListGetCount_VuoSyphonServerDescription(matchingServers) == 0)
188  {
189  // No desired server is available.
191  [syphonClient stop];
192  else
193  {
194  VUOLOG_PROFILE_BEGIN(mainQueue);
195  dispatch_sync(dispatch_get_main_queue(), ^{
196  VUOLOG_PROFILE_END(mainQueue);
197  [syphonClient stop];
198  });
199  }
200  self.syphonClient = nil;
201  VuoRelease(matchingServers);
202  return;
203  }
204 
206  VuoRelease(matchingServers);
207 
208  // Look up the official server description. (SyphonClient won't accept one constructed from chosenServer's fields.)
209  NSDictionary *chosenServerDict = nil;
210  NSString *chosenUUID = [NSString stringWithUTF8String:chosenServer.serverUUID];
211  NSArray *allServerDicts = [[SyphonServerDirectory sharedDirectory] servers];
212  for (NSDictionary *serverDict in allServerDicts)
213  {
214  if ([chosenUUID isEqualToString:[serverDict objectForKey:SyphonServerDescriptionUUIDKey]])
215  {
216  chosenServerDict = serverDict;
217  break;
218  }
219  }
220 
221  // Connect the client to the server.
222  void (^connect)(void) = ^{
223  __block SyphonClient *s;
224  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
225  s = [[SyphonClient alloc] initWithServerDescription:chosenServerDict context:cgl_ctx options:nil newFrameHandler:^(SyphonClient *client) {
226  SyphonImage *frame = [client newFrameImage];
227 
228  if (frame.textureSize.width < 1 || frame.textureSize.height < 1)
229  {
230  [frame release];
231  return;
232  }
233 
234  VuoImage image = VuoImage_makeClientOwnedGlTextureRectangle(frame.textureName, GL_RGBA, frame.textureSize.width, frame.textureSize.height, VuoSyphonListener_freeSyphonImageCallback, frame);
235  VuoRetain(image);
236  callback(VuoImage_makeCopy(image, false, 0, 0, false));
237  VuoRelease(image);
238 
239  }];
240  });
241  self.syphonClient = s;
242  [s release];
243  };
245  connect();
246  else
247  {
248  VUOLOG_PROFILE_BEGIN(mainQueue);
249  dispatch_sync(dispatch_get_main_queue(), ^{
250  VUOLOG_PROFILE_END(mainQueue);
251  connect();
252  });
253  }
254 }
255 
259 -(void) refreshSyphonClient:(VuoList_VuoSyphonServerDescription)serverDescriptions
260 {
261  dispatch_async(refreshQueue, ^{
262  [self refreshSyphonClientThreadUnsafe:serverDescriptions];
263  });
264 }
265 
269 -(void) stopListening
270 {
271  dispatch_sync(refreshQueue, ^{
272 
275  serverNotifier = NULL;
276 
277  VUOLOG_PROFILE_BEGIN(mainQueue);
278  dispatch_sync(dispatch_get_main_queue(), ^{
279  VUOLOG_PROFILE_END(mainQueue);
280  [syphonClient stop];
281  });
282  self.syphonClient = nil;
283  });
284 }
285 
286 -(void) dealloc
287 {
289  dispatch_release(refreshQueue);
290  [super dealloc];
291 }
292 
293 @end