iOS使用AVFoundation实现二维码扫描

原文:http://strivingboy.github.io/blog/2014/11/08/scan-qrcode/

关于二维码扫描有不少优秀第三方库如:

最近项目需要,看了下使用ios7自带的 AVFoundation Framework 来实现二维码扫描,Demo 见:scan_qrcode_demo

关于AVFoundation

AVFoundation 是一个很大基础库,用来创建基于时间的视听媒体,可以使用它来检查,创建、编辑或媒体文件。也可以输入流从设备和操作视频实时捕捉和回放。详细框架介绍见官网:About AV Foundation,本文只是介绍如果使用AVFoundation获取二维码。

首先获取流媒体信息我们需要AVCaptureSession对象来管理输入流和输出流,AVCaptureVideoPreviewLayer对象来显示信息,基本流程如下图所示:

技术分享

注:

  • AVCaptureSession 管理输入(AVCaptureInput)和输出(AVCaptureOutput)流,包含开启和停止会话方法。
  • AVCaptureDeviceInput 是AVCaptureInput的子类,可以作为输入捕获会话,用AVCaptureDevice实例初始化。
  • AVCaptureDevice 代表了物理捕获设备如:摄像机。用于配置等底层硬件设置相机的自动对焦模式。
  • AVCaptureMetadataOutput 是AVCaptureOutput的子类,处理输出捕获会话。捕获的对象传递给一个委托实现AVCaptureMetadataOutputObjectsDelegate协议。协议方法在指定的派发队列(dispatch queue)上执行。
  • AVCaptureVideoPreviewLayerCALayer的一个子类,显示捕获到的相机输出流。

下面看下实现过程如下:

Step1:需要导入:AVFoundation Framework 包含头文件:

#import <AVFoundation/AVFoundation.h>

Step2:设置捕获会话

设置 AVCaptureSession 和 AVCaptureVideoPreviewLayer 成员

1
2
3
4
5
6 7 8 9 10 
    #import <AVFoundation/AVFoundation.h>

    static const char *kScanQRCodeQueueName = "ScanQRCodeQueue";   @interface ViewController () <AVCaptureMetadataOutputObjectsDelegate>  .....  @property (nonatomic) AVCaptureSession *captureSession;  @property (nonatomic) AVCaptureVideoPreviewLayer *videoPreviewLayer;  @property (nonatomic) BOOL lastResult;  @end 

Step3:创建会话,读取输入流

1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 
    - (BOOL)startReading  {  // 获取 AVCaptureDevice 实例  NSError * error;  AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];  // 初始化输入流  AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];  if (!input) {  NSLog(@"%@", [error localizedDescription]);  return NO;  }  // 创建会话  _captureSession = [[AVCaptureSession alloc] init];  // 添加输入流  [_captureSession addInput:input];  // 初始化输出流  AVCaptureMetadataOutput *captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];  // 添加输出流  [_captureSession addOutput:captureMetadataOutput];   // 创建dispatch queue.  dispatch_queue_t dispatchQueue;  dispatchQueue = dispatch_queue_create(kScanQRCodeQueueName, NULL);  [captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatchQueue];  // 设置元数据类型 AVMetadataObjectTypeQRCode  [captureMetadataOutput setMetadataObjectTypes:[NSArray arrayWithObject:AVMetadataObjectTypeQRCode]];   // 创建输出对象  _videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];  [_videoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];  [_videoPreviewLayer setFrame:_sanFrameView.layer.bounds];  [_sanFrameView.layer addSublayer:_videoPreviewLayer];  // 开始会话  [_captureSession startRunning]; return YES; } 

Step4:停止读取

1
2
3
4
5
6 7 
    - (void)stopReading  {  // 停止会话  [_captureSession stopRunning];  _captureSession = nil;  } 

Step5:获取捕获数据

1
2
3
4
5
6 7 8 9 10 11 12 13 14 
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects  fromConnection:(AVCaptureConnection *)connection {  if (metadataObjects != nil && [metadataObjects count] > 0) {  AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];  NSString *result;  if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode]) {  result = metadataObj.stringValue;  } else {  NSLog(@"不是二维码");  }  [self performSelectorOnMainThread:@selector(reportScanResult:) withObject:result waitUntilDone:NO];  } } 

Step6:处理结果

1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 
  - (void)reportScanResult:(NSString *)result  {  [self stopReading];  if (!_lastResult) {  return;  }  _lastResut = NO;  UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"二维码扫描"  message:result  delegate:nil  cancelButtonTitle:@"取消"  otherButtonTitles: nil];  [alert show];  // 以下处理了结果,继续下次扫描  _lastResult = YES;  } 

以上基本就是二维码的获取流程,和扫一扫二维码伴随的就是开启系统照明,这个比较简单,也是利用 AVCaptureDevice,请看如下实现:

1
2
3
4
5
6 7 8 9 10 11 12 13 
  - (void)systemLightSwitch:(BOOL)open  {  AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];  if ([device hasTorch]) {  [device lockForConfiguration:nil];  if (open) {  [device setTorchMode:AVCaptureTorchModeOn];  } else {  [device setTorchMode:AVCaptureTorchModeOff];  }  [device unlockForConfiguration];  }  } 

以上就是本文介绍的大部分内容,详细代码请看demo scan_qrcode_deomo

实现过程中遇到一下两个问题:

1、扫描一个二维码,输出流回重复调用,代理方法头文件介绍:

1
2
3
4
5
6 7 8 9 10 11 12 13 
   /*!
  @method captureOutput:didOutputMetadataObjects:fromConnection:
 .....  @discussion  Delegates receive this message whenever the output captures and emits new objects, as specified by  its metadataObjectTypes property. Delegates can use the provided objects in conjunction with other APIs  for further processing. This method will be called on the dispatch queue specified by the output‘s  metadataObjectsCallbackQueue property. **This method may be called frequently** so it must be efficient to  prevent capture performance problems, including dropped metadata objects.   Clients that need to reference metadata objects outside of the scope of this method must retain them and  then release them when they are finished with them.  */ 

代理方法会频繁调用,我暂且用一个标记(@property (nonatomic) BOOL lastResult)表示是否是第一次扫描成功,来处理。

2、AVFoundation 该库不能扫描相册中的二维码图片,不知为啥苹果没有支持,有知道实现的麻烦告诉我哈。

参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值