iOS 自定义相机 - 拍照

一:简介

截图1.png

AVCaptureDevice录制视频过程.png
 AVCaptureStillImageOutput    输出图片
 AVCapturePhotoOutput         照片输出流
 AVCaptureSession             把输入输出结合在一起,并开始启动捕获设备(摄像头)
                              媒体(音、视频)捕获会话,负责把捕获的音视频数据输出到输出设备中。一个AVCaptureSession可以有多个输入输出
 AVCaptureDevice              捕获设备,通常是前置摄像头,后置摄像头,麦克风(音频输入)
 AVCaptureDeviceInput         代表输入设备,他使用AVCaptureDevice 来初始化
 AVCaptureOutput              输出数据管理对象,用于接收各类输出数据,通常使用对应的子类AVCaptureAudioDataOutput、
                              AVCaptureStillImageOutput、AVCaptureVideoDataOutput、AVCaptureFileOutput,该对象将会被添加到
                              AVCaptureSession中管理。

注意:
1,前面几个对象的输出数据都是NSData类型,而AVCaptureFileOutput代表数据以文件形式输出,类似的,AVCcaptureFileOutput也不会直接创建使用,通常会使用其子类:AVCaptureAudioFileOutputAVCaptureMovieFileOutput
2,当把一个输入或者输出添加到AVCaptureSession之后AVCaptureSession就会在所有相符的输入、输出设备之间建立连接(AVCaptionConnection):

AVCaptureVideoPreviewLayer:相机拍摄预览图层,是CALayer的子类,使用该对象可以实时查看拍照或视频录制效果,创建该对象需要指定对应的AVCaptureSession对象。
AVCaptureDevicePositionBack  后置摄像头
AVCaptureDevicePositionFront 前置摄像头
AVCaptureMetadataOutput      当启动摄像头开始捕获输入
AVCaptureDevicePosition      摄像头位置

闪光灯和白平衡可以在生成相机时候设置
曝光要根据对焦点的光线状况而决定,所以和对焦一块写

 point为点击的位置
 AVCaptureFlashMode           闪光灯
 AVCaptureFocusMode           对焦
 AVCaptureExposureMode        曝光
 AVCaptureWhiteBalanceMode    白平衡

一定要先设置位置,再设置对焦模式

拿到的图像的大小可以自行设定

 AVCaptureSessionPreset320x240
 AVCaptureSessionPreset352x288
 AVCaptureSessionPreset640x480
 AVCaptureSessionPreset960x540
 AVCaptureSessionPreset1280x720
 AVCaptureSessionPreset1920x1080
 AVCaptureSessionPreset3840x2160

二:拍照和录制视频的一般步骤
使用AVFoundation拍照和录制视频的一般步骤如下:

1.创建AVCaptureSession对象。
2.使用AVCaptureDevice的静态方法获得需要使用的设备,例如拍照和录像
3.就需要获得摄像头设备,录音就要获得麦克风设备。
4.利用输入设备AVCaptureDevice初始化AVCaptureDeviceInput对象。
5.初始化输出数据管理对象,如果要拍照就初始化
6.AVCaptureStillImageOutput对象;如果拍摄视频就初始化AVCaptureMovieFileOutput对象。
7.将数据输入对象AVCaptureDeviceInput、数据输出对象AVCaptureOutput添加到媒体会话管理对象AVCaptureSession中。
8.创建视频预览图层AVCaptureVideoPreviewLayer并指定媒体会话,添加图层到显示容器中,调用AVCaptureSessionstartRuning方法开始捕获。
9.将捕获的音频或视频数据输出到指定文件。
*/

iOS相机的基本开发

目录
一、设置会话

1、初始化会话枢纽
2、创建视频输入
3、添加到会话中
4、创建音频输入
5、添加音频输入
6、设置静态图片输出
7、设置录像输出

二、开启会话

1、开启会话
2、摄像头处理
2.1 获取指定位置摄像头
2.2 获取当前活跃的摄像头
2.3 获取当前可用摄像头数和未使用的摄像头
2.4 切换摄像头
3、对焦处理
3.1 是否支持对焦
3.2 设置对焦点
4、曝光处理
4.1 是否支持曝光
4.2 设置曝光
4.3 重置对焦曝光
5、闪光灯 & 手电筒
5.1 判断是否有闪光灯
5.2 闪光灯模式
5.3 设置闪光灯模式
5.4 是否支持手电筒
5.5 手电筒模式
5.6 设置手电筒模式

三、拍摄

1、拍摄静态图片
2、写入媒体库
3、捕捉视频
3.1 判断是否录制中
3.2 开始录制
3.3 结束录制
3.4 录制结束回调
3.5 将视频写入媒体库
3.6 制作视频缩略图

ps: 这里可以看到录制的视频包。

image.png

四、具体demo
关联的摄像头页面

image.png

对应控制器的代码:

#import "HBViewController2.h"
#import 

#define kScreenBounds   [UIScreen mainScreen].bounds
#define kScreenWidth  kScreenBounds.size.width*1.0
#define kScreenHeight kScreenBounds.size.height*1.0

@interface HBViewController2 ()
//捕获设备,通常是前置摄像头,后置摄像头,麦克风(音频输入)
@property(nonatomic)AVCaptureDevice *device;
//AVCaptureDeviceInput 代表输入设备,他使用AVCaptureDevice 来初始化
@property(nonatomic)AVCaptureDeviceInput *input;
//当启动摄像头开始捕获输入
@property(nonatomic)AVCaptureMetadataOutput *output;
@property (nonatomic)AVCaptureStillImageOutput *ImageOutPut;
//session:由他把输入输出结合在一起,并开始启动捕获设备(摄像头)
@property(nonatomic)AVCaptureSession *session;
//图像预览层,实时显示捕获的图像
@property(nonatomic)AVCaptureVideoPreviewLayer *previewLayer;

@property (weak, nonatomic) IBOutlet UIButton *PhotoButton;
@property (weak, nonatomic) IBOutlet UIButton *flashButton;

@property (weak, nonatomic) IBOutlet UIButton *cancleButton;
@property (weak, nonatomic) IBOutlet UIButton *changeButton;

@property (nonatomic)UIImageView *imageView;
@property (nonatomic)UIView *greenView;
@property (nonatomic)BOOL isflashOn;
@property (nonatomic)UIImage *image;

@property (nonatomic)BOOL canCa;
@property (weak, nonatomic) IBOutlet UIView *xyView;

@end

@implementation HBViewController2

- (void)viewDidLoad {
    [super viewDidLoad];
    _canCa = [self canUserCamear];
    if (_canCa) {
        [self customCamera];
        
        
    }
    
}

- (void)customCamera{

    
#pragma mark --  相机 牌照相关
    //AVCaptureDevice捕获设备,通常是前置摄像头,后置摄像头,麦克风(音频输入) 使用AVMediaTypeVideo 指明self.device代表视频,默认使用后置摄像头进行初始化
    self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    //AVCaptureDeviceInput 代表输入设备,他使用AVCaptureDevice 来初始化
    self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
    
    //当启动摄像头开始捕获输入
    self.output = [[AVCaptureMetadataOutput alloc]init];
    self.ImageOutPut = [[AVCaptureStillImageOutput alloc] init];
    
    //session:由他把输入输出结合在一起,并开始启动捕获设备(摄像头) 生成会话,用来结合输入输出
    self.session = [[AVCaptureSession alloc]init];
    
    if ([self.session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
         self.session.sessionPreset = AVCaptureSessionPreset1280x720;
    }
    
    if ([self.session canAddInput:self.input]) {
        [self.session addInput:self.input];
    }
    
    if ([self.session canAddOutput:self.ImageOutPut]) {
        [self.session addOutput:self.ImageOutPut];
    }
    
    //使用self.session,初始化预览层,self.session负责驱动input进行信息的采集,layer负责把图像渲染显示 图像预览层,实时显示捕获的图像
    self.previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
    
    self.previewLayer.frame = CGRectMake(0, 0,  kScreenWidth-20*2,  240);
    self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;

    //layer 加到这里 就是把摄像头捕捉的视频画面 给到这个view去显示   
    [self.xyView.layer addSublayer:self.previewLayer];
    
    //开始启动
    [self.session startRunning];
    if ([_device lockForConfiguration:nil]) {
        if ([_device isFlashModeSupported:AVCaptureFlashModeAuto]) {
            [_device setFlashMode:AVCaptureFlashModeAuto];
        }
        //自动白平衡
        if ([_device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
            [_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];
        }
        [_device unlockForConfiguration];
    }
    
#pragma mark -- 四个按钮  和 绿色框
    self.view.backgroundColor = [UIColor systemGroupedBackgroundColor];
    
    self.greenView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 60, 60)];
    self.greenView .layer.borderWidth = 1.0;
    self.greenView .layer.borderColor =[UIColor greenColor].CGColor;
    self.greenView .backgroundColor = [UIColor clearColor];
    [self.xyView addSubview: self.greenView ];
    self.greenView .hidden = YES;
    
    
    [self.view bringSubviewToFront: self.xyView];
    
    [self.view bringSubviewToFront:self.cancleButton];
    [self.view bringSubviewToFront:self.PhotoButton];
    [self.view bringSubviewToFront:self.changeButton];
    [self.view bringSubviewToFront:self.flashButton];
    [self.view bringSubviewToFront: self.greenView ];

    
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapGesture:)];
    [self.xyView addGestureRecognizer:tapGesture];
}


#pragma mark - 按钮点击事件
- (IBAction)click:(UIButton *)sender {
    
    switch (sender.tag) {
            
        case 0:
        {
            //拍照
            AVCaptureConnection * videoConnection = [self.ImageOutPut connectionWithMediaType:AVMediaTypeVideo];
            if (!videoConnection) {
                NSLog(@"take photo failed!");
                return;
            }
            
            [self.ImageOutPut captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
                if (imageDataSampleBuffer == NULL) {
                    return;
                }
                NSData * imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
                self.image = [UIImage imageWithData:imageData];
                [self.session stopRunning];
                [self saveImageToPhotoAlbum:self.image];
                self.imageView = [[UIImageView alloc]initWithFrame:self.previewLayer.frame];
                [self.xyView insertSubview:_imageView belowSubview:_PhotoButton];
                self.imageView.layer.masksToBounds = YES;
                self.imageView.image = _image;
                NSLog(@"image size = %@",NSStringFromCGSize(self.image.size));
            }];
        }
            break;
        case 1:
        {
            //取消
            
            [self.imageView removeFromSuperview];
            [self.session stopRunning];
            
            [self.navigationController popViewControllerAnimated:YES];
        }
            break;
        case 2:
        {
            //切换
            NSUInteger cameraCount = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
            if (cameraCount > 1) {
                NSError *error;
                
                CATransition *animation = [CATransition animation];
                
                animation.duration = .5f;
                
                animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
                
                animation.type = @"oglFlip";
                AVCaptureDevice *newCamera = nil;
                AVCaptureDeviceInput *newInput = nil;
                AVCaptureDevicePosition position = [[_input device] position];
                if (position == AVCaptureDevicePositionFront){
                    newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
                    animation.subtype = kCATransitionFromLeft;
                }
                else {
                    newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
                    animation.subtype = kCATransitionFromRight;
                }
                
                newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
                [self.previewLayer addAnimation:animation forKey:nil];
                if (newInput != nil) {
                    [self.session beginConfiguration];
                    [self.session removeInput:_input];
                    if ([self.session canAddInput:newInput]) {
                        [self.session addInput:newInput];
                        self.input = newInput;
                        
                    } else {
                        [self.session addInput:self.input];
                    }
                    
                    [self.session commitConfiguration];
                    
                } else if (error) {
                    NSLog(@"toggle carema failed, error = %@", error);
                }
                
            }
        }
            break;
        case 3:
        {
            //闪光灯
            if ([_device lockForConfiguration:nil]) {
                if (_isflashOn) {
                    if ([_device isFlashModeSupported:AVCaptureFlashModeOff]) {
                        [_device setFlashMode:AVCaptureFlashModeOff];
                        _isflashOn = NO;
                        [_flashButton setTitle:@"闪光灯关" forState:UIControlStateNormal];
                    }
                }else{
                    if ([_device isFlashModeSupported:AVCaptureFlashModeOn]) {
                        [_device setFlashMode:AVCaptureFlashModeOn];
                        _isflashOn = YES;
                        [_flashButton setTitle:@"闪光灯开" forState:UIControlStateNormal];
                    }
                }
                
                [_device unlockForConfiguration];
            }
        }
            break;
            
        default:
            break;
    }
}


- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position{
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for ( AVCaptureDevice *device in devices )
        if ( device.position == position ) return device;
    return nil;
}

#pragma mark -- 绿框 触碰
- (void)tapGesture:(UITapGestureRecognizer*)gesture{
    CGPoint point = [gesture locationInView:gesture.view];
    [self focusAtPoint:point];
}

- (void)focusAtPoint:(CGPoint)point{
    
    CGSize size = self.view.bounds.size;
    CGPoint focusPoint = CGPointMake( point.y /size.height ,1-point.x/size.width );
    NSError *error;
    if ([self.device lockForConfiguration:&error]) {
        
        if ([self.device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
            [self.device setFocusPointOfInterest:focusPoint];
            [self.device setFocusMode:AVCaptureFocusModeAutoFocus];
        }
        
        if ([self.device isExposureModeSupported:AVCaptureExposureModeAutoExpose ]) {
            [self.device setExposurePointOfInterest:focusPoint];
            [self.device setExposureMode:AVCaptureExposureModeAutoExpose];
        }
        
        [self.device unlockForConfiguration];
        _greenView.center = point;
        _greenView.hidden = NO;
        [UIView animateWithDuration:0.3 animations:^{
            _greenView.transform = CGAffineTransformMakeScale(1.25, 1.25);
        }completion:^(BOOL finished) {
            [UIView animateWithDuration:0.5 animations:^{
                _greenView.transform = CGAffineTransformIdentity;
            } completion:^(BOOL finished) {
                _greenView.hidden = YES;
            }];
        }];
    }
    
}

#pragma mark -  保存至相册
- (void)saveImageToPhotoAlbum:(UIImage*)savedImage{
    
    UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);
    
}

// 回调方法
- (void)image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo{
    
    NSString *msg = nil ;
    if(error != NULL){
        msg = @"保存图片失败" ;
    }else{
        msg = @"保存图片成功" ;
    }
    
    UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"保存图片结果提示" message:msg preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"点击了按钮1,进入按钮1的事件");
        
        [self.imageView removeFromSuperview];
        [self.session startRunning];
        
        [self.navigationController popViewControllerAnimated:YES];
    }];
    
    UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"点击了取消");
        
        [self.imageView removeFromSuperview];
        [self.session stopRunning];
        
        [self.navigationController popViewControllerAnimated:YES];
    }];
    
    [alertController addAction:action1];
    [alertController addAction:action2];
    
    [self presentViewController:alertController animated:YES completion:nil];
    
}

#pragma mark - 检查相机权限
- (BOOL)canUserCamear{
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (authStatus == AVAuthorizationStatusDenied) {
        
        UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"请打开相机权限" message:@"设置-隐私-相机" preferredStyle:UIAlertControllerStyleAlert];
        
        UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            
            NSURL * url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
            if([[UIApplication sharedApplication] canOpenURL:url]) {
                [[UIApplication sharedApplication] openURL:url options:nil completionHandler:^(BOOL success) {
                    
                }];
            }
        }];
        
        UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            NSLog(@"点击了取消");
            
            
        }];
        
        [alertController addAction:action1];
        [alertController addAction:action2];
        
        [self presentViewController:alertController animated:YES completion:nil];
        
        return NO;
    }
    else{
        return YES;
    }
    return YES;
}


@end

你可能感兴趣的:(iOS 自定义相机 - 拍照)