iOS二维码扫描

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MEzYA1RB-1675666218742)(http://ichenwin.qiniudn.com/avfoundation.png)]

  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有三种对焦模式:


AVCaptureFocusModeContinuousAutoFocus

AVCaptureFocusModeAutoFocus

AVCaptureFocusModeLocked

监测扫描区域的变化:


- (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];

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值