-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCameraImageSource.m
More file actions
493 lines (398 loc) · 13 KB
/
Copy pathCameraImageSource.m
File metadata and controls
493 lines (398 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
//
// CameraImageSource.m
// Gawker
//
// Created by Phil Piwonka on 10/9/05.
// Copyright 2005 Phil Piwonka. All rights reserved.
//
// Based heavily on CSGCamera
// Created by Tim Omernick.
//
#import "CameraImageSource.h"
#import "CameraManager.h"
#import "ImageText.h"
@interface CameraImageSource (PrivateMethods)
- (void)sequenceGrabberIdle;
- (BOOL)setupDecompression;
- (void)didUpdate;
- (NSImage *)imageFromGWorld:(GWorldPtr)gworld;
@end
@interface CameraImageSource (SequenceGrabber)
pascal OSErr CameraImageSourceSGDataProc(SGChannel channel, Ptr data, long dataLength, long *offset, long channelRefCon, TimeValue time, short writeType, long refCon);
@end
@implementation CameraImageSource
// Init and dealloc
- (id)initWithCameraName:(NSString *)name
{
if (self = [super init]) {
isEnabled = NO;
timeZone = [[NSTimeZone localTimeZone] retain];
recentImage = nil;
cameraName = [name retain];
[self setSourceDescription:@"Local Camera"];
NSString *newSubDesc = [NSString stringWithFormat:@"My %@",cameraName];
[self setSourceSubDescription:newSubDesc];
}
return self;
}
- (id)init
{
return [self initWithCameraName:[NSString stringWithString:@"My Camera"]];
}
- (void)dealloc;
{
[self stop];
[timeZone release];
[recentImage release];
[recentTime release];
[cameraName release];
[sourceDescription release];
[sourceSubDescription release];
[super dealloc];
}
- (BOOL)startWithSize:(NSSize)frameSize;
{
if (isEnabled) {
NSLog(@"Camera already on!");
return isEnabled;
}
OSErr theErr;
// Initialize movie toolbox
theErr = EnterMovies();
if (theErr != noErr) {
NSLog(@"EnterMovies() returned %ld", theErr);
return NO;
}
component = 0;
ComponentDescription grabbers;
grabbers.componentType = SeqGrabComponentType;
grabbers.componentSubType = 0;
grabbers.componentManufacturer = 'appl';
grabbers.componentFlags = 0;
grabbers.componentFlagsMask = 0;
NSLog(@"Number of SeqGrab Components: %d", CountComponents(&grabbers));
// Open default sequence grabber component
NSLog(@"Opening Default SeqGrabComponent");
component = OpenDefaultComponent(SeqGrabComponentType, 0);
if (!component) {
NSLog(@"Could not open sequence grabber component.");
return NO;
}
// Initialize sequence grabber component
theErr = SGInitialize(component);
if (theErr != noErr) {
NSLog(@"SGInitialize() returned %ld", theErr);
return NO;
}
if (![CameraManager findCamera:cameraName
inComponent:component
channel:&channel]) {
NSLog(@"Couldn't find \"%@\" in channel list", cameraName);
return NO;
}
// Don't make movie
theErr = SGSetDataRef(component, 0, 0, seqGrabDontMakeMovie);
if (theErr != noErr) {
NSLog(@"SGSetDataRef() returned %ld", theErr);
return NO;
}
// Set the grabber's bounds
bounds.top = 0;
bounds.left = 0;
bounds.bottom = frameSize.height;
bounds.right = frameSize.width;
// Create the GWorld
theErr = QTNewGWorld(&gWorld, k32ARGBPixelFormat, &bounds, 0, NULL, 0);
if (theErr != noErr) {
NSLog(@"QTNewGWorld() returned %ld", theErr);
return NO;
}
// Lock the pixmap
if (!LockPixels(GetPortPixMap(gWorld))) {
NSLog(@"Could not lock pixels.");
return NO;
}
// Set GWorld
theErr = SGSetGWorld(component, gWorld, GetMainDevice());
if (theErr != noErr) {
NSLog(@"SGSetGWorld() returned %ld", theErr);
return NO;
}
// Set the channel's bounds
theErr = SGSetChannelBounds(channel, &bounds);
if (theErr != noErr) {
NSLog(@"SGSetChannelBounds(2) returned %ld", theErr);
return NO;
}
// Set the channel usage to record
theErr = SGSetChannelUsage(channel, seqGrabRecord);
if (theErr != noErr) {
NSLog(@"SGSetChannelUsage() returned %ld", theErr);
return NO;
}
// Set data proc
theErr = SGSetDataProc(component, NewSGDataUPP(&CameraImageSourceSGDataProc), (long)self);
if (theErr != noErr) {
NSLog(@"SGSetDataProc() returned %ld", theErr);
return NO;
}
// Prepare
theErr = SGPrepare(component, false, true);
if (theErr != noErr) {
NSLog(@"SGPrepare() returned %ld", theErr);
return NO;
}
// Start recording
theErr = SGStartRecord(component);
if (theErr != noErr) {
NSLog(@"SGStartRecord() returned %ld", theErr);
return NO;
}
// Set up decompression sequence (camera -> GWorld)
[self setupDecompression];
// Start frame timer
idleTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0/2.0 target:self selector:@selector(sequenceGrabberIdle) userInfo:nil repeats:YES] retain];
isEnabled = YES;
return isEnabled;
}
- (BOOL)stop;
{
// Stop frame timer
if (idleTimer) {
[idleTimer invalidate];
[idleTimer release];
idleTimer = nil;
}
// Stop recording
if (component)
SGStop(component);
ComponentResult theErr;
// End decompression sequence
if (decompressionSequence) {
theErr = CDSequenceEnd(decompressionSequence);
if (theErr != noErr) {
NSLog(@"CDSequenceEnd() returned %ld", theErr);
}
decompressionSequence = 0;
}
// Dispose of channel
if (channel) {
SGDisposeChannel(component, channel);
}
// Close sequence grabber component
if (component) {
theErr = CloseComponent(component);
if (theErr != noErr) {
NSLog(@"CloseComponent() returned %ld", theErr);
}
component = NULL;
}
// Dispose of GWorld
if (gWorld) {
DisposeGWorld(gWorld);
gWorld = NULL;
}
[recentImage release];
recentImage = nil;
isEnabled = NO;
return YES;
}
- (BOOL)isEnabled
{
return isEnabled;
}
- (BOOL)setEnabled:(BOOL)state
{
BOOL success;
if (state) {
success = [self startWithSize:NSMakeSize(640,480)];
}
else {
success = [self stop];
}
if (!success) {
// Something failed
NSLog(@"Notifying of SourceDisconnect");
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:@"SourceDisconnect"
object:self
userInfo:nil];
}
return success;
}
- (NSImage *)recentImage
{
return recentImage;
}
- (void)setRecentImage:(NSImage *)newImage
{
[newImage retain];
[recentImage release];
recentImage = newImage;
[recentTime release];
recentTime = [[ImageText timeString:timeZone] retain];
}
- (NSString *)recentTime
{
return recentTime;
}
- (NSString *)sourceDescription
{
return sourceDescription;
}
- (void)setSourceDescription:(NSString *)newDesc
{
[newDesc retain];
[sourceDescription release];
sourceDescription = newDesc;
}
- (NSString *)sourceSubDescription
{
return sourceSubDescription;
}
- (void)setSourceSubDescription:(NSString *)newDesc
{
[newDesc retain];
[sourceSubDescription release];
sourceSubDescription = newDesc;
}
@end
@implementation CameraImageSource (PrivateMethods)
- (void)sequenceGrabberIdle;
{
OSErr theErr;
theErr = SGIdle(component);
if (theErr != noErr) {
NSLog(@"SGIdle returned %ld", theErr);
return;
}
}
- (BOOL)setupDecompression;
{
ComponentResult theErr;
ImageDescriptionHandle imageDesc = (ImageDescriptionHandle)NewHandle(0);
theErr = SGGetChannelSampleDescription(channel, (Handle)imageDesc);
if (theErr != noErr) {
NSLog(@"SGGetChannelSampleDescription() returned %ld", theErr);
return NO;
}
Rect sourceRect;
sourceRect.top = 0;
sourceRect.left = 0;
sourceRect.right = (**imageDesc).width;
sourceRect.bottom = (**imageDesc).height;
MatrixRecord scaleMatrix;
RectMatrix(&scaleMatrix, &sourceRect, &bounds);
theErr = DecompressSequenceBegin(&decompressionSequence, imageDesc, gWorld, NULL, NULL, &scaleMatrix, srcCopy, NULL, 0, codecNormalQuality, bestSpeedCodec);
if (theErr != noErr) {
NSLog(@"DecompressionSequenceBegin() returned %ld", theErr);
return NO;
}
DisposeHandle((Handle)imageDesc);
return YES;
}
- (void)didUpdate;
{
BOOL notifyReadyToShare = NO;
if (!recentImage) {
notifyReadyToShare = YES;
}
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[self setRecentImage:[self imageFromGWorld:gWorld]];
[nc postNotificationName:@"ImageFromSource"
object:self
userInfo:nil];
if (notifyReadyToShare) {
[nc postNotificationName:@"SourceConnect"
object:self
userInfo:nil];
}
}
//
// Thanks to Chris Meyer from http://www.cocoadev.com/
// for imageFromGWorld
//
- (NSImage *)imageFromGWorld:(GWorldPtr)gworld;
{
NSParameterAssert( gworld != NULL );
PixMapHandle pixMapHandle = GetGWorldPixMap( gworld );
if ( LockPixels( pixMapHandle ) )
{
Rect portRect;
GetPortBounds( gworld, &portRect );
int pixels_wide = (portRect.right - portRect.left);
int pixels_high = (portRect.bottom - portRect.top);
int bps = 8;
int spp = 4;
BOOL has_alpha = YES;
NSBitmapImageRep *frameBitmap = [[[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:pixels_wide
pixelsHigh:pixels_high
bitsPerSample:bps
samplesPerPixel:spp
hasAlpha:has_alpha
isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:0
bitsPerPixel:0] autorelease];
CGColorSpaceRef dst_colorspaceref = CGColorSpaceCreateDeviceRGB();
CGImageAlphaInfo dst_alphainfo = has_alpha ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNone;
CGContextRef dst_contextref = CGBitmapContextCreate( [frameBitmap bitmapData],
pixels_wide,
pixels_high,
bps,
[frameBitmap bytesPerRow],
dst_colorspaceref,
dst_alphainfo );
void *pixBaseAddr = GetPixBaseAddr(pixMapHandle);
long pixmapRowBytes = GetPixRowBytes(pixMapHandle);
CGDataProviderRef dataproviderref = CGDataProviderCreateWithData( NULL, pixBaseAddr, pixmapRowBytes * pixels_high, NULL );
int src_bps = 8;
int src_spp = 4;
BOOL src_has_alpha = YES;
CGColorSpaceRef src_colorspaceref = CGColorSpaceCreateDeviceRGB();
CGImageAlphaInfo src_alphainfo = src_has_alpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNone;
CGImageRef src_imageref = CGImageCreate( pixels_wide,
pixels_high,
src_bps,
src_bps * src_spp,
pixmapRowBytes,
src_colorspaceref,
src_alphainfo,
dataproviderref,
NULL,
NO, // shouldInterpolate
kCGRenderingIntentDefault );
CGRect rect = CGRectMake( 0, 0, pixels_wide, pixels_high );
CGContextDrawImage( dst_contextref, rect, src_imageref );
CGImageRelease( src_imageref );
CGColorSpaceRelease( src_colorspaceref );
CGDataProviderRelease( dataproviderref );
CGContextRelease( dst_contextref );
CGColorSpaceRelease( dst_colorspaceref );
UnlockPixels( pixMapHandle );
NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(pixels_wide, pixels_high)];
[image addRepresentation:frameBitmap];
return [image autorelease];
}
return NULL;
}
@end
@implementation CameraImageSource (SequenceGrabber)
pascal OSErr CameraImageSourceSGDataProc(SGChannel channel, Ptr data, long dataLength, long *offset, long channelRefCon, TimeValue time, short writeType, long refCon)
{
CameraImageSource *camera = (CameraImageSource *)refCon;
ComponentResult theErr;
if (camera->gWorld) {
CodecFlags ignore;
theErr = DecompressSequenceFrameS(camera->decompressionSequence, data, dataLength, 0, &ignore, NULL);
if (theErr != noErr) {
NSLog(@"DecompressSequenceFrameS() returned %ld", theErr);
return theErr;
}
}
[camera didUpdate];
return noErr;
}
@end