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 該庫不能掃描相冊中的二維碼圖片,不知為啥蘋果沒有支持,有知道實現的麻煩告訴我哈。

參考鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM