image_snap 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []