Files
FreeJ/xcode/CVGrabber.mm
xant f05ea9df4d getTexture now returns an autoreleased object. Callers will retain it if necessary
refactoring the feedFrame/renderFrame/getTexture flow to make a more
clever use of memory and autorelease pools

started implementing CVFilter

(on the way of having CoreImage filters usable from javascript)
2010-05-22 11:11:23 +02:00

221 lines
5.7 KiB
Plaintext

/* FreeJ
* (c) Copyright 2009 Andrea Guzzo <xant@dyne.org>
*
* This source code is free software; you can redistribute it and/or
* modify it under the terms of the GNU Public License as published
* by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* This source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* Please refer to the GNU Public License for more details.
*
* You should have received a copy of the GNU Public License along with
* this source code; if not, write to:
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#import <CVGrabber.h>
#import <CFreej.h>
#import <CIAlphaFade.h>
#include <jutils.h>
#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>
#define CV_GRABBER_WIDTH_MAX 640
#define CV_GRABBER_HEIGHT_MAX 480
/* Apple sample code */
@implementation CVGrabber : QTCaptureDecompressedVideoOutput
- (id)init
{
if( self = [super init] ) {
currentFrame = nil;
currentPts = 0;
previousPts = 0;
width = 352;
height = 288;
lock = [[NSRecursiveLock alloc] init];
}
return self;
}
- (void)dealloc
{
[self stopCapture:self];
CVBufferRelease(currentFrame);
[lock release];
[super dealloc];
}
- (void)outputVideoFrame:(CVImageBufferRef)videoFrame withSampleBuffer:(QTSampleBuffer *)sampleBuffer fromConnection:(QTCaptureConnection *)connection
{
// Store the latest frame
// This must be done in a @synchronized block because this delegate method is not called on the main thread
CVImageBufferRef imageBufferToRelease;
CVBufferRetain(videoFrame);
imageBufferToRelease = currentFrame;
currentFrame = videoFrame;
currentPts = (time_t)(1000000L / [sampleBuffer presentationTime].timeScale * [sampleBuffer presentationTime].timeValue);
/* Try to use hosttime of the sample if available, because iSight Pts seems broken */
//NSNumber *hosttime = (NSNumber *)[sampleBuffer attributeForKey:QTSampleBufferHostTimeAttribute];
//if( hosttime ) currentPts = (time_t)AudioConvertHostTimeToNanos([hosttime unsignedLongLongValue])/1000;
[grabberController feedFrame:currentFrame];
if (imageBufferToRelease)
CVBufferRelease(imageBufferToRelease);
}
- (IBAction)startCapture:(id)sender
{
notice( "QTCapture opened" );
bool ret = false;
NSError *o_returnedError;
Context *ctx = [freej getContext];
width = ctx->screen->geo.w;
height = ctx->screen->geo.h;
/* Hack - using max resolution seems to lower cpu consuption for some reason */
int h = (height < CV_GRABBER_HEIGHT_MAX)
? height
: CV_GRABBER_HEIGHT_MAX;
int w = (width < CV_GRABBER_WIDTH_MAX)
? width
: CV_GRABBER_WIDTH_MAX;
device = [QTCaptureDevice defaultInputDeviceWithMediaType: QTMediaTypeVideo];
if( !device )
{
error ( "Can't find any Video device" );
goto error;
}
[device retain];
if( ![device open: &o_returnedError] )
{
error( "Unable to open the capture device (%i)", [o_returnedError code] );
goto error;
}
if( [device isInUseByAnotherApplication] == YES )
{
error( "default capture device is exclusively in use by another application" );
goto error;
}
input = [[QTCaptureDeviceInput alloc] initWithDevice: device];
if( !input )
{
error( "can't create a valid capture input facility" );
goto error;
}
[self setPixelBufferAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:h], kCVPixelBufferHeightKey,
[NSNumber numberWithInt:w], kCVPixelBufferWidthKey,
[NSNumber numberWithInt:kCVPixelFormatType_32ARGB],
(id)kCVPixelBufferPixelFormatTypeKey, nil]];
session = [[QTCaptureSession alloc] init];
ret = [session addInput:input error: &o_returnedError];
if( !ret )
{
error( "default video capture device could not be added to capture session (%i)", [o_returnedError code] );
goto error;
}
ret = [session addOutput:self error: &o_returnedError];
if( !ret )
{
error ( "output could not be added to capture session (%i)", [o_returnedError code] );
goto error;
}
[session startRunning]; // start the capture session
notice( "Video device ready!" );
running = true;
[self setDelegate:grabberController];
[grabberController start];
return;
error:
//[= exitQTKitOnThread];
[input release];
}
- (IBAction)stopCapture:(id)sender
{
[lock lock];
running = false;
[grabberController stop];
if (session) {
[session stopRunning];
if (input) {
[session removeInput:input];
[input release];
input = NULL;
}
[session removeOutput:self];
[session release];
}
/*
if (output) {
[output release];
output = NULL;
}
*/
if (device) {
if ([device isOpen])
[device close];
[device release];
device = NULL;
}
[lock unlock];
}
- (IBAction)toggleCapture:(id)sender
{
if (running)
[self stopCapture:self];
else
[self startCapture:self];
}
@end
/* This 'glue' class is necessary only because the CVGrabber needs to be a subclass
* QTCaptureDecompressedVideoOutput.
*/
@implementation CVGrabberController : CVLayerController
- (id)init
{
exportedFrame = nil;
currentFrame = nil;
return [super init];
}
- (void)dealloc
{
if (currentFrame)
CVPixelBufferRelease(currentFrame);
[super dealloc];
}
@end