Add files via upload
This commit is contained in:
parent
5ce2bf3b35
commit
47c0ba2719
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// SCBlackCameraDetector.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 24/01/2018.
|
||||
//
|
||||
|
||||
#import "SCBlackCameraReporter.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class SCBlackCameraNoOutputDetector;
|
||||
|
||||
@interface SCBlackCameraDetector : NSObject
|
||||
|
||||
@property (nonatomic, strong) SCBlackCameraNoOutputDetector *blackCameraNoOutputDetector;
|
||||
|
||||
SC_INIT_AND_NEW_UNAVAILABLE
|
||||
- (instancetype)initWithTicketCreator:(id<SCManiphestTicketCreator>)ticketCreator;
|
||||
|
||||
// CameraView visible/invisible
|
||||
- (void)onCameraViewVisible:(BOOL)visible;
|
||||
|
||||
- (void)onCameraViewVisibleWithTouch:(UIGestureRecognizer *)touch;
|
||||
|
||||
// Call this when [AVCaptureSession startRunning] is called
|
||||
- (void)sessionWillCallStartRunning;
|
||||
- (void)sessionDidCallStartRunning;
|
||||
|
||||
// Call this when [AVCaptureSession stopRunning] is called
|
||||
- (void)sessionWillCallStopRunning;
|
||||
- (void)sessionDidCallStopRunning;
|
||||
|
||||
// Call this when [AVCaptureSession commitConfiguration] is called
|
||||
- (void)sessionWillCommitConfiguration;
|
||||
- (void)sessionDidCommitConfiguration;
|
||||
|
||||
- (void)sessionDidChangeIsRunning:(BOOL)running;
|
||||
|
||||
// For CapturePreview visibility detector
|
||||
- (void)capturePreviewDidBecomeVisible:(BOOL)visible;
|
||||
|
||||
/**
|
||||
Mark the start of creating new session
|
||||
When we fix black camera by creating new session, some detector may report black camera because we called
|
||||
[AVCaptureSession stopRunning] on old AVCaptureSession, so we need to tell the detector the session is recreating, so
|
||||
it is fine to call [AVCaptureSession stopRunning] on old AVCaptureSession.
|
||||
*/
|
||||
- (void)sessionWillRecreate;
|
||||
/**
|
||||
Mark the end of creating new session
|
||||
*/
|
||||
- (void)sessionDidRecreate;
|
||||
|
||||
@end
|
|
@ -0,0 +1,134 @@
|
|||
//
|
||||
// SCBlackCameraDetector.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 24/01/2018.
|
||||
//
|
||||
|
||||
#import "SCBlackCameraDetector.h"
|
||||
|
||||
#import "SCBlackCameraNoOutputDetector.h"
|
||||
#import "SCBlackCameraPreviewDetector.h"
|
||||
#import "SCBlackCameraRunningDetector.h"
|
||||
#import "SCBlackCameraSessionBlockDetector.h"
|
||||
#import "SCBlackCameraViewDetector.h"
|
||||
|
||||
#import <SCFoundation/SCQueuePerformer.h>
|
||||
|
||||
#if !TARGET_IPHONE_SIMULATOR
|
||||
static char *const kSCBlackCameraDetectorQueueLabel = "com.snapchat.black-camera-detector";
|
||||
#endif
|
||||
@interface SCBlackCameraDetector () {
|
||||
BOOL _sessionIsRunning;
|
||||
BOOL _cameraIsVisible;
|
||||
BOOL _previewIsVisible;
|
||||
}
|
||||
@property (nonatomic, strong) SCQueuePerformer *queuePerformer;
|
||||
@property (nonatomic, strong) SCBlackCameraViewDetector *cameraViewDetector;
|
||||
@property (nonatomic, strong) SCBlackCameraRunningDetector *sessionRunningDetector;
|
||||
@property (nonatomic, strong) SCBlackCameraPreviewDetector *previewDetector;
|
||||
@property (nonatomic, strong) SCBlackCameraSessionBlockDetector *sessionBlockDetector;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SCBlackCameraDetector
|
||||
|
||||
- (instancetype)initWithTicketCreator:(id<SCManiphestTicketCreator>)ticketCreator
|
||||
{
|
||||
#if !TARGET_IPHONE_SIMULATOR
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_queuePerformer = [[SCQueuePerformer alloc] initWithLabel:kSCBlackCameraDetectorQueueLabel
|
||||
qualityOfService:QOS_CLASS_BACKGROUND
|
||||
queueType:DISPATCH_QUEUE_SERIAL
|
||||
context:SCQueuePerformerContextCamera];
|
||||
|
||||
SCBlackCameraReporter *reporter = [[SCBlackCameraReporter alloc] initWithTicketCreator:ticketCreator];
|
||||
_cameraViewDetector = [[SCBlackCameraViewDetector alloc] initWithPerformer:_queuePerformer reporter:reporter];
|
||||
_sessionRunningDetector =
|
||||
[[SCBlackCameraRunningDetector alloc] initWithPerformer:_queuePerformer reporter:reporter];
|
||||
_previewDetector = [[SCBlackCameraPreviewDetector alloc] initWithPerformer:_queuePerformer reporter:reporter];
|
||||
_sessionBlockDetector = [[SCBlackCameraSessionBlockDetector alloc] initWithReporter:reporter];
|
||||
_blackCameraNoOutputDetector = [[SCBlackCameraNoOutputDetector alloc] initWithReporter:reporter];
|
||||
}
|
||||
return self;
|
||||
#else
|
||||
return nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - Camera view visibility detector
|
||||
- (void)onCameraViewVisible:(BOOL)visible
|
||||
{
|
||||
SC_GUARD_ELSE_RETURN(visible != _cameraIsVisible);
|
||||
_cameraIsVisible = visible;
|
||||
[_cameraViewDetector onCameraViewVisible:visible];
|
||||
}
|
||||
|
||||
- (void)onCameraViewVisibleWithTouch:(UIGestureRecognizer *)gesture
|
||||
{
|
||||
[_cameraViewDetector onCameraViewVisibleWithTouch:gesture];
|
||||
}
|
||||
|
||||
#pragma mark - Track [AVCaptureSession startRunning] call
|
||||
- (void)sessionWillCallStartRunning
|
||||
{
|
||||
[_cameraViewDetector sessionWillCallStartRunning];
|
||||
[_sessionBlockDetector sessionWillCallStartRunning];
|
||||
}
|
||||
|
||||
- (void)sessionDidCallStartRunning
|
||||
{
|
||||
[_sessionRunningDetector sessionDidCallStartRunning];
|
||||
[_sessionBlockDetector sessionDidCallStartRunning];
|
||||
}
|
||||
|
||||
#pragma mark - Track [AVCaptureSession stopRunning] call
|
||||
- (void)sessionWillCallStopRunning
|
||||
{
|
||||
[_cameraViewDetector sessionWillCallStopRunning];
|
||||
[_sessionRunningDetector sessionWillCallStopRunning];
|
||||
}
|
||||
|
||||
- (void)sessionDidCallStopRunning
|
||||
{
|
||||
}
|
||||
|
||||
- (void)sessionDidChangeIsRunning:(BOOL)running
|
||||
{
|
||||
SC_GUARD_ELSE_RETURN(running != _sessionIsRunning);
|
||||
_sessionIsRunning = running;
|
||||
[_sessionRunningDetector sessionDidChangeIsRunning:running];
|
||||
[_previewDetector sessionDidChangeIsRunning:running];
|
||||
}
|
||||
|
||||
#pragma mark - Capture preview visibility detector
|
||||
- (void)capturePreviewDidBecomeVisible:(BOOL)visible
|
||||
{
|
||||
SC_GUARD_ELSE_RETURN(visible != _previewIsVisible);
|
||||
_previewIsVisible = visible;
|
||||
[_previewDetector capturePreviewDidBecomeVisible:visible];
|
||||
}
|
||||
|
||||
#pragma mark - AVCaptureSession block detector
|
||||
- (void)sessionWillCommitConfiguration
|
||||
{
|
||||
[_sessionBlockDetector sessionWillCommitConfiguration];
|
||||
}
|
||||
|
||||
- (void)sessionDidCommitConfiguration
|
||||
{
|
||||
[_sessionBlockDetector sessionDidCommitConfiguration];
|
||||
}
|
||||
|
||||
- (void)sessionWillRecreate
|
||||
{
|
||||
[_cameraViewDetector sessionWillRecreate];
|
||||
}
|
||||
|
||||
- (void)sessionDidRecreate
|
||||
{
|
||||
[_cameraViewDetector sessionDidRecreate];
|
||||
}
|
||||
@end
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// SCBlackCameraNoOutputDetector.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 05/12/2017.
|
||||
//
|
||||
|
||||
#import "SCManagedCapturerListener.h"
|
||||
|
||||
#import <SCCameraFoundation/SCManagedVideoDataSourceListener.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class SCBlackCameraNoOutputDetector, SCBlackCameraReporter;
|
||||
@protocol SCManiphestTicketCreator;
|
||||
|
||||
@protocol SCBlackCameraDetectorDelegate
|
||||
- (void)detector:(SCBlackCameraNoOutputDetector *)detector didDetectBlackCamera:(id<SCCapturer>)capture;
|
||||
@end
|
||||
|
||||
@interface SCBlackCameraNoOutputDetector : NSObject <SCManagedVideoDataSourceListener, SCManagedCapturerListener>
|
||||
|
||||
@property (nonatomic, weak) id<SCBlackCameraDetectorDelegate> delegate;
|
||||
- (instancetype)initWithReporter:(SCBlackCameraReporter *)reporter;
|
||||
|
||||
@end
|
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// SCBlackCameraDetectorNoOutput.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 05/12/2017.
|
||||
//
|
||||
// This detector is used to detect the case that session is running, but there is no sample buffer output
|
||||
|
||||
#import "SCBlackCameraNoOutputDetector.h"
|
||||
|
||||
#import "SCBlackCameraReporter.h"
|
||||
|
||||
#import <SCFoundation/SCAssertWrapper.h>
|
||||
#import <SCFoundation/SCLog.h>
|
||||
#import <SCFoundation/SCQueuePerformer.h>
|
||||
#import <SCFoundation/SCTraceODPCompatible.h>
|
||||
#import <SCFoundation/SCZeroDependencyExperiments.h>
|
||||
#import <SCLogger/SCCameraMetrics.h>
|
||||
#import <SCLogger/SCLogger.h>
|
||||
|
||||
static CGFloat const kShortCheckingDelay = 0.5f;
|
||||
static CGFloat const kLongCheckingDelay = 3.0f;
|
||||
static char *const kSCBlackCameraDetectorQueueLabel = "com.snapchat.black-camera-detector";
|
||||
|
||||
@interface SCBlackCameraNoOutputDetector () {
|
||||
BOOL _sampleBufferReceived;
|
||||
BOOL _blackCameraDetected;
|
||||
// Whether we receive first frame after we detected black camera, that's maybe because the checking delay is too
|
||||
// short, and we will switch to kLongCheckingDelay next time we do the checking
|
||||
BOOL _blackCameraRecovered;
|
||||
// Whether checking is scheduled, to avoid duplicated checking
|
||||
BOOL _checkingScheduled;
|
||||
// Whether AVCaptureSession is stopped, if stopped, we don't need to check black camera any more
|
||||
// It is set on main thread, read on background queue
|
||||
BOOL _sessionStoppedRunning;
|
||||
}
|
||||
@property (nonatomic) SCQueuePerformer *queuePerformer;
|
||||
@property (nonatomic) SCBlackCameraReporter *reporter;
|
||||
@end
|
||||
|
||||
@implementation SCBlackCameraNoOutputDetector
|
||||
|
||||
- (instancetype)initWithReporter:(SCBlackCameraReporter *)reporter
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_queuePerformer = [[SCQueuePerformer alloc] initWithLabel:kSCBlackCameraDetectorQueueLabel
|
||||
qualityOfService:QOS_CLASS_BACKGROUND
|
||||
queueType:DISPATCH_QUEUE_SERIAL
|
||||
context:SCQueuePerformerContextCamera];
|
||||
_reporter = reporter;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)managedVideoDataSource:(id<SCManagedVideoDataSource>)managedVideoDataSource
|
||||
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
||||
devicePosition:(SCManagedCaptureDevicePosition)devicePosition
|
||||
{
|
||||
// The block is very light-weight
|
||||
[self.queuePerformer perform:^{
|
||||
if (_blackCameraDetected) {
|
||||
// Detected a black camera case
|
||||
_blackCameraDetected = NO;
|
||||
_blackCameraRecovered = YES;
|
||||
SCLogCoreCameraInfo(@"[BlackCamera] Black camera recovered");
|
||||
if (SCExperimentWithBlackCameraReporting()) {
|
||||
[[SCLogger sharedInstance] logUnsampledEvent:KSCCameraBlackCamera
|
||||
parameters:@{
|
||||
@"type" : @"RECOVERED"
|
||||
}
|
||||
secretParameters:nil
|
||||
metrics:nil];
|
||||
}
|
||||
}
|
||||
|
||||
// Received buffer!
|
||||
_sampleBufferReceived = YES;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didStartRunning:(SCManagedCapturerState *)state
|
||||
{
|
||||
SCAssertMainThread();
|
||||
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
|
||||
SCLogCoreCameraInfo(@"[BlackCamera] In background, skip checking");
|
||||
return;
|
||||
}
|
||||
_sessionStoppedRunning = NO;
|
||||
[self.queuePerformer perform:^{
|
||||
SCTraceODPCompatibleStart(2);
|
||||
if (_checkingScheduled) {
|
||||
SCLogCoreCameraInfo(@"[BlackCamera] Checking is scheduled, skip");
|
||||
return;
|
||||
}
|
||||
if (_sessionStoppedRunning) {
|
||||
SCLogCoreCameraInfo(@"[BlackCamera] AVCaptureSession stopped, should not check");
|
||||
return;
|
||||
}
|
||||
_sampleBufferReceived = NO;
|
||||
if (_blackCameraRecovered) {
|
||||
SCLogCoreCameraInfo(@"[BlackCamera] Last black camera recovered, let's wait longer to check this time");
|
||||
}
|
||||
SCLogCoreCameraInfo(@"[BlackCamera] Schedule black camera checking");
|
||||
[self.queuePerformer perform:^{
|
||||
SCTraceODPCompatibleStart(2);
|
||||
if (!_sessionStoppedRunning) {
|
||||
if (!_sampleBufferReceived) {
|
||||
_blackCameraDetected = YES;
|
||||
[_reporter reportBlackCameraWithCause:SCBlackCameraNoOutputData];
|
||||
[self.delegate detector:self didDetectBlackCamera:managedCapturer];
|
||||
} else {
|
||||
SCLogCoreCameraInfo(@"[BlackCamera] No black camera");
|
||||
_blackCameraDetected = NO;
|
||||
}
|
||||
} else {
|
||||
SCLogCoreCameraInfo(@"[BlackCamera] AVCaptureSession stopped");
|
||||
_blackCameraDetected = NO;
|
||||
}
|
||||
_blackCameraRecovered = NO;
|
||||
_checkingScheduled = NO;
|
||||
}
|
||||
after:_blackCameraRecovered ? kLongCheckingDelay : kShortCheckingDelay];
|
||||
_checkingScheduled = YES;
|
||||
}];
|
||||
}
|
||||
- (void)managedCapturer:(id<SCCapturer>)managedCapturer didStopRunning:(SCManagedCapturerState *)state
|
||||
{
|
||||
SCAssertMainThread();
|
||||
_sessionStoppedRunning = YES;
|
||||
[self.queuePerformer perform:^{
|
||||
SCTraceODPCompatibleStart(2);
|
||||
_sampleBufferReceived = NO;
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// SCBlackCameraPreviewDetector.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 25/01/2018.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class SCQueuePerformer, SCBlackCameraReporter;
|
||||
@protocol SCManiphestTicketCreator;
|
||||
|
||||
@interface SCBlackCameraPreviewDetector : NSObject
|
||||
|
||||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter;
|
||||
|
||||
- (void)sessionDidChangeIsRunning:(BOOL)running;
|
||||
- (void)capturePreviewDidBecomeVisible:(BOOL)visible;
|
||||
|
||||
@end
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// SCBlackCameraPreviewDetector.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 25/01/2018.
|
||||
//
|
||||
|
||||
#import "SCBlackCameraPreviewDetector.h"
|
||||
|
||||
#import "SCBlackCameraReporter.h"
|
||||
#import "SCMetalUtils.h"
|
||||
|
||||
#import <SCCrashLogger/SCCrashLogger.h>
|
||||
#import <SCFoundation/SCQueuePerformer.h>
|
||||
#import <SCFoundation/SCThreadHelpers.h>
|
||||
#import <SCFoundation/SCZeroDependencyExperiments.h>
|
||||
|
||||
// Check whether preview is visible when AVCaptureSession is running
|
||||
static CGFloat const kSCBlackCameraCheckingDelay = 0.5;
|
||||
|
||||
@interface SCBlackCameraPreviewDetector () {
|
||||
BOOL _previewVisible;
|
||||
dispatch_block_t _checkingBlock;
|
||||
}
|
||||
@property (nonatomic) SCQueuePerformer *queuePerformer;
|
||||
@property (nonatomic) SCBlackCameraReporter *reporter;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SCBlackCameraPreviewDetector
|
||||
|
||||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_queuePerformer = performer;
|
||||
_reporter = reporter;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)capturePreviewDidBecomeVisible:(BOOL)visible
|
||||
{
|
||||
[_queuePerformer perform:^{
|
||||
_previewVisible = visible;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sessionDidChangeIsRunning:(BOOL)running
|
||||
{
|
||||
if (running) {
|
||||
[self _scheduleCheck];
|
||||
} else {
|
||||
[_queuePerformer perform:^{
|
||||
if (_checkingBlock) {
|
||||
dispatch_block_cancel(_checkingBlock);
|
||||
_checkingBlock = nil;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_scheduleCheck
|
||||
{
|
||||
[_queuePerformer perform:^{
|
||||
@weakify(self);
|
||||
_checkingBlock = dispatch_block_create(0, ^{
|
||||
@strongify(self);
|
||||
SC_GUARD_ELSE_RETURN(self);
|
||||
self->_checkingBlock = nil;
|
||||
[self _checkPreviewState];
|
||||
});
|
||||
[_queuePerformer perform:_checkingBlock after:kSCBlackCameraCheckingDelay];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_checkPreviewState
|
||||
{
|
||||
if (!_previewVisible) {
|
||||
runOnMainThreadAsynchronously(^{
|
||||
// Make sure the app is in foreground
|
||||
SC_GUARD_ELSE_RETURN([UIApplication sharedApplication].applicationState == UIApplicationStateActive);
|
||||
|
||||
SCBlackCameraCause cause =
|
||||
SCDeviceSupportsMetal() ? SCBlackCameraRenderingPaused : SCBlackCameraPreviewIsHidden;
|
||||
[_reporter reportBlackCameraWithCause:cause];
|
||||
[_reporter fileShakeTicketWithCause:cause];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// SCBlackCameraReporter.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 09/01/2018.
|
||||
//
|
||||
|
||||
#import <SCBase/SCMacros.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, SCBlackCameraCause) {
|
||||
SCBlackCameraStartRunningNotCalled, // 1. View is visible, but session startRunning is not called
|
||||
SCBlackCameraSessionNotRunning, // 2. Session startRunning is called, but isRunning is still false
|
||||
SCBlackCameraRenderingPaused, // 3.1 View is visible, but capture preview rendering is paused
|
||||
SCBlackCameraPreviewIsHidden, // 3.2 For non-metal devices, capture preview is hidden
|
||||
SCBlackCameraSessionStartRunningBlocked, // 4.1 AVCaptureSession is blocked at startRunning
|
||||
SCBlackCameraSessionConfigurationBlocked, // 4.2 AVCaptureSession is blocked at commitConfiguration
|
||||
|
||||
SCBlackCameraNoOutputData, // 5. Session is running, but no data output
|
||||
};
|
||||
|
||||
@protocol SCManiphestTicketCreator;
|
||||
|
||||
@interface SCBlackCameraReporter : NSObject
|
||||
|
||||
SC_INIT_AND_NEW_UNAVAILABLE
|
||||
- (instancetype)initWithTicketCreator:(id<SCManiphestTicketCreator>)ticketCreator;
|
||||
|
||||
- (NSString *)causeNameFor:(SCBlackCameraCause)cause;
|
||||
|
||||
- (void)reportBlackCameraWithCause:(SCBlackCameraCause)cause;
|
||||
- (void)fileShakeTicketWithCause:(SCBlackCameraCause)cause;
|
||||
|
||||
@end
|
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// SCBlackCameraReporter.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 09/01/2018.
|
||||
//
|
||||
|
||||
#import "SCBlackCameraReporter.h"
|
||||
|
||||
#import "SCManiphestTicketCreator.h"
|
||||
|
||||
#import <SCFoundation/SCAssertWrapper.h>
|
||||
#import <SCFoundation/SCLog.h>
|
||||
#import <SCFoundation/SCLogHelper.h>
|
||||
#import <SCFoundation/SCZeroDependencyExperiments.h>
|
||||
#import <SCLogger/SCCameraMetrics.h>
|
||||
#import <SCLogger/SCLogger.h>
|
||||
|
||||
@interface SCBlackCameraReporter ()
|
||||
|
||||
@property (nonatomic) id<SCManiphestTicketCreator> ticketCreator;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SCBlackCameraReporter
|
||||
|
||||
- (instancetype)initWithTicketCreator:(id<SCManiphestTicketCreator>)ticketCreator
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_ticketCreator = ticketCreator;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)causeNameFor:(SCBlackCameraCause)cause
|
||||
{
|
||||
switch (cause) {
|
||||
case SCBlackCameraStartRunningNotCalled:
|
||||
return @"StartRunningNotCalled";
|
||||
case SCBlackCameraSessionNotRunning:
|
||||
return @"SessionNotRunning";
|
||||
case SCBlackCameraRenderingPaused:
|
||||
return @"RenderingPause";
|
||||
case SCBlackCameraPreviewIsHidden:
|
||||
return @"PreviewIsHidden";
|
||||
case SCBlackCameraSessionStartRunningBlocked:
|
||||
return @"SessionStartRunningBlocked";
|
||||
case SCBlackCameraSessionConfigurationBlocked:
|
||||
return @"SessionConfigurationBlocked";
|
||||
case SCBlackCameraNoOutputData:
|
||||
return @"NoOutputData";
|
||||
default:
|
||||
SCAssert(NO, @"illegate cause");
|
||||
break;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)reportBlackCameraWithCause:(SCBlackCameraCause)cause
|
||||
{
|
||||
NSString *causeStr = [self causeNameFor:cause];
|
||||
SCLogCoreCameraError(@"[BlackCamera] Detected black camera, cause: %@", causeStr);
|
||||
|
||||
NSDictionary *parameters = @{ @"type" : @"DETECTED", @"cause" : causeStr };
|
||||
|
||||
[_ticketCreator createAndFileBetaReport:JSONStringSerializeObjectForLogging(parameters)];
|
||||
|
||||
if (SCExperimentWithBlackCameraReporting()) {
|
||||
[[SCLogger sharedInstance] logUnsampledEvent:KSCCameraBlackCamera
|
||||
parameters:parameters
|
||||
secretParameters:nil
|
||||
metrics:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)fileShakeTicketWithCause:(SCBlackCameraCause)cause
|
||||
{
|
||||
if (SCExperimentWithBlackCameraExceptionLogging()) {
|
||||
// Log exception with auto S2R
|
||||
NSString *errMsg =
|
||||
[NSString sc_stringWithFormat:@"[BlackCamera] Detected black camera, cause: %@", [self causeNameFor:cause]];
|
||||
[_ticketCreator createAndFile:nil creationTime:0 description:errMsg email:nil project:@"Camera" subproject:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// SCBlackCameraRunningDetector.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 30/01/2018.
|
||||
//
|
||||
|
||||
#import <SCBase/SCMacros.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class SCQueuePerformer, SCBlackCameraReporter;
|
||||
@protocol SCManiphestTicketCreator;
|
||||
|
||||
@interface SCBlackCameraRunningDetector : NSObject
|
||||
|
||||
SC_INIT_AND_NEW_UNAVAILABLE
|
||||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter;
|
||||
|
||||
// When session isRunning changed
|
||||
- (void)sessionDidChangeIsRunning:(BOOL)running;
|
||||
// Call this after [AVCaptureSession startRunning] is called
|
||||
- (void)sessionDidCallStartRunning;
|
||||
// Call this before [AVCaptureSession stopRunning] is called
|
||||
- (void)sessionWillCallStopRunning;
|
||||
|
||||
@end
|
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// SCBlackCameraRunningDetector.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 30/01/2018.
|
||||
//
|
||||
|
||||
#import "SCBlackCameraRunningDetector.h"
|
||||
|
||||
#import "SCBlackCameraReporter.h"
|
||||
|
||||
#import <SCFoundation/SCAssertWrapper.h>
|
||||
#import <SCFoundation/SCQueuePerformer.h>
|
||||
#import <SCFoundation/SCTraceODPCompatible.h>
|
||||
#import <SCLogger/SCCameraMetrics.h>
|
||||
|
||||
// Check whether we called AVCaptureSession isRunning within this period
|
||||
static CGFloat const kSCBlackCameraCheckingDelay = 5;
|
||||
|
||||
@interface SCBlackCameraRunningDetector () {
|
||||
BOOL _isSessionRunning;
|
||||
dispatch_block_t _checkSessionBlock;
|
||||
}
|
||||
@property (nonatomic) SCQueuePerformer *queuePerformer;
|
||||
@property (nonatomic) SCBlackCameraReporter *reporter;
|
||||
@end
|
||||
|
||||
@implementation SCBlackCameraRunningDetector
|
||||
|
||||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_queuePerformer = performer;
|
||||
_reporter = reporter;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)sessionDidChangeIsRunning:(BOOL)running
|
||||
{
|
||||
[_queuePerformer perform:^{
|
||||
_isSessionRunning = running;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sessionDidCallStartRunning
|
||||
{
|
||||
[self _scheduleCheck];
|
||||
}
|
||||
|
||||
- (void)sessionWillCallStopRunning
|
||||
{
|
||||
[_queuePerformer perform:^{
|
||||
if (_checkSessionBlock) {
|
||||
dispatch_block_cancel(_checkSessionBlock);
|
||||
_checkSessionBlock = nil;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_scheduleCheck
|
||||
{
|
||||
[_queuePerformer perform:^{
|
||||
@weakify(self);
|
||||
_checkSessionBlock = dispatch_block_create(0, ^{
|
||||
@strongify(self);
|
||||
SC_GUARD_ELSE_RETURN(self);
|
||||
self->_checkSessionBlock = nil;
|
||||
[self _checkSessionState];
|
||||
});
|
||||
|
||||
[_queuePerformer perform:_checkSessionBlock after:kSCBlackCameraCheckingDelay];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_checkSessionState
|
||||
{
|
||||
if (!_isSessionRunning) {
|
||||
[_reporter reportBlackCameraWithCause:SCBlackCameraSessionNotRunning];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,23 @@
|
|||
//
|
||||
// SCBlackCameraSessionBlockDetector.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 25/01/2018.
|
||||
//
|
||||
|
||||
#import "SCBlackCameraReporter.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface SCBlackCameraSessionBlockDetector : NSObject
|
||||
|
||||
SC_INIT_AND_NEW_UNAVAILABLE
|
||||
- (instancetype)initWithReporter:(SCBlackCameraReporter *)reporter;
|
||||
|
||||
- (void)sessionWillCallStartRunning;
|
||||
- (void)sessionDidCallStartRunning;
|
||||
|
||||
- (void)sessionWillCommitConfiguration;
|
||||
- (void)sessionDidCommitConfiguration;
|
||||
|
||||
@end
|
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// SCBlackCameraSessionBlockDetector.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 25/01/2018.
|
||||
//
|
||||
|
||||
#import "SCBlackCameraSessionBlockDetector.h"
|
||||
|
||||
#import "SCBlackCameraReporter.h"
|
||||
|
||||
#import <SCLogger/SCCameraMetrics.h>
|
||||
#import <SCLogger/SCLogger.h>
|
||||
|
||||
@import CoreGraphics;
|
||||
|
||||
// Longer than 5 seconds is considerred as black camera
|
||||
static CGFloat const kSCBlackCameraBlockingThreshold = 5;
|
||||
// Will report if session blocks longer than 1 second
|
||||
static CGFloat const kSCSessionBlockingLogThreshold = 1;
|
||||
|
||||
@interface SCBlackCameraSessionBlockDetector () {
|
||||
NSTimeInterval _startTime;
|
||||
}
|
||||
@property (nonatomic) SCBlackCameraReporter *reporter;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SCBlackCameraSessionBlockDetector
|
||||
|
||||
- (instancetype)initWithReporter:(SCBlackCameraReporter *)reporter
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_reporter = reporter;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)sessionWillCallStartRunning
|
||||
{
|
||||
_startTime = [NSDate timeIntervalSinceReferenceDate];
|
||||
}
|
||||
|
||||
- (void)sessionDidCallStartRunning
|
||||
{
|
||||
[self _reportBlackCameraIfNeededWithCause:SCBlackCameraSessionStartRunningBlocked];
|
||||
[self _reportBlockingIfNeededWithCause:SCBlackCameraSessionStartRunningBlocked];
|
||||
}
|
||||
|
||||
- (void)sessionWillCommitConfiguration
|
||||
{
|
||||
_startTime = [NSDate timeIntervalSinceReferenceDate];
|
||||
}
|
||||
|
||||
- (void)sessionDidCommitConfiguration
|
||||
{
|
||||
[self _reportBlackCameraIfNeededWithCause:SCBlackCameraSessionConfigurationBlocked];
|
||||
[self _reportBlockingIfNeededWithCause:SCBlackCameraSessionConfigurationBlocked];
|
||||
}
|
||||
|
||||
- (void)_reportBlockingIfNeededWithCause:(SCBlackCameraCause)cause
|
||||
{
|
||||
NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - _startTime;
|
||||
if (duration >= kSCSessionBlockingLogThreshold) {
|
||||
NSString *causeStr = [_reporter causeNameFor:cause];
|
||||
[[SCLogger sharedInstance] logEvent:KSCCameraCaptureSessionBlocked
|
||||
parameters:@{
|
||||
@"cause" : causeStr,
|
||||
@"duration" : @(duration)
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_reportBlackCameraIfNeededWithCause:(SCBlackCameraCause)cause
|
||||
{
|
||||
NSTimeInterval endTime = [NSDate timeIntervalSinceReferenceDate];
|
||||
if (endTime - _startTime >= kSCBlackCameraBlockingThreshold) {
|
||||
[_reporter reportBlackCameraWithCause:cause];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// SCBlackCameraDetectorCameraView.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 24/01/2018.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class SCQueuePerformer, SCBlackCameraReporter;
|
||||
@protocol SCManiphestTicketCreator;
|
||||
|
||||
@interface SCBlackCameraViewDetector : NSObject
|
||||
|
||||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter;
|
||||
|
||||
// CameraView visible/invisible
|
||||
- (void)onCameraViewVisible:(BOOL)visible;
|
||||
|
||||
- (void)onCameraViewVisibleWithTouch:(UIGestureRecognizer *)gesture;
|
||||
|
||||
// Call this when [AVCaptureSession startRunning] is called
|
||||
- (void)sessionWillCallStartRunning;
|
||||
// Call this when [AVCaptureSession stopRunning] is called
|
||||
- (void)sessionWillCallStopRunning;
|
||||
|
||||
- (void)sessionWillRecreate;
|
||||
- (void)sessionDidRecreate;
|
||||
|
||||
@end
|
|
@ -0,0 +1,136 @@
|
|||
//
|
||||
// SCBlackCameraDetectorCameraView.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 24/01/2018.
|
||||
//
|
||||
|
||||
#import "SCBlackCameraViewDetector.h"
|
||||
|
||||
#import "SCBlackCameraReporter.h"
|
||||
#import "SCCaptureDeviceAuthorization.h"
|
||||
|
||||
#import <SCFoundation/SCAssertWrapper.h>
|
||||
#import <SCFoundation/SCLog.h>
|
||||
#import <SCFoundation/SCQueuePerformer.h>
|
||||
#import <SCFoundation/SCTraceODPCompatible.h>
|
||||
#import <SCLogger/SCCameraMetrics.h>
|
||||
|
||||
// Check whether we called [AVCaptureSession startRunning] within this period
|
||||
static CGFloat const kSCBlackCameraCheckingDelay = 0.5;
|
||||
|
||||
@interface SCBlackCameraViewDetector () {
|
||||
BOOL _startRunningCalled;
|
||||
BOOL _sessionIsRecreating;
|
||||
dispatch_block_t _checkSessionBlock;
|
||||
}
|
||||
@property (nonatomic) SCQueuePerformer *queuePerformer;
|
||||
@property (nonatomic) SCBlackCameraReporter *reporter;
|
||||
@property (nonatomic, weak) UIGestureRecognizer *cameraViewGesture;
|
||||
@end
|
||||
|
||||
@implementation SCBlackCameraViewDetector
|
||||
|
||||
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer reporter:(SCBlackCameraReporter *)reporter
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_queuePerformer = performer;
|
||||
_reporter = reporter;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Camera view visibility change trigger
|
||||
- (void)onCameraViewVisible:(BOOL)visible
|
||||
{
|
||||
SCTraceODPCompatibleStart(2);
|
||||
SCLogCoreCameraInfo(@"[BlackCamera] onCameraViewVisible: %d", visible);
|
||||
BOOL firstTimeAccess = [SCCaptureDeviceAuthorization notDeterminedForVideoCapture];
|
||||
if (firstTimeAccess) {
|
||||
// We don't want to check black camera for firstTimeAccess
|
||||
return;
|
||||
}
|
||||
// Visible and application is active
|
||||
if (visible && [UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
|
||||
// Since this method is usually called before the view is actually visible, leave some margin to check
|
||||
[self _scheduleCheckDelayed:YES];
|
||||
} else {
|
||||
[_queuePerformer perform:^{
|
||||
if (_checkSessionBlock) {
|
||||
dispatch_block_cancel(_checkSessionBlock);
|
||||
_checkSessionBlock = nil;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// Call this when [AVCaptureSession startRunning] is called
|
||||
- (void)sessionWillCallStartRunning
|
||||
{
|
||||
[_queuePerformer perform:^{
|
||||
_startRunningCalled = YES;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sessionWillCallStopRunning
|
||||
{
|
||||
[_queuePerformer perform:^{
|
||||
_startRunningCalled = NO;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_scheduleCheckDelayed:(BOOL)delay
|
||||
{
|
||||
[_queuePerformer perform:^{
|
||||
SC_GUARD_ELSE_RETURN(!_checkSessionBlock);
|
||||
@weakify(self);
|
||||
_checkSessionBlock = dispatch_block_create(0, ^{
|
||||
@strongify(self);
|
||||
SC_GUARD_ELSE_RETURN(self);
|
||||
self->_checkSessionBlock = nil;
|
||||
[self _checkSessionState];
|
||||
});
|
||||
|
||||
if (delay) {
|
||||
[_queuePerformer perform:_checkSessionBlock after:kSCBlackCameraCheckingDelay];
|
||||
} else {
|
||||
[_queuePerformer perform:_checkSessionBlock];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_checkSessionState
|
||||
{
|
||||
SCLogCoreCameraInfo(@"[BlackCamera] checkSessionState startRunning: %d, sessionIsRecreating: %d",
|
||||
_startRunningCalled, _sessionIsRecreating);
|
||||
if (!_startRunningCalled && !_sessionIsRecreating) {
|
||||
[_reporter reportBlackCameraWithCause:SCBlackCameraStartRunningNotCalled];
|
||||
[_reporter fileShakeTicketWithCause:SCBlackCameraStartRunningNotCalled];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sessionWillRecreate
|
||||
{
|
||||
[_queuePerformer perform:^{
|
||||
_sessionIsRecreating = YES;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sessionDidRecreate
|
||||
{
|
||||
[_queuePerformer perform:^{
|
||||
_sessionIsRecreating = NO;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)onCameraViewVisibleWithTouch:(UIGestureRecognizer *)gesture
|
||||
{
|
||||
if (gesture != _cameraViewGesture) {
|
||||
// Skip repeating gesture
|
||||
self.cameraViewGesture = gesture;
|
||||
[self _scheduleCheckDelayed:NO];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// SCCaptureSessionFixer.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 05/12/2017.
|
||||
//
|
||||
|
||||
#import "SCBlackCameraNoOutputDetector.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface SCCaptureSessionFixer : NSObject <SCBlackCameraDetectorDelegate>
|
||||
|
||||
@end
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// SCCaptureSessionFixer.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Wang on 05/12/2017.
|
||||
//
|
||||
|
||||
#import "SCCaptureSessionFixer.h"
|
||||
|
||||
#import "SCCameraTweaks.h"
|
||||
|
||||
@implementation SCCaptureSessionFixer
|
||||
|
||||
- (void)detector:(SCBlackCameraNoOutputDetector *)detector didDetectBlackCamera:(id<SCCapturer>)capture
|
||||
{
|
||||
if (SCCameraTweaksBlackCameraRecoveryEnabled()) {
|
||||
[capture recreateAVCaptureSession];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// SCContextAwareSnapCreationThrottleRequest.h
|
||||
// SCCamera
|
||||
//
|
||||
// Created by Cheng Jiang on 4/24/18.
|
||||
//
|
||||
|
||||
#import <SCFoundation/SCContextAwareThrottleRequester.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface SCContextAwareSnapCreationThrottleRequest : NSObject <SCContextAwareThrottleRequest>
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
@end
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// SCContextAwareSnapCreationThrottleRequest.m
|
||||
// SCCamera
|
||||
//
|
||||
// Created by Cheng Jiang on 4/24/18.
|
||||
//
|
||||
|
||||
#import "SCContextAwareSnapCreationThrottleRequest.h"
|
||||
|
||||
#import <SCFoundation/SCAssertWrapper.h>
|
||||
#import <SCFoundation/SCContextAwareTaskManagementResourceProvider.h>
|
||||
#import <SCFoundation/SCZeroDependencyExperiments.h>
|
||||
|
||||
#import <Tweaks/FBTweakInline.h>
|
||||
|
||||
BOOL SCCATMSnapCreationEnabled(void)
|
||||
{
|
||||
static dispatch_once_t capturingOnceToken;
|
||||
static BOOL capturingImprovementEnabled;
|
||||
dispatch_once(&capturingOnceToken, ^{
|
||||
BOOL enabledWithAB = SCExperimentWithContextAwareTaskManagementCapturingImprovementEnabled();
|
||||
NSInteger tweakOption = [FBTweakValue(@"CATM", @"Performance Improvement", @"Capturing", (id) @0,
|
||||
(@{ @0 : @"Respect A/B",
|
||||
@1 : @"YES",
|
||||
@2 : @"NO" })) integerValue];
|
||||
switch (tweakOption) {
|
||||
case 0:
|
||||
capturingImprovementEnabled = enabledWithAB;
|
||||
break;
|
||||
case 1:
|
||||
capturingImprovementEnabled = YES;
|
||||
break;
|
||||
case 2:
|
||||
capturingImprovementEnabled = NO;
|
||||
break;
|
||||
default:
|
||||
SCCAssertFail(@"Illegal option");
|
||||
}
|
||||
});
|
||||
return capturingImprovementEnabled;
|
||||
}
|
||||
|
||||
@implementation SCContextAwareSnapCreationThrottleRequest {
|
||||
NSString *_requestID;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_requestID = @"SCContextAwareSnapCreationThrottleRequest";
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)shouldThrottle:(SCApplicationContextState)context
|
||||
{
|
||||
return SCCATMSnapCreationEnabled() && context != SCApplicationContextStateCamera;
|
||||
}
|
||||
|
||||
- (NSString *)requestID
|
||||
{
|
||||
return _requestID;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id<SCContextAwareThrottleRequest>)object
|
||||
{
|
||||
return [[object requestID] isEqualToString:_requestID];
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// SCSnapCreationTriggers.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Cheng Jiang on 4/1/18.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface SCSnapCreationTriggers : NSObject
|
||||
|
||||
- (void)markSnapCreationStart;
|
||||
|
||||
- (void)markSnapCreationPreviewAnimationFinish;
|
||||
|
||||
- (void)markSnapCreationPreviewImageSetupFinish;
|
||||
|
||||
- (void)markSnapCreationPreviewVideoFirstFrameRenderFinish;
|
||||
|
||||
- (void)markSnapCreationEndWithContext:(NSString *)context;
|
||||
|
||||
@end
|
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// SCSnapCreationTriggers.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Cheng Jiang on 3/30/18.
|
||||
//
|
||||
|
||||
#import "SCSnapCreationTriggers.h"
|
||||
|
||||
#import "SCContextAwareSnapCreationThrottleRequest.h"
|
||||
|
||||
#import <SCBase/SCMacros.h>
|
||||
#import <SCFoundation/SCContextAwareThrottleRequester.h>
|
||||
#import <SCFoundation/SCLog.h>
|
||||
#import <SCFoundation/SCQueuePerformer.h>
|
||||
|
||||
@implementation SCSnapCreationTriggers {
|
||||
BOOL _snapCreationStarted;
|
||||
BOOL _previewAnimationFinished;
|
||||
BOOL _previewImageSetupFinished;
|
||||
BOOL _previewVideoFirstFrameRendered;
|
||||
}
|
||||
|
||||
- (void)markSnapCreationStart
|
||||
{
|
||||
SC_GUARD_ELSE_RUN_AND_RETURN(
|
||||
!_snapCreationStarted,
|
||||
SCLogCoreCameraWarning(@"markSnapCreationStart skipped because previous SnapCreation session is not complete"));
|
||||
@synchronized(self)
|
||||
{
|
||||
_snapCreationStarted = YES;
|
||||
}
|
||||
[[SCContextAwareThrottleRequester shared] submitSuspendRequest:[SCContextAwareSnapCreationThrottleRequest new]];
|
||||
}
|
||||
|
||||
- (void)markSnapCreationPreviewAnimationFinish
|
||||
{
|
||||
@synchronized(self)
|
||||
{
|
||||
_previewAnimationFinished = YES;
|
||||
if (_previewImageSetupFinished || _previewVideoFirstFrameRendered) {
|
||||
[self markSnapCreationEndWithContext:@"markSnapCreationPreviewAnimationFinish"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)markSnapCreationPreviewImageSetupFinish
|
||||
{
|
||||
@synchronized(self)
|
||||
{
|
||||
_previewImageSetupFinished = YES;
|
||||
if (_previewAnimationFinished) {
|
||||
[self markSnapCreationEndWithContext:@"markSnapCreationPreviewImageSetupFinish"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)markSnapCreationPreviewVideoFirstFrameRenderFinish
|
||||
{
|
||||
@synchronized(self)
|
||||
{
|
||||
_previewVideoFirstFrameRendered = YES;
|
||||
if (_previewAnimationFinished) {
|
||||
[self markSnapCreationEndWithContext:@"markSnapCreationPreviewVideoFirstFrameRenderFinish"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)markSnapCreationEndWithContext:(NSString *)context
|
||||
{
|
||||
SC_GUARD_ELSE_RETURN(_snapCreationStarted);
|
||||
SCLogCoreCameraInfo(@"markSnapCreationEnd triggered with context: %@", context);
|
||||
@synchronized(self)
|
||||
{
|
||||
_snapCreationStarted = NO;
|
||||
_previewAnimationFinished = NO;
|
||||
_previewImageSetupFinished = NO;
|
||||
_previewVideoFirstFrameRendered = NO;
|
||||
}
|
||||
[[SCContextAwareThrottleRequester shared] submitResumeRequest:[SCContextAwareSnapCreationThrottleRequest new]];
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue