AVCaptureSession - 基于iOS自带框架实现二维码/条码扫描

做项目的过程中,或许都遇到过扫码的需求,从最早通过zxin和zbar的解码库,自定义扫码界面完成基本的扫码需求。到近期因为公司项目对引用的三方库有严格的过审要求,从而想到基于AVFoundation框架的自定义扫码解析。这里做一个简单的小计。充当个备胎,以备不时之需。

step 1:导入框架

AVFoundation/AVFoundation.h/AVFoundation/AVCaptureDevice.h/AVFoundation/AVMediaFormat.h

step2:加入代理AVCaptureMetadataOutputObjectsDelegate/AVCaptureVideoDataOutputSampleBufferDelegate

step3:实例对象

/** 设备 */

@property (nonatomic, strong) AVCaptureDevice * device;

/** 输入输出的中间桥梁 */

@property (nonatomic, strong) AVCaptureSession * session;

/** 相机图层 */

@property (nonatomic, strong) AVCaptureVideoPreviewLayer * previewLayer;

/** 扫描支持的编码格式的数组 */

@property (nonatomic, strong) NSMutableArray * metadataObjectTypes;

这里的metadataObjectTypes数组是为了区分二维码和条形码的不同解析

/**

* MARK:根据条码状态返回不同的解码数组

*/

- (NSMutableArray * )setMetaDataOutPutWithBarcode :(BOOL)isBarcode{

    NSMutableArray *metadataOutput_Arr = [[NSMutableArray alloc]init];

    if (!isBarcode) {

        metadataOutput_Arr = [NSMutableArray arrayWithObjects:AVMetadataObjectTypeAztecCode, AVMetadataObjectTypePDF417Code, AVMetadataObjectTypeQRCode, nil];

        // >= iOS 8

        if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) {

            [metadataOutput_Arr addObjectsFromArray:@[ AVMetadataObjectTypeDataMatrixCode]];

        }

    }else{

        metadataOutput_Arr = [NSMutableArray arrayWithObjects:AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeCode39Code, AVMetadataObjectTypeCode39Mod43Code, AVMetadataObjectTypeCode93Code, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeUPCECode, nil];

        // >= iOS 8

        if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) {

            [metadataOutput_Arr addObjectsFromArray:@[AVMetadataObjectTypeInterleaved2of5Code, AVMetadataObjectTypeITF14Code]];

        }

    }

    return metadataOutput_Arr;

}

下面看看初始化

/**

*  扫描初始化

*/

- (void)capture{

    //获取摄像设备

    self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    //创建输入流

    AVCaptureDeviceInput * input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];

    //创建输出流

    self.metadataOutput = [[AVCaptureMetadataOutput alloc] init];

    {//规范化扫描有效范围

        CGFloat scanView_w = 0;

        CGFloat scanView_H = 0;

        if (self.isBarcode) {

            scanView_w = FTNScreenW-20*2;

            scanView_H = 156.0;

        }else{

            scanView_w = scanView_Width;

            scanView_H = scanView_Height;

        }

//设置你需要解析的范围(确保范围在你自定义框的内部,避免未进扫描框就可以解析条码了)

        [self.metadataOutput setRectOfInterest:CGRectMake(((FTNScreenH-scanView_H)/2)/FTNScreenH, ((FTNScreenW-scanView_w)/2)/FTNScreenW, scanView_H/FTNScreenH, scanView_w/FTNScreenW)];

    }

    //设置代理 在主线程里刷新

    [self.metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];


    //创建视频输出流

    AVCaptureVideoDataOutput * videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];

    //设置代理在子线程里刷新

    dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);

    [videoDataOutput setSampleBufferDelegate:self queue:queue];

    //设置像素格式

    videoDataOutput.videoSettings = [NSDictionary dictionaryWithObject:

                                    [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey];


    //高质量采集率

    self.session.sessionPreset = AVCaptureSessionPresetHigh;

    [self.session addInput:input];

    [self.session addOutput:self.metadataOutput];

    [self.session addOutput:videoDataOutput];


    self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];

    self.previewLayer.frame = CGRectMake(0, 0, FTNScreenW, FTNScreenH);

    self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;

    self.previewLayer.backgroundColor = [UIColor yellowColor].CGColor;

    [self.view.layer addSublayer:self.previewLayer];


    //设置扫描支持的编码格式(如下设置条形码和二维码兼容)

    //    metadataOutput.metadataObjectTypes = self.metadataObjectTypes;

    self.metadataOutput.metadataObjectTypes = [self setMetaDataOutPutWithBarcode:NO];


    //开始捕获

    [self.session startRunning];


    //自动调焦 (解决条形码扫描难)

    //先进行判断是否支持控制对焦

    if (self.device.isFocusPointOfInterestSupported) {

        NSError *error = nil;

        //对cameraDevice进行操作前,需要先锁定,防止其他线程访问

        [self.device lockForConfiguration:&error];

        [self.device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];

        //操作完成后,记得进行unlock。

        [self.device unlockForConfiguration];

    }


    if (self.device.isExposurePointOfInterestSupported) {

        NSError *error = nil;

        //对cameraDevice进行操作前,需要先锁定,防止其他线程访问

        [self.device lockForConfiguration:&error];

        [self.device setExposureMode:AVCaptureExposureModeAutoExpose];

        //操作完成后,记得进行unlock。

        [self.device unlockForConfiguration];

    }


    if (self.device.isAutoFocusRangeRestrictionSupported) {

        NSError *error = nil;

        //对cameraDevice进行操作前,需要先锁定,防止其他线程访问

        [self.device lockForConfiguration:&error];

        [self.device setAutoFocusRangeRestriction:AVCaptureAutoFocusRangeRestrictionNear];

        //操作完成后,记得进行unlock。

        [self.device unlockForConfiguration];

    }

}

最后,扫描结果的返回方法

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{

    if (metadataObjects.count > 0) {

        [self.session stopRunning];

        AVMetadataMachineReadableCodeObject * metadataObject = metadataObjects.firstObject;

        for (AVMetadataMachineReadableCodeObject * metadataObject in metadataObjects) {

            NSLog(@"-----jie guo ---- %@",metadataObject.stringValue);

        }

        self.returnScanBarCodeValue(metadataObject.stringValue);

    }

}


需要注意。扫码过程中可能会遇到很长的条形码。这时候可能会扫码解析很慢。解决办法一个是检查解析范围的合理性(setRectOfInterest),一个是设置相机对焦模式(setFocusMode)为连续自动对焦。再则可以将条码和二维码分开解析(设置metadataOutput.metadataObjectTypes = ??的不同解析模式)。

你可能感兴趣的:(AVCaptureSession - 基于iOS自带框架实现二维码/条码扫描)