Source-SCCamera/ManagedCapturer/SCManagedCaptureDeviceFaceD...

154 lines
6.4 KiB
Objective-C

//
// 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