自定义相机需要完成闪光灯设置、摄像头切换、聚焦和曝光等关于摄像头的操作。一般涉及到摄像头操作,我们就需要使用AVCaptureDevice
进行操作。
具体的相机设置代码可以参考SCCamera中的SCCameraManager.m代码,觉得有用的可以Star支持一下
基本技巧
- 在修改设置之前都需要判断当前设备是否支持
supported
- 比如用
[device isFlashModeSupported:AVCaptureFlashModeOn]
判断设备是否支持打开闪光灯
- 比如用
- 每次对设备进行修改前都需要获取设备上的一个排它锁
- 应用程序有很多个,
AVCaptureDevice
对象也有很多个,但是硬件设备是有限的,因此,系统在我们设置相机的时候需要申请锁(lockForConfiguration:
),并在设置完毕后释放锁(unlockForConfiguration
)。
- 应用程序有很多个,
为了方便起见,我为AVCaptureDevice
写了一个分类方法:
- (void)settingWithConfig:(void(^)(AVCaptureDevice* device, NSError* error))config {
NSError *error;
if ([self lockForConfiguration:&error]) {
config(self, nil);
[self unlockForConfiguration];
}
if (error) {
config(nil, error);
}
}
PS:调用此方法只需要在闭包中执行设备操作即可,默认进行锁的申请与释放。
※ 下面各种设备设置的介绍,代码部分均省略设备锁的获取与释放。
聚焦模式
聚焦就是把目标对象的清晰度调至最高。
可用的聚焦的模式(AVCaptureFocusMode
)有以下三种:
-
AVCaptureFocusModeLocked
:锁定当前聚焦状态 -
AVCaptureFocusModeAutoFocus
:自动聚焦模式 -
AVCaptureFocusModeContinuousAutoFocus
:摄像头在需要的情况下持续地进行自动对焦。- 通过监听
adjustingFocus
这个属性可以找到设备当前是否在聚焦
- 通过监听
PS:AVCaptureFocusModeAutoFocus
=AVCaptureFocusModeContinuousAutoFocus
+AVCaptureFocusModeLocked
,即自动聚焦相当于设备持续聚焦完成后,自动设置为锁定状态
一般设备还会支持对某个兴趣点(PointOfInterest
)进行聚焦。值得注意的是,该兴趣点的坐标系是设备坐标系({0,0}~{1,1}
),可以使用AVCaptureVideoPreviewLayer
的captureDevicePointOfInterestForPoint:
方法进行获取。
if (device.isFocusPointOfInterestSupported && [device isFocusModeSupported:focusMode]) {
device.focusPointOfInterest = point;
// 需要设置 focusMode 才应用 focusPointOfInterest
device.focusMode = focusMode;
}
曝光模式
曝光就是指相机的感光元件接受外界光线,再形成图像的过程,感光元件接收外界光线的多少直接影响照片的亮度。
可用曝光模式(AVCaptureExposureMode
)有以下两种:
-
AVCaptureExposureModeLocked
:锁定当前曝光状态 -
AVCaptureExposureModeAutoExpose
:自动曝光模式- 实现类似于聚焦模式中的
AVCaptureFocusModeAutoFocus
- 实现类似于聚焦模式中的
-
AVCaptureExposureModeContinuousAutoExposure
:摄像头在需要的情况下持续自动曝光 -
AVCaptureExposureModeCustom
:根据ISO
与exposureDuration
进行曝光调整
曝光的使用于聚焦相似,它也可以在设备的支持下使用ExposurePointOfInterest
:
if (device.isExposurePointOfInterestSupported && [device isExposureModeSupported:exposureMode]) {
device.exposurePointOfInterest = point;
// 需要设置 exposureMode 才应用 exposurePointOfInterest
device.exposureMode = exposureMode;
}
曝光的使用除了和聚焦一样都使用兴趣点设置和自动曝光,还能调整感光度ISO
,做出亮度调整的操作(由于出现嵌套,这里的将会出现设备锁的代码):
if ([device lockForConfiguration:&error]) { // 第1次设置
[device setExposureModeCustomWithDuration:device.exposureDuration ISO:val completionHandler:^(CMTime syncTime) {
if ([device lockForConfiguration:&error]) { // 第2次设置
[device setExposureMode:AVCaptureExposureModeCustom];
[device unlockForConfiguration];
}
}];
[device unlockForConfiguration];
}
ISO
属性值需要根据大家的需要自行运算,在运算之前,我们可以通过device.activeFormat.minISO
和device.activeFormat.maxISO
获取ISO
的最大值和最小值,以免出现设备不支持导致崩溃的情况出现
PS:通过device.activeFormat
我们可以获取很多硬件设备的信息,具体可到AVCaptureDeviceFormat
的文档中查阅。
白平衡模式
白平衡指的是在任何拍摄场景的光源下,都能将白色的物体还原为白色。相机的白平衡控制,就是相机在不同的光线环境中把白色的物体拍出来的相片尽可能还原为标准的白色,其实就是为了使相片不发生偏色的过程。
可用的白平衡模式(AVCaptureWhiteBalanceMode
)和聚焦模式相似,都是有三种:
-
AVCaptureWhiteBalanceModeLocked
:锁定当前状态 -
AVCaptureWhiteBalanceModeAutoWhiteBalance
:自动白平衡模式 -
AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance
:持续自动白平衡模式
if ([device isWhiteBalanceModeSupported:mode]) {
[device setWhiteBalanceMode:mode];
}
缩放操作
在自定义相机中,我们需要通过捏合手势来控制镜头的远近,即缩放操作。缩放操作的使用比上面的ISO
操作要更简单一点,只需要在合适的范围内进行一次设置即可。
if (device.activeFormat.videoMaxZoomFactor > factor && factor >= 1.0) {
[device rampToVideoZoomFactor:factor withRate:4.0];
}
-
factor
: 缩放因子 -
rate
: 缩放速率
闪光灯操作
参照着平时使用相机,闪光灯是用三种模式AVCaptureFlashMode
的:
-
AVCaptureFlashModeOff
:关闭闪光灯 -
AVCaptureFlashModeOn
:开启闪光灯 -
AVCaptureFlashModeAuto
:设备会根据周围的光线环境来决定是否开启闪光灯
if ([device hasFlash]) { // 判断设备是否有闪光灯
if ([device isFlashModeSupported: mode]) {
device.flashMode = mode;
}
}
手电筒操作
为了对场景进行持续补光,可以使用手电筒功能。一共有三种手电筒模式AVCaptureTorchMode
:
-
AVCaptureTorchModeOff
:关闭手电筒 -
AVCaptureTorchModeOn
:开启手电筒 -
AVCaptureTorchModeAuto
:根据周围光线条件自动使用手电筒
if ([device hasTorch]) { // 判断设备是否有手电筒
if ([device isTorchModeSupported: mode]) {
device.torchMode = mode;
}
}
切换摄像头操作
正常手机都有两个摄像头,前置和后置,那么自定义相机就需要实现相机切换功能。切换相机其实就是切换一个输入流,那么就意味着我们需要修改当前的AVCaptureSession
。还好,我们可以进行动态重新配置会话,也不用担心停止会话和重启会话带来的开销。
[session beginConfiguration];
[session removeInput:oldInput];
if ([session canAddInput:newInput]) {
[session addInput:newInput];
} else {
// 防止 newInput 不可用
[session addInput:oldInput];
}
[session commitConfiguration];
注意事项:
- 只要是涉及到会话配置,都需要将操作置于
beginConfiguration
和commitConfiguration
之间 - 切换会话操作也需要有放在
sessionQueue
中执行,以免影响主线程 - ※ 由于有切换摄像头的操作,我们上面提到的一切摄像头的配置都应该放在
sessionQueue
中执行。一方面避免影响主线程,另一方面避免摄像头切换带来的Bug。
PS: sessionQueue
是一个GCD的串行队列,我们通过串行队列异步执行达成在同一个子线程顺序执行的效果。
拓展
重置聚焦与曝光
自定义相机聚焦和曝光被调整后,想恢复到原来原始就需要使用以下的操作:
AVCaptureFocusMode focusMode = AVCaptureFocusModeContinuousAutoFocus;
AVCaptureExposureMode exposureMode = AVCaptureExposureModeContinuousAutoExposure;
BOOL canResetFocus = [device isFocusPointOfInterestSupported] &&
[device isFocusModeSupported:focusMode];
BOOL canResetExposure = [device isExposurePointOfInterestSupported] &&
[device isExposureModeSupported:exposureMode];
CGPoint centerPoint = CGPointMake(0.5f, 0.5f);
if (canResetFocus) {
device.focusPointOfInterest = centerPoint;
device.focusMode = focusMode;
}
if (canResetExposure) {
device.exposurePointOfInterest = centerPoint;
device.exposureMode = exposureMode;
}
PS:重置的方式其实就是将聚焦和曝光的兴趣点都设置为镜头中央,并且都采用一次性的自动模式。
具体的相机设置代码可以参考SCCamera中的SCCameraManager.m代码,觉得有用的可以Star支持一下