mirror of
https://github.com/opencv/opencv.git
synced 2024-11-24 19:20:28 +08:00
added ios camera support in highgui. turned on optimization in opencv2.framework (ticket #2232)
This commit is contained in:
parent
c26d543e1e
commit
8efb84b5a4
@ -42,6 +42,8 @@ set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}")
|
||||
set (CMAKE_C_FLAGS "")
|
||||
set (CMAKE_CXX_FLAGS "-headerpad_max_install_names -fvisibility=hidden -fvisibility-inlines-hidden")
|
||||
|
||||
set (CMAKE_CXX_FLAGS_RELEASE "-O3 -fomit-frame-pointer")
|
||||
|
||||
if (HAVE_FLAG_SEARCH_PATHS_FIRST)
|
||||
set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}")
|
||||
set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}")
|
||||
|
@ -440,7 +440,7 @@ template<typename _Tp, int m, int n> class CV_EXPORTS Matx
|
||||
{
|
||||
public:
|
||||
typedef _Tp value_type;
|
||||
typedef Matx<_Tp, MIN(m, n), 1> diag_type;
|
||||
typedef Matx<_Tp, (m < n ? m : n), 1> diag_type;
|
||||
typedef Matx<_Tp, m, n> mat_type;
|
||||
enum { depth = DataDepth<_Tp>::value, rows = m, cols = n, channels = rows*cols,
|
||||
type = CV_MAKETYPE(depth, channels) };
|
||||
|
@ -193,6 +193,12 @@ elseif(APPLE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(IOS)
|
||||
add_definitions(-DHAVE_IOS=1)
|
||||
list(APPEND highgui_srcs src/cap_ios_abstract_camera.mm src/cap_ios_photo_camera.mm src/cap_ios_video_camera.mm)
|
||||
list(APPEND HIGHGUI_LIBRARIES "-framework Accelerate" "-framework AVFoundation" "-framework CoreGraphics" "-framework CoreImage" "-framework CoreMedia" "-framework CoreVideo" "-framework QuartzCore" "-framework AssetsLibrary")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
link_directories("${OpenCV_SOURCE_DIR}/3rdparty/lib") # for ffmpeg wrapper only
|
||||
include_directories(AFTER SYSTEM "${OpenCV_SOURCE_DIR}/3rdparty/include") # for directshow in VS2005 and multi-monitor support on MinGW
|
||||
|
160
modules/highgui/include/opencv2/highgui/cap_ios.h
Normal file
160
modules/highgui/include/opencv2/highgui/cap_ios.h
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* cap_ios.h
|
||||
* For iOS video I/O
|
||||
* by Eduard Feicho on 29/07/12
|
||||
* Copyright 2012. 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Accelerate/Accelerate.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <ImageIO/ImageIO.h>
|
||||
#include "opencv2/core/core.hpp"
|
||||
|
||||
/////////////////////////////////////// CvAbstractCamera /////////////////////////////////////
|
||||
|
||||
@class CvAbstractCamera;
|
||||
|
||||
@interface CvAbstractCamera : NSObject
|
||||
{
|
||||
AVCaptureSession* captureSession;
|
||||
AVCaptureConnection* videoCaptureConnection;
|
||||
AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;
|
||||
|
||||
UIDeviceOrientation currentDeviceOrientation;
|
||||
|
||||
BOOL cameraAvailable;
|
||||
BOOL captureSessionLoaded;
|
||||
BOOL running;
|
||||
BOOL useAVCaptureVideoPreviewLayer;
|
||||
|
||||
AVCaptureDevicePosition defaultAVCaptureDevicePosition;
|
||||
AVCaptureVideoOrientation defaultAVCaptureVideoOrientation;
|
||||
NSString *const defaultAVCaptureSessionPreset;
|
||||
|
||||
int defaultFPS;
|
||||
|
||||
UIView* parentView;
|
||||
|
||||
int imageWidth;
|
||||
int imageHeight;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) AVCaptureSession* captureSession;
|
||||
@property (nonatomic, retain) AVCaptureConnection* videoCaptureConnection;
|
||||
|
||||
@property (nonatomic, readonly) BOOL running;
|
||||
@property (nonatomic, readonly) BOOL captureSessionLoaded;
|
||||
|
||||
@property (nonatomic, assign) int defaultFPS;
|
||||
@property (nonatomic, assign) AVCaptureDevicePosition defaultAVCaptureDevicePosition;
|
||||
@property (nonatomic, assign) AVCaptureVideoOrientation defaultAVCaptureVideoOrientation;
|
||||
@property (nonatomic, assign) BOOL useAVCaptureVideoPreviewLayer;
|
||||
@property (nonatomic, strong) NSString *const defaultAVCaptureSessionPreset;
|
||||
|
||||
@property (nonatomic, assign) int imageWidth;
|
||||
@property (nonatomic, assign) int imageHeight;
|
||||
|
||||
@property (nonatomic, retain) UIView* parentView;
|
||||
|
||||
- (void)pause;
|
||||
- (void)start;
|
||||
- (void)stop;
|
||||
- (void)switchCameras;
|
||||
|
||||
- (id)initWithParentView:(UIView*)parent;
|
||||
|
||||
- (void)createCaptureOutput;
|
||||
- (void)createVideoPreviewLayer;
|
||||
- (void)updateOrientation;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
///////////////////////////////// CvVideoCamera ///////////////////////////////////////////
|
||||
|
||||
@class CvVideoCamera;
|
||||
|
||||
@protocol CvVideoCameraDelegate <NSObject>
|
||||
|
||||
#ifdef __cplusplus
|
||||
// delegate method for processing image frames
|
||||
- (void)processImage:(cv::Mat&)image;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@interface CvVideoCamera : CvAbstractCamera<AVCaptureVideoDataOutputSampleBufferDelegate>
|
||||
{
|
||||
AVCaptureVideoDataOutput *videoDataOutput;
|
||||
|
||||
dispatch_queue_t videoDataOutputQueue;
|
||||
CALayer *customPreviewLayer;
|
||||
|
||||
BOOL grayscaleMode;
|
||||
|
||||
BOOL recordVideo;
|
||||
AVAssetWriterInput* recordAssetWriterInput;
|
||||
AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor;
|
||||
AVAssetWriter* recordAssetWriter;
|
||||
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) id<CvVideoCameraDelegate> delegate;
|
||||
@property (nonatomic, assign) BOOL grayscaleMode;
|
||||
|
||||
@property (nonatomic, assign) BOOL recordVideo;
|
||||
@property (nonatomic, retain) AVAssetWriterInput* recordAssetWriterInput;
|
||||
@property (nonatomic, retain) AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor;
|
||||
@property (nonatomic, retain) AVAssetWriter* recordAssetWriter;
|
||||
|
||||
- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
|
||||
- (void)layoutPreviewLayer;
|
||||
- (void)saveVideo;
|
||||
|
||||
@end
|
||||
|
||||
///////////////////////////////// CvPhotoCamera ///////////////////////////////////////////
|
||||
|
||||
@class CvPhotoCamera;
|
||||
|
||||
@protocol CvPhotoCameraDelegate <NSObject>
|
||||
|
||||
- (void)photoCamera:(CvPhotoCamera*)photoCamera capturedImage:(UIImage *)image;
|
||||
- (void)photoCameraCancel:(CvPhotoCamera*)photoCamera;
|
||||
|
||||
@end
|
||||
|
||||
@interface CvPhotoCamera : CvAbstractCamera
|
||||
{
|
||||
AVCaptureStillImageOutput *stillImageOutput;
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) id<CvPhotoCameraDelegate> delegate;
|
||||
|
||||
- (void)takePicture;
|
||||
|
||||
@end
|
408
modules/highgui/src/cap_ios_abstract_camera.mm
Normal file
408
modules/highgui/src/cap_ios_abstract_camera.mm
Normal file
@ -0,0 +1,408 @@
|
||||
/*
|
||||
* cap_ios_abstract_camera.mm
|
||||
* For iOS video I/O
|
||||
* by Eduard Feicho on 29/07/12
|
||||
* Copyright 2012. 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#import "opencv2/highgui/cap_ios.h"
|
||||
#include "precomp.hpp"
|
||||
|
||||
#pragma mark - Private Interface
|
||||
|
||||
@interface CvAbstractCamera ()
|
||||
|
||||
@property (nonatomic, retain) AVCaptureVideoPreviewLayer* captureVideoPreviewLayer;
|
||||
|
||||
- (void)deviceOrientationDidChange:(NSNotification*)notification;
|
||||
- (void)startCaptureSession;
|
||||
|
||||
- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
|
||||
|
||||
- (void)updateSize;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - Implementation
|
||||
|
||||
|
||||
@implementation CvAbstractCamera
|
||||
|
||||
|
||||
|
||||
#pragma mark Public
|
||||
|
||||
@synthesize imageWidth;
|
||||
@synthesize imageHeight;
|
||||
|
||||
|
||||
@synthesize defaultFPS;
|
||||
@synthesize defaultAVCaptureDevicePosition;
|
||||
@synthesize defaultAVCaptureVideoOrientation;
|
||||
@synthesize defaultAVCaptureSessionPreset;
|
||||
|
||||
|
||||
|
||||
@synthesize captureSession;
|
||||
@synthesize captureVideoPreviewLayer;
|
||||
@synthesize videoCaptureConnection;
|
||||
@synthesize running;
|
||||
@synthesize captureSessionLoaded;
|
||||
@synthesize useAVCaptureVideoPreviewLayer;
|
||||
|
||||
@synthesize parentView;
|
||||
|
||||
#pragma mark - Constructors
|
||||
|
||||
- (id)init;
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// react to device orientation notifications
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(deviceOrientationDidChange:)
|
||||
name:UIDeviceOrientationDidChangeNotification
|
||||
object:nil];
|
||||
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
|
||||
currentDeviceOrientation = [[UIDevice currentDevice] orientation];
|
||||
|
||||
|
||||
// check if camera available
|
||||
cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
|
||||
NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") );
|
||||
|
||||
running = NO;
|
||||
|
||||
// set camera default configuration
|
||||
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
|
||||
self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
|
||||
self.defaultFPS = 15;
|
||||
self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset352x288;
|
||||
|
||||
self.parentView = nil;
|
||||
self.useAVCaptureVideoPreviewLayer = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (id)initWithParentView:(UIView*)parent;
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// react to device orientation notifications
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(deviceOrientationDidChange:)
|
||||
name:UIDeviceOrientationDidChangeNotification
|
||||
object:nil];
|
||||
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
|
||||
currentDeviceOrientation = [[UIDevice currentDevice] orientation];
|
||||
|
||||
|
||||
// check if camera available
|
||||
cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
|
||||
NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") );
|
||||
|
||||
running = NO;
|
||||
|
||||
// set camera default configuration
|
||||
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
|
||||
self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft;
|
||||
self.defaultFPS = 15;
|
||||
self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480;
|
||||
|
||||
self.parentView = parent;
|
||||
self.useAVCaptureVideoPreviewLayer = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)dealloc;
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Public interface
|
||||
|
||||
|
||||
- (void)start;
|
||||
{
|
||||
if (![NSThread isMainThread]) {
|
||||
NSLog(@"[Camera] Warning: Call start only from main thread");
|
||||
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
|
||||
return;
|
||||
}
|
||||
|
||||
if (running == YES) {
|
||||
return;
|
||||
}
|
||||
running = YES;
|
||||
|
||||
// TOOD update image size data before actually starting (needed for recording)
|
||||
[self updateSize];
|
||||
|
||||
if (cameraAvailable) {
|
||||
[self startCaptureSession];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)pause;
|
||||
{
|
||||
running = NO;
|
||||
[self.captureSession stopRunning];
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)stop;
|
||||
{
|
||||
running = NO;
|
||||
|
||||
// Release any retained subviews of the main view.
|
||||
// e.g. self.myOutlet = nil;
|
||||
|
||||
[self.captureSession stopRunning];
|
||||
self.captureSession = nil;
|
||||
self.captureVideoPreviewLayer = nil;
|
||||
self.videoCaptureConnection = nil;
|
||||
captureSessionLoaded = NO;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// use front/back camera
|
||||
- (void)switchCameras;
|
||||
{
|
||||
BOOL was_running = self.running;
|
||||
if (was_running) {
|
||||
[self stop];
|
||||
}
|
||||
if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {
|
||||
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack;
|
||||
} else {
|
||||
self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
|
||||
}
|
||||
if (was_running) {
|
||||
[self start];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark - Device Orientation Changes
|
||||
|
||||
|
||||
- (void)deviceOrientationDidChange:(NSNotification*)notification
|
||||
{
|
||||
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
|
||||
|
||||
switch (orientation)
|
||||
{
|
||||
case UIDeviceOrientationPortrait:
|
||||
case UIDeviceOrientationPortraitUpsideDown:
|
||||
case UIDeviceOrientationLandscapeLeft:
|
||||
case UIDeviceOrientationLandscapeRight:
|
||||
currentDeviceOrientation = orientation;
|
||||
break;
|
||||
|
||||
case UIDeviceOrientationFaceUp:
|
||||
case UIDeviceOrientationFaceDown:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
NSLog(@"deviceOrientationDidChange: %d", orientation);
|
||||
|
||||
[self updateOrientation];
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark - Private Interface
|
||||
|
||||
- (void)createCaptureSession;
|
||||
{
|
||||
// set a av capture session preset
|
||||
self.captureSession = [[AVCaptureSession alloc] init];
|
||||
if ([self.captureSession canSetSessionPreset:self.defaultAVCaptureSessionPreset]) {
|
||||
[self.captureSession setSessionPreset:self.defaultAVCaptureSessionPreset];
|
||||
} else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetLow]) {
|
||||
[self.captureSession setSessionPreset:AVCaptureSessionPresetLow];
|
||||
} else {
|
||||
NSLog(@"[Camera] Error: could not set session preset");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createCaptureDevice;
|
||||
{
|
||||
// setup the device
|
||||
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
||||
[self setDesiredCameraPosition:self.defaultAVCaptureDevicePosition];
|
||||
NSLog(@"[Camera] device connected? %@", device.connected ? @"YES" : @"NO");
|
||||
NSLog(@"[Camera] device position %@", (device.position == AVCaptureDevicePositionBack) ? @"back" : @"front");
|
||||
}
|
||||
|
||||
|
||||
- (void)createVideoPreviewLayer;
|
||||
{
|
||||
self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
|
||||
|
||||
if ([self.captureVideoPreviewLayer isOrientationSupported]) {
|
||||
[self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation];
|
||||
}
|
||||
|
||||
if (parentView != nil) {
|
||||
self.captureVideoPreviewLayer.frame = self.parentView.bounds;
|
||||
self.captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
|
||||
[self.parentView.layer addSublayer:self.captureVideoPreviewLayer];
|
||||
}
|
||||
NSLog(@"[Camera] created AVCaptureVideoPreviewLayer");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition;
|
||||
{
|
||||
for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
|
||||
if ([device position] == desiredPosition) {
|
||||
[self.captureSession beginConfiguration];
|
||||
|
||||
NSError* error;
|
||||
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
|
||||
if (!input) {
|
||||
NSLog(@"error creating input %@", [error localizedDescription]);
|
||||
}
|
||||
|
||||
// support for autofocus
|
||||
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
|
||||
NSError *error = nil;
|
||||
if ([device lockForConfiguration:&error]) {
|
||||
device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
|
||||
[device unlockForConfiguration];
|
||||
} else {
|
||||
NSLog(@"unable to lock device for autofocos configuration %@", [error localizedDescription]);
|
||||
}
|
||||
}
|
||||
[self.captureSession addInput:input];
|
||||
|
||||
for (AVCaptureInput *oldInput in self.captureSession.inputs) {
|
||||
[self.captureSession removeInput:oldInput];
|
||||
}
|
||||
[self.captureSession addInput:input];
|
||||
[self.captureSession commitConfiguration];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)startCaptureSession
|
||||
{
|
||||
if (!cameraAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.captureSessionLoaded == NO) {
|
||||
[self createCaptureSession];
|
||||
[self createCaptureDevice];
|
||||
[self createCaptureOutput];
|
||||
|
||||
// setup preview layer
|
||||
if (self.useAVCaptureVideoPreviewLayer) {
|
||||
[self createVideoPreviewLayer];
|
||||
} else {
|
||||
[self createCustomVideoPreview];
|
||||
}
|
||||
|
||||
captureSessionLoaded = YES;
|
||||
}
|
||||
|
||||
[self.captureSession startRunning];
|
||||
}
|
||||
|
||||
|
||||
- (void)createCaptureOutput;
|
||||
{
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
|
||||
}
|
||||
|
||||
- (void)createCustomVideoPreview;
|
||||
{
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
|
||||
}
|
||||
|
||||
- (void)updateOrientation;
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
|
||||
- (void)updateSize;
|
||||
{
|
||||
if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetPhoto]) {
|
||||
//TODO: find the correct resolution
|
||||
self.imageWidth = 640;
|
||||
self.imageHeight = 480;
|
||||
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetHigh]) {
|
||||
//TODO: find the correct resolution
|
||||
self.imageWidth = 640;
|
||||
self.imageHeight = 480;
|
||||
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetMedium]) {
|
||||
//TODO: find the correct resolution
|
||||
self.imageWidth = 640;
|
||||
self.imageHeight = 480;
|
||||
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetLow]) {
|
||||
//TODO: find the correct resolution
|
||||
self.imageWidth = 640;
|
||||
self.imageHeight = 480;
|
||||
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset352x288]) {
|
||||
self.imageWidth = 352;
|
||||
self.imageHeight = 288;
|
||||
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset640x480]) {
|
||||
self.imageWidth = 640;
|
||||
self.imageHeight = 480;
|
||||
} else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset1280x720]) {
|
||||
self.imageWidth = 1280;
|
||||
self.imageHeight = 720;
|
||||
} else {
|
||||
self.imageWidth = 640;
|
||||
self.imageHeight = 480;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
165
modules/highgui/src/cap_ios_photo_camera.mm
Normal file
165
modules/highgui/src/cap_ios_photo_camera.mm
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* cap_ios_photo_camera.mm
|
||||
* For iOS video I/O
|
||||
* by Eduard Feicho on 29/07/12
|
||||
* Copyright 2012. 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#import "opencv2/highgui/cap_ios.h"
|
||||
#include "precomp.hpp"
|
||||
|
||||
#pragma mark - Private Interface
|
||||
|
||||
|
||||
@interface CvPhotoCamera ()
|
||||
|
||||
@property (nonatomic, retain) AVCaptureStillImageOutput* stillImageOutput;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#pragma mark - Implementation
|
||||
|
||||
|
||||
@implementation CvPhotoCamera
|
||||
|
||||
|
||||
|
||||
#pragma mark Public
|
||||
|
||||
@synthesize stillImageOutput;
|
||||
@synthesize delegate;
|
||||
|
||||
|
||||
#pragma mark - Public interface
|
||||
|
||||
|
||||
- (void)takePicture
|
||||
{
|
||||
if (cameraAvailable == NO) {
|
||||
return;
|
||||
}
|
||||
cameraAvailable = NO;
|
||||
|
||||
|
||||
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:self.videoCaptureConnection
|
||||
completionHandler:
|
||||
^(CMSampleBufferRef imageSampleBuffer, NSError *error)
|
||||
{
|
||||
if (error == nil && imageSampleBuffer != NULL)
|
||||
{
|
||||
// TODO check
|
||||
// NSNumber* imageOrientation = [UIImage cgImageOrientationForUIDeviceOrientation:currentDeviceOrientation];
|
||||
// CMSetAttachment(imageSampleBuffer, kCGImagePropertyOrientation, imageOrientation, 1);
|
||||
|
||||
NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.captureSession stopRunning];
|
||||
|
||||
// Make sure we create objects on the main thread in the main context
|
||||
UIImage* newImage = [UIImage imageWithData:jpegData];
|
||||
|
||||
//UIImageOrientation orientation = [newImage imageOrientation];
|
||||
|
||||
// TODO: only apply rotation, don't scale, since we can set this directly in the camera
|
||||
/*
|
||||
switch (orientation) {
|
||||
case UIImageOrientationUp:
|
||||
case UIImageOrientationDown:
|
||||
newImage = [newImage imageWithAppliedRotationAndMaxSize:CGSizeMake(640.0, 480.0)];
|
||||
break;
|
||||
case UIImageOrientationLeft:
|
||||
case UIImageOrientationRight:
|
||||
newImage = [newImage imageWithMaxSize:CGSizeMake(640.0, 480.0)];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
// We have captured the image, we can allow the user to take another picture
|
||||
cameraAvailable = YES;
|
||||
|
||||
NSLog(@"CvPhotoCamera captured image");
|
||||
if (self.delegate) {
|
||||
[self.delegate photoCamera:self capturedImage:newImage];
|
||||
}
|
||||
|
||||
[self.captureSession startRunning];
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (void)stop;
|
||||
{
|
||||
[super stop];
|
||||
self.stillImageOutput = nil;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private Interface
|
||||
|
||||
|
||||
- (void)createStillImageOutput;
|
||||
{
|
||||
// setup still image output with jpeg codec
|
||||
self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
|
||||
NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil];
|
||||
[self.stillImageOutput setOutputSettings:outputSettings];
|
||||
[self.captureSession addOutput:self.stillImageOutput];
|
||||
|
||||
for (AVCaptureConnection *connection in self.stillImageOutput.connections) {
|
||||
for (AVCaptureInputPort *port in [connection inputPorts]) {
|
||||
if ([port.mediaType isEqual:AVMediaTypeVideo]) {
|
||||
self.videoCaptureConnection = connection;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (self.videoCaptureConnection) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
NSLog(@"[Camera] still image output created");
|
||||
}
|
||||
|
||||
|
||||
- (void)createCaptureOutput;
|
||||
{
|
||||
[self createStillImageOutput];
|
||||
}
|
||||
|
||||
- (void)createCustomVideoPreview;
|
||||
{
|
||||
//do nothing, always use AVCaptureVideoPreviewLayer
|
||||
}
|
||||
|
||||
|
||||
@end
|
656
modules/highgui/src/cap_ios_video_camera.mm
Normal file
656
modules/highgui/src/cap_ios_video_camera.mm
Normal file
@ -0,0 +1,656 @@
|
||||
/*
|
||||
* cap_ios_video_camera.mm
|
||||
* For iOS video I/O
|
||||
* by Eduard Feicho on 29/07/12
|
||||
* Copyright 2012. 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#import "opencv2/highgui/cap_ios.h"
|
||||
#include "precomp.hpp"
|
||||
|
||||
#import <AssetsLibrary/AssetsLibrary.h>
|
||||
|
||||
|
||||
static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};
|
||||
|
||||
#pragma mark - Private Interface
|
||||
|
||||
|
||||
|
||||
|
||||
@interface CvVideoCamera ()
|
||||
|
||||
- (void)createVideoDataOutput;
|
||||
- (void)createVideoFileOutput;
|
||||
|
||||
|
||||
@property (nonatomic, retain) CALayer *customPreviewLayer;
|
||||
@property (nonatomic, retain) AVCaptureVideoDataOutput *videoDataOutput;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
#pragma mark - Implementation
|
||||
|
||||
|
||||
|
||||
@implementation CvVideoCamera
|
||||
|
||||
|
||||
|
||||
|
||||
@synthesize delegate;
|
||||
@synthesize grayscaleMode;
|
||||
|
||||
@synthesize customPreviewLayer;
|
||||
@synthesize videoDataOutput;
|
||||
|
||||
@synthesize recordVideo;
|
||||
//@synthesize videoFileOutput;
|
||||
@synthesize recordAssetWriterInput;
|
||||
@synthesize recordPixelBufferAdaptor;
|
||||
@synthesize recordAssetWriter;
|
||||
|
||||
|
||||
|
||||
#pragma mark - Constructors
|
||||
|
||||
- (id)initWithParentView:(UIView*)parent;
|
||||
{
|
||||
self = [super initWithParentView:parent];
|
||||
if (self) {
|
||||
self.useAVCaptureVideoPreviewLayer = NO;
|
||||
self.recordVideo = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark - Public interface
|
||||
|
||||
|
||||
- (void)start;
|
||||
{
|
||||
[super start];
|
||||
|
||||
if (self.recordVideo == YES) {
|
||||
// [self.videoFileOutput startRecordingToOutputFileURL:[self tempFileURL] recordingDelegate:self];
|
||||
|
||||
NSError* error;
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:[self tempFileString]]) [[NSFileManager defaultManager] removeItemAtPath:[self tempFileString] error:&error];
|
||||
if (error == nil) {
|
||||
NSLog(@"[Camera] Delete file %@", [self tempFileString]);
|
||||
}
|
||||
|
||||
|
||||
BOOL started = [self.recordAssetWriter startWriting];
|
||||
[self.recordAssetWriter startSessionAtSourceTime:kCMTimeZero];
|
||||
|
||||
NSLog(@"[Camera] Session started? %d", started);
|
||||
|
||||
if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) {
|
||||
NSLog(@"AVAssetWriter status: unknown");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
|
||||
NSLog(@"AVAssetWriter status: writing");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) {
|
||||
NSLog(@"AVAssetWriter status: completed");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) {
|
||||
NSLog(@"AVAssetWriter status: failed");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) {
|
||||
NSLog(@"AVAssetWriter status: cancelled");
|
||||
}
|
||||
|
||||
if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) {
|
||||
NSLog(@"[Camera] Recording Error: asset writer status is not writing: %@", self.recordAssetWriter.error);
|
||||
} else {
|
||||
NSLog(@"[Camera] Recording started");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)pause;
|
||||
{
|
||||
[super pause];
|
||||
if (self.recordVideo == YES) {
|
||||
// [self.videoFileOutput stopRecording];
|
||||
|
||||
|
||||
if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) {
|
||||
NSLog(@"AVAssetWriter status: unknown");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
|
||||
NSLog(@"AVAssetWriter status: writing");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) {
|
||||
NSLog(@"AVAssetWriter status: completed");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) {
|
||||
NSLog(@"AVAssetWriter status: failed");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) {
|
||||
NSLog(@"AVAssetWriter status: cancelled");
|
||||
}
|
||||
|
||||
if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
|
||||
[self.recordAssetWriter finishWriting];
|
||||
NSLog(@"[Camera] recording stopped");
|
||||
} else {
|
||||
NSLog(@"[Camera] Recording Error: asset writer status is not writing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)stop;
|
||||
{
|
||||
[super stop];
|
||||
|
||||
if (self.recordVideo == YES) {
|
||||
NSLog(@"recording stop");
|
||||
if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) {
|
||||
NSLog(@"AVAssetWriter status: unknown");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
|
||||
NSLog(@"AVAssetWriter status: writing");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) {
|
||||
NSLog(@"AVAssetWriter status: completed");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) {
|
||||
NSLog(@"AVAssetWriter status: failed");
|
||||
} else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) {
|
||||
NSLog(@"AVAssetWriter status: cancelled");
|
||||
}
|
||||
|
||||
|
||||
if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) {
|
||||
[self.recordAssetWriter finishWriting];
|
||||
NSLog(@"[Camera] recording stopped");
|
||||
} else {
|
||||
NSLog(@"[Camera] Recording Error: asset writer status is not writing");
|
||||
}
|
||||
|
||||
self.recordAssetWriter = nil;
|
||||
self.recordAssetWriterInput = nil;
|
||||
self.recordPixelBufferAdaptor = nil;
|
||||
}
|
||||
|
||||
self.videoDataOutput = nil;
|
||||
if (videoDataOutputQueue) {
|
||||
dispatch_release(videoDataOutputQueue);
|
||||
}
|
||||
|
||||
[self.customPreviewLayer removeFromSuperlayer];
|
||||
self.customPreviewLayer = nil;
|
||||
}
|
||||
|
||||
// TODO fix
|
||||
- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
|
||||
{
|
||||
|
||||
NSLog(@"layout preview layer");
|
||||
if (self.parentView != nil) {
|
||||
|
||||
CALayer* layer = self.customPreviewLayer;
|
||||
CGRect bounds = self.customPreviewLayer.bounds;
|
||||
int rotation_angle = 0;
|
||||
bool flip_bounds = false;
|
||||
|
||||
switch (interfaceOrientation) {
|
||||
case UIInterfaceOrientationPortrait:
|
||||
NSLog(@"to Portrait");
|
||||
rotation_angle = 270;
|
||||
break;
|
||||
case UIInterfaceOrientationPortraitUpsideDown:
|
||||
rotation_angle = 90;
|
||||
NSLog(@"to UpsideDown");
|
||||
break;
|
||||
case UIInterfaceOrientationLandscapeLeft:
|
||||
rotation_angle = 0;
|
||||
NSLog(@"to LandscapeLeft");
|
||||
break;
|
||||
case UIInterfaceOrientationLandscapeRight:
|
||||
rotation_angle = 180;
|
||||
NSLog(@"to LandscapeRight");
|
||||
break;
|
||||
default:
|
||||
break; // leave the layer in its last known orientation
|
||||
}
|
||||
|
||||
switch (defaultAVCaptureVideoOrientation) {
|
||||
case AVCaptureVideoOrientationLandscapeRight:
|
||||
rotation_angle += 180;
|
||||
break;
|
||||
case AVCaptureVideoOrientationPortraitUpsideDown:
|
||||
rotation_angle += 270;
|
||||
break;
|
||||
case AVCaptureVideoOrientationPortrait:
|
||||
rotation_angle += 90;
|
||||
case AVCaptureVideoOrientationLandscapeLeft:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
rotation_angle = rotation_angle % 360;
|
||||
|
||||
if (rotation_angle == 90 || rotation_angle == 270) {
|
||||
flip_bounds = true;
|
||||
}
|
||||
|
||||
if (flip_bounds) {
|
||||
NSLog(@"flip bounds");
|
||||
bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width);
|
||||
}
|
||||
|
||||
layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.);
|
||||
self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
|
||||
|
||||
layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) );
|
||||
layer.bounds = bounds;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO fix
|
||||
- (void)layoutPreviewLayer;
|
||||
{
|
||||
NSLog(@"layout preview layer");
|
||||
if (self.parentView != nil) {
|
||||
|
||||
CALayer* layer = self.customPreviewLayer;
|
||||
CGRect bounds = self.customPreviewLayer.bounds;
|
||||
int rotation_angle = 0;
|
||||
bool flip_bounds = false;
|
||||
|
||||
switch (currentDeviceOrientation) {
|
||||
case UIDeviceOrientationPortrait:
|
||||
rotation_angle = 270;
|
||||
break;
|
||||
case UIDeviceOrientationPortraitUpsideDown:
|
||||
rotation_angle = 90;
|
||||
break;
|
||||
case UIDeviceOrientationLandscapeLeft:
|
||||
NSLog(@"left");
|
||||
rotation_angle = 180;
|
||||
break;
|
||||
case UIDeviceOrientationLandscapeRight:
|
||||
NSLog(@"right");
|
||||
rotation_angle = 0;
|
||||
break;
|
||||
case UIDeviceOrientationFaceUp:
|
||||
case UIDeviceOrientationFaceDown:
|
||||
default:
|
||||
break; // leave the layer in its last known orientation
|
||||
}
|
||||
|
||||
switch (defaultAVCaptureVideoOrientation) {
|
||||
case AVCaptureVideoOrientationLandscapeRight:
|
||||
rotation_angle += 180;
|
||||
break;
|
||||
case AVCaptureVideoOrientationPortraitUpsideDown:
|
||||
rotation_angle += 270;
|
||||
break;
|
||||
case AVCaptureVideoOrientationPortrait:
|
||||
rotation_angle += 90;
|
||||
case AVCaptureVideoOrientationLandscapeLeft:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
rotation_angle = rotation_angle % 360;
|
||||
|
||||
if (rotation_angle == 90 || rotation_angle == 270) {
|
||||
flip_bounds = true;
|
||||
}
|
||||
|
||||
if (flip_bounds) {
|
||||
NSLog(@"flip bounds");
|
||||
bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width);
|
||||
}
|
||||
|
||||
layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.);
|
||||
layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) );
|
||||
layer.bounds = bounds;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma mark - Private Interface
|
||||
|
||||
|
||||
|
||||
- (void)createVideoDataOutput;
|
||||
{
|
||||
// Make a video data output
|
||||
self.videoDataOutput = [AVCaptureVideoDataOutput new];
|
||||
|
||||
// In grayscale mode we want YUV (YpCbCr 4:2:0) so we can directly access the graylevel intensity values (Y component)
|
||||
// In color mode we, BGRA format is used
|
||||
OSType format = self.grayscaleMode ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA;
|
||||
|
||||
self.videoDataOutput.videoSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:format]
|
||||
forKey:(id)kCVPixelBufferPixelFormatTypeKey];
|
||||
|
||||
// discard if the data output queue is blocked (as we process the still image)
|
||||
[self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];
|
||||
|
||||
if ( [self.captureSession canAddOutput:self.videoDataOutput] ) {
|
||||
[self.captureSession addOutput:self.videoDataOutput];
|
||||
}
|
||||
[[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo] setEnabled:YES];
|
||||
|
||||
|
||||
// set default FPS
|
||||
if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMinFrameDuration) {
|
||||
[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMinFrameDuration = CMTimeMake(1, self.defaultFPS);
|
||||
}
|
||||
if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMaxFrameDuration) {
|
||||
[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMaxFrameDuration = CMTimeMake(1, self.defaultFPS);
|
||||
}
|
||||
|
||||
// set video mirroring for front camera (more intuitive)
|
||||
if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMirroring) {
|
||||
if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) {
|
||||
[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = YES;
|
||||
} else {
|
||||
[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = NO;
|
||||
}
|
||||
}
|
||||
|
||||
// set default video orientation
|
||||
if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoOrientation) {
|
||||
[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoOrientation = self.defaultAVCaptureVideoOrientation;
|
||||
}
|
||||
|
||||
|
||||
// create a custom preview layer
|
||||
self.customPreviewLayer = [CALayer layer];
|
||||
self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
|
||||
[self layoutPreviewLayer];
|
||||
|
||||
// create a serial dispatch queue used for the sample buffer delegate as well as when a still image is captured
|
||||
// a serial dispatch queue must be used to guarantee that video frames will be delivered in order
|
||||
// see the header doc for setSampleBufferDelegate:queue: for more information
|
||||
videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL);
|
||||
[self.videoDataOutput setSampleBufferDelegate:self queue:videoDataOutputQueue];
|
||||
|
||||
|
||||
NSLog(@"[Camera] created AVCaptureVideoDataOutput at %d FPS", self.defaultFPS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)createVideoFileOutput;
|
||||
{
|
||||
/*
|
||||
if (self.recordVideo == YES) {
|
||||
self.videoFileOutput = [[AVCaptureMovieFileOutput alloc] init];
|
||||
if ( [self.captureSession canAddOutput:self.videoFileOutput] ) {
|
||||
[self.captureSession addOutput:self.videoFileOutput];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/* Video File Output in H.264, via AVAsserWriter */
|
||||
NSLog(@"Create Video with dimensions %dx%d", self.imageWidth, self.imageHeight);
|
||||
|
||||
NSDictionary *outputSettings
|
||||
= [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:self.imageWidth], AVVideoWidthKey,
|
||||
[NSNumber numberWithInt:self.imageHeight], AVVideoHeightKey,
|
||||
AVVideoCodecH264, AVVideoCodecKey,
|
||||
nil
|
||||
];
|
||||
|
||||
|
||||
self.recordAssetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings];
|
||||
|
||||
|
||||
/* I'm going to push pixel buffers to it, so will need a
|
||||
AVAssetWriterPixelBufferAdaptor, to expect the same 32BGRA input as I've
|
||||
asked the AVCaptureVideDataOutput to supply */
|
||||
int pixelBufferFormat = (self.grayscaleMode == YES) ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA;
|
||||
|
||||
self.recordPixelBufferAdaptor =
|
||||
[[AVAssetWriterInputPixelBufferAdaptor alloc]
|
||||
initWithAssetWriterInput:self.recordAssetWriterInput
|
||||
sourcePixelBufferAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:pixelBufferFormat], kCVPixelBufferPixelFormatTypeKey,nil]];
|
||||
|
||||
NSError* error = nil;
|
||||
NSLog(@"Create AVAssetWriter with url: %@", [self tempFileURL]);
|
||||
self.recordAssetWriter = [AVAssetWriter assetWriterWithURL:[self tempFileURL]
|
||||
fileType:AVFileTypeMPEG4
|
||||
error:&error];
|
||||
if (error != nil) {
|
||||
NSLog(@"[Camera] Unable to create AVAssetWriter: %@", error);
|
||||
}
|
||||
|
||||
[self.recordAssetWriter addInput:self.recordAssetWriterInput];
|
||||
self.recordAssetWriterInput.expectsMediaDataInRealTime = NO;
|
||||
|
||||
NSLog(@"[Camera] created AVAssetWriter");
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (void)createCaptureOutput;
|
||||
{
|
||||
[self createVideoDataOutput];
|
||||
if (self.recordVideo == YES) {
|
||||
[self createVideoFileOutput];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createCustomVideoPreview;
|
||||
{
|
||||
[self.parentView.layer addSublayer:self.customPreviewLayer];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Protocol AVCaptureVideoDataOutputSampleBufferDelegate
|
||||
|
||||
|
||||
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
|
||||
{
|
||||
if (self.delegate) {
|
||||
|
||||
// convert from Core Media to Core Video
|
||||
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
|
||||
CVPixelBufferLockBaseAddress(imageBuffer, 0);
|
||||
|
||||
void* bufferAddress;
|
||||
size_t width;
|
||||
size_t height;
|
||||
size_t bytesPerRow;
|
||||
|
||||
CGColorSpaceRef colorSpace;
|
||||
CGContextRef context;
|
||||
|
||||
int format_opencv;
|
||||
|
||||
OSType format = CVPixelBufferGetPixelFormatType(imageBuffer);
|
||||
if (format == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
|
||||
|
||||
format_opencv = CV_8UC1;
|
||||
|
||||
bufferAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
|
||||
width = CVPixelBufferGetWidthOfPlane(imageBuffer, 0);
|
||||
height = CVPixelBufferGetHeightOfPlane(imageBuffer, 0);
|
||||
bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0);
|
||||
|
||||
} else { // expect kCVPixelFormatType_32BGRA
|
||||
|
||||
format_opencv = CV_8UC4;
|
||||
|
||||
bufferAddress = CVPixelBufferGetBaseAddress(imageBuffer);
|
||||
width = CVPixelBufferGetWidth(imageBuffer);
|
||||
height = CVPixelBufferGetHeight(imageBuffer);
|
||||
bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
|
||||
|
||||
}
|
||||
|
||||
// delegate image processing to the delegate
|
||||
cv::Mat image(height, width, format_opencv, bufferAddress, bytesPerRow);
|
||||
|
||||
cv::Mat* result = NULL;
|
||||
CGImage* dstImage;
|
||||
|
||||
if ([self.delegate respondsToSelector:@selector(processImage:)]) {
|
||||
[self.delegate processImage:image];
|
||||
}
|
||||
|
||||
// check if matrix data pointer or dimensions were changed by the delegate
|
||||
bool iOSimage = true;
|
||||
if (height == image.rows && width == image.cols && format_opencv == image.type() && bufferAddress == image.data && bytesPerRow == image.step) {
|
||||
iOSimage = false;
|
||||
}
|
||||
|
||||
|
||||
// (create color space, create graphics context, render buffer)
|
||||
CGBitmapInfo bitmapInfo;
|
||||
|
||||
// basically we decide if it's a grayscale, rgb or rgba image
|
||||
if (image.channels() == 1) {
|
||||
colorSpace = CGColorSpaceCreateDeviceGray();
|
||||
bitmapInfo = kCGImageAlphaNone;
|
||||
} else if (image.channels() == 3) {
|
||||
colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
bitmapInfo = kCGImageAlphaNone;
|
||||
if (iOSimage) {
|
||||
bitmapInfo |= kCGBitmapByteOrder32Little;
|
||||
} else {
|
||||
bitmapInfo |= kCGBitmapByteOrder32Big;
|
||||
}
|
||||
} else {
|
||||
colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
bitmapInfo = kCGImageAlphaPremultipliedFirst;
|
||||
if (iOSimage) {
|
||||
bitmapInfo |= kCGBitmapByteOrder32Little;
|
||||
} else {
|
||||
bitmapInfo |= kCGBitmapByteOrder32Big;
|
||||
}
|
||||
}
|
||||
|
||||
if (iOSimage) {
|
||||
context = CGBitmapContextCreate(bufferAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo);
|
||||
dstImage = CGBitmapContextCreateImage(context);
|
||||
CGContextRelease(context);
|
||||
} else {
|
||||
|
||||
NSData *data = [NSData dataWithBytes:image.data length:image.elemSize()*image.total()];
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
|
||||
|
||||
// Creating CGImage from cv::Mat
|
||||
dstImage = CGImageCreate(image.cols, // width
|
||||
image.rows, // height
|
||||
8, // bits per component
|
||||
8 * image.elemSize(), // bits per pixel
|
||||
image.step, // bytesPerRow
|
||||
colorSpace, // colorspace
|
||||
bitmapInfo, // bitmap info
|
||||
provider, // CGDataProviderRef
|
||||
NULL, // decode
|
||||
false, // should interpolate
|
||||
kCGRenderingIntentDefault // intent
|
||||
);
|
||||
|
||||
CGDataProviderRelease(provider);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// render buffer
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
self.customPreviewLayer.contents = (__bridge id)dstImage;
|
||||
});
|
||||
|
||||
|
||||
if (self.recordVideo == YES) {
|
||||
// a very dense way to keep track of the time at which this frame
|
||||
// occurs relative to the output stream, but it's just an example!
|
||||
|
||||
// TODO reset frame number
|
||||
static int64_t frameNumber = 0;
|
||||
if (self.recordAssetWriterInput.readyForMoreMediaData) {
|
||||
[self.recordPixelBufferAdaptor appendPixelBuffer:imageBuffer
|
||||
withPresentationTime:CMTimeMake(frameNumber, self.defaultFPS)];
|
||||
}
|
||||
frameNumber++;
|
||||
}
|
||||
|
||||
|
||||
// cleanup
|
||||
CGImageRelease(dstImage);
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)updateOrientation;
|
||||
{
|
||||
NSLog(@"rotate..");
|
||||
self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
|
||||
[self layoutPreviewLayer];
|
||||
}
|
||||
|
||||
|
||||
- (void)saveVideo;
|
||||
{
|
||||
if (self.recordVideo == NO) {
|
||||
return;
|
||||
}
|
||||
|
||||
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
|
||||
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:[self tempFileURL]]) {
|
||||
[library writeVideoAtPathToSavedPhotosAlbum:[self tempFileURL]
|
||||
completionBlock:^(NSURL *assetURL, NSError *error){}];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSURL *)tempFileURL;
|
||||
{
|
||||
NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
|
||||
NSURL *outputURL = [NSURL fileURLWithPath:outputPath];
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
if ([fileManager fileExistsAtPath:outputPath]) {
|
||||
NSLog(@"file exists");
|
||||
}
|
||||
return outputURL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (NSString *)tempFileString;
|
||||
{
|
||||
NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
|
||||
return outputPath;
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue
Block a user