iOS二维码扫描

来源:互联网 发布:uva 知乎 编辑:程序博客网 时间:2024/06/05 11:03

扫码器所用AVFoundation模块图

  1. 导入AVFoundation库,并将它加入.pch预编译文件

  2. 给相机预览控制器DTCameraPreviewController添加四个私有成员,获取AVFoundation的“终端”、“输入”、“输出”、“管理员”对象:

@implementation DTCameraPreviewController{    AVCaptureDevice *_camera;    AVCaptureDeviceInput *_videoInput;    AVCaptureStillImageOutput *_imageOutput;    AVCaptureSession *_captureSession;}
  1. 选取录制设备(摄像头或麦克风)

AVCaptureDevice提供了一个类方法,指定一种媒体类型(AVMediaTypeVideo or AVMediaTypeAudio)它便能返回对应的录制设备。其他媒体类型可以在AVMediaFormat.h中找到,不过它们不需要录制设备(如文本、字幕等)。

DTCameraPreviewController.m中实现_setupCamera方法,用来初始化若干个AVFoundation中用于录制的对象,

- (void)_setupCamera {    //获取到一个录制设备(摄像头)    _camera = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];    //创建摄像头的输入,initWithDevice:方法自动为设备分配了一个端口,每个端口只能传输一路媒体数据    NSError *error;    _videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:_camera error:&error];    if (!_videoInput) {        NSLog(@"Error connecting video input: %@", [error localizedDescription]);        return;    }}
  1. 媒体录制“管理进程”

AVCaptureSession是媒体录制进程的的管理员。控制着设备的输入输出。将输入添加至设备(_setupCamera方法):

    //创建录制“管理进程”,将输入添加至设备    _captureSession = [[AVCaptureSession alloc] init];    if (![_captureSession canAddInput:_videoInput]) {        NSLog(@"Unable to add video input to capture session");        return;    }    [_captureSession addInput:_videoInput];
  1. 显示实时视频预览

苹果提供了预览层AVCaptureVideoPreviewLayer,它可以提供摄像头画面的实时预览。因为它是CALayer的子类,将它封装至UIView,方便使用。所以新建一个继承自UIViewDTVideoPreviewView类。头文件中,定义一个属性以获取视频预览层:

@interface DTVideoPreviewView : UIView@property (readonly) AVCaptureVideoPreviewLayer *previewLayer;@end

实现文件:

@implementation DTVideoPreviewView//代码创建实例时调用- (id)initWithFrame:(CGRect)frame {   self = [super initWithFrame:frame];   if (self)   {      [self _commonSetup];   }   return self;}//通过Nib创建- (void)awakeFromNib {    [self _commonSetup];}//替代默认的CALayer+ (Class)layerClass {    return [AVCaptureVideoPreviewLayer class];}- (void)_commonSetup {    self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;    self.backgroundColor = [UIColor blackColor];    [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; }//类型转换- (AVCaptureVideoPreviewLayer *)previewLayer {    return (AVCaptureVideoPreviewLayer *)self.layer;}@end

将storyboard中的根视图类型改为DTVideoPreviewView

DTCameraPreviewController中添加以下viewDidLoad方法:

- (void)viewDidLoad {    [super viewDidLoad];    NSAssert([self.view isKindOfClass:[DTVideoPreviewView class]], @"Wrong root view class %@ in %@", NSStringFromClass([self.view class]), NSStringFromClass([self class]));    _videoPreview = (DTVideoPreviewView *)self.view;     [self _setupCamera];}

以及在_setupCamera最后将预览图层添加至管理进程中:

_videoPreview.previewLayer.session = _captureSession;

至此,我们已将流程图中的AVCaptureDeviceInput连至预览图层。

启动摄像头需调用-startRunning

- (void)viewWillAppear:(BOOL)animated {   [super viewWillAppear:animated];   [_captureSession startRunning];}- (void)viewDidDisappear:(BOOL)animated {    [super viewDidDisappear:animated];    [_captureSession stopRunning];}
  1. 设置闪光灯
- (void)_setupTorchToggleButton {    if ([_camera hasTorch]) {        self.toggleTorchButton.hidden = NO;    } else {        self.toggleTorchButton.hidden = YES;    }}- (IBAction)toggleTorch:(id)sender {    if ([_camera hasTorch]) {        BOOL torchActive = [_camera isTorchActive];        if ([_camera lockForConfiguration:nil]) {            if (torchActive) {                if ([_camera isTorchModeSupported:AVCaptureTorchModeOff]) {                    [_camera setTorchMode:AVCaptureTorchModeOff];                }            } else {                if ([_camera isTorchModeSupported:AVCaptureTorchModeOn]) {                    [_camera setTorchMode:AVCaptureTorchModeOn];                }            }            [_camera unlockForConfiguration];        }    }}
  1. 抓取照片

完整的_setupCamera

- (void)_setupCamera {    _camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];    if (!_camera) {        [self.snapButton setTitle:@"No Camera Found" forState:UIControlStateNormal];        self.snapButton.enabled = NO;        [self _informUserAboutNoCam];        return;    }    NSError *error;    _videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:_camera error:&error];    if(!_videoInput) {        NSLog(@"error connectiong video input: %@", [error localizedDescription]);        return;    }    _captureSession = [[AVCaptureSession alloc] init];    if (![_captureSession canAddInput:_videoInput]) {        NSLog(@"Unable to add video input to capture session");        return;    }    [_captureSession addInput:_videoInput];//    [self _configureCurrentCamera];    _imageOutput = [AVCapturePhotoOutput new];    if (![_captureSession canAddOutput:_imageOutput]) {        NSLog(@"Unable to add still image output to capture session");        return;    }    [_captureSession addOutput:_imageOutput];    _videoPreview.previewLayer.session = _captureSession;}

获取当前链路:

- (AVCaptureConnection *)_captureConnection {    for (AVCaptureConnection *connection in _imageOutput.connections) {        for (AVCaptureInputPort *port in [connection inputPorts]) {            if ([port.mediaType isEqual:AVMediaTypeVideo]) {                return connection;            }        }     }    return nil; }

拍照:

- (IBAction)snap:(id)sender {    if (!_camera) {        return;    }    AVCaptureConnection *videoConnection = [self _captureConnection];    if (!videoConnection) {        NSLog(@"Error:No Video connection found on still image output");    }    [_imageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) {        if (error) {            NSLog(@"Error capturing still image: %@", [error localizedDescription]);            return;        }        NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation: imageSampleBuffer];        UIImage *image = [UIImage imageWithData:imageData];        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);     }];}
  1. 对焦

iOS有三种对焦模式:

AVCaptureFocusModeContinuousAutoFocusAVCaptureFocusModeAutoFocusAVCaptureFocusModeLocked

监测扫描区域的变化:

- (void)_configureCurrentCamera {    if ([_camera isFocusModeSupported:AVCaptureFocusModeLocked]) {        if ([_camera lockForConfiguration:nil]) {            _camera.subjectAreaChangeMonitoringEnabled = YES;            [_camera unlockForConfiguration];        }    }}

一旦画面有变化,iOS系统就会发出AVCaptureDeviceSubjectAreaDidChangeNotification通知,我们可以再-viewDidLoad中订阅这一通知:

- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.    NSAssert([self.view isKindOfClass:[CWVideoPreviewView class]],             @"Wrong root view class %@ in %@",             NSStringFromClass([self.view class]),             NSStringFromClass([self class]));    _videoPreview = (CWVideoPreviewView *)self.view;    [self _setupCamera];    _videoPreview.previewLayer.session = _captureSession;    [self _setupCameraAfterCheckingAuthorization];    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];    [self.view addGestureRecognizer:tap];    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];    [center addObserver:self               selector:@selector(subjectChanged:)                   name:AVCaptureDeviceSubjectAreaDidChangeNotification                 object:nil];}
原创粉丝点击