master f031dd9e0f08 cached
311 files
1.1 MB
254.3k tokens
65 symbols
1 requests
Download .txt
Showing preview only (1,206K chars total). Download the full file or copy to clipboard to get everything.
Repository: JonnyBanana/Snapchat-Source-Code-Leak
Branch: master
Commit: f031dd9e0f08
Files: 311
Total size: 1.1 MB

Directory structure:
gitextract_jfvnmt3e/

├── BlackCamera/
│   ├── SCBlackCameraDetector.h
│   ├── SCBlackCameraDetector.m
│   ├── SCBlackCameraNoOutputDetector.h
│   ├── SCBlackCameraNoOutputDetector.m
│   ├── SCBlackCameraPreviewDetector.h
│   ├── SCBlackCameraPreviewDetector.m
│   ├── SCBlackCameraReporter.h
│   ├── SCBlackCameraReporter.m
│   ├── SCBlackCameraRunningDetector.h
│   ├── SCBlackCameraRunningDetector.m
│   ├── SCBlackCameraSessionBlockDetector.h
│   ├── SCBlackCameraSessionBlockDetector.m
│   ├── SCBlackCameraViewDetector.h
│   ├── SCBlackCameraViewDetector.m
│   ├── SCCaptureSessionFixer.h
│   └── SCCaptureSessionFixer.m
├── ContextAwareTaskManagement/
│   ├── OWNERS
│   ├── Requests/
│   │   ├── SCContextAwareSnapCreationThrottleRequest.h
│   │   └── SCContextAwareSnapCreationThrottleRequest.m
│   └── Triggers/
│       ├── SCSnapCreationTriggers.h
│       └── SCSnapCreationTriggers.m
├── Features/
│   ├── Core/
│   │   ├── SCFeature.h
│   │   ├── SCFeatureContainerView.h
│   │   ├── SCFeatureCoordinator.h
│   │   ├── SCFeatureCoordinator.m
│   │   └── SCFeatureProvider.h
│   ├── Flash/
│   │   ├── SCFeatureFlash.h
│   │   ├── SCFeatureFlashImpl.h
│   │   ├── SCFeatureFlashImpl.m
│   │   ├── SCFlashButton.h
│   │   └── SCFlashButton.m
│   ├── HandsFree/
│   │   └── SCFeatureHandsFree.h
│   ├── ImageCapture/
│   │   ├── SCFeatureImageCapture.h
│   │   ├── SCFeatureImageCaptureImpl.h
│   │   └── SCFeatureImageCaptureImpl.m
│   ├── NightMode/
│   │   ├── SCFeatureNightMode.h
│   │   ├── SCNightModeButton.h
│   │   └── SCNightModeButton.m
│   ├── Scanning/
│   │   └── SCFeatureScanning.h
│   ├── Shazam/
│   │   └── SCFeatureShazam.h
│   ├── SnapKit/
│   │   └── SCFeatureSnapKit.h
│   ├── TapToFocus/
│   │   ├── SCFeatureTapToFocusAndExposure.h
│   │   ├── SCFeatureTapToFocusAndExposureImpl.h
│   │   ├── SCFeatureTapToFocusAndExposureImpl.m
│   │   ├── SCTapAnimationView.h
│   │   └── SCTapAnimationView.m
│   ├── ToggleCamera/
│   │   └── SCFeatureToggleCamera.h
│   └── Zooming/
│       └── SCFeatureZooming.h
├── Lens/
│   ├── SCManagedCapturerARImageCaptureProvider.h
│   ├── SCManagedCapturerGLViewManagerAPI.h
│   ├── SCManagedCapturerLSAComponentTrackerAPI.h
│   ├── SCManagedCapturerLensAPI.h
│   └── SCManagedCapturerLensAPIProvider.h
├── Logging/
│   ├── SCCoreCameraLogger.h
│   ├── SCCoreCameraLogger.m
│   ├── SCLogger+Camera.h
│   ├── SCLogger+Camera.m
│   └── SCManiphestTicketCreator.h
├── ManagedCapturer/
│   ├── ARConfiguration+SCConfiguration.h
│   ├── ARConfiguration+SCConfiguration.m
│   ├── AVCaptureConnection+InputDevice.h
│   ├── AVCaptureConnection+InputDevice.m
│   ├── AVCaptureDevice+ConfigurationLock.h
│   ├── AVCaptureDevice+ConfigurationLock.m
│   ├── CapturerV2/
│   │   ├── Configuration/
│   │   │   ├── SCCaptureConfiguration.h
│   │   │   ├── SCCaptureConfiguration.m
│   │   │   ├── SCCaptureConfigurationAnnouncer.h
│   │   │   ├── SCCaptureConfigurationAnnouncer.m
│   │   │   ├── SCCaptureConfigurationAnnouncer_Private.h
│   │   │   ├── SCCaptureConfigurationListener.h
│   │   │   ├── SCCaptureConfiguration_Private.h
│   │   │   ├── SCCaptureConfigurator.h
│   │   │   └── SCCaptureConfigurator.m
│   │   └── Core/
│   │       ├── SCCaptureCore.h
│   │       └── SCCaptureCore.m
│   ├── ImageProcessing/
│   │   ├── SCDepthBlurMetalModule.metal
│   │   ├── SCDepthBlurMetalRenderCommand.h
│   │   ├── SCDepthBlurMetalRenderCommand.m
│   │   ├── SCDepthToGrayscaleMetalModule.metal
│   │   ├── SCDepthToGrayscaleMetalRenderCommand.h
│   │   ├── SCDepthToGrayscaleMetalRenderCommand.m
│   │   ├── SCDigitalExposureHandler.h
│   │   ├── SCDigitalExposureHandler.m
│   │   ├── SCExposureAdjustMetalModule.metal
│   │   ├── SCExposureAdjustMetalRenderCommand.h
│   │   ├── SCExposureAdjustMetalRenderCommand.m
│   │   ├── SCExposureAdjustProcessingModule.h
│   │   ├── SCExposureAdjustProcessingModule.m
│   │   ├── SCMetalModule.h
│   │   ├── SCMetalModule.m
│   │   ├── SCMetalTextureResource.h
│   │   ├── SCMetalTextureResource.m
│   │   ├── SCNightModeEnhancementMetalModule.metal
│   │   ├── SCNightModeEnhancementMetalRenderCommand.h
│   │   ├── SCNightModeEnhancementMetalRenderCommand.m
│   │   ├── SCProcessingModule.h
│   │   ├── SCProcessingModuleUtils.h
│   │   ├── SCProcessingModuleUtils.m
│   │   ├── SCProcessingPipeline.h
│   │   ├── SCProcessingPipeline.m
│   │   ├── SCProcessingPipelineBuilder.h
│   │   ├── SCProcessingPipelineBuilder.m
│   │   ├── SCStillImageDepthBlurFilter.h
│   │   └── SCStillImageDepthBlurFilter.m
│   ├── NSURL+Asset.h
│   ├── NSURL+Asset.m
│   ├── OWNERS
│   ├── SCAudioCaptureSession.h
│   ├── SCAudioCaptureSession.m
│   ├── SCCameraSettingUtils.h
│   ├── SCCameraSettingUtils.m
│   ├── SCCaptureCommon.h
│   ├── SCCaptureCommon.m
│   ├── SCCaptureCoreImageFaceDetector.h
│   ├── SCCaptureCoreImageFaceDetector.m
│   ├── SCCaptureDeviceAuthorization.h
│   ├── SCCaptureDeviceAuthorization.m
│   ├── SCCaptureDeviceAuthorizationChecker.h
│   ├── SCCaptureDeviceAuthorizationChecker.m
│   ├── SCCaptureDeviceResolver.h
│   ├── SCCaptureDeviceResolver.m
│   ├── SCCaptureFaceDetectionParser.h
│   ├── SCCaptureFaceDetectionParser.m
│   ├── SCCaptureFaceDetector.h
│   ├── SCCaptureFaceDetectorTrigger.h
│   ├── SCCaptureFaceDetectorTrigger.m
│   ├── SCCaptureMetadataObjectParser.h
│   ├── SCCaptureMetadataObjectParser.m
│   ├── SCCaptureMetadataOutputDetector.h
│   ├── SCCaptureMetadataOutputDetector.m
│   ├── SCCapturer.h
│   ├── SCCapturerBufferedVideoWriter.h
│   ├── SCCapturerBufferedVideoWriter.m
│   ├── SCCapturerDefines.h
│   ├── SCCapturerToken.h
│   ├── SCCapturerToken.m
│   ├── SCCapturerTokenProvider.h
│   ├── SCCapturerTokenProvider.m
│   ├── SCExposureState.h
│   ├── SCExposureState.m
│   ├── SCFileAudioCaptureSession.h
│   ├── SCFileAudioCaptureSession.m
│   ├── SCManagedAudioStreamer.h
│   ├── SCManagedAudioStreamer.m
│   ├── SCManagedCaptureDevice+SCManagedCapturer.h
│   ├── SCManagedCaptureDevice+SCManagedDeviceCapacityAnalyzer.h
│   ├── SCManagedCaptureDevice.h
│   ├── SCManagedCaptureDevice.m
│   ├── SCManagedCaptureDeviceAutoExposureHandler.h
│   ├── SCManagedCaptureDeviceAutoExposureHandler.m
│   ├── SCManagedCaptureDeviceAutoFocusHandler.h
│   ├── SCManagedCaptureDeviceAutoFocusHandler.m
│   ├── SCManagedCaptureDeviceDefaultZoomHandler.h
│   ├── SCManagedCaptureDeviceDefaultZoomHandler.m
│   ├── SCManagedCaptureDeviceDefaultZoomHandler_Private.h
│   ├── SCManagedCaptureDeviceExposureHandler.h
│   ├── SCManagedCaptureDeviceFaceDetectionAutoExposureHandler.h
│   ├── SCManagedCaptureDeviceFaceDetectionAutoExposureHandler.m
│   ├── SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.h
│   ├── SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.m
│   ├── SCManagedCaptureDeviceFocusHandler.h
│   ├── SCManagedCaptureDeviceHandler.h
│   ├── SCManagedCaptureDeviceHandler.m
│   ├── SCManagedCaptureDeviceLinearInterpolationZoomHandler.h
│   ├── SCManagedCaptureDeviceLinearInterpolationZoomHandler.m
│   ├── SCManagedCaptureDeviceLockOnRecordExposureHandler.h
│   ├── SCManagedCaptureDeviceLockOnRecordExposureHandler.m
│   ├── SCManagedCaptureDeviceSavitzkyGolayZoomHandler.h
│   ├── SCManagedCaptureDeviceSavitzkyGolayZoomHandler.m
│   ├── SCManagedCaptureDeviceSubjectAreaHandler.h
│   ├── SCManagedCaptureDeviceSubjectAreaHandler.m
│   ├── SCManagedCaptureDeviceThresholdExposureHandler.h
│   ├── SCManagedCaptureDeviceThresholdExposureHandler.m
│   ├── SCManagedCaptureFaceDetectionAdjustingPOIResource.h
│   ├── SCManagedCaptureFaceDetectionAdjustingPOIResource.m
│   ├── SCManagedCapturePreviewLayerController.h
│   ├── SCManagedCapturePreviewLayerController.m
│   ├── SCManagedCapturePreviewView.h
│   ├── SCManagedCapturePreviewView.m
│   ├── SCManagedCapturePreviewViewDebugView.h
│   ├── SCManagedCapturePreviewViewDebugView.m
│   ├── SCManagedCaptureSession.h
│   ├── SCManagedCaptureSession.m
│   ├── SCManagedCapturer.h
│   ├── SCManagedCapturer.m
│   ├── SCManagedCapturerARSessionHandler.h
│   ├── SCManagedCapturerARSessionHandler.m
│   ├── SCManagedCapturerListener.h
│   ├── SCManagedCapturerListenerAnnouncer.h
│   ├── SCManagedCapturerListenerAnnouncer.mm
│   ├── SCManagedCapturerSampleMetadata.h
│   ├── SCManagedCapturerSampleMetadata.m
│   ├── SCManagedCapturerState.h
│   ├── SCManagedCapturerState.m
│   ├── SCManagedCapturerState.value
│   ├── SCManagedCapturerStateBuilder.h
│   ├── SCManagedCapturerStateBuilder.m
│   ├── SCManagedCapturerUtils.h
│   ├── SCManagedCapturerUtils.m
│   ├── SCManagedCapturerV1.h
│   ├── SCManagedCapturerV1.m
│   ├── SCManagedCapturerV1_Private.h
│   ├── SCManagedDeviceCapacityAnalyzer.h
│   ├── SCManagedDeviceCapacityAnalyzer.m
│   ├── SCManagedDeviceCapacityAnalyzerHandler.h
│   ├── SCManagedDeviceCapacityAnalyzerHandler.m
│   ├── SCManagedDeviceCapacityAnalyzerListener.h
│   ├── SCManagedDeviceCapacityAnalyzerListenerAnnouncer.h
│   ├── SCManagedDeviceCapacityAnalyzerListenerAnnouncer.mm
│   ├── SCManagedDroppedFramesReporter.h
│   ├── SCManagedDroppedFramesReporter.m
│   ├── SCManagedFrameHealthChecker.h
│   ├── SCManagedFrameHealthChecker.m
│   ├── SCManagedFrontFlashController.h
│   ├── SCManagedFrontFlashController.m
│   ├── SCManagedLegacyStillImageCapturer.h
│   ├── SCManagedLegacyStillImageCapturer.m
│   ├── SCManagedPhotoCapturer.h
│   ├── SCManagedPhotoCapturer.m
│   ├── SCManagedRecordedVideo.h
│   ├── SCManagedRecordedVideo.m
│   ├── SCManagedRecordedVideo.value
│   ├── SCManagedStillImageCapturer.h
│   ├── SCManagedStillImageCapturer.mm
│   ├── SCManagedStillImageCapturerHandler.h
│   ├── SCManagedStillImageCapturerHandler.m
│   ├── SCManagedStillImageCapturer_Protected.h
│   ├── SCManagedVideoARDataSource.h
│   ├── SCManagedVideoCapturer.h
│   ├── SCManagedVideoCapturer.m
│   ├── SCManagedVideoCapturerHandler.h
│   ├── SCManagedVideoCapturerHandler.m
│   ├── SCManagedVideoCapturerLogger.h
│   ├── SCManagedVideoCapturerLogger.m
│   ├── SCManagedVideoCapturerOutputSettings.h
│   ├── SCManagedVideoCapturerOutputSettings.m
│   ├── SCManagedVideoCapturerOutputSettings.value
│   ├── SCManagedVideoCapturerOutputType.h
│   ├── SCManagedVideoCapturerTimeObserver.h
│   ├── SCManagedVideoCapturerTimeObserver.m
│   ├── SCManagedVideoFileStreamer.h
│   ├── SCManagedVideoFileStreamer.m
│   ├── SCManagedVideoFrameSampler.h
│   ├── SCManagedVideoFrameSampler.m
│   ├── SCManagedVideoNoSoundLogger.h
│   ├── SCManagedVideoNoSoundLogger.m
│   ├── SCManagedVideoScanner.h
│   ├── SCManagedVideoScanner.m
│   ├── SCManagedVideoStreamReporter.h
│   ├── SCManagedVideoStreamReporter.m
│   ├── SCManagedVideoStreamer.h
│   ├── SCManagedVideoStreamer.m
│   ├── SCMetalUtils.h
│   ├── SCMetalUtils.m
│   ├── SCScanConfiguration.h
│   ├── SCScanConfiguration.m
│   ├── SCSingleFrameStreamCapturer.h
│   ├── SCSingleFrameStreamCapturer.m
│   ├── SCStillImageCaptureVideoInputMethod.h
│   ├── SCStillImageCaptureVideoInputMethod.m
│   ├── SCTimedTask.h
│   ├── SCTimedTask.m
│   ├── SCVideoCaptureSessionInfo.h
│   ├── StateMachine/
│   │   ├── SCCaptureBaseState.h
│   │   ├── SCCaptureBaseState.m
│   │   ├── SCCaptureStateDelegate.h
│   │   ├── SCCaptureStateMachineBookKeeper.h
│   │   ├── SCCaptureStateMachineBookKeeper.m
│   │   ├── SCCaptureStateMachineContext.h
│   │   ├── SCCaptureStateMachineContext.m
│   │   ├── SCCaptureStateUtil.h
│   │   ├── SCCaptureStateUtil.m
│   │   ├── SCManagedCapturerLogging.h
│   │   └── States/
│   │       ├── SCCaptureImageState.h
│   │       ├── SCCaptureImageState.m
│   │       ├── SCCaptureImageStateTransitionPayload.h
│   │       ├── SCCaptureImageStateTransitionPayload.m
│   │       ├── SCCaptureImageWhileRecordingState.h
│   │       ├── SCCaptureImageWhileRecordingState.m
│   │       ├── SCCaptureImageWhileRecordingStateTransitionPayload.h
│   │       ├── SCCaptureImageWhileRecordingStateTransitionPayload.m
│   │       ├── SCCaptureInitializedState.h
│   │       ├── SCCaptureInitializedState.m
│   │       ├── SCCaptureRecordingState.h
│   │       ├── SCCaptureRecordingState.m
│   │       ├── SCCaptureRecordingStateTransitionPayload.h
│   │       ├── SCCaptureRecordingStateTransitionPayload.m
│   │       ├── SCCaptureRunningState.h
│   │       ├── SCCaptureRunningState.m
│   │       ├── SCCaptureScanningState.h
│   │       ├── SCCaptureScanningState.m
│   │       ├── SCCaptureUninitializedState.h
│   │       ├── SCCaptureUninitializedState.m
│   │       ├── SCStateTransitionPayload.h
│   │       └── SCStateTransitionPayload.m
│   ├── UIScreen+Debug.h
│   └── UIScreen+Debug.m
├── README.md
├── Resource/
│   ├── SCCaptureResource.h
│   └── SCCaptureResource.m
├── Tweaks/
│   ├── SCCameraTweaks.h
│   └── SCCameraTweaks.m
├── UI/
│   ├── AVCameraViewEnums.h
│   ├── Lenses/
│   │   ├── LensButtonZ/
│   │   │   └── SCFeatureLensButtonZ.h
│   │   └── LensSideButton/
│   │       └── SCFeatureLensSideButton.h
│   ├── SCLongPressGestureRecognizer.h
│   └── SCLongPressGestureRecognizer.m
├── VolumeButton/
│   ├── SCCameraVolumeButtonHandler.h
│   └── SCCameraVolumeButtonHandler.m
└── Worker/
    ├── SCCaptureWorker.h
    └── SCCaptureWorker.m

================================================
FILE CONTENTS
================================================

================================================
FILE: BlackCamera/SCBlackCameraDetector.h
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraDetector.m
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraNoOutputDetector.h
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraNoOutputDetector.m
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraPreviewDetector.h
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraPreviewDetector.m
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraReporter.h
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraReporter.m
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraRunningDetector.h
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraRunningDetector.m
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraSessionBlockDetector.h
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraSessionBlockDetector.m
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraViewDetector.h
================================================
//
//  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


================================================
FILE: BlackCamera/SCBlackCameraViewDetector.m
================================================
//
//  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


================================================
FILE: BlackCamera/SCCaptureSessionFixer.h
================================================
//
//  SCCaptureSessionFixer.h
//  Snapchat
//
//  Created by Derek Wang on 05/12/2017.
//

#import "SCBlackCameraNoOutputDetector.h"

#import <Foundation/Foundation.h>

@interface SCCaptureSessionFixer : NSObject <SCBlackCameraDetectorDelegate>

@end


================================================
FILE: BlackCamera/SCCaptureSessionFixer.m
================================================
//
//  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


================================================
FILE: ContextAwareTaskManagement/OWNERS
================================================
--- !OWNERS

version: 2

default:
  jira_project: CCAM
  owners:
    num_required_reviewers: 1
    teams:
    - Snapchat/core-camera-ios
    users:
    - cjiang
    - ljia


================================================
FILE: ContextAwareTaskManagement/Requests/SCContextAwareSnapCreationThrottleRequest.h
================================================
//
//  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


================================================
FILE: ContextAwareTaskManagement/Requests/SCContextAwareSnapCreationThrottleRequest.m
================================================
//
//  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


================================================
FILE: ContextAwareTaskManagement/Triggers/SCSnapCreationTriggers.h
================================================
//
//  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


================================================
FILE: ContextAwareTaskManagement/Triggers/SCSnapCreationTriggers.m
================================================
//
//  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


================================================
FILE: Features/Core/SCFeature.h
================================================
//
//  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


================================================
FILE: Features/Core/SCFeatureContainerView.h
================================================
//
//  SCFeatureContainerView.h
//  SCCamera
//
//  Created by Kristian Bauer on 4/17/18.
//

#import <UIKit/UIKit.h>

@protocol SCFeatureContainerView
- (BOOL)isTapGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer;
- (CGRect)initialCameraTimerFrame;
@end


================================================
FILE: Features/Core/SCFeatureCoordinator.h
================================================
//
//  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


================================================
FILE: Features/Core/SCFeatureCoordinator.m
================================================
//
//  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


================================================
FILE: Features/Core/SCFeatureProvider.h
================================================
//
//  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


================================================
FILE: Features/Flash/SCFeatureFlash.h
================================================
//
//  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


================================================
FILE: Features/Flash/SCFeatureFlashImpl.h
================================================
//
//  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


================================================
FILE: Features/Flash/SCFeatureFlashImpl.m
================================================
//
//  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


================================================
FILE: Features/Flash/SCFlashButton.h
================================================
//
//  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


================================================
FILE: Features/Flash/SCFlashButton.m
================================================
//
//  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


================================================
FILE: Features/HandsFree/SCFeatureHandsFree.h
================================================
//
//  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


================================================
FILE: Features/ImageCapture/SCFeatureImageCapture.h
================================================
//
//  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


================================================
FILE: Features/ImageCapture/SCFeatureImageCaptureImpl.h
================================================
//
//  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


================================================
FILE: Features/ImageCapture/SCFeatureImageCaptureImpl.m
================================================
//
//  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


================================================
FILE: Features/NightMode/SCFeatureNightMode.h
================================================
//
//  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


================================================
FILE: Features/NightMode/SCNightModeButton.h
================================================
//
//  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


================================================
FILE: Features/NightMode/SCNightModeButton.m
================================================
//
//  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


================================================
FILE: Features/Scanning/SCFeatureScanning.h
================================================
//
//  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


================================================
FILE: Features/Shazam/SCFeatureShazam.h
================================================
//
//  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


================================================
FILE: Features/SnapKit/SCFeatureSnapKit.h
================================================
//
//  SCFeatureSnapKit.h
//  SCCamera
//
//  Created by Michel Loenngren on 3/19/18.
//

#import "SCFeature.h"

@class SCCameraDeepLinkMetadata;

@protocol SCFeatureSnapKit <SCFeature>
- (void)setDeepLinkMetadata:(SCCameraDeepLinkMetadata *)metadata;
@end


================================================
FILE: Features/TapToFocus/SCFeatureTapToFocusAndExposure.h
================================================
//
//  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


================================================
FILE: Features/TapToFocus/SCFeatureTapToFocusAndExposureImpl.h
================================================
//
//  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


================================================
FILE: Features/TapToFocus/SCFeatureTapToFocusAndExposureImpl.m
================================================
//
//  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


================================================
FILE: Features/TapToFocus/SCTapAnimationView.h
================================================
//
//  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


================================================
FILE: Features/TapToFocus/SCTapAnimationView.m
================================================
//
//  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


================================================
FILE: Features/ToggleCamera/SCFeatureToggleCamera.h
================================================
//
//  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


================================================
FILE: Features/Zooming/SCFeatureZooming.h
================================================
//
//  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


================================================
FILE: Lens/SCManagedCapturerARImageCaptureProvider.h
================================================
//
//  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


================================================
FILE: Lens/SCManagedCapturerGLViewManagerAPI.h
================================================
//
//  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


================================================
FILE: Lens/SCManagedCapturerLSAComponentTrackerAPI.h
================================================
//
//  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


================================================
FILE: Lens/SCManagedCapturerLensAPI.h
================================================
//
//  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


================================================
FILE: Lens/SCManagedCapturerLensAPIProvider.h
================================================
//
//  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


================================================
FILE: Logging/SCCoreCameraLogger.h
================================================
//
//  SCCoreCameraLogger.h
//  Snapchat
//
//  Created by Chao Pang on 3/6/18.
//

#import <Foundation/Foundation.h>

/**
 *  CAMERA_CREATION_DELAY event
 */
extern NSString *const kSCCameraCreationDelayEventStartTimeKey;
extern NSString *const kSCCameraCreationDelayEventStartTimeAdjustmentKey;
extern NSString *const kSCCameraCreationDelayEventEndTimeKey;
extern NSString *const kSCCameraCreationDelayEventCaptureSessionIdKey;
extern NSString *const kSCCameraCreationDelayEventFilterLensIdKey;
extern NSString *const kSCCameraCreationDelayEventNightModeDetectedKey;
extern NSString *const kSCCameraCreationDelayEventNightModeActiveKey;
extern NSString *const kSCCameraCreationDelayEventCameraApiKey;
extern NSString *const kSCCameraCreationDelayEventCameraLevelKey;
extern NSString *const kSCCameraCreationDelayEventCameraPositionKey;
extern NSString *const kSCCameraCreationDelayEventCameraOpenSourceKey;
extern NSString *const kSCCameraCreationDelayEventContentDurationKey;
extern NSString *const kSCCameraCreationDelayEventMediaTypeKey;
extern NSString *const kSCCameraCreationDelayEventStartTypeKey;
extern NSString *const kSCCameraCreationDelayEventStartSubTypeKey;
extern NSString *const kSCCameraCreationDelayEventAnalyticsVersion;

@interface SCCoreCameraLogger : NSObject

+ (instancetype)sharedInstance;

/**
 *  CAMERA_CREATION_DELAY event
 */
- (void)logCameraCreationDelayEventStartWithCaptureSessionId:(NSString *)captureSessionId
                                                filterLensId:(NSString *)filterLensId
                                      underLowLightCondition:(BOOL)underLowLightCondition
                                           isNightModeActive:(BOOL)isNightModeActive
                                                isBackCamera:(BOOL)isBackCamera
                                                isMainCamera:(BOOL)isMainCamera;

- (void)logCameraCreationDelaySplitPointRecordingGestureFinished;

- (void)logCameraCreationDelaySplitPointStillImageCaptureApi:(NSString *)api;

- (void)logCameraCreationDelaySplitPointPreCaptureOperationRequested;

- (void)logCameraCreationDelaySplitPointPreCaptureOperationFinishedAt:(CFTimeInterval)time;

- (void)updatedCameraCreationDelayWithContentDuration:(CFTimeInterval)duration;

- (void)logCameraCreationDelaySplitPointCameraCaptureContentReady;

- (void)logCameraCreationDelaySplitPointPreviewFinishedPreparation;

- (void)logCameraCreationDelaySplitPointPreviewDisplayedForImage:(BOOL)isImage;

- (void)logCameraCreationDelaySplitPointPreviewAnimationComplete:(BOOL)isImage;

- (void)logCameraCreationDelaySplitPointPreviewFirstFramePlayed:(BOOL)isImage;

- (void)cancelCameraCreationDelayEvent;

@end


================================================
FILE: Logging/SCCoreCameraLogger.m
================================================
//
//  SCCoreCameraLogger.m
//  Snapchat
//
//  Created by Chao Pang on 3/6/18.
//

#import "SCCoreCameraLogger.h"

#import <BlizzardSchema/SCAEvents.h>
#import <SCFoundation/SCQueuePerformer.h>
#import <SCGhostToSnappable/SCGhostToSnappableSignal.h>
#import <SCLogger/SCCameraMetrics+CameraCreationDelay.h>

static const char *kSCCoreCameraLoggerQueueLabel = "com.snapchat.core-camera-logger-queue";

NSString *const kSCCameraCreationDelayEventStartTimeKey = @"start_time";
NSString *const kSCCameraCreationDelayEventStartTimeAdjustmentKey = @"start_time_adjustment";
NSString *const kSCCameraCreationDelayEventEndTimeKey = @"end_time";
NSString *const kSCCameraCreationDelayEventCaptureSessionIdKey = @"capture_session_id";
NSString *const kSCCameraCreationDelayEventFilterLensIdKey = @"filter_lens_id";
NSString *const kSCCameraCreationDelayEventNightModeDetectedKey = @"night_mode_detected";
NSString *const kSCCameraCreationDelayEventNightModeActiveKey = @"night_mode_active";
NSString *const kSCCameraCreationDelayEventCameraApiKey = @"camera_api";
NSString *const kSCCameraCreationDelayEventCameraLevelKey = @"camera_level";
NSString *const kSCCameraCreationDelayEventCameraPositionKey = @"camera_position";
NSString *const kSCCameraCreationDelayEventCameraOpenSourceKey = @"camera_open_source";
NSString *const kSCCameraCreationDelayEventContentDurationKey = @"content_duration";
NSString *const kSCCameraCreationDelayEventMediaTypeKey = @"media_type";
NSString *const kSCCameraCreationDelayEventStartTypeKey = @"start_type";
NSString *const kSCCameraCreationDelayEventStartSubTypeKey = @"start_sub_type";
NSString *const kSCCameraCreationDelayEventAnalyticsVersion = @"ios_v1";

static inline NSUInteger SCTimeToMS(CFTimeInterval time)
{
    return (NSUInteger)(time * 1000);
}

static NSString *SCDictionaryToJSONString(NSDictionary *dictionary)
{
    NSData *dictData = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:nil];
    return [[NSString alloc] initWithData:dictData encoding:NSUTF8StringEncoding];
}

@implementation SCCoreCameraLogger {
    SCQueuePerformer *_performer;
    NSMutableDictionary *_cameraCreationDelayParameters;
    NSMutableDictionary *_cameraCreationDelaySplits;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _cameraCreationDelayParameters = [NSMutableDictionary dictionary];
        _cameraCreationDelaySplits = [NSMutableDictionary dictionary];
        _performer = [[SCQueuePerformer alloc] initWithLabel:kSCCoreCameraLoggerQueueLabel
                                            qualityOfService:QOS_CLASS_UNSPECIFIED
                                                   queueType:DISPATCH_QUEUE_SERIAL
                                                     context:SCQueuePerformerContextCoreCamera];
    }
    return self;
}

+ (instancetype)sharedInstance
{
    static SCCoreCameraLogger *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[SCCoreCameraLogger alloc] init];
    });
    return sharedInstance;
}

// Camera creation delay metrics

- (void)logCameraCreationDelayEventStartWithCaptureSessionId:(NSString *)captureSessionId
                                                filterLensId:(NSString *)filterLensId
                                      underLowLightCondition:(BOOL)underLowLightCondition
                                           isNightModeActive:(BOOL)isNightModeActive
                                                isBackCamera:(BOOL)isBackCamera
                                                isMainCamera:(BOOL)isMainCamera
{
    CFTimeInterval startTime = CACurrentMediaTime();
    [_performer perform:^{
        [_cameraCreationDelayParameters removeAllObjects];
        [_cameraCreationDelaySplits removeAllObjects];
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] = @(startTime);
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventCaptureSessionIdKey] = captureSessionId ?: @"null";
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventFilterLensIdKey] = filterLensId ?: @"null";
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeDetectedKey] = @(underLowLightCondition);
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeActiveKey] = @(isNightModeActive);
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraPositionKey] =
            isBackCamera ? @"back" : @"front";
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraOpenSourceKey] =
            isMainCamera ? @"main_camera" : @"reply_camera";
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTypeKey] = SCLaunchType() ?: @"null";
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartSubTypeKey] = SCLaunchSubType() ?: @"null";
    }];
}

- (void)logCameraCreationDelaySplitPointRecordingGestureFinished
{
    CFTimeInterval time = CACurrentMediaTime();
    [_performer perform:^{
        CFTimeInterval endRecordingTimeOffset =
            time - [_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] doubleValue];
        NSNumber *recordStartTimeMillis =
            (NSNumber *)_cameraCreationDelaySplits[kSCCameraSubmetricsPreCaptureOperationFinished];
        if (recordStartTimeMillis) {
            CFTimeInterval timeDisplacement = ([recordStartTimeMillis doubleValue] / 1000.0) - endRecordingTimeOffset;
            _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeAdjustmentKey] = @(timeDisplacement);
        }
        [self _addSplitPointForKey:kSCCameraSubmetricsRecordingGestureFinished atTime:time];
    }];
}

- (void)logCameraCreationDelaySplitPointStillImageCaptureApi:(NSString *)api
{
    CFTimeInterval time = CACurrentMediaTime();
    [_performer perform:^{
        if (api) {
            _cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraApiKey] = api;
        }
        [self _addSplitPointForKey:kSCCameraSubmetricsPreCaptureOperationRequested atTime:time];
    }];
}

- (void)logCameraCreationDelaySplitPointPreCaptureOperationRequested
{
    CFTimeInterval time = CACurrentMediaTime();
    [_performer perform:^{
        [self _addSplitPointForKey:kSCCameraSubmetricsPreCaptureOperationRequested atTime:time];
    }];
}

- (void)logCameraCreationDelaySplitPointPreCaptureOperationFinishedAt:(CFTimeInterval)time
{
    [_performer perform:^{
        [self _addSplitPointForKey:kSCCameraSubmetricsPreCaptureOperationFinished atTime:time];
    }];
}

- (void)updatedCameraCreationDelayWithContentDuration:(CFTimeInterval)duration
{
    [_performer perform:^{
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventContentDurationKey] = @(SCTimeToMS(duration));
    }];
}

- (void)logCameraCreationDelaySplitPointCameraCaptureContentReady
{
    CFTimeInterval time = CACurrentMediaTime();
    [_performer perform:^{
        [self _addSplitPointForKey:kSCCameraSubmetricsCameraCaptureContentReady atTime:time];
    }];
}

- (void)logCameraCreationDelaySplitPointPreviewFinishedPreparation
{
    CFTimeInterval time = CACurrentMediaTime();
    [_performer perform:^{
        [self _addSplitPointForKey:kSCCameraSubmetricsCameraCaptureContentReady atTime:time];
    }];
}

- (void)logCameraCreationDelaySplitPointPreviewDisplayedForImage:(BOOL)isImage
{
    CFTimeInterval time = CACurrentMediaTime();
    [_performer perform:^{
        [self _addSplitPointForKey:kSCCameraSubmetricsPreviewLayoutReady atTime:time];
    }];
}

- (void)logCameraCreationDelaySplitPointPreviewAnimationComplete:(BOOL)isImage
{
    CFTimeInterval time = CACurrentMediaTime();
    [_performer perform:^{
        [self _addSplitPointForKey:kSCCameraSubmetricsPreviewAnimationFinish atTime:time];
        if (_cameraCreationDelaySplits[kSCCameraSubmetricsPreviewPlayerReady]) {
            [self _completeLogCameraCreationDelayEventWithIsImage:isImage atTime:time];
        }
    }];
}

- (void)logCameraCreationDelaySplitPointPreviewFirstFramePlayed:(BOOL)isImage
{
    CFTimeInterval time = CACurrentMediaTime();
    [_performer perform:^{
        [self _addSplitPointForKey:kSCCameraSubmetricsPreviewPlayerReady atTime:time];
        if (_cameraCreationDelaySplits[kSCCameraSubmetricsPreviewAnimationFinish]) {
            [self _completeLogCameraCreationDelayEventWithIsImage:isImage atTime:time];
        }
    }];
}

- (void)cancelCameraCreationDelayEvent
{
    [_performer perform:^{
        [_cameraCreationDelayParameters removeAllObjects];
        [_cameraCreationDelaySplits removeAllObjects];
    }];
}

#pragma - Private methods

- (void)_completeLogCameraCreationDelayEventWithIsImage:(BOOL)isImage atTime:(CFTimeInterval)time
{
    SCAssertPerformer(_performer);
    if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventCaptureSessionIdKey]) {
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventMediaTypeKey] = isImage ? @"image" : @"video";
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventEndTimeKey] = @(time);
        [self _logCameraCreationDelayBlizzardEvent];
    }
    [_cameraCreationDelayParameters removeAllObjects];
    [_cameraCreationDelaySplits removeAllObjects];
}

- (void)_addSplitPointForKey:(NSString *)key atTime:(CFTimeInterval)time
{
    SCAssertPerformer(_performer);
    if (key) {
        CFTimeInterval timeOffset =
            time - [_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] doubleValue];
        NSNumber *timeAdjustment =
            _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeAdjustmentKey] ?: @(0);
        _cameraCreationDelaySplits[key] = @(SCTimeToMS(timeOffset + [timeAdjustment doubleValue]));
    }
}

- (void)_logCameraCreationDelayBlizzardEvent
{
    SCAssertPerformer(_performer);
    SCASharedCameraMetricParams *sharedCameraMetricsParams = [[SCASharedCameraMetricParams alloc] init];
    [sharedCameraMetricsParams setAnalyticsVersion:kSCCameraCreationDelayEventAnalyticsVersion];
    NSString *mediaType = _cameraCreationDelayParameters[kSCCameraCreationDelayEventMediaTypeKey];
    if (mediaType) {
        if ([mediaType isEqualToString:@"image"]) {
            [sharedCameraMetricsParams setMediaType:SCAMediaType_IMAGE];
        } else if ([mediaType isEqualToString:@"video"]) {
            [sharedCameraMetricsParams setMediaType:SCAMediaType_VIDEO];
        }
    }
    if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeDetectedKey] &&
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeActiveKey]) {
        BOOL isNightModeDetected =
            [_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeDetectedKey] boolValue];
        BOOL isNightModeActive =
            [_cameraCreationDelayParameters[kSCCameraCreationDelayEventNightModeActiveKey] boolValue];
        if (!isNightModeDetected) {
            [sharedCameraMetricsParams setLowLightStatus:SCALowLightStatus_NOT_DETECTED];
        } else if (!isNightModeActive) {
            [sharedCameraMetricsParams setLowLightStatus:SCALowLightStatus_DETECTED];
        } else if (isNightModeActive) {
            [sharedCameraMetricsParams setLowLightStatus:SCALowLightStatus_ENABLED];
        }
    }

    [sharedCameraMetricsParams setPowerMode:[[NSProcessInfo processInfo] isLowPowerModeEnabled]
                                                ? @"LOW_POWER_MODE_ENABLED"
                                                : @"LOW_POWER_MODE_DISABLED"];
    [sharedCameraMetricsParams
        setFilterLensId:_cameraCreationDelayParameters[kSCCameraCreationDelayEventFilterLensIdKey] ?: @"null"];
    [sharedCameraMetricsParams
        setCaptureSessionId:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCaptureSessionIdKey] ?: @"null"];
    [sharedCameraMetricsParams
        setCameraApi:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraApiKey] ?: @"null"];
    [sharedCameraMetricsParams
        setCameraPosition:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraPositionKey] ?: @"null"];
    [sharedCameraMetricsParams
        setCameraOpenSource:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraOpenSourceKey] ?: @"null"];
    [sharedCameraMetricsParams
        setCameraLevel:_cameraCreationDelayParameters[kSCCameraCreationDelayEventCameraLevelKey] ?: @"null"];
    [sharedCameraMetricsParams
        setStartType:_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTypeKey] ?: @"null"];
    [sharedCameraMetricsParams
        setStartSubType:_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartSubTypeKey] ?: @"null"];
    [sharedCameraMetricsParams setSplits:SCDictionaryToJSONString(_cameraCreationDelaySplits)];

    SCACameraSnapCreateDelay *creationDelay = [[SCACameraSnapCreateDelay alloc] init];
    if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] &&
        _cameraCreationDelayParameters[kSCCameraCreationDelayEventEndTimeKey]) {
        double startTime = [_cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeKey] doubleValue];
        double endTime = [_cameraCreationDelayParameters[kSCCameraCreationDelayEventEndTimeKey] doubleValue];
        NSNumber *timeAdjustment =
            _cameraCreationDelayParameters[kSCCameraCreationDelayEventStartTimeAdjustmentKey] ?: @(0);
        [creationDelay setLatencyMillis:SCTimeToMS(endTime - startTime + [timeAdjustment doubleValue])];
    } else {
        [creationDelay setLatencyMillis:0];
    }

    if (_cameraCreationDelayParameters[kSCCameraCreationDelayEventContentDurationKey]) {
        [creationDelay
            setContentDurationMillis:SCTimeToMS(
                                         [_cameraCreationDelayParameters[kSCCameraCreationDelayEventContentDurationKey]
                                             doubleValue])];
    } else {
        [creationDelay setContentDurationMillis:0];
    }
    [creationDelay setSharedCameraMetricParams:sharedCameraMetricsParams];
    [[SCLogger sharedInstance] logUserTrackedEvent:creationDelay];
}

@end


================================================
FILE: Logging/SCLogger+Camera.h
================================================
//
//  SCLogger+Camera.h
//  Snapchat
//
//  Created by Derek Peirce on 5/8/17.
//  Copyright © 2017 Snapchat, Inc. All rights reserved.
//

#import "AVCameraViewEnums.h"

#import <SCBase/SCSignPost.h>
#import <SCLogger/SCLogger.h>

#import <CoreMedia/CoreMedia.h>

typedef NS_ENUM(NSUInteger, CameraCreationDelayLoggingStatus) {
    CAMERA_CREATION_DELAY_LOGGING_START,
    CAMERA_CREATION_DELAY_LOGGINT_LAST_STEP,
    CAMERA_CREATION_DELAY_LOGGING_END,
};

@interface SCLogger (Camera)

@property (nonatomic, strong) NSNumber *cameraCreationDelayLoggingStatus;

- (void)logCameraCreationStartWithMethod:(SCCameraRecordingMethod)method
                           lensesEnabled:(BOOL)lensesEnabled
                            activeLensId:(NSString *)activeLensId
                        captureSessionId:(NSString *)captureSessionId;
- (void)logStillImageCaptureApi:(NSString *)api;
- (void)logPreCaptureOperationRequestedAt:(CFTimeInterval)requestTime;
- (void)logPreCaptureOperationFinishedAt:(CFTimeInterval)time;
- (void)logCameraCaptureRecordingGestureFinishedAtTime:(CFTimeInterval)endRecordingTime;
- (void)logCameraCaptureFinishedWithDuration:(CFTimeInterval)duration;
- (void)logCameraCaptureContentReady;
- (void)logPreviewFinishedPreparation;
- (void)logPreviewDisplayedForImage:(BOOL)isImage;
- (void)logPreviewAnimationComplete:(BOOL)isImage;
- (void)logPreviewFirstFramePlayed:(BOOL)isImage;
- (void)cancelCameraCreationEvent;

- (void)logRecordingMayBeTooShortWithMethod:(SCCameraRecordingMethod)method;
- (void)logRecordingWasTooShortWithFirstFrame:(CMTime)firstFrame
                            frontFacingCamera:(BOOL)isFrontFacing
                                  cameraFlips:(NSInteger)cameraFlips;

- (void)logManagedCapturerSettingFailure:(NSString *)settingTask error:(NSError *)error;
- (void)logCameraExposureAdjustmentDelayStart;
- (void)logCameraExposureAdjustmentDelayEndWithStrategy:(NSString *)strategy;
- (void)logCameraCreationDelaySubMetricsStartWithSignCode:(kSCSignPostCodeEnum)signPostCode;
- (void)logCameraCreationDelaySubMetricsEndWithSignCode:(kSCSignPostCodeEnum)signPostCod;

@end


================================================
FILE: Logging/SCLogger+Camera.m
================================================
//
//  SCLogger+Camera.m
//  Snapchat
//
//  Created by Derek Peirce on 5/8/17.
//  Copyright © 2017 Snapchat, Inc. All rights reserved.
//

#import "SCLogger+Camera.h"

#import "SCCameraTweaks.h"

#import <SCFoundation/SCTrace.h>
#import <SCFoundation/SCTraceODPCompatible.h>
#import <SCLogger/SCCameraMetrics+CameraCreationDelay.h>
#import <SCLogger/SCCameraMetrics.h>
#import <SCLogger/SCLogger+Performance.h>

#import <objc/runtime.h>

@implementation SCLogger (Camera)

@dynamic cameraCreationDelayLoggingStatus;

- (NSNumber *)cameraCreationDelayLoggingStatus
{
    return objc_getAssociatedObject(self, @selector(cameraCreationDelayLoggingStatus));
}

- (void)setCameraCreationDelayLoggingStatus:(NSNumber *)status
{
    objc_setAssociatedObject(self, @selector(cameraCreationDelayLoggingStatus), status,
                             OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)shouldLogCameraCreationDelay
{
    return [[self cameraCreationDelayLoggingStatus] intValue] != CAMERA_CREATION_DELAY_LOGGING_END;
}

- (void)logCameraCreationDelayEnd
{
    if ([[self cameraCreationDelayLoggingStatus] intValue] == CAMERA_CREATION_DELAY_LOGGINT_LAST_STEP) {
        SCTraceSignPostEndForMetrics(kSCSignPostCameraCreationDelay, 0, 0, 0, 0);
        [self setCameraCreationDelayLoggingStatus:@(CAMERA_CREATION_DELAY_LOGGING_END)];
    } else {
        [self setCameraCreationDelayLoggingStatus:@(CAMERA_CREATION_DELAY_LOGGINT_LAST_STEP)];
    }
}

- (void)logCameraCreationStartWithMethod:(SCCameraRecordingMethod)method
                           lensesEnabled:(BOOL)lensesEnabled
                            activeLensId:(NSString *)activeLensId
                        captureSessionId:(NSString *)captureSessionId
{
    NSMutableDictionary *parameters = [@{
        @"lens_ui_enabled" : @(lensesEnabled),
        @"analytics_version" : kSCCameraDelayEventVersion,
        @"method" : @(method),
    } mutableCopy];
    if (lensesEnabled && activeLensId) {
        [parameters setObject:activeLensId forKey:@"lens_id"];
    }
    if (captureSessionId) {
        [parameters setObject:captureSessionId forKey:@"capture_session_id"];
    }
    [self setCameraCreationDelayLoggingStatus:@(CAMERA_CREATION_DELAY_LOGGING_START)];
    [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraCreationDelay];
    [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraRecordingGestureFinished];
    [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreCaptureOperationRequested];
    [[SCLogger sharedInstance] logTimedEventStart:kSCCameraCaptureDelayEvent
                                         uniqueId:@""
                                    isUniqueEvent:NO
                                       parameters:parameters
                               shouldLogStartTime:YES];
}

- (void)logCameraExposureAdjustmentDelayStart
{
    [[SCLogger sharedInstance] logTimedEventStart:kSCCameraExposureAdjustmentDelay
                                         uniqueId:@""
                                    isUniqueEvent:NO
                                       parameters:nil
                               shouldLogStartTime:YES];
}

- (void)logCameraExposureAdjustmentDelayEndWithStrategy:(NSString *)strategy
{
    [[SCLogger sharedInstance] logTimedEventEnd:kSCCameraExposureAdjustmentDelay
                                       uniqueId:@""
                                     parameters:@{
                                         @"strategy" : strategy
                                     }];
}

- (void)logCameraCaptureRecordingGestureFinishedAtTime:(CFTimeInterval)endRecordingTime
{
    [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraRecordingGestureFinished];
    [[SCLogger sharedInstance]
        updateLogTimedEvent:kSCCameraCaptureDelayEvent
                   uniqueId:@""
                     update:^(NSMutableDictionary *startParameters) {
                         NSMutableDictionary *eventParameters =
                             startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
                         NSNumber *recordStartTime =
                             (NSNumber *)eventParameters[kSCCameraSubmetricsPreCaptureOperationFinished];
                         CFTimeInterval endRecordingTimeOffset =
                             endRecordingTime -
                             [startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventTimeKey] doubleValue];
                         if (recordStartTime) {
                             CFTimeInterval timeDisplacement =
                                 ([recordStartTime doubleValue] / 1000.0) - endRecordingTimeOffset;
                             [eventParameters setObject:@(timeDisplacement)
                                                 forKey:SCPerformanceMetricsKey.kSCLoggerStartEventTimeAdjustmentKey];
                         }
                         [self addSplitPoint:kSCCameraSubmetricsRecordingGestureFinished
                                      atTime:endRecordingTime
                                     toEvent:startParameters];
                     }];
}

- (void)logStillImageCaptureApi:(NSString *)api
{
    [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreCaptureOperationRequested];
    [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreCaptureOperationFinished];
    [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraCaptureContentReady];
    CFTimeInterval requestTime = CACurrentMediaTime();
    [self updateLogTimedEvent:kSCCameraCaptureDelayEvent
                     uniqueId:@""
                       update:^(NSMutableDictionary *startParameters) {
                           NSMutableDictionary *eventParameters =
                               startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
                           [eventParameters setObject:api forKey:@"api_type"];
                           [eventParameters setObject:@(1) forKey:@"camera_api_level"];
                           [self addSplitPoint:@"PRE_CAPTURE_OPERATION_REQUESTED"
                                        atTime:requestTime
                                       toEvent:startParameters];
                       }];
}

- (void)logPreCaptureOperationRequestedAt:(CFTimeInterval)requestTime
{
    [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreCaptureOperationRequested];
    [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreCaptureOperationFinished];
    [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraCaptureContentReady];
    [self updateLogTimedEvent:kSCCameraCaptureDelayEvent
                     uniqueId:@""
                   splitPoint:kSCCameraSubmetricsPreCaptureOperationRequested
                         time:requestTime];
}

- (void)logPreCaptureOperationFinishedAt:(CFTimeInterval)time
{
    [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreCaptureOperationFinished];
    [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreviewPlayerReady];
    [self updateLogTimedEvent:kSCCameraCaptureDelayEvent
                     uniqueId:@""
                   splitPoint:kSCCameraSubmetricsPreCaptureOperationFinished
                         time:time];
}

- (void)logCameraCaptureFinishedWithDuration:(CFTimeInterval)duration
{
    [[SCLogger sharedInstance]
        updateLogTimedEvent:kSCCameraCaptureDelayEvent
                   uniqueId:@""
                     update:^(NSMutableDictionary *startParameters) {
                         NSMutableDictionary *eventParameters =
                             startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
                         [eventParameters setObject:@(SCTimeInMillisecond(duration)) forKey:@"content_duration"];
                     }];
}

- (void)logCameraCaptureContentReady
{
    [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraCaptureContentReady];
    [[SCLogger sharedInstance] updateLogTimedEvent:kSCCameraCaptureDelayEvent
                                          uniqueId:@""
                                        splitPoint:kSCCameraSubmetricsCameraCaptureContentReady];
}

- (void)logPreviewFinishedPreparation
{
    [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewFinishPreparation];
    [self logCameraCreationDelaySubMetricsStartWithSignCode:kSCSignPostCameraPreviewAnimationFinish];
    [self updateLogTimedEvent:kSCCameraCaptureDelayEvent
                     uniqueId:@""
                   splitPoint:kSCCameraSubmetricsPreviewFinishPreparation];
}

- (void)logPreviewDisplayedForImage:(BOOL)isImage
{
    [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewLayoutReady];
    [self updateLogTimedEvent:kSCCameraCaptureDelayEvent uniqueId:@"" splitPoint:kSCCameraSubmetricsPreviewLayoutReady];
}

- (void)logPreviewAnimationComplete:(BOOL)isImage
{
    [self updateLogTimedEvent:kSCCameraCaptureDelayEvent
                     uniqueId:@""
                   splitPoint:kSCCameraSubmetricsPreviewAnimationFinish];
    [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewAnimationFinish];
    [self logCameraCreationDelayEnd];
    [self conditionallyLogTimedEventEnd:kSCCameraCaptureDelayEvent
                               uniqueId:@""
                             parameters:@{
                                 @"type" : isImage ? @"image" : @"video",
                             }
                              shouldLog:^BOOL(NSDictionary *startParameters) {
                                  // For video, PREVIEW_PLAYER_READY and PREVIEW_ANIMATION_FINISH can happen in either
                                  // order. So here we check for existence of this key, and end timer if the other
                                  // event have happened.
                                  NSMutableDictionary *eventParameters =
                                      startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
                                  return eventParameters[kSCCameraSubmetricsPreviewPlayerReady] != nil;
                              }];
}

- (void)logPreviewFirstFramePlayed:(BOOL)isImage
{
    [self updateLogTimedEvent:kSCCameraCaptureDelayEvent uniqueId:@"" splitPoint:kSCCameraSubmetricsPreviewPlayerReady];
    [self logCameraCreationDelaySubMetricsEndWithSignCode:kSCSignPostCameraPreviewPlayerReady];
    [self logCameraCreationDelayEnd];
    [self conditionallyLogTimedEventEnd:kSCCameraCaptureDelayEvent
                               uniqueId:@""
                             parameters:@{
                                 @"type" : isImage ? @"image" : @"video",
                             }
                              shouldLog:^BOOL(NSDictionary *startParameters) {
                                  NSMutableDictionary *eventParameters =
                                      startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
                                  // See the comment above for PREVIEW_PLAYER_READY and PREVIEW_ANIMATION_FINISH.
                                  return eventParameters[kSCCameraSubmetricsPreviewAnimationFinish] != nil;
                              }];
}

- (void)cancelCameraCreationEvent
{
    [self cancelLogTimedEvent:kSCCameraCaptureDelayEvent uniqueId:@""];
}

- (void)logRecordingMayBeTooShortWithMethod:(SCCameraRecordingMethod)method
{
    [[SCLogger sharedInstance] cancelLogTimedEvent:kSCCameraMetricsRecordingTooShort uniqueId:@""];
    [[SCLogger sharedInstance] logTimedEventStart:kSCCameraMetricsRecordingTooShort
                                         uniqueId:@""
                                    isUniqueEvent:NO
                                       parameters:@{
                                           @"method" : @(method),
                                           @"analytics_version" : kSCCameraRecordingTooShortVersion,
                                       }
                               shouldLogStartTime:YES];
}

- (void)logRecordingWasTooShortWithFirstFrame:(CMTime)firstFrame
                            frontFacingCamera:(BOOL)isFrontFacing
                                  cameraFlips:(NSInteger)cameraFlips
{
    [self logTimedEventEnd:kSCCameraMetricsRecordingTooShort
                  uniqueId:@""
                    update:^(NSDictionary *startParameters, CFTimeInterval eventEndTime, CFTimeInterval adjustedTime) {
                        NSMutableDictionary *eventParameters =
                            startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventParametersKey];
                        if (CMTIME_IS_VALID(firstFrame)) {
                            CFTimeInterval startTime =
                                [startParameters[SCPerformanceMetricsKey.kSCLoggerStartEventTimeKey] doubleValue];
                            CFTimeInterval firstFrameRelative = CMTimeGetSeconds(firstFrame) - startTime;
                            [eventParameters setObject:@(firstFrameRelative) forKey:@"first_frame_s"];
                        }
                        [eventParameters setObject:@(isFrontFacing) forKey:@"is_front_facing"];
                        if (cameraFlips) {
                            [eventParameters setObject:@(cameraFlips > 0) forKey:@"has_camera_been_flipped"];
                        }
                    }];
}

- (void)logManagedCapturerSettingFailure:(NSString *)settingTask error:(NSError *)error
{
    NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
    parameters[@"setting_task"] = settingTask;
    if (error) {
        parameters[@"setting error"] = error;
    }
    [[SCLogger sharedInstance] logTimedEventEnd:kSCCameraManagedCaptureSettingFailure
                                       uniqueId:@""
                                     parameters:parameters];
}

- (void)logCameraCreationDelaySubMetricsStartWithSignCode:(kSCSignPostCodeEnum)signPostCode
{
    if ([self shouldLogCameraCreationDelay]) {
        SCTraceSignPostStartForMetrics(signPostCode, 0, 0, 0, 0);
    }
}

- (void)logCameraCreationDelaySubMetricsEndWithSignCode:(kSCSignPostCodeEnum)signPostCode
{
    if ([self shouldLogCameraCreationDelay]) {
        SCTraceSignPostEndForMetrics(signPostCode, 0, 0, 0, 0);
    }
}

@end


================================================
FILE: Logging/SCManiphestTicketCreator.h
================================================
//
//  SCManiphestTicketCreator.h
//  SCCamera
//
//  Created by Michel Loenngren on 4/16/18.
//

#import <Foundation/Foundation.h>

/**
 Protocol for filing jira tickets and beta s2r.
 */
@protocol SCManiphestTicketCreator

- (void)createAndFile:(NSData *)image
         creationTime:(long)reportCreationTime
          description:(NSString *)bugDescription
                email:(NSString *)otherEmail
              project:(NSString *)projectName
           subproject:(NSString *)subprojectName;

- (void)createAndFileBetaReport:(NSString *)msg;

@end


================================================
FILE: ManagedCapturer/ARConfiguration+SCConfiguration.h
================================================
//
//  ARConfiguration+SCConfiguration.h
//  Snapchat
//
//  Created by Max Goedjen on 11/7/17.
//

#import "SCManagedCaptureDevice.h"

#import <ARKit/ARKit.h>

@interface ARConfiguration (SCConfiguration)

+ (BOOL)sc_supportedForDevicePosition:(SCManagedCaptureDevicePosition)position;
+ (ARConfiguration *_Nullable)sc_configurationForDevicePosition:(SCManagedCaptureDevicePosition)position;

@end


================================================
FILE: ManagedCapturer/ARConfiguration+SCConfiguration.m
================================================
//
//  ARConfiguration+SCConfiguration.m
//  Snapchat
//
//  Created by Max Goedjen on 11/7/17.
//

#import "ARConfiguration+SCConfiguration.h"

#import "SCCapturerDefines.h"

@implementation ARConfiguration (SCConfiguration)

+ (BOOL)sc_supportedForDevicePosition:(SCManagedCaptureDevicePosition)position
{
    return [[[self sc_configurationForDevicePosition:position] class] isSupported];
}

+ (ARConfiguration *)sc_configurationForDevicePosition:(SCManagedCaptureDevicePosition)position
{
    if (@available(iOS 11.0, *)) {
        if (position == SCManagedCaptureDevicePositionBack) {
            ARWorldTrackingConfiguration *config = [[ARWorldTrackingConfiguration alloc] init];
            config.planeDetection = ARPlaneDetectionHorizontal;
            config.lightEstimationEnabled = NO;
            return config;
        } else {
#ifdef SC_USE_ARKIT_FACE
            return [[ARFaceTrackingConfiguration alloc] init];
#endif
        }
    }
    return nil;
}

@end


================================================
FILE: ManagedCapturer/AVCaptureConnection+InputDevice.h
================================================
//
//  AVCaptureConnection+InputDevice.h
//  Snapchat
//
//  Created by William Morriss on 1/20/15
//  Copyright (c) 2015 Snapchat, Inc. All rights reserved.
//

#import <AVFoundation/AVFoundation.h>

@interface AVCaptureConnection (InputDevice)

- (AVCaptureDevice *)inputDevice;

@end


================================================
FILE: ManagedCapturer/AVCaptureConnection+InputDevice.m
================================================
//
//  AVCaptureConnection+InputDevice.m
//  Snapchat
//
//  Created by William Morriss on 1/20/15
//  Copyright (c) 2015 Snapchat, Inc. All rights reserved.
//

#import "AVCaptureConnection+InputDevice.h"

#import <SCFoundation/SCAssertWrapper.h>

@implementation AVCaptureConnection (InputDevice)

- (AVCaptureDevice *)inputDevice
{
    NSArray *inputPorts = self.inputPorts;
    AVCaptureInputPort *port = [inputPorts firstObject];
    SCAssert([port.input isKindOfClass:[AVCaptureDeviceInput class]], @"unexpected port");
    AVCaptureDeviceInput *deviceInput = (AVCaptureDeviceInput *)port.input;
    AVCaptureDevice *device = deviceInput.device;
    return device;
}

@end


================================================
FILE: ManagedCapturer/AVCaptureDevice+ConfigurationLock.h
================================================
//
//  AVCaptureDevice+ConfigurationLock.h
//  Snapchat
//
//  Created by Derek Peirce on 4/19/17.
//  Copyright © 2017 Snapchat, Inc. All rights reserved.
//

#import <AVFoundation/AVFoundation.h>
#import <Foundation/Foundation.h>

@interface AVCaptureDevice (ConfigurationLock)

/*
 The following method will lock this AVCaptureDevice, run the task, then unlock the device.
 The task is usually related to set AVCaptureDevice.
 It will return a boolean telling you whether or not your task ran successfully. You can use the boolean to adjust your
 strategy to handle this failure. For some cases, we don't have a good mechanism to handle the failure. E.g. if we want
 to re-focus, but failed to do so. What is next step? Pop up a alert view to user? If yes, it is intrusive, if not, user
 will get confused. Just because the error handling is difficulty, we would like to notify you if the task fails.
 If the task does not run successfully. We will log an event using SCLogger for better visibility.
 */
- (BOOL)runTask:(NSString *)taskName withLockedConfiguration:(void (^)(void))task;

/*
 The following method has the same function as the above one.
 The difference is that it retries the operation for certain times. Please give a number below or equal 2.
 When retry equals 0, we will only try to lock for once.
 When retry equals 1, we will retry once if the 1st try fails.
 ....
 */
- (BOOL)runTask:(NSString *)taskName withLockedConfiguration:(void (^)(void))task retry:(NSUInteger)retryTimes;

@end


================================================
FILE: ManagedCapturer/AVCaptureDevice+ConfigurationLock.m
================================================
//
//  AVCaptureDevice+ConfigurationLock.m
//  Snapchat
//
//  Created by Derek Peirce on 4/19/17.
//  Copyright © 2017 Snapchat, Inc. All rights reserved.
//

#import "AVCaptureDevice+ConfigurationLock.h"

#import "SCLogger+Camera.h"

#import <SCFoundation/SCAssertWrapper.h>
#import <SCFoundation/SCLog.h>
#import <SCLogger/SCLogger.h>

@implementation AVCaptureDevice (ConfigurationLock)

- (BOOL)runTask:(NSString *)taskName withLockedConfiguration:(void (^)(void))task
{
    return [self runTask:taskName withLockedConfiguration:task retry:0];
}

- (BOOL)runTask:(NSString *)taskName withLockedConfiguration:(void (^)(void))task retry:(NSUInteger)retryTimes
{
    SCAssert(taskName, @"camera logger taskString should not be empty");
    SCAssert(retryTimes <= 2 && retryTimes >= 0, @"retry times should be equal to or below 2.");
    NSError *error = nil;
    BOOL deviceLockSuccess = NO;
    NSUInteger retryCounter = 0;
    while (retryCounter <= retryTimes && !deviceLockSuccess) {
        deviceLockSuccess = [self lockForConfiguration:&error];
        retryCounter++;
    }
    if (deviceLockSuccess) {
        task();
        [self unlockForConfiguration];
        SCLogCoreCameraInfo(@"AVCapture Device setting success, task:%@ tryCount:%zu", taskName,
                            (unsigned long)retryCounter);
    } else {
        SCLogCoreCameraError(@"AVCapture Device Encountered error when %@ %@", taskName, error);
        [[SCLogger sharedInstance] logManagedCapturerSettingFailure:taskName error:error];
    }
    return deviceLockSuccess;
}

@end


================================================
FILE: ManagedCapturer/CapturerV2/Configuration/SCCaptureConfiguration.h
================================================
//
//  SCCaptureConfiguration.h
//  Snapchat
//
//  Created by Lin Jia on 10/3/17.
//
//

#import "SCCaptureConfigurationAnnouncer.h"
#import "SCManagedCaptureDevice.h"
#import "SCManagedCapturerState.h"
#import "SCVideoCaptureSessionInfo.h"

#import <SCFoundation/SCQueuePerformer.h>

#import <Looksery/LSAGLView.h>

#import <Foundation/Foundation.h>

/*
 SCCaptureConfiguration is the configuration class which is going to be used for customer to configure camera. This is
 how to use it:

 SCCaptureConfiguration *configuration = [SCCaptureConfiguration new];

 // Conduct the setting here.
 e.g:
 configuration.torchActive = YES;

 // Commit your configuration
 [captureConfigurator commitConfiguration:configuration
                        completionHandler:handler]

 Here are several interesting facts about SCCaptureConfiguration:
 1) Though SCCaptureConfiguration has so many parameters, you don't need to care the parameters which you do not intend
to set. For example, if you only want to set night mode active, here is the code:

 SCCaptureConfiguration *configuration = [SCCaptureConfiguration new];

 configuration.isNightModeActive = YES;

 [captureConfigurator commitConfiguration:configuration
                        completionHandler:handler]

 That is it.

 2) you can set multiple configuration settings, then commit, before you commit, nothing will happen, e.g.:

 SCCaptureConfiguration *configuration = [SCCaptureConfiguration new];

 configuration.isNightModeActive = YES;
 configuration.zoomFactor = 5;
 configuration.lensesActive = YES;

 [captureConfigurator commitConfiguration:configuration
                        completionHandler:handler]

 3) commit a configuration means the configuration is gone. If you set parameters on configuration after it is commited,
it will crash on debug build, and on other builds such as production, the setting will be ignored, e.g.:

 SCCaptureConfiguration *configuration = [SCCaptureConfiguration new];

 configuration.isNightModeActive = YES;

 [captureConfigurator commitConfiguration:configuration
                        completionHandler:handler]

 // The line below will crash on debug, and ignored on other builds.
 configuration.zoomFactor = 5;

 4) commiting a configuration is an atomic action. That means all changes customers want to have on camera will happen
in a group. If 2 customers commit at the same time, we will handle them one by one.

 5) We are still figuring out what parameters should be in this configuration, parameters could be added or deleted
 later. In the end, the configuration is going to be the only way customers confige the camera.

 */

@interface SCCaptureConfiguration : NSObject

@property (nonatomic, assign) BOOL isRunning;

@property (nonatomic, assign) BOOL isNightModeActive;

@property (nonatomic, assign) BOOL lowLightCondition;

@property (nonatomic, assign) BOOL adjustingExposure;

@property (nonatomic, assign) SCManagedCaptureDevicePosition devicePosition;

@property (nonatomic, assign) CGFloat zoomFactor;

@property (nonatomic, assign) BOOL flashSupported;

@property (nonatomic, assign) BOOL torchSupported;

@property (nonatomic, assign) BOOL flashActive;

@property (nonatomic, assign) BOOL torchActive;

@property (nonatomic, assign) BOOL lensesActive;

@property (nonatomic, assign) BOOL arSessionActive;

@property (nonatomic, assign) BOOL liveVideoStreaming;

@property (nonatomic, strong) AVCaptureVideoPreviewLayer *videoPreviewLayer;

@property (nonatomic, strong) LSAGLView *videoPreviewGLView;

@property (nonatomic, assign) SCVideoCaptureSessionInfo captureSessionInfo;

@end


================================================
FILE: ManagedCapturer/CapturerV2/Configuration/SCCaptureConfiguration.m
================================================
//
//  SCCaptureConfiguration.m
//  Snapchat
//
//  Created by Lin Jia on 10/3/17.
//
//

#import "SCCaptureConfiguration.h"
#import "SCCaptureConfiguration_Private.h"

#import <SCFoundation/SCAppEnvironment.h>
#import <SCFoundation/SCAssertWrapper.h>

@interface SCCaptureConfiguration () {
    BOOL _sealed;
    NSMutableSet<SCCaptureConfigurationDirtyKey *> *_dirtyKeys;
}
@end

@implementation SCCaptureConfiguration

- (instancetype)init
{
    self = [super init];
    if (self) {
        _dirtyKeys = [[NSMutableSet<SCCaptureConfigurationDirtyKey *> alloc] init];
        _sealed = NO;
    }
    return self;
}

- (void)setIsRunning:(BOOL)running
{
    if ([self _configurationSealed]) {
        return;
    }
    _isRunning = running;
    [_dirtyKeys addObject:@(SCCaptureConfigurationKeyIsRunning)];
}

/*
 All set methods will be added later. They follow the format of setIsRunning.
 */

@end

@implementation SCCaptureConfiguration (privateMethods)

- (NSArray *)dirtyKeys
{
    if (!_sealed && SCIsDebugBuild()) {
        SCAssert(NO, @"Configuration not sealed yet, setting is still happening!");
    }
    return [_dirtyKeys allObjects];
}

- (void)seal
{
    _sealed = YES;
}

- (BOOL)_configurationSealed
{
    if (_sealed) {
        if (SCIsDebugBuild()) {
            SCAssert(NO, @"Try to set property after commit configuration to configurator");
        }
        return YES;
    } else {
        return NO;
    }
}

@end


================================================
FILE: ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationAnnouncer.h
================================================
//
//  SCCaptureConfigurationAnnouncer.h
//  Snapchat
//
//  Created by Lin Jia on 10/2/17.
//
//

#import "SCCaptureConfigurationListener.h"

#import <Foundation/Foundation.h>

/*
 All APIs are thread safe. Announcer will not retain your object. So even if customer forgets to call remove listener,
 it will not create zombie objects.
 */
@interface SCCaptureConfigurationAnnouncer : NSObject

/*
 When customer adds an object to be a listener, that object will receive an update of current truth. That is the chance
 for the object to do adjustment according to the current configuration of the camera.
 */
- (void)addListener:(id<SCCaptureConfigurationListener>)listener;

- (void)removeListener:(id<SCCaptureConfigurationListener>)listener;

@end


================================================
FILE: ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationAnnouncer.m
================================================
//
//  SCCaptureConfigurationAnnouncer.m
//  Snapchat
//
//  Created by Lin Jia on 10/2/17.
//
//

#import "SCCaptureConfigurationAnnouncer.h"
#import "SCCaptureConfigurationAnnouncer_Private.h"

#import "SCCaptureConfigurator.h"

#import <SCFoundation/SCAssertWrapper.h>
#import <SCFoundation/SCPerforming.h>

@interface SCCaptureConfigurationAnnouncer () {
    NSHashTable<id<SCCaptureConfigurationListener>> *_listeners;
    SCQueuePerformer *_performer;
    __weak SCCaptureConfigurator *_configurator;
}
@end

@implementation SCCaptureConfigurationAnnouncer

- (instancetype)initWithPerformer:(SCQueuePerformer *)performer configurator:(SCCaptureConfigurator *)configurator
{
    self = [super init];
    if (self) {
        _listeners = [NSHashTable<id<SCCaptureConfigurationListener>> hashTableWithOptions:NSHashTableWeakMemory];
        SCAssert(performer, @"performer should not be nil");
        _performer = performer;
        _configurator = configurator;
    }
    return self;
}

- (void)addListener:(id<SCCaptureConfigurationListener>)listener
{
    [_performer perform:^{
        SCAssert(listener, @"listener should not be nil");
        [_listeners addObject:listener];
        [listener captureConfigurationDidChangeTo:_configurator.currentConfiguration];
    }];
}

- (void)removeListener:(id<SCCaptureConfigurationListener>)listener
{
    [_performer perform:^{
        SCAssert(listener, @"listener should not be nil");
        [_listeners removeObject:listener];
    }];
}

- (void)deliverConfigurationChange:(id<SCManagedCapturerState>)configuration
{
    SCAssertPerformer(_performer);
    for (id<SCCaptureConfigurationListener> listener in _listeners) {
        [listener captureConfigurationDidChangeTo:configuration];
    }
}

- (void)dealloc
{
    [_listeners removeAllObjects];
}
@end


================================================
FILE: ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationAnnouncer_Private.h
================================================
//
//  SCCaptureConfigurationAnnouncer_Private.h
//  Snapchat
//
//  Created by Lin Jia on 10/2/17.
//
//

#import "SCCaptureConfigurationAnnouncer.h"
#import "SCManagedCapturerState.h"

#import <SCFoundation/SCQueuePerformer.h>

@class SCCaptureConfigurator;

/*
 This private header is only going to be used by SCCaptureConfigurator. Other customers should only use the public
 header.
 */
@interface SCCaptureConfigurationAnnouncer ()
/*
 The announcer is going to be instantiated by SCCaptureConfigurator. It will take in a queue performer. The design is
 that announcer and configurator is going to share the same serial queue to avoid racing. This is something we could
 change later.
 */
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer configurator:(SCCaptureConfigurator *)configurator;

/*
 The API below is called by configurator to notify listener that configuration has changed.
 */
- (void)deliverConfigurationChange:(id<SCManagedCapturerState>)configuration;

@end


================================================
FILE: ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationListener.h
================================================
//
//  SCCaptureConfigurationListener.h
//  Snapchat
//
//  Created by Lin Jia on 10/2/17.
//

#import "SCManagedCapturerState.h"

#import <Foundation/Foundation.h>

@class SCCaptureConfiguration;

/*
 As a listener to configuration of camera core, you will get an update whenever the configuration changes, and you will
 receive an immutable state object for the current truth.
 */

@protocol SCCaptureConfigurationListener <NSObject>

- (void)captureConfigurationDidChangeTo:(id<SCManagedCapturerState>)state;

@end


================================================
FILE: ManagedCapturer/CapturerV2/Configuration/SCCaptureConfiguration_Private.h
================================================
//
//  SCCaptureConfiguration_Private.h
//  Snapchat
//
//  Created by Lin Jia on 10/3/17.
//
//

#import "SCCaptureConfiguration_Private.h"

typedef NSNumber SCCaptureConfigurationDirtyKey;

/*
 The key values to identify dirty keys in SCCaptureConfiguration.
 Dirty key is defined as the key customer changes.

 e.g. if customer toggle device position. Dirty keys will have SCCaptureConfigurationKeyDevicePosition.

 It is not complete, and it is only a draft now. It
 will be gradually tuned while we work on the APIs.
 */

typedef NS_ENUM(NSUInteger, SCCaptureConfigurationKey) {
    SCCaptureConfigurationKeyIsRunning,
    SCCaptureConfigurationKeyIsNightModeActive,
    SCCaptureConfigurationKeyLowLightCondition,
    SCCaptureConfigurationKeyDevicePosition,
    SCCaptureConfigurationKeyZoomFactor,
    SCCaptureConfigurationKeyFlashActive,
    SCCaptureConfigurationKeyTorchActive,
    SCCaptureConfigurationKeyARSessionActive,
    SCCaptureConfigurationKeyLensesActive,
    SCCaptureConfigurationKeyVideoRecording,
};

@interface SCCaptureConfiguration (internalMethods)

// Return dirtyKeys, which identify the parameters customer want to set.
- (NSArray *)dirtyKeys;

// Called by SCCaptureConfigurator to seal a configuration, so future changes are ignored.
- (void)seal;

- (BOOL)_configurationSealed;

@end


================================================
FILE: ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurator.h
================================================
//
//  SCCaptureConfigurator.h
//  Snapchat
//
//  Created by Lin Jia on 10/2/17.
//
//

#import "SCCaptureConfiguration.h"
#import "SCCaptureConfigurationAnnouncer.h"
#import "SCManagedCaptureDevice.h"
#import "SCVideoCaptureSessionInfo.h"

#import <SCFoundation/SCQueuePerformer.h>

#import <Looksery/LSAGLView.h>

#import <Foundation/Foundation.h>

/*
 SCCaptureConfigurator is the class you use to config the setting of the camera hardware. Such as setting the camera to
 be front or back, setting camera hardware to be certain resolution, or to activate night mode.

 You can use this class for many things:

 a) do 1 time poking to checkout the current camera configuration via the currentConfiguration.

 Note that we represent configuration via id<SCManagedCapturerState>. It is going to be an immutable object.

 b) register to be the listener of the configuration change via the announcer.
 Every time a camera configuration change, you will receive an update.

 c) set the configuration via commitConfiguration API. You convey your setting intention via SCCaptureConfiguration.

 You can register a completionHandler to be called after your configuration gets done.

 Inside the completionHandler, we will pass you an error if it happens, and there will be a boolean cameraChanged. If
 your configuration already equals the current configuration of the camera, we will not change the camera, the boolean
 will be true.

 d) All APIs are thread safe.
 */

typedef void (^SCCaptureConfigurationCompletionHandler)(NSError *error, BOOL cameraChanged);

@interface SCCaptureConfigurator : NSObject

@property (nonatomic, strong, readonly) SCCaptureConfigurationAnnouncer *announcer;

@property (nonatomic, strong, readonly) id<SCManagedCapturerState> currentConfiguration;

- (instancetype)init NS_UNAVAILABLE;

- (instancetype)initWithPerformer:(SCQueuePerformer *)performer;

- (void)commitConfiguration:(SCCaptureConfiguration *)configuration
          completionHandler:(SCCaptureConfigurationCompletionHandler)completionHandler;

@end


================================================
FILE: ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurator.m
================================================
//
//  SCCaptureConfiguration.m
//  Snapchat
//
//  Created by Lin Jia on 10/2/17.
//
//

#import "SCCaptureConfigurator.h"

#import "SCCaptureConfigurationAnnouncer_Private.h"
#import "SCCaptureConfiguration_Private.h"

#import <SCFoundation/SCAssertWrapper.h>

@interface SCCaptureConfigurator () {
    SCQueuePerformer *_performer;
}
@end

@implementation SCCaptureConfigurator

- (instancetype)initWithPerformer:(SCQueuePerformer *)performer
{
    self = [super init];
    if (self) {
        _announcer = [[SCCaptureConfigurationAnnouncer alloc] initWithPerformer:performer configurator:self];
        _performer = performer;
        // TODO: initialize _currentConfiguration
    }
    return self;
}

- (void)commitConfiguration:(SCCaptureConfiguration *)configuration
          completionHandler:(SCCaptureConfigurationCompletionHandler)completionHandler
{
    [configuration seal];
    [_performer perform:^() {
        SCAssert(configuration, @"Configuration must be a valid input parameter");
        NSArray<SCCaptureConfigurationDirtyKey *> *dirtyKeys = [configuration dirtyKeys];
        for (SCCaptureConfigurationDirtyKey *key in dirtyKeys) {
            [self _processKey:[key integerValue] configuration:configuration];
        }
        if (completionHandler) {
            // TODO: passing in right parameters.
            completionHandler(NULL, YES);
        }
    }];
}

- (void)_processKey:(SCCaptureConfigurationKey)key configuration:(SCCaptureConfiguration *)configuration
{
    // Tune the hardware depending on what key is dirty, and what is the value is inside configuration.
}

@end


================================================
FILE: ManagedCapturer/CapturerV2/Core/SCCaptureCore.h
================================================
//
//  SCCaptureCore.h
//  Snapchat
//
//  Created by Lin Jia on 10/2/17.
//
//

#import "SCCaptureStateMachineContext.h"
#import "SCCapturer.h"

#import <SCFoundation/SCPerforming.h>

#import <Foundation/Foundation.h>

@class SCCaptureConfigurator;

/*
 SCCaptureCore abstracts away the hardware aspect of a camera. SCCaptureCore is the V2 version of the
 SCManagedCapturerV1.

 SCCaptureCore itself does very little things actually. Its main job is to expose APIs of camera hardware to outside
 customers. The actual heavy lifting is done via delegating the jobs to multiple worker classes.

 We generally categorize the operation of camera hardware into 2 categories:

 1) make camera hardware do state transition. Such as what is shown in this graph:
 https://docs.google.com/presentation/d/1KWk-XSgO0wFAjBZXsl_OnHBGpi_pd9-ds6Wje8vX-0s/edit#slide=id.g2017e46295_1_10

 2) config camera hardware setting, such as setting the camera to be front or back, such as setting camera hardware to
 be certain resolution, or to activate night mode.

 Indeed, we create 2 working classes to do the heavy lifting. Both of them are under construction. Feel free to checkout
 SCCaptureConfigurator, which is responsible for 2).

 */

@interface SCCaptureCore : NSObject <SCCapturer>

@property (nonatomic, strong, readonly) SCCaptureStateMachineContext *stateMachine;

@end


================================================
FILE: ManagedCapturer/CapturerV2/Core/SCCaptureCore.m
================================================
//
//  SCCaptureCore.m
//  Snapchat
//
//  Created by Lin Jia on 10/2/17.
//
//

#import "SCCaptureCore.h"

#import "SCCaptureDeviceAuthorizationChecker.h"
#import "SCCaptureResource.h"
#import "SCCaptureWorker.h"
#import "SCManagedCapturePreviewLayerController.h"
#import "SCManagedCapturerGLViewManagerAPI.h"
#import "SCManagedCapturerLSAComponentTrackerAPI.h"
#import "SCManagedCapturerV1_Private.h"

#import <SCAudio/SCAudioConfiguration.h>
#import <SCFoundation/SCAssertWrapper.h>

static const char *kSCCaptureDeviceAuthorizationManagerQueueLabel =
    "com.snapchat.capture_device_authorization_checker_queue";

@implementation SCCaptureCore {
    SCManagedCapturerV1 *_managedCapturerV1;
    SCQueuePerformer *_queuePerformer;
    SCCaptureDeviceAuthorizationChecker *_authorizationChecker;
}
@synthesize blackCameraDetector = _blackCameraDetector;

- (instancetype)init
{
    SCTraceStart();
    SCAssertMainThread();
    self = [super init];
    if (self) {
        _managedCapturerV1 = [SCManagedCapturerV1 sharedInstance];
        SCCaptureResource *resource = _managedCapturerV1.captureResource;
        _queuePerformer = resource.queuePerformer;
        _stateMachine = [[SCCaptureStateMachineContext alloc] initWithResource:resource];
        SCQueuePerformer *authorizationCheckPerformer =
            [[SCQueuePerformer alloc] initWithLabel:kSCCaptureDeviceAuthorizationManagerQueueLabel
                                   qualityOfService:QOS_CLASS_USER_INTERACTIVE
                                          queueType:DISPATCH_QUEUE_SERIAL
                                            context:SCQueuePerformerContextCamera];
        _authorizationChecker =
            [[SCCaptureDeviceAuthorizationChecker alloc] initWithPerformer:authorizationCheckPerformer];
    }
    return self;
}

- (id<SCManagedCapturerLensAPI>)lensProcessingCore
{
    return _managedCapturerV1.lensProcessingCore;
}

// For APIs inside protocol SCCapture, if they are related to capture state machine, we delegate to state machine.
- (void)setupWithDevicePositionAsynchronously:(SCManagedCaptureDevicePosition)devicePosition
                            completionHandler:(dispatch_block_t)completionHandler
                                      context:(NSString *)context
{
    [_stateMachine initializeCaptureWithDevicePositionAsynchronously:devicePosition
                                                   completionHandler:completionHandler
                                                             context:context];
}

- (SCCapturerToken *)startRunningAsynchronouslyWithCompletionHandler:(dispatch_block_t)completionHandler
                                                             context:(NSString *)context
{
    return [_stateMachine startRunningWithContext:context completionHandler:completionHandler];
}

#pragma mark - Recording / Capture

- (void)captureStillImageAsynchronouslyWithAspectRatio:(CGFloat)aspectRatio
                                      captureSessionID:(NSString *)captureSessionID
                                     completionHandler:
                                         (sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler
                                               context:(NSString *)context
{
    [_stateMachine captureStillImageAsynchronouslyWithAspectRatio:aspectRatio
                                                 captureSessionID:captureSessionID
                                                completionHandler:completionHandler
                                                          context:context];
}

- (void)stopRunningAsynchronously:(SCCapturerToken *)token
                completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler
                          context:(NSString *)context
{
    [_stateMachine stopRunningWithCapturerToken:token completionHandler:completionHandler context:context];
}

- (void)stopRunningAsynchronously:(SCCapturerToken *)token
                completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler
                            after:(NSTimeInterval)delay
                          context:(NSString *)context
{
    [_stateMachine stopRunningWithCapturerToken:token after:delay completionHandler:completionHandler context:context];
}

#pragma mark - Scanning

- (void)startScanAsynchronouslyWithScanConfiguration:(SCScanConfiguration *)configuration context:(NSString *)context
{
    [_stateMachine startScanAsynchronouslyWithScanConfiguration:configuration context:context];
}

- (void)stopScanAsynchronouslyWithCompletionHandler:(dispatch_block_t)completionHandler context:(NSString *)context
{
    [_stateMachine stopScanAsynchronouslyWithCompletionHandler:completionHandler context:context];
}

- (void)prepareForRecordingAsynchronouslyWithContext:(NSString *)context
                                  audioConfiguration:(SCAudioConfiguration *)configuration
{
    [_stateMachine prepareForRecordingAsynchronouslyWithAudioConfiguration:configuration context:context];
}

- (void)startRecordingAsynchronouslyWithOutputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings
                                    audioConfiguration:(SCAudioConfiguration *)configuration
                                           maxDuration:(NSTimeInterval)maxDuration
                                               fileURL:(NSURL *)fileURL
                                      captureSessionID:(NSString *)captureSessionID
                                     completionHandler:
                                         (sc_managed_capturer_start_recording_completion_handler_t)completionHandler
                                               context:(NSString *)context
{
    [_stateMachine startRecordingWithOutputSettings:outputSettings
                                 audioConfiguration:configuration
                                        maxDuration:maxDuration
                                            fileURL:fileURL
                                   captureSessionID:captureSessionID
                                  completionHandler:completionHandler
                                            context:context];
}

- (void)stopRecordingAsynchronouslyWithContext:(NSString *)context
{
    [_stateMachine stopRecordingWithContext:context];
}

- (void)cancelRecordingAsynchronouslyWithContext:(NSString *)context
{
    [_stateMachine cancelRecordingWithContext:context];
    [[self snapCreationTriggers] markSnapCreationEndWithContext:context];
}

#pragma mark -

- (void)startStreamingAsynchronouslyWithCompletionHandler:(dispatch_block_t)completionHandler
                                                  context:(NSString *)context
{
    [_managedCapturerV1 startStreamingAsynchronouslyWithCompletionHandler:completionHandler context:context];
}
- (void)addSampleBufferDisplayController:(id<SCManagedSampleBufferDisplayController>)sampleBufferDisplayController
                                 context:(NSString *)context
{
    [_managedCapturerV1 addSampleBufferDisplayController:sampleBufferDisplayController context:context];
}

#pragma mark - Utilities

- (void)convertViewCoordinates:(CGPoint)viewCoordinates
             completionHandler:(sc_managed_capturer_convert_view_coordniates_completion_handler_t)completionHandler
                       context:(NSString *)context
{
    [_managedCapturerV1 convertViewCoordinates:viewCoordinates completionHandler:completionHandler context:context];
}

- (void)detectLensCategoryOnNextFrame:(CGPoint)point
                               lenses:(NSArray<SCLens *> *)lenses
                           completion:(sc_managed_lenses_processor_category_point_completion_handler_t)completion
                              context:(NSString *)context
{
    [_managedCapturerV1 detectLensCategoryOnNextFrame:point lenses:lenses completion:completion context:context];
}

#pragma mark - Configurations

- (void)setDevicePositionAsynchronously:(SCManagedCaptureDevicePosition)devicePosition
                      completionHandler:(dispatch_block_t)completionHandler
                                context:(NSString *)context
{
    [_managedCapturerV1 setDevicePositionAsynchronously:devicePosition
                                      completionHandler:completionHandler
                                                context:context];
}

- (void)setFlashActive:(BOOL)flashActive
     completionHandler:(dispatch_block_t)completionHandler
               context:(NSString *)context
{
    [_managedCapturerV1 setFlashActive:flashActive completionHandler:completionHandler context:context];
}

- (void)setLensesActive:(BOOL)lensesActive
      completionHandler:(dispatch_block_t)completionHandler
                context:(NSString *)context
{
    [_managedCapturerV1 setLensesActive:lensesActive completionHandler:completionHandler context:context];
}

- (void)setLensesActive:(BOOL)lensesActive
          filterFactory:(SCLookseryFilterFactory *)filterFactory
      completionHandler:(dispatch_block_t)completionHandler
                context:(NSString *)context
{
    [_managedCapturerV1 setLensesActive:lensesActive
                          filterFactory:filterFactory
                      completionHandler:completionHandler
                                context:context];
}

- (void)setLensesInTalkActive:(BOOL)lensesActive
            completionHandler:(dispatch_block_t)completionHandler
                      context:(NSString *)context
{
    [_managedCapturerV1 setLensesInTalkActive:lensesActive completionHandler:completionHandler context:context];
}

- (void)setTorchActiveAsynchronously:(BOOL)torchActive
                   completionHandler:(dispatch_block_t)completionHandler
                             context:(NSString *)context
{
    [_managedCapturerV1 setTorchActiveAsynchronously:torchActive completionHandler:completionHandler context:context];
}

- (void)setNightModeActiveAsynchronously:(BOOL)active
                       completionHandler:(dispatch_block_t)completionHandler
                                 context:(NSString *)context
{
    [_managedCapturerV1 setNightModeActiveAsynchronously:active completionHandler:completionHandler context:context];
}

- (void)lockZoomWithContext:(NSString *)context
{
    [_managedCapturerV1 lockZoomWithContext:context];
}

- (void)unlockZoomWithContext:(NSString *)context
{
    [_managedCapturerV1 unlockZoomWithContext:context];
}

- (void)setZoomFactorAsynchronously:(CGFloat)zoomFactor context:(NSString *)context
{
    [_managedCapturerV1 setZoomFactorAsynchronously:zoomFactor context:context];
}

- (void)resetZoomFactorAsynchronously:(CGFloat)zoomFactor
                       devicePosition:(SCManagedCaptureDevicePosition)devicePosition
                              context:(NSString *)context
{
    [_managedCapturerV1 resetZoomFactorAsynchronously:zoomFactor devicePosition:devicePosition context:context];
}

- (void)setExposurePointOfInterestAsynchronously:(CGPoint)pointOfInterest
                                        fromUser:(BOOL)fromUser
                               completionHandler:(dispatch_block_t)completionHandler
                                         context:(NSString *)context
{
    [_managedCapturerV1 setExposurePointOfInterestAsynchronously:pointOfInterest
                                                        fromUser:fromUser
                                               completionHandler:completionHandler
                                                         context:context];
}

- (void)setAutofocusPointOfInterestAsynchronously:(CGPoint)pointOfInterest
                                completionHandler:(dispatch_block_t)completionHandler
                                          context:(NSString *)context
{
    [_managedCapturerV1 setAutofocusPointOfInterestAsynchronously:pointOfInterest
                                                completionHandler:completionHandler
                                                          context:context];
}

- (void)setPortraitModePointOfInterestAsynchronously:(CGPoint)pointOfInterest
                                   completionHandler:(dispatch_block_t)completionHandler
                                             context:(NSString *)context
{
    [_managedCapturerV1 setPortraitModePointOfInterestAsynchronously:pointOfInterest
                                                   completionHandler:completionHandler
                                                             context:context];
}

- (void)continuousAutofocusAndExposureAsynchronouslyWithCompletionHandler:(dispatch_block_t)completionHandler
                                                                  context:(NSString *)context
{
    [_managedCapturerV1 continuousAutofocusAndExposureAsynchronouslyWithCompletionHandler:completionHandler
                                                                                  context:context];
}

// I need to call these three methods from SCAppDelegate explicitly so that I get the latest information.
- (void)applicationDidEnterBackground
{
    [_managedCapturerV1 applicationDidEnterBackground];
}

- (void)applicationWillEnterForeground
{
    [_managedCapturerV1 applicationWillEnterForeground];
}

- (void)applicationDidBecomeActive
{
    [_managedCapturerV1 applicationDidBecomeActive];
}
- (void)applicationWillResignActive
{
    [_managedCapturerV1 applicationWillResignActive];
}

- (void)mediaServicesWereReset
{
    [_managedCapturerV1 mediaServicesWereReset];
}

- (void)mediaServicesWereLost
{
    [_managedCapturerV1 mediaServicesWereLost];
}

#pragma mark - Add / Remove Listener

- (void)addListener:(id<SCManagedCapturerListener>)listener
{
    [_managedCapturerV1 addListener:listener];
}

- (void)removeListener:(id<SCManagedCapturerListener>)listener
{
    [_managedCapturerV1 removeListener:listener];
}

- (void)addVideoDataSourceListener:(id<SCManagedVideoDataSourceListener>)listener
{
    [_managedCapturerV1 addVideoDataSourceListener:listener];
}

- (void)removeVideoDataSourceListener:(id<SCManagedVideoDataSourceListener>)listener
{
    [_managedCapturerV1 removeVideoDataSourceListener:listener];
}

- (void)addDeviceCapacityAnalyzerListener:(id<SCManagedDeviceCapacityAnalyzerListener>)listener
{
    [_managedCapturerV1 addDeviceCapacityAnalyzerListener:listener];
}

- (void)removeDeviceCapacityAnalyzerListener:(id<SCManagedDeviceCapacityAnalyzerListener>)listener
{
    [_managedCapturerV1 removeDeviceCapacityAnalyzerListener:listener];
}

- (NSString *)debugInfo
{
    return [_managedCapturerV1 debugInfo];
}

- (id<SCManagedVideoDataSource>)currentVideoDataSource
{
    return [_managedCapturerV1 currentVideoDataSource];
}

// For APIs inside protocol SCCapture, if they are not related to capture state machine, we directly delegate to V1.
- (void)checkRestrictedCamera:(void (^)(BOOL, BOOL, AVAuthorizationStatus))callback
{
    [_managedCapturerV1 checkRestrictedCamera:callback];
}

- (void)recreateAVCaptureSession
{
    [_managedCapturerV1 recreateAVCaptureSession];
}

#pragma mark -
- (CMTime)firstWrittenAudioBufferDelay
{
    return [SCCaptureWorker firstWrittenAudioBufferDelay:_managedCapturerV1.captureResource];
}

- (BOOL)audioQueueStarted
{
    return [SCCaptureWorker audioQueueStarted:_managedCapturerV1.captureResource];
}

- (BOOL)isLensApplied
{
    return [SCCaptureWorker isLensApplied:_managedCapturerV1.captureResource];
}

- (BOOL)isVideoMirrored
{
    return [SCCaptureWorker isVideoMirrored:_managedCapturerV1.captureResource];
}

- (SCVideoCaptureSessionInfo)activeSession
{
    return _managedCapturerV1.activeSession;
}

- (void)setBlackCameraDetector:(SCBlackCameraDetector *)blackCameraDetector
                             deviceMotionProvider:(id<SCDeviceMotionProvider>)deviceMotionProvider
                                 fileInputDecider:(id<SCFileInputDecider>)fileInputDecider
                           arImageCaptureProvider:(id<SCManagedCapturerARImageCaptureProvider>)arImageCaptureProvider
                                    glviewManager:(id<SCManagedCapturerGLViewManagerAPI>)glViewManager
                                  lensAPIProvider:(id<SCManagedCapturerLensAPIProvider>)lensAPIProvider
                              lsaComponentTracker:(id<SCManagedCapturerLSAComponentTrackerAPI>)lsaComponentTracker
    managedCapturerPreviewLayerControllerDelegate:
        (id<SCManagedCapturePreviewLayerControllerDelegate>)previewLayerControllerDelegate
{
    _managedCapturerV1.captureResource.blackCameraDetector = blackCameraDetector;
    _managedCapturerV1.captureResource.deviceMotionProvider = deviceMotionProvider;
    _managedCapturerV1.captureResource.fileInputDecider = fileInputDecider;
    _managedCapturerV1.captureResource.arImageCaptureProvider = arImageCaptureProvider;
    _managedCapturerV1.captureResource.videoPreviewGLViewManager = glViewManager;
    [_managedCapturerV1.captureResource.videoPreviewGLViewManager
        configureWithCaptureResource:_managedCapturerV1.captureResource];
    _managedCapturerV1.captureResource.lensAPIProvider = lensAPIProvider;
    _managedCapturerV1.captureResource.lsaTrackingComponentHandler = lsaComponentTracker;
    [_managedCapturerV1.captureResource.lsaTrackingComponentHandler
        configureWithCaptureResource:_managedCapturerV1.captureResource];
    _managedCapturerV1.captureResource.previewLayerControllerDelegate = previewLayerControllerDelegate;
    [SCManagedCapturePreviewLayerController sharedInstance].delegate =
        _managedCapturerV1.captureResource.previewLayerControllerDelegate;
}

- (SCBlackCameraDetector *)blackCameraDetector
{
    return _managedCapturerV1.captureResource.blackCameraDetector;
}

- (void)captureSingleVideoFrameAsynchronouslyWithCompletionHandler:
            (sc_managed_capturer_capture_video_frame_completion_handler_t)completionHandler
                                                           context:(NSString *)context
{
    [_managedCapturerV1 captureSingleVideoFrameAsynchronouslyWithCompletionHandler:completionHandler context:context];
}

- (void)sampleFrameWithCompletionHandler:(void (^)(UIImage *frame, CMTime presentationTime))completionHandler
                                 context:(NSString *)context
{
    [_managedCapturerV1 sampleFrameWithCompletionHandler:completionHandler context:context];
}

- (void)addTimedTask:(SCTimedTask *)task context:(NSString *)context
{
    [_managedCapturerV1 addTimedTask:task context:context];
}

- (void)clearTimedTasksWithContext:(NSString *)context
{
    [_managedCapturerV1 clearTimedTasksWithContext:context];
}

- (BOOL)authorizedForVideoCapture
{
    return [_authorizationChecker authorizedForVideoCapture];
}

- (void)preloadVideoCaptureAuthorization
{
    [_authorizationChecker preloadVideoCaptureAuthorization];
}

#pragma mark - Snap Creation triggers

- (SCSnapCreationTriggers *)snapCreationTriggers
{
    return [_managedCapturerV1 snapCreationTriggers];
}

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCDepthBlurMetalModule.metal
================================================
//
//  SCDepthBlurMetalModule.metal
//  Snapchat
//
//  Created by Brian Ng on 10/31/17.
//

#include <metal_stdlib>
using namespace metal;

struct DepthBlurRenderData {
    float depthRange;
    float depthOffset;
    float depthBlurForegroundThreshold;
    float depthBlurBackgroundThreshold;
};

kernel void kernel_depth_blur(texture2d<float, access::read> sourceYTexture [[texture(0)]],
                              texture2d<float, access::read> sourceUVTexture [[texture(1)]],
                              texture2d<float, access::read> sourceDepthTexture[[texture(2)]],
                              texture2d<float, access::read> sourceBlurredYTexture [[texture(3)]],
                              texture2d<float, access::write> destinationYTexture [[texture(4)]],
                              texture2d<float, access::write> destinationUVTexture [[texture(5)]],
                              constant DepthBlurRenderData &renderData [[buffer(0)]],
                              uint2 gid [[thread_position_in_grid]],
                              uint2 size [[threads_per_grid]]) {
    float2 valueUV = sourceUVTexture.read(gid).rg;
    float depthValue = sourceDepthTexture.read(uint2(gid.x/4, gid.y/4)).r;
    float normalizedDepthValue = (depthValue - renderData.depthOffset) / renderData.depthRange;
    float valueYUnblurred = sourceYTexture.read(gid).r;
    float valueYBlurred = sourceBlurredYTexture.read(gid).r;
    
    float valueY = 0;
    if (normalizedDepthValue > renderData.depthBlurForegroundThreshold) {
        valueY = valueYUnblurred;
    } else if (normalizedDepthValue < renderData.depthBlurBackgroundThreshold) {
        valueY = valueYBlurred;
    } else {
        float blendRange = renderData.depthBlurForegroundThreshold - renderData.depthBlurBackgroundThreshold;
        float normalizedBlendDepthValue = (normalizedDepthValue - renderData.depthBlurBackgroundThreshold) / blendRange;
        valueY = valueYUnblurred * normalizedBlendDepthValue + valueYBlurred * (1 - normalizedBlendDepthValue);
    }
    
    destinationYTexture.write(valueY, gid);
    destinationUVTexture.write(float4(valueUV.r, valueUV.g, 0, 0), gid);
}



================================================
FILE: ManagedCapturer/ImageProcessing/SCDepthBlurMetalRenderCommand.h
================================================
//
//  SCDepthBlurMetalRenderCommand.h
//  Snapchat
//
//  Created by Brian Ng on 11/8/17.
//
//

#import "SCMetalModule.h"

#import <Foundation/Foundation.h>

/*
 @class SCDepthBlurMetalRenderCommand
    Prepares the command buffer for the SCDepthBlurMetalModule.metal shader.
 */
@interface SCDepthBlurMetalRenderCommand : NSObject <SCMetalRenderCommand>

@property (nonatomic, readonly) NSString *functionName;

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCDepthBlurMetalRenderCommand.m
================================================
//
//  SCDepthBlurMetalRenderCommand.m
//  Snapchat
//
//  Created by Brian Ng on 11/8/17.
//
//

#import "SCDepthBlurMetalRenderCommand.h"

#import "SCCameraTweaks.h"
#import "SCMetalUtils.h"

#import <SCFoundation/NSString+SCFormat.h>

@import MetalPerformanceShaders;

@implementation SCDepthBlurMetalRenderCommand

typedef struct DepthBlurRenderData {
    float depthRange;
    float depthOffset;
    float depthBlurForegroundThreshold;
    float depthBlurBackgroundThreshold;
} DepthBlurRenderData;

#pragma mark - SCMetalRenderCommand

- (id<MTLComputeCommandEncoder>)encodeMetalCommand:(id<MTLCommandBuffer>)commandBuffer
                                     pipelineState:(id<MTLComputePipelineState>)pipelineState
                                   textureResource:(SCMetalTextureResource *)textureResource
{
#if !TARGET_IPHONE_SIMULATOR
    CGFloat depthBlurForegroundThreshold = textureResource.depthBlurForegroundThreshold;
    CGFloat depthBlurBackgroundThreshold =
        textureResource.depthBlurForegroundThreshold > SCCameraTweaksDepthBlurBackgroundThreshold()
            ? SCCameraTweaksDepthBlurBackgroundThreshold()
            : 0;
    DepthBlurRenderData depthBlurRenderData = {
        .depthRange = textureResource.depthRange,
        .depthOffset = textureResource.depthOffset,
        .depthBlurBackgroundThreshold = depthBlurBackgroundThreshold,
        .depthBlurForegroundThreshold = depthBlurForegroundThreshold,
    };
    id<MTLBuffer> depthBlurRenderDataBuffer =
        [textureResource.device newBufferWithLength:sizeof(DepthBlurRenderData)
                                            options:MTLResourceOptionCPUCacheModeDefault];
    memcpy(depthBlurRenderDataBuffer.contents, &depthBlurRenderData, sizeof(DepthBlurRenderData));

    MPSImageGaussianBlur *kernel =
        [[MPSImageGaussianBlur alloc] initWithDevice:textureResource.device sigma:SCCameraTweaksBlurSigma()];
    [kernel encodeToCommandBuffer:commandBuffer
                    sourceTexture:textureResource.sourceYTexture
               destinationTexture:textureResource.sourceBlurredYTexture];

    id<MTLComputeCommandEncoder> commandEncoder = [commandBuffer computeCommandEncoder];
    [commandEncoder setComputePipelineState:pipelineState];

    [commandEncoder setTexture:textureResource.sourceYTexture atIndex:0];
    [commandEncoder setTexture:textureResource.sourceUVTexture atIndex:1];
    [commandEncoder setTexture:textureResource.sourceDepthTexture atIndex:2];
    [commandEncoder setTexture:textureResource.sourceBlurredYTexture atIndex:3];
    [commandEncoder setTexture:textureResource.destinationYTexture atIndex:4];
    [commandEncoder setTexture:textureResource.destinationUVTexture atIndex:5];
    [commandEncoder setBuffer:depthBlurRenderDataBuffer offset:0 atIndex:0];

    return commandEncoder;
#else
    return nil;
#endif
}

- (BOOL)requiresDepthData
{
    return YES;
}

#pragma mark - SCMetalModuleFunctionProvider

- (NSString *)functionName
{
    return @"kernel_depth_blur";
}

- (NSString *)description
{
    return [NSString sc_stringWithFormat:@"SCDepthBlurMetalRenderCommand (shader function = %@)", self.functionName];
}

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCDepthToGrayscaleMetalModule.metal
================================================
//
//  SCDepthToGrayscaleMetalModule.metal
//  Snapchat
//
//  Created by Brian Ng on 12/7/17.
//

#include <metal_stdlib>
using namespace metal;

typedef struct DepthToGrayscaleRenderData {
    float depthRange;
    float depthOffset;
} DepthToGrayscaleRenderData;

kernel void kernel_depth_to_grayscale(texture2d<float, access::read> sourceDepthTexture[[texture(0)]],
                                      texture2d<float, access::write> destinationYTexture [[texture(1)]],
                                      texture2d<float, access::write> destinationUVTexture [[texture(2)]],
                                      constant DepthToGrayscaleRenderData &renderData [[buffer(0)]],
                                      uint2 gid [[thread_position_in_grid]],
                                      uint2 size [[threads_per_grid]]) {
    float depthValue = sourceDepthTexture.read(uint2(gid.x/4, gid.y/4)).r;
    float normalizedDepthValue = (depthValue - renderData.depthOffset) / renderData.depthRange;
    
    destinationYTexture.write(normalizedDepthValue, gid);
    destinationUVTexture.write(float4(0.5, 0.5, 0, 0), gid);
}




================================================
FILE: ManagedCapturer/ImageProcessing/SCDepthToGrayscaleMetalRenderCommand.h
================================================
//
//  SCDepthToGrayscaleMetalRenderCommand.h
//  Snapchat
//
//  Created by Brian Ng on 12/7/17.
//
//

#import "SCMetalModule.h"

#import <Foundation/Foundation.h>

/*
 @class SCDepthToGrayscaleMetalRenderCommand
 Prepares the command buffer for the SCDepthToGrayscaleMetalModule.metal shader.
 */
@interface SCDepthToGrayscaleMetalRenderCommand : NSObject <SCMetalRenderCommand>

@property (nonatomic, readonly) NSString *functionName;

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCDepthToGrayscaleMetalRenderCommand.m
================================================
//
//  SCDepthToGrayscaleMetalRenderCommand.m
//  Snapchat
//
//  Created by Brian Ng on 12/7/17.
//
//

#import "SCDepthToGrayscaleMetalRenderCommand.h"

#import "SCCameraTweaks.h"
#import "SCMetalUtils.h"

#import <SCFoundation/NSString+SCFormat.h>

@import MetalPerformanceShaders;

@implementation SCDepthToGrayscaleMetalRenderCommand

typedef struct DepthToGrayscaleRenderData {
    float depthRange;
    float depthOffset;
} DepthToGrayscaleRenderData;

#pragma mark - SCMetalRenderCommand

- (id<MTLComputeCommandEncoder>)encodeMetalCommand:(id<MTLCommandBuffer>)commandBuffer
                                     pipelineState:(id<MTLComputePipelineState>)pipelineState
                                   textureResource:(SCMetalTextureResource *)textureResource
{
#if !TARGET_IPHONE_SIMULATOR
    DepthToGrayscaleRenderData depthToGrayscaleRenderData = {
        .depthRange = textureResource.depthRange, .depthOffset = textureResource.depthOffset,
    };
    id<MTLBuffer> depthToGrayscaleDataBuffer =
        [textureResource.device newBufferWithLength:sizeof(DepthToGrayscaleRenderData)
                                            options:MTLResourceOptionCPUCacheModeDefault];
    memcpy(depthToGrayscaleDataBuffer.contents, &depthToGrayscaleRenderData, sizeof(DepthToGrayscaleRenderData));

    id<MTLComputeCommandEncoder> commandEncoder = [commandBuffer computeCommandEncoder];
    [commandEncoder setComputePipelineState:pipelineState];

    [commandEncoder setTexture:textureResource.sourceDepthTexture atIndex:0];
    [commandEncoder setTexture:textureResource.destinationYTexture atIndex:1];
    [commandEncoder setTexture:textureResource.destinationUVTexture atIndex:2];
    [commandEncoder setBuffer:depthToGrayscaleDataBuffer offset:0 atIndex:0];

    return commandEncoder;
#else
    return nil;
#endif
}

- (BOOL)requiresDepthData
{
    return YES;
}

#pragma mark - SCMetalModuleFunctionProvider

- (NSString *)functionName
{
    return @"kernel_depth_to_grayscale";
}

- (NSString *)description
{
    return [NSString
        sc_stringWithFormat:@"SCDepthToGrayscaleMetalRenderCommand (shader function = %@)", self.functionName];
}

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCDigitalExposureHandler.h
================================================
//
//  SCDigitalExposureHandler.h
//  Snapchat
//
//  Created by Yu-Kuan (Anthony) Lai on 6/15/17.
//  Copyright © 2017 Snapchat, Inc. All rights reserved.
//

#import <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h>

@class SCExposureAdjustProcessingModule;

/*
 @class SCDigitalExposureHandler
    The SCDigitalExposureHandler will be built by the SCProcessingBuilder when the user indicates that he/she
        wants to add SCExposureAdjustProcessingModule to the processing pipeline. The builder will take care
        of initializing the handler by linking the processing module. Caller of the builder can then link up
        the handler to the UI element (in this case, SCExposureSlider) so that user's control is hooked up to
        the processing module.

 */
@interface SCDigitalExposureHandler : NSObject

- (instancetype)initWithProcessingModule:(SCExposureAdjustProcessingModule *)processingModule;
- (void)setExposureParameter:(CGFloat)value;

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCDigitalExposureHandler.m
================================================
//
//  SCDigitalExposureHandler.m
//  Snapchat
//
//  Created by Yu-Kuan (Anthony) Lai on 6/15/17.
//  Copyright © 2017 Snapchat, Inc. All rights reserved.
//

#import "SCDigitalExposureHandler.h"

#import "SCExposureAdjustProcessingModule.h"

@implementation SCDigitalExposureHandler {
    __weak SCExposureAdjustProcessingModule *_processingModule;
}

- (instancetype)initWithProcessingModule:(SCExposureAdjustProcessingModule *)processingModule
{
    if (self = [super init]) {
        _processingModule = processingModule;
    }
    return self;
}

- (void)setExposureParameter:(CGFloat)value
{
    [_processingModule setEVValue:value];
}

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCExposureAdjustMetalModule.metal
================================================
//
//  SCExposureAdjustMetalModule.metal
//  Snapchat
//
//  Created by Michel Loenngren on 7/11/17.
//
//

#include <metal_stdlib>
using namespace metal;

kernel void kernel_exposure_adjust(texture2d<float, access::read> sourceYTexture [[texture(0)]],
                                   texture2d<float, access::read> sourceUVTexture [[texture(1)]],
                                   texture2d<float, access::write> destinationYTexture [[texture(2)]],
                                   texture2d<float, access::write> destinationUVTexture [[texture(3)]],
                                   uint2 gid [[thread_position_in_grid]],
                                   uint2 size [[threads_per_grid]]) {
    float valueY = sourceYTexture.read(gid).r;
    float2 valueUV = sourceUVTexture.read(gid).rg;

    float factor = 1.0 / pow(1.0 + valueY, 5) + 1.0;
    valueY *= factor;
    destinationYTexture.write(valueY, gid);
    destinationUVTexture.write(float4(valueUV.r, valueUV.g, 0, 0), gid);

}

kernel void kernel_exposure_adjust_nightvision(texture2d<float, access::read> sourceYTexture [[texture(0)]],
                                   texture2d<float, access::read> sourceUVTexture [[texture(1)]],
                                   texture2d<float, access::write> destinationYTexture [[texture(2)]],
                                   texture2d<float, access::write> destinationUVTexture [[texture(3)]],
                                   uint2 gid [[thread_position_in_grid]],
                                   uint2 size [[threads_per_grid]]) {
    float valueY = sourceYTexture.read(gid).r;
    
    float u =  0.5 - 0.368;
    float v = 0.5 - 0.291;
    
    destinationYTexture.write(valueY, gid);
    destinationUVTexture.write(float4(u, v, 0, 0), gid);
    
}

kernel void kernel_exposure_adjust_inverted_nightvision(texture2d<float, access::read> sourceYTexture [[texture(0)]],
                                               texture2d<float, access::read> sourceUVTexture [[texture(1)]],
                                               texture2d<float, access::write> destinationYTexture [[texture(2)]],
                                               texture2d<float, access::write> destinationUVTexture [[texture(3)]],
                                               uint2 gid [[thread_position_in_grid]],
                                               uint2 size [[threads_per_grid]]) {
    float valueY = sourceYTexture.read(gid).r;
    
    valueY = 1.0 - valueY;
    
    float u =  0.5 - 0.368;
    float v = 0.5 - 0.291;
    
    destinationYTexture.write(valueY, gid);
    destinationUVTexture.write(float4(u, v, 0, 0), gid);
    
}


================================================
FILE: ManagedCapturer/ImageProcessing/SCExposureAdjustMetalRenderCommand.h
================================================
//
//  SCExposureAdjustMetalRenderCommand.h
//  Snapchat
//
//  Created by Michel Loenngren on 7/11/17.
//
//

#import "SCMetalModule.h"

#import <Foundation/Foundation.h>

/*
 @class SCExposureAdjustProcessingModule
    Prepares the command buffer for the SCExposureAdjustProcessingModule.metal shader.
 */
@interface SCExposureAdjustMetalRenderCommand : SCMetalModule <SCMetalRenderCommand>

@property (nonatomic, readonly) NSString *functionName;

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCExposureAdjustMetalRenderCommand.m
================================================
//
//  SCExposureAdjustMetalRenderCommand.m
//  Snapchat
//
//  Created by Michel Loenngren on 7/11/17.
//
//

#import "SCExposureAdjustMetalRenderCommand.h"

#import "SCCameraTweaks.h"
#import "SCMetalUtils.h"

#import <SCFoundation/SCAssertWrapper.h>

@import Metal;

@implementation SCExposureAdjustMetalRenderCommand

#pragma mark - SCMetalRenderCommand

- (id<MTLComputeCommandEncoder>)encodeMetalCommand:(id<MTLCommandBuffer>)commandBuffer
                                     pipelineState:(id<MTLComputePipelineState>)pipelineState
                                   textureResource:(SCMetalTextureResource *)textureResource
{
    id<MTLComputeCommandEncoder> commandEncoder = [commandBuffer computeCommandEncoder];
    [commandEncoder setComputePipelineState:pipelineState];
#if !TARGET_IPHONE_SIMULATOR
    [commandEncoder setTexture:textureResource.sourceYTexture atIndex:0];
    [commandEncoder setTexture:textureResource.sourceUVTexture atIndex:1];
    [commandEncoder setTexture:textureResource.destinationYTexture atIndex:2];
    [commandEncoder setTexture:textureResource.destinationUVTexture atIndex:3];
#endif

    return commandEncoder;
}

#pragma mark - SCMetalModuleFunctionProvider

- (NSString *)functionName
{
    if (SCCameraExposureAdjustmentMode() == 1) {
        return @"kernel_exposure_adjust";
    } else if (SCCameraExposureAdjustmentMode() == 2) {
        return @"kernel_exposure_adjust_nightvision";
    } else if (SCCameraExposureAdjustmentMode() == 3) {
        return @"kernel_exposure_adjust_inverted_nightvision";
    } else {
        SCAssertFail(@"Incorrect value from SCCameraExposureAdjustmentMode() %ld",
                     (long)SCCameraExposureAdjustmentMode());
        return nil;
    }
}

- (BOOL)requiresDepthData
{
    return NO;
}

- (NSString *)description
{
    return
        [NSString sc_stringWithFormat:@"SCExposureAdjustMetalRenderCommand (shader function = %@)", self.functionName];
}

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCExposureAdjustProcessingModule.h
================================================
//
//  SCExposureAdjustProcessingModule.h
//  Snapchat
//
//  Created by Yu-Kuan (Anthony) Lai on 6/1/17.
//  Copyright © 2017 Snapchat, Inc. All rights reserved.
//

#import "SCProcessingModule.h"

#import <Foundation/Foundation.h>

/**
 NOTE: If we start chaining multiple CIImage modules we should
 not run them back to back but instead in one CIImage pass
 as CoreImage will merge the shaders for best performance
*/

/*
 @class SCExposureAdjustProcessingModule
    This module use the CIExposureAdjust CIFilter to process the frames. It use the value provided by
    the SCDigitalExposurehandler as evValue (default is 0).
  */
@interface SCExposureAdjustProcessingModule : NSObject <SCProcessingModule>

- (void)setEVValue:(CGFloat)value;

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCExposureAdjustProcessingModule.m
================================================
//
//  SCExposureAdjustProcessingModule.m
//  Snapchat
//
//  Created by Yu-Kuan (Anthony) Lai on 6/1/17.
//  Copyright © 2017 Snapchat, Inc. All rights reserved.
//

#import "SCExposureAdjustProcessingModule.h"

#import "SCProcessingModuleUtils.h"

@import CoreImage;
@import CoreMedia;

static const CGFloat kSCExposureAdjustProcessingModuleMaxEVValue = 2.0;

@implementation SCExposureAdjustProcessingModule {
    CIContext *_context;
    CIFilter *_filter;
    CFMutableDictionaryRef _attributes;
    CVPixelBufferPoolRef _bufferPool;
}

- (instancetype)init
{
    if (self = [super init]) {
        _context = [CIContext context];
        _filter = [CIFilter filterWithName:@"CIExposureAdjust"];
        [_filter setValue:@0.0 forKey:@"inputEV"];
    }
    return self;
}

- (void)setEVValue:(CGFloat)value
{
    CGFloat newEVValue = value * kSCExposureAdjustProcessingModuleMaxEVValue;
    [_filter setValue:@(newEVValue) forKey:@"inputEV"];
}

- (void)dealloc
{
    CVPixelBufferPoolFlush(_bufferPool, kCVPixelBufferPoolFlushExcessBuffers);
    CVPixelBufferPoolRelease(_bufferPool);
}

- (BOOL)requiresDepthData
{
    return NO;
}

- (CMSampleBufferRef)render:(RenderData)renderData
{
    CMSampleBufferRef input = renderData.sampleBuffer;
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(input);
    CIImage *image = [CIImage imageWithCVPixelBuffer:pixelBuffer];

    [_filter setValue:image forKey:kCIInputImageKey];
    CIImage *result = [_filter outputImage];

    return [SCProcessingModuleUtils sampleBufferFromImage:result
                                          oldSampleBuffer:input
                                               bufferPool:_bufferPool
                                                  context:_context];
}

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCMetalModule.h
================================================
//
//  SCMetalModule.h
//  Snapchat
//
//  Created by Michel Loenngren on 7/19/17.
//
//

#import "SCMetalTextureResource.h"
#import "SCMetalUtils.h"
#import "SCProcessingModule.h"

#import <Foundation/Foundation.h>

@protocol SCMetalModuleFunctionProvider <NSObject>

@property (nonatomic, readonly) NSString *functionName;

@end

@protocol SCMetalRenderCommand <SCMetalModuleFunctionProvider>

/**
 Sets textures and parameters for the shader function. When implementing this function, the command encoder must be
 computed and the pipeline state set. That is, ensure that there are calls to: [commandBuffer computeCommandEncoder]
 and [commandEncoder setComputePipelineState:pipelineState].
 */
#if !TARGET_IPHONE_SIMULATOR
- (id<MTLComputeCommandEncoder>)encodeMetalCommand:(id<MTLCommandBuffer>)commandBuffer
                                     pipelineState:(id<MTLComputePipelineState>)pipelineState
                                   textureResource:(SCMetalTextureResource *)textureResource;
#endif

- (BOOL)requiresDepthData;

@end

/**
 NOTE: If we start chaining multiple metal modules we should
 not run them back to back but instead chain different render
 passes.
 */
@interface SCMetalModule : NSObject <SCProcessingModule>

// Designated initializer: SCMetalModule should always have a SCMetalRenderCommand
- (instancetype)initWithMetalRenderCommand:(id<SCMetalRenderCommand>)metalRenderCommand;

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCMetalModule.m
================================================
//
//  SCMetalModule.m
//  Snapchat
//
//  Created by Michel Loenngren on 7/19/17.
//
//

#import "SCMetalModule.h"

#import "SCCameraTweaks.h"

#import <SCFoundation/SCAssertWrapper.h>
#import <SCFoundation/SCLog.h>

@interface SCMetalModule ()
#if !TARGET_IPHONE_SIMULATOR
@property (nonatomic, readonly) id<MTLLibrary> library;
@property (nonatomic, readonly) id<MTLDevice> device;
@property (nonatomic, readonly) id<MTLFunction> function;
@property (nonatomic, readonly) id<MTLComputePipelineState> computePipelineState;
@property (nonatomic, readonly) id<MTLCommandQueue> commandQueue;
@property (nonatomic, readonly) CVMetalTextureCacheRef textureCache;
#endif
@end

@implementation SCMetalModule {
    id<SCMetalRenderCommand> _metalRenderCommand;
}

#if !TARGET_IPHONE_SIMULATOR
@synthesize library = _library;
@synthesize function = _function;
@synthesize computePipelineState = _computePipelineState;
@synthesize commandQueue = _commandQueue;
@synthesize textureCache = _textureCache;
#endif

- (instancetype)initWithMetalRenderCommand:(id<SCMetalRenderCommand>)metalRenderCommand
{
    self = [super init];
    if (self) {
        _metalRenderCommand = metalRenderCommand;
    }
    return self;
}

#pragma mark - SCProcessingModule

- (CMSampleBufferRef)render:(RenderData)renderData
{
    CMSampleBufferRef input = renderData.sampleBuffer;
#if !TARGET_IPHONE_SIMULATOR
    id<MTLComputePipelineState> pipelineState = self.computePipelineState;
    SC_GUARD_ELSE_RETURN_VALUE(pipelineState, input);

    CVMetalTextureCacheRef textureCache = self.textureCache;
    SC_GUARD_ELSE_RETURN_VALUE(textureCache, input);

    id<MTLCommandQueue> commandQueue = self.commandQueue;
    SC_GUARD_ELSE_RETURN_VALUE(commandQueue, input);

    SCMetalTextureResource *textureResource =
        [[SCMetalTextureResource alloc] initWithRenderData:renderData textureCache:textureCache device:self.device];
    id<MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
    if (!_metalRenderCommand) {
        SCAssertFail(@"Metal module must be initialized with an SCMetalRenderCommand");
    }
    id<MTLComputeCommandEncoder> commandEncoder = [_metalRenderCommand encodeMetalCommand:commandBuffer
                                                                            pipelineState:pipelineState
                                                                          textureResource:textureResource];

    NSUInteger w = pipelineState.threadExecutionWidth;
    NSUInteger h = pipelineState.maxTotalThreadsPerThreadgroup / w;

    MTLSize threadsPerThreadgroup = MTLSizeMake(w, h, 1);
    MTLSize threadgroupsPerGrid = MTLSizeMake((textureResource.sourceYTexture.width + w - 1) / w,
                                              (textureResource.sourceYTexture.height + h - 1) / h, 1);

    [commandEncoder dispatchThreadgroups:threadgroupsPerGrid threadsPerThreadgroup:threadsPerThreadgroup];

    [commandEncoder endEncoding];
    [commandBuffer commit];
    [commandBuffer waitUntilCompleted];

    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(renderData.sampleBuffer);
    SCMetalCopyTexture(textureResource.destinationYTexture, imageBuffer, 0);
    SCMetalCopyTexture(textureResource.destinationUVTexture, imageBuffer, 1);
#endif
    return input;
}

- (BOOL)requiresDepthData
{
    return [_metalRenderCommand requiresDepthData];
}

#pragma mark - Lazy properties

#if !TARGET_IPHONE_SIMULATOR

- (id<MTLLibrary>)library
{
    if (!_library) {
        NSString *libPath = [[NSBundle mainBundle] pathForResource:@"sccamera-default" ofType:@"metallib"];
        NSError *error = nil;
        _library = [self.device newLibraryWithFile:libPath error:&error];
        if (error) {
            SCLogGeneralError(@"Create metallib error: %@", error.description);
        }
    }
    return _library;
}

- (id<MTLDevice>)device
{
    return SCGetManagedCaptureMetalDevice();
}

- (id<MTLFunction>)function
{
    return [self.library newFunctionWithName:[_metalRenderCommand functionName]];
}

- (id<MTLComputePipelineState>)computePipelineState
{
    if (!_computePipelineState) {
        NSError *error = nil;
        _computePipelineState = [self.device newComputePipelineStateWithFunction:self.function error:&error];
        if (error) {
            SCLogGeneralError(@"Error while creating compute pipeline state %@", error.description);
        }
    }
    return _computePipelineState;
}

- (id<MTLCommandQueue>)commandQueue
{
    if (!_commandQueue) {
        _commandQueue = [self.device newCommandQueue];
    }
    return _commandQueue;
}

- (CVMetalTextureCacheRef)textureCache
{
    if (!_textureCache) {
        CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, self.device, nil, &_textureCache);
    }
    return _textureCache;
}

#endif

@end


================================================
FILE: ManagedCapturer/ImageProcessing/SCMetalTextureResource.h
================================================
//
//  SCMetalTextureResource.h
//  Snapchat
//
//  Created by Brian Ng on 11/7/17.
//

#import "SCProcessingModule.h"
#import "SCCapturerDefines.h"

#import <Foundation/Foundation.h>
#if !TARGET_IPHONE_SIMULATOR
#import <Metal/Metal.h>
#endif

/*
 @class SCMetalT
Download .txt
gitextract_jfvnmt3e/

├── BlackCamera/
│   ├── SCBlackCameraDetector.h
│   ├── SCBlackCameraDetector.m
│   ├── SCBlackCameraNoOutputDetector.h
│   ├── SCBlackCameraNoOutputDetector.m
│   ├── SCBlackCameraPreviewDetector.h
│   ├── SCBlackCameraPreviewDetector.m
│   ├── SCBlackCameraReporter.h
│   ├── SCBlackCameraReporter.m
│   ├── SCBlackCameraRunningDetector.h
│   ├── SCBlackCameraRunningDetector.m
│   ├── SCBlackCameraSessionBlockDetector.h
│   ├── SCBlackCameraSessionBlockDetector.m
│   ├── SCBlackCameraViewDetector.h
│   ├── SCBlackCameraViewDetector.m
│   ├── SCCaptureSessionFixer.h
│   └── SCCaptureSessionFixer.m
├── ContextAwareTaskManagement/
│   ├── OWNERS
│   ├── Requests/
│   │   ├── SCContextAwareSnapCreationThrottleRequest.h
│   │   └── SCContextAwareSnapCreationThrottleRequest.m
│   └── Triggers/
│       ├── SCSnapCreationTriggers.h
│       └── SCSnapCreationTriggers.m
├── Features/
│   ├── Core/
│   │   ├── SCFeature.h
│   │   ├── SCFeatureContainerView.h
│   │   ├── SCFeatureCoordinator.h
│   │   ├── SCFeatureCoordinator.m
│   │   └── SCFeatureProvider.h
│   ├── Flash/
│   │   ├── SCFeatureFlash.h
│   │   ├── SCFeatureFlashImpl.h
│   │   ├── SCFeatureFlashImpl.m
│   │   ├── SCFlashButton.h
│   │   └── SCFlashButton.m
│   ├── HandsFree/
│   │   └── SCFeatureHandsFree.h
│   ├── ImageCapture/
│   │   ├── SCFeatureImageCapture.h
│   │   ├── SCFeatureImageCaptureImpl.h
│   │   └── SCFeatureImageCaptureImpl.m
│   ├── NightMode/
│   │   ├── SCFeatureNightMode.h
│   │   ├── SCNightModeButton.h
│   │   └── SCNightModeButton.m
│   ├── Scanning/
│   │   └── SCFeatureScanning.h
│   ├── Shazam/
│   │   └── SCFeatureShazam.h
│   ├── SnapKit/
│   │   └── SCFeatureSnapKit.h
│   ├── TapToFocus/
│   │   ├── SCFeatureTapToFocusAndExposure.h
│   │   ├── SCFeatureTapToFocusAndExposureImpl.h
│   │   ├── SCFeatureTapToFocusAndExposureImpl.m
│   │   ├── SCTapAnimationView.h
│   │   └── SCTapAnimationView.m
│   ├── ToggleCamera/
│   │   └── SCFeatureToggleCamera.h
│   └── Zooming/
│       └── SCFeatureZooming.h
├── Lens/
│   ├── SCManagedCapturerARImageCaptureProvider.h
│   ├── SCManagedCapturerGLViewManagerAPI.h
│   ├── SCManagedCapturerLSAComponentTrackerAPI.h
│   ├── SCManagedCapturerLensAPI.h
│   └── SCManagedCapturerLensAPIProvider.h
├── Logging/
│   ├── SCCoreCameraLogger.h
│   ├── SCCoreCameraLogger.m
│   ├── SCLogger+Camera.h
│   ├── SCLogger+Camera.m
│   └── SCManiphestTicketCreator.h
├── ManagedCapturer/
│   ├── ARConfiguration+SCConfiguration.h
│   ├── ARConfiguration+SCConfiguration.m
│   ├── AVCaptureConnection+InputDevice.h
│   ├── AVCaptureConnection+InputDevice.m
│   ├── AVCaptureDevice+ConfigurationLock.h
│   ├── AVCaptureDevice+ConfigurationLock.m
│   ├── CapturerV2/
│   │   ├── Configuration/
│   │   │   ├── SCCaptureConfiguration.h
│   │   │   ├── SCCaptureConfiguration.m
│   │   │   ├── SCCaptureConfigurationAnnouncer.h
│   │   │   ├── SCCaptureConfigurationAnnouncer.m
│   │   │   ├── SCCaptureConfigurationAnnouncer_Private.h
│   │   │   ├── SCCaptureConfigurationListener.h
│   │   │   ├── SCCaptureConfiguration_Private.h
│   │   │   ├── SCCaptureConfigurator.h
│   │   │   └── SCCaptureConfigurator.m
│   │   └── Core/
│   │       ├── SCCaptureCore.h
│   │       └── SCCaptureCore.m
│   ├── ImageProcessing/
│   │   ├── SCDepthBlurMetalModule.metal
│   │   ├── SCDepthBlurMetalRenderCommand.h
│   │   ├── SCDepthBlurMetalRenderCommand.m
│   │   ├── SCDepthToGrayscaleMetalModule.metal
│   │   ├── SCDepthToGrayscaleMetalRenderCommand.h
│   │   ├── SCDepthToGrayscaleMetalRenderCommand.m
│   │   ├── SCDigitalExposureHandler.h
│   │   ├── SCDigitalExposureHandler.m
│   │   ├── SCExposureAdjustMetalModule.metal
│   │   ├── SCExposureAdjustMetalRenderCommand.h
│   │   ├── SCExposureAdjustMetalRenderCommand.m
│   │   ├── SCExposureAdjustProcessingModule.h
│   │   ├── SCExposureAdjustProcessingModule.m
│   │   ├── SCMetalModule.h
│   │   ├── SCMetalModule.m
│   │   ├── SCMetalTextureResource.h
│   │   ├── SCMetalTextureResource.m
│   │   ├── SCNightModeEnhancementMetalModule.metal
│   │   ├── SCNightModeEnhancementMetalRenderCommand.h
│   │   ├── SCNightModeEnhancementMetalRenderCommand.m
│   │   ├── SCProcessingModule.h
│   │   ├── SCProcessingModuleUtils.h
│   │   ├── SCProcessingModuleUtils.m
│   │   ├── SCProcessingPipeline.h
│   │   ├── SCProcessingPipeline.m
│   │   ├── SCProcessingPipelineBuilder.h
│   │   ├── SCProcessingPipelineBuilder.m
│   │   ├── SCStillImageDepthBlurFilter.h
│   │   └── SCStillImageDepthBlurFilter.m
│   ├── NSURL+Asset.h
│   ├── NSURL+Asset.m
│   ├── OWNERS
│   ├── SCAudioCaptureSession.h
│   ├── SCAudioCaptureSession.m
│   ├── SCCameraSettingUtils.h
│   ├── SCCameraSettingUtils.m
│   ├── SCCaptureCommon.h
│   ├── SCCaptureCommon.m
│   ├── SCCaptureCoreImageFaceDetector.h
│   ├── SCCaptureCoreImageFaceDetector.m
│   ├── SCCaptureDeviceAuthorization.h
│   ├── SCCaptureDeviceAuthorization.m
│   ├── SCCaptureDeviceAuthorizationChecker.h
│   ├── SCCaptureDeviceAuthorizationChecker.m
│   ├── SCCaptureDeviceResolver.h
│   ├── SCCaptureDeviceResolver.m
│   ├── SCCaptureFaceDetectionParser.h
│   ├── SCCaptureFaceDetectionParser.m
│   ├── SCCaptureFaceDetector.h
│   ├── SCCaptureFaceDetectorTrigger.h
│   ├── SCCaptureFaceDetectorTrigger.m
│   ├── SCCaptureMetadataObjectParser.h
│   ├── SCCaptureMetadataObjectParser.m
│   ├── SCCaptureMetadataOutputDetector.h
│   ├── SCCaptureMetadataOutputDetector.m
│   ├── SCCapturer.h
│   ├── SCCapturerBufferedVideoWriter.h
│   ├── SCCapturerBufferedVideoWriter.m
│   ├── SCCapturerDefines.h
│   ├── SCCapturerToken.h
│   ├── SCCapturerToken.m
│   ├── SCCapturerTokenProvider.h
│   ├── SCCapturerTokenProvider.m
│   ├── SCExposureState.h
│   ├── SCExposureState.m
│   ├── SCFileAudioCaptureSession.h
│   ├── SCFileAudioCaptureSession.m
│   ├── SCManagedAudioStreamer.h
│   ├── SCManagedAudioStreamer.m
│   ├── SCManagedCaptureDevice+SCManagedCapturer.h
│   ├── SCManagedCaptureDevice+SCManagedDeviceCapacityAnalyzer.h
│   ├── SCManagedCaptureDevice.h
│   ├── SCManagedCaptureDevice.m
│   ├── SCManagedCaptureDeviceAutoExposureHandler.h
│   ├── SCManagedCaptureDeviceAutoExposureHandler.m
│   ├── SCManagedCaptureDeviceAutoFocusHandler.h
│   ├── SCManagedCaptureDeviceAutoFocusHandler.m
│   ├── SCManagedCaptureDeviceDefaultZoomHandler.h
│   ├── SCManagedCaptureDeviceDefaultZoomHandler.m
│   ├── SCManagedCaptureDeviceDefaultZoomHandler_Private.h
│   ├── SCManagedCaptureDeviceExposureHandler.h
│   ├── SCManagedCaptureDeviceFaceDetectionAutoExposureHandler.h
│   ├── SCManagedCaptureDeviceFaceDetectionAutoExposureHandler.m
│   ├── SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.h
│   ├── SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.m
│   ├── SCManagedCaptureDeviceFocusHandler.h
│   ├── SCManagedCaptureDeviceHandler.h
│   ├── SCManagedCaptureDeviceHandler.m
│   ├── SCManagedCaptureDeviceLinearInterpolationZoomHandler.h
│   ├── SCManagedCaptureDeviceLinearInterpolationZoomHandler.m
│   ├── SCManagedCaptureDeviceLockOnRecordExposureHandler.h
│   ├── SCManagedCaptureDeviceLockOnRecordExposureHandler.m
│   ├── SCManagedCaptureDeviceSavitzkyGolayZoomHandler.h
│   ├── SCManagedCaptureDeviceSavitzkyGolayZoomHandler.m
│   ├── SCManagedCaptureDeviceSubjectAreaHandler.h
│   ├── SCManagedCaptureDeviceSubjectAreaHandler.m
│   ├── SCManagedCaptureDeviceThresholdExposureHandler.h
│   ├── SCManagedCaptureDeviceThresholdExposureHandler.m
│   ├── SCManagedCaptureFaceDetectionAdjustingPOIResource.h
│   ├── SCManagedCaptureFaceDetectionAdjustingPOIResource.m
│   ├── SCManagedCapturePreviewLayerController.h
│   ├── SCManagedCapturePreviewLayerController.m
│   ├── SCManagedCapturePreviewView.h
│   ├── SCManagedCapturePreviewView.m
│   ├── SCManagedCapturePreviewViewDebugView.h
│   ├── SCManagedCapturePreviewViewDebugView.m
│   ├── SCManagedCaptureSession.h
│   ├── SCManagedCaptureSession.m
│   ├── SCManagedCapturer.h
│   ├── SCManagedCapturer.m
│   ├── SCManagedCapturerARSessionHandler.h
│   ├── SCManagedCapturerARSessionHandler.m
│   ├── SCManagedCapturerListener.h
│   ├── SCManagedCapturerListenerAnnouncer.h
│   ├── SCManagedCapturerListenerAnnouncer.mm
│   ├── SCManagedCapturerSampleMetadata.h
│   ├── SCManagedCapturerSampleMetadata.m
│   ├── SCManagedCapturerState.h
│   ├── SCManagedCapturerState.m
│   ├── SCManagedCapturerState.value
│   ├── SCManagedCapturerStateBuilder.h
│   ├── SCManagedCapturerStateBuilder.m
│   ├── SCManagedCapturerUtils.h
│   ├── SCManagedCapturerUtils.m
│   ├── SCManagedCapturerV1.h
│   ├── SCManagedCapturerV1.m
│   ├── SCManagedCapturerV1_Private.h
│   ├── SCManagedDeviceCapacityAnalyzer.h
│   ├── SCManagedDeviceCapacityAnalyzer.m
│   ├── SCManagedDeviceCapacityAnalyzerHandler.h
│   ├── SCManagedDeviceCapacityAnalyzerHandler.m
│   ├── SCManagedDeviceCapacityAnalyzerListener.h
│   ├── SCManagedDeviceCapacityAnalyzerListenerAnnouncer.h
│   ├── SCManagedDeviceCapacityAnalyzerListenerAnnouncer.mm
│   ├── SCManagedDroppedFramesReporter.h
│   ├── SCManagedDroppedFramesReporter.m
│   ├── SCManagedFrameHealthChecker.h
│   ├── SCManagedFrameHealthChecker.m
│   ├── SCManagedFrontFlashController.h
│   ├── SCManagedFrontFlashController.m
│   ├── SCManagedLegacyStillImageCapturer.h
│   ├── SCManagedLegacyStillImageCapturer.m
│   ├── SCManagedPhotoCapturer.h
│   ├── SCManagedPhotoCapturer.m
│   ├── SCManagedRecordedVideo.h
│   ├── SCManagedRecordedVideo.m
│   ├── SCManagedRecordedVideo.value
│   ├── SCManagedStillImageCapturer.h
│   ├── SCManagedStillImageCapturer.mm
│   ├── SCManagedStillImageCapturerHandler.h
│   ├── SCManagedStillImageCapturerHandler.m
│   ├── SCManagedStillImageCapturer_Protected.h
│   ├── SCManagedVideoARDataSource.h
│   ├── SCManagedVideoCapturer.h
│   ├── SCManagedVideoCapturer.m
│   ├── SCManagedVideoCapturerHandler.h
│   ├── SCManagedVideoCapturerHandler.m
│   ├── SCManagedVideoCapturerLogger.h
│   ├── SCManagedVideoCapturerLogger.m
│   ├── SCManagedVideoCapturerOutputSettings.h
│   ├── SCManagedVideoCapturerOutputSettings.m
│   ├── SCManagedVideoCapturerOutputSettings.value
│   ├── SCManagedVideoCapturerOutputType.h
│   ├── SCManagedVideoCapturerTimeObserver.h
│   ├── SCManagedVideoCapturerTimeObserver.m
│   ├── SCManagedVideoFileStreamer.h
│   ├── SCManagedVideoFileStreamer.m
│   ├── SCManagedVideoFrameSampler.h
│   ├── SCManagedVideoFrameSampler.m
│   ├── SCManagedVideoNoSoundLogger.h
│   ├── SCManagedVideoNoSoundLogger.m
│   ├── SCManagedVideoScanner.h
│   ├── SCManagedVideoScanner.m
│   ├── SCManagedVideoStreamReporter.h
│   ├── SCManagedVideoStreamReporter.m
│   ├── SCManagedVideoStreamer.h
│   ├── SCManagedVideoStreamer.m
│   ├── SCMetalUtils.h
│   ├── SCMetalUtils.m
│   ├── SCScanConfiguration.h
│   ├── SCScanConfiguration.m
│   ├── SCSingleFrameStreamCapturer.h
│   ├── SCSingleFrameStreamCapturer.m
│   ├── SCStillImageCaptureVideoInputMethod.h
│   ├── SCStillImageCaptureVideoInputMethod.m
│   ├── SCTimedTask.h
│   ├── SCTimedTask.m
│   ├── SCVideoCaptureSessionInfo.h
│   ├── StateMachine/
│   │   ├── SCCaptureBaseState.h
│   │   ├── SCCaptureBaseState.m
│   │   ├── SCCaptureStateDelegate.h
│   │   ├── SCCaptureStateMachineBookKeeper.h
│   │   ├── SCCaptureStateMachineBookKeeper.m
│   │   ├── SCCaptureStateMachineContext.h
│   │   ├── SCCaptureStateMachineContext.m
│   │   ├── SCCaptureStateUtil.h
│   │   ├── SCCaptureStateUtil.m
│   │   ├── SCManagedCapturerLogging.h
│   │   └── States/
│   │       ├── SCCaptureImageState.h
│   │       ├── SCCaptureImageState.m
│   │       ├── SCCaptureImageStateTransitionPayload.h
│   │       ├── SCCaptureImageStateTransitionPayload.m
│   │       ├── SCCaptureImageWhileRecordingState.h
│   │       ├── SCCaptureImageWhileRecordingState.m
│   │       ├── SCCaptureImageWhileRecordingStateTransitionPayload.h
│   │       ├── SCCaptureImageWhileRecordingStateTransitionPayload.m
│   │       ├── SCCaptureInitializedState.h
│   │       ├── SCCaptureInitializedState.m
│   │       ├── SCCaptureRecordingState.h
│   │       ├── SCCaptureRecordingState.m
│   │       ├── SCCaptureRecordingStateTransitionPayload.h
│   │       ├── SCCaptureRecordingStateTransitionPayload.m
│   │       ├── SCCaptureRunningState.h
│   │       ├── SCCaptureRunningState.m
│   │       ├── SCCaptureScanningState.h
│   │       ├── SCCaptureScanningState.m
│   │       ├── SCCaptureUninitializedState.h
│   │       ├── SCCaptureUninitializedState.m
│   │       ├── SCStateTransitionPayload.h
│   │       └── SCStateTransitionPayload.m
│   ├── UIScreen+Debug.h
│   └── UIScreen+Debug.m
├── README.md
├── Resource/
│   ├── SCCaptureResource.h
│   └── SCCaptureResource.m
├── Tweaks/
│   ├── SCCameraTweaks.h
│   └── SCCameraTweaks.m
├── UI/
│   ├── AVCameraViewEnums.h
│   ├── Lenses/
│   │   ├── LensButtonZ/
│   │   │   └── SCFeatureLensButtonZ.h
│   │   └── LensSideButton/
│   │       └── SCFeatureLensSideButton.h
│   ├── SCLongPressGestureRecognizer.h
│   └── SCLongPressGestureRecognizer.m
├── VolumeButton/
│   ├── SCCameraVolumeButtonHandler.h
│   └── SCCameraVolumeButtonHandler.m
└── Worker/
    ├── SCCaptureWorker.h
    └── SCCaptureWorker.m
Download .txt
SYMBOL INDEX (65 symbols across 12 files)

FILE: BlackCamera/SCBlackCameraReporter.h
  type SCBlackCameraStartRunningNotCalled (line 12) | typedef NS_ENUM(NSInteger, SCBlackCameraCause) {

FILE: Logging/SCLogger+Camera.h
  type CAMERA_CREATION_DELAY_LOGGING_START (line 16) | typedef NS_ENUM(NSUInteger, CameraCreationDelayLoggingStatus) {

FILE: ManagedCapturer/CapturerV2/Configuration/SCCaptureConfiguration_Private.h
  type NSNumber (line 11) | typedef NSNumber SCCaptureConfigurationDirtyKey;
  type SCCaptureConfigurationKeyIsRunning (line 23) | typedef NS_ENUM(NSUInteger, SCCaptureConfigurationKey) {

FILE: ManagedCapturer/ImageProcessing/SCProcessingModule.h
  type RenderData (line 13) | typedef struct RenderData {

FILE: ManagedCapturer/SCCapturerDefines.h
  type SampleBufferMetadata (line 16) | typedef struct SampleBufferMetadata {

FILE: ManagedCapturer/SCManagedStillImageCapturer_Protected.h
  function interface (line 26) | interface SCManagedStillImageCapturer () {

FILE: ManagedCapturer/SCMetalUtils.h
  function SC_ALWAYS_INLINE (line 23) | static SC_ALWAYS_INLINE BOOL SCDeviceSupportsMetal(void)
  function SCMetalCopyTexture (line 50) | static inline void SCMetalCopyTexture(id<MTLTexture> texture, CVPixelBuf...

FILE: ManagedCapturer/SCVideoCaptureSessionInfo.h
  type SCManagedVideoCapturerInfoAudioQueueError (line 14) | typedef NS_ENUM(NSInteger, SCManagedVideoCapturerInfoType) {
  type u_int32_t (line 23) | typedef u_int32_t sc_managed_capturer_recording_session_t;
  type SCVideoCaptureSessionInfo (line 29) | typedef struct {
  function SCVideoCaptureSessionInfo (line 36) | static inline SCVideoCaptureSessionInfo SCVideoCaptureSessionInfoMake(CM...
  function NSTimeInterval (line 51) | static inline NSTimeInterval SCVideoCaptureSessionInfoGetCurrentDuration...
  function NSString (line 62) | static inline NSString *SCVideoCaptureSessionInfoGetDebugString(CMTime t...
  function NSString (line 71) | static inline NSString *SCVideoCaptureSessionInfoGetDebugDescription(SCV...

FILE: ManagedCapturer/StateMachine/SCCaptureStateUtil.h
  type NSNumber (line 19) | typedef NSNumber SCCaptureStateKey;

FILE: Resource/SCCaptureResource.h
  type SCManagedCapturerStatus (line 92) | typedef enum : NSUInteger {

FILE: Tweaks/SCCameraTweaks.h
  type SCManagedCaptureDeviceDefaultZoom (line 20) | typedef NS_ENUM(NSUInteger, SCManagedCaptureDeviceZoomHandlerType) {
  function SC_EXTERN_C_END (line 85) | SC_EXTERN_C_END
  function BOOL (line 92) | static inline BOOL SCCameraTweaksEnableForceTouchToToggleCamera(void)
  function BOOL (line 97) | static inline BOOL SCCameraTweaksEnableStayOnCameraAfterPostingStory(void)
  function BOOL (line 102) | static inline BOOL SCCameraTweaksEnableKeepLastFrameOnCamera(void)
  function BOOL (line 107) | static inline BOOL SCCameraTweaksSmoothAutoFocusWhileRecording(void)
  function NSInteger (line 112) | static inline NSInteger SCCameraExposureAdjustmentMode(void)
  function BOOL (line 122) | static inline BOOL SCCameraTweaksRotateToggleCameraButton(void)
  function CGFloat (line 127) | static inline CGFloat SCCameraTweaksRotateToggleCameraButtonTime(void)
  function BOOL (line 132) | static inline BOOL SCCameraTweaksDefaultPortrait(void)
  function BOOL (line 138) | static inline BOOL SCCameraTweaksTranscodingAlwaysFails(void)
  function BOOL (line 146) | static inline BOOL SCCameraTweaksDisableOverlayVideoMask(void)
  function NSInteger (line 151) | static inline NSInteger SCCameraTweaksDelayTurnOnFilters(void)
  function BOOL (line 159) | static inline BOOL SCCameraTweaksEnableExposurePointObservation(void)
  function BOOL (line 164) | static inline BOOL SCCameraTweaksEnableFocusPointObservation(void)
  function CGFloat (line 169) | static inline CGFloat SCCameraTweaksSmoothZoomThresholdTime()
  function CGFloat (line 174) | static inline CGFloat SCCameraTweaksSmoothZoomThresholdFactor()
  function CGFloat (line 179) | static inline CGFloat SCCameraTweaksSmoothZoomIntermediateFramesPerSecond()
  function CGFloat (line 184) | static inline CGFloat SCCameraTweaksSmoothZoomDelayTolerantTime()
  function CGFloat (line 189) | static inline CGFloat SCCameraTweaksSmoothZoomMinStepLength()
  function CGFloat (line 194) | static inline CGFloat SCCameraTweaksExposureDeadline()
  function BOOL (line 199) | static inline BOOL SCCameraTweaksKillFrontCamera(void)
  function BOOL (line 204) | static inline BOOL SCCameraTweaksKillBackCamera(void)
  function BOOL (line 211) | static inline BOOL SCCameraTweaksUseRealMockImage(void)
  function CGFloat (line 218) | static inline CGFloat SCCameraTweaksShortPreviewTransitionAnimationDurat...
  function SCCameraTweaksStrategyType (line 223) | static inline SCCameraTweaksStrategyType SCCameraTweaksPreviewTransition...
  function CGFloat (line 234) | static inline CGFloat SCCameraTweaksEnablePortraitModeButton(void)
  function CGFloat (line 239) | static inline CGFloat SCCameraTweaksDepthBlurForegroundThreshold(void)
  function CGFloat (line 244) | static inline CGFloat SCCameraTweaksDepthBlurBackgroundThreshold(void)
  function CGFloat (line 249) | static inline CGFloat SCCameraTweaksBlurSigma(void)
  function BOOL (line 254) | static inline BOOL SCCameraTweaksEnableFilterInputFocusRect(void)
  function BOOL (line 259) | static inline BOOL SCCameraTweaksEnablePortraitModeTapToFocus(void)
  function BOOL (line 264) | static inline BOOL SCCameraTweaksEnablePortraitModeAutofocus(void)
  function BOOL (line 269) | static inline BOOL SCCameraTweaksDepthToGrayscaleOverride(void)
  function SCCameraTweaksStrategyType (line 274) | static inline SCCameraTweaksStrategyType SCCameraTweaksEnableHandsFreeXT...
  function SCCameraHandsFreeModeType (line 285) | static inline SCCameraHandsFreeModeType SCCameraTweaksHandsFreeModeType()
  function SCCameraTweaksStrategyType (line 299) | static inline SCCameraTweaksStrategyType SCCameraTweaksPreviewPresenterF...
  function NSInteger (line 310) | static inline NSInteger SCCameraTweaksEnableCaptureKeepRecordedVideoStra...
  function NSInteger (line 322) | static inline NSInteger SCCameraTweaksEnableCaptureSharePerformerStrateg...
  function SCCameraFaceFocusModeStrategyType (line 334) | static inline SCCameraFaceFocusModeStrategyType SCCameraTweaksFaceFocusS...
  function SCCameraFaceFocusDetectionMethodType (line 347) | static inline SCCameraFaceFocusDetectionMethodType SCCameraTweaksFaceFoc...
  function SCCameraTweaksFaceFocusDetectionFrequency (line 359) | static inline int SCCameraTweaksFaceFocusDetectionFrequency()
  function BOOL (line 364) | static inline BOOL SCCameraTweaksFaceFocusMinFaceSizeRespectABTesting()
  function CGFloat (line 369) | static inline CGFloat SCCameraTweaksFaceFocusMinFaceSizeValue()
  function BOOL (line 374) | static inline BOOL SCCameraTweaksEnableDualCamera(void)

FILE: UI/AVCameraViewEnums.h
  type AVCameraViewControllerRecordingStateDefault (line 34) | typedef NS_ENUM(NSUInteger, AVCameraViewControllerRecordingState) {
  type SCCameraRecordingMethodCameraButton (line 43) | typedef NS_ENUM(NSUInteger, SCCameraRecordingMethod) {
Condensed preview — 311 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,200K chars).
[
  {
    "path": "BlackCamera/SCBlackCameraDetector.h",
    "chars": 1635,
    "preview": "//\n//  SCBlackCameraDetector.h\n//  Snapchat\n//\n//  Created by Derek Wang on 24/01/2018.\n//\n\n#import \"SCBlackCameraReport"
  },
  {
    "path": "BlackCamera/SCBlackCameraDetector.m",
    "chars": 4271,
    "preview": "//\n//  SCBlackCameraDetector.m\n//  Snapchat\n//\n//  Created by Derek Wang on 24/01/2018.\n//\n\n#import \"SCBlackCameraDetect"
  },
  {
    "path": "BlackCamera/SCBlackCameraNoOutputDetector.h",
    "chars": 749,
    "preview": "//\n//  SCBlackCameraNoOutputDetector.h\n//  Snapchat\n//\n//  Created by Derek Wang on 05/12/2017.\n//\n\n#import \"SCManagedCa"
  },
  {
    "path": "BlackCamera/SCBlackCameraNoOutputDetector.m",
    "chars": 5447,
    "preview": "//\n//  SCBlackCameraDetectorNoOutput.m\n//  Snapchat\n//\n//  Created by Derek Wang on 05/12/2017.\n//\n//  This detector is "
  },
  {
    "path": "BlackCamera/SCBlackCameraPreviewDetector.h",
    "chars": 489,
    "preview": "//\n//  SCBlackCameraPreviewDetector.h\n//  Snapchat\n//\n//  Created by Derek Wang on 25/01/2018.\n//\n\n#import <Foundation/F"
  },
  {
    "path": "BlackCamera/SCBlackCameraPreviewDetector.m",
    "chars": 2400,
    "preview": "//\n//  SCBlackCameraPreviewDetector.m\n//  Snapchat\n//\n//  Created by Derek Wang on 25/01/2018.\n//\n\n#import \"SCBlackCamer"
  },
  {
    "path": "BlackCamera/SCBlackCameraReporter.h",
    "chars": 1297,
    "preview": "//\n//  SCBlackCameraReporter.h\n//  Snapchat\n//\n//  Created by Derek Wang on 09/01/2018.\n//\n\n#import <SCBase/SCMacros.h>\n"
  },
  {
    "path": "BlackCamera/SCBlackCameraReporter.m",
    "chars": 2600,
    "preview": "//\n//  SCBlackCameraReporter.m\n//  Snapchat\n//\n//  Created by Derek Wang on 09/01/2018.\n//\n\n#import \"SCBlackCameraReport"
  },
  {
    "path": "BlackCamera/SCBlackCameraRunningDetector.h",
    "chars": 720,
    "preview": "//\n//  SCBlackCameraRunningDetector.h\n//  Snapchat\n//\n//  Created by Derek Wang on 30/01/2018.\n//\n\n#import <SCBase/SCMac"
  },
  {
    "path": "BlackCamera/SCBlackCameraRunningDetector.m",
    "chars": 1962,
    "preview": "//\n//  SCBlackCameraRunningDetector.m\n//  Snapchat\n//\n//  Created by Derek Wang on 30/01/2018.\n//\n\n#import \"SCBlackCamer"
  },
  {
    "path": "BlackCamera/SCBlackCameraSessionBlockDetector.h",
    "chars": 487,
    "preview": "//\n//  SCBlackCameraSessionBlockDetector.h\n//  Snapchat\n//\n//  Created by Derek Wang on 25/01/2018.\n//\n\n#import \"SCBlack"
  },
  {
    "path": "BlackCamera/SCBlackCameraSessionBlockDetector.m",
    "chars": 2321,
    "preview": "//\n//  SCBlackCameraSessionBlockDetector.m\n//  Snapchat\n//\n//  Created by Derek Wang on 25/01/2018.\n//\n\n#import \"SCBlack"
  },
  {
    "path": "BlackCamera/SCBlackCameraViewDetector.h",
    "chars": 806,
    "preview": "//\n//  SCBlackCameraDetectorCameraView.h\n//  Snapchat\n//\n//  Created by Derek Wang on 24/01/2018.\n//\n\n#import <Foundatio"
  },
  {
    "path": "BlackCamera/SCBlackCameraViewDetector.m",
    "chars": 3856,
    "preview": "//\n//  SCBlackCameraDetectorCameraView.m\n//  Snapchat\n//\n//  Created by Derek Wang on 24/01/2018.\n//\n\n#import \"SCBlackCa"
  },
  {
    "path": "BlackCamera/SCCaptureSessionFixer.h",
    "chars": 252,
    "preview": "//\n//  SCCaptureSessionFixer.h\n//  Snapchat\n//\n//  Created by Derek Wang on 05/12/2017.\n//\n\n#import \"SCBlackCameraNoOutp"
  },
  {
    "path": "BlackCamera/SCCaptureSessionFixer.m",
    "chars": 412,
    "preview": "//\n//  SCCaptureSessionFixer.m\n//  Snapchat\n//\n//  Created by Derek Wang on 05/12/2017.\n//\n\n#import \"SCCaptureSessionFix"
  },
  {
    "path": "ContextAwareTaskManagement/OWNERS",
    "chars": 172,
    "preview": "--- !OWNERS\n\nversion: 2\n\ndefault:\n  jira_project: CCAM\n  owners:\n    num_required_reviewers: 1\n    teams:\n    - Snapchat"
  },
  {
    "path": "ContextAwareTaskManagement/Requests/SCContextAwareSnapCreationThrottleRequest.h",
    "chars": 328,
    "preview": "//\n//  SCContextAwareSnapCreationThrottleRequest.h\n//  SCCamera\n//\n//  Created by Cheng Jiang on 4/24/18.\n//\n\n#import <S"
  },
  {
    "path": "ContextAwareTaskManagement/Requests/SCContextAwareSnapCreationThrottleRequest.m",
    "chars": 1934,
    "preview": "//\n//  SCContextAwareSnapCreationThrottleRequest.m\n//  SCCamera\n//\n//  Created by Cheng Jiang on 4/24/18.\n//\n\n#import \"S"
  },
  {
    "path": "ContextAwareTaskManagement/Triggers/SCSnapCreationTriggers.h",
    "chars": 429,
    "preview": "//\n//  SCSnapCreationTriggers.h\n//  Snapchat\n//\n//  Created by Cheng Jiang on 4/1/18.\n//\n\n#import <Foundation/Foundation"
  },
  {
    "path": "ContextAwareTaskManagement/Triggers/SCSnapCreationTriggers.m",
    "chars": 2345,
    "preview": "//\n//  SCSnapCreationTriggers.m\n//  Snapchat\n//\n//  Created by Cheng Jiang on 3/30/18.\n//\n\n#import \"SCSnapCreationTrigge"
  },
  {
    "path": "Features/Core/SCFeature.h",
    "chars": 821,
    "preview": "//\n//  SCFeature.h\n//  SCCamera\n//\n//  Created by Kristian Bauer on 1/4/18.\n//\n\n#import <UIKit/UIKit.h>\n\n/**\n * Top leve"
  },
  {
    "path": "Features/Core/SCFeatureContainerView.h",
    "chars": 265,
    "preview": "//\n//  SCFeatureContainerView.h\n//  SCCamera\n//\n//  Created by Kristian Bauer on 4/17/18.\n//\n\n#import <UIKit/UIKit.h>\n\n@"
  },
  {
    "path": "Features/Core/SCFeatureCoordinator.h",
    "chars": 1418,
    "preview": "//\n//  SCFeatureCoordinator.h\n//  SCCamera\n//\n//  Created by Kristian Bauer on 1/4/18.\n//\n\n#import \"SCFeature.h\"\n\n#impor"
  },
  {
    "path": "Features/Core/SCFeatureCoordinator.m",
    "chars": 3532,
    "preview": "//\n//  SCFeatureCoordinator.m\n//  SCCamera\n//\n//  Created by Kristian Bauer on 1/4/18.\n//\n\n#import \"SCFeatureCoordinator"
  },
  {
    "path": "Features/Core/SCFeatureProvider.h",
    "chars": 1943,
    "preview": "//\n//  SCFeatureProvider.h\n//  SCCamera\n//\n//  Created by Kristian Bauer on 1/4/18.\n//\n\n#import <SCCamera/AVCameraViewEn"
  },
  {
    "path": "Features/Flash/SCFeatureFlash.h",
    "chars": 370,
    "preview": "//\n//  SCFeatureFlash.h\n//  SCCamera\n//\n//  Created by Kristian Bauer on 3/27/18.\n//\n\n#import \"SCFeature.h\"\n\n@class SCNa"
  },
  {
    "path": "Features/Flash/SCFeatureFlashImpl.h",
    "chars": 642,
    "preview": "//\n//  SCFeatureFlashImpl.h\n//  SCCamera\n//\n//  Created by Kristian Bauer on 3/27/18.\n//\n\n#import \"SCFeatureFlash.h\"\n\n#i"
  },
  {
    "path": "Features/Flash/SCFeatureFlashImpl.m",
    "chars": 7793,
    "preview": "//\n//  SCFeatureFlashImpl.m\n//  SCCamera\n//\n//  Created by Kristian Bauer on 3/27/18.\n//\n\n#import \"SCFeatureFlashImpl.h\""
  },
  {
    "path": "Features/Flash/SCFlashButton.h",
    "chars": 389,
    "preview": "//\n//  SCFlashButton.h\n//  SCCamera\n//\n//  Created by Will Wu on 2/13/14.\n//  Copyright (c) 2014 Snapchat, Inc. All righ"
  },
  {
    "path": "Features/Flash/SCFlashButton.m",
    "chars": 962,
    "preview": "//\n//  SCFlashButton.m\n//  SCCamera\n//\n//  Created by Will Wu on 2/13/14.\n//  Copyright (c) 2014 Snapchat, Inc. All righ"
  },
  {
    "path": "Features/HandsFree/SCFeatureHandsFree.h",
    "chars": 758,
    "preview": "//\n//  SCFeatureHandsFree.h\n//  SCCamera\n//\n//  Created by Kristian Bauer on 2/26/18.\n//\n\n#import \"SCFeature.h\"\n\n#import"
  },
  {
    "path": "Features/ImageCapture/SCFeatureImageCapture.h",
    "chars": 870,
    "preview": "//\n//  SCFeatureImageCapture.h\n//  SCCamera\n//\n//  Created by Kristian Bauer on 4/18/18.\n//\n\n#import \"SCFeature.h\"\n\n#imp"
  },
  {
    "path": "Features/ImageCapture/SCFeatureImageCaptureImpl.h",
    "chars": 539,
    "preview": "//\n//  SCFeatureImageCaptureImpl.h\n//  SCCamera\n//\n//  Created by Kristian Bauer on 4/18/18.\n//\n\n#import \"AVCameraViewEn"
  },
  {
    "path": "Features/ImageCapture/SCFeatureImageCaptureImpl.m",
    "chars": 7159,
    "preview": "//\n//  SCFeatureImageCaptureImpl.m\n//  SCCamera\n//\n//  Created by Kristian Bauer on 4/18/18.\n//\n\n#import \"SCFeatureImage"
  },
  {
    "path": "Features/NightMode/SCFeatureNightMode.h",
    "chars": 562,
    "preview": "//\n//  SCFeatureNightMode.h\n//  SCCamera\n//\n//  Created by Kristian Bauer on 4/9/18.\n//\n\n#import \"SCFeature.h\"\n\n@class S"
  },
  {
    "path": "Features/NightMode/SCNightModeButton.h",
    "chars": 427,
    "preview": "//\n//  SCNightModeButton.h\n//  SCCamera\n//\n//  Created by Liu Liu on 3/19/15.\n//  Copyright (c) 2015 Snapchat, Inc. All "
  },
  {
    "path": "Features/NightMode/SCNightModeButton.m",
    "chars": 2601,
    "preview": "//\n//  SCNightModeButton.m\n//  SCCamera\n//\n//  Created by Liu Liu on 3/19/15.\n//  Copyright (c) 2015 Snapchat, Inc. All "
  },
  {
    "path": "Features/Scanning/SCFeatureScanning.h",
    "chars": 640,
    "preview": "//\n//  SCFeatureScanning.h\n//  Snapchat\n//\n//  Created by Xiaokang Liu on 2018/4/19.\n//\n\n#import \"SCFeature.h\"\n\n@protoco"
  },
  {
    "path": "Features/Shazam/SCFeatureShazam.h",
    "chars": 626,
    "preview": "//\n//  SCFeatureShazam.h\n//  SCCamera\n//\n//  Created by Xiaokang Liu on 2018/4/18.\n//\n\n#import \"SCFeature.h\"\n\n@class SCL"
  },
  {
    "path": "Features/SnapKit/SCFeatureSnapKit.h",
    "chars": 257,
    "preview": "//\n//  SCFeatureSnapKit.h\n//  SCCamera\n//\n//  Created by Michel Loenngren on 3/19/18.\n//\n\n#import \"SCFeature.h\"\n\n@class "
  },
  {
    "path": "Features/TapToFocus/SCFeatureTapToFocusAndExposure.h",
    "chars": 292,
    "preview": "//\n//  SCFeatureTapToFocusAndExposure.h\n//  SCCamera\n//\n//  Created by Michel Loenngren on 4/5/18.\n//\n\n#import \"SCFeatur"
  },
  {
    "path": "Features/TapToFocus/SCFeatureTapToFocusAndExposureImpl.h",
    "chars": 1311,
    "preview": "//\n//  SCFeatureTapToFocusAndExposureImpl.h\n//  SCCamera\n//\n//  Created by Michel Loenngren on 4/5/18.\n//\n\n#import \"SCFe"
  },
  {
    "path": "Features/TapToFocus/SCFeatureTapToFocusAndExposureImpl.m",
    "chars": 4046,
    "preview": "//\n//  SCFeatureTapToFocusImpl.m\n//  SCCamera\n//\n//  Created by Michel Loenngren on 4/5/18.\n//\n\n#import \"SCFeatureTapToF"
  },
  {
    "path": "Features/TapToFocus/SCTapAnimationView.h",
    "chars": 425,
    "preview": "//\n//  SCTapAnimationView.h\n//  SCCamera\n//\n//  Created by Alexander Grytsiuk on 8/26/15.\n//  Copyright (c) 2015 Snapcha"
  },
  {
    "path": "Features/TapToFocus/SCTapAnimationView.m",
    "chars": 7103,
    "preview": "//\n//  SCTapAnimationView.m\n//  SCCamera\n//\n//  Created by Alexander Grytsiuk on 8/26/15.\n//  Copyright (c) 2015 Snapcha"
  },
  {
    "path": "Features/ToggleCamera/SCFeatureToggleCamera.h",
    "chars": 1079,
    "preview": "//\n//  SCFeatureToggleCamera.h\n//  SCCamera\n//\n//  Created by Michel Loenngren on 4/17/18.\n//\n\n#import <SCCamera/SCFeatu"
  },
  {
    "path": "Features/Zooming/SCFeatureZooming.h",
    "chars": 944,
    "preview": "//\n//  SCFeatureZooming.h\n//  SCCamera\n//\n//  Created by Xiaokang Liu on 2018/4/19.\n//\n\n#import \"SCFeature.h\"\n\n#import <"
  },
  {
    "path": "Lens/SCManagedCapturerARImageCaptureProvider.h",
    "chars": 603,
    "preview": "//\n//  SCManagedCapturerARImageCaptureProvider.h\n//  SCCamera\n//\n//  Created by Michel Loenngren on 4/11/18.\n//\n\n#import"
  },
  {
    "path": "Lens/SCManagedCapturerGLViewManagerAPI.h",
    "chars": 542,
    "preview": "//\n//  SCManagedCapturerGLViewManagerAPI.h\n//  SCCamera\n//\n//  Created by Michel Loenngren on 4/11/18.\n//\n\n#import <Look"
  },
  {
    "path": "Lens/SCManagedCapturerLSAComponentTrackerAPI.h",
    "chars": 375,
    "preview": "//\n//  SCManagedCapturerLSAComponentTrackerAPI.h\n//  SCCamera\n//\n//  Created by Michel Loenngren on 4/11/18.\n//\n\n#import"
  },
  {
    "path": "Lens/SCManagedCapturerLensAPI.h",
    "chars": 2219,
    "preview": "//\n//  SCManagedCapturerLensAPI.h\n//  SCCamera\n//\n//  Created by Michel Loenngren on 4/11/18.\n//\n\n#import \"SCManagedCapt"
  },
  {
    "path": "Lens/SCManagedCapturerLensAPIProvider.h",
    "chars": 450,
    "preview": "//\n//  SCManagedCapturerLensAPIProvider.h\n//  SCCamera\n//\n//  Created by Michel Loenngren on 4/12/18.\n//\n\n#import <Found"
  },
  {
    "path": "Logging/SCCoreCameraLogger.h",
    "chars": 2687,
    "preview": "//\n//  SCCoreCameraLogger.h\n//  Snapchat\n//\n//  Created by Chao Pang on 3/6/18.\n//\n\n#import <Foundation/Foundation.h>\n\n/"
  },
  {
    "path": "Logging/SCCoreCameraLogger.m",
    "chars": 14118,
    "preview": "//\n//  SCCoreCameraLogger.m\n//  Snapchat\n//\n//  Created by Chao Pang on 3/6/18.\n//\n\n#import \"SCCoreCameraLogger.h\"\n\n#imp"
  },
  {
    "path": "Logging/SCLogger+Camera.h",
    "chars": 2125,
    "preview": "//\n//  SCLogger+Camera.h\n//  Snapchat\n//\n//  Created by Derek Peirce on 5/8/17.\n//  Copyright © 2017 Snapchat, Inc. All "
  },
  {
    "path": "Logging/SCLogger+Camera.m",
    "chars": 14487,
    "preview": "//\n//  SCLogger+Camera.m\n//  Snapchat\n//\n//  Created by Derek Peirce on 5/8/17.\n//  Copyright © 2017 Snapchat, Inc. All "
  },
  {
    "path": "Logging/SCManiphestTicketCreator.h",
    "chars": 556,
    "preview": "//\n//  SCManiphestTicketCreator.h\n//  SCCamera\n//\n//  Created by Michel Loenngren on 4/16/18.\n//\n\n#import <Foundation/Fo"
  },
  {
    "path": "ManagedCapturer/ARConfiguration+SCConfiguration.h",
    "chars": 399,
    "preview": "//\n//  ARConfiguration+SCConfiguration.h\n//  Snapchat\n//\n//  Created by Max Goedjen on 11/7/17.\n//\n\n#import \"SCManagedCa"
  },
  {
    "path": "ManagedCapturer/ARConfiguration+SCConfiguration.m",
    "chars": 977,
    "preview": "//\n//  ARConfiguration+SCConfiguration.m\n//  Snapchat\n//\n//  Created by Max Goedjen on 11/7/17.\n//\n\n#import \"ARConfigura"
  },
  {
    "path": "ManagedCapturer/AVCaptureConnection+InputDevice.h",
    "chars": 287,
    "preview": "//\n//  AVCaptureConnection+InputDevice.h\n//  Snapchat\n//\n//  Created by William Morriss on 1/20/15\n//  Copyright (c) 201"
  },
  {
    "path": "ManagedCapturer/AVCaptureConnection+InputDevice.m",
    "chars": 679,
    "preview": "//\n//  AVCaptureConnection+InputDevice.m\n//  Snapchat\n//\n//  Created by William Morriss on 1/20/15\n//  Copyright (c) 201"
  },
  {
    "path": "ManagedCapturer/AVCaptureDevice+ConfigurationLock.h",
    "chars": 1511,
    "preview": "//\n//  AVCaptureDevice+ConfigurationLock.h\n//  Snapchat\n//\n//  Created by Derek Peirce on 4/19/17.\n//  Copyright © 2017 "
  },
  {
    "path": "ManagedCapturer/AVCaptureDevice+ConfigurationLock.m",
    "chars": 1568,
    "preview": "//\n//  AVCaptureDevice+ConfigurationLock.m\n//  Snapchat\n//\n//  Created by Derek Peirce on 4/19/17.\n//  Copyright © 2017 "
  },
  {
    "path": "ManagedCapturer/CapturerV2/Configuration/SCCaptureConfiguration.h",
    "chars": 3614,
    "preview": "//\n//  SCCaptureConfiguration.h\n//  Snapchat\n//\n//  Created by Lin Jia on 10/3/17.\n//\n//\n\n#import \"SCCaptureConfiguratio"
  },
  {
    "path": "ManagedCapturer/CapturerV2/Configuration/SCCaptureConfiguration.m",
    "chars": 1442,
    "preview": "//\n//  SCCaptureConfiguration.m\n//  Snapchat\n//\n//  Created by Lin Jia on 10/3/17.\n//\n//\n\n#import \"SCCaptureConfiguratio"
  },
  {
    "path": "ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationAnnouncer.h",
    "chars": 751,
    "preview": "//\n//  SCCaptureConfigurationAnnouncer.h\n//  Snapchat\n//\n//  Created by Lin Jia on 10/2/17.\n//\n//\n\n#import \"SCCaptureCon"
  },
  {
    "path": "ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationAnnouncer.m",
    "chars": 1816,
    "preview": "//\n//  SCCaptureConfigurationAnnouncer.m\n//  Snapchat\n//\n//  Created by Lin Jia on 10/2/17.\n//\n//\n\n#import \"SCCaptureCon"
  },
  {
    "path": "ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationAnnouncer_Private.h",
    "chars": 995,
    "preview": "//\n//  SCCaptureConfigurationAnnouncer_Private.h\n//  Snapchat\n//\n//  Created by Lin Jia on 10/2/17.\n//\n//\n\n#import \"SCCa"
  },
  {
    "path": "ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationListener.h",
    "chars": 518,
    "preview": "//\n//  SCCaptureConfigurationListener.h\n//  Snapchat\n//\n//  Created by Lin Jia on 10/2/17.\n//\n\n#import \"SCManagedCapture"
  },
  {
    "path": "ManagedCapturer/CapturerV2/Configuration/SCCaptureConfiguration_Private.h",
    "chars": 1321,
    "preview": "//\n//  SCCaptureConfiguration_Private.h\n//  Snapchat\n//\n//  Created by Lin Jia on 10/3/17.\n//\n//\n\n#import \"SCCaptureConf"
  },
  {
    "path": "ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurator.h",
    "chars": 2046,
    "preview": "//\n//  SCCaptureConfigurator.h\n//  Snapchat\n//\n//  Created by Lin Jia on 10/2/17.\n//\n//\n\n#import \"SCCaptureConfiguration"
  },
  {
    "path": "ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurator.m",
    "chars": 1612,
    "preview": "//\n//  SCCaptureConfiguration.m\n//  Snapchat\n//\n//  Created by Lin Jia on 10/2/17.\n//\n//\n\n#import \"SCCaptureConfigurator"
  },
  {
    "path": "ManagedCapturer/CapturerV2/Core/SCCaptureCore.h",
    "chars": 1363,
    "preview": "//\n//  SCCaptureCore.h\n//  Snapchat\n//\n//  Created by Lin Jia on 10/2/17.\n//\n//\n\n#import \"SCCaptureStateMachineContext.h"
  },
  {
    "path": "ManagedCapturer/CapturerV2/Core/SCCaptureCore.m",
    "chars": 18977,
    "preview": "//\n//  SCCaptureCore.m\n//  Snapchat\n//\n//  Created by Lin Jia on 10/2/17.\n//\n//\n\n#import \"SCCaptureCore.h\"\n\n#import \"SCC"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCDepthBlurMetalModule.metal",
    "chars": 2169,
    "preview": "//\n//  SCDepthBlurMetalModule.metal\n//  Snapchat\n//\n//  Created by Brian Ng on 10/31/17.\n//\n\n#include <metal_stdlib>\nusi"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCDepthBlurMetalRenderCommand.h",
    "chars": 420,
    "preview": "//\n//  SCDepthBlurMetalRenderCommand.h\n//  Snapchat\n//\n//  Created by Brian Ng on 11/8/17.\n//\n//\n\n#import \"SCMetalModule"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCDepthBlurMetalRenderCommand.m",
    "chars": 3168,
    "preview": "//\n//  SCDepthBlurMetalRenderCommand.m\n//  Snapchat\n//\n//  Created by Brian Ng on 11/8/17.\n//\n//\n\n#import \"SCDepthBlurMe"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCDepthToGrayscaleMetalModule.metal",
    "chars": 1133,
    "preview": "//\n//  SCDepthToGrayscaleMetalModule.metal\n//  Snapchat\n//\n//  Created by Brian Ng on 12/7/17.\n//\n\n#include <metal_stdli"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCDepthToGrayscaleMetalRenderCommand.h",
    "chars": 445,
    "preview": "//\n//  SCDepthToGrayscaleMetalRenderCommand.h\n//  Snapchat\n//\n//  Created by Brian Ng on 12/7/17.\n//\n//\n\n#import \"SCMeta"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCDepthToGrayscaleMetalRenderCommand.m",
    "chars": 2165,
    "preview": "//\n//  SCDepthToGrayscaleMetalRenderCommand.m\n//  Snapchat\n//\n//  Created by Brian Ng on 12/7/17.\n//\n//\n\n#import \"SCDept"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCDigitalExposureHandler.h",
    "chars": 979,
    "preview": "//\n//  SCDigitalExposureHandler.h\n//  Snapchat\n//\n//  Created by Yu-Kuan (Anthony) Lai on 6/15/17.\n//  Copyright © 2017 "
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCDigitalExposureHandler.m",
    "chars": 649,
    "preview": "//\n//  SCDigitalExposureHandler.m\n//  Snapchat\n//\n//  Created by Yu-Kuan (Anthony) Lai on 6/15/17.\n//  Copyright © 2017 "
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCExposureAdjustMetalModule.metal",
    "chars": 2655,
    "preview": "//\n//  SCExposureAdjustMetalModule.metal\n//  Snapchat\n//\n//  Created by Michel Loenngren on 7/11/17.\n//\n//\n\n#include <me"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCExposureAdjustMetalRenderCommand.h",
    "chars": 456,
    "preview": "//\n//  SCExposureAdjustMetalRenderCommand.h\n//  Snapchat\n//\n//  Created by Michel Loenngren on 7/11/17.\n//\n//\n\n#import \""
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCExposureAdjustMetalRenderCommand.m",
    "chars": 1954,
    "preview": "//\n//  SCExposureAdjustMetalRenderCommand.m\n//  Snapchat\n//\n//  Created by Michel Loenngren on 7/11/17.\n//\n//\n\n#import \""
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCExposureAdjustProcessingModule.h",
    "chars": 751,
    "preview": "//\n//  SCExposureAdjustProcessingModule.h\n//  Snapchat\n//\n//  Created by Yu-Kuan (Anthony) Lai on 6/1/17.\n//  Copyright "
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCExposureAdjustProcessingModule.m",
    "chars": 1764,
    "preview": "//\n//  SCExposureAdjustProcessingModule.m\n//  Snapchat\n//\n//  Created by Yu-Kuan (Anthony) Lai on 6/1/17.\n//  Copyright "
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCMetalModule.h",
    "chars": 1420,
    "preview": "//\n//  SCMetalModule.h\n//  Snapchat\n//\n//  Created by Michel Loenngren on 7/19/17.\n//\n//\n\n#import \"SCMetalTextureResourc"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCMetalModule.m",
    "chars": 4787,
    "preview": "//\n//  SCMetalModule.m\n//  Snapchat\n//\n//  Created by Michel Loenngren on 7/19/17.\n//\n//\n\n#import \"SCMetalModule.h\"\n\n#im"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCMetalTextureResource.h",
    "chars": 2146,
    "preview": "//\n//  SCMetalTextureResource.h\n//  Snapchat\n//\n//  Created by Brian Ng on 11/7/17.\n//\n\n#import \"SCProcessingModule.h\"\n#"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCMetalTextureResource.m",
    "chars": 8129,
    "preview": "//\n//  SCMetalTextureResource.m\n//  Snapchat\n//\n//  Created by Brian Ng on 11/7/17.\n//\n\n#import \"SCMetalTextureResource."
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCNightModeEnhancementMetalModule.metal",
    "chars": 1396,
    "preview": "//\n//  SCNightModeEnhancementMetalModule.metal\n//  Snapchat\n//\n//  Created by Chao Pang on 12/21/17.\n//\n//\n\n#include <me"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCNightModeEnhancementMetalRenderCommand.h",
    "chars": 409,
    "preview": "//\n//  SCNightModeEnhancementMetalRenderCommand.h\n//  Snapchat\n//\n//  Created by Chao Pang on 12/21/17.\n//\n\n#import \"SCM"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCNightModeEnhancementMetalRenderCommand.m",
    "chars": 2176,
    "preview": "//\n//  SCNightModeEnhancementMetalRenderCommand.m\n//  Snapchat\n//\n//  Created by Chao Pang on 12/21/17.\n//\n\n#import \"SCN"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCProcessingModule.h",
    "chars": 982,
    "preview": "//\n//  SCProcessingModule.h\n//  Snapchat\n//\n//  Created by Yu-Kuan (Anthony) Lai on 5/30/17.\n//  Copyright © 2017 Snapch"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCProcessingModuleUtils.h",
    "chars": 716,
    "preview": "//\n//  SCProcessingModuleUtils.h\n//  Snapchat\n//\n//  Created by Brian Ng on 11/10/17.\n//\n\n#import <CoreImage/CoreImage.h"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCProcessingModuleUtils.m",
    "chars": 3202,
    "preview": "//\n//  SCProcessingModuleUtils.m\n//  Snapchat\n//\n//  Created by Brian Ng on 11/10/17.\n//\n\n#import \"SCProcessingModuleUti"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCProcessingPipeline.h",
    "chars": 723,
    "preview": "//\n//  SCProcessingPipeline.h\n//  Snapchat\n//\n//  Created by Yu-Kuan (Anthony) Lai on 5/30/17.\n//  Copyright © 2017 Snap"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCProcessingPipeline.m",
    "chars": 1103,
    "preview": "//\n//  SCProcessingPipeline.m\n//  Snapchat\n//\n//  Created by Yu-Kuan (Anthony) Lai on 5/30/17.\n//  Copyright © 2017 Snap"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCProcessingPipelineBuilder.h",
    "chars": 826,
    "preview": "//\n//  SCProcessingPipelineBuilder.h\n//  Snapchat\n//\n//  Created by Yu-Kuan (Anthony) Lai on 6/1/17.\n//  Copyright © 201"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCProcessingPipelineBuilder.m",
    "chars": 2224,
    "preview": "//\n//  SCProcessingPipelineBuilder.m\n//  Snapchat\n//\n//  Created by Yu-Kuan (Anthony) Lai on 6/1/17.\n//  Copyright © 201"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCStillImageDepthBlurFilter.h",
    "chars": 689,
    "preview": "//\n//  SCStillImageDepthBlurFilter.h\n//  Snapchat\n//\n//  Created by Brian Ng on 10/11/17.\n//\n\n#import \"SCProcessingModul"
  },
  {
    "path": "ManagedCapturer/ImageProcessing/SCStillImageDepthBlurFilter.m",
    "chars": 2379,
    "preview": "//\n//  SCStillImageDepthBlurFilter.m\n//  Snapchat\n//\n//  Created by Brian Ng on 10/11/17.\n//\n\n#import \"SCStillImageDepth"
  },
  {
    "path": "ManagedCapturer/NSURL+Asset.h",
    "chars": 495,
    "preview": "//\n//  NSURL+NSURL_Asset.h\n//  Snapchat\n//\n//  Created by Michel Loenngren on 4/30/17.\n//  Copyright © 2017 Snapchat, In"
  },
  {
    "path": "ManagedCapturer/NSURL+Asset.m",
    "chars": 452,
    "preview": "//\n//  NSURL+NSURL_Asset.m\n//  Snapchat\n//\n//  Created by Michel Loenngren on 4/30/17.\n//  Copyright © 2017 Snapchat, In"
  },
  {
    "path": "ManagedCapturer/OWNERS",
    "chars": 137,
    "preview": "--- !OWNERS\n\nversion: 2\n\ndefault:\n  jira_project: CCAM\n  owners:\n    num_required_reviewers: 0\n    teams:\n    - Snapchat"
  },
  {
    "path": "ManagedCapturer/SCAudioCaptureSession.h",
    "chars": 1119,
    "preview": "//\n//  SCAudioCaptureSession.h\n//  Snapchat\n//\n//  Created by Liu Liu on 3/5/15.\n//  Copyright (c) 2015 Snapchat, Inc. A"
  },
  {
    "path": "ManagedCapturer/SCAudioCaptureSession.m",
    "chars": 11557,
    "preview": "//\n//  SCAudioCaptureSession.m\n//  Snapchat\n//\n//  Created by Liu Liu on 3/5/15.\n//  Copyright (c) 2015 Snapchat, Inc. A"
  },
  {
    "path": "ManagedCapturer/SCCameraSettingUtils.h",
    "chars": 708,
    "preview": "//\n//  SCCameraSettingUtils.h\n//  Snapchat\n//\n//  Created by Pinlin Chen on 12/09/2017.\n//\n\n#import <SCBase/SCMacros.h>\n"
  },
  {
    "path": "ManagedCapturer/SCCameraSettingUtils.m",
    "chars": 2679,
    "preview": "//\n//  SCCameraSettingUtils.m\n//  Snapchat\n//\n//  Created by Pinlin Chen on 12/09/2017.\n//\n\n#import \"SCCameraSettingUtil"
  },
  {
    "path": "ManagedCapturer/SCCaptureCommon.h",
    "chars": 3031,
    "preview": "//\n//  SCCaptureCommon.h\n//  Snapchat\n//\n//  Created by Lin Jia on 9/29/17.\n//\n//\n\n#import \"SCManagedCaptureDevice.h\"\n#i"
  },
  {
    "path": "ManagedCapturer/SCCaptureCommon.m",
    "chars": 920,
    "preview": "//\n//  SCCaptureCommon.m\n//  Snapchat\n//\n//  Created by Lin Jia on 9/29/17.\n//\n//\n\n#import \"SCCaptureCommon.h\"\n\nCGFloat "
  },
  {
    "path": "ManagedCapturer/SCCaptureCoreImageFaceDetector.h",
    "chars": 633,
    "preview": "//\n//  SCCaptureCoreImageFaceDetector.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/27/18.\n//  Copyright © 2018 Snapc"
  },
  {
    "path": "ManagedCapturer/SCCaptureCoreImageFaceDetector.m",
    "chars": 7975,
    "preview": "//\n//  SCCaptureCoreImageFaceDetector.m\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/27/18.\n//  Copyright © 2018 Snapc"
  },
  {
    "path": "ManagedCapturer/SCCaptureDeviceAuthorization.h",
    "chars": 831,
    "preview": "//\n//  SCCaptureDeviceAuthorization.h\n//  Snapchat\n//\n//  Created by Xiaomu Wu on 8/19/14.\n//  Copyright (c) 2014 Snapch"
  },
  {
    "path": "ManagedCapturer/SCCaptureDeviceAuthorization.m",
    "chars": 2241,
    "preview": "//\n//  SCCaptureDeviceAuthorization.m\n//  Snapchat\n//\n//  Created by Xiaomu Wu on 8/19/14.\n//  Copyright (c) 2014 Snapch"
  },
  {
    "path": "ManagedCapturer/SCCaptureDeviceAuthorizationChecker.h",
    "chars": 830,
    "preview": "//\n//  SCCaptureDeviceAuthorizationChecker.h\n//  Snapchat\n//\n//  Created by Sun Lei on 15/03/2018.\n//\n\n@class SCQueuePer"
  },
  {
    "path": "ManagedCapturer/SCCaptureDeviceAuthorizationChecker.m",
    "chars": 2059,
    "preview": "//\n//  SCCaptureDeviceAuthorizationChecker.m\n//  Snapchat\n//\n//  Created by Sun Lei on 15/03/2018.\n//\n\n#import \"SCCaptur"
  },
  {
    "path": "ManagedCapturer/SCCaptureDeviceResolver.h",
    "chars": 697,
    "preview": "//\n//  SCCaptureDeviceResolver.h\n//  Snapchat\n//\n//  Created by Lin Jia on 11/8/17.\n//\n//\n\n#import <AVFoundation/AVFound"
  },
  {
    "path": "ManagedCapturer/SCCaptureDeviceResolver.m",
    "chars": 5096,
    "preview": "//\n//  SCCaptureDeviceResolver.m\n//  Snapchat\n//\n//  Created by Lin Jia on 11/8/17.\n//\n//\n\n#import \"SCCaptureDeviceResol"
  },
  {
    "path": "ManagedCapturer/SCCaptureFaceDetectionParser.h",
    "chars": 1538,
    "preview": "//\n//  SCCaptureFaceDetectionParser.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/13/18.\n//  Copyright © 2018 Snapcha"
  },
  {
    "path": "ManagedCapturer/SCCaptureFaceDetectionParser.m",
    "chars": 3961,
    "preview": "//\n//  SCCaptureFaceDetectionParser.m\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/13/18.\n//  Copyright © 2018 Snapcha"
  },
  {
    "path": "ManagedCapturer/SCCaptureFaceDetector.h",
    "chars": 770,
    "preview": "//\n//  SCCaptureFaceDetector.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/27/18.\n//  Copyright © 2018 Snapchat, Inc."
  },
  {
    "path": "ManagedCapturer/SCCaptureFaceDetectorTrigger.h",
    "chars": 495,
    "preview": "//\n//  SCCaptureFaceDetectorTrigger.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/22/18.\n//  Copyright © 2018 Snapcha"
  },
  {
    "path": "ManagedCapturer/SCCaptureFaceDetectorTrigger.m",
    "chars": 3382,
    "preview": "//\n//  SCCaptureFaceDetectorTrigger.m\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/22/18.\n//  Copyright © 2018 Snapcha"
  },
  {
    "path": "ManagedCapturer/SCCaptureMetadataObjectParser.h",
    "chars": 643,
    "preview": "//\n//  SCCaptureMetadataObjectParser.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/13/18.\n//  Copyright © 2018 Snapch"
  },
  {
    "path": "ManagedCapturer/SCCaptureMetadataObjectParser.m",
    "chars": 1191,
    "preview": "//\n//  SCCaptureMetadataObjectParser.m\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/13/18.\n//  Copyright © 2018 Snapch"
  },
  {
    "path": "ManagedCapturer/SCCaptureMetadataOutputDetector.h",
    "chars": 465,
    "preview": "//\n//  SCCaptureMetadataOutputDetector.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 12/21/17.\n//  Copyright © 2017 Sna"
  },
  {
    "path": "ManagedCapturer/SCCaptureMetadataOutputDetector.m",
    "chars": 7232,
    "preview": "//\n//  SCCaptureMetadataOutputDetector.m\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 12/21/17.\n//  Copyright © 2017 Sna"
  },
  {
    "path": "ManagedCapturer/SCCapturer.h",
    "chars": 11099,
    "preview": "//\n//  SCManagedCapturer.h\n//  Snapchat\n//\n//  Created by Liu Liu on 4/20/15.\n//  Copyright (c) 2015 Liu Liu. All rights"
  },
  {
    "path": "ManagedCapturer/SCCapturerBufferedVideoWriter.h",
    "chars": 1178,
    "preview": "//\n//  SCCapturerBufferedVideoWriter.h\n//  Snapchat\n//\n//  Created by Chao Pang on 12/5/17.\n//\n\n#import <SCFoundation/SC"
  },
  {
    "path": "ManagedCapturer/SCCapturerBufferedVideoWriter.m",
    "chars": 19175,
    "preview": "//\n//  SCCapturerBufferedVideoWriter.m\n//  Snapchat\n//\n//  Created by Chao Pang on 12/5/17.\n//\n\n#import \"SCCapturerBuffe"
  },
  {
    "path": "ManagedCapturer/SCCapturerDefines.h",
    "chars": 454,
    "preview": "//\n//  SCCapturerDefines.h\n//  Snapchat\n//\n//  Created by Chao Pang on 12/20/17.\n//\n\n#import <Foundation/Foundation.h>\n\n"
  },
  {
    "path": "ManagedCapturer/SCCapturerToken.h",
    "chars": 480,
    "preview": "//\n//  SCCapturerToken.h\n//  Snapchat\n//\n//  Created by Xishuo Liu on 3/24/17.\n//  Copyright © 2017 Snapchat, Inc. All r"
  },
  {
    "path": "ManagedCapturer/SCCapturerToken.m",
    "chars": 545,
    "preview": "//\n//  SCCapturerToken.m\n//  Snapchat\n//\n//  Created by Xishuo Liu on 3/24/17.\n//  Copyright © 2017 Snapchat, Inc. All r"
  },
  {
    "path": "ManagedCapturer/SCCapturerTokenProvider.h",
    "chars": 377,
    "preview": "//\n// Created by Aaron Levine on 10/16/17.\n//\n\n#import <SCBase/SCMacros.h>\n\n#import <Foundation/Foundation.h>\n\n@class SC"
  },
  {
    "path": "ManagedCapturer/SCCapturerTokenProvider.m",
    "chars": 750,
    "preview": "//\n// Created by Aaron Levine on 10/16/17.\n//\n\n#import \"SCCapturerTokenProvider.h\"\n\n#import \"SCCapturerToken.h\"\n\n#import"
  },
  {
    "path": "ManagedCapturer/SCExposureState.h",
    "chars": 390,
    "preview": "//\n//  SCExposureState.h\n//  Snapchat\n//\n//  Created by Derek Peirce on 4/10/17.\n//  Copyright © 2017 Snapchat, Inc. All"
  },
  {
    "path": "ManagedCapturer/SCExposureState.m",
    "chars": 1420,
    "preview": "//\n//  SCExposureState.m\n//  Snapchat\n//\n//  Created by Derek Peirce on 4/10/17.\n//  Copyright © 2017 Snapchat, Inc. All"
  },
  {
    "path": "ManagedCapturer/SCFileAudioCaptureSession.h",
    "chars": 439,
    "preview": "//\n//  SCFileAudioCaptureSession.h\n//  Snapchat\n//\n//  Created by Xiaomu Wu on 2/2/17.\n//  Copyright © 2017 Snapchat, In"
  },
  {
    "path": "ManagedCapturer/SCFileAudioCaptureSession.m",
    "chars": 7427,
    "preview": "//\n//  SCFileAudioCaptureSession.m\n//  Snapchat\n//\n//  Created by Xiaomu Wu on 2/2/17.\n//  Copyright © 2017 Snapchat, In"
  },
  {
    "path": "ManagedCapturer/SCManagedAudioStreamer.h",
    "chars": 435,
    "preview": "//\n//  SCManagedAudioStreamer.h\n//  Snapchat\n//\n//  Created by Ricardo Sánchez-Sáez on 7/28/16.\n//  Copyright © 2016 Sna"
  },
  {
    "path": "ManagedCapturer/SCManagedAudioStreamer.m",
    "chars": 3610,
    "preview": "//\n//  SCManagedAudioStreamer.m\n//  Snapchat\n//\n//  Created by Ricardo Sánchez-Sáez on 7/28/16.\n//  Copyright © 2016 Sna"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDevice+SCManagedCapturer.h",
    "chars": 1979,
    "preview": "//\n//  SCManagedCaptureDevice+SCManagedCapturer.h\n//  Snapchat\n//\n//  Created by Liu Liu on 5/9/15.\n//  Copyright (c) 20"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDevice+SCManagedDeviceCapacityAnalyzer.h",
    "chars": 398,
    "preview": "//\n//  SCManagedCaptureDevice+SCManagedDeviceCapacityAnalyzer.h\n//  Snapchat\n//\n//  Created by Kam Sheffield on 10/29/15"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDevice.h",
    "chars": 1515,
    "preview": "//\n//  SCManagedCaptureDevice.h\n//  Snapchat\n//\n//  Created by Liu Liu on 4/22/15.\n//  Copyright (c) 2015 Liu Liu. All r"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDevice.m",
    "chars": 28257,
    "preview": "//\n//  SCManagedCaptureDevice.m\n//  Snapchat\n//\n//  Created by Liu Liu on 4/22/15.\n//  Copyright (c) 2015 Liu Liu. All r"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceAutoExposureHandler.h",
    "chars": 468,
    "preview": "//\n//  SCManagedCaptureDeviceAutoExposureHandler.h\n//  Snapchat\n//\n//  Created by Derek Peirce on 3/21/17.\n//  Copyright"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceAutoExposureHandler.m",
    "chars": 1719,
    "preview": "//\n//  SCManagedCaptureDeviceAutoExposureHandler.m\n//  Snapchat\n//\n//  Created by Derek Peirce on 3/21/17.\n//  Copyright"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceAutoFocusHandler.h",
    "chars": 560,
    "preview": "//\n//  SCManagedCaptureDeviceAutoFocusHandler.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/7/18.\n//  Copyright © 201"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceAutoFocusHandler.m",
    "chars": 4765,
    "preview": "//\n//  SCManagedCaptureDeviceAutoFocusHandler.m\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/7/18.\n//  Copyright © 201"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceDefaultZoomHandler.h",
    "chars": 684,
    "preview": "//\n//  SCManagedCaptureDeviceDefaultZoomHandler.h\n//  Snapchat\n//\n//  Created by Yu-Kuan Lai on 4/12/17.\n//  Copyright ©"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceDefaultZoomHandler.m",
    "chars": 3554,
    "preview": "//\n//  SCManagedCaptureDeviceDefaultZoomHandler.m\n//  Snapchat\n//\n//  Created by Yu-Kuan Lai on 4/12/17.\n//  Copyright ©"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceDefaultZoomHandler_Private.h",
    "chars": 466,
    "preview": "//\n//  SCManagedCaptureDeviceDefaultZoomHandler_Private.h\n//  Snapchat\n//\n//  Created by Joe Qiao on 04/01/2018.\n//\n\n#im"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceExposureHandler.h",
    "chars": 512,
    "preview": "//\n//  SCManagedCaptureDeviceExposureHandler.h\n//  Snapchat\n//\n//  Created by Derek Peirce on 3/21/17.\n//  Copyright © 2"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceFaceDetectionAutoExposureHandler.h",
    "chars": 864,
    "preview": "//\n//  SCManagedCaptureDeviceFaceDetectionAutoExposureHandler.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/6/18.\n// "
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceFaceDetectionAutoExposureHandler.m",
    "chars": 4274,
    "preview": "//\n//  SCManagedCaptureDeviceFaceDetectionAutoExposureHandler.m\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/6/18.\n// "
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.h",
    "chars": 830,
    "preview": "//\n//  SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/7/18.\n//  Co"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.m",
    "chars": 6503,
    "preview": "//\n//  SCManagedCaptureDeviceFaceDetectionAutoFocusHandler.m\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/7/18.\n//  Co"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceFocusHandler.h",
    "chars": 605,
    "preview": "//\n//  SCManagedCaptureDeviceFocusHandler.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/7/18.\n//  Copyright © 2018 Sn"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceHandler.h",
    "chars": 479,
    "preview": "//\n//  SCManagedCaptureDeviceHandler.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/8/18.\n//  Copyright © 2018 Snapcha"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceHandler.m",
    "chars": 2723,
    "preview": "//\n//  SCManagedCaptureDeviceHandler.m\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/8/18.\n//  Copyright © 2018 Snapcha"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceLinearInterpolationZoomHandler.h",
    "chars": 288,
    "preview": "//\n//  SCManagedCaptureDeviceLinearInterpolationZoomHandler.h\n//  Snapchat\n//\n//  Created by Joe Qiao on 03/01/2018.\n//\n"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceLinearInterpolationZoomHandler.m",
    "chars": 5499,
    "preview": "//\n//  SCManagedCaptureDeviceLinearInterpolationZoomHandler.m\n//  Snapchat\n//\n//  Created by Joe Qiao on 03/01/2018.\n//\n"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceLockOnRecordExposureHandler.h",
    "chars": 634,
    "preview": "//\n//  SCManagedCaptureDeviceLockOnRecordExposureHandler.h\n//  Snapchat\n//\n//  Created by Derek Peirce on 3/24/17.\n//  C"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceLockOnRecordExposureHandler.m",
    "chars": 2952,
    "preview": "//\n//  SCManagedCaptureDeviceLockOnRecordExposureHandler.m\n//  Snapchat\n//\n//  Created by Derek Peirce on 3/24/17.\n//  C"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceSavitzkyGolayZoomHandler.h",
    "chars": 333,
    "preview": "//\n//  SCManagedCaptureDeviceSavitzkyGolayZoomHandler.h\n//  Snapchat\n//\n//  Created by Yu-Kuan Lai on 4/12/17.\n//  Copyr"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceSavitzkyGolayZoomHandler.m",
    "chars": 3421,
    "preview": "//\n//  SCManagedCaptureDeviceSavitzkyGolayZoomHandler.m\n//  Snapchat\n//\n//  Created by Yu-Kuan Lai on 4/12/17.\n//  Copyr"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceSubjectAreaHandler.h",
    "chars": 654,
    "preview": "//\n//  SCManagedCaptureDeviceSubjectAreaHandler.h\n//  Snapchat\n//\n//  Created by Xiaokang Liu on 19/03/2018.\n//\n// This "
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceSubjectAreaHandler.m",
    "chars": 2298,
    "preview": "//\n//  SCManagedCaptureDeviceSubjectAreaHandler.m\n//  Snapchat\n//\n//  Created by Xiaokang Liu on 19/03/2018.\n//\n\n#import"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceThresholdExposureHandler.h",
    "chars": 543,
    "preview": "//\n//  SCManagedCaptureDeviceThresholdExposureHandler.h\n//  Snapchat\n//\n//  Created by Derek Peirce on 4/11/17.\n//  Copy"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureDeviceThresholdExposureHandler.m",
    "chars": 5805,
    "preview": "//\n//  SCManagedCaptureDeviceThresholdExposureHandler.m\n//  Snapchat\n//\n//  Created by Derek Peirce on 4/11/17.\n//  Copy"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureFaceDetectionAdjustingPOIResource.h",
    "chars": 2486,
    "preview": "//\n//  SCManagedCaptureFaceDetectionAdjustingPOIResource.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/7/18.\n//  Copy"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureFaceDetectionAdjustingPOIResource.m",
    "chars": 9500,
    "preview": "//\n//  SCManagedCaptureFaceDetectionAdjustingPOIResource.m\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 3/7/18.\n//  Copy"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturePreviewLayerController.h",
    "chars": 2749,
    "preview": "//\n//  SCManagedCapturePreviewLayerController.h\n//  Snapchat\n//\n//  Created by Liu Liu on 5/5/15.\n//  Copyright (c) 2015"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturePreviewLayerController.m",
    "chars": 23209,
    "preview": "//\n//  SCManagedCapturePreviewLayerController.m\n//  Snapchat\n//\n//  Created by Liu Liu on 5/5/15.\n//  Copyright (c) 2015"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturePreviewView.h",
    "chars": 722,
    "preview": "//\n//  SCManagedCapturePreviewView.h\n//  Snapchat\n//\n//  Created by Liu Liu on 5/5/15.\n//  Copyright (c) 2015 Snapchat, "
  },
  {
    "path": "ManagedCapturer/SCManagedCapturePreviewView.m",
    "chars": 5361,
    "preview": "//\n//  SCManagedCapturePreviewView.m\n//  Snapchat\n//\n//  Created by Liu Liu on 5/5/15.\n//  Copyright (c) 2015 Snapchat, "
  },
  {
    "path": "ManagedCapturer/SCManagedCapturePreviewViewDebugView.h",
    "chars": 287,
    "preview": "//\n//  SCManagedCapturePreviewViewDebugView.h\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 1/19/18.\n//  Copyright © 2018"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturePreviewViewDebugView.m",
    "chars": 8033,
    "preview": "//\n//  SCManagedCapturePreviewViewDebugView.m\n//  Snapchat\n//\n//  Created by Jiyang Zhu on 1/19/18.\n//  Copyright © 2018"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureSession.h",
    "chars": 2072,
    "preview": "//\n//  SCManagedCaptureSession.h\n//  Snapchat\n//\n//  Created by Derek Wang on 02/03/2018.\n//\n\n#import <SCBase/SCMacros.h"
  },
  {
    "path": "ManagedCapturer/SCManagedCaptureSession.m",
    "chars": 1519,
    "preview": "//\n//  SCManagedCaptureSession.m\n//  Snapchat\n//\n//  Created by Derek Wang on 02/03/2018.\n//\n\n#import \"SCManagedCaptureS"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturer.h",
    "chars": 642,
    "preview": "//  SCManagedCapturer.h\n//  Snapchat\n//\n//  Created by Liu Liu on 4/20/15.\n\n#import \"SCCapturer.h\"\n#import \"SCManagedCap"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturer.m",
    "chars": 483,
    "preview": "//\n//  SCManagedCapturer.m\n//  Snapchat\n//\n//  Created by Lin Jia on 9/28/17.\n//\n\n#import \"SCManagedCapturer.h\"\n\n#import"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerARSessionHandler.h",
    "chars": 750,
    "preview": "//\n//  SCManagedCapturerARSessionHandler.h\n//  Snapchat\n//\n//  Created by Xiaokang Liu on 16/03/2018.\n//\n// This class i"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerARSessionHandler.m",
    "chars": 2794,
    "preview": "//\n//  SCManagedCapturerARSessionHandler.m\n//  Snapchat\n//\n//  Created by Xiaokang Liu on 16/03/2018.\n//\n\n#import \"SCMan"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerListener.h",
    "chars": 5975,
    "preview": "//#!announcer.rb\n//\n//  SCManagedCaptuerListener\n//  Snapchat\n//\n//  Created by Liu Liu on 4/23/15.\n//  Copyright (c) 20"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerListenerAnnouncer.h",
    "chars": 340,
    "preview": "// Generated by the announcer.rb  DO NOT EDIT!!\n\n#import \"SCManagedCapturerListener.h\"\n\n#import <Foundation/Foundation.h"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerListenerAnnouncer.mm",
    "chars": 19645,
    "preview": "// Generated by the announcer.rb  DO NOT EDIT!!\n\n#import \"SCManagedCapturerListenerAnnouncer.h\"\n\n#include <mutex>\nusing "
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerSampleMetadata.h",
    "chars": 563,
    "preview": "//\n//  SCRecordingMetadata.h\n//  Snapchat\n//\n\n#import <SCBase/SCMacros.h>\n\n#import <CoreMedia/CoreMedia.h>\n#import <Foun"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerSampleMetadata.m",
    "chars": 461,
    "preview": "//\n//  SCRecordingMetadata.m\n//  Snapchat\n//\n\n#import \"SCManagedCapturerSampleMetadata.h\"\n\nNS_ASSUME_NONNULL_BEGIN\n\n@imp"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerState.h",
    "chars": 3180,
    "preview": "// 49126048c3d19dd5b676b8d39844cf133833b67a\n// Generated by the value-object.rb  DO NOT EDIT!!\n\n#import \"SCManagedCaptur"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerState.m",
    "chars": 12894,
    "preview": "// 49126048c3d19dd5b676b8d39844cf133833b67a\n// Generated by the value-object.rb  DO NOT EDIT!!\n\n#import \"SCManagedCaptur"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerState.value",
    "chars": 509,
    "preview": "#import <CoreGraphics/CoreGraphics.h>\n#import \"SCManagedCaptureDevice.h\"\n\ninterface SCManagedCapturerState\n    BOOL isRu"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerStateBuilder.h",
    "chars": 1283,
    "preview": "// 49126048c3d19dd5b676b8d39844cf133833b67a\n// Generated by the value-object.rb  DO NOT EDIT!!\n\n#import \"SCManagedCaptur"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerStateBuilder.m",
    "chars": 4927,
    "preview": "// 49126048c3d19dd5b676b8d39844cf133833b67a\n// Generated by the value-object.rb  DO NOT EDIT!!\n\n#import \"SCManagedCaptur"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerUtils.h",
    "chars": 1266,
    "preview": "//\n//  SCManagedCapturerUtils.h\n//  Snapchat\n//\n//  Created by Chao Pang on 10/4/17.\n//\n\n#import <SCBase/SCMacros.h>\n\n#i"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerUtils.m",
    "chars": 6565,
    "preview": "//\n//  SCManagedCapturerUtils.m\n//  Snapchat\n//\n//  Created by Chao Pang on 10/4/17.\n//\n\n#import \"SCManagedCapturerUtils"
  },
  {
    "path": "ManagedCapturer/SCManagedCapturerV1.h",
    "chars": 2580,
    "preview": "//\n//  SCManagedCapturer.h\n//  Snapchat\n//\n//  Created by Liu Liu on 4/20/15.\n//  Copyright (c) 2015 Liu Liu. All rights"
  }
]

// ... and 111 more files (download for full content)

About this extraction

This page contains the full source code of the JonnyBanana/Snapchat-Source-Code-Leak GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 311 files (1.1 MB), approximately 254.3k tokens, and a symbol index with 65 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!