iOS二维码扫描

iOS二维码扫描导入AVFoundation库,并将它加入.pch预编译文件给相机预览控制器DTCameraPreviewController添加四个私有成员,获取AVFoundation的“终端”、“输入”、“输出”、“管理员”对象:@implementationDTCameraPreviewController{AVCaptureDevice*_camera;AVCaptureDevice

大家好,又见面了,我是你们的朋友全栈君。

扫码器所用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,方便使用。所以新建一个继承自UIView的DTVideoPreviewView类。头文件中,定义一个属性以获取视频预览层:


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

}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/149600.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 直接学 Vue 3 吧 —— 对话 Vue.js 作者尤雨溪[通俗易懂]

    直接学 Vue 3 吧 —— 对话 Vue.js 作者尤雨溪[通俗易懂]《程序员》于2000年创刊,其理念为技术改变世界,创新驱动中国。2021年,《程序员》2.0全新起航,首期以「开发者的黄金十年」为主题,以音视频、图文专栏等丰富的多媒体形式为载体,立足当下,放眼未来,为读者带来全方位的技术和产业解读。本文为《程序员》2.0第一期内容,在UNIX开发者BrianW.Kernighan之后,我们采访到Vue.js的作者尤雨溪,与其共谈精彩程序人生、共论顶级开源项目的成功之道。从复杂的jQuery插件化开发到模块化及组件化,现代前端技术在迭代.

    2022年4月29日
  • https,httpClient 绕过证书验证的两种写法「建议收藏」

    https,httpClient 绕过证书验证的两种写法「建议收藏」https,httpClient 绕过证书验证的两种写法

    2022年4月23日
  • python不报错但计算不出结果_excel表格不能用公式怎么办

    python不报错但计算不出结果_excel表格不能用公式怎么办excel模板设置好公式即可。在下面这行代码:workbook.write(out);// 输出Excel内容,生成Excel文件 之前,添加这个语句:workbook.setForceFormulaRecalculation(true);// 执行公式。workbook.setForceFormulaRecalculation(true);// 执行公式workbook.write(out);// 输出Excel内容,生成Excel文件…

    2022年8月19日
  • Clipboard.js_js复制文本代码

    Clipboard.js_js复制文本代码Clipboard.js实现文本复制或者剪切到剪切板引用js文件使用一使用二使用三

    2022年8月4日
  • 【转载】互联网架构,如何进行容量设计?

    【转载】互联网架构,如何进行容量设计?

    2021年11月18日
  • 快速排序—(面试碰到过好几次)

    快速排序—(面试碰到过好几次)原理:  快速排序,说白了就是给基准数据找其正确索引位置的过程.  如下图所示,假设最开始的基准数据为数组第一个元素23,则首先用一个临时变量去存储基准数据,即tmp=23;然后分别从数组的两端扫描数组,设两个指示标志:low指向起始位置,high指向末尾.  首先从后半部分开始,如果扫描到的值大于基准数据就让high减1,如果发现有元素比该基准数据的值小(如上图中18&lt…

    2022年7月11日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号