/filesystems/procfs/sequencegrab/CSGCamera.m

http://macfuse.googlecode.com/ · Objective C · 394 lines · 273 code · 86 blank · 35 comment · 52 complexity · 765be65e04626eaba271d5479686a5b5 MD5 · raw file

  1. //
  2. // CSGCamera.m
  3. // MotionTracker
  4. //
  5. // Created by Tim Omernick on 3/7/05.
  6. // Copyright 2005 Tim Omernick. All rights reserved.
  7. //
  8. // Portions of this file were inspired by Apple Computer, Inc.'s Cocoa SGDataProc example, which can be found here:
  9. // <http://developer.apple.com/samplecode/Cocoa_-_SGDataProc/Cocoa_-_SGDataProc.html>
  10. // Also, I'd like to thank Chris Meyer for his excellent -imageFromGWorld: method, which he gave me permission to use for this framework.
  11. #import "CSGCamera.h"
  12. #import "CSGImage.h"
  13. @interface CSGCamera (Private)
  14. - (void)_sequenceGrabberIdle;
  15. - (BOOL)_setupDecompression;
  16. - (void)_didUpdate;
  17. - (CSGImage *)_imageFromGWorld:(GWorldPtr)gworld;
  18. @end
  19. @interface CSGCamera (SequenceGrabber)
  20. pascal OSErr CSGCameraSGDataProc(SGChannel channel, Ptr data, long dataLength, long *offset, long channelRefCon, TimeValue time, short writeType, long refCon);
  21. @end
  22. @implementation CSGCamera
  23. // Init and dealloc
  24. - (void)dealloc;
  25. {
  26. [self stop];
  27. [delegate release];
  28. [super dealloc];
  29. }
  30. // API
  31. - (void)setDelegate:(id)newDelegate;
  32. {
  33. if (delegate == newDelegate)
  34. return;
  35. [delegate release];
  36. delegate = [newDelegate retain];
  37. }
  38. - (BOOL)startWithSize:(NSSize)frameSize;
  39. {
  40. OSErr theErr;
  41. timeScale = 0;
  42. lastTime = 0;
  43. // Initialize movie toolbox
  44. theErr = EnterMovies();
  45. if (theErr != noErr) {
  46. NSLog(@"EnterMovies() returned %ld", theErr);
  47. return NO;
  48. }
  49. // Open default sequence grabber component
  50. component = OpenDefaultComponent(SeqGrabComponentType, 0);
  51. if (!component) {
  52. NSLog(@"Could not open sequence grabber component.");
  53. return NO;
  54. }
  55. // Initialize sequence grabber component
  56. theErr = SGInitialize(component);
  57. if (theErr != noErr) {
  58. NSLog(@"SGInitialize() returned %ld", theErr);
  59. return NO;
  60. }
  61. // Don't make movie
  62. theErr = SGSetDataRef(component, 0, 0, seqGrabDontMakeMovie);
  63. if (theErr != noErr) {
  64. NSLog(@"SGSetDataRef() returned %ld", theErr);
  65. return NO;
  66. }
  67. // Create sequence grabber video channel
  68. theErr = SGNewChannel(component, VideoMediaType, &channel);
  69. if (theErr != noErr) {
  70. NSLog(@"SGNewChannel() returned %ld", theErr);
  71. return NO;
  72. }
  73. // Set the grabber's bounds
  74. boundsRect.top = 0;
  75. boundsRect.left = 0;
  76. boundsRect.bottom = frameSize.height;
  77. boundsRect.right = frameSize.width;
  78. // NSLog(@"boundsRect=(%d, %d, %d, %d)", boundsRect.top, boundsRect.left, boundsRect.bottom, boundsRect.right);
  79. theErr = SGSetChannelBounds(component, &boundsRect);
  80. // Create the GWorld
  81. theErr = QTNewGWorld(&gWorld, k32ARGBPixelFormat, &boundsRect, 0, NULL, 0);
  82. if (theErr != noErr) {
  83. NSLog(@"QTNewGWorld() returned %ld", theErr);
  84. return NO;
  85. }
  86. // Lock the pixmap
  87. if (!LockPixels(GetPortPixMap(gWorld))) {
  88. NSLog(@"Could not lock pixels.");
  89. return NO;
  90. }
  91. // Set GWorld
  92. theErr = SGSetGWorld(component, gWorld, GetMainDevice());
  93. if (theErr != noErr) {
  94. NSLog(@"SGSetGWorld() returned %ld", theErr);
  95. return NO;
  96. }
  97. // Set the channel's bounds
  98. theErr = SGSetChannelBounds(channel, &boundsRect);
  99. if (theErr != noErr) {
  100. NSLog(@"SGSetChannelBounds(2) returned %ld", theErr);
  101. return NO;
  102. }
  103. // Set the channel usage to record
  104. theErr = SGSetChannelUsage(channel, seqGrabRecord);
  105. if (theErr != noErr) {
  106. NSLog(@"SGSetChannelUsage() returned %ld", theErr);
  107. return NO;
  108. }
  109. // Set data proc
  110. theErr = SGSetDataProc(component, NewSGDataUPP(&CSGCameraSGDataProc), (long)self);
  111. if (theErr != noErr) {
  112. NSLog(@"SGSetDataProc() returned %ld", theErr);
  113. return NO;
  114. }
  115. // Prepare
  116. theErr = SGPrepare(component, false, true);
  117. if (theErr != noErr) {
  118. NSLog(@"SGPrepare() returned %ld", theErr);
  119. return NO;
  120. }
  121. // Start recording
  122. theErr = SGStartRecord(component);
  123. if (theErr != noErr) {
  124. NSLog(@"SGStartRecord() returned %ld", theErr);
  125. return NO;
  126. }
  127. startTime = [NSDate timeIntervalSinceReferenceDate];
  128. // Set up decompression sequence (camera -> GWorld)
  129. [self _setupDecompression];
  130. // Start frame timer
  131. frameTimer = [[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(_sequenceGrabberIdle) userInfo:nil repeats:YES] retain];
  132. [self retain]; // Matches autorelease in -stop
  133. return YES;
  134. }
  135. - (BOOL)stop;
  136. {
  137. // Stop frame timer
  138. if (frameTimer) {
  139. [frameTimer invalidate];
  140. [frameTimer release];
  141. frameTimer = nil;
  142. }
  143. // Stop recording
  144. if (component)
  145. SGStop(component);
  146. ComponentResult theErr;
  147. // End decompression sequence
  148. if (decompressionSequence) {
  149. theErr = CDSequenceEnd(decompressionSequence);
  150. if (theErr != noErr) {
  151. NSLog(@"CDSequenceEnd() returned %ld", theErr);
  152. }
  153. decompressionSequence = 0;
  154. }
  155. // Close sequence grabber component
  156. if (component) {
  157. theErr = CloseComponent(component);
  158. if (theErr != noErr) {
  159. NSLog(@"CloseComponent() returned %ld", theErr);
  160. }
  161. component = NULL;
  162. }
  163. // Dispose of GWorld
  164. if (gWorld) {
  165. DisposeGWorld(gWorld);
  166. gWorld = NULL;
  167. }
  168. [self autorelease]; // Matches retain in -start
  169. return YES;
  170. }
  171. @end
  172. @implementation CSGCamera (Private)
  173. - (void)_sequenceGrabberIdle;
  174. {
  175. OSErr theErr;
  176. theErr = SGIdle(component);
  177. if (theErr != noErr) {
  178. NSLog(@"SGIdle returned %ld", theErr);
  179. return;
  180. }
  181. }
  182. - (BOOL)_setupDecompression;
  183. {
  184. ComponentResult theErr;
  185. ImageDescriptionHandle imageDesc = (ImageDescriptionHandle)NewHandle(0);
  186. theErr = SGGetChannelSampleDescription(channel, (Handle)imageDesc);
  187. if (theErr != noErr) {
  188. NSLog(@"SGGetChannelSampleDescription() returned %ld", theErr);
  189. return NO;
  190. }
  191. Rect sourceRect;
  192. sourceRect.top = 0;
  193. sourceRect.left = 0;
  194. sourceRect.right = (**imageDesc).width;
  195. sourceRect.bottom = (**imageDesc).height;
  196. MatrixRecord scaleMatrix;
  197. RectMatrix(&scaleMatrix, &sourceRect, &boundsRect);
  198. theErr = DecompressSequenceBegin(&decompressionSequence, imageDesc, gWorld, NULL, NULL, &scaleMatrix, srcCopy, NULL, 0, codecNormalQuality, bestSpeedCodec);
  199. if (theErr != noErr) {
  200. NSLog(@"DecompressionSequenceBegin() returned %ld", theErr);
  201. return NO;
  202. }
  203. DisposeHandle((Handle)imageDesc);
  204. return YES;
  205. }
  206. - (void)_didUpdate;
  207. {
  208. if ([delegate respondsToSelector:@selector(camera:didReceiveFrame:)]) {
  209. CSGImage *frameImage = [self _imageFromGWorld:gWorld];
  210. if (frameImage) {
  211. [frameImage setSampleTime:startTime + ((double)lastTime / (double)timeScale)];
  212. [delegate camera:self didReceiveFrame:frameImage];
  213. }
  214. }
  215. }
  216. // Thanks to Chris Meyer from http://www.cocoadev.com/
  217. - (CSGImage *)_imageFromGWorld:(GWorldPtr)gworld;
  218. {
  219. NSParameterAssert( gworld != NULL );
  220. PixMapHandle pixMapHandle = GetGWorldPixMap( gworld );
  221. if ( LockPixels( pixMapHandle ) )
  222. {
  223. Rect portRect;
  224. GetPortBounds( gworld, &portRect );
  225. int pixels_wide = (portRect.right - portRect.left);
  226. int pixels_high = (portRect.bottom - portRect.top);
  227. int bps = 8;
  228. int spp = 4;
  229. BOOL has_alpha = YES;
  230. NSBitmapImageRep *frameBitmap = [[[NSBitmapImageRep alloc]
  231. initWithBitmapDataPlanes:NULL
  232. pixelsWide:pixels_wide
  233. pixelsHigh:pixels_high
  234. bitsPerSample:bps
  235. samplesPerPixel:spp
  236. hasAlpha:has_alpha
  237. isPlanar:NO
  238. colorSpaceName:NSDeviceRGBColorSpace
  239. bytesPerRow:0
  240. bitsPerPixel:0] autorelease];
  241. CGColorSpaceRef dst_colorspaceref = CGColorSpaceCreateDeviceRGB();
  242. CGImageAlphaInfo dst_alphainfo = has_alpha ? kCGImageAlphaPremultipliedLast : kCGImageAlphaNone;
  243. CGContextRef dst_contextref = CGBitmapContextCreate( [frameBitmap bitmapData],
  244. pixels_wide,
  245. pixels_high,
  246. bps,
  247. [frameBitmap bytesPerRow],
  248. dst_colorspaceref,
  249. dst_alphainfo );
  250. void *pixBaseAddr = GetPixBaseAddr(pixMapHandle);
  251. long pixmapRowBytes = GetPixRowBytes(pixMapHandle);
  252. CGDataProviderRef dataproviderref = CGDataProviderCreateWithData( NULL, pixBaseAddr, pixmapRowBytes * pixels_high, NULL );
  253. int src_bps = 8;
  254. int src_spp = 4;
  255. BOOL src_has_alpha = YES;
  256. CGColorSpaceRef src_colorspaceref = CGColorSpaceCreateDeviceRGB();
  257. CGImageAlphaInfo src_alphainfo = src_has_alpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNone;
  258. CGImageRef src_imageref = CGImageCreate( pixels_wide,
  259. pixels_high,
  260. src_bps,
  261. src_bps * src_spp,
  262. pixmapRowBytes,
  263. src_colorspaceref,
  264. src_alphainfo,
  265. dataproviderref,
  266. NULL,
  267. NO, // shouldInterpolate
  268. kCGRenderingIntentDefault );
  269. CGRect rect = CGRectMake( 0, 0, pixels_wide, pixels_high );
  270. CGContextDrawImage( dst_contextref, rect, src_imageref );
  271. CGImageRelease( src_imageref );
  272. CGColorSpaceRelease( src_colorspaceref );
  273. CGDataProviderRelease( dataproviderref );
  274. CGContextRelease( dst_contextref );
  275. CGColorSpaceRelease( dst_colorspaceref );
  276. UnlockPixels( pixMapHandle );
  277. CSGImage *image = [[CSGImage alloc] initWithSize:NSMakeSize(pixels_wide, pixels_high)];
  278. [image addRepresentation:frameBitmap];
  279. return [image autorelease];
  280. }
  281. return NULL;
  282. }
  283. @end
  284. @implementation CSGCamera (SequenceGrabber)
  285. pascal OSErr CSGCameraSGDataProc(SGChannel channel, Ptr data, long dataLength, long *offset, long channelRefCon, TimeValue time, short writeType, long refCon)
  286. {
  287. CSGCamera *camera = (CSGCamera *)refCon;
  288. ComponentResult theErr;
  289. if (camera->timeScale == 0) {
  290. theErr = SGGetChannelTimeScale(camera->channel, &camera->timeScale);
  291. if (theErr != noErr) {
  292. NSLog(@"SGGetChannelTimeScale() returned %ld", theErr);
  293. return theErr;
  294. }
  295. }
  296. if (camera->gWorld) {
  297. CodecFlags ignore;
  298. theErr = DecompressSequenceFrameS(camera->decompressionSequence, data, dataLength, 0, &ignore, NULL);
  299. if (theErr != noErr) {
  300. NSLog(@"DecompressSequenceFrameS() returned %ld", theErr);
  301. return theErr;
  302. }
  303. }
  304. camera->lastTime = time;
  305. [camera _didUpdate];
  306. return noErr;
  307. }
  308. @end