使用AVFoundation框架自定义摄像头界面

2016-10-28

涉及到的类

  • AVCaptureSession会话层
  • AVCaptureDevice 创建 配置输入设备
  • AVCaptureDeviceInput 输入
  • AVCaptureStillImageOutput 输出
  • AVCaptureVideoPreviewLayer layer层
  • UIImageOrientation 图片朝向
  • 需要遵循的协议

    AVCaptureVideoDataOutputSampleBufferDelegate

    初始化

    //1.创建会话层
        self.session = [[[AVCaptureSession alloc] init] autorelease];
        [self.session setSessionPreset:AVCaptureSessionPresetPhoto];
    
    
        //2.创建、配置输入设备
        AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    #if 1
        int flags = NSKeyValueObservingOptionNew; //监听自动对焦
        [device addObserver:self forKeyPath:@"adjustingFocus" options:flags context:nil];
    #endif
    
        NSError *error;
        AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
        if (!captureInput)
        {
            NSLog(@"Error: %@", error);
            return;
        }
        [self.session addInput:captureInput];
    
    
        //3.创建、配置输出       
        captureOutput = [[[AVCaptureStillImageOutput alloc] init] autorelease];
        NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey,nil];
        [captureOutput setOutputSettings:outputSettings];
    
        [outputSettings release];
        [self.session addOutput:captureOutput];
    

    KVO回调

    //对焦回调
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
        if( [keyPath isEqualToString:@"adjustingFocus"] ){
            BOOL adjustingFocus = [ [change objectForKey:NSKeyValueChangeNewKey] isEqualToNumber:[NSNumber numberWithInt:1] ];
            NSLog(@"Is adjusting focus? %@", adjustingFocus ? @"YES" : @"NO" );
            NSLog(@"Change dictionary: %@", change);
            if (delegate) {
                [delegate foucusStatus:adjustingFocus];
            }
        }
    }
    

    嵌入摄像头捕捉的界面

    -(void) embedPreviewInView: (UIView *) aView {
        if (!session) return;
        //设置取景
        preview = [AVCaptureVideoPreviewLayer layerWithSession: session];
        preview.frame = aView.bounds;
        preview.videoGravity = AVLayerVideoGravityResizeAspectFill; 
        [aView.layer addSublayer: preview];
    }
    

    横竖屏适配

    - (void)changePreviewOrientation:(UIInterfaceOrientation)interfaceOrientation
    {
        if (!preview) {
            return;
        }
         [CATransaction begin];
        if (interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
            g_orientation = UIImageOrientationUp;
            preview.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
    
        }else if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft){
            g_orientation = UIImageOrientationDown;
            preview.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft;
    
        }else if (interfaceOrientation == UIDeviceOrientationPortrait){
            g_orientation = UIImageOrientationRight;
            preview.connection.videoOrientation = AVCaptureVideoOrientationPortrait;
    
        }else if (interfaceOrientation == UIDeviceOrientationPortraitUpsideDown){
            g_orientation = UIImageOrientationLeft;
            preview.connection.videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown;
        }
        [CATransaction commit];
    }
    
    ### 开始工作/停止工作/拍照
    
    - (void) startRunning
    {
        [[self session] startRunning];    
    }
    
    - (void) stopRunning
    {
        [[self session] stopRunning];
    }
    
    -(void)CaptureStillImage
    {
        [self  Captureimage];
    }
    

    拍照

    -(void)Captureimage
    {
        //get connection
        AVCaptureConnection *videoConnection = nil;
        for (AVCaptureConnection *connection in captureOutput.connections) {
            for (AVCaptureInputPort *port in [connection inputPorts]) {
                if ([[port mediaType] isEqual:AVMediaTypeVideo] ) {
                    videoConnection = connection;
                    break;
                }
            }
            if (videoConnection) { break; }
        }
    
        //get UIImage
        [captureOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:
         ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
             CFDictionaryRef exifAttachments =
             CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
             if (exifAttachments) {
                 // Do something with the attachments.
             }
    
             // Continue as appropriate.
             NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
             UIImage *t_image = [UIImage imageWithData:imageData];   
             image = [[UIImage alloc]initWithCGImage:t_image.CGImage scale:1.0 orientation:g_orientation];
    
             [self giveImg2Delegate];
         }];
    }
    
    #pragma mark 切换前后摄像头
    - (IBAction)toggleButtonClick:(UIButton *)sender {
        AVCaptureDevice *currentDevice=[self.captureDeviceInput device];
        AVCaptureDevicePosition currentPosition=[currentDevice position];
        [self removeNotificationFromCaptureDevice:currentDevice];
        AVCaptureDevice *toChangeDevice;
        AVCaptureDevicePosition toChangePosition=AVCaptureDevicePositionFront;
        if (currentPosition==AVCaptureDevicePositionUnspecified||currentPosition==AVCaptureDevicePositionFront) {
            toChangePosition=AVCaptureDevicePositionBack;
        }
        toChangeDevice=[self getCameraDeviceWithPosition:toChangePosition];
        [self addNotificationToCaptureDevice:toChangeDevice];
        //获得要调整的设备输入对象
        AVCaptureDeviceInput *toChangeDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil];
    
        //改变会话的配置前一定要先开启配置,配置完成后提交配置改变
        [self.captureSession beginConfiguration];
        //移除原有输入对象
        [self.captureSession removeInput:self.captureDeviceInput];
        //添加新的输入对象
        if ([self.captureSession canAddInput:toChangeDeviceInput]) {
            [self.captureSession addInput:toChangeDeviceInput];
            self.captureDeviceInput=toChangeDeviceInput;
        }
        //提交会话配置
        [self.captureSession commitConfiguration];
    
        [self setFlashModeButtonStatus];
    }
    
    #pragma mark 自动闪光灯开启
    - (IBAction)flashAutoClick:(UIButton *)sender {
        [self setFlashMode:AVCaptureFlashModeAuto];
        [self setFlashModeButtonStatus];
    }
    #pragma mark 打开闪光灯
    - (IBAction)flashOnClick:(UIButton *)sender {
        [self setFlashMode:AVCaptureFlashModeOn];
        [self setFlashModeButtonStatus];
    }
    #pragma mark 关闭闪光灯
    - (IBAction)flashOffClick:(UIButton *)sender {
        [self setFlashMode:AVCaptureFlashModeOff];
        [self setFlashModeButtonStatus];
    }
    
    /**
     *  设置聚焦点
     *
     *  @param point 聚焦点
     */
    -(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{
        [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
            if ([captureDevice isFocusModeSupported:focusMode]) {
                [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
            }
            if ([captureDevice isFocusPointOfInterestSupported]) {
                [captureDevice setFocusPointOfInterest:point];
            }
            if ([captureDevice isExposureModeSupported:exposureMode]) {
                [captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];
            }
            if ([captureDevice isExposurePointOfInterestSupported]) {
                [captureDevice setExposurePointOfInterest:point];
            }
        }];
    }
    
    /**
     *  添加点按手势,点按时聚焦
     */
    -(void)addGenstureRecognizer{
        UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapScreen:)];
        [self.viewContainer addGestureRecognizer:tapGesture];
    }
    -(void)tapScreen:(UITapGestureRecognizer *)tapGesture{
        CGPoint point= [tapGesture locationInView:self.viewContainer];
        //将UI坐标转化为摄像头坐标
        CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
        [self setFocusCursorWithPoint:point];
        [self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
    }
    

    ###本文代码
    请查看此目录下面的 wx2 文件夹