iOS 自定義相機 拍照+視頻錄制(一)



BSFramework 組件包:


shortshoot.gif

框架:<AVFoundation/AVFoundation.h>

大致流程:

  • session 初始化
  • device 初始化
  • input,output 初始化,并關聯device
  • session 添加 input 和 output
  • 初始化預覽層 AVCaptureVideoPreviewLayer
  • session startRunning
  • 點擊拍照, didFinishProcessingPhotoSampleBuffer 代理回調后,處理數據,生成照片

PS : 其中還含有一些攝像頭翻轉,閃光燈等操作,但屬于額外功能,不算流程,具體全部功能看源碼


一、使用 AVCaptureSession 進行拍照



需要的屬性大致有

@property (nonatomic ,strong) AVCaptureSession *session;
@property (nonatomic ,strong) AVCaptureDevice *device;
@property (nonatomic ,strong) AVCaptureDeviceInput *deviceInput;//圖像輸入源
@property (nonatomic ,strong) AVCaptureOutput *outPut;          //圖像輸出源
@property (nonatomic ,strong) AVPlayer *player;
@property (nonatomic ,strong) AVPlayerLayer *playerLayer;
@property (nonatomic ,strong) AVCaptureConnection *connection;



首先處理權限問題

#pragma mark - 相機授權
-(void)authorization{
    //請求相機權限
    AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    
    if (status == AVAuthorizationStatusNotDetermined) {
        
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
                        
            dispatch_async(dispatch_get_main_queue(), ^{
                if (granted) {
                    [self authorization];
                }else{
                    UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"" message:@"相機權限未開啟,請前往 手機-設置 開啟相機權限" preferredStyle:UIAlertControllerStyleAlert];
                    UIAlertAction *action = [UIAlertAction actionWithTitle:@"我知道了" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                        
                        [self.navigationController popViewControllerAnimated:YES];
                        [self dismissViewControllerAnimated:YES completion:nil];
                    }];
                    
                    [controller addAction:action];
                    [self presentViewController:controller animated:YES completion:nil];
                    return;
                }
            });
        }];
        
    }else if (status == AVAuthorizationStatusDenied) {
        
        UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"" message:@"相機權限未開啟,請前往 手機-設置 開啟相機權限" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *action = [UIAlertAction actionWithTitle:@"我知道了" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            
            [self.navigationController popViewControllerAnimated:YES];
            [self dismissViewControllerAnimated:YES completion:nil];
        }];
        
        [controller addAction:action];
        [self presentViewController:controller animated:YES completion:nil];
        
        return;
    }else if (status == AVAuthorizationStatusAuthorized){
        [self configData];
        [self initSubViews];
        [self masonryLayout];
    }
}

只有權限通過了,我才初始化 UI 界面,否則就 alert 提示。

然后初始化 session ,添加 device 、input、output

-(void)configData{
    
    self.session = [[AVCaptureSession alloc]init];
    if ([self.session canSetSessionPreset:AVCaptureSessionPresetHigh]){
        self.session.sessionPreset = AVCaptureSessionPresetHigh;
    }else if ([self.session canSetSessionPreset:AVCaptureSessionPresetiFrame960x540]) {
        self.session.sessionPreset = AVCaptureSessionPresetiFrame960x540;
    }

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //如果不放在子線程里,跳轉到相機的時候,會卡
        [self addPicIO];
        [self addVideoIO];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            self.previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
            self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
            self.previewLayer.frame = CGRectMake(0, 30, SCREEN_WIDTH, SCREEN_HEIGHT - 30 - 150);
            [self.view.layer insertSublayer:self.previewLayer atIndex:0];
            [self.session startRunning];
        });
    });
}

#pragma mark 設置 拍照類型的輸入輸出源
-(void)addPicIO{
    NSError *error = nil;

    self.deviceInput = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:&error];

    if (!error) {

        if ([self.session canAddInput:self.deviceInput]) {
            [self.session addInput:self.deviceInput];
        }

        if ([self.session canAddOutput:self.outPut]) {
            [self.session addOutput:self.outPut];
        }
    }
}


點擊拍照處理

#pragma mark - action 交互事件

// 拍照
-(void)takePicture{
    
    // 防止快速連點
    if (self.bottomView.recordStatus == RECORD_STATUS_UNRECORD) {
        return;
    }

    if (@available(iOS 10.0, *)) {

        AVCapturePhotoOutput *outPut = (AVCapturePhotoOutput *)self.outPut;

        AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettingsWithFormat:@{AVVideoCodecKey:AVVideoCodecJPEG}];
        [outPut capturePhotoWithSettings:settings delegate:self];
   
    }else{
        
        // 兼容iOS 10以下機型,未測試,不清楚可不可以用
        self.connection = [self.outPut connectionWithMediaType:AVMediaTypeVideo];
        [self.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];

        AVCaptureStillImageOutput *outPut = (AVCaptureStillImageOutput *)self.outPut;

        [outPut captureStillImageAsynchronouslyFromConnection:self.connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {

            [self.session stopRunning];
            
            NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
            UIImage *image = [UIImage imageWithData:jpegData];
            self.photoImageView.image = image;
            self.photoImageView.hidden = NO;
        }];
    }

    if ([self.delegate respondsToSelector:@selector(photoCameraTakeBtnClicked)]) {
        [self.delegate photoCameraTakeBtnClicked];
    }
}

代理回調處理

-(void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings error:(NSError *)error API_AVAILABLE(ios(10.0)){

    if (photoSampleBuffer) {
        [self.session stopRunning];
        NSData *data = [AVCapturePhotoOutput JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer previewPhotoSampleBuffer:previewPhotoSampleBuffer];
        UIImage *image = [UIImage imageWithData:data];
        self.photoImageView.image = image;
        self.photoImageView.hidden = NO;
    }
}

PS : 此代理是 iOS 10 以后的方法,低于 iOS 10 需要使用 ↓↓↓

// 兼容iOS 10以下機型,未測試,不清楚可不可以用
    self.connection = [self.outPut connectionWithMediaType:AVMediaTypeVideo];
    [self.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];

    AVCaptureStillImageOutput *outPut = (AVCaptureStillImageOutput *)self.outPut;

    [outPut captureStillImageAsynchronouslyFromConnection:self.connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {

        [self.session stopRunning];
            
        NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
        UIImage *image = [UIImage imageWithData:jpegData];
        self.photoImageView.image = image;
        self.photoImageView.hidden = NO;
    }];

友情鏈接


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。