Vuo  2.0.0
VuoScreenCapture.m
Go to the documentation of this file.
1 
10 #include "module.h"
11 #include "VuoScreenCapture.h"
12 #include"VuoGlPool.h"
13 
14 #ifndef NS_RETURNS_INNER_POINTER
15 #define NS_RETURNS_INNER_POINTER
16 #endif
17 #import <AVFoundation/AVFoundation.h>
18 #import <OpenGL/gl.h>
19 
20 #ifdef VUO_COMPILER
22  "title" : "VuoScreenCapture",
23  "dependencies" : [
24  "AVFoundation.framework",
25  "CoreMedia.framework",
26  "CoreVideo.framework"
27  ]
28  });
29 #endif
30 
32 
36 typedef struct
37 {
38  CVOpenGLTextureCacheRef textureCache;
39  dispatch_queue_t queue;
40  AVCaptureSession *session;
42  void (*capturedImageTrigger)(VuoImage image);
44 
48 static void VuoScreenCapture_freeCallback(VuoImage imageToFree)
49 {
50 }
51 
55 @interface VuoScreenCaptureDelegate : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
57 @end
58 
59 @implementation VuoScreenCaptureDelegate
65 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
66 {
67  CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
68 
69  __block CVOpenGLTextureRef texture;
70  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
71  CVOpenGLTextureCacheCreateTextureFromImage(NULL, _sci->textureCache, pixelBuffer, NULL, &texture);
72  });
73 
75  CVOpenGLTextureGetName(texture),
76  GL_RGB,
77  CVPixelBufferGetWidth(pixelBuffer),
78  CVPixelBufferGetHeight(pixelBuffer),
80  VuoRetain(rectImage);
81  VuoImage image = VuoImage_makeCopy(rectImage, CVOpenGLTextureIsFlipped(texture), 0, 0, false);
82  _sci->capturedImageTrigger(image);
83  CVOpenGLTextureRelease(texture);
84  VuoRelease(rectImage);
85 
86  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
87  CVOpenGLTextureCacheFlush(_sci->textureCache, 0);
88  });
89 }
90 @end
91 
95 void VuoScreenCapture_free(void *p)
96 {
98 
99  VUOLOG_PROFILE_BEGIN(mainQueue);
100  dispatch_sync(dispatch_get_main_queue(), ^{
101  VUOLOG_PROFILE_END(mainQueue);
102  [sci->session stopRunning];
103  });
104  dispatch_sync(sci->queue, ^{});
105 
106  [sci->session release];
107  [sci->delegate release];
108 
109  if (sci->textureCache)
110  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
111  CVOpenGLTextureCacheRelease(sci->textureCache);
112  });
113 
114  dispatch_release(sci->queue);
115 }
116 
120 VuoScreenCapture VuoScreenCapture_make(VuoScreen screen, VuoRectangle rectangle, void (*capturedImage)(VuoImage image))
121 {
122  if ( (rectangle.center.x-rectangle.size.x/2.) >= screen.width
123  || (rectangle.center.y-rectangle.size.y/2.) >= screen.height)
124  return NULL;
125 
128 
129  sci->capturedImageTrigger = capturedImage;
130 
131  sci->queue = dispatch_queue_create("org.vuo.VuoScreenCapture", NULL);
132 
133  VUOLOG_PROFILE_BEGIN(mainQueue);
134  dispatch_sync(dispatch_get_main_queue(), ^{
135  VUOLOG_PROFILE_END(mainQueue);
137  sci->delegate.sci = sci;
138 
139  sci->session = [AVCaptureSession new];
140  sci->session.sessionPreset = AVCaptureSessionPresetPhoto;
141 
142  {
143  AVCaptureScreenInput *input = [[AVCaptureScreenInput alloc] initWithDisplayID:screen.id];
144  input.minFrameDuration = CMTimeMake(1, 120);
145 
146  double width = rectangle.size.x;
147  if (width < 1)
148  width = screen.width;
149 
150  double height = rectangle.size.y;
151  if (height < 1)
152  height = screen.height;
153 
154  input.cropRect = CGRectMake(
155  (int)(rectangle.center.x - rectangle.size.x/2.),
156  (int)(screen.height - (rectangle.center.y - rectangle.size.y/2.) - height),
157  (int)width,
158  (int)height);
159  // input.capturesMouseClicks = YES;
160  [sci->session addInput:input];
161  [input release];
162  }
163 
164  {
165  AVCaptureVideoDataOutput *output = [AVCaptureVideoDataOutput new];
166  [output setAlwaysDiscardsLateVideoFrames:YES];
167  [output setSampleBufferDelegate:sci->delegate queue:sci->queue];
168  [sci->session addOutput:output];
169  [output release];
170  }
171  });
172 
173  {
174  CGLPixelFormatObj pf = VuoGlContext_makePlatformPixelFormat(false, false, -1);
175  __block CVReturn ret;
176  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
177  ret = CVOpenGLTextureCacheCreate(NULL, NULL, cgl_ctx, pf, NULL, &sci->textureCache);
178  });
179  CGLReleasePixelFormat(pf);
180 
181  if (ret != kCVReturnSuccess)
182  {
183  VUserLog("Error: Couldn't create texture cache: %d", ret);
185  return NULL;
186  }
187  }
188 
189  {
190  VUOLOG_PROFILE_BEGIN(mainQueue);
191  dispatch_sync(dispatch_get_main_queue(), ^{
192  VUOLOG_PROFILE_END(mainQueue);
193  [sci->session startRunning];
194  });
195  }
196 
197  return (VuoScreenCapture)sci;
198 }