iOS 自定义相机, UIImagePickerController && AVCaptureSession (附微信小视频模仿demo)
序 今天介绍自定义相机的两种方式,一种是UIImagePickerController
,一种是AVCaptureSession
.
UIImagePickerController UIImagePickerController
非常方便简单,是苹果自己封装好了的一套API,效果如下:
AVCaptureSession 但是上面的 API只能进行简单的相机视图修改,有时无法满足我们的需求.例如我们需要更加复杂的OverlayerView
(自定义相机视图),这时候我们就要自定义一个相机了.AVCaptureSession
能帮助我们.
code 心急的人,完整Demo在最下边儿.其他朋友可依次看下去
UIImagePickerController 属性 首先看到,需要遵守的协议有2个,分别是UINavigationControllerDelegate
和UIImagePickerControllerDelegate
1
@property (nullable ,nonatomic ,weak ) id <UINavigationControllerDelegate , UIImagePickerControllerDelegate > delegate;
还有一些基本设置,如输入源/媒体类型,还有allowsEditing
为 YES 即可允许用户拍照后立即进行编辑,并且出现编辑框1
2
3
4
5
@property (nonatomic) UIImagePickerControllerSourceType sourceType;
@property (nonatomic,copy) NSArray<NSString *> *mediaTypes;
@property (nonatomic) BOOL allowsEditing
@property (nonatomic) BOOL allowsImageEditing
这2个属性和视频相关1
2
@property (nonatomic) NSTimeInterval videoMaximumDuration
@property (nonatomic) UIImagePickerControllerQualityType videoQuality
这2个和自定义相机视图有关,注意,下面的只有当输入源为UIImagePickerControllerSourceTypeCamera
才可用,否则崩溃1
2
3
4
5
6
7
@property (nonatomic) BOOL showsCameraControls
@property (nullable, nonatomic,strong) __kindof UIView *cameraOverlayView
@property (nonatomic) CGAffineTransform cameraViewTransform
@property (nonatomic) UIImagePickerControllerCameraCaptureMode cameraCaptureMode
@property (nonatomic) UIImagePickerControllerCameraDevice cameraDevice
@property (nonatomic) UIImagePickerControllerCameraFlashMode cameraFlashMode
方法 第一个是判断输入源的,一般用来判断是否支持拍照
1
2
3
4
5
6
7
typedef NS_ENUM (NSInteger , UIImagePickerControllerSourceType ) {
UIImagePickerControllerSourceTypePhotoLibrary ,
UIImagePickerControllerSourceTypeCamera ,
UIImagePickerControllerSourceTypeSavedPhotosAlbum
};
+ (BOOL )isSourceTypeAvailable:(UIImagePickerControllerSourceType )sourceType;
这个是判断是否支持前/后置摄像头1
2
3
4
5
6
typedef NS_ENUM (NSInteger , UIImagePickerControllerCameraDevice ) {
UIImagePickerControllerCameraDeviceRear ,
UIImagePickerControllerCameraDeviceFront
};
+ (BOOL )isCameraDeviceAvailable:(UIImagePickerControllerCameraDevice )cameraDevice
拍照/摄像的方法1
2
3
- (void)takePicture; // 手动调取拍照的方法,也可当做`-imagePickerController:didFinishPickingMediaWithInfo:` 代理的回调,无法捕捉动态图像
- (BOOL)startVideoCapture; // 开始摄像
- (void)stopVideoCapture; // 停止摄像
代理相关 注意,UIImagePickerController
不能直接取消,必须在收到以下代理方法后才可以 dismiss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@protocol UIImagePickerControllerDelegate <NSObject >
@optional
- (void )imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(nullable NSDictionary <NSString *,id > *)editingInfo;
- (void )imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary <NSString *,id > *)info;
- (void )imagePickerControllerDidCancel:(UIImagePickerController *)picker;
@end
保存照片/视频到手机,三个比较重要的方法,记得导入AssetsLibrary.framework
,并且在需要调用存储方法的的地方导入<AssetsLibrary/AssetsLibrary.h>
1
2
3
4
5
6
7
8
9
UIKIT_EXTERN void UIImageWriteToSavedPhotosAlbum (UIImage *image, __nullable id completionTarget, __nullable SEL completionSelector, void * __nullable contextInfo);
UIKIT_EXTERN BOOL UIVideoAtPathIsCompatibleWithSavedPhotosAlbum (NSString *videoPath) NS_AVAILABLE_IOS (3 _1);
UIKIT_EXTERN void UISaveVideoAtPathToSavedPhotosAlbum (NSString *videoPath, __nullable id completionTarget, __nullable SEL completionSelector, void * __nullable contextInfo) NS_AVAILABLE_IOS (3 _1);
用法如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (void )savePic
{
UIImageWriteToSavedPhotosAlbum (imgPath, self , @selector (image:didFinishSavingWithError:contextInfo:), nil );
}
- (void )image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{
if (error) {
NSLog (@"保存照片过程中发生错误,错误信息:%@" ,error.localizedDescription);
}else {
NSLog (@"照片保存成功." );
}
}
- (void )saveVideo
{
UISaveVideoAtPathToSavedPhotosAlbum (videoPath,self , @selector (video:didFinishSavingWithError:contextInfo:), nil );
}
- (void )video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
if (error) {
NSLog (@"保存视频过程中发生错误,错误信息:%@" ,error.localizedDescription);
}else {
NSLog (@"视频保存成功." );
}
}
另外一个是ALAssetsLibrary
,不过在 iOS9.0 已经全部被废弃了,改成了PHPhotoLibrary
(这是 Photos
框架的一个组成部分,完整的可以点这个链接 ,关于这个框架的用法,改天再写一个吧)
9.0及以前的写法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[[[ALAssetsLibrary alloc]init] writeImageToSavedPhotosAlbum:[img CGImage ] orientation:(ALAssetOrientation)img.imageOrientation completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
NSLog (@"Save image fail:%@" ,error);
}else {
NSLog (@"Save image succeed." );
}
}];
[[[ALAssetsLibrary alloc]init] writeVideoAtPathToSavedPhotosAlbum:[NSURL URLWithString:videoPath] completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
NSLog (@"Save video fail:%@" ,error);
}else {
NSLog (@"Save video succeed." );
}
}];
AVCaptureSession 先放上一个微信小视频的模仿 Demo, 因为只能真机,所以比较嘈杂~大家别介意
流程 AVCaptureSession
通过把设备的麦克风/摄像头(AVCaptureDevice
)实例化成数据流输入对象(AVCaptureDeviceInput
)后,再通过建立连接(AVCaptionConnection
)将录制数据通过数据流输出对象(AVCaptureOutput
)导出,而录制的时候咱们可以同步预览当前的录制界面(AVCaptureVideoPreviewLayer
).
1
2
3
4
5
6
`AVCaptureSession`是一个会话对象,是设备音频/视频整个录制期间的管理者.
`AVCaptureDevice`其实是咱们的物理设备映射到程序中的一个对象.咱们可以通过其来操作:闪光灯,手电筒,聚焦模式等
`AVCaptureDeviceInput`是录制期间输入流数据的管理对象.
`AVCaptionConnection`是将输入流/输出流连接起来的连接对象,视频/音频稳定,预览与录制方向一致都在这里设置,还有audioChannels声道
`AVCaptureOutput`是输出流数据的管理对象,通过头文件可以看到有很多子类,而我们通常也使用其子类
`AVCaptureVideoPreviewLayer`是一个 `CALyer` ,可以让我们预览拍摄过程中的图像
详解各部分 此处,咱们就模仿一个微信小视频的 demo, 然后把各个主要步骤写在下面.
授权 首先获取授权AVAuthorizationStatus
,我们需要获取哪个设备的使用权限,就进行请求,需要注意的是,如果用户未进行授权授权选择,咱们还要重复请求一次.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- (void )getAuthorization
{
switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo ])
{
case AVAuthorizationStatusAuthorized :
{
break ;
}
case AVAuthorizationStatusNotDetermined :
{
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted){
return ;
} else {
return ;
}
}];
break ;
}
default :
{
break ;
}
}
}
相关枚举
1
2
3
4
5
6
7
8
9
/*
AVAuthorizationStatusNotDetermined = 0 ,
AVAuthorizationStatusRestricted,
AVAuthorizationStatusDenied,
AVAuthorizationStatusAuthorized,
*/
根据流程创建对象 AVCaptureSession 先创建本次小视频的会话对象_captureSession
,设置视频分辨率.注意,这个地方设置的模式/分辨率大小将影响你后面拍摄照片/视频的大小
1
2
3
4
5
6
7
8
- (void )addSession
{
_captureSession = [[AVCaptureSession alloc] init];
if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset640x480 ]) {
[_captureSession setSessionPreset:AVCaptureSessionPreset640x480 ];
}
}
相关枚举
1
2
3
4
5
6
7
8
9
10
11
12
/* 通常支持如下格式
(
AVAssetExportPresetLowQuality,
AVAssetExportPreset960x540 ,
AVAssetExportPreset640x480 ,
AVAssetExportPresetMediumQuality,
AVAssetExportPreset1920x1080 ,
AVAssetExportPreset1280x720 ,
AVAssetExportPresetHighestQuality,
AVAssetExportPresetAppleM4A
)
*/
AVCaptureDevice 然后咱们要把设备接入进来了,依次是_videoDevice
和_audioDevice
,还有注意,在给会话信息添加设备对象的时候,需要调用_captureSession
的一个方法组beginConfiguration
和commitConfiguration
,最后,咱们把录制过程中的预览图层PreviewLayer
也添加进来,设置完毕后,开启会话startRunning
–>注意,不等于开始录制,在不再需要使用会话相关时,还需要stopRunning
1
2
3
4
5
6
7
8
[_captureSession beginConfiguration]
[self addVideo]
[self addAudio]
[self addPreviewLayer]
[_captureSession commitConfiguration]
[_captureSession startRunning]
video 相关 1
2
3
4
5
6
7
8
9
- (void )addVideo
{
_videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack ];
[self addVideoInput];
[self addMovieOutput];
}
相关枚举
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* MediaType
AVF_EXPORT NSString *const AVMediaTypeVideo NS_AVAILABLE (10 _7, 4 _0); //视频
AVF_EXPORT NSString *const AVMediaTypeAudio NS_AVAILABLE (10 _7, 4 _0); //音频
AVF_EXPORT NSString *const AVMediaTypeText NS_AVAILABLE (10 _7, 4 _0);
AVF_EXPORT NSString *const AVMediaTypeClosedCaption NS_AVAILABLE (10 _7, 4 _0);
AVF_EXPORT NSString *const AVMediaTypeSubtitle NS_AVAILABLE (10 _7, 4 _0);
AVF_EXPORT NSString *const AVMediaTypeTimecode NS_AVAILABLE (10 _7, 4 _0);
AVF_EXPORT NSString *const AVMediaTypeMetadata NS_AVAILABLE (10 _8, 6 _0);
AVF_EXPORT NSString *const AVMediaTypeMuxed NS_AVAILABLE (10 _7, 4 _0);
*/
/* AVCaptureDevicePosition
typedef NS_ENUM (NSInteger , AVCaptureDevicePosition ) {
AVCaptureDevicePositionUnspecified = 0,
AVCaptureDevicePositionBack = 1, //后置摄像头
AVCaptureDevicePositionFront = 2 //前置摄像头
} NS_AVAILABLE (10_7, 4_0) __TVOS_PROHIBITED;
*/
下面是获取摄像头的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#pragma mark 获取摄像头-->前/后
- (AVCaptureDevice *)deviceWithMediaType:(NSString *)mediaType preferringPosition:(AVCaptureDevicePosition )position
{
NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType];
AVCaptureDevice *captureDevice = devices.firstObject;
for ( AVCaptureDevice *device in devices ) {
if ( device.position == position ) {
captureDevice = device;
break ;
}
}
return captureDevice;
}
添加视频输入对象AVCaptureDeviceInput
,根据输入设备初始化输入对象,用户获取输入数据,将视频输入对象添加到会话 (AVCaptureSession) 中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void )addVideoInput
{
NSError *videoError;
_videoInput = [[AVCaptureDeviceInput alloc] initWithDevice: _videoDevice error: &videoError];
if (videoError) {
NSLog(@"---- 取得摄像头设备时出错 ------ %@" ,videoError);
return ;
}
if ([_captureSession canAddInput: _videoInput]) {
[_captureSession addInput: _videoInput];
}
}
接下来是视频输出对象AVCaptureMovieFileOutput
及连接管理对象AVCaptureConnection
,还有视频稳定设置preferredVideoStabilizationMode
,视频旋转方向setVideoOrientation
等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)addMovieOutput
{
_movieOutput = [[AVCaptureMovieFileOutput alloc] init]
if ([_captureSession canAddOutput:_movieOutput]) {
[_captureSession addOutput:_movieOutput];
AVCaptureConnection *captureConnection = [_movieOutput connectionWithMediaType:AVMediaTypeVideo]
if ([captureConnection isVideoStabilizationSupported]) {
captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto
}
captureConnection.videoScaleAndCropFactor = captureConnection.videoMaxScaleAndCropFactor
}
}
相关枚举1
2
3
4
5
6
7
8
9
//设置视频旋转方向
/*
typedef NS_ENUM (NSInteger , AVCaptureVideoOrientation ) {
AVCaptureVideoOrientationPortrait = 1,
AVCaptureVideoOrientationPortraitUpsideDown = 2,
AVCaptureVideoOrientationLandscapeRight = 3,
AVCaptureVideoOrientationLandscapeLeft = 4,
} NS_AVAILABLE (10_7, 4_0) __TVOS_PROHIBITED;
*/
audio 相关 咱们再接着添加音频相关的,包括音频输入设备AVCaptureDevice
,音频输入对象AVCaptureDeviceInput
,并且将音频输入对象添加到会话
1
2
3
4
5
6
7
8
9
10
11
12
13
- (void )addAudio
{
NSError *audioError;
_audioDevice = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeAudio];
_audioInput = [[AVCaptureDeviceInput alloc] initWithDevice: _audioDevice error: &audioError];
if (audioError) {
NSLog(@"取得录音设备时出错 ------ %@" ,audioError);
return ;
}
if ([_captureSession canAddInput: _audioInput]) {
[_captureSession addInput: _audioInput];
}
}
AVCaptureVideoPreviewLayer 通过会话AVCaptureSession
创建预览层AVCaptureVideoPreviewLayer
,设置填充模式videoGravity
,预览图层方向videoOrientation
,并且设置 layer
想要显示的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void )addPreviewLayer
{
_captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
_captureVideoPreviewLayer.frame = self .view.layer.bounds;
_captureVideoPreviewLayer.connection.videoOrientation = [_movieOutput connectionWithMediaType:AVMediaTypeVideo ].videoOrientation;
_captureVideoPreviewLayer.position = CGPointMake (self .view.width*0.5 ,self .videoView.height*0.5 );
CALayer *layer = self .videoView.layer;
layer.masksToBounds = true ;
[self .view layoutIfNeeded];
[layer addSublayer:_captureVideoPreviewLayer];
}
相关枚举
1
2
3
/* 填充模式
Options are AVLayerVideoGravityResize, AVLayerVideoGravityResizeAspect and AVLayerVideoGravityResizeAspectFill. AVLayerVideoGravityResizeAspect is default .
*/
最后,开始录制视频,结束录制视频,重新录制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)startRecord
{
[_movieOutput startRecordingToOutputFileURL:[self outPutFileURL] recordingDelegate :self ];
}
- (NSURL *)outPutFileURL
{
return [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), @"outPut.mov"] ];
}
- (void)stopRecord
{
[_movieOutput stopRecording] ;
}
录制相关delegate 包括开始录制,录制结束等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void )captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections
{
NSLog (@"---- 开始录制 ----" );
}
- (void )captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
NSLog (@"---- 录制结束 ----%@ " ,captureOutput.outputFileURL);
if (self .canSave) {
[self pushToPlay:captureOutput.outputFileURL];
self .canSave = NO ;
}
}
压缩/保存视频 咱们需要把录制完毕的视频保存下来.而通常录制完毕的视频是很大的,咱们需要压缩一下再保存.
可以通过AVAssetExportSession
来进行压缩,并且可以优化网络shouldOptimizeForNetworkUse
,设置转后的格式outputFileType
,并且开启异步压缩exportAsynchronouslyWithCompletionHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#pragma mark 保存压缩
- (NSURL *)compressedURL
{
return [NSURL fileURLWithPath:[[NSSearchPathForDirectoriesInDomains (NSDocumentDirectory , NSUserDomainMask , true ) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"compressed.mp4" ]]];
}
- (CGFloat )fileSize:(NSURL *)path
{
return [[NSData dataWithContentsOfURL:path] length]/1024.00 /1024.00 ;
}
- (IBAction )compressVideo:(id )sender
{
NSLog (@"开始压缩,压缩前大小 %f MB" ,[self fileSize:self .videoUrl]);
AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:self .videoUrl options:nil ];
NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality ]) {
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPreset640x480 ];
exportSession.outputURL = [self compressedURL];
exportSession.shouldOptimizeForNetworkUse = true ;
exportSession.outputFileType = AVFileTypeMPEG4 ;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if ([exportSession status] == AVAssetExportSessionStatusCompleted ) {
NSLog (@"压缩完毕,压缩后大小 %f MB" ,[self fileSize:[self compressedURL]]);
[self saveVideo:[self compressedURL]];
}else {
NSLog (@"当前压缩进度:%f" ,exportSession.progress);
}
}];
}
}
- (void )saveVideo:(NSURL *)outputFileURL
{
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeVideoAtPathToSavedPhotosAlbum:outputFileURL
completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
NSLog (@"保存视频失败:%@" ,error);
} else {
NSLog (@"保存视频到相册成功" );
}
}];
}
播放录制完的视频以及重复播放 播放录制的视频,以及重复播放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void )create
{
_playItem = [AVPlayerItem playerItemWithURL:self .videoUrl];
_player = [AVPlayer playerWithPlayerItem:_playItem];
_playerLayer =[AVPlayerLayer playerLayerWithPlayer:_player];
_playerLayer.frame = CGRectMake (200 , 200 , 100 , 100 );
_playerLayer.videoGravity=AVLayerVideoGravityResizeAspectFill ;
[self .view.layer addSublayer:_playerLayer];
[_player play];
}
-(void )playbackFinished:(NSNotification *)notification
{
[_player seekToTime:CMTimeMake (0 , 1 )];
[_player play];
}
交互相关 接下来,是一些和交互相关的,比如切换摄像头,开关闪光灯,还有白平衡啥的.
注意,改变设备属性前一定要首先调用lockForConfiguration
方法加锁,调用完之后使用unlockForConfiguration
方法解锁.
意义是—进行设备属性修改期间,先锁定设备,防止多处同时修改设备.因为可能有多处不同的修改,咱们将其封装起来最好
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-(void )changeDevicePropertySafety:(void (^)(AVCaptureDevice *captureDevice))propertyChange{
AVCaptureDevice *captureDevice= [_videoInput device];
NSError *error;
BOOL lockAcquired = [captureDevice lockForConfiguration:&error];
if (!lockAcquired) {
NSLog (@"锁定设备过程error,错误信息:%@" ,error.localizedDescription);
}else {
[_captureSession beginConfiguration];
propertyChange(captureDevice);
[captureDevice unlockForConfiguration];
[_captureSession commitConfiguration];
}
}
开/关闪光灯 闪光模式开启后,并无明显感觉,所以还需要开启手电筒,并且开启前先判断是否自持,否则崩溃
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (IBAction )changeFlashlight:(UIButton *)sender {
BOOL con1 = [_videoDevice hasTorch];
BOOL con2 = [_videoDevice hasFlash];
if (con1 && con2)
{
[self changeDevicePropertySafety:^(AVCaptureDevice *captureDevice) {
if (_videoDevice.flashMode == AVCaptureFlashModeOn )
{
[_videoDevice setFlashMode:AVCaptureFlashModeOff ];
[_videoDevice setTorchMode:AVCaptureTorchModeOff ];
}else if (_videoDevice.flashMode == AVCaptureFlashModeOff )
{
[_videoDevice setFlashMode:AVCaptureFlashModeOn ];
[_videoDevice setTorchMode:AVCaptureTorchModeOn ];
}
}];
sender.selected=!sender.isSelected;
}else {
NSLog (@"不能切换闪光模式" );
}
}
切换摄像头 根据现在正在使用的摄像头来判断需要切换的摄像头
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- (IBAction )changeCamera{
switch (_videoDevice.position) {
case AVCaptureDevicePositionBack :
_videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionFront ];
break ;
case AVCaptureDevicePositionFront :
_videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack ];
break ;
default :
return ;
break ;
}
[self changeDevicePropertySafety:^(AVCaptureDevice *captureDevice) {
NSError *error;
AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:_videoDevice error:&error];
if (newVideoInput != nil ) {
[_captureSession removeInput:_videoInput];
if ([_captureSession canAddInput:newVideoInput]) {
[_captureSession addInput:newVideoInput];
_videoInput = newVideoInput;
}else {
[_captureSession addInput:_videoInput];
}
} else if (error) {
NSLog (@"切换前/后摄像头失败, error = %@" , error);
}
}];
}
聚焦模式,曝光模式,拉近/远镜头(焦距) 同时存在单击和双击的手势,咱们如下设置,requireGestureRecognizerToFail
的作用就是每次只生效一个手势
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-(void )addGenstureRecognizer{
UITapGestureRecognizer *singleTapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector (singleTap:)];
singleTapGesture.numberOfTapsRequired = 1 ;
singleTapGesture.delaysTouchesBegan = YES ;
UITapGestureRecognizer *doubleTapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector (doubleTap:)];
doubleTapGesture.numberOfTapsRequired = 2 ;
doubleTapGesture.delaysTouchesBegan = YES ;
[singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];
[self .videoView addGestureRecognizer:singleTapGesture];
[self .videoView addGestureRecognizer:doubleTapGesture];
}
单击修改聚焦模式setFocusMode
及聚焦点setFocusPointOfInterest
,还有曝光模式setExposureMode
及曝光点setExposurePointOfInterest
注意,摄像头的点范围是0~1,咱们需要把点转化一下,使用captureDevicePointOfInterestForPoint
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
-(void )singleTap:(UITapGestureRecognizer *)tapGesture{
CGPoint point= [tapGesture locationInView:self .videoView];
CGPoint cameraPoint= [_captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
[self setFocusCursorAnimationWithPoint:point];
[self changeDevicePropertySafety:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus ]) {
[captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus ];
}else {
NSLog (@"聚焦模式修改失败" );
}
if ([captureDevice isFocusPointOfInterestSupported]) {
[captureDevice setFocusPointOfInterest:cameraPoint];
}
if ([captureDevice isExposureModeSupported:AVCaptureExposureModeAutoExpose ]) {
[captureDevice setExposureMode:AVCaptureExposureModeAutoExpose ];
}else {
NSLog (@"曝光模式修改失败" );
}
if ([captureDevice isExposurePointOfInterestSupported]) {
[captureDevice setExposurePointOfInterest:cameraPoint];
}
}];
}
下面是双击设置焦距videoZoomFactor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-(void )doubleTap: (UITapGestureRecognizer *)tapGesture{
NSLog(@"双击" );
[self changeDevicePropertySafety: ^(AVCaptureDevice *captureDevice) {
if (captureDevice.videoZoomFactor == 1.0 ) {
CGFloat current = 1.5 ;
if (current < captureDevice.activeFormat.videoMaxZoomFactor) {
[captureDevice rampToVideoZoomFactor: current withRate: 10 ];
}
}else {
[captureDevice rampToVideoZoomFactor: 1.0 withRate: 10 ];
}
}];
}
相关枚举
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@constant AVCaptureFocusModeLocked 锁定在当前焦距
Indicates that the focus should be locked at the lens' current position.
@constant AVCaptureFocusModeAutoFocus 自动对焦一次,然后切换到焦距锁定
Indicates that the device should autofocus once and then change the focus mode to AVCaptureFocusModeLocked.
@constant AVCaptureFocusModeContinuousAutoFocus 当需要时.自动调整焦距
Indicates that the device should automatically focus when needed.
*/
@constant AVCaptureExposureModeLocked 曝光锁定在当前值
Indicates that the exposure should be locked at its current value.
@constant AVCaptureExposureModeAutoExpose 曝光自动调整一次然后锁定
Indicates that the device should automatically adjust exposure once and then change the exposure mode to AVCaptureExposureModeLocked.
@constant AVCaptureExposureModeContinuousAutoExposure 曝光自动调整
Indicates that the device should automatically adjust exposure when needed.
@constant AVCaptureExposureModeCustom 曝光只根据设定的值来
Indicates that the device should only adjust exposure according to user provided ISO, exposureDuration values.
*/
Demo 终于说完了,感觉越到后面越无力,还是放上一个 demo 吧,里面包含了上面介绍的两种方式.
Demo 下载地址
原创文章,转载请注明地址: https://kevinmky.github.io