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