Browse Source

Add files via upload

pull/2/head
Jonny Banana 6 years ago
committed by GitHub
parent
commit
c7fd130a81
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 56
      BlackCamera/SCBlackCameraDetector.h
  2. 134
      BlackCamera/SCBlackCameraDetector.m
  3. 26
      BlackCamera/SCBlackCameraNoOutputDetector.h
  4. 137
      BlackCamera/SCBlackCameraNoOutputDetector.m
  5. 20
      BlackCamera/SCBlackCameraPreviewDetector.h
  6. 92
      BlackCamera/SCBlackCameraPreviewDetector.m
  7. 35
      BlackCamera/SCBlackCameraReporter.h
  8. 86
      BlackCamera/SCBlackCameraReporter.m
  9. 27
      BlackCamera/SCBlackCameraRunningDetector.h
  10. 84
      BlackCamera/SCBlackCameraRunningDetector.m
  11. 23
      BlackCamera/SCBlackCameraSessionBlockDetector.h
  12. 82
      BlackCamera/SCBlackCameraSessionBlockDetector.m
  13. 31
      BlackCamera/SCBlackCameraViewDetector.h
  14. 136
      BlackCamera/SCBlackCameraViewDetector.m
  15. 14
      BlackCamera/SCCaptureSessionFixer.h
  16. 21
      BlackCamera/SCCaptureSessionFixer.m
  17. 13
      ContextAwareTaskManagement/OWNERS
  18. 16
      ContextAwareTaskManagement/Requests/SCContextAwareSnapCreationThrottleRequest.h
  19. 70
      ContextAwareTaskManagement/Requests/SCContextAwareSnapCreationThrottleRequest.m
  20. 22
      ContextAwareTaskManagement/Triggers/SCSnapCreationTriggers.h
  21. 83
      ContextAwareTaskManagement/Triggers/SCSnapCreationTriggers.m
  22. 26
      Features/Core/SCFeature.h
  23. 13
      Features/Core/SCFeatureContainerView.h
  24. 44
      Features/Core/SCFeatureCoordinator.h
  25. 117
      Features/Core/SCFeatureCoordinator.m
  26. 50
      Features/Core/SCFeatureProvider.h
  27. 20
      Features/Flash/SCFeatureFlash.h
  28. 23
      Features/Flash/SCFeatureFlashImpl.h
  29. 226
      Features/Flash/SCFeatureFlashImpl.m
  30. 15
      Features/Flash/SCFlashButton.h
  31. 35
      Features/Flash/SCFlashButton.m
  32. 30
      Features/HandsFree/SCFeatureHandsFree.h
  33. 27
      Features/ImageCapture/SCFeatureImageCapture.h
  34. 21
      Features/ImageCapture/SCFeatureImageCaptureImpl.h
  35. 184
      Features/ImageCapture/SCFeatureImageCaptureImpl.m
  36. 22
      Features/NightMode/SCFeatureNightMode.h
  37. 18
      Features/NightMode/SCNightModeButton.h
  38. 95
      Features/NightMode/SCNightModeButton.m
  39. 26
      Features/Scanning/SCFeatureScanning.h
  40. 23
      Features/Shazam/SCFeatureShazam.h
  41. 14
      Features/SnapKit/SCFeatureSnapKit.h
  42. 17
      Features/TapToFocus/SCFeatureTapToFocusAndExposure.h
  43. 49
      Features/TapToFocus/SCFeatureTapToFocusAndExposureImpl.h
  44. 118
      Features/TapToFocus/SCFeatureTapToFocusAndExposureImpl.m
  45. 21
      Features/TapToFocus/SCTapAnimationView.h
  46. 178
      Features/TapToFocus/SCTapAnimationView.m
  47. 37
      Features/ToggleCamera/SCFeatureToggleCamera.h
  48. 34
      Features/Zooming/SCFeatureZooming.h
  49. 23
      Lens/SCManagedCapturerARImageCaptureProvider.h
  50. 27
      Lens/SCManagedCapturerGLViewManagerAPI.h
  51. 19
      Lens/SCManagedCapturerLSAComponentTrackerAPI.h
  52. 67
      Lens/SCManagedCapturerLensAPI.h
  53. 20
      Lens/SCManagedCapturerLensAPIProvider.h

56
BlackCamera/SCBlackCameraDetector.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

134
BlackCamera/SCBlackCameraDetector.m

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

26
BlackCamera/SCBlackCameraNoOutputDetector.h

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

137
BlackCamera/SCBlackCameraNoOutputDetector.m

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

20
BlackCamera/SCBlackCameraPreviewDetector.h

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

92
BlackCamera/SCBlackCameraPreviewDetector.m

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

35
BlackCamera/SCBlackCameraReporter.h

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

86
BlackCamera/SCBlackCameraReporter.m

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

27
BlackCamera/SCBlackCameraRunningDetector.h

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

84
BlackCamera/SCBlackCameraRunningDetector.m

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

23
BlackCamera/SCBlackCameraSessionBlockDetector.h

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

82
BlackCamera/SCBlackCameraSessionBlockDetector.m

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

31
BlackCamera/SCBlackCameraViewDetector.h

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

136
BlackCamera/SCBlackCameraViewDetector.m

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

14
BlackCamera/SCCaptureSessionFixer.h

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

21
BlackCamera/SCCaptureSessionFixer.m

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

13
ContextAwareTaskManagement/OWNERS

@ -0,0 +1,13 @@
--- !OWNERS
version: 2
default:
jira_project: CCAM
owners:
num_required_reviewers: 1
teams:
- Snapchat/core-camera-ios
users:
- cjiang
- ljia

16
ContextAwareTaskManagement/Requests/SCContextAwareSnapCreationThrottleRequest.h

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

70
ContextAwareTaskManagement/Requests/SCContextAwareSnapCreationThrottleRequest.m

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

22
ContextAwareTaskManagement/Triggers/SCSnapCreationTriggers.h

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

83
ContextAwareTaskManagement/Triggers/SCSnapCreationTriggers.m

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

26
Features/Core/SCFeature.h

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

13
Features/Core/SCFeatureContainerView.h

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

44
Features/Core/SCFeatureCoordinator.h

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

117
Features/Core/SCFeatureCoordinator.m

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

50
Features/Core/SCFeatureProvider.h

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

20
Features/Flash/SCFeatureFlash.h

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

23
Features/Flash/SCFeatureFlashImpl.h

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

226
Features/Flash/SCFeatureFlashImpl.m

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

15
Features/Flash/SCFlashButton.h

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

35
Features/Flash/SCFlashButton.m

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

30
Features/HandsFree/SCFeatureHandsFree.h

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

27
Features/ImageCapture/SCFeatureImageCapture.h

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

21
Features/ImageCapture/SCFeatureImageCaptureImpl.h

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

184
Features/ImageCapture/SCFeatureImageCaptureImpl.m

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

22
Features/NightMode/SCFeatureNightMode.h

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

18
Features/NightMode/SCNightModeButton.h

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

95
Features/NightMode/SCNightModeButton.m

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

26
Features/Scanning/SCFeatureScanning.h

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

23
Features/Shazam/SCFeatureShazam.h

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

14
Features/SnapKit/SCFeatureSnapKit.h

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

17
Features/TapToFocus/SCFeatureTapToFocusAndExposure.h

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

49
Features/TapToFocus/SCFeatureTapToFocusAndExposureImpl.h

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

118
Features/TapToFocus/SCFeatureTapToFocusAndExposureImpl.m

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

21
Features/TapToFocus/SCTapAnimationView.h

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

178
Features/TapToFocus/SCTapAnimationView.m

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

37
Features/ToggleCamera/SCFeatureToggleCamera.h

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

34
Features/Zooming/SCFeatureZooming.h

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

23
Lens/SCManagedCapturerARImageCaptureProvider.h

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

27
Lens/SCManagedCapturerGLViewManagerAPI.h

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

19
Lens/SCManagedCapturerLSAComponentTrackerAPI.h

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

67
Lens/SCManagedCapturerLensAPI.h

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

20
Lens/SCManagedCapturerLensAPIProvider.h

@ -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
Loading…
Cancel
Save