Jonny Banana
6 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 2847 additions and 0 deletions
-
56BlackCamera/SCBlackCameraDetector.h
-
134BlackCamera/SCBlackCameraDetector.m
-
26BlackCamera/SCBlackCameraNoOutputDetector.h
-
137BlackCamera/SCBlackCameraNoOutputDetector.m
-
20BlackCamera/SCBlackCameraPreviewDetector.h
-
92BlackCamera/SCBlackCameraPreviewDetector.m
-
35BlackCamera/SCBlackCameraReporter.h
-
86BlackCamera/SCBlackCameraReporter.m
-
27BlackCamera/SCBlackCameraRunningDetector.h
-
84BlackCamera/SCBlackCameraRunningDetector.m
-
23BlackCamera/SCBlackCameraSessionBlockDetector.h
-
82BlackCamera/SCBlackCameraSessionBlockDetector.m
-
31BlackCamera/SCBlackCameraViewDetector.h
-
136BlackCamera/SCBlackCameraViewDetector.m
-
14BlackCamera/SCCaptureSessionFixer.h
-
21BlackCamera/SCCaptureSessionFixer.m
-
13ContextAwareTaskManagement/OWNERS
-
16ContextAwareTaskManagement/Requests/SCContextAwareSnapCreationThrottleRequest.h
-
70ContextAwareTaskManagement/Requests/SCContextAwareSnapCreationThrottleRequest.m
-
22ContextAwareTaskManagement/Triggers/SCSnapCreationTriggers.h
-
83ContextAwareTaskManagement/Triggers/SCSnapCreationTriggers.m
-
26Features/Core/SCFeature.h
-
13Features/Core/SCFeatureContainerView.h
-
44Features/Core/SCFeatureCoordinator.h
-
117Features/Core/SCFeatureCoordinator.m
-
50Features/Core/SCFeatureProvider.h
-
20Features/Flash/SCFeatureFlash.h
-
23Features/Flash/SCFeatureFlashImpl.h
-
226Features/Flash/SCFeatureFlashImpl.m
-
15Features/Flash/SCFlashButton.h
-
35Features/Flash/SCFlashButton.m
-
30Features/HandsFree/SCFeatureHandsFree.h
-
27Features/ImageCapture/SCFeatureImageCapture.h
-
21Features/ImageCapture/SCFeatureImageCaptureImpl.h
-
184Features/ImageCapture/SCFeatureImageCaptureImpl.m
-
22Features/NightMode/SCFeatureNightMode.h
-
18Features/NightMode/SCNightModeButton.h
-
95Features/NightMode/SCNightModeButton.m
-
26Features/Scanning/SCFeatureScanning.h
-
23Features/Shazam/SCFeatureShazam.h
-
14Features/SnapKit/SCFeatureSnapKit.h
-
17Features/TapToFocus/SCFeatureTapToFocusAndExposure.h
-
49Features/TapToFocus/SCFeatureTapToFocusAndExposureImpl.h
-
118Features/TapToFocus/SCFeatureTapToFocusAndExposureImpl.m
-
21Features/TapToFocus/SCTapAnimationView.h
-
178Features/TapToFocus/SCTapAnimationView.m
-
37Features/ToggleCamera/SCFeatureToggleCamera.h
-
34Features/Zooming/SCFeatureZooming.h
-
23Lens/SCManagedCapturerARImageCaptureProvider.h
-
27Lens/SCManagedCapturerGLViewManagerAPI.h
-
19Lens/SCManagedCapturerLSAComponentTrackerAPI.h
-
67Lens/SCManagedCapturerLensAPI.h
-
20Lens/SCManagedCapturerLensAPIProvider.h
@ -0,0 +1,56 @@ |
|||
// |
|||
// SCBlackCameraDetector.h |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 24/01/2018. |
|||
// |
|||
|
|||
#import "SCBlackCameraReporter.h" |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
#import <UIKit/UIKit.h> |
|||
|
|||
@class SCBlackCameraNoOutputDetector; |
|||
|
|||
@interface SCBlackCameraDetector : NSObject |
|||
|
|||
@property (nonatomic, strong) SCBlackCameraNoOutputDetector *blackCameraNoOutputDetector; |
|||
|
|||
SC_INIT_AND_NEW_UNAVAILABLE |
|||
- (instancetype)initWithTicketCreator:(id<SCManiphestTicketCreator>)ticketCreator; |
|||
|
|||
// CameraView visible/invisible |
|||
- (void)onCameraViewVisible:(BOOL)visible; |
|||
|
|||
- (void)onCameraViewVisibleWithTouch:(UIGestureRecognizer *)touch; |
|||
|
|||
// Call this when [AVCaptureSession startRunning] is called |
|||
- (void)sessionWillCallStartRunning; |
|||
- (void)sessionDidCallStartRunning; |
|||
|
|||
// Call this when [AVCaptureSession stopRunning] is called |
|||
- (void)sessionWillCallStopRunning; |
|||
- (void)sessionDidCallStopRunning; |
|||
|
|||
// Call this when [AVCaptureSession commitConfiguration] is called |
|||
- (void)sessionWillCommitConfiguration; |
|||
- (void)sessionDidCommitConfiguration; |
|||
|
|||
- (void)sessionDidChangeIsRunning:(BOOL)running; |
|||
|
|||
// For CapturePreview visibility detector |
|||
- (void)capturePreviewDidBecomeVisible:(BOOL)visible; |
|||
|
|||
/** |
|||
Mark the start of creating new session |
|||
When we fix black camera by creating new session, some detector may report black camera because we called |
|||
[AVCaptureSession stopRunning] on old AVCaptureSession, so we need to tell the detector the session is recreating, so |
|||
it is fine to call [AVCaptureSession stopRunning] on old AVCaptureSession. |
|||
*/ |
|||
- (void)sessionWillRecreate; |
|||
/** |
|||
Mark the end of creating new session |
|||
*/ |
|||
- (void)sessionDidRecreate; |
|||
|
|||
@end |
@ -0,0 +1,134 @@ |
|||
// |
|||
// SCBlackCameraDetector.m |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 24/01/2018. |
|||
// |
|||
|
|||
#import "SCBlackCameraDetector.h" |
|||
|
|||
#import "SCBlackCameraNoOutputDetector.h" |
|||
#import "SCBlackCameraPreviewDetector.h" |
|||
#import "SCBlackCameraRunningDetector.h" |
|||
#import "SCBlackCameraSessionBlockDetector.h" |
|||
#import "SCBlackCameraViewDetector.h" |
|||
|
|||
#import <SCFoundation/SCQueuePerformer.h> |
|||
|
|||
#if !TARGET_IPHONE_SIMULATOR |
|||
static char *const kSCBlackCameraDetectorQueueLabel = "com.snapchat.black-camera-detector"; |
|||
#endif |
|||
@interface SCBlackCameraDetector () { |
|||
BOOL _sessionIsRunning; |
|||
BOOL _cameraIsVisible; |
|||
BOOL _previewIsVisible; |
|||
} |
|||
@property (nonatomic, strong) SCQueuePerformer *queuePerformer; |
|||
@property (nonatomic, strong) SCBlackCameraViewDetector *cameraViewDetector; |
|||
@property (nonatomic, strong) SCBlackCameraRunningDetector *sessionRunningDetector; |
|||
@property (nonatomic, strong) SCBlackCameraPreviewDetector *previewDetector; |
|||
@property (nonatomic, strong) SCBlackCameraSessionBlockDetector *sessionBlockDetector; |
|||
|
|||
@end |
|||
|
|||
@implementation SCBlackCameraDetector |
|||
|
|||
- (instancetype)initWithTicketCreator:(id<SCManiphestTicketCreator>)ticketCreator |
|||
{ |
|||
#if !TARGET_IPHONE_SIMULATOR |
|||
|
|||
self = [super init]; |
|||
if (self) { |
|||
_queuePerformer = [[SCQueuePerformer alloc] initWithLabel:kSCBlackCameraDetectorQueueLabel |
|||
qualityOfService:QOS_CLASS_BACKGROUND |
|||
queueType:DISPATCH_QUEUE_SERIAL |
|||
context:SCQueuePerformerContextCamera]; |
|||
|
|||
SCBlackCameraReporter *reporter = [[SCBlackCameraReporter alloc] initWithTicketCreator:ticketCreator]; |
|||
_cameraViewDetector = [[SCBlackCameraViewDetector alloc] initWithPerformer:_queuePerformer reporter:reporter]; |
|||
_sessionRunningDetector = |
|||
[[SCBlackCameraRunningDetector alloc] initWithPerformer:_queuePerformer reporter:reporter]; |
|||
_previewDetector = [[SCBlackCameraPreviewDetector alloc] initWithPerformer:_queuePerformer reporter:reporter]; |
|||
_sessionBlockDetector = [[SCBlackCameraSessionBlockDetector alloc] initWithReporter:reporter]; |
|||
_blackCameraNoOutputDetector = [[SCBlackCameraNoOutputDetector alloc] initWithReporter:reporter]; |
|||
} |
|||
return self; |
|||
#else |
|||
return nil; |
|||
#endif |
|||
} |
|||
|
|||
#pragma mark - Camera view visibility detector |
|||
- (void)onCameraViewVisible:(BOOL)visible |
|||
{ |
|||
SC_GUARD_ELSE_RETURN(visible != _cameraIsVisible); |
|||
_cameraIsVisible = visible; |
|||
[_cameraViewDetector onCameraViewVisible:visible]; |
|||
} |
|||
|
|||
- (void)onCameraViewVisibleWithTouch:(UIGestureRecognizer *)gesture |
|||
{ |
|||
[_cameraViewDetector onCameraViewVisibleWithTouch:gesture]; |
|||
} |
|||
|
|||
#pragma mark - Track [AVCaptureSession startRunning] call |
|||
- (void)sessionWillCallStartRunning |
|||
{ |
|||
[_cameraViewDetector sessionWillCallStartRunning]; |
|||
[_sessionBlockDetector sessionWillCallStartRunning]; |
|||
} |
|||
|
|||
- (void)sessionDidCallStartRunning |
|||
{ |
|||
[_sessionRunningDetector sessionDidCallStartRunning]; |
|||
[_sessionBlockDetector sessionDidCallStartRunning]; |
|||
} |
|||
|
|||
#pragma mark - Track [AVCaptureSession stopRunning] call |
|||
- (void)sessionWillCallStopRunning |
|||
{ |
|||
[_cameraViewDetector sessionWillCallStopRunning]; |
|||
[_sessionRunningDetector sessionWillCallStopRunning]; |
|||
} |
|||
|
|||
- (void)sessionDidCallStopRunning |
|||
{ |
|||
} |
|||
|
|||
- (void)sessionDidChangeIsRunning:(BOOL)running |
|||
{ |
|||
SC_GUARD_ELSE_RETURN(running != _sessionIsRunning); |
|||
_sessionIsRunning = running; |
|||
[_sessionRunningDetector sessionDidChangeIsRunning:running]; |
|||
[_previewDetector sessionDidChangeIsRunning:running]; |
|||
} |
|||
|
|||
#pragma mark - Capture preview visibility detector |
|||
- (void)capturePreviewDidBecomeVisible:(BOOL)visible |
|||
{ |
|||
SC_GUARD_ELSE_RETURN(visible != _previewIsVisible); |
|||
_previewIsVisible = visible; |
|||
[_previewDetector capturePreviewDidBecomeVisible:visible]; |
|||
} |
|||
|
|||
#pragma mark - AVCaptureSession block detector |
|||
- (void)sessionWillCommitConfiguration |
|||
{ |
|||
[_sessionBlockDetector sessionWillCommitConfiguration]; |
|||
} |
|||
|
|||
- (void)sessionDidCommitConfiguration |
|||
{ |
|||
[_sessionBlockDetector sessionDidCommitConfiguration]; |
|||
} |
|||
|
|||
- (void)sessionWillRecreate |
|||
{ |
|||
[_cameraViewDetector sessionWillRecreate]; |
|||
} |
|||
|
|||
- (void)sessionDidRecreate |
|||
{ |
|||
[_cameraViewDetector sessionDidRecreate]; |
|||
} |
|||
@end |
@ -0,0 +1,26 @@ |
|||
// |
|||
// SCBlackCameraNoOutputDetector.h |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 05/12/2017. |
|||
// |
|||
|
|||
#import "SCManagedCapturerListener.h" |
|||
|
|||
#import <SCCameraFoundation/SCManagedVideoDataSourceListener.h> |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@class SCBlackCameraNoOutputDetector, SCBlackCameraReporter; |
|||
@protocol SCManiphestTicketCreator; |
|||
|
|||
@protocol SCBlackCameraDetectorDelegate |
|||
- (void)detector:(SCBlackCameraNoOutputDetector *)detector didDetectBlackCamera:(id<SCCapturer>)capture; |
|||
@end |
|||
|
|||
@interface SCBlackCameraNoOutputDetector : NSObject <SCManagedVideoDataSourceListener, SCManagedCapturerListener> |
|||
|
|||
@property (nonatomic, weak) id<SCBlackCameraDetectorDelegate> delegate; |
|||
- (instancetype)initWithReporter:(SCBlackCameraReporter *)reporter; |
|||
|
|||
@end |
@ -0,0 +1,137 @@ |
|||
// |
|||
// SCBlackCameraDetectorNoOutput.m |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 05/12/2017. |
|||
// |
|||
// This detector is used to detect the case that session is running, but there is no sample buffer output |
|||
|
|||
#import "SCBlackCameraNoOutputDetector.h" |
|||
|
|||
#import "SCBlackCameraReporter.h" |
|||
|
|||
#import <SCFoundation/SCAssertWrapper.h> |
|||
#import <SCFoundation/SCLog.h> |
|||
#import <SCFoundation/SCQueuePerformer.h> |
|||
#import <SCFoundation/SCTraceODPCompatible.h> |
|||
#import <SCFoundation/SCZeroDependencyExperiments.h> |
|||
#import <SCLogger/SCCameraMetrics.h> |
|||
#import <SCLogger/SCLogger.h> |
|||
|
|||
static CGFloat const kShortCheckingDelay = 0.5f; |
|||
static CGFloat const kLongCheckingDelay = 3.0f; |
|||
static char *const kSCBlackCameraDetectorQueueLabel = "com.snapchat.black-camera-detector"; |
|||
|
|||
@interface SCBlackCameraNoOutputDetector () { |
|||
BOOL _sampleBufferReceived; |
|||
BOOL _blackCameraDetected; |
|||
// Whether we receive first frame after we detected black camera, that's maybe because the checking delay is too |
|||
// short, and we will switch to kLongCheckingDelay next time we do the checking |
|||
BOOL _blackCameraRecovered; |
|||
// Whether checking is scheduled, to avoid duplicated checking |
|||
BOOL _checkingScheduled; |
|||
// Whether AVCaptureSession is stopped, if stopped, we don't need to check black camera any more |
|||
// It is set on main thread, read on background queue |
|||
BOOL _sessionStoppedRunning; |
|||
} |
|||
@property (nonatomic) SCQueuePerformer *queuePerformer; |
|||
@property (nonatomic) SCBlackCameraReporter *reporter; |
|||
@end |
|||
|
|||
@implementation SCBlackCameraNoOutputDetector |
|||
|
|||
- (instancetype)initWithReporter:(SCBlackCameraReporter *)reporter |
|||
{ |
|||
self = [super init]; |
|||
if (self) { |
|||
_queuePerformer = [[SCQueuePerformer alloc] initWithLabel:kSCBlackCameraDetectorQueueLabel |
|||
qualityOfService:QOS_CLASS_BACKGROUND |
|||
queueType:DISPATCH_QUEUE_SERIAL |
|||
context:SCQueuePerformerContextCamera]; |
|||
_reporter = reporter; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (void)managedVideoDataSource:(id<SCManagedVideoDataSource>)managedVideoDataSource |
|||
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer |
|||
devicePosition:(SCManagedCaptureDevicePosition)devicePosition |
|||
{ |
|||
// The block is very light-weight |
|||
[self.queuePerformer perform:^{ |
|||
if (_blackCameraDetected) { |
|||
// Detected a black camera case |
|||
_blackCameraDetected = NO; |
|||
_blackCameraRecovered = YES; |
|||
SCLogCoreCameraInfo(@"[BlackCamera] Black camera recovered"); |
|||
if (SCExperimentWithBlackCameraReporting()) { |
|||
[[SCLogger sharedInstance] logUnsampledEvent:KSCCameraBlackCamera |
|||
parameters:@{ |
|||
@"type" : @"RECOVERED" |
|||
} |
|||
secretParameters:nil |
|||
metrics:nil]; |
|||
} |
|||
} |
|||
|
|||
// Received buffer! |
|||
_sampleBufferReceived = YES; |
|||
}]; |
|||
} |
|||
|
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didStartRunning:(SCManagedCapturerState *)state |
|||
{ |
|||
SCAssertMainThread(); |
|||
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { |
|||
SCLogCoreCameraInfo(@"[BlackCamera] In background, skip checking"); |
|||
return; |
|||
} |
|||
_sessionStoppedRunning = NO; |
|||
[self.queuePerformer perform:^{ |
|||
SCTraceODPCompatibleStart(2); |
|||
if (_checkingScheduled) { |
|||
SCLogCoreCameraInfo(@"[BlackCamera] Checking is scheduled, skip"); |
|||
return; |
|||
} |
|||
if (_sessionStoppedRunning) { |
|||
SCLogCoreCameraInfo(@"[BlackCamera] AVCaptureSession stopped, should not check"); |
|||
return; |
|||
} |
|||
_sampleBufferReceived = NO; |
|||
if (_blackCameraRecovered) { |
|||
SCLogCoreCameraInfo(@"[BlackCamera] Last black camera recovered, let's wait longer to check this time"); |
|||
} |
|||
SCLogCoreCameraInfo(@"[BlackCamera] Schedule black camera checking"); |
|||
[self.queuePerformer perform:^{ |
|||
SCTraceODPCompatibleStart(2); |
|||
if (!_sessionStoppedRunning) { |
|||
if (!_sampleBufferReceived) { |
|||
_blackCameraDetected = YES; |
|||
[_reporter reportBlackCameraWithCause:SCBlackCameraNoOutputData]; |
|||
[self.delegate detector:self didDetectBlackCamera:managedCapturer]; |
|||
} else { |
|||
SCLogCoreCameraInfo(@"[BlackCamera] No black camera"); |
|||
_blackCameraDetected = NO; |
|||
} |
|||
} else { |
|||
SCLogCoreCameraInfo(@"[BlackCamera] AVCaptureSession stopped"); |
|||
_blackCameraDetected = NO; |
|||
} |
|||
_blackCameraRecovered = NO; |
|||
_checkingScheduled = NO; |
|||
} |
|||
after:_blackCameraRecovered ? kLongCheckingDelay : kShortCheckingDelay]; |
|||
_checkingScheduled = YES; |
|||
}]; |
|||
} |
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didStopRunning:(SCManagedCapturerState *)state |
|||
{ |
|||
SCAssertMainThread(); |
|||
_sessionStoppedRunning = YES; |
|||
[self.queuePerformer perform:^{ |
|||
SCTraceODPCompatibleStart(2); |
|||
_sampleBufferReceived = NO; |
|||
}]; |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,20 @@ |
|||
// |
|||
// SCBlackCameraPreviewDetector.h |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 25/01/2018. |
|||
// |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@class SCQueuePerformer, SCBlackCameraReporter; |
|||
@protocol SCManiphestTicketCreator; |
|||
|
|||
@interface SCBlackCameraPreviewDetector : NSObject |
|||
|
|||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter; |
|||
|
|||
- (void)sessionDidChangeIsRunning:(BOOL)running; |
|||
- (void)capturePreviewDidBecomeVisible:(BOOL)visible; |
|||
|
|||
@end |
@ -0,0 +1,92 @@ |
|||
// |
|||
// SCBlackCameraPreviewDetector.m |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 25/01/2018. |
|||
// |
|||
|
|||
#import "SCBlackCameraPreviewDetector.h" |
|||
|
|||
#import "SCBlackCameraReporter.h" |
|||
#import "SCMetalUtils.h" |
|||
|
|||
#import <SCCrashLogger/SCCrashLogger.h> |
|||
#import <SCFoundation/SCQueuePerformer.h> |
|||
#import <SCFoundation/SCThreadHelpers.h> |
|||
#import <SCFoundation/SCZeroDependencyExperiments.h> |
|||
|
|||
// Check whether preview is visible when AVCaptureSession is running |
|||
static CGFloat const kSCBlackCameraCheckingDelay = 0.5; |
|||
|
|||
@interface SCBlackCameraPreviewDetector () { |
|||
BOOL _previewVisible; |
|||
dispatch_block_t _checkingBlock; |
|||
} |
|||
@property (nonatomic) SCQueuePerformer *queuePerformer; |
|||
@property (nonatomic) SCBlackCameraReporter *reporter; |
|||
|
|||
@end |
|||
|
|||
@implementation SCBlackCameraPreviewDetector |
|||
|
|||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter |
|||
{ |
|||
self = [super init]; |
|||
if (self) { |
|||
_queuePerformer = performer; |
|||
_reporter = reporter; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (void)capturePreviewDidBecomeVisible:(BOOL)visible |
|||
{ |
|||
[_queuePerformer perform:^{ |
|||
_previewVisible = visible; |
|||
}]; |
|||
} |
|||
|
|||
- (void)sessionDidChangeIsRunning:(BOOL)running |
|||
{ |
|||
if (running) { |
|||
[self _scheduleCheck]; |
|||
} else { |
|||
[_queuePerformer perform:^{ |
|||
if (_checkingBlock) { |
|||
dispatch_block_cancel(_checkingBlock); |
|||
_checkingBlock = nil; |
|||
} |
|||
}]; |
|||
} |
|||
} |
|||
|
|||
- (void)_scheduleCheck |
|||
{ |
|||
[_queuePerformer perform:^{ |
|||
@weakify(self); |
|||
_checkingBlock = dispatch_block_create(0, ^{ |
|||
@strongify(self); |
|||
SC_GUARD_ELSE_RETURN(self); |
|||
self->_checkingBlock = nil; |
|||
[self _checkPreviewState]; |
|||
}); |
|||
[_queuePerformer perform:_checkingBlock after:kSCBlackCameraCheckingDelay]; |
|||
}]; |
|||
} |
|||
|
|||
- (void)_checkPreviewState |
|||
{ |
|||
if (!_previewVisible) { |
|||
runOnMainThreadAsynchronously(^{ |
|||
// Make sure the app is in foreground |
|||
SC_GUARD_ELSE_RETURN([UIApplication sharedApplication].applicationState == UIApplicationStateActive); |
|||
|
|||
SCBlackCameraCause cause = |
|||
SCDeviceSupportsMetal() ? SCBlackCameraRenderingPaused : SCBlackCameraPreviewIsHidden; |
|||
[_reporter reportBlackCameraWithCause:cause]; |
|||
[_reporter fileShakeTicketWithCause:cause]; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,35 @@ |
|||
// |
|||
// SCBlackCameraReporter.h |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 09/01/2018. |
|||
// |
|||
|
|||
#import <SCBase/SCMacros.h> |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
typedef NS_ENUM(NSInteger, SCBlackCameraCause) { |
|||
SCBlackCameraStartRunningNotCalled, // 1. View is visible, but session startRunning is not called |
|||
SCBlackCameraSessionNotRunning, // 2. Session startRunning is called, but isRunning is still false |
|||
SCBlackCameraRenderingPaused, // 3.1 View is visible, but capture preview rendering is paused |
|||
SCBlackCameraPreviewIsHidden, // 3.2 For non-metal devices, capture preview is hidden |
|||
SCBlackCameraSessionStartRunningBlocked, // 4.1 AVCaptureSession is blocked at startRunning |
|||
SCBlackCameraSessionConfigurationBlocked, // 4.2 AVCaptureSession is blocked at commitConfiguration |
|||
|
|||
SCBlackCameraNoOutputData, // 5. Session is running, but no data output |
|||
}; |
|||
|
|||
@protocol SCManiphestTicketCreator; |
|||
|
|||
@interface SCBlackCameraReporter : NSObject |
|||
|
|||
SC_INIT_AND_NEW_UNAVAILABLE |
|||
- (instancetype)initWithTicketCreator:(id<SCManiphestTicketCreator>)ticketCreator; |
|||
|
|||
- (NSString *)causeNameFor:(SCBlackCameraCause)cause; |
|||
|
|||
- (void)reportBlackCameraWithCause:(SCBlackCameraCause)cause; |
|||
- (void)fileShakeTicketWithCause:(SCBlackCameraCause)cause; |
|||
|
|||
@end |
@ -0,0 +1,86 @@ |
|||
// |
|||
// SCBlackCameraReporter.m |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 09/01/2018. |
|||
// |
|||
|
|||
#import "SCBlackCameraReporter.h" |
|||
|
|||
#import "SCManiphestTicketCreator.h" |
|||
|
|||
#import <SCFoundation/SCAssertWrapper.h> |
|||
#import <SCFoundation/SCLog.h> |
|||
#import <SCFoundation/SCLogHelper.h> |
|||
#import <SCFoundation/SCZeroDependencyExperiments.h> |
|||
#import <SCLogger/SCCameraMetrics.h> |
|||
#import <SCLogger/SCLogger.h> |
|||
|
|||
@interface SCBlackCameraReporter () |
|||
|
|||
@property (nonatomic) id<SCManiphestTicketCreator> ticketCreator; |
|||
|
|||
@end |
|||
|
|||
@implementation SCBlackCameraReporter |
|||
|
|||
- (instancetype)initWithTicketCreator:(id<SCManiphestTicketCreator>)ticketCreator |
|||
{ |
|||
if (self = [super init]) { |
|||
_ticketCreator = ticketCreator; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (NSString *)causeNameFor:(SCBlackCameraCause)cause |
|||
{ |
|||
switch (cause) { |
|||
case SCBlackCameraStartRunningNotCalled: |
|||
return @"StartRunningNotCalled"; |
|||
case SCBlackCameraSessionNotRunning: |
|||
return @"SessionNotRunning"; |
|||
case SCBlackCameraRenderingPaused: |
|||
return @"RenderingPause"; |
|||
case SCBlackCameraPreviewIsHidden: |
|||
return @"PreviewIsHidden"; |
|||
case SCBlackCameraSessionStartRunningBlocked: |
|||
return @"SessionStartRunningBlocked"; |
|||
case SCBlackCameraSessionConfigurationBlocked: |
|||
return @"SessionConfigurationBlocked"; |
|||
case SCBlackCameraNoOutputData: |
|||
return @"NoOutputData"; |
|||
default: |
|||
SCAssert(NO, @"illegate cause"); |
|||
break; |
|||
} |
|||
return nil; |
|||
} |
|||
|
|||
- (void)reportBlackCameraWithCause:(SCBlackCameraCause)cause |
|||
{ |
|||
NSString *causeStr = [self causeNameFor:cause]; |
|||
SCLogCoreCameraError(@"[BlackCamera] Detected black camera, cause: %@", causeStr); |
|||
|
|||
NSDictionary *parameters = @{ @"type" : @"DETECTED", @"cause" : causeStr }; |
|||
|
|||
[_ticketCreator createAndFileBetaReport:JSONStringSerializeObjectForLogging(parameters)]; |
|||
|
|||
if (SCExperimentWithBlackCameraReporting()) { |
|||
[[SCLogger sharedInstance] logUnsampledEvent:KSCCameraBlackCamera |
|||
parameters:parameters |
|||
secretParameters:nil |
|||
metrics:nil]; |
|||
} |
|||
} |
|||
|
|||
- (void)fileShakeTicketWithCause:(SCBlackCameraCause)cause |
|||
{ |
|||
if (SCExperimentWithBlackCameraExceptionLogging()) { |
|||
// Log exception with auto S2R |
|||
NSString *errMsg = |
|||
[NSString sc_stringWithFormat:@"[BlackCamera] Detected black camera, cause: %@", [self causeNameFor:cause]]; |
|||
[_ticketCreator createAndFile:nil creationTime:0 description:errMsg email:nil project:@"Camera" subproject:nil]; |
|||
} |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,27 @@ |
|||
// |
|||
// SCBlackCameraRunningDetector.h |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 30/01/2018. |
|||
// |
|||
|
|||
#import <SCBase/SCMacros.h> |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@class SCQueuePerformer, SCBlackCameraReporter; |
|||
@protocol SCManiphestTicketCreator; |
|||
|
|||
@interface SCBlackCameraRunningDetector : NSObject |
|||
|
|||
SC_INIT_AND_NEW_UNAVAILABLE |
|||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter; |
|||
|
|||
// When session isRunning changed |
|||
- (void)sessionDidChangeIsRunning:(BOOL)running; |
|||
// Call this after [AVCaptureSession startRunning] is called |
|||
- (void)sessionDidCallStartRunning; |
|||
// Call this before [AVCaptureSession stopRunning] is called |
|||
- (void)sessionWillCallStopRunning; |
|||
|
|||
@end |
@ -0,0 +1,84 @@ |
|||
// |
|||
// SCBlackCameraRunningDetector.m |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 30/01/2018. |
|||
// |
|||
|
|||
#import "SCBlackCameraRunningDetector.h" |
|||
|
|||
#import "SCBlackCameraReporter.h" |
|||
|
|||
#import <SCFoundation/SCAssertWrapper.h> |
|||
#import <SCFoundation/SCQueuePerformer.h> |
|||
#import <SCFoundation/SCTraceODPCompatible.h> |
|||
#import <SCLogger/SCCameraMetrics.h> |
|||
|
|||
// Check whether we called AVCaptureSession isRunning within this period |
|||
static CGFloat const kSCBlackCameraCheckingDelay = 5; |
|||
|
|||
@interface SCBlackCameraRunningDetector () { |
|||
BOOL _isSessionRunning; |
|||
dispatch_block_t _checkSessionBlock; |
|||
} |
|||
@property (nonatomic) SCQueuePerformer *queuePerformer; |
|||
@property (nonatomic) SCBlackCameraReporter *reporter; |
|||
@end |
|||
|
|||
@implementation SCBlackCameraRunningDetector |
|||
|
|||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter |
|||
{ |
|||
self = [super init]; |
|||
if (self) { |
|||
_queuePerformer = performer; |
|||
_reporter = reporter; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (void)sessionDidChangeIsRunning:(BOOL)running |
|||
{ |
|||
[_queuePerformer perform:^{ |
|||
_isSessionRunning = running; |
|||
}]; |
|||
} |
|||
|
|||
- (void)sessionDidCallStartRunning |
|||
{ |
|||
[self _scheduleCheck]; |
|||
} |
|||
|
|||
- (void)sessionWillCallStopRunning |
|||
{ |
|||
[_queuePerformer perform:^{ |
|||
if (_checkSessionBlock) { |
|||
dispatch_block_cancel(_checkSessionBlock); |
|||
_checkSessionBlock = nil; |
|||
} |
|||
}]; |
|||
} |
|||
|
|||
- (void)_scheduleCheck |
|||
{ |
|||
[_queuePerformer perform:^{ |
|||
@weakify(self); |
|||
_checkSessionBlock = dispatch_block_create(0, ^{ |
|||
@strongify(self); |
|||
SC_GUARD_ELSE_RETURN(self); |
|||
self->_checkSessionBlock = nil; |
|||
[self _checkSessionState]; |
|||
}); |
|||
|
|||
[_queuePerformer perform:_checkSessionBlock after:kSCBlackCameraCheckingDelay]; |
|||
}]; |
|||
} |
|||
|
|||
- (void)_checkSessionState |
|||
{ |
|||
if (!_isSessionRunning) { |
|||
[_reporter reportBlackCameraWithCause:SCBlackCameraSessionNotRunning]; |
|||
} |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,23 @@ |
|||
// |
|||
// SCBlackCameraSessionBlockDetector.h |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 25/01/2018. |
|||
// |
|||
|
|||
#import "SCBlackCameraReporter.h" |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@interface SCBlackCameraSessionBlockDetector : NSObject |
|||
|
|||
SC_INIT_AND_NEW_UNAVAILABLE |
|||
- (instancetype)initWithReporter:(SCBlackCameraReporter *)reporter; |
|||
|
|||
- (void)sessionWillCallStartRunning; |
|||
- (void)sessionDidCallStartRunning; |
|||
|
|||
- (void)sessionWillCommitConfiguration; |
|||
- (void)sessionDidCommitConfiguration; |
|||
|
|||
@end |
@ -0,0 +1,82 @@ |
|||
// |
|||
// SCBlackCameraSessionBlockDetector.m |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 25/01/2018. |
|||
// |
|||
|
|||
#import "SCBlackCameraSessionBlockDetector.h" |
|||
|
|||
#import "SCBlackCameraReporter.h" |
|||
|
|||
#import <SCLogger/SCCameraMetrics.h> |
|||
#import <SCLogger/SCLogger.h> |
|||
|
|||
@import CoreGraphics; |
|||
|
|||
// Longer than 5 seconds is considerred as black camera |
|||
static CGFloat const kSCBlackCameraBlockingThreshold = 5; |
|||
// Will report if session blocks longer than 1 second |
|||
static CGFloat const kSCSessionBlockingLogThreshold = 1; |
|||
|
|||
@interface SCBlackCameraSessionBlockDetector () { |
|||
NSTimeInterval _startTime; |
|||
} |
|||
@property (nonatomic) SCBlackCameraReporter *reporter; |
|||
|
|||
@end |
|||
|
|||
@implementation SCBlackCameraSessionBlockDetector |
|||
|
|||
- (instancetype)initWithReporter:(SCBlackCameraReporter *)reporter |
|||
{ |
|||
if (self = [super init]) { |
|||
_reporter = reporter; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (void)sessionWillCallStartRunning |
|||
{ |
|||
_startTime = [NSDate timeIntervalSinceReferenceDate]; |
|||
} |
|||
|
|||
- (void)sessionDidCallStartRunning |
|||
{ |
|||
[self _reportBlackCameraIfNeededWithCause:SCBlackCameraSessionStartRunningBlocked]; |
|||
[self _reportBlockingIfNeededWithCause:SCBlackCameraSessionStartRunningBlocked]; |
|||
} |
|||
|
|||
- (void)sessionWillCommitConfiguration |
|||
{ |
|||
_startTime = [NSDate timeIntervalSinceReferenceDate]; |
|||
} |
|||
|
|||
- (void)sessionDidCommitConfiguration |
|||
{ |
|||
[self _reportBlackCameraIfNeededWithCause:SCBlackCameraSessionConfigurationBlocked]; |
|||
[self _reportBlockingIfNeededWithCause:SCBlackCameraSessionConfigurationBlocked]; |
|||
} |
|||
|
|||
- (void)_reportBlockingIfNeededWithCause:(SCBlackCameraCause)cause |
|||
{ |
|||
NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - _startTime; |
|||
if (duration >= kSCSessionBlockingLogThreshold) { |
|||
NSString *causeStr = [_reporter causeNameFor:cause]; |
|||
[[SCLogger sharedInstance] logEvent:KSCCameraCaptureSessionBlocked |
|||
parameters:@{ |
|||
@"cause" : causeStr, |
|||
@"duration" : @(duration) |
|||
}]; |
|||
} |
|||
} |
|||
|
|||
- (void)_reportBlackCameraIfNeededWithCause:(SCBlackCameraCause)cause |
|||
{ |
|||
NSTimeInterval endTime = [NSDate timeIntervalSinceReferenceDate]; |
|||
if (endTime - _startTime >= kSCBlackCameraBlockingThreshold) { |
|||
[_reporter reportBlackCameraWithCause:cause]; |
|||
} |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,31 @@ |
|||
// |
|||
// SCBlackCameraDetectorCameraView.h |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 24/01/2018. |
|||
// |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
#import <UIKit/UIKit.h> |
|||
|
|||
@class SCQueuePerformer, SCBlackCameraReporter; |
|||
@protocol SCManiphestTicketCreator; |
|||
|
|||
@interface SCBlackCameraViewDetector : NSObject |
|||
|
|||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter; |
|||
|
|||
// CameraView visible/invisible |
|||
- (void)onCameraViewVisible:(BOOL)visible; |
|||
|
|||
- (void)onCameraViewVisibleWithTouch:(UIGestureRecognizer *)gesture; |
|||
|
|||
// Call this when [AVCaptureSession startRunning] is called |
|||
- (void)sessionWillCallStartRunning; |
|||
// Call this when [AVCaptureSession stopRunning] is called |
|||
- (void)sessionWillCallStopRunning; |
|||
|
|||
- (void)sessionWillRecreate; |
|||
- (void)sessionDidRecreate; |
|||
|
|||
@end |
@ -0,0 +1,136 @@ |
|||
// |
|||
// SCBlackCameraDetectorCameraView.m |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 24/01/2018. |
|||
// |
|||
|
|||
#import "SCBlackCameraViewDetector.h" |
|||
|
|||
#import "SCBlackCameraReporter.h" |
|||
#import "SCCaptureDeviceAuthorization.h" |
|||
|
|||
#import <SCFoundation/SCAssertWrapper.h> |
|||
#import <SCFoundation/SCLog.h> |
|||
#import <SCFoundation/SCQueuePerformer.h> |
|||
#import <SCFoundation/SCTraceODPCompatible.h> |
|||
#import <SCLogger/SCCameraMetrics.h> |
|||
|
|||
// Check whether we called [AVCaptureSession startRunning] within this period |
|||
static CGFloat const kSCBlackCameraCheckingDelay = 0.5; |
|||
|
|||
@interface SCBlackCameraViewDetector () { |
|||
BOOL _startRunningCalled; |
|||
BOOL _sessionIsRecreating; |
|||
dispatch_block_t _checkSessionBlock; |
|||
} |
|||
@property (nonatomic) SCQueuePerformer *queuePerformer; |
|||
@property (nonatomic) SCBlackCameraReporter *reporter; |
|||
@property (nonatomic, weak) UIGestureRecognizer *cameraViewGesture; |
|||
@end |
|||
|
|||
@implementation SCBlackCameraViewDetector |
|||
|
|||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter |
|||
{ |
|||
self = [super init]; |
|||
if (self) { |
|||
_queuePerformer = performer; |
|||
_reporter = reporter; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
#pragma mark - Camera view visibility change trigger |
|||
- (void)onCameraViewVisible:(BOOL)visible |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
SCLogCoreCameraInfo(@"[BlackCamera] onCameraViewVisible: %d", visible); |
|||
BOOL firstTimeAccess = [SCCaptureDeviceAuthorization notDeterminedForVideoCapture]; |
|||
if (firstTimeAccess) { |
|||
// We don't want to check black camera for firstTimeAccess |
|||
return; |
|||
} |
|||
// Visible and application is active |
|||
if (visible && [UIApplication sharedApplication].applicationState == UIApplicationStateActive) { |
|||
// Since this method is usually called before the view is actually visible, leave some margin to check |
|||
[self _scheduleCheckDelayed:YES]; |
|||
} else { |
|||
[_queuePerformer perform:^{ |
|||
if (_checkSessionBlock) { |
|||
dispatch_block_cancel(_checkSessionBlock); |
|||
_checkSessionBlock = nil; |
|||
} |
|||
}]; |
|||
} |
|||
} |
|||
|
|||
// Call this when [AVCaptureSession startRunning] is called |
|||
- (void)sessionWillCallStartRunning |
|||
{ |
|||
[_queuePerformer perform:^{ |
|||
_startRunningCalled = YES; |
|||
}]; |
|||
} |
|||
|
|||
- (void)sessionWillCallStopRunning |
|||
{ |
|||
[_queuePerformer perform:^{ |
|||
_startRunningCalled = NO; |
|||
}]; |
|||
} |
|||
|
|||
- (void)_scheduleCheckDelayed:(BOOL)delay |
|||
{ |
|||
[_queuePerformer perform:^{ |
|||
SC_GUARD_ELSE_RETURN(!_checkSessionBlock); |
|||
@weakify(self); |
|||
_checkSessionBlock = dispatch_block_create(0, ^{ |
|||
@strongify(self); |
|||
SC_GUARD_ELSE_RETURN(self); |
|||
self->_checkSessionBlock = nil; |
|||
[self _checkSessionState]; |
|||
}); |
|||
|
|||
if (delay) { |
|||
[_queuePerformer perform:_checkSessionBlock after:kSCBlackCameraCheckingDelay]; |
|||
} else { |
|||
[_queuePerformer perform:_checkSessionBlock]; |
|||
} |
|||
}]; |
|||
} |
|||
|
|||
- (void)_checkSessionState |
|||
{ |
|||
SCLogCoreCameraInfo(@"[BlackCamera] checkSessionState startRunning: %d, sessionIsRecreating: %d", |
|||
_startRunningCalled, _sessionIsRecreating); |
|||
if (!_startRunningCalled && !_sessionIsRecreating) { |
|||
[_reporter reportBlackCameraWithCause:SCBlackCameraStartRunningNotCalled]; |
|||
[_reporter fileShakeTicketWithCause:SCBlackCameraStartRunningNotCalled]; |
|||
} |
|||
} |
|||
|
|||
- (void)sessionWillRecreate |
|||
{ |
|||
[_queuePerformer perform:^{ |
|||
_sessionIsRecreating = YES; |
|||
}]; |
|||
} |
|||
|
|||
- (void)sessionDidRecreate |
|||
{ |
|||
[_queuePerformer perform:^{ |
|||
_sessionIsRecreating = NO; |
|||
}]; |
|||
} |
|||
|
|||
- (void)onCameraViewVisibleWithTouch:(UIGestureRecognizer *)gesture |
|||
{ |
|||
if (gesture != _cameraViewGesture) { |
|||
// Skip repeating gesture |
|||
self.cameraViewGesture = gesture; |
|||
[self _scheduleCheckDelayed:NO]; |
|||
} |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,14 @@ |
|||
// |
|||
// SCCaptureSessionFixer.h |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 05/12/2017. |
|||
// |
|||
|
|||
#import "SCBlackCameraNoOutputDetector.h" |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@interface SCCaptureSessionFixer : NSObject <SCBlackCameraDetectorDelegate> |
|||
|
|||
@end |
@ -0,0 +1,21 @@ |
|||
// |
|||
// SCCaptureSessionFixer.m |
|||
// Snapchat |
|||
// |
|||
// Created by Derek Wang on 05/12/2017. |
|||
// |
|||
|
|||
#import "SCCaptureSessionFixer.h" |
|||
|
|||
#import "SCCameraTweaks.h" |
|||
|
|||
@implementation SCCaptureSessionFixer |
|||
|
|||
- (void)detector:(SCBlackCameraNoOutputDetector *)detector didDetectBlackCamera:(id<SCCapturer>)capture |
|||
{ |
|||
if (SCCameraTweaksBlackCameraRecoveryEnabled()) { |
|||
[capture recreateAVCaptureSession]; |
|||
} |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,13 @@ |
|||
--- !OWNERS |
|||
|
|||
version: 2 |
|||
|
|||
default: |
|||
jira_project: CCAM |
|||
owners: |
|||
num_required_reviewers: 1 |
|||
teams: |
|||
- Snapchat/core-camera-ios |
|||
users: |
|||
- cjiang |
|||
- ljia |
@ -0,0 +1,16 @@ |
|||
// |
|||
// SCContextAwareSnapCreationThrottleRequest.h |
|||
// SCCamera |
|||
// |
|||
// Created by Cheng Jiang on 4/24/18. |
|||
// |
|||
|
|||
#import <SCFoundation/SCContextAwareThrottleRequester.h> |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@interface SCContextAwareSnapCreationThrottleRequest : NSObject <SCContextAwareThrottleRequest> |
|||
|
|||
- (instancetype)init; |
|||
|
|||
@end |
@ -0,0 +1,70 @@ |
|||
// |
|||
// SCContextAwareSnapCreationThrottleRequest.m |
|||
// SCCamera |
|||
// |
|||
// Created by Cheng Jiang on 4/24/18. |
|||
// |
|||
|
|||
#import "SCContextAwareSnapCreationThrottleRequest.h" |
|||
|
|||
#import <SCFoundation/SCAssertWrapper.h> |
|||
#import <SCFoundation/SCContextAwareTaskManagementResourceProvider.h> |
|||
#import <SCFoundation/SCZeroDependencyExperiments.h> |
|||
|
|||
#import <Tweaks/FBTweakInline.h> |
|||
|
|||
BOOL SCCATMSnapCreationEnabled(void) |
|||
{ |
|||
static dispatch_once_t capturingOnceToken; |
|||
static BOOL capturingImprovementEnabled; |
|||
dispatch_once(&capturingOnceToken, ^{ |
|||
BOOL enabledWithAB = SCExperimentWithContextAwareTaskManagementCapturingImprovementEnabled(); |
|||
NSInteger tweakOption = [FBTweakValue(@"CATM", @"Performance Improvement", @"Capturing", (id) @0, |
|||
(@{ @0 : @"Respect A/B", |
|||
@1 : @"YES", |
|||
@2 : @"NO" })) integerValue]; |
|||
switch (tweakOption) { |
|||
case 0: |
|||
capturingImprovementEnabled = enabledWithAB; |
|||
break; |
|||
case 1: |
|||
capturingImprovementEnabled = YES; |
|||
break; |
|||
case 2: |
|||
capturingImprovementEnabled = NO; |
|||
break; |
|||
default: |
|||
SCCAssertFail(@"Illegal option"); |
|||
} |
|||
}); |
|||
return capturingImprovementEnabled; |
|||
} |
|||
|
|||
@implementation SCContextAwareSnapCreationThrottleRequest { |
|||
NSString *_requestID; |
|||
} |
|||
|
|||
- (instancetype)init |
|||
{ |
|||
if (self = [super init]) { |
|||
_requestID = @"SCContextAwareSnapCreationThrottleRequest"; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (BOOL)shouldThrottle:(SCApplicationContextState)context |
|||
{ |
|||
return SCCATMSnapCreationEnabled() && context != SCApplicationContextStateCamera; |
|||
} |
|||
|
|||
- (NSString *)requestID |
|||
{ |
|||
return _requestID; |
|||
} |
|||
|
|||
- (BOOL)isEqual:(id<SCContextAwareThrottleRequest>)object |
|||
{ |
|||
return [[object requestID] isEqualToString:_requestID]; |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,22 @@ |
|||
// |
|||
// SCSnapCreationTriggers.h |
|||
// Snapchat |
|||
// |
|||
// Created by Cheng Jiang on 4/1/18. |
|||
// |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@interface SCSnapCreationTriggers : NSObject |
|||
|
|||
- (void)markSnapCreationStart; |
|||
|
|||
- (void)markSnapCreationPreviewAnimationFinish; |
|||
|
|||
- (void)markSnapCreationPreviewImageSetupFinish; |
|||
|
|||
- (void)markSnapCreationPreviewVideoFirstFrameRenderFinish; |
|||
|
|||
- (void)markSnapCreationEndWithContext:(NSString *)context; |
|||
|
|||
@end |
@ -0,0 +1,83 @@ |
|||
// |
|||
// SCSnapCreationTriggers.m |
|||
// Snapchat |
|||
// |
|||
// Created by Cheng Jiang on 3/30/18. |
|||
// |
|||
|
|||
#import "SCSnapCreationTriggers.h" |
|||
|
|||
#import "SCContextAwareSnapCreationThrottleRequest.h" |
|||
|
|||
#import <SCBase/SCMacros.h> |
|||
#import <SCFoundation/SCContextAwareThrottleRequester.h> |
|||
#import <SCFoundation/SCLog.h> |
|||
#import <SCFoundation/SCQueuePerformer.h> |
|||
|
|||
@implementation SCSnapCreationTriggers { |
|||
BOOL _snapCreationStarted; |
|||
BOOL _previewAnimationFinished; |
|||
BOOL _previewImageSetupFinished; |
|||
BOOL _previewVideoFirstFrameRendered; |
|||
} |
|||
|
|||
- (void)markSnapCreationStart |
|||
{ |
|||
SC_GUARD_ELSE_RUN_AND_RETURN( |
|||
!_snapCreationStarted, |
|||
SCLogCoreCameraWarning(@"markSnapCreationStart skipped because previous SnapCreation session is not complete")); |
|||
@synchronized(self) |
|||
{ |
|||
_snapCreationStarted = YES; |
|||
} |
|||
[[SCContextAwareThrottleRequester shared] submitSuspendRequest:[SCContextAwareSnapCreationThrottleRequest new]]; |
|||
} |
|||
|
|||
- (void)markSnapCreationPreviewAnimationFinish |
|||
{ |
|||
@synchronized(self) |
|||
{ |
|||
_previewAnimationFinished = YES; |
|||
if (_previewImageSetupFinished || _previewVideoFirstFrameRendered) { |
|||
[self markSnapCreationEndWithContext:@"markSnapCreationPreviewAnimationFinish"]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
- (void)markSnapCreationPreviewImageSetupFinish |
|||
{ |
|||
@synchronized(self) |
|||
{ |
|||
_previewImageSetupFinished = YES; |
|||
if (_previewAnimationFinished) { |
|||
[self markSnapCreationEndWithContext:@"markSnapCreationPreviewImageSetupFinish"]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
- (void)markSnapCreationPreviewVideoFirstFrameRenderFinish |
|||
{ |
|||
@synchronized(self) |
|||
{ |
|||
_previewVideoFirstFrameRendered = YES; |
|||
if (_previewAnimationFinished) { |
|||
[self markSnapCreationEndWithContext:@"markSnapCreationPreviewVideoFirstFrameRenderFinish"]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
- (void)markSnapCreationEndWithContext:(NSString *)context |
|||
{ |
|||
SC_GUARD_ELSE_RETURN(_snapCreationStarted); |
|||
SCLogCoreCameraInfo(@"markSnapCreationEnd triggered with context: %@", context); |
|||
@synchronized(self) |
|||
{ |
|||
_snapCreationStarted = NO; |
|||
_previewAnimationFinished = NO; |
|||
_previewImageSetupFinished = NO; |
|||
_previewVideoFirstFrameRendered = NO; |
|||
} |
|||
[[SCContextAwareThrottleRequester shared] submitResumeRequest:[SCContextAwareSnapCreationThrottleRequest new]]; |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,26 @@ |
|||
// |
|||
// SCFeature.h |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 1/4/18. |
|||
// |
|||
|
|||
#import <UIKit/UIKit.h> |
|||
|
|||
/** |
|||
* Top level protocol for UI features |
|||
*/ |
|||
#define SCLogCameraFeatureInfo(fmt, ...) SCLogCoreCameraInfo(@"[SCFeature] " fmt, ##__VA_ARGS__) |
|||
@protocol SCFeatureContainerView; |
|||
@protocol SCFeature <NSObject> |
|||
|
|||
@optional |
|||
- (void)configureWithView:(UIView<SCFeatureContainerView> *)view; |
|||
- (void)forwardCameraTimerGesture:(UIGestureRecognizer *)gestureRecognizer; |
|||
- (void)forwardCameraOverlayTapGesture:(UIGestureRecognizer *)gestureRecognizer; |
|||
- (void)forwardLongPressGesture:(UIGestureRecognizer *)gestureRecognizer; |
|||
- (void)forwardPinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer; |
|||
- (void)forwardPanGesture:(UIPanGestureRecognizer *)gestureRecognizer; |
|||
- (BOOL)shouldBlockTouchAtPoint:(CGPoint)point; |
|||
|
|||
@end |
@ -0,0 +1,13 @@ |
|||
// |
|||
// SCFeatureContainerView.h |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 4/17/18. |
|||
// |
|||
|
|||
#import <UIKit/UIKit.h> |
|||
|
|||
@protocol SCFeatureContainerView |
|||
- (BOOL)isTapGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer; |
|||
- (CGRect)initialCameraTimerFrame; |
|||
@end |
@ -0,0 +1,44 @@ |
|||
// |
|||
// SCFeatureCoordinator.h |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 1/4/18. |
|||
// |
|||
|
|||
#import "SCFeature.h" |
|||
|
|||
#import <SCBase/SCMacros.h> |
|||
|
|||
@protocol SCFeatureProvider; |
|||
@class SCCameraOverlayView; |
|||
|
|||
/** |
|||
* Handles creation of SCFeatures and communication between owner and features. |
|||
*/ |
|||
@interface SCFeatureCoordinator : NSObject |
|||
|
|||
SC_INIT_AND_NEW_UNAVAILABLE; |
|||
- (instancetype)initWithFeatureContainerView:(SCCameraOverlayView *)containerView |
|||
provider:(id<SCFeatureProvider>)provider; |
|||
|
|||
/** |
|||
* Asks provider for features with given featureTypes specified in initializer. |
|||
*/ |
|||
- (void)reloadFeatures; |
|||
|
|||
/** |
|||
* Eventually won't need this, but in order to use new framework w/ existing architecture, need a way to forward |
|||
* gestures to individual features. |
|||
*/ |
|||
- (void)forwardCameraTimerGesture:(UIGestureRecognizer *)gestureRecognizer; |
|||
- (void)forwardCameraOverlayTapGesture:(UIGestureRecognizer *)gestureRecognizer; |
|||
- (void)forwardLongPressGesture:(UIGestureRecognizer *)gestureRecognizer; |
|||
- (void)forwardPinchGesture:(UIPinchGestureRecognizer *)recognizer; |
|||
- (void)forwardPanGesture:(UIPanGestureRecognizer *)recognizer; |
|||
/** |
|||
* To prevent gestures on AVCameraViewController from triggering at the same time as feature controls, need to provide a |
|||
* way for features to indicate that they will block a touch with given point. |
|||
*/ |
|||
- (BOOL)shouldBlockTouchAtPoint:(CGPoint)point; |
|||
|
|||
@end |
@ -0,0 +1,117 @@ |
|||
// |
|||
// SCFeatureCoordinator.m |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 1/4/18. |
|||
// |
|||
|
|||
#import "SCFeatureCoordinator.h" |
|||
|
|||
#import "SCFeature.h" |
|||
#import "SCFeatureProvider.h" |
|||
|
|||
#import <SCFoundation/SCAssertWrapper.h> |
|||
#import <SCFoundation/SCTraceODPCompatible.h> |
|||
|
|||
typedef NSString SCFeatureDictionaryKey; |
|||
|
|||
@interface SCFeatureCoordinator () |
|||
@property (nonatomic, weak) UIView<SCFeatureContainerView> *containerView; |
|||
@property (nonatomic, strong) id<SCFeatureProvider> provider; |
|||
@end |
|||
|
|||
@implementation SCFeatureCoordinator |
|||
|
|||
- (instancetype)initWithFeatureContainerView:(UIView<SCFeatureContainerView> *)containerView |
|||
provider:(id<SCFeatureProvider>)provider |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
SCAssert(containerView, @"SCFeatureCoordinator containerView must be non-nil"); |
|||
SCAssert(provider, @"SCFeatureCoordinator provider must be non-nil"); |
|||
self = [super init]; |
|||
if (self) { |
|||
_containerView = containerView; |
|||
_provider = provider; |
|||
[self reloadFeatures]; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (void)reloadFeatures |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
[_provider resetInstances]; |
|||
NSMutableArray *features = [NSMutableArray array]; |
|||
for (id<SCFeature> feature in _provider.supportedFeatures) { |
|||
if ([feature respondsToSelector:@selector(configureWithView:)]) { |
|||
[feature configureWithView:_containerView]; |
|||
} |
|||
if (feature) { |
|||
[features addObject:feature]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
- (void)forwardCameraTimerGesture:(UIGestureRecognizer *)gestureRecognizer |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
for (id<SCFeature> feature in _provider.supportedFeatures) { |
|||
if ([feature respondsToSelector:@selector(forwardCameraTimerGesture:)]) { |
|||
[feature forwardCameraTimerGesture:gestureRecognizer]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
- (void)forwardCameraOverlayTapGesture:(UIGestureRecognizer *)gestureRecognizer |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
for (id<SCFeature> feature in _provider.supportedFeatures) { |
|||
if ([feature respondsToSelector:@selector(forwardCameraOverlayTapGesture:)]) { |
|||
[feature forwardCameraOverlayTapGesture:gestureRecognizer]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
- (void)forwardLongPressGesture:(UIGestureRecognizer *)gestureRecognizer |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
for (id<SCFeature> feature in _provider.supportedFeatures) { |
|||
if ([feature respondsToSelector:@selector(forwardLongPressGesture:)]) { |
|||
[feature forwardLongPressGesture:gestureRecognizer]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
- (void)forwardPinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
for (id<SCFeature> feature in _provider.supportedFeatures) { |
|||
if ([feature respondsToSelector:@selector(forwardPinchGesture:)]) { |
|||
[feature forwardPinchGesture:gestureRecognizer]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
- (void)forwardPanGesture:(UIPanGestureRecognizer *)gestureRecognizer |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
for (id<SCFeature> feature in _provider.supportedFeatures) { |
|||
if ([feature respondsToSelector:@selector(forwardPanGesture:)]) { |
|||
[feature forwardPanGesture:gestureRecognizer]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
- (BOOL)shouldBlockTouchAtPoint:(CGPoint)point |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
for (id<SCFeature> feature in _provider.supportedFeatures) { |
|||
if ([feature respondsToSelector:@selector(shouldBlockTouchAtPoint:)] && |
|||
[feature shouldBlockTouchAtPoint:point]) { |
|||
return YES; |
|||
} |
|||
} |
|||
return NO; |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,50 @@ |
|||
// |
|||
// SCFeatureProvider.h |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 1/4/18. |
|||
// |
|||
|
|||
#import <SCCamera/AVCameraViewEnums.h> |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@class SCFeatureSettingsManager, SCCapturerToken, SCUserSession; |
|||
|
|||
@protocol SCFeature |
|||
, SCCapturer, SCFeatureFlash, SCFeatureHandsFree, SCFeatureLensSideButton, SCFeatureLensButtonZ, SCFeatureMemories, |
|||
SCFeatureNightMode, SCFeatureSnapKit, SCFeatureTapToFocusAndExposure, SCFeatureToggleCamera, SCFeatureShazam, |
|||
SCFeatureImageCapture, SCFeatureScanning, SCFeatureZooming; |
|||
|
|||
/** |
|||
* Provides single location for creating and configuring SCFeatures. |
|||
*/ |
|||
@protocol SCFeatureProvider <NSObject> |
|||
|
|||
@property (nonatomic) AVCameraViewType cameraViewType; |
|||
|
|||
@property (nonatomic, readonly) id<SCCapturer> capturer; |
|||
@property (nonatomic, strong, readwrite) SCCapturerToken *token; |
|||
@property (nonatomic, readonly) SCUserSession *userSession; |
|||
// TODO: We should not be reusing AVCameraViewController so eventually the |
|||
// context should be removed. |
|||
@property (nonatomic, readonly) AVCameraViewControllerContext context; |
|||
@property (nonatomic) id<SCFeatureHandsFree> handsFreeRecording; |
|||
@property (nonatomic) id<SCFeatureSnapKit> snapKit; |
|||
@property (nonatomic) id<SCFeatureTapToFocusAndExposure> tapToFocusAndExposure; |
|||
@property (nonatomic) id<SCFeatureMemories> memories; |
|||
@property (nonatomic) id<SCFeatureFlash> flash; |
|||
@property (nonatomic) id<SCFeatureLensSideButton> lensSideButton; |
|||
@property (nonatomic) id<SCFeatureLensButtonZ> lensZButton; |
|||
@property (nonatomic) id<SCFeatureNightMode> nightMode; |
|||
@property (nonatomic) id<SCFeatureToggleCamera> toggleCamera; |
|||
@property (nonatomic) id<SCFeatureShazam> shazam; |
|||
@property (nonatomic) id<SCFeatureScanning> scanning; |
|||
@property (nonatomic) id<SCFeatureImageCapture> imageCapture; |
|||
@property (nonatomic) id<SCFeatureZooming> zooming; |
|||
|
|||
@property (nonatomic, readonly) NSArray<id<SCFeature>> *supportedFeatures; |
|||
|
|||
- (void)resetInstances; |
|||
|
|||
@end |
@ -0,0 +1,20 @@ |
|||
// |
|||
// SCFeatureFlash.h |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 3/27/18. |
|||
// |
|||
|
|||
#import "SCFeature.h" |
|||
|
|||
@class SCNavigationBarButtonItem; |
|||
|
|||
/** |
|||
* Public interface for interacting with camera flash feature. |
|||
*/ |
|||
@protocol SCFeatureFlash <SCFeature> |
|||
@property (nonatomic, readonly) SCNavigationBarButtonItem *navigationBarButtonItem; |
|||
|
|||
- (void)interruptGestures; |
|||
|
|||
@end |
@ -0,0 +1,23 @@ |
|||
// |
|||
// SCFeatureFlashImpl.h |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 3/27/18. |
|||
// |
|||
|
|||
#import "SCFeatureFlash.h" |
|||
|
|||
#import <SCBase/SCMacros.h> |
|||
|
|||
@class SCLogger; |
|||
@protocol SCCapturer; |
|||
|
|||
/** |
|||
* Interface for camera flash feature. Handles enabling/disabling of camera flash via SCCapturer and UI for displaying |
|||
* flash button. |
|||
* Should only expose initializer. All other vars and methods should be declared in SCFeatureFlash protocol. |
|||
*/ |
|||
@interface SCFeatureFlashImpl : NSObject <SCFeatureFlash> |
|||
SC_INIT_AND_NEW_UNAVAILABLE |
|||
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer logger:(SCLogger *)logger NS_DESIGNATED_INITIALIZER; |
|||
@end |
@ -0,0 +1,226 @@ |
|||
// |
|||
// SCFeatureFlashImpl.m |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 3/27/18. |
|||
// |
|||
|
|||
#import "SCFeatureFlashImpl.h" |
|||
|
|||
#import "SCCapturer.h" |
|||
#import "SCFlashButton.h" |
|||
#import "SCManagedCapturerListener.h" |
|||
#import "SCManagedCapturerState.h" |
|||
|
|||
#import <SCFoundation/SCLocale.h> |
|||
#import <SCFoundation/SCLog.h> |
|||
#import <SCFoundation/SCTraceODPCompatible.h> |
|||
#import <SCLogger/SCLogger.h> |
|||
#import <SCUIKit/SCNavigationBarButtonItem.h> |
|||
|
|||
static CGFloat const kSCFlashButtonInsets = -2.f; |
|||
static CGRect const kSCFlashButtonFrame = {0, 0, 36, 44}; |
|||
|
|||
static NSString *const kSCFlashEventName = @"TOGGLE_CAMERA_FLASH_BUTTON"; |
|||
static NSString *const kSCFlashEventParameterFlashName = @"flash_on"; |
|||
static NSString *const kSCFlashEventParameterCameraName = @"front_facing_camera_on"; |
|||
|
|||
@interface SCFeatureFlashImpl () |
|||
@property (nonatomic, strong, readwrite) id<SCCapturer> capturer; |
|||
@property (nonatomic, strong, readwrite) SCLogger *logger; |
|||
@property (nonatomic, strong, readwrite) SCFlashButton *flashButton; |
|||
@property (nonatomic, weak, readwrite) UIView<SCFeatureContainerView> *containerView; |
|||
@property (nonatomic, strong, readwrite) SCManagedCapturerState *managedCapturerState; |
|||
@property (nonatomic, assign, readwrite) BOOL canEnable; |
|||
@end |
|||
|
|||
@interface SCFeatureFlashImpl (SCManagedCapturerListener) <SCManagedCapturerListener> |
|||
@end |
|||
|
|||
@implementation SCFeatureFlashImpl |
|||
@synthesize navigationBarButtonItem = _navigationBarButtonItem; |
|||
|
|||
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer logger:(SCLogger *)logger |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
self = [super init]; |
|||
if (self) { |
|||
_capturer = capturer; |
|||
[_capturer addListener:self]; |
|||
_logger = logger; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (void)dealloc |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
[_capturer removeListener:self]; |
|||
} |
|||
|
|||
#pragma mark - SCFeature |
|||
|
|||
- (void)configureWithView:(UIView<SCFeatureContainerView> *)view |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
_containerView = view; |
|||
} |
|||
|
|||
- (BOOL)shouldBlockTouchAtPoint:(CGPoint)point |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
SC_GUARD_ELSE_RETURN_VALUE(_flashButton.userInteractionEnabled && !_flashButton.hidden, NO); |
|||
CGPoint convertedPoint = [_flashButton convertPoint:point fromView:_containerView]; |
|||
return [_flashButton pointInside:convertedPoint withEvent:nil]; |
|||
} |
|||
|
|||
#pragma mark - SCFeatureFlash |
|||
|
|||
- (void)interruptGestures |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
[_flashButton interruptGestures]; |
|||
} |
|||
|
|||
- (SCNavigationBarButtonItem *)navigationBarButtonItem |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
SC_GUARD_ELSE_RETURN_VALUE(!_navigationBarButtonItem, _navigationBarButtonItem); |
|||
_navigationBarButtonItem = [[SCNavigationBarButtonItem alloc] initWithCustomView:self.flashButton]; |
|||
return _navigationBarButtonItem; |
|||
} |
|||
|
|||
#pragma mark - Getters |
|||
|
|||
- (SCFlashButton *)flashButton |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
SC_GUARD_ELSE_RETURN_VALUE(!_flashButton, _flashButton); |
|||
_flashButton = [[SCFlashButton alloc] initWithFrame:kSCFlashButtonFrame]; |
|||
_flashButton.layer.sublayerTransform = CATransform3DMakeTranslation(kSCFlashButtonInsets, 0, 0); |
|||
_flashButton.buttonState = SCFlashButtonStateOff; |
|||
_flashButton.maximumScale = 1.1111f; |
|||
[_flashButton addTarget:self action:@selector(_flashTapped)]; |
|||
|
|||
_flashButton.accessibilityIdentifier = @"flash"; |
|||
_flashButton.accessibilityLabel = SCLocalizedString(@"flash", 0); |
|||
return _flashButton; |
|||
} |
|||
|
|||
#pragma mark - Setters |
|||
|
|||
- (void)setCanEnable:(BOOL)canEnable |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
SCLogCameraFeatureInfo(@"[%@] setCanEnable new: %@ old: %@", NSStringFromClass([self class]), |
|||
canEnable ? @"YES" : @"NO", _canEnable ? @"YES" : @"NO"); |
|||
self.flashButton.userInteractionEnabled = canEnable; |
|||
} |
|||
|
|||
#pragma mark - Internal Helpers |
|||
|
|||
- (void)_flashTapped |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
BOOL flashActive = !_managedCapturerState.flashActive; |
|||
|
|||
SCLogCameraFeatureInfo(@"[%@] _flashTapped flashActive new: %@ old: %@", NSStringFromClass([self class]), |
|||
flashActive ? @"YES" : @"NO", !flashActive ? @"YES" : @"NO"); |
|||
_containerView.userInteractionEnabled = NO; |
|||
@weakify(self); |
|||
[_capturer setFlashActive:flashActive |
|||
completionHandler:^{ |
|||
@strongify(self); |
|||
SCLogCameraFeatureInfo(@"[%@] _flashTapped setFlashActive completion", NSStringFromClass([self class])); |
|||
self.containerView.userInteractionEnabled = YES; |
|||
} |
|||
context:SCCapturerContext]; |
|||
|
|||
NSDictionary *loggingParameters = @{ |
|||
kSCFlashEventParameterFlashName : @(flashActive), |
|||
kSCFlashEventParameterCameraName : |
|||
@(_managedCapturerState.devicePosition == SCManagedCaptureDevicePositionFront) |
|||
}; |
|||
[_logger logEvent:kSCFlashEventName parameters:loggingParameters]; |
|||
} |
|||
|
|||
- (BOOL)_shouldHideForState:(SCManagedCapturerState *)state |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
return (!state.flashSupported && !state.torchSupported && |
|||
state.devicePosition != SCManagedCaptureDevicePositionFront) || |
|||
state.arSessionActive; |
|||
} |
|||
|
|||
@end |
|||
|
|||
@implementation SCFeatureFlashImpl (SCManagedCapturerListener) |
|||
|
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeFlashActive:(SCManagedCapturerState *)state |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
SCLogCameraFeatureInfo(@"[%@] didChangeFlashActive flashActive: %@", NSStringFromClass([self class]), |
|||
state.flashActive ? @"YES" : @"NO"); |
|||
self.flashButton.buttonState = state.flashActive ? SCFlashButtonStateOn : SCFlashButtonStateOff; |
|||
} |
|||
|
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer |
|||
didChangeFlashSupportedAndTorchSupported:(SCManagedCapturerState *)state |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
SCLogCameraFeatureInfo( |
|||
@"[%@] didChangeFlashSupportedAndTorchSupported flashSupported: %@ torchSupported: %@ devicePosition: %@", |
|||
NSStringFromClass([self class]), state.flashSupported ? @"YES" : @"NO", state.torchSupported ? @"YES" : @"NO", |
|||
state.devicePosition == SCManagedCaptureDevicePositionFront ? @"front" : @"back"); |
|||
self.flashButton.hidden = [self _shouldHideForState:state]; |
|||
} |
|||
|
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeState:(SCManagedCapturerState *)state |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
_managedCapturerState = [state copy]; |
|||
} |
|||
|
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeARSessionActive:(SCManagedCapturerState *)state |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
SCLogCameraFeatureInfo(@"[%@] didChangeARSessionActive: %@", NSStringFromClass([self class]), |
|||
state.arSessionActive ? @"YES" : @"NO"); |
|||
self.flashButton.hidden = [self _shouldHideForState:state]; |
|||
} |
|||
|
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer |
|||
didBeginVideoRecording:(SCManagedCapturerState *)state |
|||
session:(SCVideoCaptureSessionInfo)session |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
self.canEnable = NO; |
|||
} |
|||
|
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer |
|||
didFinishRecording:(SCManagedCapturerState *)state |
|||
session:(SCVideoCaptureSessionInfo)session |
|||
recordedVideo:(SCManagedRecordedVideo *)recordedVideo |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
self.canEnable = YES; |
|||
} |
|||
|
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer |
|||
didFailRecording:(SCManagedCapturerState *)state |
|||
session:(SCVideoCaptureSessionInfo)session |
|||
error:(NSError *)error |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
self.canEnable = YES; |
|||
} |
|||
|
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer |
|||
didCancelRecording:(SCManagedCapturerState *)state |
|||
session:(SCVideoCaptureSessionInfo)session |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
self.canEnable = YES; |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,15 @@ |
|||
// |
|||
// SCFlashButton.h |
|||
// SCCamera |
|||
// |
|||
// Created by Will Wu on 2/13/14. |
|||
// Copyright (c) 2014 Snapchat, Inc. All rights reserved. |
|||
// |
|||
|
|||
#import <SCUIKit/SCGrowingButton.h> |
|||
|
|||
typedef NS_ENUM(NSInteger, SCFlashButtonState) { SCFlashButtonStateOn = 0, SCFlashButtonStateOff = 1 }; |
|||
|
|||
@interface SCFlashButton : SCGrowingButton |
|||
@property (nonatomic, assign) SCFlashButtonState buttonState; |
|||
@end |
@ -0,0 +1,35 @@ |
|||
// |
|||
// SCFlashButton.m |
|||
// SCCamera |
|||
// |
|||
// Created by Will Wu on 2/13/14. |
|||
// Copyright (c) 2014 Snapchat, Inc. All rights reserved. |
|||
// |
|||
|
|||
#import "SCFlashButton.h" |
|||
|
|||
#import <SCUIKit/SCPixelRounding.h> |
|||
|
|||
@implementation SCFlashButton |
|||
|
|||
- (void)setButtonState:(SCFlashButtonState)buttonState |
|||
{ |
|||
// Don't reset flash button state if it doesn't change. |
|||
if (_buttonState == buttonState) { |
|||
return; |
|||
} |
|||
_buttonState = buttonState; |
|||
|
|||
if (buttonState == SCFlashButtonStateOn) { |
|||
self.image = [UIImage imageNamed:@"camera_flash_on_v10"]; |
|||
self.accessibilityValue = @"on"; |
|||
} else { |
|||
self.image = [UIImage imageNamed:@"camera_flash_off_v10"]; |
|||
self.accessibilityValue = @"off"; |
|||
} |
|||
|
|||
self.imageInset = SCRoundSizeToPixels(CGSizeMake((CGRectGetWidth(self.bounds) - self.image.size.width) / 2, |
|||
(CGRectGetHeight(self.bounds) - self.image.size.height) / 2)); |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,30 @@ |
|||
// |
|||
// SCFeatureHandsFree.h |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 2/26/18. |
|||
// |
|||
|
|||
#import "SCFeature.h" |
|||
|
|||
#import <SCCamera/AVCameraViewEnums.h> |
|||
|
|||
@class SCLongPressGestureRecognizer, SCPreviewPresenter; |
|||
|
|||
@protocol SCFeatureHandsFree <SCFeature> |
|||
@property (nonatomic, weak) SCPreviewPresenter *previewPresenter; |
|||
@property (nonatomic, strong, readonly) SCLongPressGestureRecognizer *longPressGestureRecognizer; |
|||
|
|||
/** |
|||
* Whether the feature is enabled or not. |
|||
*/ |
|||
@property (nonatomic) BOOL enabled; |
|||
- (void)setupRecordLifecycleEventsWithMethod:(SCCameraRecordingMethod)method; |
|||
- (BOOL)shouldDisplayMultiSnapTooltip; |
|||
|
|||
/** |
|||
* Block called when user cancels hands-free recording via X button. |
|||
*/ |
|||
- (void)setCancelBlock:(dispatch_block_t)cancelBlock; |
|||
|
|||
@end |
@ -0,0 +1,27 @@ |
|||
// |
|||
// SCFeatureImageCapture.h |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 4/18/18. |
|||
// |
|||
|
|||
#import "SCFeature.h" |
|||
|
|||
#import <SCFoundation/SCFuture.h> |
|||
|
|||
@protocol SCFeatureImageCapture; |
|||
|
|||
@protocol SCFeatureImageCaptureDelegate <NSObject> |
|||
- (void)featureImageCapture:(id<SCFeatureImageCapture>)featureImageCapture willCompleteWithImage:(UIImage *)image; |
|||
- (void)featureImageCapture:(id<SCFeatureImageCapture>)featureImageCapture didCompleteWithError:(NSError *)error; |
|||
- (void)featureImageCapturedDidComplete:(id<SCFeatureImageCapture>)featureImageCapture; |
|||
@end |
|||
|
|||
/** |
|||
SCFeature protocol for capturing an image. |
|||
*/ |
|||
@protocol SCFeatureImageCapture <SCFeature> |
|||
@property (nonatomic, weak, readwrite) id<SCFeatureImageCaptureDelegate> delegate; |
|||
@property (nonatomic, strong, readonly) SCPromise<UIImage *> *imagePromise; |
|||
- (void)captureImage:(NSString *)captureSessionID; |
|||
@end |
@ -0,0 +1,21 @@ |
|||
// |
|||
// SCFeatureImageCaptureImpl.h |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 4/18/18. |
|||
// |
|||
|
|||
#import "AVCameraViewEnums.h" |
|||
#import "SCFeatureImageCapture.h" |
|||
|
|||
#import <SCBase/SCMacros.h> |
|||
|
|||
@protocol SCCapturer; |
|||
@class SCLogger; |
|||
|
|||
@interface SCFeatureImageCaptureImpl : NSObject <SCFeatureImageCapture> |
|||
SC_INIT_AND_NEW_UNAVAILABLE |
|||
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer |
|||
logger:(SCLogger *)logger |
|||
cameraViewType:(AVCameraViewType)cameraViewType NS_DESIGNATED_INITIALIZER; |
|||
@end |
@ -0,0 +1,184 @@ |
|||
// |
|||
// SCFeatureImageCaptureImpl.m |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 4/18/18. |
|||
// |
|||
|
|||
#import "SCFeatureImageCaptureImpl.h" |
|||
|
|||
#import "SCLogger+Camera.h" |
|||
#import "SCManagedCapturePreviewLayerController.h" |
|||
#import "SCManagedCapturerLensAPI.h" |
|||
#import "SCManagedCapturerListener.h" |
|||
#import "SCManagedCapturerUtils.h" |
|||
#import "SCManagedStillImageCapturer.h" |
|||
|
|||
#import <SCFoundation/SCDeviceName.h> |
|||
#import <SCFoundation/SCLog.h> |
|||
#import <SCFoundation/SCQueuePerformer.h> |
|||
#import <SCFoundation/SCTraceODPCompatible.h> |
|||
#import <SCGhostToSnappable/SCGhostToSnappableSignal.h> |
|||
#import <SCLogger/SCCameraMetrics.h> |
|||
#import <SCLogger/SCLogger+Performance.h> |
|||
|
|||
@interface SCFeatureImageCaptureImpl () |
|||
@property (nonatomic, strong, readwrite) id<SCCapturer> capturer; |
|||
@property (nonatomic, strong, readwrite) SCLogger *logger; |
|||
@property (nonatomic, assign) AVCameraViewType cameraViewType; |
|||
@property (nonatomic, strong, readwrite) SCManagedCapturerState *managedCapturerState; |
|||
|
|||
/** |
|||
* Whether user has attempted image capture in current session. Reset on foreground of app. |
|||
*/ |
|||
@property (nonatomic, assign) BOOL hasTriedCapturing; |
|||
@end |
|||
|
|||
@interface SCFeatureImageCaptureImpl (SCManagedCapturerListener) <SCManagedCapturerListener> |
|||
@end |
|||
|
|||
@implementation SCFeatureImageCaptureImpl |
|||
@synthesize delegate = _delegate; |
|||
@synthesize imagePromise = _imagePromise; |
|||
|
|||
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer |
|||
logger:(SCLogger *)logger |
|||
cameraViewType:(AVCameraViewType)cameraViewType |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
self = [super init]; |
|||
if (self) { |
|||
_capturer = capturer; |
|||
[_capturer addListener:self]; |
|||
_logger = logger; |
|||
_cameraViewType = cameraViewType; |
|||
|
|||
[[NSNotificationCenter defaultCenter] addObserver:self |
|||
selector:@selector(_viewWillEnterForeground) |
|||
name:UIApplicationWillEnterForegroundNotification |
|||
object:nil]; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (void)dealloc |
|||
{ |
|||
[_capturer removeListener:self]; |
|||
} |
|||
|
|||
#pragma mark - SCFeatureImageCapture |
|||
|
|||
- (void)captureImage:(NSString *)captureSessionID |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
[_logger logTimedEventStart:kSCCameraMetricsRecordingDelay uniqueId:@"IMAGE" isUniqueEvent:NO]; |
|||
BOOL asyncCaptureEnabled = [self _asynchronousCaptureEnabled:_managedCapturerState]; |
|||
SCLogCameraFeatureInfo(@"[%@] takeImage begin async: %@", NSStringFromClass([self class]), |
|||
asyncCaptureEnabled ? @"YES" : @"NO"); |
|||
|
|||
if (asyncCaptureEnabled) { |
|||
SCQueuePerformer *performer = [[SCQueuePerformer alloc] initWithLabel:"com.snapchat.image-capture-promise" |
|||
qualityOfService:QOS_CLASS_USER_INTERACTIVE |
|||
queueType:DISPATCH_QUEUE_SERIAL |
|||
context:SCQueuePerformerContextCoreCamera]; |
|||
_imagePromise = [[SCPromise alloc] initWithPerformer:performer]; |
|||
} |
|||
|
|||
@weakify(self); |
|||
[_capturer captureStillImageAsynchronouslyWithAspectRatio:SCManagedCapturedImageAndVideoAspectRatio() |
|||
captureSessionID:captureSessionID |
|||
completionHandler:^(UIImage *fullScreenImage, NSDictionary *metadata, |
|||
NSError *error, SCManagedCapturerState *state) { |
|||
@strongify(self); |
|||
SC_GUARD_ELSE_RETURN(self); |
|||
[self _takeImageCallback:fullScreenImage |
|||
metadata:metadata |
|||
error:error |
|||
state:state]; |
|||
} |
|||
context:SCCapturerContext]; |
|||
[_logger logCameraCaptureFinishedWithDuration:0]; |
|||
} |
|||
|
|||
#pragma mark - Private |
|||
|
|||
- (void)_viewWillEnterForeground |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
_hasTriedCapturing = NO; |
|||
} |
|||
|
|||
- (void)_takeImageCallback:(UIImage *)image |
|||
metadata:(NSDictionary *)metadata |
|||
error:(NSError *)error |
|||
state:(SCManagedCapturerState *)state |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
[self _logCaptureComplete:state]; |
|||
|
|||
if (image) { |
|||
[_delegate featureImageCapture:self willCompleteWithImage:image]; |
|||
if (_imagePromise) { |
|||
[_imagePromise completeWithValue:image]; |
|||
} |
|||
} else { |
|||
if (_imagePromise) { |
|||
[_imagePromise completeWithError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]]; |
|||
} |
|||
[_delegate featureImageCapture:self didCompleteWithError:error]; |
|||
} |
|||
_imagePromise = nil; |
|||
[_delegate featureImageCapturedDidComplete:self]; |
|||
} |
|||
|
|||
- (BOOL)_asynchronousCaptureEnabled:(SCManagedCapturerState *)state |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
BOOL shouldCaptureImageFromVideoBuffer = |
|||
[SCDeviceName isSimilarToIphone5orNewer] && ![SCDeviceName isSimilarToIphone6orNewer]; |
|||
// Fast image capture is disabled in following cases |
|||
// (1) flash is on; |
|||
// (2) lenses are active; |
|||
// (3) SCPhotoCapturer is not supported; |
|||
// (4) not main camera for iPhoneX; |
|||
return !state.flashActive && !state.lensesActive && !_capturer.lensProcessingCore.appliedLens && |
|||
(SCPhotoCapturerIsEnabled() || shouldCaptureImageFromVideoBuffer) && |
|||
(![SCDeviceName isIphoneX] || (_cameraViewType == AVCameraViewNoReply)); |
|||
} |
|||
|
|||
- (void)_logCaptureComplete:(SCManagedCapturerState *)state |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
NSDictionary *params = @{ |
|||
@"type" : @"image", |
|||
@"lenses_active" : @(state.lensesActive), |
|||
@"is_back_camera" : @(state.devicePosition != SCManagedCaptureDevicePositionFront), |
|||
@"is_main_camera" : @(_cameraViewType == AVCameraViewNoReply), |
|||
@"is_first_attempt_after_app_startup" : @(!_hasTriedCapturing), |
|||
@"app_startup_type" : SCLaunchType(), |
|||
@"app_startup_time" : @(SCAppStartupTimeMicros() / 1000.0), |
|||
@"time_elapse_after_app_startup" : @(SCTimeElapseAfterAppStartupMicros() / 1000.0), |
|||
}; |
|||
[_logger logTimedEventEnd:kSCCameraMetricsRecordingDelay uniqueId:@"IMAGE" parameters:params]; |
|||
_hasTriedCapturing = YES; |
|||
} |
|||
|
|||
@end |
|||
|
|||
@implementation SCFeatureImageCaptureImpl (SCManagedCapturerListener) |
|||
|
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didChangeState:(SCManagedCapturerState *)state |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
_managedCapturerState = [state copy]; |
|||
} |
|||
|
|||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didCapturePhoto:(SCManagedCapturerState *)state |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
if (_imagePromise) { |
|||
[[SCManagedCapturePreviewLayerController sharedInstance] pause]; |
|||
} |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,22 @@ |
|||
// |
|||
// SCFeatureNightMode.h |
|||
// SCCamera |
|||
// |
|||
// Created by Kristian Bauer on 4/9/18. |
|||
// |
|||
|
|||
#import "SCFeature.h" |
|||
|
|||
@class SCNavigationBarButtonItem, SCPreviewPresenter; |
|||
|
|||
/** |
|||
* Public interface for interacting with camera night mode feature. |
|||
* User spec: https://snapchat.quip.com/w4h4ArzcmXCS |
|||
*/ |
|||
@protocol SCFeatureNightMode <SCFeature> |
|||
@property (nonatomic, weak, readwrite) SCPreviewPresenter *previewPresenter; |
|||
@property (nonatomic, readonly) SCNavigationBarButtonItem *navigationBarButtonItem; |
|||
|
|||
- (void)interruptGestures; |
|||
- (void)hideWithDelayIfNeeded; |
|||
@end |
@ -0,0 +1,18 @@ |
|||
// |
|||
// SCNightModeButton.h |
|||
// SCCamera |
|||
// |
|||
// Created by Liu Liu on 3/19/15. |
|||
// Copyright (c) 2015 Snapchat, Inc. All rights reserved. |
|||
// |
|||
|
|||
#import <SCBase/SCMacros.h> |
|||
#import <SCUIKit/SCGrowingButton.h> |
|||
|
|||
@interface SCNightModeButton : SCGrowingButton |
|||
@property (nonatomic, assign, getter=isSelected) BOOL selected; |
|||
SC_INIT_AND_NEW_UNAVAILABLE |
|||
- (void)show; |
|||
- (void)hideWithDelay:(BOOL)delay; |
|||
- (BOOL)willHideAfterDelay; |
|||
@end |
@ -0,0 +1,95 @@ |
|||
// |
|||
// SCNightModeButton.m |
|||
// SCCamera |
|||
// |
|||
// Created by Liu Liu on 3/19/15. |
|||
// Copyright (c) 2015 Snapchat, Inc. All rights reserved. |
|||
// |
|||
|
|||
#import "SCNightModeButton.h" |
|||
|
|||
#import <SCFoundation/SCAssertWrapper.h> |
|||
|
|||
static NSTimeInterval const kSCNightModeButtonHiddenDelay = 2.5; |
|||
|
|||
@implementation SCNightModeButton { |
|||
dispatch_block_t _delayedHideBlock; |
|||
} |
|||
|
|||
- (instancetype)initWithFrame:(CGRect)frame |
|||
{ |
|||
self = [super initWithFrame:frame]; |
|||
if (self) { |
|||
self.image = [UIImage imageNamed:@"camera_nightmode_off_v10"]; |
|||
self.imageInset = CGSizeMake((CGRectGetWidth(self.bounds) - self.image.size.width) / 2, |
|||
(CGRectGetHeight(self.bounds) - self.image.size.height) / 2); |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (void)setSelected:(BOOL)selected |
|||
{ |
|||
SC_GUARD_ELSE_RETURN(_selected != selected); |
|||
if (selected) { |
|||
[self _cancelDelayedHideAnimation]; |
|||
self.image = [UIImage imageNamed:@"camera_nightmode_on_v10"]; |
|||
} else { |
|||
self.image = [UIImage imageNamed:@"camera_nightmode_off_v10"]; |
|||
} |
|||
self.imageInset = CGSizeMake((CGRectGetWidth(self.bounds) - self.image.size.width) / 2, |
|||
(CGRectGetHeight(self.bounds) - self.image.size.height) / 2); |
|||
_selected = selected; |
|||
} |
|||
|
|||
- (void)show |
|||
{ |
|||
SC_GUARD_ELSE_RETURN(self.hidden); |
|||
SCAssertMainThread(); |
|||
[self _cancelDelayedHideAnimation]; |
|||
self.hidden = NO; |
|||
[self animate]; |
|||
} |
|||
|
|||
- (void)hideWithDelay:(BOOL)delay |
|||
{ |
|||
SC_GUARD_ELSE_RETURN(!self.hidden); |
|||
SCAssertMainThread(); |
|||
[self _cancelDelayedHideAnimation]; |
|||
if (delay) { |
|||
@weakify(self); |
|||
_delayedHideBlock = dispatch_block_create(0, ^{ |
|||
@strongify(self); |
|||
SC_GUARD_ELSE_RETURN(self); |
|||
[UIView animateWithDuration:0.3 |
|||
animations:^{ |
|||
self.alpha = 0; |
|||
} |
|||
completion:^(BOOL finished) { |
|||
self.alpha = 1; |
|||
self.hidden = YES; |
|||
_delayedHideBlock = nil; |
|||
}]; |
|||
}); |
|||
dispatch_time_t delayTime = |
|||
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kSCNightModeButtonHiddenDelay * NSEC_PER_SEC)); |
|||
dispatch_after(delayTime, dispatch_get_main_queue(), _delayedHideBlock); |
|||
} else { |
|||
self.hidden = YES; |
|||
} |
|||
} |
|||
|
|||
- (BOOL)willHideAfterDelay |
|||
{ |
|||
return _delayedHideBlock != nil; |
|||
} |
|||
|
|||
#pragma mark - Private |
|||
|
|||
- (void)_cancelDelayedHideAnimation |
|||
{ |
|||
SC_GUARD_ELSE_RETURN(_delayedHideBlock); |
|||
dispatch_cancel(_delayedHideBlock); |
|||
_delayedHideBlock = nil; |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,26 @@ |
|||
// |
|||
// SCFeatureScanning.h |
|||
// Snapchat |
|||
// |
|||
// Created by Xiaokang Liu on 2018/4/19. |
|||
// |
|||
|
|||
#import "SCFeature.h" |
|||
|
|||
@protocol SCFeatureScanning; |
|||
|
|||
@protocol SCFeatureScanningDelegate <NSObject> |
|||
- (void)featureScanning:(id<SCFeatureScanning>)featureScanning didFinishWithResult:(NSObject *)resultObject; |
|||
@end |
|||
|
|||
/** |
|||
This SCFeature allows the user to long press on the screen to scan a snapcode. |
|||
*/ |
|||
@protocol SCFeatureScanning <SCFeature> |
|||
@property (nonatomic, weak) id<SCFeatureScanningDelegate> delegate; |
|||
@property (nonatomic, assign) NSTimeInterval lastSuccessfulScanTime; |
|||
- (void)startScanning; |
|||
- (void)stopScanning; |
|||
|
|||
- (void)stopSearch; |
|||
@end |
@ -0,0 +1,23 @@ |
|||
// |
|||
// SCFeatureShazam.h |
|||
// SCCamera |
|||
// |
|||
// Created by Xiaokang Liu on 2018/4/18. |
|||
// |
|||
|
|||
#import "SCFeature.h" |
|||
|
|||
@class SCLens; |
|||
@protocol SCFeatureShazam; |
|||
|
|||
@protocol SCFeatureShazamDelegate <NSObject> |
|||
- (void)featureShazam:(id<SCFeatureShazam>)featureShazam didFinishWithResult:(NSObject *)result; |
|||
- (void)featureShazamDidSubmitSearchRequest:(id<SCFeatureShazam>)featureShazam; |
|||
- (SCLens *)filterLensForFeatureShazam:(id<SCFeatureShazam>)featureShazam; |
|||
@end |
|||
|
|||
@protocol SCFeatureShazam <SCFeature> |
|||
@property (nonatomic, weak) id<SCFeatureShazamDelegate> delegate; |
|||
- (void)stopAudioRecordingAsynchronously; |
|||
- (void)resetInfo; |
|||
@end |
@ -0,0 +1,14 @@ |
|||
// |
|||
// SCFeatureSnapKit.h |
|||
// SCCamera |
|||
// |
|||
// Created by Michel Loenngren on 3/19/18. |
|||
// |
|||
|
|||
#import "SCFeature.h" |
|||
|
|||
@class SCCameraDeepLinkMetadata; |
|||
|
|||
@protocol SCFeatureSnapKit <SCFeature> |
|||
- (void)setDeepLinkMetadata:(SCCameraDeepLinkMetadata *)metadata; |
|||
@end |
@ -0,0 +1,17 @@ |
|||
// |
|||
// SCFeatureTapToFocusAndExposure.h |
|||
// SCCamera |
|||
// |
|||
// Created by Michel Loenngren on 4/5/18. |
|||
// |
|||
|
|||
#import "SCFeature.h" |
|||
|
|||
/** |
|||
This SCFeature allows the user to tap on the screen to adjust focus and exposure. |
|||
*/ |
|||
@protocol SCFeatureTapToFocusAndExposure <SCFeature> |
|||
|
|||
- (void)reset; |
|||
|
|||
@end |
@ -0,0 +1,49 @@ |
|||
// |
|||
// SCFeatureTapToFocusAndExposureImpl.h |
|||
// SCCamera |
|||
// |
|||
// Created by Michel Loenngren on 4/5/18. |
|||
// |
|||
|
|||
#import "SCFeatureTapToFocusAndExposure.h" |
|||
|
|||
#import <SCBase/SCMacros.h> |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@protocol SCCapturer; |
|||
|
|||
/** |
|||
Protocol describing unique camera commands to run when the user taps on screen. These could be focus, exposure or tap |
|||
to portrait mode. |
|||
*/ |
|||
@protocol SCFeatureCameraTapCommand <NSObject> |
|||
- (void)execute:(CGPoint)pointOfInterest capturer:(id<SCCapturer>)capturer; |
|||
@end |
|||
|
|||
/** |
|||
This is the default implementation of SCFeatureTapToFocusAndExposure allowing the user to tap on the camera overlay |
|||
view in order to adjust focus and exposure. |
|||
*/ |
|||
@interface SCFeatureTapToFocusAndExposureImpl : NSObject <SCFeatureTapToFocusAndExposure> |
|||
SC_INIT_AND_NEW_UNAVAILABLE |
|||
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer commands:(NSArray<id<SCFeatureCameraTapCommand>> *)commands; |
|||
@end |
|||
|
|||
/** |
|||
Adjust focus on tap. |
|||
*/ |
|||
@interface SCFeatureCameraFocusTapCommand : NSObject <SCFeatureCameraTapCommand> |
|||
@end |
|||
|
|||
/** |
|||
Adjust exposure on tap. |
|||
*/ |
|||
@interface SCFeatureCameraExposureTapCommand : NSObject <SCFeatureCameraTapCommand> |
|||
@end |
|||
|
|||
/** |
|||
Adjust portrait mode point of interest on tap. |
|||
*/ |
|||
@interface SCFeatureCameraPortraitTapCommand : NSObject <SCFeatureCameraTapCommand> |
|||
@end |
@ -0,0 +1,118 @@ |
|||
// |
|||
// SCFeatureTapToFocusImpl.m |
|||
// SCCamera |
|||
// |
|||
// Created by Michel Loenngren on 4/5/18. |
|||
// |
|||
|
|||
#import "SCFeatureTapToFocusAndExposureImpl.h" |
|||
|
|||
#import "SCCameraTweaks.h" |
|||
#import "SCCapturer.h" |
|||
#import "SCFeatureContainerView.h" |
|||
#import "SCTapAnimationView.h" |
|||
|
|||
#import <SCFoundation/SCLog.h> |
|||
#import <SCFoundation/SCTraceODPCompatible.h> |
|||
|
|||
@interface SCFeatureTapToFocusAndExposureImpl () |
|||
@property (nonatomic, weak) id<SCCapturer> capturer; |
|||
@property (nonatomic, weak) UIView<SCFeatureContainerView> *containerView; |
|||
@property (nonatomic) BOOL userTappedToFocusAndExposure; |
|||
@property (nonatomic) NSArray<id<SCFeatureCameraTapCommand>> *commands; |
|||
@end |
|||
|
|||
@implementation SCFeatureTapToFocusAndExposureImpl |
|||
|
|||
- (instancetype)initWithCapturer:(id<SCCapturer>)capturer commands:(NSArray<id<SCFeatureCameraTapCommand>> *)commands |
|||
{ |
|||
if (self = [super init]) { |
|||
_capturer = capturer; |
|||
_commands = commands; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
- (void)reset |
|||
{ |
|||
SC_GUARD_ELSE_RETURN(_userTappedToFocusAndExposure); |
|||
_userTappedToFocusAndExposure = NO; |
|||
[_capturer continuousAutofocusAndExposureAsynchronouslyWithCompletionHandler:nil context:SCCapturerContext]; |
|||
} |
|||
|
|||
#pragma mark - SCFeature |
|||
|
|||
- (void)configureWithView:(UIView<SCFeatureContainerView> *)view |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
_containerView = view; |
|||
} |
|||
|
|||
- (void)forwardCameraOverlayTapGesture:(UIGestureRecognizer *)gestureRecognizer |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
CGPoint point = [gestureRecognizer locationInView:gestureRecognizer.view]; |
|||
@weakify(self); |
|||
[_capturer convertViewCoordinates:[gestureRecognizer locationInView:_containerView] |
|||
completionHandler:^(CGPoint pointOfInterest) { |
|||
@strongify(self); |
|||
SC_GUARD_ELSE_RETURN(self); |
|||
SCLogCameraFeatureInfo(@"Tapped to focus: %@", NSStringFromCGPoint(pointOfInterest)); |
|||
[self _applyTapCommands:pointOfInterest]; |
|||
[self _showTapAnimationAtPoint:point forGesture:gestureRecognizer]; |
|||
} |
|||
context:SCCapturerContext]; |
|||
} |
|||
|
|||
#pragma mark - Private helpers |
|||
|
|||
- (void)_applyTapCommands:(CGPoint)pointOfInterest |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
for (id<SCFeatureCameraTapCommand> command in _commands) { |
|||
[command execute:pointOfInterest capturer:_capturer]; |
|||
} |
|||
self.userTappedToFocusAndExposure = YES; |
|||
} |
|||
|
|||
- (void)_showTapAnimationAtPoint:(CGPoint)point forGesture:(UIGestureRecognizer *)gestureRecognizer |
|||
{ |
|||
SCTraceODPCompatibleStart(2); |
|||
SC_GUARD_ELSE_RETURN([self.containerView isTapGestureRecognizer:gestureRecognizer]) |
|||
SCTapAnimationView *tapAnimationView = [SCTapAnimationView tapAnimationView]; |
|||
[_containerView addSubview:tapAnimationView]; |
|||
tapAnimationView.center = point; |
|||
[tapAnimationView showWithCompletion:^(SCTapAnimationView *view) { |
|||
[view removeFromSuperview]; |
|||
}]; |
|||
} |
|||
|
|||
@end |
|||
|
|||
@implementation SCFeatureCameraFocusTapCommand |
|||
- (void)execute:(CGPoint)pointOfInterest capturer:(id<SCCapturer>)capturer |
|||
{ |
|||
[capturer setAutofocusPointOfInterestAsynchronously:pointOfInterest |
|||
completionHandler:nil |
|||
context:SCCapturerContext]; |
|||
} |
|||
@end |
|||
|
|||
@implementation SCFeatureCameraExposureTapCommand |
|||
- (void)execute:(CGPoint)pointOfInterest capturer:(id<SCCapturer>)capturer |
|||
{ |
|||
[capturer setExposurePointOfInterestAsynchronously:pointOfInterest |
|||
fromUser:YES |
|||
completionHandler:nil |
|||
context:SCCapturerContext]; |
|||
} |
|||
@end |
|||
|
|||
@implementation SCFeatureCameraPortraitTapCommand |
|||
- (void)execute:(CGPoint)pointOfInterest capturer:(id<SCCapturer>)capturer |
|||
{ |
|||
[capturer setPortraitModePointOfInterestAsynchronously:pointOfInterest |
|||
completionHandler:nil |
|||
context:SCCapturerContext]; |
|||
} |
|||
@end |
@ -0,0 +1,21 @@ |
|||
// |
|||
// SCTapAnimationView.h |
|||
// SCCamera |
|||
// |
|||
// Created by Alexander Grytsiuk on 8/26/15. |
|||
// Copyright (c) 2015 Snapchat, Inc. All rights reserved. |
|||
// |
|||
|
|||
#import <UIKit/UIKit.h> |
|||
|
|||
@class SCTapAnimationView; |
|||
|
|||
typedef void (^SCTapAnimationViewCompletion)(SCTapAnimationView *); |
|||
|
|||
@interface SCTapAnimationView : UIView |
|||
|
|||
+ (instancetype)tapAnimationView; |
|||
|
|||
- (void)showWithCompletion:(SCTapAnimationViewCompletion)completion; |
|||
|
|||
@end |
@ -0,0 +1,178 @@ |
|||
// |
|||
// SCTapAnimationView.m |
|||
// SCCamera |
|||
// |
|||
// Created by Alexander Grytsiuk on 8/26/15. |
|||
// Copyright (c) 2015 Snapchat, Inc. All rights reserved. |
|||
// |
|||
|
|||
#import "SCTapAnimationView.h" |
|||
|
|||
#import <SCBase/SCMacros.h> |
|||
|
|||
@import QuartzCore; |
|||
|
|||
static const CGFloat kSCAnimationStep = 0.167; |
|||
static const CGFloat kSCInnerCirclePadding = 2.5; |
|||
static const CGFloat kSCTapAnimationViewWidth = 55; |
|||
static const CGFloat kSCOuterRingBorderWidth = 1; |
|||
|
|||
static NSString *const kSCOpacityAnimationKey = @"opacity"; |
|||
static NSString *const kSCScaleAnimationKey = @"scale"; |
|||
|
|||
@implementation SCTapAnimationView { |
|||
CALayer *_outerRing; |
|||
CALayer *_innerCircle; |
|||
} |
|||
|
|||
#pragma mark Class Methods |
|||
|
|||
+ (instancetype)tapAnimationView |
|||
{ |
|||
return [[self alloc] initWithFrame:CGRectMake(0, 0, kSCTapAnimationViewWidth, kSCTapAnimationViewWidth)]; |
|||
} |
|||
|
|||
#pragma mark Life Cycle |
|||
|
|||
- (instancetype)initWithFrame:(CGRect)frame |
|||
{ |
|||
self = [super initWithFrame:frame]; |
|||
if (self) { |
|||
self.userInteractionEnabled = NO; |
|||
_outerRing = [CALayer layer]; |
|||
_outerRing.backgroundColor = [UIColor clearColor].CGColor; |
|||
_outerRing.borderColor = [UIColor whiteColor].CGColor; |
|||
_outerRing.borderWidth = kSCOuterRingBorderWidth; |
|||
_outerRing.shadowColor = [UIColor blackColor].CGColor; |
|||
_outerRing.shadowOpacity = 0.4; |
|||
_outerRing.shadowOffset = CGSizeMake(0.5, 0.5); |
|||
_outerRing.opacity = 0.0; |
|||
_outerRing.frame = self.bounds; |
|||
_outerRing.cornerRadius = CGRectGetMidX(_outerRing.bounds); |
|||
[self.layer addSublayer:_outerRing]; |
|||
|
|||
_innerCircle = [CALayer layer]; |
|||
_innerCircle.backgroundColor = [UIColor whiteColor].CGColor; |
|||
_innerCircle.opacity = 0.0; |
|||
_innerCircle.frame = CGRectInset(self.bounds, kSCInnerCirclePadding, kSCInnerCirclePadding); |
|||
_innerCircle.cornerRadius = CGRectGetMidX(_innerCircle.bounds); |
|||
[self.layer addSublayer:_innerCircle]; |
|||
} |
|||
return self; |
|||
} |
|||
|
|||
#pragma mark Public |
|||
|
|||
- (void)showWithCompletion:(SCTapAnimationViewCompletion)completion |
|||
{ |
|||
[_outerRing removeAllAnimations]; |
|||
[_innerCircle removeAllAnimations]; |
|||
|
|||
[CATransaction begin]; |
|||
[CATransaction setCompletionBlock:^{ |
|||
if (completion) { |
|||
completion(self); |
|||
} |
|||
}]; |
|||
[self addOuterRingOpacityAnimation]; |
|||
[self addOuterRingScaleAnimation]; |
|||
[self addInnerCircleOpacityAnimation]; |
|||
[self addInnerCircleScaleAnimation]; |
|||
[CATransaction commit]; |
|||
} |
|||
|
|||
#pragma mark Private |
|||
|
|||
- (CAKeyframeAnimation *)keyFrameAnimationWithKeyPath:(NSString *)keyPath |
|||
duration:(CGFloat)duration |
|||
values:(NSArray *)values |
|||
keyTimes:(NSArray *)keyTimes |
|||
timingFunctions:(NSArray *)timingFunctions |
|||
{ |
|||
CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:keyPath]; |
|||
keyframeAnimation.duration = duration; |
|||
keyframeAnimation.values = values; |
|||
keyframeAnimation.keyTimes = keyTimes; |
|||
keyframeAnimation.timingFunctions = timingFunctions; |
|||
keyframeAnimation.fillMode = kCAFillModeForwards; |
|||
keyframeAnimation.removedOnCompletion = NO; |
|||
|
|||
return keyframeAnimation; |
|||
} |
|||
|
|||
- (CABasicAnimation *)animationWithKeyPath:(NSString *)keyPath |
|||
duration:(CGFloat)duration |
|||
fromValue:(NSValue *)fromValue |
|||
toValue:(NSValue *)toValue |
|||
timingFunction:(CAMediaTimingFunction *)timingFunction |
|||
{ |
|||
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath]; |
|||
animation.duration = duration; |
|||
animation.fromValue = fromValue; |
|||
animation.toValue = toValue; |
|||
animation.timingFunction = timingFunction; |
|||
animation.fillMode = kCAFillModeForwards; |
|||
animation.removedOnCompletion = NO; |
|||
|
|||
return animation; |
|||
} |
|||
|
|||
- (void)addOuterRingOpacityAnimation |
|||
{ |
|||
CAKeyframeAnimation *animation = |
|||
[self keyFrameAnimationWithKeyPath:@keypath(_outerRing, opacity) |
|||
duration:kSCAnimationStep * 5 |
|||
values:@[ @0.0, @1.0, @1.0, @0.0 ] |
|||
keyTimes:@[ @0.0, @0.2, @0.8, @1.0 ] |
|||
timingFunctions:@[ |
|||
[CAMediaTimingFunction functionWithControlPoints:0.0:0.0:0.0:1.0], |
|||
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], |
|||
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], |
|||
]]; |
|||
[_outerRing addAnimation:animation forKey:kSCOpacityAnimationKey]; |
|||
} |
|||
|
|||
- (void)addOuterRingScaleAnimation |
|||
{ |
|||
CAKeyframeAnimation *animation = |
|||
[self keyFrameAnimationWithKeyPath:@keypath(_innerCircle, transform) |
|||
duration:kSCAnimationStep * 3 |
|||
values:@[ |
|||
[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.50, 0.50, 1.0)], |
|||
[NSValue valueWithCATransform3D:CATransform3DIdentity], |
|||
[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.83, 0.83, 1.0)], |
|||
] |
|||
keyTimes:@[ @0.0, @0.66, @1.0 ] |
|||
timingFunctions:@[ |
|||
[CAMediaTimingFunction functionWithControlPoints:0.0:0.0:0.0:1.0], |
|||
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], |
|||
]]; |
|||
[_outerRing addAnimation:animation forKey:kSCScaleAnimationKey]; |
|||
} |
|||
|
|||
- (void)addInnerCircleOpacityAnimation |
|||
{ |
|||
CAKeyframeAnimation *animation = |
|||
[self keyFrameAnimationWithKeyPath:@keypath(_innerCircle, opacity) |
|||
duration:kSCAnimationStep * 3 |
|||
values:@[ @0.0, @0.40, @0.0 ] |
|||
keyTimes:@[ @0.0, @0.33, @1.0 ] |
|||
timingFunctions:@[ |
|||
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn], |
|||
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], |
|||
]]; |
|||
[_innerCircle addAnimation:animation forKey:kSCOpacityAnimationKey]; |
|||
} |
|||
|
|||
- (void)addInnerCircleScaleAnimation |
|||
{ |
|||
CABasicAnimation *animation = |
|||
[self animationWithKeyPath:@keypath(_innerCircle, transform) |
|||
duration:kSCAnimationStep * 2 |
|||
fromValue:[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.0, 0.0, 1.0)] |
|||
toValue:[NSValue valueWithCATransform3D:CATransform3DIdentity] |
|||
timingFunction:[CAMediaTimingFunction functionWithControlPoints:0.0:0.0:0.0:1.0]]; |
|||
[_innerCircle addAnimation:animation forKey:kSCScaleAnimationKey]; |
|||
} |
|||
|
|||
@end |
@ -0,0 +1,37 @@ |
|||
// |
|||
// SCFeatureToggleCamera.h |
|||
// SCCamera |
|||
// |
|||
// Created by Michel Loenngren on 4/17/18. |
|||
// |
|||
|
|||
#import <SCCamera/SCFeature.h> |
|||
#import <SCCameraFoundation/SCManagedCaptureDevicePosition.h> |
|||
|
|||
@protocol SCCapturer |
|||
, SCFeatureToggleCamera, SCLensCameraScreenDataProviderProtocol; |
|||
|
|||
@protocol SCFeatureToggleCameraDelegate <NSObject> |
|||
|
|||
- (void)featureToggleCamera:(id<SCFeatureToggleCamera>)feature |
|||
willToggleToDevicePosition:(SCManagedCaptureDevicePosition)devicePosition; |
|||
- (void)featureToggleCamera:(id<SCFeatureToggleCamera>)feature |
|||
didToggleToDevicePosition:(SCManagedCaptureDevicePosition)devicePosition; |
|||
|
|||
@end |
|||
|
|||
/** |
|||
SCFeature protocol for toggling the camera. |
|||
*/ |
|||
@protocol SCFeatureToggleCamera <SCFeature> |
|||
|
|||
@property (nonatomic, weak) id<SCFeatureToggleCameraDelegate> delegate; |
|||
|
|||
- (void)toggleCameraWithRecording:(BOOL)isRecording |
|||
takingPicture:(BOOL)isTakingPicture |
|||
lensDataProvider:(id<SCLensCameraScreenDataProviderProtocol>)lensDataProvider |
|||
completion:(void (^)(BOOL success))completion; |
|||
|
|||
- (void)reset; |
|||
|
|||
@end |
@ -0,0 +1,34 @@ |
|||
// |
|||
// SCFeatureZooming.h |
|||
// SCCamera |
|||
// |
|||
// Created by Xiaokang Liu on 2018/4/19. |
|||
// |
|||
|
|||
#import "SCFeature.h" |
|||
|
|||
#import <SCCameraFoundation/SCManagedCaptureDevicePosition.h> |
|||
#import <SCSearch/SCSearchSnapZoomLevelProviding.h> |
|||
|
|||
@class SCPreviewPresenter; |
|||
@protocol SCFeatureZooming; |
|||
|
|||
@protocol SCFeatureZoomingDelegate <NSObject> |
|||
- (void)featureZoomingForceTouchedWhileRecording:(id<SCFeatureZooming>)featureZooming; |
|||
- (BOOL)featureZoomingIsInitiatedRecording:(id<SCFeatureZooming>)featureZooming; |
|||
@end |
|||
|
|||
@protocol SCFeatureZooming <SCFeature, SCSearchSnapZoomLevelProviding> |
|||
@property (nonatomic, weak) id<SCFeatureZoomingDelegate> delegate; |
|||
@property (nonatomic, weak) SCPreviewPresenter *previewPresenter; |
|||
|
|||
- (void)resetOffset; |
|||
- (void)resetScale; |
|||
|
|||
- (void)cancelPreview; |
|||
- (void)flipOffset; |
|||
|
|||
- (void)resetBeginningScale; |
|||
- (void)toggleCameraForReset:(SCManagedCaptureDevicePosition)devicePosition; |
|||
- (void)recordCurrentZoomStateForReset; |
|||
@end |
@ -0,0 +1,23 @@ |
|||
// |
|||
// SCManagedCapturerARImageCaptureProvider.h |
|||
// SCCamera |
|||
// |
|||
// Created by Michel Loenngren on 4/11/18. |
|||
// |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@class SCManagedStillImageCapturer; |
|||
@protocol SCManagedCapturerLensAPI |
|||
, SCPerforming; |
|||
|
|||
/** |
|||
Bridging protocol providing the ARImageCapturer subclass of SCManagedStillImageCapturer |
|||
to capture core. |
|||
*/ |
|||
@protocol SCManagedCapturerARImageCaptureProvider <NSObject> |
|||
|
|||
- (SCManagedStillImageCapturer *)arImageCapturerWith:(id<SCPerforming>)performer |
|||
lensProcessingCore:(id<SCManagedCapturerLensAPI>)lensProcessingCore; |
|||
|
|||
@end |
@ -0,0 +1,27 @@ |
|||
// |
|||
// SCManagedCapturerGLViewManagerAPI.h |
|||
// SCCamera |
|||
// |
|||
// Created by Michel Loenngren on 4/11/18. |
|||
// |
|||
|
|||
#import <Looksery/LSAGLView.h> |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@class SCCaptureResource; |
|||
|
|||
/** |
|||
Bridging protocol for providing a glViewManager to capture core. |
|||
*/ |
|||
@protocol SCManagedCapturerGLViewManagerAPI <NSObject> |
|||
|
|||
@property (nonatomic, readonly, strong) LSAGLView *view; |
|||
|
|||
- (void)configureWithCaptureResource:(SCCaptureResource *)captureResource; |
|||
|
|||
- (void)setLensesActive:(BOOL)active; |
|||
|
|||
- (void)prepareViewIfNecessary; |
|||
|
|||
@end |
@ -0,0 +1,19 @@ |
|||
// |
|||
// SCManagedCapturerLSAComponentTrackerAPI.h |
|||
// SCCamera |
|||
// |
|||
// Created by Michel Loenngren on 4/11/18. |
|||
// |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@class SCCaptureResource; |
|||
|
|||
/** |
|||
SCCamera protocol providing LSA tracking logic. |
|||
*/ |
|||
@protocol SCManagedCapturerLSAComponentTrackerAPI <NSObject> |
|||
|
|||
- (void)configureWithCaptureResource:(SCCaptureResource *)captureResource; |
|||
|
|||
@end |
@ -0,0 +1,67 @@ |
|||
// |
|||
// SCManagedCapturerLensAPI.h |
|||
// SCCamera |
|||
// |
|||
// Created by Michel Loenngren on 4/11/18. |
|||
// |
|||
|
|||
#import "SCManagedCapturerListener.h" |
|||
#import "SCManagedVideoARDataSource.h" |
|||
|
|||
#import <SCCameraFoundation/SCManagedCaptureDevicePosition.h> |
|||
#import <SCLenses/SCLens.h> |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@protocol SCManagedAudioDataSourceListener |
|||
, SCManagedVideoARDataSource; |
|||
@class LSAComponentManager; |
|||
|
|||
/** |
|||
Encapsulation of LensesProcessingCore for use in SCCamera. |
|||
*/ |
|||
@protocol SCManagedCapturerLensAPI <SCManagedCapturerListener> |
|||
|
|||
@property (nonatomic, strong, readonly) LSAComponentManager *componentManager; |
|||
@property (nonatomic, strong) NSString *activeLensId; |
|||
@property (nonatomic, readonly) BOOL isLensApplied; |
|||
@property (nonatomic, strong, readonly) |
|||
id<SCManagedAudioDataSourceListener, SCManagedVideoDataSourceListener> capturerListener; |
|||
|
|||
typedef void (^SCManagedCapturerLensAPIPointOfInterestCompletion)(SCLensCategory *category, NSInteger categoriesCount); |
|||
|
|||
- (void)setAspectRatio:(BOOL)isLiveStreaming; |
|||
|
|||
- (SCLens *)appliedLens; |
|||
|
|||
- (void)setFieldOfView:(float)fieldOfView; |
|||
|
|||
- (void)setAsFieldOfViewListenerForDevice:(SCManagedCaptureDevice *)captureDevice; |
|||
|
|||
- (void)setAsFieldOfViewListenerForARDataSource:(id<SCManagedVideoARDataSource>)arDataSource NS_AVAILABLE_IOS(11_0); |
|||
|
|||
- (void)removeFieldOfViewListener; |
|||
|
|||
- (void)setModifySource:(BOOL)modifySource; |
|||
|
|||
- (void)setLensesActive:(BOOL)lensesActive |
|||
videoOrientation:(AVCaptureVideoOrientation)videoOrientation |
|||
filterFactory:(SCLookseryFilterFactory *)filterFactory; |
|||
|
|||
- (void)detectLensCategoryOnNextFrame:(CGPoint)point |
|||
videoOrientation:(AVCaptureVideoOrientation)videoOrientation |
|||
lenses:(NSArray<SCLens *> *)lenses |
|||
completion:(SCManagedCapturerLensAPIPointOfInterestCompletion)completion; |
|||
|
|||
- (void)setShouldMuteAllSounds:(BOOL)shouldMuteAllSounds; |
|||
|
|||
- (UIImage *)processImage:(UIImage *)image |
|||
maxPixelSize:(NSInteger)maxPixelSize |
|||
devicePosition:(SCManagedCaptureDevicePosition)position |
|||
fieldOfView:(float)fieldOfView; |
|||
|
|||
- (void)setShouldProcessARFrames:(BOOL)shouldProcessARFrames; |
|||
|
|||
- (NSInteger)maxPixelSize; |
|||
|
|||
@end |
@ -0,0 +1,20 @@ |
|||
// |
|||
// SCManagedCapturerLensAPIProvider.h |
|||
// SCCamera |
|||
// |
|||
// Created by Michel Loenngren on 4/12/18. |
|||
// |
|||
|
|||
#import <Foundation/Foundation.h> |
|||
|
|||
@protocol SCManagedCapturerLensAPI; |
|||
@class SCCaptureResource; |
|||
|
|||
/** |
|||
Provider for creating new instances of SCManagedCapturerLensAPI within SCCamera. |
|||
*/ |
|||
@protocol SCManagedCapturerLensAPIProvider <NSObject> |
|||
|
|||
- (id<SCManagedCapturerLensAPI>)lensAPIForCaptureResource:(SCCaptureResource *)captureResouce; |
|||
|
|||
@end |
Write
Preview
Loading…
Cancel
Save
Reference in new issue