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