AVFoundation-自定義拍照

AVCaptureSession是AVFoundation的核心類(lèi),用于捕捉視頻和音頻,協(xié)調(diào)視頻和音頻的輸入和輸出流.

  • 輸入源
    設(shè)備AVCaptureDevice
    [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]
  • 輸出源
    添加AVCaptureOutput,即AVCaptureSession的輸出源.一般輸出源分成:音視頻源,圖片源,文件源等.
    音視頻輸出AVCaptureAudioDataOutput,AVCaptureVideoDataOutput.
    靜態(tài)圖片輸出AVCaptureStillImageOutput(iOS10中被AVCapturePhotoOutput取代了)
    AVCaptureMovieFileOutput表示文件源.

創(chuàng)建會(huì)話AVCaptureSession

-(AVCaptureSession *)captureSession{
    if (!_captureSession) {
        _captureSession = [[AVCaptureSession alloc]init];
        if ([[UIDevice currentDevice]userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
            customLog(@"當(dāng)前使用的是手機(jī)");
        }
    }
    return _captureSession;
}

創(chuàng)建輸入源

1省咨、創(chuàng)建AVCaptureDevice

-(AVCaptureDevice *)getCaptureDevice{
    // 每次獲取都要重新創(chuàng)建
    NSArray * devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice * device in devices) {
        if (device.position == self.captureDevicePosition) {
            NSError * error = nil;
            if ([device lockForConfiguration:&error]) {
                // 焦點(diǎn) 曝光 黑白色 -> 自動(dòng)
                if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
                    device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
                }
                if ([device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
                    device.exposureMode = AVCaptureExposureModeContinuousAutoExposure;
                }
                if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) {
                    device.whiteBalanceMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance;
                }
                
                [device unlockForConfiguration];
            }
            
            _captureDevice = device;
            return _captureDevice;
        }
    }
    return nil;
}

2官边、創(chuàng)建AVCaptureDeviceInput

-(AVCaptureDeviceInput *)getCaptureDeviceInput{
    NSError * error = nil;
    AVCaptureDevice * device = [self getCaptureDevice];
    _captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
    if (error || device == nil) {
        NSLog(@"創(chuàng)建失敗: %@",error.description);
    }
    return _captureDeviceInput;
}

3、添加輸入源到會(huì)話中

AVCaptureSession * session = self.captureSession;
    // 添加輸入對(duì)象到會(huì)話
    if ([session.inputs containsObject:self.captureDeviceInput]) {
        [session removeInput:self.captureDeviceInput];
    }
    AVCaptureDeviceInput * input = [self getCaptureDeviceInput];
    if ([session canAddInput:input]) {
        [session addInput:input];
    }

創(chuàng)建輸出源并添加到會(huì)話中

 // 添加照片輸出
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10.0")) {
        AVCapturePhotoOutput * photoOutput = [[AVCapturePhotoOutput alloc]init];
        
        // 先移除
        if ([session.outputs containsObject:self.captureOutput]) {
            [session removeOutput:self.captureOutput];
        }
        // 添加
        if ([session canAddOutput:photoOutput]) {
            [session addOutput:photoOutput];
        }
        
        self.captureOutput = photoOutput;
    }else{
        AVCaptureStillImageOutput * stillImageOutput = [[AVCaptureStillImageOutput alloc]init];
        NSDictionary *myOutputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey,nil];
        [stillImageOutput setOutputSettings:myOutputSettings];
        
        // 先移除
        if ([session.outputs containsObject:self.captureOutput]) {
            [session removeOutput:self.captureOutput];
        }
        // 添加
        if ([session canAddOutput:stillImageOutput]){
            [session addOutput:stillImageOutput];
        }
        
        self.captureOutput = stillImageOutput;
    }   

輸出對(duì)象-視頻流對(duì)象作為輸出適合及時(shí)獲取輸出數(shù)據(jù)(直播)

    AVCaptureVideoDataOutput *videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
    [videoDataOutput setAlwaysDiscardsLateVideoFrames:YES];// 默認(rèn)為YES
    // 設(shè)置代理脚粟,捕獲視頻樣品數(shù)據(jù)
    // 注意:隊(duì)列必須是串行隊(duì)列,才能獲取到數(shù)據(jù),而且不能為空
    dispatch_queue_t photoCaptureQueue = dispatch_queue_create(MDPhotoCaptureQueue, DISPATCH_QUEUE_SERIAL);
    [videoDataOutput setSampleBufferDelegate:self queue:photoCaptureQueue];
    if ([session canAddOutput:videoDataOutput]) {
        [session addOutput:videoDataOutput];
    }
    #pragma mark AVCaptureVideoDataOutputSampleBufferDelegate
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
- // 及時(shí)獲取視頻輸出數(shù)據(jù)
    customLog(@"采集到數(shù)據(jù)");
}

添加預(yù)覽圖層

-(void)addPreviewLayer{
    // 視頻預(yù)覽圖層
    AVCaptureVideoPreviewLayer *previedLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
    previedLayer.frame = [UIScreen mainScreen].bounds;
    _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    [self.view.layer insertSublayer:previedLayer atIndex:0];
    _previewLayer = previedLayer;
}

切換前后置攝像頭-切換輸入源

- (void)changeInput{
    // 移除輸入對(duì)象
    if (self.captureDeviceInput && [self.captureSession.inputs containsObject:self.captureDeviceInput]) {
        [self.captureSession removeInput:self.captureDeviceInput];
    }
    // 添加輸入對(duì)象到會(huì)話
    AVCaptureDeviceInput * input = [self getCaptureDeviceInput];
    if ([self.captureSession canAddInput:input]) {
        [self.captureSession addInput:input];
    }
    self.isChangeCaptureDevicePostion = NO;
    // 給攝像頭的切換添加翻轉(zhuǎn)動(dòng)畫(huà)
    CATransition *animation = [CATransition animation];
    animation.duration = .5f;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    animation.type = @"oglFlip";
    if (self.captureDevicePosition == AVCaptureDevicePositionFront) {
        animation.subtype = kCATransitionFromLeft;
    }else{
        animation.subtype = kCATransitionFromRight;
    }
    [self.previewLayer addAnimation:animation forKey:nil];
}

拍照按鈕點(diǎn)擊-獲取圖片

-(void)commitButtonAction{
    // 拍照
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10.0")) {
        
        AVCapturePhotoOutput * output = (AVCapturePhotoOutput *)self.captureOutput;
        
        //從 AVCaptureStillImageOutput 中取得 AVCaptureConnection
        self.captureConnection = [output connectionWithMediaType:AVMediaTypeVideo];
        AVCapturePhotoSettings * settings = [AVCapturePhotoSettings photoSettings];
        
        [output capturePhotoWithSettings:settings delegate:self];
    }else{
        AVCaptureStillImageOutput * stillImageOutput = (AVCaptureStillImageOutput *)self.captureOutput;
        //從 AVCaptureStillImageOutput 中取得 AVCaptureConnection
        self.captureConnection = [stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
        [stillImageOutput captureStillImageAsynchronouslyFromConnection:self.captureConnection completionHandler:^(CMSampleBufferRef  _Nullable imageDataSampleBuffer, NSError * _Nullable error) {
            if (imageDataSampleBuffer != nil) {
                NSData * data = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
                // 取得靜態(tài)影像 兩種獲取方式
                //UIImage * image = [[UIImage alloc]initWithData:data];
                
                CFDataRef cfData = CFBridgingRetain(data);
                CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(cfData);
                CGImageRef cgImage = CGImageCreateWithJPEGDataProvider(dataProvider, nil, YES, kCGRenderingIntentDefault);
                CFBridgingRelease(cfData);
                
                // 裁剪圖片
                [self cropWithImage:[UIImage imageWithCGImage:cgImage]];

                // 取得影像數(shù)據(jù)(需要ImageIO.framework 與 CoreMedia.framework)
                CFDictionaryRef attachments = CMGetAttachment(imageDataSampleBuffer, kCGImagePropertyExifDictionary, NULL);
                customLog(@"影像屬性: %@", attachments);
            }
        }];
    }
}

代理獲取圖片AVCapturePhotoOutput

#pragma mark AVCapturePhotoCaptureDelegate
// IOS - 11
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error{
    if (error) {
        customLog(@"獲取圖片錯(cuò)誤 --- %@",error.localizedDescription);
    }
    if (photo) {
        if (@available(iOS 11.0, *)) {
            CGImageRef cgImage = [photo CGImageRepresentation];
            UIImage * image = [UIImage imageWithCGImage:cgImage];
            customLog(@"獲取圖片成功 --- %@",image);
        } else {
            customLog(@"不是走這個(gè)代理方法");
        }
    }
}

- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhotoSampleBuffer:(nullable CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(nullable CMSampleBufferRef)previewPhotoSampleBuffer resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings bracketSettings:(nullable AVCaptureBracketedStillImageSettings *)bracketSettings error:(nullable NSError *)error{
    if (error) {
        customLog(@"獲取圖片錯(cuò)誤 --- %@",error.localizedDescription);
    }
    if (photoSampleBuffer) {
        NSData *data = [AVCapturePhotoOutput JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer previewPhotoSampleBuffer:previewPhotoSampleBuffer];
        UIImage *image = [UIImage imageWithData:data];
        customLog(@"獲取圖片成功 --- %@",image);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鬓催,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恨锚,老刑警劉巖宇驾,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異眠冈,居然都是意外死亡飞苇,警方通過(guò)查閱死者的電腦和手機(jī)菌瘫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)蜗顽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人雨让,你說(shuō)我怎么就攤上這事雇盖。” “怎么了栖忠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵崔挖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我庵寞,道長(zhǎng)狸相,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任捐川,我火速辦了婚禮脓鹃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘古沥。我一直安慰自己瘸右,他們只是感情好娇跟,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著太颤,像睡著了一般苞俘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上龄章,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天吃谣,我揣著相機(jī)與錄音,去河邊找鬼瓦堵。 笑死基协,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的菇用。 我是一名探鬼主播澜驮,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼惋鸥!你這毒婦竟也來(lái)了杂穷?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤卦绣,失蹤者是張志新(化名)和其女友劉穎耐量,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體滤港,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡廊蜒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了溅漾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片山叮。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖添履,靈堂內(nèi)的尸體忽然破棺而出屁倔,到底是詐尸還是另有隱情,我是刑警寧澤暮胧,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布锐借,位于F島的核電站,受9級(jí)特大地震影響往衷,放射性物質(zhì)發(fā)生泄漏钞翔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一席舍、第九天 我趴在偏房一處隱蔽的房頂上張望布轿。 院中可真熱鬧,春花似錦、人聲如沸驮捍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)东且。三九已至启具,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間珊泳,已是汗流浹背鲁冯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留色查,地道東北人薯演。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像秧了,于是被迫代替她去往敵國(guó)和親跨扮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容