2014 snapchat source code
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

  1. //
  2. // SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.m
  3. // Snapchat
  4. //
  5. // Created by Jiyang Zhu on 3/7/18.
  6. // Copyright © 2018 Snapchat, Inc. All rights reserved.
  7. //
  8. #import "SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.h"
  9. #import "AVCaptureDevice+ConfigurationLock.h"
  10. #import "SCCameraTweaks.h"
  11. #import "SCManagedCaptureFaceDetectionAdjustingPOIResource.h"
  12. #import "SCManagedCapturer.h"
  13. #import "SCManagedCapturerListener.h"
  14. #import <SCFoundation/SCAssertWrapper.h>
  15. #import <SCFoundation/SCTrace.h>
  16. #import <SCFoundation/SCTraceODPCompatible.h>
  17. @interface SCManagedCaptureDeviceFaceDetectionAutoFocusHandler () <SCManagedCapturerListener>
  18. @property (nonatomic, strong) AVCaptureDevice *device;
  19. @property (nonatomic, weak) id<SCCapturer> managedCapturer;
  20. @property (nonatomic, assign) CGPoint focusPointOfInterest;
  21. @property (nonatomic, assign) BOOL isVisible;
  22. @property (nonatomic, assign) BOOL isContinuousAutofocus;
  23. @property (nonatomic, assign) BOOL focusLock;
  24. @property (nonatomic, copy) NSDictionary<NSNumber *, NSValue *> *faceBoundsByFaceID;
  25. @property (nonatomic, strong) SCManagedCaptureFaceDetectionAdjustingPOIResource *resource;
  26. @end
  27. @implementation SCManagedCaptureDeviceFaceDetectionAutoFocusHandler
  28. - (instancetype)initWithDevice:(AVCaptureDevice *)device
  29. pointOfInterest:(CGPoint)pointOfInterest
  30. managedCapturer:(id<SCCapturer>)managedCapturer
  31. {
  32. if (self = [super init]) {
  33. SCAssert(device, @"AVCaptureDevice should not be nil.");
  34. SCAssert(managedCapturer, @"id<SCCapturer> should not be nil.");
  35. _device = device;
  36. _focusPointOfInterest = pointOfInterest;
  37. SCManagedCaptureDevicePosition position =
  38. (device.position == AVCaptureDevicePositionFront ? SCManagedCaptureDevicePositionFront
  39. : SCManagedCaptureDevicePositionBack);
  40. _resource = [[SCManagedCaptureFaceDetectionAdjustingPOIResource alloc]
  41. initWithDefaultPointOfInterest:pointOfInterest
  42. shouldTargetOnFaceAutomatically:SCCameraTweaksTurnOnFaceDetectionFocusByDefault(position)];
  43. _managedCapturer = managedCapturer;
  44. }
  45. return self;
  46. }
  47. - (CGPoint)getFocusPointOfInterest
  48. {
  49. return self.focusPointOfInterest;
  50. }
  51. // called when user taps on a point on screen, to re-adjust camera focus onto that tapped spot.
  52. // this re-adjustment is always necessary, regardless of scenarios (recording video, taking photo, etc),
  53. // therefore we don't have to check self.focusLock in this method.
  54. - (void)setAutofocusPointOfInterest:(CGPoint)pointOfInterest
  55. {
  56. SCTraceODPCompatibleStart(2);
  57. pointOfInterest = [self.resource updateWithNewProposedPointOfInterest:pointOfInterest fromUser:YES];
  58. SC_GUARD_ELSE_RETURN(!CGPointEqualToPoint(pointOfInterest, self.focusPointOfInterest) ||
  59. self.isContinuousAutofocus);
  60. [self _actuallySetFocusPointOfInterestIfNeeded:pointOfInterest
  61. withFocusMode:AVCaptureFocusModeAutoFocus
  62. taskName:@"set autofocus"];
  63. }
  64. - (void)continuousAutofocus
  65. {
  66. SCTraceODPCompatibleStart(2);
  67. SC_GUARD_ELSE_RETURN(!self.isContinuousAutofocus);
  68. CGPoint pointOfInterest = [self.resource updateWithNewProposedPointOfInterest:CGPointMake(0.5, 0.5) fromUser:NO];
  69. [self _actuallySetFocusPointOfInterestIfNeeded:pointOfInterest
  70. withFocusMode:AVCaptureFocusModeContinuousAutoFocus
  71. taskName:@"set continuous autofocus"];
  72. }
  73. - (void)setFocusLock:(BOOL)focusLock
  74. {
  75. // Disabled focus lock for face detection and focus handler.
  76. }
  77. - (void)setSmoothFocus:(BOOL)smoothFocus
  78. {
  79. SCTraceODPCompatibleStart(2);
  80. SC_GUARD_ELSE_RETURN(smoothFocus != self.device.smoothAutoFocusEnabled);
  81. [self.device runTask:@"set smooth autofocus"
  82. withLockedConfiguration:^() {
  83. [self.device setSmoothAutoFocusEnabled:smoothFocus];
  84. }];
  85. }
  86. - (void)setVisible:(BOOL)visible
  87. {
  88. SCTraceODPCompatibleStart(2);
  89. SC_GUARD_ELSE_RETURN(_isVisible != visible);
  90. self.isVisible = visible;
  91. if (visible) {
  92. [[SCManagedCapturer sharedInstance] addListener:self];
  93. } else {
  94. [[SCManagedCapturer sharedInstance] removeListener:self];
  95. [self.resource reset];
  96. }
  97. }
  98. - (void)_actuallySetFocusPointOfInterestIfNeeded:(CGPoint)pointOfInterest
  99. withFocusMode:(AVCaptureFocusMode)focusMode
  100. taskName:(NSString *)taskName
  101. {
  102. SCTraceODPCompatibleStart(2);
  103. SC_GUARD_ELSE_RETURN(!CGPointEqualToPoint(pointOfInterest, self.focusPointOfInterest) &&
  104. [self.device isFocusModeSupported:focusMode] && [self.device isFocusPointOfInterestSupported]);
  105. [self.device runTask:taskName
  106. withLockedConfiguration:^() {
  107. // Set focus point before changing focus mode
  108. // Be noticed that order does matter
  109. self.device.focusPointOfInterest = pointOfInterest;
  110. self.device.focusMode = focusMode;
  111. }];
  112. self.focusPointOfInterest = pointOfInterest;
  113. self.isContinuousAutofocus = (focusMode == AVCaptureFocusModeContinuousAutoFocus);
  114. }
  115. #pragma mark - SCManagedCapturerListener
  116. - (void)managedCapturer:(id<SCCapturer>)managedCapturer
  117. didDetectFaceBounds:(NSDictionary<NSNumber *, NSValue *> *)faceBoundsByFaceID
  118. {
  119. SCTraceODPCompatibleStart(2);
  120. SC_GUARD_ELSE_RETURN(self.isVisible);
  121. CGPoint pointOfInterest = [self.resource updateWithNewDetectedFaceBounds:faceBoundsByFaceID];
  122. // If pointOfInterest is equal to CGPointMake(0.5, 0.5), it means no valid face is found, so that we should reset to
  123. // AVCaptureFocusModeContinuousAutoFocus. Otherwise, focus on the point and set the mode as
  124. // AVCaptureFocusModeAutoFocus.
  125. // TODO(Jiyang): Refactor SCManagedCaptureFaceDetectionAdjustingPOIResource to include focusMode and exposureMode.
  126. AVCaptureFocusMode focusMode = CGPointEqualToPoint(pointOfInterest, CGPointMake(0.5, 0.5))
  127. ? AVCaptureFocusModeContinuousAutoFocus
  128. : AVCaptureFocusModeAutoFocus;
  129. [self _actuallySetFocusPointOfInterestIfNeeded:pointOfInterest
  130. withFocusMode:focusMode
  131. taskName:@"set autofocus from face detection"];
  132. }
  133. @end