Skip to content

Commit

Permalink
Add macam node. Switch from qmake to cmake.
Browse files Browse the repository at this point in the history
  • Loading branch information
smokris committed Jan 20, 2020
1 parent cd64c6d commit cb4bf09
Show file tree
Hide file tree
Showing 8 changed files with 319 additions and 44 deletions.
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
.DS_Store
.qmake.cache
.qmake.stash
Makefile
*.pro.user
smokris/build
24 changes: 14 additions & 10 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
os: osx
osx_image: xcode9.4
osx_image: xcode6.4
language: objective-c
compiler: clang

before_install:
- brew update &> /dev/null
&& travis_wait brew install --force-bottle qt
script:
- curl -OL https://vuo.org/sites/default/files/vuo-1.2.7-editor.zip
&& unzip -q vuo-1.2.7-editor.zip
- curl -OL https://vuo.org/sites/default/files/release/vuo-1.2.8-editor.zip
&& unzip -q vuo-1.2.8-editor.zip
&& curl -OL https://github.com/smokris/macam64/archive/0.1.0.tar.gz
&& tar zxf 0.1.0.tar.gz
&& curl -OL https://github.com/smokris/macam64/releases/download/0.1.0/macam64.zip
&& unzip macam64.zip
&& cd smokris
&& /usr/local/opt/qt/bin/qmake
&& make
&& mkdir build
&& cd build
&& cmake ..
&& cmake --build .
before_deploy:
- zip ../smokris-vuo-nodes.zip *.vuonode
- zip --junk-paths smokris-vuo-nodes.zip smokris.vuonode ../../libmacam64.a
deploy:
provider: releases
api_key:
secure: JBpyTIwjbpKpZmni3f+9QlOFFNvP8QvIuir0piZEZBlNqr3YSIMlloTApml5egyeHxdXxleeZZPfUl8YyJJOdg6jKRAJuXoCsDHyEjmGIcArqcS4/pKPqEjV51nKEUSVzA9dAhkzjbebZVBuYVgAynW5PoMn/u0H80SobfYduJRshhe641wqwFThqh+ff8BorNZFTTyG7be45ora8Cafawo2LXPEdAaoIeQX/v0pqGTinPtNoUZukQq2sTZI/YmiHnYK5/DHMLym5SOAOJNSHw73rtYp37gA1c/kCxji6GAsIkaMCSKW5kfqHn/TliwGBZoyR8tWHGVhfrOy39/wkcz/tEP1SmlO0zqnusd7pfX2DOyeLqgOKZag/p/bwXLDqYqyXkFrUVbuAIzwrJxGcKbg4YFJNqpCNB7HfoTxniuKJzXLaIEzQ2FXa7YWZMt8kAk9mR824X+0gu3tu5yNEh/A4KGHSkDZDQo65WcVWpgrEELVjDcL1Hp7ZXkGxrL3NnXrSPGpvaxtZtif755sjnPSSTf1vqzOM1R/1DPvxSbhY+lQ1ysJQJzl96wIQBfBY0jhLHGSu1ToI24bhCqGhFscyxvDunAyInt++xmwC1J8MVxb3q0viCQaLA7AOx6AVe4y1pyZ8SgPq0pTh44JXC+HkVw8b83L857UaDwvGSM=
secure: dUnGfkU3ezG9jhza2vwaRjxLRtudeGrkxDrEvV0cytjfw21P12A4/NyPk5qESWc9h6vyGao0a+VU92yyUaTPLPsDoDs6ERMDdL7VyDEfVv0EOaSTSt19+EYhnRHffxd3e4E5117wUSI4xgU7/HlV6f7/K1plRCRnIG9QZBW3yOCYj8fvE4BGJz+N/2sl/KYsd3V8jjjveavLA96gKF/CXThQB6s1F6Oc925lEmLnoM09qx+hl0MLq3Alu7YCa4QadlT/DSa67Vz37gFFU1jRRKxGPZR+Knd2qfSzCHnVuzM7vmbhfh2905Ha0l4roASaMB3HS304uNKcvKUMpPPRpJfrDLeiQ1DuRVsmxZYZEWp3Ef7OVJp4zt79Nc/t36yMkf6gDOgoY+FPLiLPnYeek7dPTPrM8jwVvrJ76GFXGOTtwwodo6AAWmZ7QC3h1V3RsCskp2Ks4jSOvjT2FEjbqxgamiGJAl3e9ykoFk1T54Q0+rySsSeMch9/rWVkwdGuuiTSP4aD9Ok3Exw2EGT5SW8JpH6xCUOi2ES1xuH8ykv5S8iSKiYAde0tXDvMuvs7LGH+h4XzVJLQS1cgMccSKNBN6alrYCwlkbpWcS4UqoqnkqCjNdKeDYYK+RHZucMRthkO08L6RPoCI4LCo4KllShqEo/K/OLzkDTL3oBt8lE=
file: ../smokris-vuo-nodes.zip
skip_cleanup: true
on:
repo: smokris/vuo-nodes
tags: true
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ These nodes are built for Vuo 1.2.x. You may need to modify and rebuild them to

## To install
- Go to the [Releases page](https://github.com/smokris/vuo-nodes/releases) and download the latest binary release (not the source code).
- Unzip the archive, and copy all the `.vuonode` files into your `~/Library/Application Support/Vuo/Modules` folder.
- Unzip the archive, and copy its contents into your `~/Library/Application Support/Vuo/Modules` folder.

## Nodes

Node | Output
------------------------------------------- | ------------------------------------------------
`Get Message Values (List)` |
![](smokris/smokris.macam.receive.png)<br>Uses https://github.com/smokris/macam64 to stream video from old USB cameras, such at the Intel QX3 microscope. | <img src="smokris/smokris.macam.receive-output.png" width="320">
![](smokris/smokris.snapshot.png) |
![](smokris/smokris.make.glitch.image.png)<br>Works on Mac OS 10.11 and earlier.<br>In macOS 10.12, Apple changed the OpenGL driver behavior such that this glitch technique is no longer available (see [#1](https://github.com/smokris/vuo-nodes/issues/1)). | <img src="smokris/smokris.make.glitch.image-output.png" width="320">
![](smokris/smokris.make.glitch.points.png) | <img src="smokris/smokris.make.glitch.points-output.png" width="320">
Expand Down
45 changes: 45 additions & 0 deletions smokris/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
cmake_minimum_required(VERSION 3.2)
project(smokris-vuo-nodes)

set(CMAKE_FRAMEWORK_PATH "${PROJECT_BINARY_DIR}/../../Vuo Editor.app/Contents/Frameworks")
find_library(VUO_FRAMEWORK Vuo)

set(userModules "~/Library/Application\ Support/Vuo/Modules")
file(MAKE_DIRECTORY ${userModules})

set(genericNodes
smokris.osc.message.get.list.c
)
set(nodes
smokris.make.glitch.image.c
smokris.make.glitch.points.c
smokris.object.reinterpret.c
smokris.macam.receive.m
smokris.snapshot.c
)

foreach (node ${genericNodes} ${nodes})
set(compiledNode ${PROJECT_BINARY_DIR}/${node})
string(REGEX REPLACE "\\.[cm]$" ".vuonode" compiledNode "${compiledNode}")
add_custom_command(
COMMAND ${VUO_FRAMEWORK}/Helpers/vuo-compile
${PROJECT_SOURCE_DIR}/${node}
--header-search-path ${PROJECT_SOURCE_DIR}/../macam64-0.1.0/driver_core
--header-search-path ${PROJECT_SOURCE_DIR}/../macam64-0.1.0/cameras
--header-search-path ${PROJECT_SOURCE_DIR}/../macam64-0.1.0/utilities
--output ${compiledNode}
OUTPUT ${compiledNode}
)
list(APPEND compiledNodes ${compiledNode})
endforeach()

set(nodeSet ${PROJECT_BINARY_DIR}/smokris.vuonode)
add_custom_command(
DEPENDS ${genericNodes} ${compiledNodes}
COMMAND zip --junk-paths ${PROJECT_BINARY_DIR}/smokris.zip ${compiledNodes}
&& (cd ${PROJECT_SOURCE_DIR} && zip ${PROJECT_BINARY_DIR}/smokris.zip ${genericNodes})
&& mv ${PROJECT_BINARY_DIR}/smokris.zip ${nodeSet}
&& cp ${nodeSet} ${userModules}
OUTPUT ${nodeSet}
)
add_custom_target(nodeSet ALL DEPENDS ${nodeSet})
Binary file added smokris/smokris.macam.receive-output.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
257 changes: 257 additions & 0 deletions smokris/smokris.macam.receive.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/**
* @file
* smokris.macam.receive node implementation.
*
* @copyright Copyright © 2020 Steve Mokris.
* This code may be modified and distributed under the terms of GPL2
* (inherited from macam, which is GPL2).
*/

#include "node.h"
#include "VuoApp.h"

#include <OpenGL/OpenGL.h>
#include <OpenGL/CGLMacro.h>

#define NS_RETURNS_INNER_POINTER
#include "MyCameraCentral.h"

VuoModuleMetadata({
"title": "Receive Webcam Video",
"keywords": [
"usb",
"camera", "capture", "streaming", "record",
"CPiA", "VLSI Vision", "Intel Play QX3 microscope",
],
"version": "1.0.0",
"dependencies": [
"IOKit.framework",
"VuoApp",
"macam64",
],
});

struct nodeInstanceData
{
MyCameraDriver *cam;
void (*receivedImage)(VuoImage image);
bool triggersEnabled;
};

@interface SMokrisMacamDelegate : NSObject
{
struct nodeInstanceData *_context;
}
@end
@implementation SMokrisMacamDelegate

- (id)initWithNode:(struct nodeInstanceData *)context
{
if (self = [super init])
{
_context = context;
}
return self;
}

- (void)imageReady:(MyCameraDriver *)cam
{
if (_context->triggersEnabled && _context->receivedImage)
{
// Flip
unsigned char *in = cam.imageBuffer;
unsigned char *pixels = (unsigned char *)malloc(cam.width*cam.height*3);
short height = cam.height;
short stride = cam.imageBufferRowBytes;
for (short y = 0; y < height; ++y)
memcpy(pixels + y * stride, in + (height - y - 1) * stride, stride);
VuoImage image = VuoImage_makeFromBuffer(pixels, GL_RGB, cam.width, cam.height, VuoImageColorDepth_8, ^(void *pixels){
free(pixels);
});
_context->receivedImage(image);
}

free(cam.imageBuffer);

[cam setImageBuffer:malloc(cam.width*cam.height*3*2) bpp:3 rowBytes:cam.imageBufferRowBytes];
}

- (void)cameraEventHappened:(id)sender event:(CameraEvent)evt
{
if (evt == CameraEventSnapshotButtonDown)
VUserLog("CameraEventSnapshotButtonDown");
else if (evt == CameraEventSnapshotButtonUp)
VUserLog("CameraEventSnapshotButtonUp");
else
VLog("unknown event");
}

- (void)grabFinished:(id)sender withError:(CameraError)err
{
VL();
}

- (void)cameraHasShutDown:(id)sender
{
VL();
}

@end

#define LogFeature(feature) if ([cam feature]) VUserLog(" %s", #feature);
#define LogResolution(resolution, wh) if ([cam findFrameRateForResolution:resolution] > 0) VUserLog(" Supports %-15s (%-11s) @ %d fps", #resolution, #wh, [cam findFrameRateForResolution:resolution]);

struct nodeInstanceData *nodeInstanceInit(
VuoInputData(VuoInteger) device,
VuoInputData(VuoInteger) width,
VuoInputData(VuoInteger) height,
VuoInputData(VuoBoolean) setTopLight,
VuoInputData(VuoBoolean) setBottomLight)
{
struct nodeInstanceData *context = (struct nodeInstanceData *)calloc(1, sizeof(struct nodeInstanceData));
VuoRegister(context, free);

MyCameraCentral *cc = [MyCameraCentral sharedCameraCentral];
if (![cc startupWithNotificationsOnMainThread:NO recognizeLaterPlugins:NO])
{
VUserLog("Macam startup failed");
goto done;
}

VUserLog("Cameras currently connected: %d", cc.numCameras);
for (int i = 0; i < cc.numCameras; ++i)
VUserLog(" Device %d = '%s'", i, [cc nameForID:[cc idOfCameraWithIndex:i]].UTF8String);

unsigned long cid = [cc idOfCameraWithIndex:device];
if (cid == 0)
{
VUserLog("Camera %lld not found", device);
goto done;
}

MyCameraDriver *cam = nil;
CameraError err = [cc useCameraWithID:cid to:&cam acceptDummy:NO];
if (err != CameraErrorOK)
{
VUserLog("Macam couldn't open camera: error %d (%s)", err, [cc localizedCStrForError:err]);
goto done;
}
context->cam = cam;

CameraResolution r = [cam findResolutionForWidth:width height:height];
short fr = [cam findFrameRateForResolution:r];
VLog("res %d = %d",r,[cam supportsResolution:r fps:fr]);
[cam setResolution:r fps:fr];

VUserLog("Selected '%s'%s %dx%d @ %d fps realCamera=%d",
[cam.class cameraName].UTF8String,
cam.hasSpecificName ? VuoText_format(" ('%s')", cam.getSpecificName.UTF8String) : "",
cam.width, cam.height, cam.fps, cam.realCamera);

LogFeature(canSetBrightness);
LogFeature(canSetOffset);
LogFeature(canSetContrast);
LogFeature(canSetSaturation);
LogFeature(canSetHue);
LogFeature(canSetGamma);
LogFeature(canSetSharpness);
LogFeature(canSetGain);
LogFeature(canSetShutter);
LogFeature(canSetAutoGain);
LogFeature(canSetLed);
LogFeature(canSetOrientationTo:NormalOrientation);
LogFeature(canSetOrientationTo:FlipHorizontal);
LogFeature(canSetOrientationTo:InvertVertical);
LogFeature(canSetOrientationTo:Rotate180);
LogFeature(canSetHFlip);
LogFeature(canSetFlicker);
LogFeature(canSetUSBReducedBandwidth);
LogFeature(canSetWhiteBalanceMode);
LogFeature(canSetWhiteBalanceModeTo:WhiteBalanceLinear)
LogFeature(canSetWhiteBalanceModeTo:WhiteBalanceIndoor)
LogFeature(canSetWhiteBalanceModeTo:WhiteBalanceOutdoor)
LogFeature(canSetWhiteBalanceModeTo:WhiteBalanceAutomatic)
LogFeature(canSetWhiteBalanceModeTo:WhiteBalanceManual)
LogFeature(canBlackWhiteMode);
LogFeature(canStoreMedia);

LogResolution(ResolutionSQSIF, 128 x 96 );
LogResolution(ResolutionQSIF , 160 x 120 );
LogResolution(ResolutionQCIF , 176 x 144 );
LogResolution(ResolutionSIF , 320 x 240 );
LogResolution(ResolutionCIF , 352 x 288 );
LogResolution(ResolutionVGA , 640 x 480 );
LogResolution(ResolutionSVGA , 800 x 600 );
LogResolution(ResolutionXGA , 1024 x 768 );
LogResolution(ResolutionUXGA , 1600 x 1200);

SMokrisMacamDelegate *qd = [[SMokrisMacamDelegate alloc] initWithNode:context];
[cam setDelegate:qd];

[cam startGrabbing];
if (!cam.isGrabbing)
{
VUserLog("Macam couldn't start grabbing");
goto done;
}

[cam setImageBuffer:malloc(cam.width*cam.height*3*2) bpp:3 rowBytes:cam.width*3];

if ([cam respondsToSelector:@selector(setTopLight:)])
[cam performSelector:@selector(setTopLight:) withObject:(id)setTopLight];
if ([cam respondsToSelector:@selector(setBottomLight:)])
[cam performSelector:@selector(setBottomLight:) withObject:(id)setBottomLight];

done:
return context;
}

void nodeInstanceTriggerStart(
VuoInstanceData(struct nodeInstanceData *) context,
VuoOutputTrigger(receivedImage, VuoImage, {"eventThrottling":"drop"})
)
{
(*context)->triggersEnabled = true;
(*context)->receivedImage = receivedImage;
}

void nodeInstanceEvent(
VuoInstanceData(struct nodeInstanceData *) context,

VuoInputData(VuoInteger, {"suggestedMin":0, "suggestedMax":15}) device,
VuoInputData(VuoInteger) width,
VuoInputData(VuoInteger) height,

VuoInputData(VuoBoolean) setTopLight,
VuoInputEvent({"data":"setTopLight"}) setTopLightEvent,
VuoInputData(VuoBoolean) setBottomLight,
VuoInputEvent({"data":"setBottomLight"}) setBottomLightEvent
)
{
if ((setTopLightEvent || setBottomLightEvent)
&& [(*context)->cam respondsToSelector:@selector(setTopLight:)]
&& [(*context)->cam respondsToSelector:@selector(setBottomLight:)])
{
[(*context)->cam performSelector:@selector(setTopLight:) withObject:(id)setTopLight];
[(*context)->cam performSelector:@selector(setBottomLight:) withObject:(id)setBottomLight];
}
}

void nodeInstanceTriggerStop(VuoInstanceData(struct nodeInstanceData *) context)
{
(*context)->receivedImage = NULL;
(*context)->triggersEnabled = false;
}

void nodeInstanceFini(VuoInstanceData(struct nodeInstanceData *) context)
{
if ((*context)->cam)
{
[(*context)->cam stopGrabbing];
[(*context)->cam shutdown];

id delegate = (*context)->cam.delegate;
(*context)->cam.delegate = nil;
[delegate release];
}
}
Binary file added smokris/smokris.macam.receive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 0 additions & 29 deletions smokris/smokris.pro

This file was deleted.

0 comments on commit cb4bf09

Please sign in to comment.