zbar-windows/iphone/ZBarCaptureReader.m
Mauro Carvalho Chehab cd5b63e5b5 Update to the very latest version of zbar
zbar was using a 2010 snapshot of its hg tree.
Take a new snapshot to get zbar's improvements.

[Imported from Fedora 26 tree]
2017-03-25 23:04:38 -03:00

371 lines
10 KiB
Objective-C

//------------------------------------------------------------------------
// 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 <libkern/OSAtomic.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreMedia/CoreMedia.h>
#import <CoreVideo/CoreVideo.h>
#import <ZBarSDK/ZBarCaptureReader.h>
#import <ZBarSDK/ZBarImageScanner.h>
#import "ZBarCVImage.h"
#define MODULE ZBarCaptureReader
#import "debug.h"
enum {
STOPPED = 0,
RUNNING = 1,
PAUSED = 2,
CAPTURE = 4,
};
@implementation ZBarCaptureReader
@synthesize captureOutput, captureDelegate, scanner, scanCrop, size,
framesPerSecond, enableCache;
@dynamic enableReader;
- (void) initResult
{
[result release];
result = [ZBarCVImage new];
result.format = [ZBarImage fourcc: @"CV2P"];
}
- (id) initWithImageScanner: (ZBarImageScanner*) _scanner
{
self = [super init];
if(!self)
return(nil);
t_fps = t_frame = timer_now();
enableCache = YES;
scanner = [_scanner retain];
scanCrop = CGRectMake(0, 0, 1, 1);
image = [ZBarImage new];
image.format = [ZBarImage fourcc: @"Y800"];
[self initResult];
captureOutput = [AVCaptureVideoDataOutput new];
captureOutput.alwaysDiscardsLateVideoFrames = YES;
#ifdef FIXED_8697526
/* iOS 4.2 introduced a bug that causes [session startRunning] to
* hang if the session has a preview layer and this property is
* specified at the output. As this happens to be the default
* setting for the currently supported devices, it can be omitted
* without causing a functional problem (for now...). Of course,
* we still have no idea what the real problem is, or how robust
* this is as a workaround...
*/
captureOutput.videoSettings =
[NSDictionary
dictionaryWithObject:
[NSNumber numberWithInt:
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]
forKey: (NSString*)kCVPixelBufferPixelFormatTypeKey];
#endif
queue = dispatch_queue_create("ZBarCaptureReader", NULL);
[captureOutput setSampleBufferDelegate:
(id<AVCaptureVideoDataOutputSampleBufferDelegate>)self
queue: queue];
return(self);
}
- (id) init
{
self = [self initWithImageScanner:
[[ZBarImageScanner new]
autorelease]];
if(!self)
return(nil);
[scanner setSymbology: 0
config: ZBAR_CFG_X_DENSITY
to: 3];
[scanner setSymbology: 0
config: ZBAR_CFG_Y_DENSITY
to: 3];
return(self);
}
- (void) dealloc
{
captureDelegate = nil;
// queue continues to run after stopping (NB even after DidStopRunning!);
// ensure released delegate is not called. (also NB that the queue
// may not be null, even in this case...)
[captureOutput setSampleBufferDelegate: nil
queue: queue];
[captureOutput release];
captureOutput = nil;
dispatch_release(queue);
[image release];
image = nil;
[result release];
result = nil;
[scanner release];
scanner = nil;
[super dealloc];
}
- (BOOL) enableReader
{
return(OSAtomicOr32Barrier(0, &state) & RUNNING);
}
- (void) setEnableReader: (BOOL) enable
{
if(!enable)
OSAtomicAnd32Barrier(STOPPED, &state);
else if(!(OSAtomicOr32OrigBarrier(RUNNING, &state) & RUNNING)) {
OSAtomicAnd32Barrier(~PAUSED, &state);
@synchronized(scanner) {
scanner.enableCache = enableCache;
}
}
}
- (void) willStartRunning
{
self.enableReader = YES;
}
- (void) willStopRunning
{
self.enableReader = NO;
}
- (void) flushCache
{
@synchronized(scanner) {
scanner.enableCache = enableCache;
}
}
- (void) captureFrame
{
OSAtomicOr32(CAPTURE, &state);
}
- (void) setCaptureDelegate: (id<ZBarCaptureDelegate>) delegate
{
@synchronized(scanner) {
captureDelegate = delegate;
}
}
- (void) cropUpdate
{
@synchronized(scanner) {
image.crop = CGRectMake(scanCrop.origin.x * width,
scanCrop.origin.y * height,
scanCrop.size.width * width,
scanCrop.size.height * height);
}
}
- (void) setScanCrop: (CGRect) crop
{
if(CGRectEqualToRect(scanCrop, crop))
return;
scanCrop = crop;
[self cropUpdate];
}
- (void) didTrackSymbols: (ZBarSymbolSet*) syms
{
[captureDelegate
captureReader: self
didTrackSymbols: syms];
}
- (void) didReadNewSymbolsFromImage: (ZBarImage*) img
{
timer_start;
[captureDelegate
captureReader: self
didReadNewSymbolsFromImage: img];
OSAtomicAnd32Barrier(~PAUSED, &state);
zlog(@"latency: delegate=%gs total=%gs",
timer_elapsed(t_start, timer_now()),
timer_elapsed(t_scan, timer_now()));
}
- (void) setFramesPerSecond: (CGFloat) fps
{
framesPerSecond = fps;
}
- (void) updateFPS: (NSNumber*) val
{
[self setFramesPerSecond: val.doubleValue];
}
- (void) setSize: (CGSize) _size
{
size = _size;
}
- (void) updateSize: (CFDictionaryRef) val
{
CGSize _size;
if(CGSizeMakeWithDictionaryRepresentation(val, &_size))
[self setSize: _size];
}
- (void) captureOutput: (AVCaptureOutput*) output
didOutputSampleBuffer: (CMSampleBufferRef) samp
fromConnection: (AVCaptureConnection*) conn
{
// queue is apparently not flushed when stopping;
// only process when running
uint32_t _state = OSAtomicOr32Barrier(0, &state);
if((_state & (PAUSED | RUNNING)) != RUNNING)
return;
NSAutoreleasePool *pool = [NSAutoreleasePool new];
image.sequence = framecnt++;
uint64_t now = timer_now();
double dt = timer_elapsed(t_frame, now);
t_frame = now;
if(dt > 2) {
t_fps = now;
dt_frame = 0;
}
else if(!dt_frame)
dt_frame = dt;
dt_frame = (dt_frame + dt) / 2;
if(timer_elapsed(t_fps, now) >= 1) {
[self performSelectorOnMainThread: @selector(updateFPS:)
withObject: [NSNumber numberWithDouble: 1 / dt_frame]
waitUntilDone: NO];
t_fps = now;
}
CVImageBufferRef buf = CMSampleBufferGetImageBuffer(samp);
if(CMSampleBufferGetNumSamples(samp) != 1 ||
!CMSampleBufferIsValid(samp) ||
!CMSampleBufferDataIsReady(samp) ||
!buf) {
zlog(@"ERROR: invalid sample");
goto error;
}
OSType format = CVPixelBufferGetPixelFormatType(buf);
int planes = CVPixelBufferGetPlaneCount(buf);
if(format != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ||
!planes) {
zlog(@"ERROR: invalid buffer format");
goto error;
}
int w = CVPixelBufferGetBytesPerRowOfPlane(buf, 0);
int h = CVPixelBufferGetHeightOfPlane(buf, 0);
CVReturn rc =
CVPixelBufferLockBaseAddress(buf, kCVPixelBufferLock_ReadOnly);
if(!w || !h || rc) {
zlog(@"ERROR: invalid buffer data");
goto error;
}
void *data = CVPixelBufferGetBaseAddressOfPlane(buf, 0);
if(data) {
[image setData: data
withLength: w * h];
BOOL doTrack = NO;
int ngood = 0;
ZBarSymbolSet *syms = nil;
@synchronized(scanner) {
if(width != w || height != h) {
width = w;
height = h;
CGSize _size = CGSizeMake(w, h);
CFDictionaryRef sized =
CGSizeCreateDictionaryRepresentation(_size);
if(sized) {
[self performSelectorOnMainThread: @selector(updateSize:)
withObject: (id)sized
waitUntilDone: NO];
CFRelease(sized);
}
image.size = _size;
[self cropUpdate];
}
ngood = [scanner scanImage: image];
syms = scanner.results;
doTrack = [captureDelegate respondsToSelector:
@selector(captureReader:didTrackSymbols:)];
}
now = timer_now();
if(ngood >= 0) {
// return unfiltered results for tracking feedback
syms.filterSymbols = NO;
int nraw = syms.count;
if(nraw > 0 || (_state & CAPTURE))
zlog(@"scan image: %dx%d crop=%@ ngood=%d nraw=%d st=%d",
w, h, NSStringFromCGRect(image.crop), ngood, nraw, _state);
if(ngood || (_state & CAPTURE)) {
// copy image data so we can release the buffer
result.size = CGSizeMake(w, h);
result.pixelBuffer = buf;
result.symbols = syms;
t_scan = now;
OSAtomicXor32Barrier((_state & CAPTURE) | PAUSED, &state);
[self performSelectorOnMainThread:
@selector(didReadNewSymbolsFromImage:)
withObject: result
waitUntilDone: NO];
[self initResult];
}
if(nraw && doTrack)
[self performSelectorOnMainThread:
@selector(didTrackSymbols:)
withObject: syms
waitUntilDone: NO];
}
[image setData: NULL
withLength: 0];
}
else
zlog(@"ERROR: invalid data");
CVPixelBufferUnlockBaseAddress(buf, kCVPixelBufferLock_ReadOnly);
error:
[pool release];
}
@end