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.

190 lines
5.4 KiB

  1. //
  2. // SCManagedCaptureDeviceLinearInterpolationZoomHandler.m
  3. // Snapchat
  4. //
  5. // Created by Joe Qiao on 03/01/2018.
  6. //
  7. #import "SCManagedCaptureDeviceLinearInterpolationZoomHandler.h"
  8. #import "SCCameraTweaks.h"
  9. #import "SCManagedCaptureDeviceDefaultZoomHandler_Private.h"
  10. #import "SCManagedCapturerLogging.h"
  11. #import <SCFoundation/SCAssertWrapper.h>
  12. #import <SCFoundation/SCMathUtils.h>
  13. @interface SCManagedCaptureDeviceLinearInterpolationZoomHandler ()
  14. @property (nonatomic, strong) CADisplayLink *displayLink;
  15. @property (nonatomic, assign) double timestamp;
  16. @property (nonatomic, assign) float targetFactor;
  17. @property (nonatomic, assign) float intermediateFactor;
  18. @property (nonatomic, assign) int trend;
  19. @property (nonatomic, assign) float stepLength;
  20. @end
  21. @implementation SCManagedCaptureDeviceLinearInterpolationZoomHandler
  22. - (instancetype)initWithCaptureResource:(SCCaptureResource *)captureResource
  23. {
  24. self = [super initWithCaptureResource:captureResource];
  25. if (self) {
  26. _timestamp = -1.0;
  27. _targetFactor = 1.0;
  28. _intermediateFactor = _targetFactor;
  29. _trend = 1;
  30. _stepLength = 0.0;
  31. }
  32. return self;
  33. }
  34. - (void)dealloc
  35. {
  36. [self _invalidate];
  37. }
  38. - (void)setZoomFactor:(CGFloat)zoomFactor forDevice:(SCManagedCaptureDevice *)device immediately:(BOOL)immediately
  39. {
  40. if (self.currentDevice != device) {
  41. if (_displayLink) {
  42. // if device changed, interupt smoothing process
  43. // and reset to target zoom factor immediately
  44. [self _resetToZoomFactor:_targetFactor];
  45. }
  46. self.currentDevice = device;
  47. immediately = YES;
  48. }
  49. if (immediately) {
  50. [self _resetToZoomFactor:zoomFactor];
  51. } else {
  52. [self _addTargetZoomFactor:zoomFactor];
  53. }
  54. }
  55. #pragma mark - Configurable
  56. // smoothen if the update time interval is greater than the threshold
  57. - (double)_thresholdTimeIntervalToSmoothen
  58. {
  59. return SCCameraTweaksSmoothZoomThresholdTime();
  60. }
  61. - (double)_thresholdFactorDiffToSmoothen
  62. {
  63. return SCCameraTweaksSmoothZoomThresholdFactor();
  64. }
  65. - (int)_intermediateFactorFramesPerSecond
  66. {
  67. return SCCameraTweaksSmoothZoomIntermediateFramesPerSecond();
  68. }
  69. - (double)_delayTolerantTime
  70. {
  71. return SCCameraTweaksSmoothZoomDelayTolerantTime();
  72. }
  73. // minimum step length between two intermediate factors,
  74. // the greater the better as long as could provide a 'smooth experience' during smoothing process
  75. - (float)_minimumStepLength
  76. {
  77. return SCCameraTweaksSmoothZoomMinStepLength();
  78. }
  79. #pragma mark - Private methods
  80. - (void)_addTargetZoomFactor:(float)factor
  81. {
  82. SCAssertMainThread();
  83. SCLogCapturerInfo(@"Smooth Zoom - [1] t=%f zf=%f", CACurrentMediaTime(), factor);
  84. if (SCFloatEqual(factor, _targetFactor)) {
  85. return;
  86. }
  87. _targetFactor = factor;
  88. float diff = _targetFactor - _intermediateFactor;
  89. if ([self _isDuringSmoothingProcess]) {
  90. // during smoothing, only update data
  91. [self _updateDataWithDiff:diff];
  92. } else {
  93. double curTimestamp = CACurrentMediaTime();
  94. if (!SCFloatEqual(_timestamp, -1.0) && (curTimestamp - _timestamp) > [self _thresholdTimeIntervalToSmoothen] &&
  95. ABS(diff) > [self _thresholdFactorDiffToSmoothen]) {
  96. // need smoothing
  97. [self _updateDataWithDiff:diff];
  98. if ([self _nextStep]) {
  99. // use timer to interpolate intermediate factors to avoid sharp jump
  100. _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_nextStep)];
  101. _displayLink.preferredFramesPerSecond = [self _intermediateFactorFramesPerSecond];
  102. [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
  103. }
  104. } else {
  105. _timestamp = curTimestamp;
  106. _intermediateFactor = factor;
  107. SCLogCapturerInfo(@"Smooth Zoom - [2] t=%f zf=%f", CACurrentMediaTime(), _intermediateFactor);
  108. [self _setZoomFactor:_intermediateFactor forManagedCaptureDevice:self.currentDevice];
  109. }
  110. }
  111. }
  112. - (void)_resetToZoomFactor:(float)factor
  113. {
  114. [self _invalidate];
  115. _timestamp = -1.0;
  116. _targetFactor = factor;
  117. _intermediateFactor = _targetFactor;
  118. [self _setZoomFactor:_intermediateFactor forManagedCaptureDevice:self.currentDevice];
  119. }
  120. - (BOOL)_nextStep
  121. {
  122. _timestamp = CACurrentMediaTime();
  123. _intermediateFactor += (_trend * _stepLength);
  124. BOOL hasNext = YES;
  125. if (_trend < 0.0) {
  126. _intermediateFactor = MAX(_intermediateFactor, _targetFactor);
  127. } else {
  128. _intermediateFactor = MIN(_intermediateFactor, _targetFactor);
  129. }
  130. SCLogCapturerInfo(@"Smooth Zoom - [3] t=%f zf=%f", CACurrentMediaTime(), _intermediateFactor);
  131. [self _setZoomFactor:_intermediateFactor forManagedCaptureDevice:self.currentDevice];
  132. if (SCFloatEqual(_intermediateFactor, _targetFactor)) {
  133. // finish smoothening
  134. [self _invalidate];
  135. hasNext = NO;
  136. }
  137. return hasNext;
  138. }
  139. - (void)_invalidate
  140. {
  141. [_displayLink invalidate];
  142. _displayLink = nil;
  143. _trend = 1;
  144. _stepLength = 0.0;
  145. }
  146. - (void)_updateDataWithDiff:(CGFloat)diff
  147. {
  148. _trend = diff < 0.0 ? -1 : 1;
  149. _stepLength =
  150. MAX(_stepLength, MAX([self _minimumStepLength],
  151. ABS(diff) / ([self _delayTolerantTime] * [self _intermediateFactorFramesPerSecond])));
  152. }
  153. - (BOOL)_isDuringSmoothingProcess
  154. {
  155. return (_displayLink ? YES : NO);
  156. }
  157. @end