image_snap 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0a5c9f2c0165330d8ca0ee2f6e05772291b0044d
4
+ data.tar.gz: efbad05a4948beb3acb1ca0d6946138cea469144
5
+ SHA512:
6
+ metadata.gz: 89bca9b26d1bb9e4e2cd52a6518498565230c56aeab20dc22ef1ff6313e93e6eeee3ff99b6072933e3d81227b01642c2d2e51730ddcfa04a655a695c0fb56795
7
+ data.tar.gz: 325cb1ea2850060ba8b4940037e3d3846848319e7d36fd50f12ce79023192a793d8f6ce5512c518ecadb1010facd92a2547f78d4352b84e2bacf0f31f61e6a14
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in image_snap.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Claus Witt
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,14 @@
1
+ # ImageSnap
2
+
3
+ A rubymotion gem for [imagesnap](https://github.com/rharder/imagesnap)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'image_snap'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'image_snap/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "image_snap"
8
+ spec.version = ImageSnap::VERSION
9
+ spec.authors = ["Claus Witt"]
10
+ spec.email = ["claus@wittnezz.dk"]
11
+ spec.summary = %q{Wrapper for image snap}
12
+ spec.description = %q{}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.5"
21
+ spec.add_development_dependency "rake"
22
+ end
@@ -0,0 +1,18 @@
1
+ require "image_snap/version"
2
+
3
+ unless defined?(Motion::Project::Config)
4
+ raise "This file must be required within a RubyMotion project Rakefile."
5
+ end
6
+
7
+ Motion::Project::App.setup do |app|
8
+
9
+ Dir.glob(File.join(File.dirname(__FILE__), "image_snap/**/*.rb")).each do |file|
10
+ app.files.unshift(file)
11
+ end
12
+
13
+ app.vendor_project(File.expand_path(File.join(File.dirname(__FILE__), '../vendor/ImageSnap')), :static)
14
+
15
+ unless app.frameworks.include?("QTKit")
16
+ app.frameworks << "QTKit"
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module ImageSnap
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,77 @@
1
+ //
2
+ // ImageSnap.h
3
+ // ImageSnap
4
+ //
5
+ // Created by Robert Harder on 9/10/09.
6
+ //
7
+ #import <Cocoa/Cocoa.h>
8
+ #import <QTKit/QTKit.h>
9
+ #include "ImageSnap.h"
10
+
11
+
12
+ @interface ImageSnap : NSObject {
13
+
14
+ QTCaptureSession *mCaptureSession;
15
+ QTCaptureDeviceInput *mCaptureDeviceInput;
16
+ QTCaptureDecompressedVideoOutput *mCaptureDecompressedVideoOutput;
17
+ QTCaptureVideoPreviewOutput *mCaptureVideoPreviewOutput;
18
+ CVImageBufferRef mCurrentImageBuffer;
19
+ }
20
+
21
+
22
+ /**
23
+ * Returns all attached QTCaptureDevice objects that have video.
24
+ * This includes video-only devices (QTMediaTypeVideo) and
25
+ * audio/video devices (QTMediaTypeMuxed).
26
+ *
27
+ * @return autoreleased array of video devices
28
+ */
29
+ +(NSArray *)videoDevices;
30
+
31
+ /**
32
+ * Returns the default QTCaptureDevice object for video
33
+ * or nil if none is found.
34
+ */
35
+ +(QTCaptureDevice *)defaultVideoDevice;
36
+
37
+ /**
38
+ * Returns the QTCaptureDevice with the given name
39
+ * or nil if the device cannot be found.
40
+ */
41
+ +(QTCaptureDevice *)deviceNamed:(NSString *)name;
42
+
43
+ /**
44
+ * Writes an NSImage to disk, formatting it according
45
+ * to the file extension. If path is "-" (a dash), then
46
+ * an jpeg representation is written to standard out.
47
+ */
48
+ + (BOOL) saveImage:(NSImage *)image toPath: (NSString*)path;
49
+
50
+ /**
51
+ * Converts an NSImage to raw NSData according to a given
52
+ * format. A simple string search is performed for such
53
+ * characters as jpeg, tiff, png, and so forth.
54
+ */
55
+ +(NSData *)dataFrom:(NSImage *)image asType:(NSString *)format;
56
+
57
+
58
+
59
+ /**
60
+ * Primary one-stop-shopping message for capturing an image.
61
+ * Activates the video source, saves a frame, stops the source,
62
+ * and saves the file.
63
+ */
64
+ +(BOOL)saveSingleSnapshotFrom:(QTCaptureDevice *)device toFile:(NSString *)path;
65
+ +(BOOL)saveSingleSnapshotFrom:(QTCaptureDevice *)device toFile:(NSString *)path withWarmup:(NSNumber *)warmup;
66
+ +(BOOL)saveSingleSnapshotFrom:(QTCaptureDevice *)device toFile:(NSString *)path withWarmup:(NSNumber *)warmup withTimelapse:(NSNumber *)timelapse;
67
+
68
+ -(id)init;
69
+ -(void)dealloc;
70
+
71
+
72
+ -(BOOL)startSession:(QTCaptureDevice *)device;
73
+ -(NSImage *)snapshot;
74
+ -(void)stopSession;
75
+
76
+
77
+ @end
@@ -0,0 +1,708 @@
1
+ //
2
+ // ImageSnap.m
3
+ // ImageSnap
4
+ //
5
+ // Created by Robert Harder on 9/10/09.
6
+ //
7
+
8
+ #import "ImageSnap.h"
9
+
10
+
11
+ #define error(...) fprintf(stderr, __VA_ARGS__)
12
+ #define console(...) (!g_quiet && printf(__VA_ARGS__))
13
+ #define verbose(...) (g_verbose && !g_quiet && fprintf(stderr, __VA_ARGS__))
14
+
15
+ BOOL g_verbose = NO;
16
+ BOOL g_quiet = NO;
17
+ //double g_timelapse = -1;
18
+ NSString *VERSION = @"0.2.5";
19
+
20
+
21
+ @interface ImageSnap()
22
+
23
+
24
+ - (void)captureOutput:(QTCaptureOutput *)captureOutput
25
+ didOutputVideoFrame:(CVImageBufferRef)videoFrame
26
+ withSampleBuffer:(QTSampleBuffer *)sampleBuffer
27
+ fromConnection:(QTCaptureConnection *)connection;
28
+
29
+ @end
30
+
31
+
32
+ @implementation ImageSnap
33
+
34
+
35
+
36
+ - (id)init{
37
+ self = [super init];
38
+ mCaptureSession = nil;
39
+ mCaptureDeviceInput = nil;
40
+ mCaptureDecompressedVideoOutput = nil;
41
+ mCaptureVideoPreviewOutput = nil;
42
+ mCurrentImageBuffer = nil;
43
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onDeviceDisconnected:) name:QTCaptureDeviceWasDisconnectedNotification object:nil];
44
+ return self;
45
+ }
46
+
47
+ - (void)dealloc{
48
+
49
+ if( mCaptureSession ) [mCaptureSession release];
50
+ if( mCaptureDeviceInput ) [mCaptureDeviceInput release];
51
+ if( mCaptureDecompressedVideoOutput ) [mCaptureDecompressedVideoOutput release];
52
+ if( mCaptureVideoPreviewOutput ) [mCaptureVideoPreviewOutput release];
53
+ CVBufferRelease(mCurrentImageBuffer);
54
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
55
+
56
+ [super dealloc];
57
+ }
58
+
59
+
60
+ // Returns an array of video devices attached to this computer.
61
+ + (NSArray *)videoDevices{
62
+ NSMutableArray *results = [NSMutableArray arrayWithCapacity:3];
63
+ [results addObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo]];
64
+ [results addObjectsFromArray:[QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeMuxed]];
65
+ return results;
66
+ }
67
+
68
+ // Returns the default video device or nil if none found.
69
+ + (QTCaptureDevice *)defaultVideoDevice{
70
+ QTCaptureDevice *device = nil;
71
+
72
+ device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo];
73
+ if( device == nil ){
74
+ device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeMuxed];
75
+ }
76
+ return device;
77
+ }
78
+
79
+ // Returns the named capture device or nil if not found.
80
+ +(QTCaptureDevice *)deviceNamed:(NSString *)name{
81
+ QTCaptureDevice *result = nil;
82
+
83
+ NSArray *devices = [ImageSnap videoDevices];
84
+ for( QTCaptureDevice *device in devices ){
85
+ if ( [name isEqualToString:[device description]] ){
86
+ result = device;
87
+ } // end if: match
88
+ } // end for: each device
89
+
90
+ return result;
91
+ } // end
92
+
93
+
94
+ // Saves an image to a file or standard out if path is nil or "-" (hyphen).
95
+ + (BOOL) saveImage:(NSImage *)image toPath: (NSString*)path{
96
+
97
+ NSString *ext = [path pathExtension];
98
+ NSData *photoData = [ImageSnap dataFrom:image asType:ext];
99
+
100
+ // If path is a dash, that means write to standard out
101
+ if( path == nil || [@"-" isEqualToString:path] ){
102
+ NSUInteger length = [photoData length];
103
+ NSUInteger i;
104
+ char *start = (char *)[photoData bytes];
105
+ for( i = 0; i < length; ++i ){
106
+ putc( start[i], stdout );
107
+ } // end for: write out
108
+ return YES;
109
+ } else {
110
+ return [photoData writeToFile:path atomically:NO];
111
+ }
112
+
113
+
114
+ return NO;
115
+ }
116
+
117
+
118
+ /**
119
+ * Converts an NSImage into NSData. Defaults to jpeg if
120
+ * format cannot be determined.
121
+ */
122
+ +(NSData *)dataFrom:(NSImage *)image asType:(NSString *)format{
123
+
124
+ NSData *tiffData = [image TIFFRepresentation];
125
+
126
+ NSBitmapImageFileType imageType = NSJPEGFileType;
127
+ NSDictionary *imageProps = nil;
128
+
129
+
130
+ // TIFF. Special case. Can save immediately.
131
+ if( [@"tif" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ||
132
+ [@"tiff" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ){
133
+ return tiffData;
134
+ }
135
+
136
+ // JPEG
137
+ else if( [@"jpg" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ||
138
+ [@"jpeg" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ){
139
+ imageType = NSJPEGFileType;
140
+ imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.9] forKey:NSImageCompressionFactor];
141
+
142
+ }
143
+
144
+ // PNG
145
+ else if( [@"png" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ){
146
+ imageType = NSPNGFileType;
147
+ }
148
+
149
+ // BMP
150
+ else if( [@"bmp" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ){
151
+ imageType = NSBMPFileType;
152
+ }
153
+
154
+ // GIF
155
+ else if( [@"gif" rangeOfString:format options:NSCaseInsensitiveSearch].location != NSNotFound ){
156
+ imageType = NSGIFFileType;
157
+ }
158
+
159
+ NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:tiffData];
160
+ NSData *photoData = [imageRep representationUsingType:imageType properties:imageProps];
161
+
162
+ return photoData;
163
+ } // end dataFrom
164
+
165
+
166
+
167
+ /**
168
+ * Primary one-stop-shopping message for capturing an image.
169
+ * Activates the video source, saves a frame, stops the source,
170
+ * and saves the file.
171
+ */
172
+
173
+ +(BOOL)saveSingleSnapshotFrom:(QTCaptureDevice *)device toFile:(NSString *)path{
174
+ return [self saveSingleSnapshotFrom:device toFile:path withWarmup:nil];
175
+ }
176
+
177
+ +(BOOL)saveSingleSnapshotFrom:(QTCaptureDevice *)device toFile:(NSString *)path withWarmup:(NSNumber *)warmup{
178
+ return [self saveSingleSnapshotFrom:device toFile:path withWarmup:warmup withTimelapse:nil];
179
+ }
180
+
181
+ +(BOOL)saveSingleSnapshotFrom:(QTCaptureDevice *)device
182
+ toFile:(NSString *)path
183
+ withWarmup:(NSNumber *)warmup
184
+ withTimelapse:(NSNumber *)timelapse{
185
+ ImageSnap *snap;
186
+ NSImage *image = nil;
187
+ double interval = timelapse == nil ? -1 : [timelapse doubleValue];
188
+
189
+ snap = [[ImageSnap alloc] init]; // Instance of this ImageSnap class
190
+ verbose("Starting device...");
191
+ if( [snap startSession:device] ){ // Try starting session
192
+ verbose("Device started.\n");
193
+
194
+ if( warmup == nil ){
195
+ // Skip warmup
196
+ verbose("Skipping warmup period.\n");
197
+ } else {
198
+ double delay = [warmup doubleValue];
199
+ verbose("Delaying %.2lf seconds for warmup...",delay);
200
+ NSDate *now = [[NSDate alloc] init];
201
+ [[NSRunLoop currentRunLoop] runUntilDate:[now dateByAddingTimeInterval: [warmup doubleValue]]];
202
+ [now release];
203
+ verbose("Warmup complete.\n");
204
+ }
205
+
206
+ if ( interval > 0 ) {
207
+
208
+ verbose("Time lapse: snapping every %.2lf seconds to current directory.\n", interval);
209
+
210
+ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
211
+ [dateFormatter setDateFormat:@"yyyy-MM-dd_HH-mm-ss.SSS"];
212
+
213
+ // wait a bit to make sure the camera is initialized
214
+ //[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow: 1.0]];
215
+
216
+ for (unsigned long seq=0; ; seq++)
217
+ {
218
+ NSDate *now = [[NSDate alloc] init];
219
+ NSString *nowstr = [dateFormatter stringFromDate:now];
220
+
221
+ verbose(" - Snapshot %5lu", seq);
222
+ verbose(" (%s)\n", [nowstr UTF8String]);
223
+
224
+ // create filename
225
+ NSString *filename = [NSString stringWithFormat:@"snapshot-%05ld-%s.jpg", seq, [nowstr UTF8String]];
226
+
227
+ // capture and write
228
+ image = [snap snapshot]; // Capture a frame
229
+ if (image != nil) {
230
+ [ImageSnap saveImage:image toPath:filename];
231
+ console( "%s\n", [filename UTF8String]);
232
+ } else {
233
+ error( "Image capture failed.\n" );
234
+ }
235
+
236
+ // sleep
237
+ [[NSRunLoop currentRunLoop] runUntilDate:[now dateByAddingTimeInterval: interval]];
238
+
239
+ [now release];
240
+ }
241
+
242
+ } else {
243
+ image = [snap snapshot]; // Capture a frame
244
+
245
+ }
246
+ //NSLog(@"Stopping...");
247
+ [snap stopSession]; // Stop session
248
+ //NSLog(@"Stopped.");
249
+ } // end if: able to start session
250
+
251
+ [snap release];
252
+
253
+ if ( interval > 0 ){
254
+ return YES;
255
+ } else {
256
+ return image == nil ? NO : [ImageSnap saveImage:image toPath:path];
257
+ }
258
+ } // end
259
+
260
+
261
+ /**
262
+ * Returns current snapshot or nil if there is a problem
263
+ * or session is not started.
264
+ */
265
+ -(NSImage *)snapshot{
266
+ verbose( "Taking snapshot...\n");
267
+
268
+ CVImageBufferRef frame = nil; // Hold frame we find
269
+ while( frame == nil ){ // While waiting for a frame
270
+
271
+ //verbose( "\tEntering synchronized block to see if frame is captured yet...");
272
+ @synchronized(self){ // Lock since capture is on another thread
273
+ frame = mCurrentImageBuffer; // Hold current frame
274
+ CVBufferRetain(frame); // Retain it (OK if nil)
275
+ } // end sync: self
276
+ //verbose( "Done.\n" );
277
+
278
+ if( frame == nil ){ // Still no frame? Wait a little while.
279
+ [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow: 0.1]];
280
+ } // end if: still nothing, wait
281
+
282
+ } // end while: no frame yet
283
+
284
+ // Convert frame to an NSImage
285
+ CIImage *flippedImage = [self flipImage:[CIImage imageWithCVImageBuffer:frame]];
286
+ NSCIImageRep *imageRep = [NSCIImageRep imageRepWithCIImage:flippedImage];
287
+ NSImage *image = [[[NSImage alloc] initWithSize:[imageRep size]] autorelease];
288
+ [image addRepresentation:imageRep];
289
+ CVBufferRelease(frame); // Nick's addition. Maybe horribly unsafe or something, but was leaking crazy memory without this.
290
+ verbose( "Snapshot taken.\n" );
291
+
292
+ return image;
293
+ }
294
+
295
+ - (CIImage *)flipImage:(CIImage *)image {
296
+ CIImage *resultImage = image;
297
+ CIFilter *flipFilter = [CIFilter filterWithName:@"CIAffineTransform"];
298
+ [flipFilter setValue:resultImage forKey:@"inputImage"];
299
+ NSAffineTransform *flipTransform = [NSAffineTransform transform];
300
+ [flipTransform scaleXBy:-1 yBy:1.0]; // horizontal flip
301
+ [flipTransform translateXBy:-1280 yBy:0];
302
+ [flipFilter setValue:flipTransform forKey:@"inputTransform"];
303
+ resultImage = [flipFilter valueForKey:@"outputImage"];
304
+ return resultImage;
305
+ }
306
+
307
+
308
+ /**
309
+ * Blocks until session is stopped.
310
+ */
311
+ -(void)stopSession{
312
+ verbose("Stopping session...\n" );
313
+
314
+ // Make sure we've stopped
315
+ while( mCaptureSession != nil ){
316
+ verbose("\tCaptureSession != nil\n");
317
+
318
+ verbose("\tStopping CaptureSession...");
319
+ [mCaptureSession stopRunning];
320
+ verbose("Done.\n");
321
+
322
+ if( [mCaptureSession isRunning] ){
323
+ verbose( "[mCaptureSession isRunning]");
324
+ [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow: 0.1]];
325
+ }else {
326
+ verbose( "\tShutting down 'stopSession(..)'" );
327
+ if( mCaptureSession ) [mCaptureSession release];
328
+ if( mCaptureDeviceInput ) [mCaptureDeviceInput release];
329
+ if( mCaptureDecompressedVideoOutput ) [mCaptureDecompressedVideoOutput release];
330
+ if( mCaptureVideoPreviewOutput ) [mCaptureVideoPreviewOutput release];
331
+
332
+ mCaptureSession = nil;
333
+ mCaptureDeviceInput = nil;
334
+ mCaptureDecompressedVideoOutput = nil;
335
+ mCaptureVideoPreviewOutput = nil;
336
+ } // end if: stopped
337
+
338
+ } // end while: not stopped
339
+ }
340
+
341
+
342
+ /**
343
+ * Begins the capture session. Frames begin coming in.
344
+ */
345
+ -(BOOL)startSession:(QTCaptureDevice *)device{
346
+
347
+ verbose( "Starting capture session...\n" );
348
+
349
+ if( device == nil ) {
350
+ verbose( "\tCannot start session: no device provided.\n" );
351
+ return NO;
352
+ }
353
+
354
+ NSError *error = nil;
355
+
356
+ // If we've already started with this device, return
357
+ if( [device isEqual:[mCaptureDeviceInput device]] &&
358
+ mCaptureSession != nil &&
359
+ [mCaptureSession isRunning] ){
360
+ return YES;
361
+ } // end if: already running
362
+
363
+ else if( mCaptureSession != nil ){
364
+ verbose( "\tStopping previous session.\n" );
365
+ [self stopSession];
366
+ } // end if: else stop session
367
+
368
+
369
+ // Create the capture session
370
+ verbose( "\tCreating QTCaptureSession..." );
371
+ mCaptureSession = [[QTCaptureSession alloc] init];
372
+ verbose( "Done.\n");
373
+ if( ![device open:&error] ){
374
+ error( "\tCould not create capture session.\n" );
375
+ [mCaptureSession release];
376
+ mCaptureSession = nil;
377
+ return NO;
378
+ }
379
+
380
+
381
+ // Create input object from the device
382
+ verbose( "\tCreating QTCaptureDeviceInput with %s...", [[device description] UTF8String] );
383
+ mCaptureDeviceInput = [[QTCaptureDeviceInput alloc] initWithDevice:device];
384
+ verbose( "Done.\n");
385
+ if (![mCaptureSession addInput:mCaptureDeviceInput error:&error]) {
386
+ error( "\tCould not convert device to input device.\n");
387
+ [mCaptureSession release];
388
+ [mCaptureDeviceInput release];
389
+ mCaptureSession = nil;
390
+ mCaptureDeviceInput = nil;
391
+ return NO;
392
+ }
393
+
394
+
395
+ // // Decompressed video output
396
+ // verbose( "\tCreating QTCaptureDecompressedVideoOutput...");
397
+ // mCaptureDecompressedVideoOutput = [[QTCaptureDecompressedVideoOutput alloc] init];
398
+ // [mCaptureDecompressedVideoOutput setDelegate:self];
399
+ // verbose( "Done.\n" );
400
+ // if (![mCaptureSession addOutput:mCaptureDecompressedVideoOutput error:&error]) {
401
+ // error( "\tCould not create decompressed output.\n");
402
+ // [mCaptureSession release];
403
+ // [mCaptureDeviceInput release];
404
+ // [mCaptureDecompressedVideoOutput release];
405
+ // mCaptureSession = nil;
406
+ // mCaptureDeviceInput = nil;
407
+ // mCaptureDecompressedVideoOutput = nil;
408
+ // return NO;
409
+ // }
410
+
411
+ // Preview video output
412
+ verbose( "\tCreating QTCaptureVideoPreviewOutput...");
413
+ mCaptureVideoPreviewOutput = [[QTCaptureVideoPreviewOutput alloc] init];
414
+ [mCaptureVideoPreviewOutput setDelegate:self];
415
+ verbose( "Done.\n" );
416
+ if (![mCaptureSession addOutput:mCaptureVideoPreviewOutput error:&error]) {
417
+ error( "\tCould not create preview output.\n");
418
+ [mCaptureSession release];
419
+ [mCaptureDeviceInput release];
420
+ [mCaptureVideoPreviewOutput release];
421
+ mCaptureSession = nil;
422
+ mCaptureDeviceInput = nil;
423
+ mCaptureVideoPreviewOutput = nil;
424
+ return NO;
425
+ }
426
+
427
+ // Clear old image?
428
+ verbose("\tEntering synchronized block to clear memory...");
429
+ @synchronized(self){
430
+ if( mCurrentImageBuffer != nil ){
431
+ CVBufferRelease(mCurrentImageBuffer);
432
+ mCurrentImageBuffer = nil;
433
+ } // end if: clear old image
434
+ } // end sync: self
435
+ verbose( "Done.\n");
436
+
437
+ [mCaptureSession startRunning];
438
+ verbose("Session started.\n");
439
+
440
+ return YES;
441
+ } // end startSession
442
+
443
+
444
+
445
+ // This delegate method is called whenever the QTCaptureDecompressedVideoOutput receives a frame
446
+ - (void)captureOutput:(QTCaptureOutput *)captureOutput
447
+ didOutputVideoFrame:(CVImageBufferRef)videoFrame
448
+ withSampleBuffer:(QTSampleBuffer *)sampleBuffer
449
+ fromConnection:(QTCaptureConnection *)connection
450
+ {
451
+ verbose( "." );
452
+ if (videoFrame == nil ) {
453
+ verbose( "'nil' Frame captured.\n" );
454
+ return;
455
+ }
456
+
457
+ // Swap out old frame for new one
458
+ CVImageBufferRef imageBufferToRelease;
459
+ CVBufferRetain(videoFrame);
460
+
461
+ @synchronized(self){
462
+ imageBufferToRelease = mCurrentImageBuffer;
463
+ mCurrentImageBuffer = videoFrame;
464
+ } // end sync
465
+ CVBufferRelease(imageBufferToRelease);
466
+
467
+ }
468
+
469
+ - (void)onDeviceDisconnected:(NSNotification *)note {
470
+ return; // This didn't work
471
+ [self stopSession];
472
+ [self startSession:[ImageSnap defaultVideoDevice]];
473
+ }
474
+
475
+ @end
476
+
477
+
478
+ // //////////////////////////////////////////////////////////
479
+ //
480
+ // //////// B E G I N C - L E V E L M A I N //////// //
481
+ //
482
+ // //////////////////////////////////////////////////////////
483
+
484
+ int processArguments(int argc, const char * argv[]);
485
+ void printUsage(int argc, const char * argv[]);
486
+ int listDevices();
487
+ NSString *generateFilename();
488
+ QTCaptureDevice *getDefaultDevice();
489
+
490
+
491
+ // Main entry point. Since we're using Cocoa and all kinds of fancy
492
+ // classes, we have to set up appropriate pools and loops.
493
+ // Thanks to the example http://lists.apple.com/archives/cocoa-dev/2003/Apr/msg01638.html
494
+ // for reminding me how to do it.
495
+ int mainRemnant (int argc, const char * argv[]) {
496
+ NSApplicationLoad(); // May be necessary for 10.5 not to crash.
497
+
498
+ NSAutoreleasePool *pool;
499
+ pool = [[NSAutoreleasePool alloc] init];
500
+ [NSApplication sharedApplication];
501
+
502
+ int result = processArguments(argc, argv);
503
+
504
+ // [pool release];
505
+ [pool drain];
506
+ return result;
507
+ }
508
+
509
+
510
+
511
+ /**
512
+ * Process command line arguments and execute program.
513
+ */
514
+ int processArguments(int argc, const char * argv[] ){
515
+
516
+ NSString *filename = nil;
517
+ QTCaptureDevice *device = nil;
518
+ NSNumber *warmup = nil;
519
+ NSNumber *timelapse = nil;
520
+
521
+
522
+ int i;
523
+ for( i = 1; i < argc; ++i ){
524
+
525
+ // Handle command line switches
526
+ if (argv[i][0] == '-') {
527
+
528
+ // Dash only? Means write image to stdout
529
+ if( argv[i][1] == 0 ){
530
+ filename = @"-";
531
+ g_quiet = YES;
532
+ } else {
533
+
534
+ // Which switch was given
535
+ switch (argv[i][1]) {
536
+
537
+ // Help
538
+ case '?':
539
+ case 'h':
540
+ printUsage( argc, argv );
541
+ return 0;
542
+ break;
543
+
544
+
545
+ // Verbose
546
+ case 'v':
547
+ g_verbose = YES;
548
+ break;
549
+
550
+ case 'q':
551
+ g_quiet = YES;
552
+ break;
553
+
554
+
555
+ // List devices
556
+ case 'l':
557
+ listDevices();
558
+ return 0;
559
+ break;
560
+
561
+ // Specify device
562
+ case 'd':
563
+ if( i+1 < argc ){
564
+ device = [ImageSnap deviceNamed:[NSString stringWithUTF8String:argv[i+1]]];
565
+ if( device == nil ){
566
+ error( "Device \"%s\" not found.\n", argv[i+1] );
567
+ return 11;
568
+ } // end if: not found
569
+ ++i; // Account for "follow on" argument
570
+ } else {
571
+ error( "Not enough arguments given with 'd' flag.\n" );
572
+ return (int)'d';
573
+ }
574
+ break;
575
+
576
+ // Specify a warmup period before picture snaps
577
+ case 'w':
578
+ if( i+1 < argc ){
579
+ warmup = [NSNumber numberWithFloat:[[NSString stringWithUTF8String:argv[i+1]] floatValue]];
580
+ ++i; // Account for "follow on" argument
581
+ } else {
582
+ error( "Not enough arguments given with 'w' flag.\n" );
583
+ return (int)'w';
584
+ }
585
+ break;
586
+
587
+ // Timelapse
588
+ case 't':
589
+ if( i+1 < argc ){
590
+ timelapse = [NSNumber numberWithDouble:[[NSString stringWithUTF8String:argv[i+1]] doubleValue]];
591
+ //g_timelapse = [timelapse doubleValue];
592
+ ++i; // Account for "follow on" argument
593
+ } else {
594
+ error( "Not enough arguments given with 't' flag.\n" );
595
+ return (int)'t';
596
+ }
597
+ break;
598
+
599
+
600
+
601
+ } // end switch: flag value
602
+ } // end else: not dash only
603
+ } // end if: '-'
604
+
605
+ // Else assume it's a filename
606
+ else {
607
+ filename = [NSString stringWithUTF8String:argv[i]];
608
+ }
609
+
610
+ } // end for: each command line argument
611
+
612
+
613
+ // Make sure we have a filename
614
+ if( filename == nil ){
615
+ filename = generateFilename();
616
+ verbose( "No filename specified. Using %s\n", [filename UTF8String] );
617
+ } // end if: no filename given
618
+
619
+ if( filename == nil ){
620
+ error( "No suitable filename could be determined.\n" );
621
+ return 1;
622
+ }
623
+
624
+
625
+ // Make sure we have a device
626
+ if( device == nil ){
627
+ device = getDefaultDevice();
628
+ verbose( "No device specified. Using %s\n", [[device description] UTF8String] );
629
+ } // end if: no device given
630
+
631
+ if( device == nil ){
632
+ error( "No video devices found.\n" );
633
+ return 2;
634
+ } else {
635
+ console( "Capturing image from device \"%s\"...", [[device description] UTF8String] );
636
+ }
637
+
638
+
639
+ // Image capture
640
+ if( [ImageSnap saveSingleSnapshotFrom:device toFile:filename withWarmup:warmup withTimelapse:timelapse] ){
641
+ console( "%s\n", [filename UTF8String] );
642
+ } else {
643
+ error( "Error.\n" );
644
+ } // end else
645
+
646
+ return 0;
647
+ }
648
+
649
+
650
+
651
+ void printUsage(int argc, const char * argv[]){
652
+ printf( "USAGE: %s [options] [filename]\n", argv[0] );
653
+ printf( "Version: %s\n", [VERSION UTF8String] );
654
+ printf( "Captures an image from a video device and saves it in a file.\n" );
655
+ printf( "If no device is specified, the system default will be used.\n" );
656
+ printf( "If no filename is specfied, snapshot.jpg will be used.\n" );
657
+ printf( "Supported image types: JPEG, TIFF, PNG, GIF, BMP\n" );
658
+ printf( " -h This help message\n" );
659
+ printf( " -v Verbose mode\n");
660
+ printf( " -l List available video devices\n" );
661
+ printf( " -t x.xx Take a picture every x.xx seconds\n" );
662
+ printf( " -q Quiet mode. Do not output any text\n");
663
+ printf( " -w x.xx Warmup. Delay snapshot x.xx seconds after turning on camera\n" );
664
+ printf( " -d device Use named video device\n" );
665
+ }
666
+
667
+
668
+
669
+
670
+
671
+ /**
672
+ * Prints a list of video capture devices to standard out.
673
+ */
674
+ int listDevices(){
675
+ NSArray *devices = [ImageSnap videoDevices];
676
+
677
+ [devices count] > 0
678
+ ? printf("Video Devices:\n")
679
+ : printf("No video devices found.\n");
680
+
681
+ for( QTCaptureDevice *device in devices ){
682
+ printf( "%s\n", [[device description] UTF8String] );
683
+ } // end for: each device
684
+ return (int)[devices count];
685
+ }
686
+
687
+ /**
688
+ * Generates a filename for saving the image, presumably
689
+ * because the user didn't specify a filename.
690
+ * Currently returns snapshot.tiff.
691
+ */
692
+ NSString *generateFilename(){
693
+ NSString *result = @"snapshot.jpg";
694
+ return result;
695
+ } // end
696
+
697
+
698
+ /**
699
+ * Gets a default video device, or nil if none is found.
700
+ * For now, simply queries ImageSnap. May be fancier
701
+ * in the future.
702
+ */
703
+ QTCaptureDevice *getDefaultDevice(){
704
+ return [ImageSnap defaultVideoDevice];
705
+ } // end
706
+
707
+
708
+
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: image_snap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Claus Witt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: ''
42
+ email:
43
+ - claus@wittnezz.dk
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - image_snap.gemspec
54
+ - lib/image_snap.rb
55
+ - lib/image_snap/version.rb
56
+ - vendor/ImageSnap/ImageSnap.h
57
+ - vendor/ImageSnap/ImageSnap.m
58
+ homepage: ''
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.2.1
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Wrapper for image snap
82
+ test_files: []