Syphon sources now use a subclass of CAOpenGLLayer and render the texture directly via OpenGL, instead of going through a CIImage/IOSurface.

This commit is contained in:
Zakk 2015-02-16 05:15:46 -05:00
parent 1c1ce046c5
commit 23162314c0
8 changed files with 265 additions and 8 deletions

View file

@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
3429A0491A41063900EE702C /* CSSyphonInjectCaptureViewNotInstalled.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3429A0481A41063900EE702C /* CSSyphonInjectCaptureViewNotInstalled.xib */; };
3479A99A1A91C5A500A524F0 /* CSSyphonCaptureLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3479A9991A91C5A500A524F0 /* CSSyphonCaptureLayer.m */; };
34AFC1D719B04EB90007C07B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34AFC1D619B04EB90007C07B /* Cocoa.framework */; };
34AFC1E119B04EB90007C07B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 34AFC1DF19B04EB90007C07B /* InfoPlist.strings */; };
34AFC1F319B04F0B0007C07B /* SyphonCaptureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34AFC1EF19B04F0B0007C07B /* SyphonCaptureViewController.m */; };
@ -67,6 +68,8 @@
344E1EAB1A79481C0081E5E7 /* CSPluginFactoryProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSPluginFactoryProtocol.h; path = PluginHeaders/CSPluginFactoryProtocol.h; sourceTree = "<group>"; };
344E1EAC1A79481C0081E5E7 /* CSExtraPluginProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSExtraPluginProtocol.h; path = PluginHeaders/CSExtraPluginProtocol.h; sourceTree = "<group>"; };
344E1EAD1A79481C0081E5E7 /* CSNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSNotifications.h; path = PluginHeaders/CSNotifications.h; sourceTree = "<group>"; };
3479A9981A91C5A500A524F0 /* CSSyphonCaptureLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSyphonCaptureLayer.h; sourceTree = "<group>"; };
3479A9991A91C5A500A524F0 /* CSSyphonCaptureLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSSyphonCaptureLayer.m; sourceTree = "<group>"; };
34AFC1D319B04EB90007C07B /* CSSyphonCapturePlugin.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CSSyphonCapturePlugin.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
34AFC1D619B04EB90007C07B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
34AFC1D919B04EB90007C07B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@ -177,6 +180,8 @@
34C7B8CA1A34621C0033AC71 /* CSSyphonCaptureFactory.h */,
34C7B8CB1A34621C0033AC71 /* CSSyphonCaptureFactory.m */,
34AFC1DD19B04EB90007C07B /* Supporting Files */,
3479A9981A91C5A500A524F0 /* CSSyphonCaptureLayer.h */,
3479A9991A91C5A500A524F0 /* CSSyphonCaptureLayer.m */,
);
path = CSSyphonCapturePlugin;
sourceTree = "<group>";
@ -300,6 +305,7 @@
34C7B8CC1A34621C0033AC71 /* CSSyphonCaptureFactory.m in Sources */,
34C7B8DB1A3465010033AC71 /* CSSyphonInjectCaptureViewController.m in Sources */,
34AFC1F519B04F0B0007C07B /* SyphonCapture.m in Sources */,
3479A99A1A91C5A500A524F0 /* CSSyphonCaptureLayer.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

@ -0,0 +1,29 @@
//
// CSSyphonCaptureLayer.h
// CSSyphonCapturePlugin
//
// Created by Zakk on 2/16/15.
// Copyright (c) 2015 Zakk. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
#import "SyphonBuildMacros.h"
#import "Syphon.h"
@interface CSSyphonCaptureLayer : CAOpenGLLayer
{
CGLContextObj _myCGLContext;
CGRect _lastBounds;
CGSize _lastImageSize;
CGRect _privateCropRect;
CGRect _lastCrop;
CGRect _calculatedCrop;
bool _needsRedraw;
}
@property (strong) SyphonClient *syphonClient;
@property (assign) bool flipImage;
@end

View file

@ -0,0 +1,191 @@
//
// CSSyphonCaptureLayer.m
// CSSyphonCapturePlugin
//
// Created by Zakk on 2/16/15.
// Copyright (c) 2015 Zakk. All rights reserved.
//
#import "CSSyphonCaptureLayer.h"
@implementation CSSyphonCaptureLayer
@synthesize flipImage = _flipImage;
-(instancetype)init
{
if (self = [super init])
{
self.asynchronous = YES;
self.needsDisplayOnBoundsChange = YES;
self.flipImage = NO;
}
return self;
}
-(void)calculateCrop:(CGSize)forSize;
{
CGRect newCrop;
newCrop.origin.x = forSize.width * _privateCropRect.origin.x;
newCrop.origin.y = forSize.height * _privateCropRect.origin.y;
newCrop.size.width = forSize.width * _privateCropRect.size.width;
newCrop.size.height = forSize.height * _privateCropRect.size.height;
//_lastImageSize = forSize;
_calculatedCrop = newCrop;
}
-(void)setContentsRect:(CGRect)contentsRect
{
_privateCropRect = contentsRect;
[self calculateCrop:_lastImageSize];
_needsRedraw = YES;
[self setNeedsDisplay];
}
-(CGRect)contentsRect
{
return _privateCropRect;
}
-(void)setFlipImage:(bool)flipImage
{
_flipImage = flipImage;
_needsRedraw = YES;
[self setNeedsDisplay];
}
-(bool)flipImage
{
return _flipImage;
}
-(CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pf
{
_myCGLContext = [super copyCGLContextForPixelFormat:pf];
return _myCGLContext;
}
-(BOOL)canDrawInCGLContext:(CGLContextObj)ctx pixelFormat:(CGLPixelFormatObj)pf forLayerTime:(CFTimeInterval)t displayTime:(const CVTimeStamp *)ts
{
bool boundsChanged = !CGRectEqualToRect(self.bounds, _lastBounds);
if (boundsChanged || self.syphonClient.hasNewFrame || _needsRedraw)
{
return YES;
}
return NO;
}
-(void)drawInCGLContext:(CGLContextObj)ctx pixelFormat:(CGLPixelFormatObj)pf forLayerTime:(CFTimeInterval)t displayTime:(const CVTimeStamp *)ts
{
CGLContextObj cgl_ctx = ctx;
CGLSetCurrentContext(ctx);
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
SyphonImage *image = [self.syphonClient newFrameImageForContext:cgl_ctx];
if (!image)
{
return;
}
bool imageSizeChanged = !CGSizeEqualToSize(_lastImageSize, image.textureSize);
if (imageSizeChanged)
{
[self calculateCrop:image.textureSize];
}
_lastCrop = _calculatedCrop;
_lastImageSize = image.textureSize;
_lastBounds = self.bounds;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, self.bounds.size.width, 0.0, self.bounds.size.height, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslated(self.bounds.size.width * 0.5, self.bounds.size.height * 0.5, 0.0);
glEnable(GL_TEXTURE_RECTANGLE_EXT);
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, image.textureName);
GLfloat tex_coords[] =
{
_calculatedCrop.origin.x , _calculatedCrop.origin.y,
_calculatedCrop.origin.x+_calculatedCrop.size.width, _calculatedCrop.origin.y,
_calculatedCrop.origin.x+_calculatedCrop.size.width, _calculatedCrop.origin.y+_calculatedCrop.size.height,
_calculatedCrop.origin.x, _calculatedCrop.origin.y+_calculatedCrop.size.height
};
NSSize useSize = self.bounds.size;
if ([self.contentsGravity isEqualToString:kCAGravityResizeAspect])
{
float wr = _calculatedCrop.size.width / self.bounds.size.width;
float hr = _calculatedCrop.size.height / self.bounds.size.height;
float ratio = (hr < wr ? wr : hr);
useSize = NSMakeSize(_calculatedCrop.size.width / ratio, _calculatedCrop.size.height / ratio);
}
float halfw = useSize.width * 0.5;
float halfh = useSize.height * 0.5;
if (self.flipImage)
{
halfh *= -1;
}
GLfloat verts[] =
{
-halfw, halfh,
halfw, halfh,
halfw, -halfh,
-halfw, -halfh
};
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glTexCoordPointer(2, GL_FLOAT, 0, tex_coords );
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, verts );
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState(GL_VERTEX_ARRAY);
glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
glDisable(GL_TEXTURE_RECTANGLE_EXT);
[super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts];
_needsRedraw = NO;
}
@end

View file

@ -12,6 +12,7 @@
#import "SyphonBuildMacros.h"
#import "Syphon.h"
#import "CSIOSurfaceLayer.h"
#import "CSSyphonCaptureLayer.h"

View file

@ -107,7 +107,13 @@
-(CALayer *)createNewLayer
{
CSIOSurfaceLayer *newLayer = [CSIOSurfaceLayer layer];
CSSyphonCaptureLayer *newLayer = [CSSyphonCaptureLayer layer];
if (_syphon_client)
{
newLayer.syphonClient = _syphon_client;
}
newLayer.flipImage = self.isFlipped;
return newLayer;
@ -133,6 +139,10 @@
}
/*
-(void)publishSurface:(IOSurfaceRef)surface
{
@ -140,14 +150,18 @@
if (newSeed != _surfaceSeed)
{
CIImage *newImage = [[CIImage alloc] initWithIOSurface:surface plane:0 format:kCIFormatARGB8 options:nil];
CIImage *newImage = [[CIImage alloc] initWithIOSurface:surface plane:0 format:kCIFormatARGB8 options:@{kCIImageColorSpace:[NSNull null]}];
_surfaceSeed = newSeed;
[self updateLayersWithBlock:^(CALayer *layer) {
((CSIOSurfaceLayer *)layer).ioImage = newImage;
}];
}
}
*/
-(void) startSyphon
{
@ -166,22 +180,32 @@
if (_syphonServer)
{
_syphon_client = [[SyphonClient alloc] initWithServerDescription:_syphonServer.copy options:nil newFrameHandler:^(SyphonClient *client) {
//[self publishFrame:client];
/*
//this call retains the surface, so be sure to release it if we don't care about it anymore
IOSurfaceRef newSurface = [client IOSurface];
[self publishSurface:newSurface];
CFRelease(newSurface);
*/
}];
[self updateLayersWithBlock:^(CALayer *layer) {
((CSSyphonCaptureLayer *)layer).syphonClient = _syphon_client;
}];
/*
IOSurfaceRef newSurface = [_syphon_client IOSurface];
if (newSurface)
{
[self publishSurface:newSurface];
CFRelease(newSurface);
}
*/
_syphon_uuid = [[_syphon_client serverDescription] objectForKey:SyphonServerDescriptionUUIDKey];

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="14B25" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="14C109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6250"/>

View file

@ -99,6 +99,8 @@
@interface CSIOSurfaceLayer()
{
CIFilter *_resizeFilter;
CIFilter *_matrixFilter;
}
@property (strong) CIImageWrapper *imageWrapper;
@end
@ -226,7 +228,7 @@
CIImageWrapper *wrappedImage = self.imageWrapper;
CGLSetCurrentContext(ctx);
glClearColor(0,0,0,0);
glClearColor(1,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
@ -266,6 +268,8 @@
croppedImage = [useImage imageByCroppingToRect:_calculatedCrop];
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (self.flipImage)
@ -304,6 +308,8 @@
//croppedImage = [_resizeFilter valueForKey:kCIOutputImageKey];
}
[_ciCtx drawImage:croppedImage inRect:inRect fromRect:croppedImage.extent];

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="14B25" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="14C109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6250"/>