summaryrefslogtreecommitdiffstats
path: root/iphone/ZBarReaderViewImpl_Capture.m
diff options
context:
space:
mode:
Diffstat (limited to 'iphone/ZBarReaderViewImpl_Capture.m')
-rw-r--r--iphone/ZBarReaderViewImpl_Capture.m402
1 files changed, 402 insertions, 0 deletions
diff --git a/iphone/ZBarReaderViewImpl_Capture.m b/iphone/ZBarReaderViewImpl_Capture.m
new file mode 100644
index 0000000..dd5a696
--- /dev/null
+++ b/iphone/ZBarReaderViewImpl_Capture.m
@@ -0,0 +1,402 @@
+//------------------------------------------------------------------------
+// Copyright 2010 (c) Jeff Brown <spadix@users.sourceforge.net>
+//
+// This file is part of the ZBar Bar Code Reader.
+//
+// The ZBar Bar Code Reader is free software; you can redistribute it
+// and/or modify it under the terms of the GNU Lesser Public License as
+// published by the Free Software Foundation; either version 2.1 of
+// the License, or (at your option) any later version.
+//
+// The ZBar Bar Code Reader 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. See the
+// GNU Lesser Public License for more details.
+//
+// You should have received a copy of the GNU Lesser Public License
+// along with the ZBar Bar Code Reader; if not, write to the Free
+// Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+// Boston, MA 02110-1301 USA
+//
+// http://sourceforge.net/projects/zbar
+//------------------------------------------------------------------------
+
+#import <AVFoundation/AVFoundation.h>
+#import <CoreMedia/CoreMedia.h>
+#import <CoreVideo/CoreVideo.h>
+#import <ZBarSDK/ZBarReaderView.h>
+#import <ZBarSDK/ZBarCaptureReader.h>
+
+#define MODULE ZBarReaderView
+#import "debug.h"
+
+// protected APIs
+@interface ZBarReaderView()
+- (void) _initWithImageScanner: (ZBarImageScanner*) _scanner;
+- (void) initSubviews;
+- (void) updateCrop;
+- (void) setImageSize: (CGSize) size;
+- (void) didTrackSymbols: (ZBarSymbolSet*) syms;
+@end
+
+@interface ZBarReaderViewImpl
+ : ZBarReaderView
+{
+ AVCaptureSession *session;
+ AVCaptureDevice *device;
+ AVCaptureInput *input;
+}
+
+@end
+
+@implementation ZBarReaderViewImpl
+
+@synthesize device, session;
+
+- (void) _initWithImageScanner: (ZBarImageScanner*) scanner
+{
+ [super _initWithImageScanner: scanner];
+
+ session = [AVCaptureSession new];
+ NSNotificationCenter *notify =
+ [NSNotificationCenter defaultCenter];
+ [notify addObserver: self
+ selector: @selector(onVideoError:)
+ name: AVCaptureSessionRuntimeErrorNotification
+ object: session];
+ [notify addObserver: self
+ selector: @selector(onVideoStart:)
+ name: AVCaptureSessionDidStartRunningNotification
+ object: session];
+ [notify addObserver: self
+ selector: @selector(onVideoStop:)
+ name: AVCaptureSessionDidStopRunningNotification
+ object: session];
+ [notify addObserver: self
+ selector: @selector(onVideoStop:)
+ name: AVCaptureSessionWasInterruptedNotification
+ object: session];
+ [notify addObserver: self
+ selector: @selector(onVideoStart:)
+ name: AVCaptureSessionInterruptionEndedNotification
+ object: session];
+
+ self.device = [AVCaptureDevice
+ defaultDeviceWithMediaType: AVMediaTypeVideo];
+
+ captureReader = [[ZBarCaptureReader alloc]
+ initWithImageScanner: scanner];
+ captureReader.captureDelegate = (id<ZBarCaptureDelegate>)self;
+ [session addOutput: captureReader.captureOutput];
+
+ if([session canSetSessionPreset: AVCaptureSessionPreset640x480])
+ session.sessionPreset = AVCaptureSessionPreset640x480;
+
+ [captureReader addObserver: self
+ forKeyPath: @"size"
+ options: 0
+ context: NULL];
+
+ [self initSubviews];
+}
+
+- (void) initSubviews
+{
+ AVCaptureVideoPreviewLayer *videoPreview =
+ [[AVCaptureVideoPreviewLayer
+ layerWithSession: session]
+ retain];
+ preview = videoPreview;
+ CGRect bounds = self.bounds;
+ bounds.origin = CGPointZero;
+ preview.bounds = bounds;
+ preview.position = CGPointMake(bounds.size.width / 2,
+ bounds.size.height / 2);
+ videoPreview.videoGravity = AVLayerVideoGravityResizeAspectFill;
+ [self.layer addSublayer: preview];
+
+ [super initSubviews];
+}
+
+- (void) dealloc
+{
+ [[NSNotificationCenter defaultCenter]
+ removeObserver: self];
+ if(showsFPS) {
+ @try {
+ [captureReader removeObserver: self
+ forKeyPath: @"framesPerSecond"];
+ }
+ @catch(...) { }
+ }
+ @try {
+ [captureReader removeObserver: self
+ forKeyPath: @"size"];
+ }
+ @catch(...) { }
+ captureReader.captureDelegate = nil;
+ [captureReader release];
+ captureReader = nil;
+ [device release];
+ device = nil;
+ [input release];
+ input = nil;
+ [session release];
+ session = nil;
+ [super dealloc];
+}
+
+- (void) updateCrop
+{
+ [super updateCrop];
+ captureReader.scanCrop = effectiveCrop;
+}
+
+- (ZBarImageScanner*) scanner
+{
+ return(captureReader.scanner);
+}
+
+- (void) setDevice: (AVCaptureDevice*) newdev
+{
+ id olddev = device;
+ AVCaptureInput *oldinput = input;
+ assert(!olddev == !oldinput);
+
+ NSError *error = nil;
+ device = [newdev retain];
+ if(device) {
+ assert([device hasMediaType: AVMediaTypeVideo]);
+ input = [[AVCaptureDeviceInput alloc]
+ initWithDevice: newdev
+ error: &error];
+ assert(input);
+ }
+ else
+ input = nil;
+
+ [session beginConfiguration];
+ if(oldinput)
+ [session removeInput: oldinput];
+ if(input)
+ [session addInput: input];
+ [session commitConfiguration];
+
+ [olddev release];
+ [oldinput release];
+}
+
+- (BOOL) enableCache
+{
+ return(captureReader.enableCache);
+}
+
+- (void) setEnableCache: (BOOL) enable
+{
+ captureReader.enableCache = enable;
+}
+
+- (void) setTorchMode: (NSInteger) mode
+{
+ [super setTorchMode: mode];
+ if(running && [device isTorchModeSupported: mode])
+ @try {
+ device.torchMode = mode;
+ }
+ @catch(...) { }
+}
+
+- (void) setShowsFPS: (BOOL) show
+{
+ [super setShowsFPS: show];
+ @try {
+ if(show)
+ [captureReader addObserver: self
+ forKeyPath: @"framesPerSecond"
+ options: 0
+ context: NULL];
+ else
+ [captureReader removeObserver: self
+ forKeyPath: @"framesPerSecond"];
+ }
+ @catch(...) { }
+}
+
+- (void) start
+{
+ if(started)
+ return;
+ [super start];
+
+ [session startRunning];
+ captureReader.enableReader = YES;
+}
+
+- (void) stop
+{
+ if(!started)
+ return;
+ [super stop];
+
+ captureReader.enableReader = NO;
+ [session stopRunning];
+}
+
+- (void) flushCache
+{
+ [captureReader flushCache];
+}
+
+- (void) configureDevice
+{
+ if([device isFocusModeSupported: AVCaptureFocusModeContinuousAutoFocus])
+ device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
+ if([device isTorchModeSupported: torchMode])
+ device.torchMode = torchMode;
+}
+
+- (void) lockDevice
+{
+ if(!running || locked) {
+ assert(0);
+ return;
+ }
+
+ // lock device and set focus mode
+ NSError *error = nil;
+ if([device lockForConfiguration: &error]) {
+ locked = YES;
+ [self configureDevice];
+ }
+ else {
+ zlog(@"failed to lock device: %@", error);
+ // just keep trying
+ [self performSelector: @selector(lockDevice)
+ withObject: nil
+ afterDelay: .5];
+ }
+}
+
+
+// AVCaptureSession notifications
+
+- (void) onVideoStart: (NSNotification*) note
+{
+ zlog(@"onVideoStart: running=%d %@", running, note);
+ if(running)
+ return;
+ running = YES;
+ locked = NO;
+
+ [self lockDevice];
+
+ if([readerDelegate respondsToSelector: @selector(readerViewDidStart:)])
+ [readerDelegate readerViewDidStart: self];
+}
+
+- (void) onVideoStop: (NSNotification*) note
+{
+ zlog(@"onVideoStop: %@", note);
+ if(!running)
+ return;
+ running = NO;
+
+ if(locked)
+ [device unlockForConfiguration];
+ else
+ [NSObject cancelPreviousPerformRequestsWithTarget: self
+ selector: @selector(lockDevice)
+ object: nil];
+ locked = NO;
+
+ if([readerDelegate respondsToSelector:
+ @selector(readerView:didStopWithError:)])
+ [readerDelegate readerView: self
+ didStopWithError: nil];
+}
+
+- (void) onVideoError: (NSNotification*) note
+{
+ zlog(@"onVideoError: %@", note);
+ if(running) {
+ // FIXME does session always stop on error?
+ running = started = NO;
+ [device unlockForConfiguration];
+ }
+ NSError *err =
+ [note.userInfo objectForKey: AVCaptureSessionErrorKey];
+
+ if([readerDelegate respondsToSelector:
+ @selector(readerView:didStopWithError:)])
+ [readerDelegate readerView: self
+ didStopWithError: err];
+ else
+ NSLog(@"ZBarReaderView: ERROR during capture: %@: %@",
+ [err localizedDescription],
+ [err localizedFailureReason]);
+}
+
+// NSKeyValueObserving
+
+- (void) observeValueForKeyPath: (NSString*) path
+ ofObject: (id) obj
+ change: (NSDictionary*) info
+ context: (void*) ctx
+{
+ if(obj == captureReader &&
+ [path isEqualToString: @"size"])
+ // adjust preview to match image size
+ [self setImageSize: captureReader.size];
+ else if(obj == captureReader &&
+ [path isEqualToString: @"framesPerSecond"])
+ fpsLabel.text = [NSString stringWithFormat: @"%.2ffps ",
+ captureReader.framesPerSecond];
+}
+
+// ZBarCaptureDelegate
+
+- (void) captureReader: (ZBarCaptureReader*) reader
+ didTrackSymbols: (ZBarSymbolSet*) syms
+{
+ [self didTrackSymbols: syms];
+}
+
+- (void) captureReader: (ZBarCaptureReader*) reader
+ didReadNewSymbolsFromImage: (ZBarImage*) zimg
+{
+ zlog(@"scanned %d symbols: %@", zimg.symbols.count, zimg);
+ if(!readerDelegate)
+ return;
+
+ UIImageOrientation orient = [UIDevice currentDevice].orientation;
+ if(!UIDeviceOrientationIsValidInterfaceOrientation(orient)) {
+ orient = interfaceOrientation;
+ if(orient == UIInterfaceOrientationLandscapeLeft)
+ orient = UIDeviceOrientationLandscapeLeft;
+ else if(orient == UIInterfaceOrientationLandscapeRight)
+ orient = UIDeviceOrientationLandscapeRight;
+ }
+ switch(orient)
+ {
+ case UIDeviceOrientationPortraitUpsideDown:
+ orient = UIImageOrientationLeft;
+ break;
+ case UIDeviceOrientationLandscapeLeft:
+ orient = UIImageOrientationUp;
+ break;
+ case UIDeviceOrientationLandscapeRight:
+ orient = UIImageOrientationDown;
+ break;
+ default:
+ orient = UIImageOrientationRight;
+ break;
+ }
+
+ UIImage *uiimg = [zimg UIImageWithOrientation: orient];
+ [readerDelegate
+ readerView: self
+ didReadSymbols: zimg.symbols
+ fromImage: uiimg];
+}
+
+@end