先上一个媒体捕捉类关系图。
捕捉会话
AVCaptureSession
:AV Foundation捕捉栈的核心类是
AVCaptureSession
。会话用于连接输入和输出,管理从输入端得到的数据流,输出到输出设备。
捕捉设备
AVCaptureDevice
: 为摄像头、麦克风等物理设备定义接口。最常用的一个方法就是
+ (nullable AVCaptureDevice *)defaultDeviceWithMediaType:(AVMediaType)mediaType;
根据给定的媒体类型返回一个系统指定的默认设备。如参数设置AVMediaTypeVideo
会返回后置摄像头,默认为后置摄像头。
捕捉设备输入
在使用AVCaptureDevice
前,需要将其添加到session的输入。
AVCaptureSession *session = [[AVCaptureSession alloc] init];
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if ([session canAddInput:input]) {
[session addInput:input];
}
输出
AV Foundation的输出核心类为AVCaptureOutput
,这是一个抽象基类。我们使用子类来做输出,如AVCaptureMovieFileOutput
视频、AVCaptureAudioDataOutput
获取捕捉到的音频数据等。
_output = [[AVCaptureMovieFileOutput alloc] init];
if ([_session canAddOutput:_output]) {
[_session addOutput:_output];
}
预览
AVCaptureVideoPreviewLayer
类似AVPlayerLayer
,属于Core Animation中CALayer
子类。可以实时预览捕捉到的视频数据。
_layer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
_layer.frame = self.view.bounds;
[self.view.layer addSublayer:_layer];
//启动捕捉
[self.session startRunning];
切换摄像头
可以通过device.position
获取摄像头位置,返回一个AVCaptureDevicePosition
枚举值,表示是前置还是后置摄像头。
typedef NS_ENUM(NSInteger, AVCaptureDevicePosition) {
AVCaptureDevicePositionUnspecified = 0,
AVCaptureDevicePositionBack = 1, //后置摄像头
AVCaptureDevicePositionFront = 2,//前置摄像头
}
在切换摄像头之前需要动态配置session。
[self.session beginConfiguration];
//删除原来的输入
[self.session removeInput:_input];
//重新构建输入
_input = [self switchCameraWithPosition:_device.position];
//添加新的输入
if ([self.session canAddInput:_input]) {
[self.session addInput:_input];
}
//切换完成后提交
[self.session commitConfiguration];
//切换
- (AVCaptureDeviceInput *)switchCameraWithPosition:(AVCaptureDevicePosition)position {
if (![[self class] canSwitchCamera]) {
return self.input;
}
AVCaptureDevice *device = [[self class] cameraWithPosition:position];
NSError *error;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
_device = device;
return input;
}
//切换相机
+ (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position {
if (position == AVCaptureDevicePositionBack) {
position = AVCaptureDevicePositionFront;
} else if (position == AVCaptureDevicePositionFront) {
position = AVCaptureDevicePositionBack;
} else {
position = AVCaptureDevicePositionBack;
}
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
AVCaptureDevice *newDevice;
for (AVCaptureDevice *device in devices) {
if (device.position == position) {
newDevice = device;
break;
}
}
return newDevice;
}
+ (BOOL)canSwitchCamera {
return [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count] > 1;
}
获取AVCaptureDevice
在iOS 10 以前,我们可以通过[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]
获取设备集合,一般就是前置后置摄像头。
localizedName:Back Camera
modelID:com.apple.avfoundation.avcapturedevice.built-in_video:0
uniqueID:com.apple.avfoundation.avcapturedevice.built-in_video:0
localizedName:Front Camera
modelID:com.apple.avfoundation.avcapturedevice.built-in_video:1
uniqueID:com.apple.avfoundation.avcapturedevice.built-in_video:1
在iOS 10 之后这个方法被废弃,需要使用AVCaptureDeviceDiscoverySession
获取。
AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
NSArray *devices = discoverySession.devices;
deviceTypes
:AVCaptureDeviceType
类型的集合;
mediaType
:媒体类型;
AVCaptureDevicePosition
:相机位置
简单介绍一下deviceTypes
,如下
//内建麦克风
AVCaptureDeviceTypeBuiltInMicrophone
//广角相机 满足大部分需求
AVCaptureDeviceTypeBuiltInWideAngleCamera
/**
长焦相机,根据官方文档,这里的长焦相机与摄影上的长焦并不是一个概念,
仅仅是用来区分设备上广角和窄角相机的一个类型
只能通过AVCaptureDeviceDiscoverySession获取
*/
AVCaptureDeviceTypeBuiltInTelephotoCamera
/**
双摄像头,结合广角和长焦相机,可根据实际场景自动切换相机,
曝光时间、光圈等参数被锁定,但是在切换相机时这些参数仍可能发生变化
仅支持双摄,如人像模式等
*/
AVCaptureDeviceTypeBuiltInDualCamera
/**
仅iPhone X及以上设备支持,红外前置深感相机
*/
AVCaptureDeviceTypeBuiltInTrueDepthCamera
配置摄像头
配置摄像头的焦距、曝光、白平衡等参数。在修改参数前需要判断设备是否支持调整该参数,否则若设备不支持,会引起崩溃。以调整焦距为例:
if ([self.device isFocusModeSupported: AVCaptureFocusModeAutoFocus]) {
//锁定设备配置
NSError *error;
if ([self.device lockForConfiguration:&error]) {
//设置自动对焦
[self.device setFocusMode:AVCaptureFocusModeAutoFocus];
//解锁
[self.device unlockForConfiguration];
}
}
拍照
在iOS 10 之前静态图片的获取通过AVCaptureStillImageOutput
,在iOS 10 之后使用AVCapturePhotoOutput
获取。
//self.output:AVCapturePhotoOutput
[self.output capturePhotoWithSettings:[AVCapturePhotoSettings photoSettings] delegate:self];
//在代理方法中获取图片
//在iOS 11及以上有效
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error {
if (@available(iOS 11.0, *)) {
NSData *data = [photo fileDataRepresentation];
UIImage *image = [UIImage imageWithData:data];
UIImageWriteToSavedPhotosAlbum(image, self, nil, nil);
}
}
//在iOS 10 到iOS 11有效
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings error:(NSError *)error {
NSData *data = [AVCapturePhotoOutput JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer previewPhotoSampleBuffer:previewPhotoSampleBuffer];
UIImage *image = [UIImage imageWithData:data];
UIImageWriteToSavedPhotosAlbum(image, self, nil, nil);
}