mirror of
https://github.com/opencv/opencv.git
synced 2024-11-29 22:00:25 +08:00
1029 lines
28 KiB
Plaintext
1029 lines
28 KiB
Plaintext
/*
|
|
* CvCapture.mm
|
|
*
|
|
* Created by Nicholas Butko on 11/3/09.
|
|
* Copyright 2009. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include "precomp.hpp"
|
|
#include "opencv2/imgproc/imgproc.hpp"
|
|
#include <iostream>
|
|
#import <QTKit/QTKit.h>
|
|
|
|
using namespace std;
|
|
|
|
/********************** Declaration of class headers ************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CaptureDelegate Declaration.
|
|
*
|
|
* CaptureDelegate is notified on a separate thread by the OS whenever there
|
|
* is a new frame. When "updateImage" is called from the main thread, it
|
|
* copies this new frame into an IplImage, but only if this frame has not
|
|
* been copied before. When "getOutput" is called from the main thread,
|
|
* it gives the last copied IplImage.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifndef QTKIT_VERSION_7_6_3
|
|
#define QTKIT_VERSION_7_6_3 70603
|
|
#define QTKIT_VERSION_7_0 70000
|
|
#endif
|
|
|
|
#ifndef QTKIT_VERSION_MAX_ALLOWED
|
|
#define QTKIT_VERSION_MAX_ALLOWED QTKIT_VERSION_7_0
|
|
#endif
|
|
|
|
#define DISABLE_AUTO_RESTART 999
|
|
|
|
@interface CaptureDelegate : NSObject
|
|
{
|
|
int newFrame;
|
|
CVImageBufferRef mCurrentImageBuffer;
|
|
char* imagedata;
|
|
IplImage* image;
|
|
char* bgr_imagedata;
|
|
IplImage* bgr_image;
|
|
size_t currSize;
|
|
}
|
|
|
|
- (void)captureOutput:(QTCaptureOutput *)captureOutput
|
|
didOutputVideoFrame:(CVImageBufferRef)videoFrame
|
|
withSampleBuffer:(QTSampleBuffer *)sampleBuffer
|
|
fromConnection:(QTCaptureConnection *)connection;
|
|
|
|
- (void)captureOutput:(QTCaptureOutput *)captureOutput
|
|
didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer
|
|
fromConnection:(QTCaptureConnection *)connection;
|
|
|
|
- (int)updateImage;
|
|
- (IplImage*)getOutput;
|
|
|
|
@end
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CvCaptureCAM Declaration.
|
|
*
|
|
* CvCaptureCAM is the instantiation of a capture source for cameras.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
class CvCaptureCAM : public CvCapture {
|
|
public:
|
|
CvCaptureCAM(int cameraNum = -1) ;
|
|
~CvCaptureCAM();
|
|
virtual bool grabFrame();
|
|
virtual IplImage* retrieveFrame(int);
|
|
virtual IplImage* queryFrame();
|
|
virtual double getProperty(int property_id);
|
|
virtual bool setProperty(int property_id, double value);
|
|
virtual int didStart();
|
|
|
|
|
|
private:
|
|
QTCaptureSession *mCaptureSession;
|
|
QTCaptureDeviceInput *mCaptureDeviceInput;
|
|
QTCaptureDecompressedVideoOutput *mCaptureDecompressedVideoOutput;
|
|
CaptureDelegate* capture;
|
|
|
|
int startCaptureDevice(int cameraNum);
|
|
void stopCaptureDevice();
|
|
|
|
void setWidthHeight();
|
|
bool grabFrame(double timeOut);
|
|
|
|
int camNum;
|
|
int width;
|
|
int height;
|
|
int settingWidth;
|
|
int settingHeight;
|
|
int started;
|
|
int disableAutoRestart;
|
|
|
|
};
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CvCaptureFile Declaration.
|
|
*
|
|
* CvCaptureFile is the instantiation of a capture source for video files.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
class CvCaptureFile : public CvCapture {
|
|
public:
|
|
CvCaptureFile(const char* filename) ;
|
|
~CvCaptureFile();
|
|
virtual bool grabFrame();
|
|
virtual IplImage* retrieveFrame(int);
|
|
virtual IplImage* queryFrame();
|
|
virtual double getProperty(int property_id);
|
|
virtual bool setProperty(int property_id, double value);
|
|
virtual int didStart();
|
|
|
|
|
|
private:
|
|
QTMovie *mCaptureSession;
|
|
|
|
char* imagedata;
|
|
IplImage* image;
|
|
char* bgr_imagedata;
|
|
IplImage* bgr_image;
|
|
size_t currSize;
|
|
|
|
//IplImage* retrieveFrameBitmap();
|
|
IplImage* retrieveFramePixelBuffer();
|
|
double getFPS();
|
|
|
|
int movieWidth;
|
|
int movieHeight;
|
|
double movieFPS;
|
|
double currentFPS;
|
|
double movieDuration;
|
|
int changedPos;
|
|
|
|
int started;
|
|
};
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CvCaptureFile Declaration.
|
|
*
|
|
* CvCaptureFile is the instantiation of a capture source for video files.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
class CvVideoWriter_QT : public CvVideoWriter{
|
|
public:
|
|
CvVideoWriter_QT(const char* filename, int fourcc,
|
|
double fps, CvSize frame_size,
|
|
int is_color=1);
|
|
~CvVideoWriter_QT();
|
|
bool writeFrame(const IplImage* image);
|
|
private:
|
|
IplImage* argbimage;
|
|
QTMovie* mMovie;
|
|
unsigned char* imagedata;
|
|
|
|
NSString* path;
|
|
NSString* codec;
|
|
double movieFPS;
|
|
CvSize movieSize;
|
|
int movieColor;
|
|
};
|
|
|
|
|
|
/****************** Implementation of interface functions ********************/
|
|
|
|
|
|
CvCapture* cvCreateFileCapture_QT(const char* filename) {
|
|
CvCaptureFile *retval = new CvCaptureFile(filename);
|
|
|
|
if(retval->didStart())
|
|
return retval;
|
|
delete retval;
|
|
return NULL;
|
|
}
|
|
|
|
CvCapture* cvCreateCameraCapture_QT(int index ) {
|
|
CvCapture* retval = new CvCaptureCAM(index);
|
|
if (!((CvCaptureCAM *)retval)->didStart())
|
|
cvReleaseCapture(&retval);
|
|
return retval;
|
|
}
|
|
|
|
CvVideoWriter* cvCreateVideoWriter_QT(const char* filename, int fourcc,
|
|
double fps, CvSize frame_size,
|
|
int is_color) {
|
|
return new CvVideoWriter_QT(filename, fourcc, fps, frame_size,is_color);
|
|
}
|
|
|
|
/********************** Implementation of Classes ****************************/
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CvCaptureCAM Implementation.
|
|
*
|
|
* CvCaptureCAM is the instantiation of a capture source for cameras.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CvCaptureCAM::CvCaptureCAM(int cameraNum) {
|
|
mCaptureSession = nil;
|
|
mCaptureDeviceInput = nil;
|
|
mCaptureDecompressedVideoOutput = nil;
|
|
capture = nil;
|
|
|
|
width = 0;
|
|
height = 0;
|
|
settingWidth = 0;
|
|
settingHeight = 0;
|
|
disableAutoRestart = 0;
|
|
|
|
camNum = cameraNum;
|
|
|
|
if (!startCaptureDevice(camNum)) {
|
|
cout << "Warning, camera failed to properly initialize!" << endl;
|
|
started = 0;
|
|
} else {
|
|
started = 1;
|
|
}
|
|
|
|
}
|
|
|
|
CvCaptureCAM::~CvCaptureCAM() {
|
|
stopCaptureDevice();
|
|
|
|
cout << "Cleaned up camera." << endl;
|
|
}
|
|
|
|
int CvCaptureCAM::didStart() {
|
|
return started;
|
|
}
|
|
|
|
|
|
bool CvCaptureCAM::grabFrame() {
|
|
return grabFrame(5);
|
|
}
|
|
|
|
bool CvCaptureCAM::grabFrame(double timeOut) {
|
|
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
double sleepTime = 0.005;
|
|
double total = 0;
|
|
|
|
NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:sleepTime];
|
|
while (![capture updateImage] && (total += sleepTime)<=timeOut &&
|
|
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
|
|
beforeDate:loopUntil])
|
|
loopUntil = [NSDate dateWithTimeIntervalSinceNow:sleepTime];
|
|
|
|
[localpool drain];
|
|
|
|
return total <= timeOut;
|
|
}
|
|
|
|
IplImage* CvCaptureCAM::retrieveFrame(int) {
|
|
return [capture getOutput];
|
|
}
|
|
|
|
IplImage* CvCaptureCAM::queryFrame() {
|
|
while (!grabFrame()) {
|
|
cout << "WARNING: Couldn't grab new frame from camera!!!" << endl;
|
|
/*
|
|
cout << "Attempting to restart camera; set capture property DISABLE_AUTO_RESTART to disable." << endl;
|
|
stopCaptureDevice();
|
|
startCaptureDevice(camNum);
|
|
*/
|
|
}
|
|
return retrieveFrame(0);
|
|
}
|
|
|
|
void CvCaptureCAM::stopCaptureDevice() {
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
|
|
[mCaptureSession stopRunning];
|
|
|
|
QTCaptureDevice *device = [mCaptureDeviceInput device];
|
|
if ([device isOpen]) [device close];
|
|
|
|
[mCaptureSession release];
|
|
[mCaptureDeviceInput release];
|
|
|
|
[mCaptureDecompressedVideoOutput setDelegate:mCaptureDecompressedVideoOutput];
|
|
[mCaptureDecompressedVideoOutput release];
|
|
[capture release];
|
|
[localpool drain];
|
|
|
|
}
|
|
|
|
int CvCaptureCAM::startCaptureDevice(int cameraNum) {
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
|
|
capture = [[CaptureDelegate alloc] init];
|
|
|
|
QTCaptureDevice *device;
|
|
NSArray* devices = [QTCaptureDevice inputDevicesWithMediaType:QTMediaTypeVideo];
|
|
if ([devices count] == 0) {
|
|
cout << "QTKit didn't find any attached Video Input Devices!" << endl;
|
|
[localpool drain];
|
|
return 0;
|
|
}
|
|
|
|
if (cameraNum >= 0) {
|
|
camNum = cameraNum % [devices count];
|
|
if (camNum != cameraNum) {
|
|
cout << "Warning: Max Camera Num is " << [devices count]-1 << "; Using camera " << camNum << endl;
|
|
}
|
|
device = [devices objectAtIndex:camNum] ;
|
|
} else {
|
|
device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo] ;
|
|
}
|
|
int success;
|
|
NSError* error;
|
|
|
|
if (device) {
|
|
|
|
success = [device open:&error];
|
|
if (!success) {
|
|
cout << "QTKit failed to open a Video Capture Device" << endl;
|
|
[localpool drain];
|
|
return 0;
|
|
}
|
|
|
|
mCaptureDeviceInput = [[QTCaptureDeviceInput alloc] initWithDevice:device] ;
|
|
mCaptureSession = [[QTCaptureSession alloc] init] ;
|
|
|
|
success = [mCaptureSession addInput:mCaptureDeviceInput error:&error];
|
|
|
|
if (!success) {
|
|
cout << "QTKit failed to start capture session with opened Capture Device" << endl;
|
|
[localpool drain];
|
|
return 0;
|
|
}
|
|
|
|
|
|
mCaptureDecompressedVideoOutput = [[QTCaptureDecompressedVideoOutput alloc] init];
|
|
[mCaptureDecompressedVideoOutput setDelegate:capture];
|
|
NSDictionary *pixelBufferOptions ;
|
|
if (width > 0 && height > 0) {
|
|
pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
[NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey,
|
|
[NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey,
|
|
//[NSNumber numberWithUnsignedInt:k32BGRAPixelFormat], (id)kCVPixelBufferPixelFormatTypeKey,
|
|
[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA],
|
|
(id)kCVPixelBufferPixelFormatTypeKey,
|
|
nil];
|
|
} else {
|
|
pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA],
|
|
(id)kCVPixelBufferPixelFormatTypeKey,
|
|
nil];
|
|
}
|
|
[mCaptureDecompressedVideoOutput setPixelBufferAttributes:pixelBufferOptions];
|
|
|
|
#if QTKIT_VERSION_MAX_ALLOWED >= QTKIT_VERSION_7_6_3
|
|
[mCaptureDecompressedVideoOutput setAutomaticallyDropsLateVideoFrames:YES];
|
|
#endif
|
|
|
|
|
|
success = [mCaptureSession addOutput:mCaptureDecompressedVideoOutput error:&error];
|
|
if (!success) {
|
|
cout << "QTKit failed to add Output to Capture Session" << endl;
|
|
[localpool drain];
|
|
return 0;
|
|
}
|
|
|
|
[mCaptureSession startRunning];
|
|
|
|
grabFrame(60);
|
|
|
|
return 1;
|
|
}
|
|
|
|
[localpool drain];
|
|
return 0;
|
|
}
|
|
|
|
void CvCaptureCAM::setWidthHeight() {
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
NSDictionary* pixelBufferOptions = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
[NSNumber numberWithDouble:1.0*width], (id)kCVPixelBufferWidthKey,
|
|
[NSNumber numberWithDouble:1.0*height], (id)kCVPixelBufferHeightKey,
|
|
[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA],
|
|
(id)kCVPixelBufferPixelFormatTypeKey,
|
|
nil];
|
|
|
|
[mCaptureDecompressedVideoOutput setPixelBufferAttributes:pixelBufferOptions];
|
|
grabFrame(60);
|
|
[localpool drain];
|
|
}
|
|
|
|
|
|
double CvCaptureCAM::getProperty(int property_id){
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
|
|
NSArray* connections = [mCaptureDeviceInput connections];
|
|
QTFormatDescription* format = [[connections objectAtIndex:0] formatDescription];
|
|
NSSize s1 = [[format attributeForKey:QTFormatDescriptionVideoCleanApertureDisplaySizeAttribute] sizeValue];
|
|
|
|
int width=s1.width, height=s1.height;
|
|
switch (property_id) {
|
|
case CV_CAP_PROP_FRAME_WIDTH:
|
|
return width;
|
|
case CV_CAP_PROP_FRAME_HEIGHT:
|
|
return height;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
[localpool drain];
|
|
|
|
}
|
|
|
|
bool CvCaptureCAM::setProperty(int property_id, double value) {
|
|
switch (property_id) {
|
|
case CV_CAP_PROP_FRAME_WIDTH:
|
|
width = value;
|
|
settingWidth = 1;
|
|
if (settingWidth && settingHeight) {
|
|
setWidthHeight();
|
|
settingWidth =0;
|
|
settingHeight = 0;
|
|
}
|
|
return true;
|
|
case CV_CAP_PROP_FRAME_HEIGHT:
|
|
height = value;
|
|
settingHeight = 1;
|
|
if (settingWidth && settingHeight) {
|
|
setWidthHeight();
|
|
settingWidth =0;
|
|
settingHeight = 0;
|
|
}
|
|
return true;
|
|
case DISABLE_AUTO_RESTART:
|
|
disableAutoRestart = value;
|
|
return 1;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CaptureDelegate Implementation.
|
|
*
|
|
* CaptureDelegate is notified on a separate thread by the OS whenever there
|
|
* is a new frame. When "updateImage" is called from the main thread, it
|
|
* copies this new frame into an IplImage, but only if this frame has not
|
|
* been copied before. When "getOutput" is called from the main thread,
|
|
* it gives the last copied IplImage.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
|
|
@implementation CaptureDelegate
|
|
|
|
- (id)init {
|
|
[super init];
|
|
newFrame = 0;
|
|
imagedata = NULL;
|
|
bgr_imagedata = NULL;
|
|
currSize = 0;
|
|
image = NULL;
|
|
bgr_image = NULL;
|
|
return self;
|
|
}
|
|
|
|
|
|
-(void)dealloc {
|
|
if (imagedata != NULL) free(imagedata);
|
|
if (bgr_imagedata != NULL) free(bgr_imagedata);
|
|
cvReleaseImage(&image);
|
|
cvReleaseImage(&bgr_image);
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void)captureOutput:(QTCaptureOutput *)captureOutput
|
|
didOutputVideoFrame:(CVImageBufferRef)videoFrame
|
|
withSampleBuffer:(QTSampleBuffer *)sampleBuffer
|
|
fromConnection:(QTCaptureConnection *)connection {
|
|
|
|
CVBufferRetain(videoFrame);
|
|
CVImageBufferRef imageBufferToRelease = mCurrentImageBuffer;
|
|
|
|
@synchronized (self) {
|
|
|
|
mCurrentImageBuffer = videoFrame;
|
|
newFrame = 1;
|
|
}
|
|
|
|
CVBufferRelease(imageBufferToRelease);
|
|
|
|
}
|
|
- (void)captureOutput:(QTCaptureOutput *)captureOutput
|
|
didDropVideoFrameWithSampleBuffer:(QTSampleBuffer *)sampleBuffer
|
|
fromConnection:(QTCaptureConnection *)connection {
|
|
cout << "Camera dropped frame!" << endl;
|
|
}
|
|
|
|
-(IplImage*) getOutput {
|
|
return bgr_image;
|
|
}
|
|
|
|
-(int) updateImage {
|
|
if (newFrame==0) return 0;
|
|
CVPixelBufferRef pixels;
|
|
|
|
@synchronized (self){
|
|
pixels = CVBufferRetain(mCurrentImageBuffer);
|
|
newFrame = 0;
|
|
}
|
|
|
|
CVPixelBufferLockBaseAddress(pixels, 0);
|
|
uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels);
|
|
|
|
size_t width = CVPixelBufferGetWidth(pixels);
|
|
size_t height = CVPixelBufferGetHeight(pixels);
|
|
size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels);
|
|
|
|
if (rowBytes != 0) {
|
|
|
|
if (currSize != rowBytes*height*sizeof(char)) {
|
|
currSize = rowBytes*height*sizeof(char);
|
|
if (imagedata != NULL) free(imagedata);
|
|
if (bgr_imagedata != NULL) free(bgr_imagedata);
|
|
imagedata = (char*)malloc(currSize);
|
|
bgr_imagedata = (char*)malloc(currSize);
|
|
}
|
|
|
|
memcpy(imagedata, baseaddress, currSize);
|
|
|
|
if (image == NULL) {
|
|
image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 4);
|
|
}
|
|
image->width =width;
|
|
image->height = height;
|
|
image->nChannels = 4;
|
|
image->depth = IPL_DEPTH_8U;
|
|
image->widthStep = rowBytes;
|
|
image->imageData = imagedata;
|
|
image->imageSize = currSize;
|
|
|
|
if (bgr_image == NULL) {
|
|
bgr_image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 3);
|
|
}
|
|
bgr_image->width =width;
|
|
bgr_image->height = height;
|
|
bgr_image->nChannels = 3;
|
|
bgr_image->depth = IPL_DEPTH_8U;
|
|
bgr_image->widthStep = rowBytes;
|
|
bgr_image->imageData = bgr_imagedata;
|
|
bgr_image->imageSize = currSize;
|
|
|
|
cvCvtColor(image, bgr_image, CV_BGRA2BGR);
|
|
|
|
}
|
|
|
|
CVPixelBufferUnlockBaseAddress(pixels, 0);
|
|
CVBufferRelease(pixels);
|
|
|
|
return 1;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CvCaptureFile Implementation.
|
|
*
|
|
* CvCaptureFile is the instantiation of a capture source for video files.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
CvCaptureFile::CvCaptureFile(const char* filename) {
|
|
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
|
|
|
|
mCaptureSession = nil;
|
|
image = NULL;
|
|
bgr_image = NULL;
|
|
imagedata = NULL;
|
|
bgr_imagedata = NULL;
|
|
currSize = 0;
|
|
|
|
movieWidth = 0;
|
|
movieHeight = 0;
|
|
movieFPS = 0;
|
|
currentFPS = 0;
|
|
movieDuration = 0;
|
|
changedPos = 0;
|
|
|
|
started = 0;
|
|
|
|
NSError* error;
|
|
|
|
|
|
mCaptureSession = [[QTMovie movieWithFile:[NSString stringWithCString:filename
|
|
encoding:NSASCIIStringEncoding]
|
|
error:&error] retain];
|
|
[mCaptureSession setAttribute:[NSNumber numberWithBool:YES]
|
|
forKey:QTMovieLoopsAttribute];
|
|
|
|
if (mCaptureSession == nil) {
|
|
cout << "WARNING: Couldn't read movie file " << filename << endl;
|
|
[localpool drain];
|
|
started = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
[mCaptureSession gotoBeginning];
|
|
|
|
NSSize size = [[mCaptureSession attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
|
|
|
|
movieWidth = size.width;
|
|
movieHeight = size.height;
|
|
movieFPS = getFPS();
|
|
currentFPS = movieFPS;
|
|
|
|
QTTime t;
|
|
|
|
[[mCaptureSession attributeForKey:QTMovieDurationAttribute] getValue:&t];
|
|
movieDuration = (t.timeValue *1000.0 / t.timeScale);
|
|
started = 1;
|
|
[localpool drain];
|
|
|
|
}
|
|
|
|
CvCaptureFile::~CvCaptureFile() {
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
if (imagedata != NULL) free(imagedata);
|
|
if (bgr_imagedata != NULL) free(bgr_imagedata);
|
|
cvReleaseImage(&image);
|
|
cvReleaseImage(&bgr_image);
|
|
[mCaptureSession release];
|
|
[localpool drain];
|
|
}
|
|
|
|
int CvCaptureFile::didStart() {
|
|
return started;
|
|
}
|
|
|
|
bool CvCaptureFile::grabFrame() {
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
double t1 = getProperty(CV_CAP_PROP_POS_MSEC);
|
|
[mCaptureSession stepForward];
|
|
double t2 = getProperty(CV_CAP_PROP_POS_MSEC);
|
|
if (t2>t1 && !changedPos) {
|
|
currentFPS = 1000.0/(t2-t1);
|
|
} else {
|
|
currentFPS = movieFPS;
|
|
}
|
|
changedPos = 0;
|
|
[localpool drain];
|
|
return 1;
|
|
}
|
|
|
|
|
|
IplImage* CvCaptureFile::retrieveFramePixelBuffer() {
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
|
|
|
|
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
QTMovieFrameImageTypeCVPixelBufferRef, QTMovieFrameImageType,
|
|
#ifdef MAC_OS_X_VERSION_10_6
|
|
[NSNumber numberWithBool:YES], QTMovieFrameImageSessionMode,
|
|
#endif
|
|
nil];
|
|
CVPixelBufferRef frame = (CVPixelBufferRef)[mCaptureSession frameImageAtTime:[mCaptureSession currentTime]
|
|
withAttributes:attributes
|
|
error:nil];
|
|
|
|
CVPixelBufferRef pixels = CVBufferRetain(frame);
|
|
CVPixelBufferLockBaseAddress(pixels, 0);
|
|
|
|
uint32_t* baseaddress = (uint32_t*)CVPixelBufferGetBaseAddress(pixels);
|
|
size_t width = CVPixelBufferGetWidth(pixels);
|
|
size_t height = CVPixelBufferGetHeight(pixels);
|
|
size_t rowBytes = CVPixelBufferGetBytesPerRow(pixels);
|
|
|
|
if (rowBytes != 0) {
|
|
|
|
if (currSize != rowBytes*height*sizeof(char)) {
|
|
currSize = rowBytes*height*sizeof(char);
|
|
if (imagedata != NULL) free(imagedata);
|
|
if (bgr_imagedata != NULL) free(bgr_imagedata);
|
|
imagedata = (char*)malloc(currSize);
|
|
bgr_imagedata = (char*)malloc(currSize);
|
|
}
|
|
|
|
memcpy(imagedata, baseaddress, currSize);
|
|
|
|
//ARGB -> BGRA
|
|
for (unsigned int i = 0; i < currSize; i+=4) {
|
|
char temp = imagedata[i];
|
|
imagedata[i] = imagedata[i+3];
|
|
imagedata[i+3] = temp;
|
|
temp = imagedata[i+1];
|
|
imagedata[i+1] = imagedata[i+2];
|
|
imagedata[i+2] = temp;
|
|
}
|
|
|
|
if (image == NULL) {
|
|
image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 4);
|
|
}
|
|
|
|
image->width =width;
|
|
image->height = height;
|
|
image->nChannels = 4;
|
|
image->depth = IPL_DEPTH_8U;
|
|
image->widthStep = width*4;
|
|
image->imageData = imagedata;
|
|
image->imageSize = currSize;
|
|
|
|
|
|
if (bgr_image == NULL) {
|
|
bgr_image = cvCreateImageHeader(cvSize(width,height), IPL_DEPTH_8U, 3);
|
|
}
|
|
|
|
bgr_image->width =width;
|
|
bgr_image->height = height;
|
|
bgr_image->nChannels = 3;
|
|
bgr_image->depth = IPL_DEPTH_8U;
|
|
bgr_image->widthStep = width*4;
|
|
bgr_image->imageData = bgr_imagedata;
|
|
bgr_image->imageSize = currSize;
|
|
|
|
cvCvtColor(image, bgr_image,CV_BGRA2BGR);
|
|
|
|
}
|
|
|
|
CVPixelBufferUnlockBaseAddress(pixels, 0);
|
|
CVBufferRelease(pixels);
|
|
|
|
[localpool drain];
|
|
|
|
return bgr_image;
|
|
}
|
|
|
|
|
|
IplImage* CvCaptureFile::retrieveFrame(int) {
|
|
return retrieveFramePixelBuffer();
|
|
}
|
|
|
|
IplImage* CvCaptureFile::queryFrame() {
|
|
grabFrame();
|
|
return retrieveFrame(0);
|
|
}
|
|
|
|
double CvCaptureFile::getFPS() {
|
|
if (mCaptureSession == nil) return 0;
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
double now = getProperty(CV_CAP_PROP_POS_MSEC);
|
|
double retval = 0;
|
|
if (now == 0) {
|
|
[mCaptureSession stepForward];
|
|
double t2 = getProperty(CV_CAP_PROP_POS_MSEC);
|
|
[mCaptureSession stepBackward];
|
|
retval = 1000.0 / (t2-now);
|
|
} else {
|
|
[mCaptureSession stepBackward];
|
|
double t2 = getProperty(CV_CAP_PROP_POS_MSEC);
|
|
[mCaptureSession stepForward];
|
|
retval = 1000.0 / (now-t2);
|
|
}
|
|
[localpool drain];
|
|
return retval;
|
|
}
|
|
|
|
double CvCaptureFile::getProperty(int property_id){
|
|
if (mCaptureSession == nil) return 0;
|
|
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
|
|
double retval;
|
|
QTTime t;
|
|
|
|
switch (property_id) {
|
|
case CV_CAP_PROP_POS_MSEC:
|
|
[[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t];
|
|
retval = t.timeValue * 1000.0 / t.timeScale;
|
|
break;
|
|
case CV_CAP_PROP_POS_FRAMES:
|
|
retval = movieFPS * getProperty(CV_CAP_PROP_POS_MSEC) / 1000;
|
|
break;
|
|
case CV_CAP_PROP_POS_AVI_RATIO:
|
|
retval = (getProperty(CV_CAP_PROP_POS_MSEC)) / (movieDuration );
|
|
break;
|
|
case CV_CAP_PROP_FRAME_WIDTH:
|
|
retval = movieWidth;
|
|
break;
|
|
case CV_CAP_PROP_FRAME_HEIGHT:
|
|
retval = movieHeight;
|
|
break;
|
|
case CV_CAP_PROP_FPS:
|
|
retval = currentFPS;
|
|
break;
|
|
case CV_CAP_PROP_FOURCC:
|
|
default:
|
|
retval = 0;
|
|
}
|
|
|
|
[localpool drain];
|
|
return retval;
|
|
}
|
|
|
|
bool CvCaptureFile::setProperty(int property_id, double value) {
|
|
|
|
if (mCaptureSession == nil) return false;
|
|
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
|
|
bool retval = false;
|
|
QTTime t;
|
|
|
|
double ms;
|
|
|
|
switch (property_id) {
|
|
case CV_CAP_PROP_POS_MSEC:
|
|
[[mCaptureSession attributeForKey:QTMovieCurrentTimeAttribute] getValue:&t];
|
|
t.timeValue = value * t.timeScale / 1000;
|
|
[mCaptureSession setCurrentTime:t];
|
|
changedPos = 1;
|
|
retval = true;
|
|
break;
|
|
case CV_CAP_PROP_POS_FRAMES:
|
|
ms = (value*1000.0 -5)/ currentFPS;
|
|
retval = setProperty(CV_CAP_PROP_POS_MSEC, ms);
|
|
break;
|
|
case CV_CAP_PROP_POS_AVI_RATIO:
|
|
ms = value * movieDuration;
|
|
retval = setProperty(CV_CAP_PROP_POS_MSEC, ms);
|
|
break;
|
|
case CV_CAP_PROP_FRAME_WIDTH:
|
|
//retval = movieWidth;
|
|
break;
|
|
case CV_CAP_PROP_FRAME_HEIGHT:
|
|
//retval = movieHeight;
|
|
break;
|
|
case CV_CAP_PROP_FPS:
|
|
//etval = currentFPS;
|
|
break;
|
|
case CV_CAP_PROP_FOURCC:
|
|
default:
|
|
retval = false;
|
|
}
|
|
|
|
[localpool drain];
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CvVideoWriter Implementation.
|
|
*
|
|
* CvVideoWriter is the instantiation of a video output class
|
|
*
|
|
*****************************************************************************/
|
|
|
|
|
|
CvVideoWriter_QT::CvVideoWriter_QT(const char* filename, int fourcc,
|
|
double fps, CvSize frame_size,
|
|
int is_color) {
|
|
|
|
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
|
|
movieFPS = fps;
|
|
movieSize = frame_size;
|
|
movieColor = is_color;
|
|
mMovie = nil;
|
|
path = [[[NSString stringWithCString:filename encoding:NSASCIIStringEncoding] stringByExpandingTildeInPath] retain];
|
|
|
|
argbimage = cvCreateImage(movieSize, IPL_DEPTH_8U, 4);
|
|
|
|
|
|
char cc[5];
|
|
cc[0] = fourcc & 255;
|
|
cc[1] = (fourcc >> 8) & 255;
|
|
cc[2] = (fourcc >> 16) & 255;
|
|
cc[3] = (fourcc >> 24) & 255;
|
|
cc[4] = 0;
|
|
int cc2 = CV_FOURCC(cc[0], cc[1], cc[2], cc[3]);
|
|
if (cc2!=fourcc) {
|
|
cout << "WARNING: Didn't properly encode FourCC. Expected " << fourcc
|
|
<< " but got " << cc2 << "." << endl;
|
|
}
|
|
|
|
codec = [[NSString stringWithCString:cc encoding:NSASCIIStringEncoding] retain];
|
|
|
|
NSError *error = nil;
|
|
if (!mMovie) {
|
|
|
|
NSFileManager* files = [NSFileManager defaultManager];
|
|
if ([files fileExistsAtPath:path]) {
|
|
if (![files removeItemAtPath:path error:nil]) {
|
|
cout << "WARNING: Failed to remove existing file " << [path cStringUsingEncoding:NSASCIIStringEncoding] << endl;
|
|
}
|
|
}
|
|
|
|
mMovie = [[QTMovie alloc] initToWritableFile:path error:&error];
|
|
if (!mMovie) {
|
|
cout << "WARNING: Could not create empty movie file container." << endl;
|
|
[localpool drain];
|
|
return;
|
|
}
|
|
}
|
|
|
|
[mMovie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];
|
|
|
|
[localpool drain];
|
|
}
|
|
|
|
|
|
CvVideoWriter_QT::~CvVideoWriter_QT() {
|
|
cvReleaseImage(&argbimage);
|
|
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
[mMovie release];
|
|
[path release];
|
|
[codec release];
|
|
[localpool drain];
|
|
}
|
|
|
|
bool CvVideoWriter_QT::writeFrame(const IplImage* image) {
|
|
NSAutoreleasePool* localpool = [[NSAutoreleasePool alloc] init];
|
|
|
|
cvCvtColor(image, argbimage, CV_BGR2BGRA);
|
|
|
|
|
|
unsigned char* imagedata = (unsigned char*)argbimage->imageData;
|
|
//BGRA --> ARGB
|
|
|
|
for (int j = 0; j < argbimage->height; j++) {
|
|
int rowstart = argbimage->widthStep * j;
|
|
for (int i = rowstart; i < rowstart+argbimage->widthStep; i+=4) {
|
|
unsigned char temp = imagedata[i];
|
|
imagedata[i] = 255;
|
|
imagedata[i+3] = temp;
|
|
temp = imagedata[i+2];
|
|
imagedata[i+2] = imagedata[i+1];
|
|
imagedata[i+1] = temp;
|
|
}
|
|
}
|
|
|
|
NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&imagedata
|
|
pixelsWide:movieSize.width
|
|
pixelsHigh:movieSize.height
|
|
bitsPerSample:8
|
|
samplesPerPixel:4
|
|
hasAlpha:YES
|
|
isPlanar:NO
|
|
colorSpaceName:NSDeviceRGBColorSpace
|
|
bitmapFormat:NSAlphaFirstBitmapFormat
|
|
bytesPerRow:argbimage->widthStep
|
|
bitsPerPixel:32] ;
|
|
|
|
|
|
NSImage* nsimage = [[NSImage alloc] init];
|
|
|
|
[nsimage addRepresentation:imageRep];
|
|
|
|
/*
|
|
codecLosslessQuality = 0x00000400,
|
|
codecMaxQuality = 0x000003FF,
|
|
codecMinQuality = 0x00000000,
|
|
codecLowQuality = 0x00000100,
|
|
codecNormalQuality = 0x00000200,
|
|
codecHighQuality = 0x00000300
|
|
*/
|
|
|
|
[mMovie addImage:nsimage forDuration:QTMakeTime(100,100*movieFPS) withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
codec, QTAddImageCodecType,
|
|
//[NSNumber numberWithInt:codecLowQuality], QTAddImageCodecQuality,
|
|
[NSNumber numberWithInt:100*movieFPS], QTTrackTimeScaleAttribute,nil]];
|
|
|
|
if (![mMovie updateMovieFile]) {
|
|
cout << "Didn't successfully update movie file." << endl;
|
|
}
|
|
|
|
[imageRep release];
|
|
[nsimage release];
|
|
[localpool drain];
|
|
|
|
return 1;
|
|
}
|
|
|