会话的参数配置:
1)、设置分辨率
使用会话上的 preset 来指定图像的质量和分辨率。预设是一个常数,确定了一部分可能的配置中的一个;在某些情况下,设计的配置是设备特有的:
Symbol | Resolution | Comments |
---|---|---|
AVCaptureSessionPresetPhoto | Photo | Full photo resolution.This is not supported for video output. |
AVCaptureSessionPresetHigh | High | Highest recording quality.This varies per device. |
AVCaptureSessionPresetMedium | Medium | Suitable for Wi-Fi sharing.The actual values may change. |
AVCaptureSessionPresetLow | Low | Suitable for 3G sharing.The actual values may change. |
AVCaptureSessionPreset352x288 | 352x288 | CIF |
AVCaptureSessionPreset640x480 | 640x480 | VGA |
AVCaptureSessionPreset1280x720 | 1280x720 | 720p HD. |
AVCaptureSessionPreset1920x1080 | 1920x1080 | 1080P |
AVCaptureSessionPreset3840x2160 | 3840x2160 | UHD or 4K |
AVCaptureSessionPresetiFrame1280x720 | 1280x720 | Specifies capture settings to achieve 1280x720 quality iFrame H.264 video at about 40 Mbits/sec with AAC audio. |
AVCaptureSessionPresetiFrame960x540 | 960x540 | Specifies capture settings to achieve 960x540 quality iFrame H.264 video at about 30 Mbits/sec with AAC audio. |
AVCaptureSessionPresetInputPriority | *** | Specifies that the capture session does not control audio and video output settings. |
设置图像的预设分辨率
if ([self.session canSetSessionPreset:self.captureSessionPreset]) {
[self.session setSessionPreset:self.captureSessionPreset];
}
如果需要比预设情况,更加精细的水平调整会话参数,或者想给一个正在运行的会话做些改变,用 beginConfiguration 和 commitConfiguration 方法。beginConfiguration 和 commitConfiguration 方法确保设备作为一个群体在变化,降低状态的清晰度或者不协调性。调用 beginConfiguration 之后,可以添加或者移除输出,改变 sessionPreset
属性,或者单独配置捕获输入或输出属性。在你调用 commitConfiguration 之前实际上是没有变化的,调用的时候它们才被应用到一起。
[session beginConfiguration];
// Remove an existing capture device.
// Add a new capture device.
// Reset the preset.
[session commitConfiguration];
2)、切换输入设备
一个 AVCaptureDevice 对象抽象出物理捕获设备,提供了输入数据(比如音频或者视频)给 AVCaptureSession
对象。例如每个输入设备都有一个对象,两个视频输入,一个用于前置摄像头,一个用于后置摄像头,一个用于麦克风的音频输入。
使用 AVCaptureDevice
类方法 devices 和 devicesWithMediaType: 可以找出哪一个捕获设备当前是可用的。而且如果有必要,可以找出 iPhone
,iPad
或者 iPod
提供了什么功能(详情看:Device Capture Settings)。虽然可用设备的列表可能会改变。当前输入设备可能会变得不可用(如果他们被另一个应用程序使用),新的输入设备可能成为可用的,(如果他们被另一个应用程序让出)。应该注册,当可用设备列表改变时接收 AVCaptureDeviceWasConnectedNotification 和 AVCaptureDeviceWasDisconnectedNotification 通知。
测试是否支持特定设备的方法,一般为这两种:
1)、hasMediaType:
2)、supportsAVCaptureSessionPreset:
NSArray *devices = [AVCaptureDevice devices];
for (AVCaptureDevice *device in devices) {
NSLog(@"Device name: %@", [device localizedName]);
if ([device hasMediaType:AVMediaTypeVideo]) {
if ([device position] == AVCaptureDevicePositionBack) {
NSLog(@"Device position : back");
}
else {
NSLog(@"Device position : front");
}
}
}
不同设备有不同的功能,一些可能支持不同的聚焦或者闪光模式;一些可能会支持聚焦在一个兴趣点。
// 如何找到有一个 torch 模式的视频输入设备,并且支持一个捕捉会话预设
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
NSMutableArray *torchDevices = [[NSMutableArray alloc] init];
for (AVCaptureDevice *device in devices) {
[if ([device hasTorch] &&
[device supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480]) {
[torchDevices addObject:device];
}
}
Switching Between Devices - 切换装置
有时,你可能想允许用户在输入设备之间进行切换,比如使用前置摄像头到后置摄像头的切换。为了避免暂停或者卡顿,可以在运行时配置一个会话,但是你应该使用 beginConfiguration 和 commitConfiguration 支持你的配置改变:
AVCaptureSession *session = [[AVCaptureSession alloc] init];
[session beginConfiguration];
[session removeInput:frontFacingCameraDeviceInput];
[session addInput:backFacingCameraDeviceInput];
[session commitConfiguration];
当最外面的 commitConfiguration 被调用,所有的改变都是一起做的。这保证了平稳过渡。
AVCaptureDevicePositionBack
AVCaptureDevicePositionFront
- (void)changeCaptureDevicePosition:(AVCaptureDevicePosition)captureDevicePosition
newVideoDeviceInput:(AVCaptureDeviceInput *)newVideoDeviceInput
{
[self.session stopRunning];
[self.session beginConfiguration];
// Remove the existing device input first, since using the front and back camera simultaneously is not supported
[self.session removeInput:self.videoDeviceInput];
if ( [self.session canAddInput:newVideoDeviceInput] ) {
[self.session addInput:newVideoDeviceInput];
self.videoDeviceInput = newVideoDeviceInput;
self.videoDevice = newVideoDeviceInput.device;
} else {
if (self.videoDeviceInput) {
[self.session addInput:self.videoDeviceInput];
}
}
AVCaptureSessionPreset preset = self.captureSessionPreset;
if (![self.captureSessionPreset isEqualToString:preset]) {
if ([[self session] canSetSessionPreset:preset]) {
[[self session] setSessionPreset:preset];
self.captureSessionPreset = preset;
}
}
if (self.captureSupportOption & XYCaptureSupportOptionVideo) {
self.videoConnection = [self.videoDataOutPut connectionWithMediaType:AVMediaTypeVideo];
if ([self.videoConnection isVideoStabilizationSupported]) {
self.videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeStandard;
}
[self.videoConnection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
if(captureDevicePosition == AVCaptureDevicePositionFront){//mirror the front camera
[self.videoConnection setVideoMirrored:YES];
}else{
[self.videoConnection setVideoMirrored:NO];
}
}
[[self session] commitConfiguration];
[self.session startRunning];
}
Focus Modes - 聚焦模式
有3个聚焦模式:
-
AVCaptureFocusModeLocked :焦点的位置是固定的。
这是很有用的,当你想让用户组成一个场景,然后锁定焦点。 -
AVCaptureFocusModeAutoFocus :照相机做一次扫描聚焦,然后将焦点锁定。
这适合于,你想要选择一个特定的项目,即使它不是现场的中心,但可以专注于该项目的焦点。 - AVCaptureFocusModeContinuousAutoFocus 相机需要不断的自动对焦。
使用 isFocusModeSupported: 方法来决定设备是否支持给定的聚焦模式,然后使用 focusMode 属性设置模式。
此外,设备可能支持一个兴趣焦点。使用 focusPointOfInterestSupported 进行支持测试。如果支持,使用 focusPointOfInterest 设置焦点。传一个 CGPoing
,横向模式下(就是 home
键在右边)图片的左上角是 {0, 0}
,右下角是 {1, 1}
, – 即使设备是纵向模式也适用。
你可以使用 adjustingFocus 属性来确定设备是否正在聚焦。当设备开始、停止聚焦时可以使用 key-value observing
观察,接收通知。
// 如果改变聚焦模式设置,可以将其返回到默认配置
if ([currentDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
CGPoint autofocusPoint = CGPointMake(0.5f, 0.5f);
[currentDevice setFocusPointOfInterest:autofocusPoint];
[currentDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
}
if (context == &CameraAdjustingFocusContext) {
NSLog(@"observeValueForKeyPath CameraAdjustingFocusContext");
AVCaptureDevice *device = (AVCaptureDevice *)object;
if (!device.isAdjustingFocus ) {
// 聚焦完成了
[object removeObserver:self forKeyPath:@"adjustingFocus" context:&CameraAdjustingFocusContext];
if (self.isAutoExposureAndFocusLock && [device isFocusModeSupported:AVCaptureFocusModeLocked]) {
dispatch_async(self.sessionQueue, ^{
BOOL adjustingFocus = [[change objectForKey:NSKeyValueChangeNewKey] isEqualToNumber:[NSNumber numberWithInt:1]];
if (adjustingFocus == NO) {
NSError *error;
if ([device lockForConfiguration:&error]) {
[device setFocusMode:AVCaptureFocusModeLocked];
[device setSubjectAreaChangeMonitoringEnabled:NO];
[device unlockForConfiguration];
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf.lockSettingBlock) weakSelf.lockSettingBlock(YES, nil);
});
}
}
});
}else {
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf.settingBlock) {
weakSelf.settingBlock(XYCameraSettingTypeAdjustFocus, YES, device.focusPointOfInterest, nil);
}
});
}
}
}
Exposure Modes - 曝光模式
有两种曝光模式:
- AVCaptureExposureModeContinuousAutoExposure :设备根据需要自动调整曝光等级。
- AVCaptureExposureModeLocked :曝光等级固定在当前等级。
使用 isExposureModeSupported: 方法来确定设备是否支持给定的曝光模式,然后使用 exposureMode 属性设置模式。
此外,一个设备支持一个曝光点。使用 exposurePointOfInterestSupported 测试支持。如果支持,使用 exposurePointOfInterest 设置曝光点。传一个 CGPoing
,横向模式下(就是 home
键在右边)图片的左上角是 {0, 0}
,右下角是 {1, 1}
, – 即使设备是纵向模式也适用。
可以使用 adjustingExposure 属性来确定设备当前是否改变它的聚焦设置。当设备开始、停止聚焦时可以使用 key-value observing
观察,接收通知。
// 如果改变曝光设置,可以将其返回到默认配置
if ([currentDevice isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
CGPoint exposurePoint = CGPointMake(0.5f, 0.5f);
[currentDevice setExposurePointOfInterest:exposurePoint];
[currentDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
}
if (context == &CameraAdjustingExposureContext) {
NSLog(@"observeValueForKeyPath CameraAdjustingExposureContext");
AVCaptureDevice *device = (AVCaptureDevice *)object;
if (!device.isAdjustingExposure) {
// 锁定曝光完成了
[object removeObserver:self forKeyPath:@"adjustingExposure" context:&CameraAdjustingExposureContext];
NSLog(@"device.exposureMode == %ld", (long)device.exposureMode);
if (self.isAutoExposureAndFocusLock && [device isExposureModeSupported:AVCaptureExposureModeLocked]) {
dispatch_async(self.sessionQueue, ^{
BOOL adjustingExposure = [[change objectForKey:NSKeyValueChangeNewKey] isEqualToNumber:[NSNumber numberWithInt:1]];
if (adjustingExposure) {
NSError *error;
if ([device lockForConfiguration:&error]) {
[device setExposureMode:AVCaptureExposureModeLocked];
[device unlockForConfiguration];
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf.lockSettingBlock) weakSelf.lockSettingBlock(YES, nil);
});
}
}
});
}else {
CGFloat delayTime = 0;
if ([weakSelf isFrontCamera]) {
delayTime = 0.5;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (weakSelf.settingBlock) {
weakSelf.settingBlock(XYCameraSettingTypeAdjustExposure, YES, device.exposurePointOfInterest, nil);
}
});
}
}
}
Flash Modes - 闪光模式
有3种闪光模式:
- AVCaptureFlashModeOff :闪光灯不会闪。
- AVCaptureFlashModeOn :闪光灯总是会闪。
- AVCaptureFlashModeAuto :闪光灯取决去周围的光感环境。
使用 hasFlash 来确定设备是否有闪光灯。如果这个方法返回 YES
,然后使用 isFlashModeSupported: 方法确定设备是否支持给定的闪光模式,然后使用 flashMode 属性设置模式。
Torch Mode - 手电筒模式
在手电筒模式下,闪光灯在一个低功率下一直开启,以照亮对视频捕获。有3个手电筒模式:
- AVCaptureTorchModeOff :总是关闭。
- AVCaptureTorchModeOn :总是打开。
- AVCaptureTorchModeAuto :闪光灯根据需要自动开关。
使用 hasTorch 来确定设备是否有闪光灯。使用 isTorchModeSupported: 方法来确定设备是否支持给定的闪光模式,然后使用 torchMode 属性来设置模式。
对于一个有手电筒的设备,只有当该设备与一个运行时捕捉会话关联时,才能打开手电筒。
防抖
电影视频的稳定化可用于连接视频上的操作,这取决于具体的硬件。尽管如此,不是所有的源格式和视频分辨率都被支持。
使用电影视频稳定化也可能会对视频采集管道引起额外的延迟。正在使用视频稳定化时,使用 videoStabilizationEnabled 属性可以检测。enablesVideoStabilizationWhenAvailable 属性允许应用程序自动使视频稳定化可用,如果它是被摄像头支持的话。由于以上限制,默认自动稳定化是禁用的。
White Balance - 白平衡
有两个白平衡模式:
- AVCaptureWhiteBalanceModeLocked :白平衡模式是固定的。
- AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance :相机需要不断调整白平衡。
使用 isWhiteBalanceModeSupported: :方法来确定设备是否支持给定的白平衡模式,然后使用 whiteBalanceMode 属性设置模式。
可以使用 adjustingWhiteBalance 属性来确定设备是否正在改变白平衡设置。当设备开始或者停止改变它的白平衡设置时,可以使用 key-value observing
观察属性,接收通知。
Setting Device Orientation - 设置设备方向
在 AVCaptureConnection
设置期望的方向,来指定你想要的图像在 AVCaptureOutput
(AVCaptureMovieFileOutput
, AVCaptureStillImageOutput
, AVCaptureVideoDataOutput
)中的方向,为了连接。
使用 AVCaptureConnectionsupportsVideoOrientation
属性来确定设备是否支持改变视频的方向,videoOrientation
属性指定你想要的图像在输出端口的方向。列表4-1显示了如何设置方向,为 AVCaptureConnection 设置 AVCaptureVideoOrientationLandscapeLeft 。
配置设备
在设备上设置捕获属性,必须先使用 lockForConfiguration: 获得设备锁。这样就避免了在其他应用程序中可能与设置不兼容的更改。下面的代码段演示了首先如何通过确定模式是否被支持的方式改变一个设备上的焦点模式,然后视图锁定设备重新配置。只有当锁被获取到,焦点模式才会被改变,并且锁被释放后立即锁定。
if ([device isFocusModeSupported:AVCaptureFocusModeLocked]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
device.focusMode = AVCaptureFocusModeLocked;
[device unlockForConfiguration];
}
else {
// Respond to the failure as appropriate.
}
只有在需要设置设备属性保持不变的时候才应该使设备锁保持。没必要的保持设备锁,可能会在其他应用程序共享设备时降低捕获质量。
Use Capture Inputs to Add a Capture Device to a Session - 使用捕获输入将捕获设备添加到会话中
添加一个捕获装置到捕获会话中,使用 AVCaptureDeviceInput (AVCaptureInput 抽象类的具体子类)的实例。捕获设备输入管理设备的端口。
NSError *error;
AVCaptureDeviceInput *input =
[AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (!input) {
// Handle the error appropriately.
}
使用 addInput: 给会话添加一个输入。如果合适的话,可以使用 canAddInput: 检查是否有输入捕获与现有会话是兼容的。
有关如果配置一个正在运行的会话,更多细节请查看 Configuring a Session .
AVCaptureInput
声明一个或者多个媒体数据流。例如,输入设备可以提供音频和视频数据。输入提供的每个媒体流都被一个 AVCaptureInputPort 所表示。一个捕获会话使用 AVCaptureConnection
对象来定义一个 一组 AVCaptureInputPort
对象和一个 AVCaptureOutput
之间的映射。
Use Capture Outputs to Get Output from a Session - 使用捕获输出从会话得到输出
要从捕获会话得到输出,可以添加一个或多个输出。一个输出是 AVCaptureOutput 的具体子类的实例。下面几种使用:
AVCaptureMovieFileOutput 输出电影文件
AVCaptureVideoDataOutput 如果你想处理被捕获视频的帧,例如,创建自己的自定义
view layer
。AVCaptureAudioDataOutput 如果你想处理被捕获的音频数据。
AVCaptureStillImageOutput 如果你想捕获有元数据的静态图像。
AVCapturePhotoOutput(AVCapturePhoto) 接替AVCaptureStillImageOutput
使用 addOutput: 把输出添加到捕获会话中。使用 canAddOutput: 检查是否一个捕获输出与现有的会话是兼容的。可以在会话正在运行的时候添加和删除所需的输出。
AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];
AVCaptureMovieFileOutput *movieOutput = <#Create and configure a movie output#>;
if ([captureSession canAddOutput:movieOutput]) {
[captureSession addOutput:movieOutput];
}
else {
// Handle the failure.
}