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.

184 lines
7.0 KiB

  1. //
  2. // SCFeatureImageCaptureImpl.m
  3. // SCCamera
  4. //
  5. // Created by Kristian Bauer on 4/18/18.
  6. //
  7. #import "SCFeatureImageCaptureImpl.h"
  8. #import "SCLogger+Camera.h"
  9. #import "SCManagedCapturePreviewLayerController.h"
  10. #import "SCManagedCapturerLensAPI.h"
  11. #import "SCManagedCapturerListener.h"
  12. #import "SCManagedCapturerUtils.h"
  13. #import "SCManagedStillImageCapturer.h"
  14. #import <SCFoundation/SCDeviceName.h>
  15. #import <SCFoundation/SCLog.h>
  16. #import <SCFoundation/SCQueuePerformer.h>
  17. #import <SCFoundation/SCTraceODPCompatible.h>
  18. #import <SCGhostToSnappable/SCGhostToSnappableSignal.h>
  19. #import <SCLogger/SCCameraMetrics.h>
  20. #import <SCLogger/SCLogger+Performance.h>
  21. @interface SCFeatureImageCaptureImpl ()
  22. @property (nonatomic, strong, readwrite) id<SCCapturer> capturer;
  23. @property (nonatomic, strong, readwrite) SCLogger *logger;
  24. @property (nonatomic, assign) AVCameraViewType cameraViewType;
  25. @property (nonatomic, strong, readwrite) SCManagedCapturerState *managedCapturerState;
  26. /**
  27. * Whether user has attempted image capture in current session. Reset on foreground of app.
  28. */
  29. @property (nonatomic, assign) BOOL hasTriedCapturing;
  30. @end
  31. @interface SCFeatureImageCaptureImpl (SCManagedCapturerListener) <SCManagedCapturerListener>
  32. @end
  33. @implementation SCFeatureImageCaptureImpl
  34. @synthesize delegate = _delegate;
  35. @synthesize imagePromise = _imagePromise;
  36. - (instancetype)initWithCapturer:(id<SCCapturer>)capturer
  37. logger:(SCLogger *)logger
  38. cameraViewType:(AVCameraViewType)cameraViewType
  39. {
  40. SCTraceODPCompatibleStart(2);
  41. self = [super init];
  42. if (self) {
  43. _capturer = capturer;
  44. [_capturer addListener:self];
  45. _logger = logger;
  46. _cameraViewType = cameraViewType;
  47. [[NSNotificationCenter defaultCenter] addObserver:self
  48. selector:@selector(_viewWillEnterForeground)
  49. name:UIApplicationWillEnterForegroundNotification
  50. object:nil];
  51. }
  52. return self;
  53. }
  54. - (void)dealloc
  55. {
  56. [_capturer removeListener:self];
  57. }
  58. #pragma mark - SCFeatureImageCapture
  59. - (void)captureImage:(NSString *)captureSessionID
  60. {
  61. SCTraceODPCompatibleStart(2);
  62. [_logger logTimedEventStart:kSCCameraMetricsRecordingDelay uniqueId:@"IMAGE" isUniqueEvent:NO];
  63. BOOL asyncCaptureEnabled = [self _asynchronousCaptureEnabled:_managedCapturerState];
  64. SCLogCameraFeatureInfo(@"[%@] takeImage begin async: %@", NSStringFromClass([self class]),
  65. asyncCaptureEnabled ? @"YES" : @"NO");
  66. if (asyncCaptureEnabled) {
  67. SCQueuePerformer *performer = [[SCQueuePerformer alloc] initWithLabel:"com.snapchat.image-capture-promise"
  68. qualityOfService:QOS_CLASS_USER_INTERACTIVE
  69. queueType:DISPATCH_QUEUE_SERIAL
  70. context:SCQueuePerformerContextCoreCamera];
  71. _imagePromise = [[SCPromise alloc] initWithPerformer:performer];
  72. }
  73. @weakify(self);
  74. [_capturer captureStillImageAsynchronouslyWithAspectRatio:SCManagedCapturedImageAndVideoAspectRatio()
  75. captureSessionID:captureSessionID
  76. completionHandler:^(UIImage *fullScreenImage, NSDictionary *metadata,
  77. NSError *error, SCManagedCapturerState *state) {
  78. @strongify(self);
  79. SC_GUARD_ELSE_RETURN(self);
  80. [self _takeImageCallback:fullScreenImage
  81. metadata:metadata
  82. error:error
  83. state:state];
  84. }
  85. context:SCCapturerContext];
  86. [_logger logCameraCaptureFinishedWithDuration:0];
  87. }
  88. #pragma mark - Private
  89. - (void)_viewWillEnterForeground
  90. {
  91. SCTraceODPCompatibleStart(2);
  92. _hasTriedCapturing = NO;
  93. }
  94. - (void)_takeImageCallback:(UIImage *)image
  95. metadata:(NSDictionary *)metadata
  96. error:(NSError *)error
  97. state:(SCManagedCapturerState *)state
  98. {
  99. SCTraceODPCompatibleStart(2);
  100. [self _logCaptureComplete:state];
  101. if (image) {
  102. [_delegate featureImageCapture:self willCompleteWithImage:image];
  103. if (_imagePromise) {
  104. [_imagePromise completeWithValue:image];
  105. }
  106. } else {
  107. if (_imagePromise) {
  108. [_imagePromise completeWithError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
  109. }
  110. [_delegate featureImageCapture:self didCompleteWithError:error];
  111. }
  112. _imagePromise = nil;
  113. [_delegate featureImageCapturedDidComplete:self];
  114. }
  115. - (BOOL)_asynchronousCaptureEnabled:(SCManagedCapturerState *)state
  116. {
  117. SCTraceODPCompatibleStart(2);
  118. BOOL shouldCaptureImageFromVideoBuffer =
  119. [SCDeviceName isSimilarToIphone5orNewer] && ![SCDeviceName isSimilarToIphone6orNewer];
  120. // Fast image capture is disabled in following cases
  121. // (1) flash is on;
  122. // (2) lenses are active;
  123. // (3) SCPhotoCapturer is not supported;
  124. // (4) not main camera for iPhoneX;
  125. return !state.flashActive && !state.lensesActive && !_capturer.lensProcessingCore.appliedLens &&
  126. (SCPhotoCapturerIsEnabled() || shouldCaptureImageFromVideoBuffer) &&
  127. (![SCDeviceName isIphoneX] || (_cameraViewType == AVCameraViewNoReply));
  128. }
  129. - (void)_logCaptureComplete:(SCManagedCapturerState *)state
  130. {
  131. SCTraceODPCompatibleStart(2);
  132. NSDictionary *params = @{
  133. @"type" : @"image",
  134. @"lenses_active" : @(state.lensesActive),
  135. @"is_back_camera" : @(state.devicePosition != SCManagedCaptureDevicePositionFront),
  136. @"is_main_camera" : @(_cameraViewType == AVCameraViewNoReply),
  137. @"is_first_attempt_after_app_startup" : @(!_hasTriedCapturing),
  138. @"app_startup_type" : SCLaunchType(),
  139. @"app_startup_time" : @(SCAppStartupTimeMicros() / 1000.0),
  140. @"time_elapse_after_app_startup" : @(SCTimeElapseAfterAppStartupMicros() / 1000.0),
  141. };
  142. [_logger logTimedEventEnd:kSCCameraMetricsRecordingDelay uniqueId:@"IMAGE" parameters:params];
  143. _hasTriedCapturing = YES;
  144. }
  145. @end
  146. @implementation SCFeatureImageCaptureImpl (SCManagedCapturerListener)
  147. - (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeState:(SCManagedCapturerState *)state
  148. {
  149. SCTraceODPCompatibleStart(2);
  150. _managedCapturerState = [state copy];
  151. }
  152. - (void)managedCapturer:(id<SCCapturer>)managedCapturer didCapturePhoto:(SCManagedCapturerState *)state
  153. {
  154. SCTraceODPCompatibleStart(2);
  155. if (_imagePromise) {
  156. [[SCManagedCapturePreviewLayerController sharedInstance] pause];
  157. }
  158. }
  159. @end