UIImagePickerController確實強大,但是與MPMoviePlayerController類似,由于它的高度封裝性,要進行某些自定義工作就比較復雜了。例如要做出一款類似于美顏相機的拍照界面就比較難以實現了,此時就可以考慮使用AVFoundation來實現。
AVFoundation中提供了很多現成的播放器和錄音機,但是事實上它還有更加底層的內容可以供開發者使用。因為AVFoundation中抽了很多和底層輸入、輸出設備打交道的類,依靠這些類開發人員面對的不再是封裝好的音頻播放器AVAudioPlayer、錄音機(AVAudioRecorder)、視頻(包括音頻)播放器AVPlayer,而是輸入設備(例如麥克風、攝像頭)、輸出設備(圖片、視頻)等。
首先了解一下使用AVFoundation做拍照和視頻錄制開發用到的相關類:
- AVCaptureSession:媒體(音、視頻)捕獲會話,負責把捕獲的音視頻數據輸出到輸出設備中。一個AVCaptureSession可以有多個輸入輸出:
- AVCaptureDevice:輸入設備,包括麥克風、攝像頭,通過該對象可以設置物理設備的一些屬性(例如相機聚焦、白平衡等)。
- AVCaptureDeviceInput:設備輸入數據管理對象,可以根據AVCaptureDevice創建對應的AVCaptureDeviceInput對象,該對象將會被添加到AVCaptureSession中管理。
- AVCaptureOutput:輸出數據管理對象,用于接收各類輸出數據,通常使用對應的子類AVCaptureAudioDataOutput、AVCaptureStillImageOutput、AVCaptureVideoDataOutput、AVCaptureFileOutput,該對象將會被添加到AVCaptureSession中管理。
注意:前面幾個對象的輸出數據都是NSData類型,而AVCaptureFileOutput代表數據以文件形式輸出,類似的,AVCcaptureFileOutput也不會直接創建使用,通常會使用其子類:AVCaptureAudioFileOutput、AVCaptureMovieFileOutput。
當把一個輸入或者輸出添加到AVCaptureSession之后AVCaptureSession就會在所有相符的輸入、輸出設備之間建立連接(AVCaptionConnection):
- AVCaptureVideoPreviewLayer:相機拍攝預覽圖層,是CALayer的子類,使用該對象可以實時查看拍照或視頻錄制效果,創建該對象需要指定對應的AVCaptureSession對象。
使用AVFoundation拍照和錄制視頻的一般步驟如下:
- 創建AVCaptureSession對象。
- 使用AVCaptureDevice的靜態方法獲得需要使用的設備,例如拍照和錄像就需要獲得攝像頭設備,錄音就要獲得麥克風設備。
- 利用輸入設備AVCaptureDevice初始化AVCaptureDeviceInput對象。
- 初始化輸出數據管理對象,如果要拍照就初始化AVCaptureStillImageOutput對象;如果拍攝視頻就初始化AVCaptureMovieFileOutput對象。
- 將數據輸入對象AVCaptureDeviceInput、數據輸出對象AVCaptureOutput添加到媒體會話管理對象AVCaptureSession中。
- 創建視頻預覽圖層AVCaptureVideoPreviewLayer并指定媒體會話,添加圖層到顯示容器中,調用AVCaptureSession的startRuning方法開始捕獲。
- 將捕獲的音頻或視頻數據輸出到指定文件。
拍照
下面看一下如何使用AVFoundation實現一個拍照程序,在這個程序中將實現攝像頭預覽、切換前后攝像頭、閃光燈設置、對焦、拍照保存等功能。應用大致效果如下:
在程序中定義會話、輸入、輸出等相關對象。
@interface ViewController ()
@property (strong,nonatomic) AVCaptureSession *captureSession;//負責輸入和輸出設備之間的數據傳遞
@property (strong,nonatomic) AVCaptureDeviceInput *captureDeviceInput;//負責從AVCaptureDevice獲得輸入數據
@property (strong,nonatomic) AVCaptureStillImageOutput *captureStillImageOutput;//照片輸出流
@property (strong,nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相機拍攝預覽圖層
@property (weak, nonatomic) IBOutlet UIView *viewContainer;
@property (weak, nonatomic) IBOutlet UIButton *takeButton;//拍照按鈕
@property (weak, nonatomic) IBOutlet UIButton *flashAutoButton;//自動閃光燈按鈕
@property (weak, nonatomic) IBOutlet UIButton *flashOnButton;//打開閃光燈按鈕
@property (weak, nonatomic) IBOutlet UIButton *flashOffButton;//關閉閃光燈按鈕
@property (weak, nonatomic) IBOutlet UIImageView *focusCursor; //聚焦光標
@end
在控制器視圖將要展示時創建并初始化會話、攝像頭設備、輸入、輸出、預覽圖層,并且添加預覽圖層到視圖中,除此之外還做了一些初始化工作,例如添加手勢(點擊屏幕進行聚焦)、初始化界面等。
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//初始化會話
_captureSession=[[AVCaptureSession alloc]init];
if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//設置分辨率
_captureSession.sessionPreset=AVCaptureSessionPreset1280x720;
}
//獲得輸入設備
AVCaptureDevice *captureDevice=[self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];//取得后置攝像頭
if (!captureDevice) {
NSLog(@"取得后置攝像頭時出現問題.");
return;
}
NSError *error=nil;
//根據輸入設備初始化設備輸入對象,用于獲得輸入數據
_captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];
if (error) {
NSLog(@"取得設備輸入對象時出錯,錯誤原因:%@",error.localizedDescription);
return;
}
//初始化設備輸出對象,用于獲得輸出數據
_captureStillImageOutput=[[AVCaptureStillImageOutput alloc]init];
NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};
[_captureStillImageOutput setOutputSettings:outputSettings];//輸出設置
//將設備輸入添加到會話中
if ([_captureSession canAddInput:_captureDeviceInput]) {
[_captureSession addInput:_captureDeviceInput];
}
//將設備輸出添加到會話中
if ([_captureSession canAddOutput:_captureStillImageOutput]) {
[_captureSession addOutput:_captureStillImageOutput];
}
//創建視頻預覽層,用于實時展示攝像頭狀態
_captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession];
CALayer *layer=self.viewContainer.layer;
layer.masksToBounds=YES;
_captureVideoPreviewLayer.frame=layer.bounds;
_captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式
//將視頻預覽層添加到界面中
//[layer addSublayer:_captureVideoPreviewLayer];
[layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer];
[self addNotificationToCaptureDevice:captureDevice];
[self addGenstureRecognizer];
[self setFlashModeButtonStatus];
}
在控制器視圖展示和視圖離開界面時啟動、停止會話。
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self.captureSession startRunning];
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[self.captureSession stopRunning];
}
定義閃光燈開閉及自動模式功能,注意無論是設置閃光燈、白平衡還是其他輸入設備屬性,在設置之前必須先鎖定配置,修改完后解鎖。
/**
* 改變設備屬性的統一操作方法
*
* @param propertyChange 屬性改變操作
*/
-(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{
AVCaptureDevice *captureDevice= [self.captureDeviceInput device];
NSError *error;
//注意改變設備屬性前一定要首先調用lockForConfiguration:調用完之后使用unlockForConfiguration方法解鎖
if ([captureDevice lockForConfiguration:&error]) {
propertyChange(captureDevice);
[captureDevice unlockForConfiguration];
}else{
NSLog(@"設置設備屬性過程發生錯誤,錯誤信息:%@",error.localizedDescription);
}
}
/**
* 設置閃光燈模式
*
* @param flashMode 閃光燈模式
*/
-(void)setFlashMode:(AVCaptureFlashMode )flashMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFlashModeSupported:flashMode]) {
[captureDevice setFlashMode:flashMode];
}
}];
}
定義切換攝像頭功能,切換攝像頭的過程就是將原有輸入移除,在會話中添加新的輸入,但是注意動態修改會話需要首先開啟配置,配置成功后提交配置。
#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];
}
添加點擊手勢操作,點按預覽視圖時進行聚焦、白平衡設置。
/**
* 設置聚焦點
*
* @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];
}
定義拍照功能,拍照的過程就是獲取連接,從連接中獲得捕獲的輸出數據并做保存操作。
#pragma mark 拍照
- (IBAction)takeButtonClick:(UIButton *)sender {
//根據設備輸出獲得連接
AVCaptureConnection *captureConnection=[self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo];
//根據連接取得設備輸出的數據
[self.captureStillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer) {
NSData *imageData=[AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image=[UIImage imageWithData:imageData];
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
// ALAssetsLibrary *assetsLibrary=[[ALAssetsLibrary alloc]init];
// [assetsLibrary writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:nil];
}
}];
}
最后附上完整代碼:
//
// ViewController.m
// AVFoundationCamera
//
// Created by Kenshin Cui on 14/04/05.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>
typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice);
@interface ViewController ()
@property (strong,nonatomic) AVCaptureSession *captureSession;//負責輸入和輸出設備之間的數據傳遞
@property (strong,nonatomic) AVCaptureDeviceInput *captureDeviceInput;//負責從AVCaptureDevice獲得輸入數據
@property (strong,nonatomic) AVCaptureStillImageOutput *captureStillImageOutput;//照片輸出流
@property (strong,nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相機拍攝預覽圖層
@property (weak, nonatomic) IBOutlet UIView *viewContainer;
@property (weak, nonatomic) IBOutlet UIButton *takeButton;//拍照按鈕
@property (weak, nonatomic) IBOutlet UIButton *flashAutoButton;//自動閃光燈按鈕
@property (weak, nonatomic) IBOutlet UIButton *flashOnButton;//打開閃光燈按鈕
@property (weak, nonatomic) IBOutlet UIButton *flashOffButton;//關閉閃光燈按鈕
@property (weak, nonatomic) IBOutlet UIImageView *focusCursor; //聚焦光標
@end
@implementation ViewController
#pragma mark - 控制器視圖方法
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//初始化會話
_captureSession=[[AVCaptureSession alloc]init];
if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//設置分辨率
_captureSession.sessionPreset=AVCaptureSessionPreset1280x720;
}
//獲得輸入設備
AVCaptureDevice *captureDevice=[self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];//取得后置攝像頭
if (!captureDevice) {
NSLog(@"取得后置攝像頭時出現問題.");
return;
}
NSError *error=nil;
//根據輸入設備初始化設備輸入對象,用于獲得輸入數據
_captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];
if (error) {
NSLog(@"取得設備輸入對象時出錯,錯誤原因:%@",error.localizedDescription);
return;
}
//初始化設備輸出對象,用于獲得輸出數據
_captureStillImageOutput=[[AVCaptureStillImageOutput alloc]init];
NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};
[_captureStillImageOutput setOutputSettings:outputSettings];//輸出設置
//將設備輸入添加到會話中
if ([_captureSession canAddInput:_captureDeviceInput]) {
[_captureSession addInput:_captureDeviceInput];
}
//將設備輸出添加到會話中
if ([_captureSession canAddOutput:_captureStillImageOutput]) {
[_captureSession addOutput:_captureStillImageOutput];
}
//創建視頻預覽層,用于實時展示攝像頭狀態
_captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession];
CALayer *layer=self.viewContainer.layer;
layer.masksToBounds=YES;
_captureVideoPreviewLayer.frame=layer.bounds;
_captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式
//將視頻預覽層添加到界面中
//[layer addSublayer:_captureVideoPreviewLayer];
[layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer];
[self addNotificationToCaptureDevice:captureDevice];
[self addGenstureRecognizer];
[self setFlashModeButtonStatus];
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self.captureSession startRunning];
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
[self.captureSession stopRunning];
}
-(void)dealloc{
[self removeNotification];
}
#pragma mark - UI方法
#pragma mark 拍照
- (IBAction)takeButtonClick:(UIButton *)sender {
//根據設備輸出獲得連接
AVCaptureConnection *captureConnection=[self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo];
//根據連接取得設備輸出的數據
[self.captureStillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer) {
NSData *imageData=[AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image=[UIImage imageWithData:imageData];
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
// ALAssetsLibrary *assetsLibrary=[[ALAssetsLibrary alloc]init];
// [assetsLibrary writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:nil];
}
}];
}
#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];
}
#pragma mark - 通知
/**
* 給輸入設備添加通知
*/
-(void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice{
//注意添加區域改變捕獲通知必須首先設置設備允許捕獲
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
captureDevice.subjectAreaChangeMonitoringEnabled=YES;
}];
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
//捕獲區域發生改變
[notificationCenter addObserver:self selector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
}
-(void)removeNotificationFromCaptureDevice:(AVCaptureDevice *)captureDevice{
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
}
/**
* 移除所有通知
*/
-(void)removeNotification{
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self];
}
-(void)addNotificationToCaptureSession:(AVCaptureSession *)captureSession{
NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter];
//會話出錯
[notificationCenter addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:captureSession];
}
/**
* 設備連接成功
*
* @param notification 通知對象
*/
-(void)deviceConnected:(NSNotification *)notification{
NSLog(@"設備已連接...");
}
/**
* 設備連接斷開
*
* @param notification 通知對象
*/
-(void)deviceDisconnected:(NSNotification *)notification{
NSLog(@"設備已斷開.");
}
/**
* 捕獲區域改變
*
* @param notification 通知對象
*/
-(void)areaChange:(NSNotification *)notification{
NSLog(@"捕獲區域改變...");
}
/**
* 會話出錯
*
* @param notification 通知對象
*/
-(void)sessionRuntimeError:(NSNotification *)notification{
NSLog(@"會話發生錯誤.");
}
#pragma mark - 私有方法
/**
* 取得指定位置的攝像頭
*
* @param position 攝像頭位置
*
* @return 攝像頭設備
*/
-(AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position{
NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *camera in cameras) {
if ([camera position]==position) {
return camera;
}
}
return nil;
}
/**
* 改變設備屬性的統一操作方法
*
* @param propertyChange 屬性改變操作
*/
-(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{
AVCaptureDevice *captureDevice= [self.captureDeviceInput device];
NSError *error;
//注意改變設備屬性前一定要首先調用lockForConfiguration:調用完之后使用unlockForConfiguration方法解鎖
if ([captureDevice lockForConfiguration:&error]) {
propertyChange(captureDevice);
[captureDevice unlockForConfiguration];
}else{
NSLog(@"設置設備屬性過程發生錯誤,錯誤信息:%@",error.localizedDescription);
}
}
/**
* 設置閃光燈模式
*
* @param flashMode 閃光燈模式
*/
-(void)setFlashMode:(AVCaptureFlashMode )flashMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFlashModeSupported:flashMode]) {
[captureDevice setFlashMode:flashMode];
}
}];
}
/**
* 設置聚焦模式
*
* @param focusMode 聚焦模式
*/
-(void)setFocusMode:(AVCaptureFocusMode )focusMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFocusModeSupported:focusMode]) {
[captureDevice setFocusMode:focusMode];
}
}];
}
/**
* 設置曝光模式
*
* @param exposureMode 曝光模式
*/
-(void)setExposureMode:(AVCaptureExposureMode)exposureMode{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isExposureModeSupported:exposureMode]) {
[captureDevice setExposureMode:exposureMode];
}
}];
}
/**
* 設置聚焦點
*
* @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];
}
/**
* 設置閃光燈按鈕狀態
*/
-(void)setFlashModeButtonStatus{
AVCaptureDevice *captureDevice=[self.captureDeviceInput device];
AVCaptureFlashMode flashMode=captureDevice.flashMode;
if([captureDevice isFlashAvailable]){
self.flashAutoButton.hidden=NO;
self.flashOnButton.hidden=NO;
self.flashOffButton.hidden=NO;
self.flashAutoButton.enabled=YES;
self.flashOnButton.enabled=YES;
self.flashOffButton.enabled=YES;
switch (flashMode) {
case AVCaptureFlashModeAuto:
self.flashAutoButton.enabled=NO;
break;
case AVCaptureFlashModeOn:
self.flashOnButton.enabled=NO;
break;
case AVCaptureFlashModeOff:
self.flashOffButton.enabled=NO;
break;
default:
break;
}
}else{
self.flashAutoButton.hidden=YES;
self.flashOnButton.hidden=YES;
self.flashOffButton.hidden=YES;
}
}
/**
* 設置聚焦光標位置
*
* @param point 光標位置
*/
-(void)setFocusCursorWithPoint:(CGPoint)point{
self.focusCursor.center=point;
self.focusCursor.transform=CGAffineTransformMakeScale(1.5, 1.5);
self.focusCursor.alpha=1.0;
[UIView animateWithDuration:1.0 animations:^{
self.focusCursor.transform=CGAffineTransformIdentity;
} completion:^(BOOL finished) {
self.focusCursor.alpha=0;
}];
}
@end