You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
6.4 KiB
153 lines
6.4 KiB
//
|
|
// SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.m
|
|
// Snapchat
|
|
//
|
|
// Created by Jiyang Zhu on 3/7/18.
|
|
// Copyright © 2018 Snapchat, Inc. All rights reserved.
|
|
//
|
|
|
|
#import "SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.h"
|
|
|
|
#import "AVCaptureDevice+ConfigurationLock.h"
|
|
#import "SCCameraTweaks.h"
|
|
#import "SCManagedCaptureFaceDetectionAdjustingPOIResource.h"
|
|
#import "SCManagedCapturer.h"
|
|
#import "SCManagedCapturerListener.h"
|
|
|
|
#import <SCFoundation/SCAssertWrapper.h>
|
|
#import <SCFoundation/SCTrace.h>
|
|
#import <SCFoundation/SCTraceODPCompatible.h>
|
|
|
|
@interface SCManagedCaptureDeviceFaceDetectionAutoFocusHandler () <SCManagedCapturerListener>
|
|
|
|
@property (nonatomic, strong) AVCaptureDevice *device;
|
|
@property (nonatomic, weak) id<SCCapturer> managedCapturer;
|
|
@property (nonatomic, assign) CGPoint focusPointOfInterest;
|
|
|
|
@property (nonatomic, assign) BOOL isVisible;
|
|
@property (nonatomic, assign) BOOL isContinuousAutofocus;
|
|
@property (nonatomic, assign) BOOL focusLock;
|
|
|
|
@property (nonatomic, copy) NSDictionary<NSNumber *, NSValue *> *faceBoundsByFaceID;
|
|
@property (nonatomic, strong) SCManagedCaptureFaceDetectionAdjustingPOIResource *resource;
|
|
|
|
@end
|
|
|
|
@implementation SCManagedCaptureDeviceFaceDetectionAutoFocusHandler
|
|
|
|
- (instancetype)initWithDevice:(AVCaptureDevice *)device
|
|
pointOfInterest:(CGPoint)pointOfInterest
|
|
managedCapturer:(id<SCCapturer>)managedCapturer
|
|
{
|
|
if (self = [super init]) {
|
|
SCAssert(device, @"AVCaptureDevice should not be nil.");
|
|
SCAssert(managedCapturer, @"id<SCCapturer> should not be nil.");
|
|
_device = device;
|
|
_focusPointOfInterest = pointOfInterest;
|
|
SCManagedCaptureDevicePosition position =
|
|
(device.position == AVCaptureDevicePositionFront ? SCManagedCaptureDevicePositionFront
|
|
: SCManagedCaptureDevicePositionBack);
|
|
_resource = [[SCManagedCaptureFaceDetectionAdjustingPOIResource alloc]
|
|
initWithDefaultPointOfInterest:pointOfInterest
|
|
shouldTargetOnFaceAutomatically:SCCameraTweaksTurnOnFaceDetectionFocusByDefault(position)];
|
|
_managedCapturer = managedCapturer;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (CGPoint)getFocusPointOfInterest
|
|
{
|
|
return self.focusPointOfInterest;
|
|
}
|
|
|
|
// called when user taps on a point on screen, to re-adjust camera focus onto that tapped spot.
|
|
// this re-adjustment is always necessary, regardless of scenarios (recording video, taking photo, etc),
|
|
// therefore we don't have to check self.focusLock in this method.
|
|
- (void)setAutofocusPointOfInterest:(CGPoint)pointOfInterest
|
|
{
|
|
SCTraceODPCompatibleStart(2);
|
|
pointOfInterest = [self.resource updateWithNewProposedPointOfInterest:pointOfInterest fromUser:YES];
|
|
SC_GUARD_ELSE_RETURN(!CGPointEqualToPoint(pointOfInterest, self.focusPointOfInterest) ||
|
|
self.isContinuousAutofocus);
|
|
[self _actuallySetFocusPointOfInterestIfNeeded:pointOfInterest
|
|
withFocusMode:AVCaptureFocusModeAutoFocus
|
|
taskName:@"set autofocus"];
|
|
}
|
|
|
|
- (void)continuousAutofocus
|
|
{
|
|
SCTraceODPCompatibleStart(2);
|
|
SC_GUARD_ELSE_RETURN(!self.isContinuousAutofocus);
|
|
CGPoint pointOfInterest = [self.resource updateWithNewProposedPointOfInterest:CGPointMake(0.5, 0.5) fromUser:NO];
|
|
[self _actuallySetFocusPointOfInterestIfNeeded:pointOfInterest
|
|
withFocusMode:AVCaptureFocusModeContinuousAutoFocus
|
|
taskName:@"set continuous autofocus"];
|
|
}
|
|
|
|
- (void)setFocusLock:(BOOL)focusLock
|
|
{
|
|
// Disabled focus lock for face detection and focus handler.
|
|
}
|
|
|
|
- (void)setSmoothFocus:(BOOL)smoothFocus
|
|
{
|
|
SCTraceODPCompatibleStart(2);
|
|
SC_GUARD_ELSE_RETURN(smoothFocus != self.device.smoothAutoFocusEnabled);
|
|
[self.device runTask:@"set smooth autofocus"
|
|
withLockedConfiguration:^() {
|
|
[self.device setSmoothAutoFocusEnabled:smoothFocus];
|
|
}];
|
|
}
|
|
|
|
- (void)setVisible:(BOOL)visible
|
|
{
|
|
SCTraceODPCompatibleStart(2);
|
|
SC_GUARD_ELSE_RETURN(_isVisible != visible);
|
|
self.isVisible = visible;
|
|
if (visible) {
|
|
[[SCManagedCapturer sharedInstance] addListener:self];
|
|
} else {
|
|
[[SCManagedCapturer sharedInstance] removeListener:self];
|
|
[self.resource reset];
|
|
}
|
|
}
|
|
|
|
- (void)_actuallySetFocusPointOfInterestIfNeeded:(CGPoint)pointOfInterest
|
|
withFocusMode:(AVCaptureFocusMode)focusMode
|
|
taskName:(NSString *)taskName
|
|
{
|
|
SCTraceODPCompatibleStart(2);
|
|
SC_GUARD_ELSE_RETURN(!CGPointEqualToPoint(pointOfInterest, self.focusPointOfInterest) &&
|
|
[self.device isFocusModeSupported:focusMode] && [self.device isFocusPointOfInterestSupported]);
|
|
[self.device runTask:taskName
|
|
withLockedConfiguration:^() {
|
|
// Set focus point before changing focus mode
|
|
// Be noticed that order does matter
|
|
self.device.focusPointOfInterest = pointOfInterest;
|
|
self.device.focusMode = focusMode;
|
|
}];
|
|
|
|
self.focusPointOfInterest = pointOfInterest;
|
|
self.isContinuousAutofocus = (focusMode == AVCaptureFocusModeContinuousAutoFocus);
|
|
}
|
|
|
|
#pragma mark - SCManagedCapturerListener
|
|
- (void)managedCapturer:(id<SCCapturer>)managedCapturer
|
|
didDetectFaceBounds:(NSDictionary<NSNumber *, NSValue *> *)faceBoundsByFaceID
|
|
{
|
|
SCTraceODPCompatibleStart(2);
|
|
SC_GUARD_ELSE_RETURN(self.isVisible);
|
|
CGPoint pointOfInterest = [self.resource updateWithNewDetectedFaceBounds:faceBoundsByFaceID];
|
|
// If pointOfInterest is equal to CGPointMake(0.5, 0.5), it means no valid face is found, so that we should reset to
|
|
// AVCaptureFocusModeContinuousAutoFocus. Otherwise, focus on the point and set the mode as
|
|
// AVCaptureFocusModeAutoFocus.
|
|
// TODO(Jiyang): Refactor SCManagedCaptureFaceDetectionAdjustingPOIResource to include focusMode and exposureMode.
|
|
AVCaptureFocusMode focusMode = CGPointEqualToPoint(pointOfInterest, CGPointMake(0.5, 0.5))
|
|
? AVCaptureFocusModeContinuousAutoFocus
|
|
: AVCaptureFocusModeAutoFocus;
|
|
[self _actuallySetFocusPointOfInterestIfNeeded:pointOfInterest
|
|
withFocusMode:focusMode
|
|
taskName:@"set autofocus from face detection"];
|
|
}
|
|
|
|
@end
|