iOS 自定義相機(jī) - 拍照

一:簡(jiǎn)介

截圖1.png

AVCaptureDevice錄制視頻過(guò)程.png
 AVCaptureStillImageOutput    輸出圖片
 AVCapturePhotoOutput         照片輸出流
 AVCaptureSession             把輸入輸出結(jié)合在一起,并開始啟動(dòng)捕獲設(shè)備(攝像頭)
                              媒體(音、視頻)捕獲會(huì)話,負(fù)責(zé)把捕獲的音視頻數(shù)據(jù)輸出到輸出設(shè)備中。一個(gè)AVCaptureSession可以有多個(gè)輸入輸出
 AVCaptureDevice              捕獲設(shè)備,通常是前置攝像頭,后置攝像頭,麥克風(fēng)(音頻輸入)
 AVCaptureDeviceInput         代表輸入設(shè)備,他使用AVCaptureDevice 來(lái)初始化
 AVCaptureOutput              輸出數(shù)據(jù)管理對(duì)象,用于接收各類輸出數(shù)據(jù),通常使用對(duì)應(yīng)的子類AVCaptureAudioDataOutput、
                              AVCaptureStillImageOutput、AVCaptureVideoDataOutput、AVCaptureFileOutput,該對(duì)象將會(huì)被添加到
                              AVCaptureSession中管理。

注意:
1,前面幾個(gè)對(duì)象的輸出數(shù)據(jù)都是NSData類型,而AVCaptureFileOutput代表數(shù)據(jù)以文件形式輸出,類似的,AVCcaptureFileOutput也不會(huì)直接創(chuàng)建使用,通常會(huì)使用其子類:AVCaptureAudioFileOutput、AVCaptureMovieFileOutput
2,當(dāng)把一個(gè)輸入或者輸出添加到AVCaptureSession之后AVCaptureSession就會(huì)在所有相符的輸入、輸出設(shè)備之間建立連接(AVCaptionConnection):

AVCaptureVideoPreviewLayer:相機(jī)拍攝預(yù)覽圖層,是CALayer的子類,使用該對(duì)象可以實(shí)時(shí)查看拍照或視頻錄制效果,創(chuàng)建該對(duì)象需要指定對(duì)應(yīng)的AVCaptureSession對(duì)象。
AVCaptureDevicePositionBack  后置攝像頭
AVCaptureDevicePositionFront 前置攝像頭
AVCaptureMetadataOutput      當(dāng)啟動(dòng)攝像頭開始捕獲輸入
AVCaptureDevicePosition      攝像頭位置

閃光燈和白平衡可以在生成相機(jī)時(shí)候設(shè)置
曝光要根據(jù)對(duì)焦點(diǎn)的光線狀況而決定,所以和對(duì)焦一塊寫

 point為點(diǎn)擊的位置
 AVCaptureFlashMode           閃光燈
 AVCaptureFocusMode           對(duì)焦
 AVCaptureExposureMode        曝光
 AVCaptureWhiteBalanceMode    白平衡

一定要先設(shè)置位置,再設(shè)置對(duì)焦模式

拿到的圖像的大小可以自行設(shè)定

 AVCaptureSessionPreset320x240
 AVCaptureSessionPreset352x288
 AVCaptureSessionPreset640x480
 AVCaptureSessionPreset960x540
 AVCaptureSessionPreset1280x720
 AVCaptureSessionPreset1920x1080
 AVCaptureSessionPreset3840x2160

二:拍照和錄制視頻的一般步驟
使用AVFoundation拍照和錄制視頻的一般步驟如下:

1.創(chuàng)建AVCaptureSession對(duì)象。
2.使用AVCaptureDevice的靜態(tài)方法獲得需要使用的設(shè)備,例如拍照和錄像
3.就需要獲得攝像頭設(shè)備,錄音就要獲得麥克風(fēng)設(shè)備。
4.利用輸入設(shè)備AVCaptureDevice初始化AVCaptureDeviceInput對(duì)象。
5.初始化輸出數(shù)據(jù)管理對(duì)象,如果要拍照就初始化
6.AVCaptureStillImageOutput對(duì)象;如果拍攝視頻就初始化AVCaptureMovieFileOutput對(duì)象。
7.將數(shù)據(jù)輸入對(duì)象AVCaptureDeviceInput、數(shù)據(jù)輸出對(duì)象AVCaptureOutput添加到媒體會(huì)話管理對(duì)象AVCaptureSession中。
8.創(chuàng)建視頻預(yù)覽圖層AVCaptureVideoPreviewLayer并指定媒體會(huì)話,添加圖層到顯示容器中,調(diào)用AVCaptureSessionstartRuning方法開始捕獲。
9.將捕獲的音頻或視頻數(shù)據(jù)輸出到指定文件。
*/

iOS相機(jī)的基本開發(fā)

目錄
一、設(shè)置會(huì)話

1、初始化會(huì)話樞紐
2、創(chuàng)建視頻輸入
3、添加到會(huì)話中
4、創(chuàng)建音頻輸入
5、添加音頻輸入
6、設(shè)置靜態(tài)圖片輸出
7、設(shè)置錄像輸出

二、開啟會(huì)話

1、開啟會(huì)話
2、攝像頭處理
2.1 獲取指定位置攝像頭
2.2 獲取當(dāng)前活躍的攝像頭
2.3 獲取當(dāng)前可用攝像頭數(shù)和未使用的攝像頭
2.4 切換攝像頭
3、對(duì)焦處理
3.1 是否支持對(duì)焦
3.2 設(shè)置對(duì)焦點(diǎn)
4、曝光處理
4.1 是否支持曝光
4.2 設(shè)置曝光
4.3 重置對(duì)焦曝光
5、閃光燈 & 手電筒
5.1 判斷是否有閃光燈
5.2 閃光燈模式
5.3 設(shè)置閃光燈模式
5.4 是否支持手電筒
5.5 手電筒模式
5.6 設(shè)置手電筒模式

三、拍攝

1、拍攝靜態(tài)圖片
2、寫入媒體庫(kù)
3、捕捉視頻
3.1 判斷是否錄制中
3.2 開始錄制
3.3 結(jié)束錄制
3.4 錄制結(jié)束回調(diào)
3.5 將視頻寫入媒體庫(kù)
3.6 制作視頻縮略圖

ps: 這里可以看到錄制的視頻包。

image.png

四、具體demo
關(guān)聯(lián)的攝像頭頁(yè)面

image.png

對(duì)應(yīng)控制器的代碼:

#import "HBViewController2.h"
#import <AVFoundation/AVFoundation.h>

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

@interface HBViewController2 ()<AVCaptureMetadataOutputObjectsDelegate,UIAlertViewDelegate>
//捕獲設(shè)備,通常是前置攝像頭,后置攝像頭,麥克風(fēng)(音頻輸入)
@property(nonatomic)AVCaptureDevice *device;
//AVCaptureDeviceInput 代表輸入設(shè)備,他使用AVCaptureDevice 來(lái)初始化
@property(nonatomic)AVCaptureDeviceInput *input;
//當(dāng)啟動(dòng)攝像頭開始捕獲輸入
@property(nonatomic)AVCaptureMetadataOutput *output;
@property (nonatomic)AVCaptureStillImageOutput *ImageOutPut;
//session:由他把輸入輸出結(jié)合在一起,并開始啟動(dòng)捕獲設(shè)備(攝像頭)
@property(nonatomic)AVCaptureSession *session;
//圖像預(yù)覽層,實(shí)時(shí)顯示捕獲的圖像
@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 --  相機(jī) 牌照相關(guān)
    //AVCaptureDevice捕獲設(shè)備,通常是前置攝像頭,后置攝像頭,麥克風(fēng)(音頻輸入) 使用AVMediaTypeVideo 指明self.device代表視頻,默認(rèn)使用后置攝像頭進(jìn)行初始化
    self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    //AVCaptureDeviceInput 代表輸入設(shè)備,他使用AVCaptureDevice 來(lái)初始化
    self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
    
    //當(dāng)啟動(dòng)攝像頭開始捕獲輸入
    self.output = [[AVCaptureMetadataOutput alloc]init];
    self.ImageOutPut = [[AVCaptureStillImageOutput alloc] init];
    
    //session:由他把輸入輸出結(jié)合在一起,并開始啟動(dòng)捕獲設(shè)備(攝像頭) 生成會(huì)話,用來(lái)結(jié)合輸入輸出
    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,初始化預(yù)覽層,self.session負(fù)責(zé)驅(qū)動(dòng)input進(jìn)行信息的采集,layer負(fù)責(zé)把圖像渲染顯示 圖像預(yù)覽層,實(shí)時(shí)顯示捕獲的圖像
    self.previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
    
    self.previewLayer.frame = CGRectMake(0, 0,  kScreenWidth-20*2,  240);
    self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;

    //layer 加到這里 就是把攝像頭捕捉的視頻畫面 給到這個(gè)view去顯示   
    [self.xyView.layer addSublayer:self.previewLayer];
    
    //開始啟動(dòng)
    [self.session startRunning];
    if ([_device lockForConfiguration:nil]) {
        if ([_device isFlashModeSupported:AVCaptureFlashModeAuto]) {
            [_device setFlashMode:AVCaptureFlashModeAuto];
        }
        //自動(dòng)白平衡
        if ([_device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
            [_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];
        }
        [_device unlockForConfiguration];
    }
    
#pragma mark -- 四個(gè)按鈕  和 綠色框
    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 - 按鈕點(diǎn)擊事件
- (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:@"閃光燈關(guān)" 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 -  保存至相冊(cè)
- (void)saveImageToPhotoAlbum:(UIImage*)savedImage{
    
    UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);
    
}

// 回調(diào)方法
- (void)image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo{
    
    NSString *msg = nil ;
    if(error != NULL){
        msg = @"保存圖片失敗" ;
    }else{
        msg = @"保存圖片成功" ;
    }
    
    UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"保存圖片結(jié)果提示" message:msg preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"點(diǎn)擊了按鈕1,進(jìn)入按鈕1的事件");
        
        [self.imageView removeFromSuperview];
        [self.session startRunning];
        
        [self.navigationController popViewControllerAnimated:YES];
    }];
    
    UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"點(diǎn)擊了取消");
        
        [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 - 檢查相機(jī)權(quán)限
- (BOOL)canUserCamear{
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (authStatus == AVAuthorizationStatusDenied) {
        
        UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"請(qǐng)打開相機(jī)權(quán)限" message:@"設(shè)置-隱私-相機(jī)" 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(@"點(diǎn)擊了取消");
            
            
        }];
        
        [alertController addAction:action1];
        [alertController addAction:action2];
        
        [self presentViewController:alertController animated:YES completion:nil];
        
        return NO;
    }
    else{
        return YES;
    }
    return YES;
}


@end

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容