committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 4472 additions and 0 deletions
-
113ManagedCapturer/CapturerV2/Configuration/SCCaptureConfiguration.h
-
75ManagedCapturer/CapturerV2/Configuration/SCCaptureConfiguration.m
-
27ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationAnnouncer.h
-
67ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationAnnouncer.m
-
33ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationAnnouncer_Private.h
-
23ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurationListener.h
-
46ManagedCapturer/CapturerV2/Configuration/SCCaptureConfiguration_Private.h
-
59ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurator.h
-
56ManagedCapturer/CapturerV2/Configuration/SCCaptureConfigurator.m
-
42ManagedCapturer/CapturerV2/Core/SCCaptureCore.h
-
475ManagedCapturer/CapturerV2/Core/SCCaptureCore.m
-
47ManagedCapturer/ImageProcessing/SCDepthBlurMetalModule.metal
-
21ManagedCapturer/ImageProcessing/SCDepthBlurMetalRenderCommand.h
-
90ManagedCapturer/ImageProcessing/SCDepthBlurMetalRenderCommand.m
-
29ManagedCapturer/ImageProcessing/SCDepthToGrayscaleMetalModule.metal
-
21ManagedCapturer/ImageProcessing/SCDepthToGrayscaleMetalRenderCommand.h
-
72ManagedCapturer/ImageProcessing/SCDepthToGrayscaleMetalRenderCommand.m
-
28ManagedCapturer/ImageProcessing/SCDigitalExposureHandler.h
-
30ManagedCapturer/ImageProcessing/SCDigitalExposureHandler.m
-
60ManagedCapturer/ImageProcessing/SCExposureAdjustMetalModule.metal
-
21ManagedCapturer/ImageProcessing/SCExposureAdjustMetalRenderCommand.h
-
66ManagedCapturer/ImageProcessing/SCExposureAdjustMetalRenderCommand.m
-
28ManagedCapturer/ImageProcessing/SCExposureAdjustProcessingModule.h
-
67ManagedCapturer/ImageProcessing/SCExposureAdjustProcessingModule.m
-
48ManagedCapturer/ImageProcessing/SCMetalModule.h
-
155ManagedCapturer/ImageProcessing/SCMetalModule.m
-
54ManagedCapturer/ImageProcessing/SCMetalTextureResource.h
-
215ManagedCapturer/ImageProcessing/SCMetalTextureResource.m
-
37ManagedCapturer/ImageProcessing/SCNightModeEnhancementMetalModule.metal
-
19ManagedCapturer/ImageProcessing/SCNightModeEnhancementMetalRenderCommand.h
-
64ManagedCapturer/ImageProcessing/SCNightModeEnhancementMetalRenderCommand.m
-
32ManagedCapturer/ImageProcessing/SCProcessingModule.h
-
22ManagedCapturer/ImageProcessing/SCProcessingModuleUtils.h
-
84ManagedCapturer/ImageProcessing/SCProcessingModuleUtils.m
-
23ManagedCapturer/ImageProcessing/SCProcessingPipeline.h
-
46ManagedCapturer/ImageProcessing/SCProcessingPipeline.m
-
29ManagedCapturer/ImageProcessing/SCProcessingPipelineBuilder.h
-
57ManagedCapturer/ImageProcessing/SCProcessingPipelineBuilder.m
-
23ManagedCapturer/ImageProcessing/SCStillImageDepthBlurFilter.h
-
68ManagedCapturer/ImageProcessing/SCStillImageDepthBlurFilter.m
-
103ManagedCapturer/StateMachine/SCCaptureBaseState.h
-
169ManagedCapturer/StateMachine/SCCaptureBaseState.m
-
30ManagedCapturer/StateMachine/SCCaptureStateDelegate.h
-
29ManagedCapturer/StateMachine/SCCaptureStateMachineBookKeeper.h
-
63ManagedCapturer/StateMachine/SCCaptureStateMachineBookKeeper.m
-
76ManagedCapturer/StateMachine/SCCaptureStateMachineContext.h
-
301ManagedCapturer/StateMachine/SCCaptureStateMachineContext.m
-
37ManagedCapturer/StateMachine/SCCaptureStateUtil.h
-
38ManagedCapturer/StateMachine/SCCaptureStateUtil.m
-
12ManagedCapturer/StateMachine/SCManagedCapturerLogging.h
-
22ManagedCapturer/StateMachine/States/SCCaptureImageState.h
-
65ManagedCapturer/StateMachine/States/SCCaptureImageState.m
-
29ManagedCapturer/StateMachine/States/SCCaptureImageStateTransitionPayload.h
-
27ManagedCapturer/StateMachine/States/SCCaptureImageStateTransitionPayload.m
-
22ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingState.h
-
85ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingState.m
-
29ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingStateTransitionPayload.h
-
27ManagedCapturer/StateMachine/States/SCCaptureImageWhileRecordingStateTransitionPayload.m
-
22ManagedCapturer/StateMachine/States/SCCaptureInitializedState.h
-
68ManagedCapturer/StateMachine/States/SCCaptureInitializedState.m
-
22ManagedCapturer/StateMachine/States/SCCaptureRecordingState.h
-
114ManagedCapturer/StateMachine/States/SCCaptureRecordingState.m
-
41ManagedCapturer/StateMachine/States/SCCaptureRecordingStateTransitionPayload.h
-
33ManagedCapturer/StateMachine/States/SCCaptureRecordingStateTransitionPayload.m
-
22ManagedCapturer/StateMachine/States/SCCaptureRunningState.h
-
176ManagedCapturer/StateMachine/States/SCCaptureRunningState.m
-
18ManagedCapturer/StateMachine/States/SCCaptureScanningState.h
-
75ManagedCapturer/StateMachine/States/SCCaptureScanningState.m
-
26ManagedCapturer/StateMachine/States/SCCaptureUninitializedState.h
-
70ManagedCapturer/StateMachine/States/SCCaptureUninitializedState.m
-
22ManagedCapturer/StateMachine/States/SCStateTransitionPayload.h
-
27ManagedCapturer/StateMachine/States/SCStateTransitionPayload.m
@ -0,0 +1,113 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,75 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,27 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,67 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,33 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,23 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,46 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,59 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,56 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,42 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,475 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,47 @@ |
|||||
|
// |
||||
|
// 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); |
||||
|
} |
||||
|
|
@ -0,0 +1,21 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,90 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,29 @@ |
|||||
|
// |
||||
|
// 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); |
||||
|
} |
||||
|
|
||||
|
|
@ -0,0 +1,21 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,72 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,28 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,30 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,60 @@ |
|||||
|
// |
||||
|
// 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); |
||||
|
|
||||
|
} |
@ -0,0 +1,21 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,66 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,28 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,67 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,48 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,155 @@ |
|||||
|
// |
||||
|
// 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 |
@ -0,0 +1,54 @@ |
|||||
|
// |
||||
|
// 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 SCMetalTextureResource |
||||
|
The SCMetalTextureResource is created by SCMetalModule and is passed to a SCMetalRenderCommand. |
||||
|
This resource provides a collection of textures for rendering, where a SCMetalRenderCommand |
||||
|
selects which textures it needs. Textures are lazily initialiazed to optimize performance. |
||||
|
Additionally, information pertaining to depth is provided if normalizing depth is desired: |
||||
|
depthRange is the range of possible depth values [depthOffset, depthOffset + depthRange], |
||||
|
where depthOffset is the min depth value in the given depth map. |
||||
|
NOTE: This class is NOT thread safe -- ensure any calls are made by a performer by calling |
||||
|
SCAssertPerformer before actually accessing any textures |
||||
|
*/ |
||||
|
@interface SCMetalTextureResource : NSObject |
||||
|
|
||||
|
#if !TARGET_IPHONE_SIMULATOR |
||||
|
@property (nonatomic, readonly) id<MTLTexture> sourceYTexture; |
||||
|
@property (nonatomic, readonly) id<MTLTexture> sourceUVTexture; |
||||
|
@property (nonatomic, readonly) id<MTLTexture> destinationYTexture; |
||||
|
@property (nonatomic, readonly) id<MTLTexture> destinationUVTexture; |
||||
|
|
||||
|
// Textures for SCDepthBlurMetalCommand |
||||
|
@property (nonatomic, readonly) id<MTLTexture> sourceBlurredYTexture; |
||||
|
@property (nonatomic, readonly) id<MTLTexture> sourceDepthTexture; |
||||
|
|
||||
|
@property (nonatomic, readonly) id<MTLDevice> device; |
||||
|
#endif |
||||
|
|
||||
|
// Available depth-related auxiliary resources (when depth data is provided) |
||||
|
@property (nonatomic, readonly) float depthRange; |
||||
|
@property (nonatomic, readonly) float depthOffset; |
||||
|
@property (nonatomic, readonly) CGFloat depthBlurForegroundThreshold; |
||||
|
@property (nonatomic, readonly) SampleBufferMetadata sampleBufferMetadata; |
||||
|
|
||||
|
#if !TARGET_IPHONE_SIMULATOR |
||||
|
- (instancetype)initWithRenderData:(RenderData)renderData |
||||
|
textureCache:(CVMetalTextureCacheRef)textureCache |
||||
|
device:(id<MTLDevice>)device; |
||||
|
#endif |
||||
|
|
||||
|
@end |
@ -0,0 +1,215 @@ |
|||||
|
// |
||||
|
// SCMetalTextureResource.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Brian Ng on 11/7/17. |
||||
|
// |
||||
|
|
||||
|
#import "SCMetalTextureResource.h" |
||||
|
|
||||
|
#import "SCCameraSettingUtils.h" |
||||
|
#import "SCCameraTweaks.h" |
||||
|
#import "SCMetalUtils.h" |
||||
|
|
||||
|
@import CoreImage; |
||||
|
|
||||
|
#if !TARGET_IPHONE_SIMULATOR |
||||
|
static NSInteger const kSCFocusRectSize = 4; |
||||
|
#endif |
||||
|
|
||||
|
@interface SCMetalTextureResource () |
||||
|
#if !TARGET_IPHONE_SIMULATOR |
||||
|
@property (nonatomic, readonly) CVMetalTextureCacheRef textureCache; |
||||
|
#endif |
||||
|
@end |
||||
|
|
||||
|
@implementation SCMetalTextureResource { |
||||
|
RenderData _renderData; |
||||
|
CVImageBufferRef _imageBuffer; |
||||
|
CIContext *_context; |
||||
|
} |
||||
|
|
||||
|
#if !TARGET_IPHONE_SIMULATOR |
||||
|
@synthesize sourceYTexture = _sourceYTexture; |
||||
|
@synthesize sourceUVTexture = _sourceUVTexture; |
||||
|
@synthesize destinationYTexture = _destinationYTexture; |
||||
|
@synthesize destinationUVTexture = _destinationUVTexture; |
||||
|
@synthesize sourceBlurredYTexture = _sourceBlurredYTexture; |
||||
|
@synthesize sourceDepthTexture = _sourceDepthTexture; |
||||
|
@synthesize depthRange = _depthRange; |
||||
|
@synthesize depthOffset = _depthOffset; |
||||
|
@synthesize depthBlurForegroundThreshold = _depthBlurForegroundThreshold; |
||||
|
@synthesize device = _device; |
||||
|
@synthesize sampleBufferMetadata = _sampleBufferMetadata; |
||||
|
|
||||
|
- (instancetype)initWithRenderData:(RenderData)renderData |
||||
|
textureCache:(CVMetalTextureCacheRef)textureCache |
||||
|
device:(id<MTLDevice>)device |
||||
|
{ |
||||
|
self = [super init]; |
||||
|
if (self) { |
||||
|
_imageBuffer = CMSampleBufferGetImageBuffer(renderData.sampleBuffer); |
||||
|
_renderData = renderData; |
||||
|
_textureCache = textureCache; |
||||
|
_device = device; |
||||
|
_context = [CIContext contextWithOptions:@{ kCIContextWorkingFormat : @(kCIFormatRGBAh) }]; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
#if !TARGET_IPHONE_SIMULATOR |
||||
|
|
||||
|
- (id<MTLTexture>)sourceYTexture |
||||
|
{ |
||||
|
if (!_sourceYTexture) { |
||||
|
CVPixelBufferLockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly); |
||||
|
_sourceYTexture = SCMetalTextureFromPixelBuffer(_imageBuffer, 0, MTLPixelFormatR8Unorm, _textureCache); |
||||
|
CVPixelBufferUnlockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly); |
||||
|
} |
||||
|
return _sourceYTexture; |
||||
|
} |
||||
|
|
||||
|
- (id<MTLTexture>)sourceUVTexture |
||||
|
{ |
||||
|
if (!_sourceUVTexture) { |
||||
|
CVPixelBufferLockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly); |
||||
|
_sourceUVTexture = SCMetalTextureFromPixelBuffer(_imageBuffer, 1, MTLPixelFormatRG8Unorm, _textureCache); |
||||
|
CVPixelBufferUnlockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly); |
||||
|
} |
||||
|
return _sourceUVTexture; |
||||
|
} |
||||
|
|
||||
|
- (id<MTLTexture>)destinationYTexture |
||||
|
{ |
||||
|
if (!_destinationYTexture) { |
||||
|
MTLTextureDescriptor *textureDescriptor = |
||||
|
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm |
||||
|
width:CVPixelBufferGetWidthOfPlane(_imageBuffer, 0) |
||||
|
height:CVPixelBufferGetHeightOfPlane(_imageBuffer, 0) |
||||
|
mipmapped:NO]; |
||||
|
textureDescriptor.usage |= MTLTextureUsageShaderWrite; |
||||
|
_destinationYTexture = [_device newTextureWithDescriptor:textureDescriptor]; |
||||
|
} |
||||
|
return _destinationYTexture; |
||||
|
} |
||||
|
|
||||
|
- (id<MTLTexture>)destinationUVTexture |
||||
|
{ |
||||
|
if (!_destinationUVTexture) { |
||||
|
MTLTextureDescriptor *textureDescriptor = |
||||
|
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRG8Unorm |
||||
|
width:CVPixelBufferGetWidthOfPlane(_imageBuffer, 1) |
||||
|
height:CVPixelBufferGetHeightOfPlane(_imageBuffer, 1) |
||||
|
mipmapped:NO]; |
||||
|
textureDescriptor.usage |= MTLTextureUsageShaderWrite; |
||||
|
_destinationUVTexture = [_device newTextureWithDescriptor:textureDescriptor]; |
||||
|
} |
||||
|
return _destinationUVTexture; |
||||
|
} |
||||
|
|
||||
|
- (id<MTLTexture>)sourceBlurredYTexture |
||||
|
{ |
||||
|
if (!_sourceBlurredYTexture) { |
||||
|
MTLTextureDescriptor *textureDescriptor = |
||||
|
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm |
||||
|
width:CVPixelBufferGetWidthOfPlane(_imageBuffer, 0) |
||||
|
height:CVPixelBufferGetHeightOfPlane(_imageBuffer, 0) |
||||
|
mipmapped:NO]; |
||||
|
textureDescriptor.usage |= MTLTextureUsageShaderWrite; |
||||
|
_sourceBlurredYTexture = [_device newTextureWithDescriptor:textureDescriptor]; |
||||
|
} |
||||
|
return _sourceBlurredYTexture; |
||||
|
} |
||||
|
|
||||
|
- (id<MTLTexture>)sourceDepthTexture |
||||
|
{ |
||||
|
if (!_sourceDepthTexture) { |
||||
|
CVPixelBufferLockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly); |
||||
|
_sourceDepthTexture = |
||||
|
SCMetalTextureFromPixelBuffer(_renderData.depthDataMap, 0, MTLPixelFormatR16Float, _textureCache); |
||||
|
CVPixelBufferUnlockBaseAddress(_imageBuffer, kCVPixelBufferLock_ReadOnly); |
||||
|
} |
||||
|
return _sourceDepthTexture; |
||||
|
} |
||||
|
|
||||
|
- (float)depthRange |
||||
|
{ |
||||
|
if (_depthRange == 0) { |
||||
|
// Get min/max values of depth image to normalize |
||||
|
size_t bufferWidth = CVPixelBufferGetWidth(_renderData.depthDataMap); |
||||
|
size_t bufferHeight = CVPixelBufferGetHeight(_renderData.depthDataMap); |
||||
|
size_t bufferBytesPerRow = CVPixelBufferGetBytesPerRow(_renderData.depthDataMap); |
||||
|
|
||||
|
CVPixelBufferLockBaseAddress(_renderData.depthDataMap, kCVPixelBufferLock_ReadOnly); |
||||
|
unsigned char *pixelBufferPointer = CVPixelBufferGetBaseAddress(_renderData.depthDataMap); |
||||
|
__fp16 *bufferPtr = (__fp16 *)pixelBufferPointer; |
||||
|
uint32_t ptrInc = (int)bufferBytesPerRow / sizeof(__fp16); |
||||
|
|
||||
|
float depthMin = MAXFLOAT; |
||||
|
float depthMax = -MAXFLOAT; |
||||
|
for (int j = 0; j < bufferHeight; j++) { |
||||
|
for (int i = 0; i < bufferWidth; i++) { |
||||
|
float value = bufferPtr[i]; |
||||
|
if (!isnan(value)) { |
||||
|
depthMax = MAX(depthMax, value); |
||||
|
depthMin = MIN(depthMin, value); |
||||
|
} |
||||
|
} |
||||
|
bufferPtr += ptrInc; |
||||
|
} |
||||
|
CVPixelBufferUnlockBaseAddress(_renderData.depthDataMap, kCVPixelBufferLock_ReadOnly); |
||||
|
_depthRange = depthMax - depthMin; |
||||
|
_depthOffset = depthMin; |
||||
|
} |
||||
|
return _depthRange; |
||||
|
} |
||||
|
|
||||
|
- (float)depthOffset |
||||
|
{ |
||||
|
if (_depthRange == 0) { |
||||
|
[self depthRange]; |
||||
|
} |
||||
|
return _depthOffset; |
||||
|
} |
||||
|
|
||||
|
- (CGFloat)depthBlurForegroundThreshold |
||||
|
{ |
||||
|
if (_renderData.depthBlurPointOfInterest) { |
||||
|
CGPoint point = *_renderData.depthBlurPointOfInterest; |
||||
|
CIImage *disparityImage = [CIImage imageWithCVPixelBuffer:_renderData.depthDataMap]; |
||||
|
CIVector *vector = |
||||
|
[CIVector vectorWithX:point.x * CVPixelBufferGetWidth(_renderData.depthDataMap) - kSCFocusRectSize / 2 |
||||
|
Y:point.y * CVPixelBufferGetHeight(_renderData.depthDataMap) - kSCFocusRectSize / 2 |
||||
|
Z:kSCFocusRectSize |
||||
|
W:kSCFocusRectSize]; |
||||
|
CIImage *minMaxImage = |
||||
|
[[disparityImage imageByClampingToExtent] imageByApplyingFilter:@"CIAreaMinMaxRed" |
||||
|
withInputParameters:@{kCIInputExtentKey : vector}]; |
||||
|
UInt8 pixel[4] = {0, 0, 0, 0}; |
||||
|
[_context render:minMaxImage |
||||
|
toBitmap:&pixel |
||||
|
rowBytes:4 |
||||
|
bounds:CGRectMake(0, 0, 1, 1) |
||||
|
format:kCIFormatRGBA8 |
||||
|
colorSpace:nil]; |
||||
|
CGFloat disparity = pixel[1] / 255.0; |
||||
|
CGFloat normalizedDisparity = (disparity - self.depthOffset) / self.depthRange; |
||||
|
return normalizedDisparity; |
||||
|
} else { |
||||
|
return SCCameraTweaksDepthBlurForegroundThreshold(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (SampleBufferMetadata)sampleBufferMetadata |
||||
|
{ |
||||
|
SampleBufferMetadata sampleMetadata = { |
||||
|
.isoSpeedRating = 0, .exposureTime = 0.033, .brightness = 0, |
||||
|
}; |
||||
|
retrieveSampleBufferMetadata(_renderData.sampleBuffer, &sampleMetadata); |
||||
|
return sampleMetadata; |
||||
|
} |
||||
|
|
||||
|
#endif |
||||
|
|
||||
|
@end |
@ -0,0 +1,37 @@ |
|||||
|
// |
||||
|
// SCNightModeEnhancementMetalModule.metal |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Chao Pang on 12/21/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#include <metal_stdlib> |
||||
|
using namespace metal; |
||||
|
|
||||
|
typedef struct SampleBufferMetadata { |
||||
|
int iosSpeedRating; |
||||
|
float exposureTime; |
||||
|
float brightness; |
||||
|
}SampleBufferMetadata; |
||||
|
|
||||
|
kernel void kernel_night_mode_enhancement(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)]], |
||||
|
constant SampleBufferMetadata &metaData [[buffer(0)]], |
||||
|
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 - metaData.brightness * 0.1; |
||||
|
factor = max(min(factor, 1.3), 1.0); |
||||
|
|
||||
|
valueY = min(valueY * factor, 1.0); |
||||
|
valueUV.rg = max(min((valueUV.rg - 0.5) * factor + 0.5, 1.0), 0.0); |
||||
|
|
||||
|
destinationYTexture.write(valueY, gid); |
||||
|
destinationUVTexture.write(float4(valueUV.r, valueUV.g, 0, 0), gid); |
||||
|
|
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
// |
||||
|
// SCNightModeEnhancementMetalRenderCommand.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Chao Pang on 12/21/17. |
||||
|
// |
||||
|
|
||||
|
#import "SCMetalModule.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
/* |
||||
|
Prepares the command buffer for the SCNightModeEnhancementMetalModule.metal. |
||||
|
*/ |
||||
|
@interface SCNightModeEnhancementMetalRenderCommand : SCMetalModule <SCMetalRenderCommand> |
||||
|
|
||||
|
@property (nonatomic, readonly) NSString *functionName; |
||||
|
|
||||
|
@end |
@ -0,0 +1,64 @@ |
|||||
|
// |
||||
|
// SCNightModeEnhancementMetalRenderCommand.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Chao Pang on 12/21/17. |
||||
|
// |
||||
|
|
||||
|
#import "SCNightModeEnhancementMetalRenderCommand.h" |
||||
|
|
||||
|
#import "SCCameraTweaks.h" |
||||
|
#import "SCMetalUtils.h" |
||||
|
|
||||
|
#import <SCFoundation/NSString+SCFormat.h> |
||||
|
|
||||
|
@import Metal; |
||||
|
|
||||
|
@implementation SCNightModeEnhancementMetalRenderCommand |
||||
|
|
||||
|
#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 |
||||
|
SampleBufferMetadata sampleBufferMetadata = { |
||||
|
.isoSpeedRating = textureResource.sampleBufferMetadata.isoSpeedRating, |
||||
|
.exposureTime = textureResource.sampleBufferMetadata.exposureTime, |
||||
|
.brightness = textureResource.sampleBufferMetadata.brightness, |
||||
|
}; |
||||
|
id<MTLBuffer> metadataBuffer = [textureResource.device newBufferWithLength:sizeof(SampleBufferMetadata) |
||||
|
options:MTLResourceOptionCPUCacheModeDefault]; |
||||
|
memcpy(metadataBuffer.contents, &sampleBufferMetadata, sizeof(SampleBufferMetadata)); |
||||
|
[commandEncoder setTexture:textureResource.sourceYTexture atIndex:0]; |
||||
|
[commandEncoder setTexture:textureResource.sourceUVTexture atIndex:1]; |
||||
|
[commandEncoder setTexture:textureResource.destinationYTexture atIndex:2]; |
||||
|
[commandEncoder setTexture:textureResource.destinationUVTexture atIndex:3]; |
||||
|
[commandEncoder setBuffer:metadataBuffer offset:0 atIndex:0]; |
||||
|
#endif |
||||
|
|
||||
|
return commandEncoder; |
||||
|
} |
||||
|
|
||||
|
#pragma mark - SCMetalModuleFunctionProvider |
||||
|
|
||||
|
- (NSString *)functionName |
||||
|
{ |
||||
|
return @"kernel_night_mode_enhancement"; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)requiresDepthData |
||||
|
{ |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
- (NSString *)description |
||||
|
{ |
||||
|
return [NSString |
||||
|
sc_stringWithFormat:@"SCNightModeEnhancementMetalRenderCommand (shader function = %@)", self.functionName]; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,32 @@ |
|||||
|
// |
||||
|
// SCProcessingModule.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Yu-Kuan (Anthony) Lai on 5/30/17. |
||||
|
// Copyright © 2017 Snapchat, Inc. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import <AVFoundation/AVFoundation.h> |
||||
|
#import <CoreMedia/CoreMedia.h> |
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
typedef struct RenderData { |
||||
|
CMSampleBufferRef sampleBuffer; |
||||
|
CVPixelBufferRef depthDataMap; // Optional - for depth blur rendering |
||||
|
CGPoint *depthBlurPointOfInterest; // Optional - for depth blur rendering |
||||
|
} RenderData; |
||||
|
|
||||
|
/* |
||||
|
@protocol SCProcessingModule |
||||
|
A single module that is responsible for the actual image processing work. Multiple modules can be chained |
||||
|
together by the SCProcessingPipelineBuilder and the frame can be passed through the entire |
||||
|
SCProcessingPipeline. |
||||
|
*/ |
||||
|
@protocol SCProcessingModule <NSObject> |
||||
|
|
||||
|
- (CMSampleBufferRef)render:(RenderData)renderData; |
||||
|
|
||||
|
// Needed to protect against depth data potentially being nil during the render pass |
||||
|
- (BOOL)requiresDepthData; |
||||
|
|
||||
|
@end |
@ -0,0 +1,22 @@ |
|||||
|
// |
||||
|
// SCProcessingModuleUtils.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Brian Ng on 11/10/17. |
||||
|
// |
||||
|
|
||||
|
#import <CoreImage/CoreImage.h> |
||||
|
#import <CoreMedia/CoreMedia.h> |
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@interface SCProcessingModuleUtils : NSObject |
||||
|
|
||||
|
+ (CVPixelBufferRef)pixelBufferFromImage:(CIImage *)image |
||||
|
bufferPool:(CVPixelBufferPoolRef)bufferPool |
||||
|
context:(CIContext *)context; |
||||
|
|
||||
|
+ (CMSampleBufferRef)sampleBufferFromImage:(CIImage *)image |
||||
|
oldSampleBuffer:(CMSampleBufferRef)oldSampleBuffer |
||||
|
bufferPool:(CVPixelBufferPoolRef)bufferPool |
||||
|
context:(CIContext *)context; |
||||
|
@end |
@ -0,0 +1,84 @@ |
|||||
|
// |
||||
|
// SCProcessingModuleUtils.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Brian Ng on 11/10/17. |
||||
|
// |
||||
|
|
||||
|
#import "SCProcessingModuleUtils.h" |
||||
|
|
||||
|
#import <SCFoundation/SCLog.h> |
||||
|
|
||||
|
@import CoreImage; |
||||
|
|
||||
|
@implementation SCProcessingModuleUtils |
||||
|
|
||||
|
+ (CVPixelBufferRef)pixelBufferFromImage:(CIImage *)image |
||||
|
bufferPool:(CVPixelBufferPoolRef)bufferPool |
||||
|
context:(CIContext *)context |
||||
|
{ |
||||
|
CVReturn result; |
||||
|
|
||||
|
if (bufferPool == NULL) { |
||||
|
NSDictionary *pixelAttributes = @{ |
||||
|
(NSString *) kCVPixelBufferIOSurfacePropertiesKey : @{}, (NSString *) |
||||
|
kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange), (NSString *) |
||||
|
kCVPixelBufferWidthKey : @(image.extent.size.width), (NSString *) |
||||
|
kCVPixelBufferHeightKey : @(image.extent.size.height) |
||||
|
}; |
||||
|
result = CVPixelBufferPoolCreate(kCFAllocatorDefault, NULL, |
||||
|
(__bridge CFDictionaryRef _Nullable)(pixelAttributes), &bufferPool); |
||||
|
if (result != kCVReturnSuccess) { |
||||
|
SCLogGeneralError(@"[Processing Pipeline] Error creating pixel buffer pool %i", result); |
||||
|
return NULL; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
CVPixelBufferRef resultBuffer = NULL; |
||||
|
result = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, bufferPool, &resultBuffer); |
||||
|
|
||||
|
if (result == kCVReturnSuccess) { |
||||
|
[context render:image toCVPixelBuffer:resultBuffer]; |
||||
|
} else { |
||||
|
SCLogGeneralError(@"[Processing Pipeline] Error creating pixel buffer from pool %i", result); |
||||
|
} |
||||
|
return resultBuffer; |
||||
|
} |
||||
|
|
||||
|
+ (CMSampleBufferRef)sampleBufferFromImage:(CIImage *)image |
||||
|
oldSampleBuffer:(CMSampleBufferRef)oldSampleBuffer |
||||
|
bufferPool:(CVPixelBufferPoolRef)bufferPool |
||||
|
context:(CIContext *)context |
||||
|
{ |
||||
|
CVPixelBufferRef pixelBuffer = |
||||
|
[SCProcessingModuleUtils pixelBufferFromImage:image bufferPool:bufferPool context:context]; |
||||
|
if (!pixelBuffer) { |
||||
|
SCLogGeneralError(@"[Processing Pipeline] Error creating new pixel buffer from image"); |
||||
|
return oldSampleBuffer; |
||||
|
} |
||||
|
|
||||
|
CMSampleBufferRef newSampleBuffer = NULL; |
||||
|
CMSampleTimingInfo timimgInfo = kCMTimingInfoInvalid; |
||||
|
CMSampleBufferGetSampleTimingInfo(oldSampleBuffer, 0, &timimgInfo); |
||||
|
|
||||
|
CMVideoFormatDescriptionRef videoInfo = NULL; |
||||
|
OSStatus status = CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixelBuffer, &videoInfo); |
||||
|
if (status != noErr) { |
||||
|
SCLogGeneralError(@"[Processing Pipeline] Error creating video format description %i", (int)status); |
||||
|
CVPixelBufferRelease(pixelBuffer); |
||||
|
return oldSampleBuffer; |
||||
|
} |
||||
|
|
||||
|
status = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, true, NULL, NULL, videoInfo, |
||||
|
&timimgInfo, &newSampleBuffer); |
||||
|
if (status != noErr) { |
||||
|
SCLogGeneralError(@"[Processing Pipeline] Error creating CMSampleBuffer %i", (int)status); |
||||
|
CVPixelBufferRelease(pixelBuffer); |
||||
|
return oldSampleBuffer; |
||||
|
} |
||||
|
|
||||
|
CVPixelBufferRelease(pixelBuffer); |
||||
|
return newSampleBuffer; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,23 @@ |
|||||
|
// |
||||
|
// SCProcessingPipeline.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Yu-Kuan (Anthony) Lai on 5/30/17. |
||||
|
// Copyright © 2017 Snapchat, Inc. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "SCProcessingModule.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
/* |
||||
|
@class SCProcessingPipeline |
||||
|
The SCProcessingPipeline chains together a series of SCProcessingModules and passes the frame through |
||||
|
each of them in a pre-determined order. This is done through a chain of command, where the resulting |
||||
|
frame from the the first module is passed to the second, then to the third, etc. |
||||
|
*/ |
||||
|
@interface SCProcessingPipeline : NSObject <SCProcessingModule> |
||||
|
|
||||
|
@property (nonatomic, strong) NSMutableArray<id<SCProcessingModule>> *processingModules; |
||||
|
|
||||
|
@end |
@ -0,0 +1,46 @@ |
|||||
|
// |
||||
|
// SCProcessingPipeline.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Yu-Kuan (Anthony) Lai on 5/30/17. |
||||
|
// Copyright © 2017 Snapchat, Inc. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "SCProcessingPipeline.h" |
||||
|
|
||||
|
#import <SCFoundation/NSString+Helpers.h> |
||||
|
|
||||
|
@import CoreMedia; |
||||
|
|
||||
|
@implementation SCProcessingPipeline |
||||
|
|
||||
|
- (CMSampleBufferRef)render:(RenderData)renderData |
||||
|
{ |
||||
|
for (id<SCProcessingModule> module in self.processingModules) { |
||||
|
if (![module requiresDepthData] || ([module requiresDepthData] && renderData.depthDataMap)) { |
||||
|
renderData.sampleBuffer = [module render:renderData]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return renderData.sampleBuffer; |
||||
|
} |
||||
|
|
||||
|
- (NSString *)description |
||||
|
{ |
||||
|
NSMutableString *desc = [NSMutableString new]; |
||||
|
[desc appendString:@"ProcessingPipeline, modules: "]; |
||||
|
for (id<SCProcessingModule> module in self.processingModules) { |
||||
|
[desc appendFormat:@"%@, ", [module description]]; |
||||
|
} |
||||
|
if (self.processingModules.count > 0) { |
||||
|
return [desc substringToIndex:desc.lengthOfCharacterSequences - 2]; |
||||
|
} |
||||
|
return desc; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)requiresDepthData |
||||
|
{ |
||||
|
return NO; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,29 @@ |
|||||
|
// |
||||
|
// SCProcessingPipelineBuilder.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Yu-Kuan (Anthony) Lai on 6/1/17. |
||||
|
// Copyright © 2017 Snapchat, Inc. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@class SCDigitalExposureHandler; |
||||
|
@class SCProcessingPipeline; |
||||
|
|
||||
|
/* |
||||
|
@class SCProcessingPipelineBuilder |
||||
|
The builder object is responsible for creating the SCProcessingPipeline, the underneath |
||||
|
SCProcessingModules, and eventually chaining the SCProcessingModules together in a pre-determined |
||||
|
order. The builder is also responsible for providing consumers with handler objects. |
||||
|
|
||||
|
*/ |
||||
|
@interface SCProcessingPipelineBuilder : NSObject |
||||
|
|
||||
|
@property (nonatomic) BOOL useExposureAdjust; |
||||
|
@property (nonatomic) BOOL portraitModeEnabled; |
||||
|
@property (nonatomic) BOOL enhancedNightMode; |
||||
|
|
||||
|
- (SCProcessingPipeline *)build; |
||||
|
|
||||
|
@end |
@ -0,0 +1,57 @@ |
|||||
|
// |
||||
|
// SCProcessingPipelineBuilder.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Yu-Kuan (Anthony) Lai on 6/1/17. |
||||
|
// Copyright © 2017 Snapchat, Inc. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import "SCProcessingPipelineBuilder.h" |
||||
|
|
||||
|
#import "SCCameraTweaks.h" |
||||
|
#import "SCDepthBlurMetalRenderCommand.h" |
||||
|
#import "SCDepthToGrayscaleMetalRenderCommand.h" |
||||
|
#import "SCDigitalExposureHandler.h" |
||||
|
#import "SCExposureAdjustMetalRenderCommand.h" |
||||
|
#import "SCMetalUtils.h" |
||||
|
#import "SCNightModeEnhancementMetalRenderCommand.h" |
||||
|
#import "SCProcessingPipeline.h" |
||||
|
|
||||
|
@implementation SCProcessingPipelineBuilder |
||||
|
|
||||
|
- (SCProcessingPipeline *)build |
||||
|
{ |
||||
|
if (!_useExposureAdjust && !_portraitModeEnabled && !_enhancedNightMode) { // in the future: && !useA && !useB ... |
||||
|
return nil; |
||||
|
} |
||||
|
|
||||
|
SCProcessingPipeline *processingPipeline = [[SCProcessingPipeline alloc] init]; |
||||
|
NSMutableArray<id<SCProcessingModule>> *processingModules = [NSMutableArray array]; |
||||
|
|
||||
|
// order of adding module matters! |
||||
|
if (_useExposureAdjust && SCDeviceSupportsMetal()) { |
||||
|
// this check looks redundant right now, but when we have more modules it will be necessary |
||||
|
SCMetalModule *exposureAdjustMetalModule = |
||||
|
[[SCMetalModule alloc] initWithMetalRenderCommand:[SCExposureAdjustMetalRenderCommand new]]; |
||||
|
[processingModules addObject:exposureAdjustMetalModule]; |
||||
|
} |
||||
|
|
||||
|
if (_portraitModeEnabled) { |
||||
|
id<SCMetalRenderCommand> renderCommand = SCCameraTweaksDepthToGrayscaleOverride() |
||||
|
? [SCDepthToGrayscaleMetalRenderCommand new] |
||||
|
: [SCDepthBlurMetalRenderCommand new]; |
||||
|
SCMetalModule *depthBlurMetalModule = [[SCMetalModule alloc] initWithMetalRenderCommand:renderCommand]; |
||||
|
[processingModules addObject:depthBlurMetalModule]; |
||||
|
} |
||||
|
|
||||
|
if (_enhancedNightMode && SCDeviceSupportsMetal()) { |
||||
|
SCMetalModule *nightModeEnhancementModule = |
||||
|
[[SCMetalModule alloc] initWithMetalRenderCommand:[SCNightModeEnhancementMetalRenderCommand new]]; |
||||
|
[processingModules addObject:nightModeEnhancementModule]; |
||||
|
} |
||||
|
|
||||
|
processingPipeline.processingModules = processingModules; |
||||
|
return processingPipeline; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,23 @@ |
|||||
|
// |
||||
|
// SCStillImageDepthBlurFilter.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Brian Ng on 10/11/17. |
||||
|
// |
||||
|
|
||||
|
#import "SCProcessingModule.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
/* |
||||
|
@class SCStillImageDepthBlurFilter |
||||
|
This module uses the CIDepthBlurEffect CIFilter that uses rgb and depth information to produce an image with |
||||
|
the portrait mode effect (background blurred, foreground sharp). |
||||
|
*/ |
||||
|
@interface SCStillImageDepthBlurFilter : NSObject |
||||
|
|
||||
|
// Applies the CIDepthBlurEffect filter to a still image capture photo. If an error occured, the original |
||||
|
// photoData will be returned |
||||
|
- (NSData *)renderWithPhotoData:(NSData *)photoData renderData:(RenderData)renderData NS_AVAILABLE_IOS(11_0); |
||||
|
|
||||
|
@end |
@ -0,0 +1,68 @@ |
|||||
|
// |
||||
|
// SCStillImageDepthBlurFilter.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Brian Ng on 10/11/17. |
||||
|
// |
||||
|
|
||||
|
#import "SCStillImageDepthBlurFilter.h" |
||||
|
|
||||
|
#import "SCCameraTweaks.h" |
||||
|
#import "SCProcessingModuleUtils.h" |
||||
|
|
||||
|
@import CoreMedia; |
||||
|
|
||||
|
@implementation SCStillImageDepthBlurFilter { |
||||
|
CIContext *_context; |
||||
|
CIFilter *_filter; |
||||
|
CVPixelBufferPoolRef _bufferPool; |
||||
|
} |
||||
|
|
||||
|
- (instancetype)init |
||||
|
{ |
||||
|
if (self = [super init]) { |
||||
|
_context = [CIContext contextWithOptions:@{ kCIContextWorkingFormat : @(kCIFormatRGBAh) }]; |
||||
|
_filter = [CIFilter filterWithName:@"CIDepthBlurEffect"]; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (void)dealloc |
||||
|
{ |
||||
|
CVPixelBufferPoolFlush(_bufferPool, kCVPixelBufferPoolFlushExcessBuffers); |
||||
|
CVPixelBufferPoolRelease(_bufferPool); |
||||
|
} |
||||
|
|
||||
|
- (NSData *)renderWithPhotoData:(NSData *)photoData renderData:(RenderData)renderData NS_AVAILABLE_IOS(11_0) |
||||
|
{ |
||||
|
CIImage *mainImage = [CIImage imageWithData:photoData]; |
||||
|
CVPixelBufferRef disparityImagePixelBuffer = renderData.depthDataMap; |
||||
|
CIImage *disparityImage = [CIImage imageWithCVPixelBuffer:disparityImagePixelBuffer]; |
||||
|
if (!disparityImage) { |
||||
|
return photoData; |
||||
|
} |
||||
|
[_filter setValue:mainImage forKey:kCIInputImageKey]; |
||||
|
[_filter setValue:disparityImage forKey:kCIInputDisparityImageKey]; |
||||
|
if (renderData.depthBlurPointOfInterest && SCCameraTweaksEnableFilterInputFocusRect()) { |
||||
|
CGPoint pointOfInterest = *renderData.depthBlurPointOfInterest; |
||||
|
[_filter setValue:[CIVector vectorWithX:pointOfInterest.x Y:pointOfInterest.y Z:1 W:1] |
||||
|
forKey:@"inputFocusRect"]; |
||||
|
} |
||||
|
CIImage *result = [_filter outputImage]; |
||||
|
if (!result) { |
||||
|
return photoData; |
||||
|
} |
||||
|
CGColorSpaceRef deviceRGBColorSpace = CGColorSpaceCreateDeviceRGB(); |
||||
|
NSData *processedPhotoData = [_context JPEGRepresentationOfImage:result colorSpace:deviceRGBColorSpace options:@{}]; |
||||
|
CGColorSpaceRelease(deviceRGBColorSpace); |
||||
|
if (!processedPhotoData) { |
||||
|
return photoData; |
||||
|
} |
||||
|
renderData.sampleBuffer = [SCProcessingModuleUtils sampleBufferFromImage:result |
||||
|
oldSampleBuffer:renderData.sampleBuffer |
||||
|
bufferPool:_bufferPool |
||||
|
context:_context]; |
||||
|
return processedPhotoData; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,103 @@ |
|||||
|
// |
||||
|
// SCCaptureBaseState.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 10/19/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureCommon.h" |
||||
|
#import "SCCaptureStateDelegate.h" |
||||
|
#import "SCCaptureStateMachineBookKeeper.h" |
||||
|
#import "SCCaptureStateUtil.h" |
||||
|
#import "SCCaptureWorker.h" |
||||
|
#import "SCManagedCaptureDevice.h" |
||||
|
#import "SCManagedCapturerState.h" |
||||
|
#import "SCStateTransitionPayload.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@class SCCaptureResource; |
||||
|
|
||||
|
@class SCCapturerToken; |
||||
|
|
||||
|
@class SCAudioConfiguration; |
||||
|
|
||||
|
@class SCQueuePerformer; |
||||
|
/* |
||||
|
Every state machine state needs to inherent SCCaptureBaseState to have the APIs. State machine state in general will |
||||
|
only implement APIs which are legal for itself. If illegal APIs are invoked, SCCaptureBaseState will handle it. |
||||
|
The intended behavior: |
||||
|
1) crash using SCAssert in Debug build, |
||||
|
2) ignore api call, and log the call, for alpha/master/production. |
||||
|
3) in the future, we will introduce dangerous API call concept, and restart camera in such case, to avoid bad state. |
||||
|
|
||||
|
Every state machine state is going to be built to follow functional programming as more as possible. The shared |
||||
|
resources between them will be passed into the API via SCCaptureResource. |
||||
|
*/ |
||||
|
|
||||
|
@interface SCCaptureBaseState : NSObject |
||||
|
|
||||
|
- (instancetype)init NS_UNAVAILABLE; |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate; |
||||
|
|
||||
|
/* The following API will be invoked at the moment state context promote the state to be current state. State use this |
||||
|
* chance to do something, such as start recording for recording state. |
||||
|
*/ |
||||
|
- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (SCCaptureStateMachineStateId)stateId; |
||||
|
|
||||
|
- (void)initializeCaptureWithDevicePosition:(SCManagedCaptureDevicePosition)devicePosition |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
completionHandler:(dispatch_block_t)completionHandler |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)startRunningWithCapturerToken:(SCCapturerToken *)token |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
completionHandler:(dispatch_block_t)completionHandler |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)prepareForRecordingWithResource:(SCCaptureResource *)resource |
||||
|
audioConfiguration:(SCAudioConfiguration *)configuration |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)startRecordingWithResource:(SCCaptureResource *)resource |
||||
|
audioConfiguration:(SCAudioConfiguration *)configuration |
||||
|
outputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings |
||||
|
maxDuration:(NSTimeInterval)maxDuration |
||||
|
fileURL:(NSURL *)fileURL |
||||
|
captureSessionID:(NSString *)captureSessionID |
||||
|
completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)stopRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context; |
||||
|
|
||||
|
- (void)cancelRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context; |
||||
|
|
||||
|
- (void)captureStillImageWithResource:(SCCaptureResource *)resource |
||||
|
aspectRatio:(CGFloat)aspectRatio |
||||
|
captureSessionID:(NSString *)captureSessionID |
||||
|
completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)startScanWithScanConfiguration:(SCScanConfiguration *)configuration |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)stopScanWithCompletionHandler:(dispatch_block_t)completionHandler |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
@property (nonatomic, strong, readonly) SCCaptureStateMachineBookKeeper *bookKeeper; |
||||
|
@end |
@ -0,0 +1,169 @@ |
|||||
|
// |
||||
|
// SCCaptureBaseState.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 10/19/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureBaseState.h" |
||||
|
|
||||
|
#import "SCCaptureStateMachineBookKeeper.h" |
||||
|
#import "SCCapturerToken.h" |
||||
|
#import "SCManagedCapturerV1_Private.h" |
||||
|
|
||||
|
#import <SCFoundation/SCAppEnvironment.h> |
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
#import <SCFoundation/SCQueuePerformer.h> |
||||
|
|
||||
|
@implementation SCCaptureBaseState { |
||||
|
SCCaptureStateMachineBookKeeper *_bookKeeper; |
||||
|
SCQueuePerformer *_performer; |
||||
|
__weak id<SCCaptureStateDelegate> _delegate; |
||||
|
} |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate |
||||
|
{ |
||||
|
self = [super init]; |
||||
|
if (self) { |
||||
|
SCAssert(performer, @""); |
||||
|
SCAssert(bookKeeper, @""); |
||||
|
_bookKeeper = bookKeeper; |
||||
|
_performer = performer; |
||||
|
_delegate = delegate; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (SCCaptureStateMachineStateId)stateId |
||||
|
{ |
||||
|
return SCCaptureBaseStateId; |
||||
|
} |
||||
|
|
||||
|
- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
[self _handleBaseStateBehavior:@"didBecomeCurrentState" context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)initializeCaptureWithDevicePosition:(SCManagedCaptureDevicePosition)devicePosition |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
completionHandler:(dispatch_block_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
[self _handleBaseStateBehavior:@"initializeCaptureWithDevicePosition" context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)startRunningWithCapturerToken:(SCCapturerToken *)token |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
completionHandler:(dispatch_block_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
[self _handleBaseStateBehavior:@"startRunningWithCapturerToken" context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(_performer); |
||||
|
BOOL actuallyStopped = [[SCManagedCapturerV1 sharedInstance] stopRunningWithCaptureToken:token |
||||
|
completionHandler:completionHandler |
||||
|
context:context]; |
||||
|
// TODO: Fix CCAM-14450 |
||||
|
// This is a temporary solution for https://jira.sc-corp.net/browse/CCAM-14450 |
||||
|
// It is caused by switching from scanning state to stop running state when the view is disappearing in the scanning |
||||
|
// state, which can be reproduced by triggering scanning and then switch to maps page. |
||||
|
// We remove SCAssert to ingore the crashes in master branch and will find a solution for the illegal call for the |
||||
|
// state machine later |
||||
|
|
||||
|
if (self.stateId != SCCaptureScanningStateId) { |
||||
|
SCAssert(!actuallyStopped, @"actuallyStopped in state: %@ with context: %@", SCCaptureStateName([self stateId]), |
||||
|
context); |
||||
|
} else { |
||||
|
SCLogCaptureStateMachineInfo(@"actuallyStopped:%d in state: %@ with context: %@", actuallyStopped, |
||||
|
SCCaptureStateName([self stateId]), context); |
||||
|
} |
||||
|
|
||||
|
if (actuallyStopped) { |
||||
|
[_delegate currentState:self |
||||
|
requestToTransferToNewState:SCCaptureInitializedStateId |
||||
|
payload:nil |
||||
|
context:context]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)prepareForRecordingWithResource:(SCCaptureResource *)resource |
||||
|
audioConfiguration:(SCAudioConfiguration *)configuration |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
[self _handleBaseStateBehavior:@"prepareForRecordingWithResource" context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)startRecordingWithResource:(SCCaptureResource *)resource |
||||
|
audioConfiguration:(SCAudioConfiguration *)configuration |
||||
|
outputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings |
||||
|
maxDuration:(NSTimeInterval)maxDuration |
||||
|
fileURL:(NSURL *)fileURL |
||||
|
captureSessionID:(NSString *)captureSessionID |
||||
|
completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
[self _handleBaseStateBehavior:@"startRecordingWithResource" context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)stopRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context |
||||
|
{ |
||||
|
[self _handleBaseStateBehavior:@"stopRecordingWithResource" context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)cancelRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context |
||||
|
{ |
||||
|
[self _handleBaseStateBehavior:@"cancelRecordingWithResource" context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)captureStillImageWithResource:(SCCaptureResource *)resource |
||||
|
aspectRatio:(CGFloat)aspectRatio |
||||
|
captureSessionID:(NSString *)captureSessionID |
||||
|
completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
[self _handleBaseStateBehavior:@"captureStillImageWithResource" context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)startScanWithScanConfiguration:(SCScanConfiguration *)configuration |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
[self _handleBaseStateBehavior:@"startScanWithScanConfiguration" context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)stopScanWithCompletionHandler:(dispatch_block_t)completionHandler |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
// Temporary solution until IDT-12520 is resolved. |
||||
|
[SCCaptureWorker stopScanWithCompletionHandler:completionHandler resource:resource]; |
||||
|
//[self _handleBaseStateBehavior:@"stopScanWithCompletionHandler"]; |
||||
|
} |
||||
|
|
||||
|
- (void)_handleBaseStateBehavior:(NSString *)illegalAPIName context:(NSString *)context |
||||
|
{ |
||||
|
[_bookKeeper state:[self stateId] |
||||
|
illegalAPIcalled:illegalAPIName |
||||
|
callStack:[NSThread callStackSymbols] |
||||
|
context:context]; |
||||
|
if (SCIsDebugBuild()) { |
||||
|
SCAssertFail(@"illegal API invoked on capture state machine"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
{ |
||||
|
return _bookKeeper; |
||||
|
} |
||||
|
@end |
@ -0,0 +1,30 @@ |
|||||
|
// |
||||
|
// SCCaptureStateDelegate.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 10/27/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureStateUtil.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@class SCCaptureBaseState; |
||||
|
@class SCStateTransitionPayload; |
||||
|
/* |
||||
|
The state machine state delegate is used by state machine states to hint to the system that "I am done, now transfer |
||||
|
to other state". |
||||
|
|
||||
|
Currently, SCCaptureStateMachineContext is the central piece that glues all states together, and it is the delegate for |
||||
|
those states. |
||||
|
*/ |
||||
|
|
||||
|
@protocol SCCaptureStateDelegate <NSObject> |
||||
|
|
||||
|
- (void)currentState:(SCCaptureBaseState *)state |
||||
|
requestToTransferToNewState:(SCCaptureStateMachineStateId)newState |
||||
|
payload:(SCStateTransitionPayload *)payload |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
@end |
@ -0,0 +1,29 @@ |
|||||
|
// |
||||
|
// SCCaptureStateTransitionBookKeeper.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 10/27/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureStateUtil.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
/* |
||||
|
Book keeper is used to record every state transition, and every illegal API call. |
||||
|
*/ |
||||
|
|
||||
|
@interface SCCaptureStateMachineBookKeeper : NSObject |
||||
|
|
||||
|
- (void)stateTransitionFrom:(SCCaptureStateMachineStateId)fromId |
||||
|
to:(SCCaptureStateMachineStateId)toId |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)state:(SCCaptureStateMachineStateId)captureState |
||||
|
illegalAPIcalled:(NSString *)illegalAPIName |
||||
|
callStack:(NSArray<NSString *> *)callStack |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)logAPICalled:(NSString *)apiName context:(NSString *)context; |
||||
|
@end |
@ -0,0 +1,63 @@ |
|||||
|
// |
||||
|
// SCCaptureStateTransitionBookKeeper.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 10/27/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureStateMachineBookKeeper.h" |
||||
|
|
||||
|
#import "SCCaptureStateUtil.h" |
||||
|
#import "SCLogger+Camera.h" |
||||
|
|
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
#import <SCLogger/SCCameraMetrics.h> |
||||
|
|
||||
|
@interface SCCaptureStateMachineBookKeeper () { |
||||
|
NSDate *_lastStateStartTime; |
||||
|
} |
||||
|
@end |
||||
|
|
||||
|
@implementation SCCaptureStateMachineBookKeeper |
||||
|
|
||||
|
- (void)stateTransitionFrom:(SCCaptureStateMachineStateId)fromId |
||||
|
to:(SCCaptureStateMachineStateId)toId |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
NSDate *date = [NSDate date]; |
||||
|
SCLogCaptureStateMachineInfo(@"State %@ life span: %f seconds, transition to: %@, in context:%@, at: %@ \n", |
||||
|
SCCaptureStateName(fromId), [date timeIntervalSinceDate:_lastStateStartTime], |
||||
|
SCCaptureStateName(toId), context, date); |
||||
|
_lastStateStartTime = date; |
||||
|
} |
||||
|
|
||||
|
- (void)state:(SCCaptureStateMachineStateId)captureState |
||||
|
illegalAPIcalled:(NSString *)illegalAPIName |
||||
|
callStack:(NSArray<NSString *> *)callStack |
||||
|
context:(NSString *)context |
||||
|
|
||||
|
{ |
||||
|
SCAssert(callStack, @"call stack empty"); |
||||
|
SCAssert(illegalAPIName, @""); |
||||
|
SCAssert(context, @"Context is empty"); |
||||
|
SCLogCaptureStateMachineError(@"State: %@, illegal API invoke: %@, at: %@, callstack: %@ \n", |
||||
|
SCCaptureStateName(captureState), illegalAPIName, [NSDate date], callStack); |
||||
|
NSArray<NSString *> *reportedArray = |
||||
|
[callStack count] > 15 ? [callStack subarrayWithRange:NSMakeRange(0, 15)] : callStack; |
||||
|
[[SCLogger sharedInstance] logEvent:kSCCameraStateMachineIllegalAPICall |
||||
|
parameters:@{ |
||||
|
@"state" : SCCaptureStateName(captureState), |
||||
|
@"API" : illegalAPIName, |
||||
|
@"call_stack" : reportedArray, |
||||
|
@"context" : context |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
- (void)logAPICalled:(NSString *)apiName context:(NSString *)context |
||||
|
{ |
||||
|
SCAssert(apiName, @"API name is empty"); |
||||
|
SCAssert(context, @"Context is empty"); |
||||
|
SCLogCaptureStateMachineInfo(@"api: %@ context: %@", apiName, context); |
||||
|
} |
||||
|
@end |
@ -0,0 +1,76 @@ |
|||||
|
// |
||||
|
// SCCaptureStateMachineContext.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 10/18/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureCommon.h" |
||||
|
#import "SCManagedCaptureDevice.h" |
||||
|
|
||||
|
#import <SCAudio/SCAudioConfiguration.h> |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
/* |
||||
|
SCCaptureStateMachineContext is the central piece that glues all states together. |
||||
|
|
||||
|
It will pass API calls to the current state. |
||||
|
|
||||
|
The classic state machine design pattern: |
||||
|
https://en.wikipedia.org/wiki/State_pattern |
||||
|
|
||||
|
It is also the delegate for the states it manages, so that those states can tell stateMachineContext to transit to next |
||||
|
state. |
||||
|
*/ |
||||
|
|
||||
|
@class SCCaptureResource; |
||||
|
|
||||
|
@class SCCapturerToken; |
||||
|
|
||||
|
@interface SCCaptureStateMachineContext : NSObject |
||||
|
|
||||
|
- (instancetype)initWithResource:(SCCaptureResource *)resource; |
||||
|
|
||||
|
- (void)initializeCaptureWithDevicePositionAsynchronously:(SCManagedCaptureDevicePosition)devicePosition |
||||
|
completionHandler:(dispatch_block_t)completionHandler |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (SCCapturerToken *)startRunningWithContext:(NSString *)context completionHandler:(dispatch_block_t)completionHandler; |
||||
|
|
||||
|
- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token |
||||
|
completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token |
||||
|
after:(NSTimeInterval)delay |
||||
|
completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)prepareForRecordingAsynchronouslyWithAudioConfiguration:(SCAudioConfiguration *)configuration |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
- (void)startRecordingWithOutputSettings:(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; |
||||
|
|
||||
|
- (void)stopRecordingWithContext:(NSString *)context; |
||||
|
|
||||
|
- (void)cancelRecordingWithContext:(NSString *)context; |
||||
|
|
||||
|
- (void)captureStillImageAsynchronouslyWithAspectRatio:(CGFloat)aspectRatio |
||||
|
captureSessionID:(NSString *)captureSessionID |
||||
|
completionHandler: |
||||
|
(sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context; |
||||
|
|
||||
|
#pragma mark - Scanning |
||||
|
- (void)startScanAsynchronouslyWithScanConfiguration:(SCScanConfiguration *)configuration context:(NSString *)context; |
||||
|
- (void)stopScanAsynchronouslyWithCompletionHandler:(dispatch_block_t)completionHandler context:(NSString *)context; |
||||
|
|
||||
|
@end |
@ -0,0 +1,301 @@ |
|||||
|
// |
||||
|
// SCCaptureStateMachineContext.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 10/18/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureStateMachineContext.h" |
||||
|
|
||||
|
#import "SCCaptureBaseState.h" |
||||
|
#import "SCCaptureImageState.h" |
||||
|
#import "SCCaptureImageWhileRecordingState.h" |
||||
|
#import "SCCaptureInitializedState.h" |
||||
|
#import "SCCaptureRecordingState.h" |
||||
|
#import "SCCaptureResource.h" |
||||
|
#import "SCCaptureRunningState.h" |
||||
|
#import "SCCaptureScanningState.h" |
||||
|
#import "SCCaptureStateMachineBookKeeper.h" |
||||
|
#import "SCCaptureStateUtil.h" |
||||
|
#import "SCCaptureUninitializedState.h" |
||||
|
#import "SCCaptureWorker.h" |
||||
|
#import "SCCapturerToken.h" |
||||
|
#import "SCStateTransitionPayload.h" |
||||
|
|
||||
|
#import <SCAudio/SCAudioConfiguration.h> |
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
#import <SCFoundation/SCQueuePerformer.h> |
||||
|
#import <SCFoundation/SCTrace.h> |
||||
|
#import <SCLogger/SCCameraMetrics.h> |
||||
|
#import <SCLogger/SCLogger+Performance.h> |
||||
|
|
||||
|
@interface SCCaptureStateMachineContext () <SCCaptureStateDelegate> { |
||||
|
SCQueuePerformer *_queuePerformer; |
||||
|
|
||||
|
// Cache all the states. |
||||
|
NSMutableDictionary<SCCaptureStateKey *, SCCaptureBaseState *> *_states; |
||||
|
SCCaptureBaseState *_currentState; |
||||
|
SCCaptureStateMachineBookKeeper *_bookKeeper; |
||||
|
SCCaptureResource *_captureResource; |
||||
|
} |
||||
|
@end |
||||
|
|
||||
|
@implementation SCCaptureStateMachineContext |
||||
|
|
||||
|
- (instancetype)initWithResource:(SCCaptureResource *)resource |
||||
|
{ |
||||
|
self = [super init]; |
||||
|
if (self) { |
||||
|
SCAssert(resource, @""); |
||||
|
SCAssert(resource.queuePerformer, @""); |
||||
|
_captureResource = resource; |
||||
|
_queuePerformer = resource.queuePerformer; |
||||
|
_states = [[NSMutableDictionary<SCCaptureStateKey *, SCCaptureBaseState *> alloc] init]; |
||||
|
_bookKeeper = [[SCCaptureStateMachineBookKeeper alloc] init]; |
||||
|
[self _setCurrentState:SCCaptureUninitializedStateId payload:nil context:SCCapturerContext]; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (void)_setCurrentState:(SCCaptureStateMachineStateId)stateId |
||||
|
payload:(SCStateTransitionPayload *)payload |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
switch (stateId) { |
||||
|
case SCCaptureUninitializedStateId: |
||||
|
if (![_states objectForKey:@(stateId)]) { |
||||
|
SCCaptureUninitializedState *uninitializedState = |
||||
|
[[SCCaptureUninitializedState alloc] initWithPerformer:_queuePerformer |
||||
|
bookKeeper:_bookKeeper |
||||
|
delegate:self]; |
||||
|
[_states setObject:uninitializedState forKey:@(stateId)]; |
||||
|
} |
||||
|
_currentState = [_states objectForKey:@(stateId)]; |
||||
|
break; |
||||
|
case SCCaptureInitializedStateId: |
||||
|
if (![_states objectForKey:@(stateId)]) { |
||||
|
SCCaptureInitializedState *initializedState = |
||||
|
[[SCCaptureInitializedState alloc] initWithPerformer:_queuePerformer |
||||
|
bookKeeper:_bookKeeper |
||||
|
delegate:self]; |
||||
|
[_states setObject:initializedState forKey:@(stateId)]; |
||||
|
} |
||||
|
_currentState = [_states objectForKey:@(stateId)]; |
||||
|
break; |
||||
|
case SCCaptureRunningStateId: |
||||
|
if (![_states objectForKey:@(stateId)]) { |
||||
|
SCCaptureRunningState *runningState = |
||||
|
[[SCCaptureRunningState alloc] initWithPerformer:_queuePerformer bookKeeper:_bookKeeper delegate:self]; |
||||
|
[_states setObject:runningState forKey:@(stateId)]; |
||||
|
} |
||||
|
_currentState = [_states objectForKey:@(stateId)]; |
||||
|
break; |
||||
|
case SCCaptureImageStateId: |
||||
|
if (![_states objectForKey:@(stateId)]) { |
||||
|
SCCaptureImageState *captureImageState = |
||||
|
[[SCCaptureImageState alloc] initWithPerformer:_queuePerformer bookKeeper:_bookKeeper delegate:self]; |
||||
|
[_states setObject:captureImageState forKey:@(stateId)]; |
||||
|
} |
||||
|
_currentState = [_states objectForKey:@(stateId)]; |
||||
|
break; |
||||
|
case SCCaptureImageWhileRecordingStateId: |
||||
|
if (![_states objectForKey:@(stateId)]) { |
||||
|
SCCaptureImageWhileRecordingState *captureImageWhileRecordingState = |
||||
|
[[SCCaptureImageWhileRecordingState alloc] initWithPerformer:_queuePerformer |
||||
|
bookKeeper:_bookKeeper |
||||
|
delegate:self]; |
||||
|
[_states setObject:captureImageWhileRecordingState forKey:@(stateId)]; |
||||
|
} |
||||
|
_currentState = [_states objectForKey:@(stateId)]; |
||||
|
break; |
||||
|
case SCCaptureScanningStateId: |
||||
|
if (![_states objectForKey:@(stateId)]) { |
||||
|
SCCaptureScanningState *scanningState = |
||||
|
[[SCCaptureScanningState alloc] initWithPerformer:_queuePerformer bookKeeper:_bookKeeper delegate:self]; |
||||
|
[_states setObject:scanningState forKey:@(stateId)]; |
||||
|
} |
||||
|
_currentState = [_states objectForKey:@(stateId)]; |
||||
|
break; |
||||
|
case SCCaptureRecordingStateId: |
||||
|
if (![_states objectForKey:@(stateId)]) { |
||||
|
SCCaptureRecordingState *recordingState = [[SCCaptureRecordingState alloc] initWithPerformer:_queuePerformer |
||||
|
bookKeeper:_bookKeeper |
||||
|
delegate:self]; |
||||
|
[_states setObject:recordingState forKey:@(stateId)]; |
||||
|
} |
||||
|
_currentState = [_states objectForKey:@(stateId)]; |
||||
|
break; |
||||
|
default: |
||||
|
SCAssert(NO, @"illigal state Id"); |
||||
|
break; |
||||
|
} |
||||
|
[_currentState didBecomeCurrentState:payload resource:_captureResource context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)initializeCaptureWithDevicePositionAsynchronously:(SCManagedCaptureDevicePosition)devicePosition |
||||
|
completionHandler:(dispatch_block_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
[SCCaptureWorker setupCapturePreviewLayerController]; |
||||
|
|
||||
|
SCTraceResumeToken resumeToken = SCTraceCapture(); |
||||
|
[_queuePerformer perform:^{ |
||||
|
SCTraceResume(resumeToken); |
||||
|
[_currentState initializeCaptureWithDevicePosition:devicePosition |
||||
|
resource:_captureResource |
||||
|
completionHandler:completionHandler |
||||
|
context:context]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
- (SCCapturerToken *)startRunningWithContext:(NSString *)context completionHandler:(dispatch_block_t)completionHandler |
||||
|
{ |
||||
|
[[SCLogger sharedInstance] updateLogTimedEventStart:kSCCameraMetricsOpen uniqueId:@""]; |
||||
|
|
||||
|
SCCapturerToken *token = [[SCCapturerToken alloc] initWithIdentifier:context]; |
||||
|
SCTraceResumeToken resumeToken = SCTraceCapture(); |
||||
|
[_queuePerformer perform:^{ |
||||
|
SCTraceResume(resumeToken); |
||||
|
[_currentState startRunningWithCapturerToken:token |
||||
|
resource:_captureResource |
||||
|
completionHandler:completionHandler |
||||
|
context:context]; |
||||
|
}]; |
||||
|
|
||||
|
return token; |
||||
|
} |
||||
|
|
||||
|
- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token |
||||
|
completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCTraceResumeToken resumeToken = SCTraceCapture(); |
||||
|
[_queuePerformer perform:^{ |
||||
|
SCTraceResume(resumeToken); |
||||
|
[_currentState stopRunningWithCapturerToken:token |
||||
|
resource:_captureResource |
||||
|
completionHandler:completionHandler |
||||
|
context:context]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token |
||||
|
after:(NSTimeInterval)delay |
||||
|
completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCTraceResumeToken resumeToken = SCTraceCapture(); |
||||
|
[_queuePerformer perform:^{ |
||||
|
SCTraceResume(resumeToken); |
||||
|
[_currentState stopRunningWithCapturerToken:token |
||||
|
resource:_captureResource |
||||
|
completionHandler:completionHandler |
||||
|
context:context]; |
||||
|
} |
||||
|
after:delay]; |
||||
|
} |
||||
|
|
||||
|
- (void)prepareForRecordingAsynchronouslyWithAudioConfiguration:(SCAudioConfiguration *)configuration |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCTraceResumeToken resumeToken = SCTraceCapture(); |
||||
|
[_queuePerformer perform:^{ |
||||
|
SCTraceResume(resumeToken); |
||||
|
[_currentState prepareForRecordingWithResource:_captureResource |
||||
|
audioConfiguration:configuration |
||||
|
context:context]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
- (void)startRecordingWithOutputSettings:(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 |
||||
|
{ |
||||
|
SCTraceResumeToken resumeToken = SCTraceCapture(); |
||||
|
[_queuePerformer perform:^{ |
||||
|
SCTraceResume(resumeToken); |
||||
|
[_currentState startRecordingWithResource:_captureResource |
||||
|
audioConfiguration:configuration |
||||
|
outputSettings:outputSettings |
||||
|
maxDuration:maxDuration |
||||
|
fileURL:fileURL |
||||
|
captureSessionID:captureSessionID |
||||
|
completionHandler:completionHandler |
||||
|
context:context]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
- (void)stopRecordingWithContext:(NSString *)context |
||||
|
{ |
||||
|
SCTraceResumeToken resumeToken = SCTraceCapture(); |
||||
|
[_queuePerformer perform:^{ |
||||
|
SCTraceResume(resumeToken); |
||||
|
[_currentState stopRecordingWithResource:_captureResource context:context]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
- (void)cancelRecordingWithContext:(NSString *)context |
||||
|
{ |
||||
|
SCTraceResumeToken resumeToken = SCTraceCapture(); |
||||
|
[_queuePerformer perform:^{ |
||||
|
SCTraceResume(resumeToken); |
||||
|
[_currentState cancelRecordingWithResource:_captureResource context:context]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
- (void)captureStillImageAsynchronouslyWithAspectRatio:(CGFloat)aspectRatio |
||||
|
captureSessionID:(NSString *)captureSessionID |
||||
|
completionHandler: |
||||
|
(sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
[_queuePerformer perform:^() { |
||||
|
[_currentState captureStillImageWithResource:_captureResource |
||||
|
aspectRatio:aspectRatio |
||||
|
captureSessionID:captureSessionID |
||||
|
completionHandler:completionHandler |
||||
|
context:context]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
- (void)startScanAsynchronouslyWithScanConfiguration:(SCScanConfiguration *)configuration context:(NSString *)context |
||||
|
{ |
||||
|
[_queuePerformer perform:^() { |
||||
|
[_currentState startScanWithScanConfiguration:configuration resource:_captureResource context:context]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
- (void)stopScanAsynchronouslyWithCompletionHandler:(dispatch_block_t)completionHandler context:(NSString *)context |
||||
|
{ |
||||
|
[_queuePerformer perform:^() { |
||||
|
[_currentState stopScanWithCompletionHandler:completionHandler resource:_captureResource context:context]; |
||||
|
}]; |
||||
|
} |
||||
|
|
||||
|
- (void)currentState:(SCCaptureBaseState *)state |
||||
|
requestToTransferToNewState:(SCCaptureStateMachineStateId)newState |
||||
|
payload:(SCStateTransitionPayload *)payload |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(_queuePerformer); |
||||
|
SCAssert(_currentState == state, @"state: %@ newState: %@ context:%@", SCCaptureStateName([state stateId]), |
||||
|
SCCaptureStateName(newState), context); |
||||
|
if (payload) { |
||||
|
SCAssert(payload.fromState == [state stateId], @"From state id check"); |
||||
|
SCAssert(payload.toState == newState, @"To state id check"); |
||||
|
} |
||||
|
|
||||
|
if (_currentState != state) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
[_bookKeeper stateTransitionFrom:[state stateId] to:newState context:context]; |
||||
|
[self _setCurrentState:newState payload:payload context:context]; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,37 @@ |
|||||
|
// |
||||
|
// SCCaptureStateUtil.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 10/27/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#import "SCLogger+Camera.h" |
||||
|
|
||||
|
#import <SCBase/SCMacros.h> |
||||
|
#import <SCFoundation/SCLog.h> |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
#define SCLogCaptureStateMachineInfo(fmt, ...) SCLogCoreCameraInfo(@"[SCCaptureStateMachine] " fmt, ##__VA_ARGS__) |
||||
|
#define SCLogCaptureStateMachineError(fmt, ...) SCLogCoreCameraError(@"[SCCaptureStateMachine] " fmt, ##__VA_ARGS__) |
||||
|
|
||||
|
typedef NSNumber SCCaptureStateKey; |
||||
|
|
||||
|
typedef NS_ENUM(NSUInteger, SCCaptureStateMachineStateId) { |
||||
|
SCCaptureBaseStateId = 0, |
||||
|
SCCaptureUninitializedStateId, |
||||
|
SCCaptureInitializedStateId, |
||||
|
SCCaptureImageStateId, |
||||
|
SCCaptureImageWhileRecordingStateId, |
||||
|
SCCaptureRunningStateId, |
||||
|
SCCaptureRecordingStateId, |
||||
|
SCCaptureScanningStateId, |
||||
|
SCCaptureStateMachineStateIdCount |
||||
|
}; |
||||
|
|
||||
|
SC_EXTERN_C_BEGIN |
||||
|
|
||||
|
NSString *SCCaptureStateName(SCCaptureStateMachineStateId stateId); |
||||
|
|
||||
|
SC_EXTERN_C_END |
@ -0,0 +1,38 @@ |
|||||
|
// |
||||
|
// SCCaptureStateUtil.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 10/27/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureStateUtil.h" |
||||
|
|
||||
|
#import <SCFoundation/SCAppEnvironment.h> |
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
|
||||
|
NSString *SCCaptureStateName(SCCaptureStateMachineStateId stateId) |
||||
|
{ |
||||
|
switch (stateId) { |
||||
|
case SCCaptureBaseStateId: |
||||
|
return @"SCCaptureBaseStateId"; |
||||
|
case SCCaptureUninitializedStateId: |
||||
|
return @"SCCaptureUninitializedStateId"; |
||||
|
case SCCaptureInitializedStateId: |
||||
|
return @"SCCaptureInitializedStateId"; |
||||
|
case SCCaptureImageStateId: |
||||
|
return @"SCCaptureImageStateId"; |
||||
|
case SCCaptureImageWhileRecordingStateId: |
||||
|
return @"SCCaptureImageWhileRecordingStateId"; |
||||
|
case SCCaptureRunningStateId: |
||||
|
return @"SCCaptureRunningStateId"; |
||||
|
case SCCaptureRecordingStateId: |
||||
|
return @"SCCaptureRecordingStateId"; |
||||
|
case SCCaptureScanningStateId: |
||||
|
return @"SCCaptureScanningStateId"; |
||||
|
default: |
||||
|
SCCAssert(NO, @"illegate state id"); |
||||
|
break; |
||||
|
} |
||||
|
return @"SCIllegalStateId"; |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
// |
||||
|
// SCManagedCapturerLogging.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 11/13/17. |
||||
|
// |
||||
|
|
||||
|
#import <SCFoundation/SCLog.h> |
||||
|
|
||||
|
#define SCLogCapturerInfo(fmt, ...) SCLogCoreCameraInfo(@"[SCManagedCapturer] " fmt, ##__VA_ARGS__) |
||||
|
#define SCLogCapturerWarning(fmt, ...) SCLogCoreCameraWarning(@"[SCManagedCapturer] " fmt, ##__VA_ARGS__) |
||||
|
#define SCLogCapturerError(fmt, ...) SCLogCoreCameraError(@"[SCManagedCapturer] " fmt, ##__VA_ARGS__) |
@ -0,0 +1,22 @@ |
|||||
|
// |
||||
|
// SCCaptureImageState.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 1/8/18. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureBaseState.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@class SCQueuePerformer; |
||||
|
|
||||
|
@interface SCCaptureImageState : SCCaptureBaseState |
||||
|
|
||||
|
SC_INIT_AND_NEW_UNAVAILABLE |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate; |
||||
|
|
||||
|
@end |
@ -0,0 +1,65 @@ |
|||||
|
// |
||||
|
// SCCaptureImageState.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 1/8/18. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureImageState.h" |
||||
|
|
||||
|
#import "SCCaptureImageStateTransitionPayload.h" |
||||
|
#import "SCManagedCapturerV1_Private.h" |
||||
|
#import "SCStateTransitionPayload.h" |
||||
|
|
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
#import <SCFoundation/SCQueuePerformer.h> |
||||
|
|
||||
|
@interface SCCaptureImageState () { |
||||
|
__weak id<SCCaptureStateDelegate> _delegate; |
||||
|
SCQueuePerformer *_performer; |
||||
|
} |
||||
|
@end |
||||
|
|
||||
|
@implementation SCCaptureImageState |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate |
||||
|
{ |
||||
|
self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate]; |
||||
|
if (self) { |
||||
|
_delegate = delegate; |
||||
|
_performer = performer; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(_performer); |
||||
|
SCAssert(payload.toState == [self stateId], @""); |
||||
|
if (![payload isKindOfClass:[SCCaptureImageStateTransitionPayload class]]) { |
||||
|
SCAssertFail(@"wrong payload pass in"); |
||||
|
[_delegate currentState:self requestToTransferToNewState:payload.fromState payload:nil context:context]; |
||||
|
return; |
||||
|
} |
||||
|
SCCaptureImageStateTransitionPayload *captureImagePayload = (SCCaptureImageStateTransitionPayload *)payload; |
||||
|
|
||||
|
[SCCaptureWorker |
||||
|
captureStillImageWithCaptureResource:resource |
||||
|
aspectRatio:captureImagePayload.aspectRatio |
||||
|
captureSessionID:captureImagePayload.captureSessionID |
||||
|
shouldCaptureFromVideo:[SCCaptureWorker shouldCaptureImageFromVideoWithResource:resource] |
||||
|
completionHandler:captureImagePayload.block |
||||
|
context:context]; |
||||
|
|
||||
|
[_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context]; |
||||
|
} |
||||
|
|
||||
|
- (SCCaptureStateMachineStateId)stateId |
||||
|
{ |
||||
|
return SCCaptureImageStateId; |
||||
|
} |
||||
|
@end |
@ -0,0 +1,29 @@ |
|||||
|
// |
||||
|
// SCCaptureImageStateTransitionPayload.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 1/9/18. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureCommon.h" |
||||
|
#import "SCStateTransitionPayload.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@interface SCCaptureImageStateTransitionPayload : SCStateTransitionPayload |
||||
|
|
||||
|
@property (nonatomic, readonly, strong) NSString *captureSessionID; |
||||
|
|
||||
|
@property (nonatomic, readonly, copy) sc_managed_capturer_capture_still_image_completion_handler_t block; |
||||
|
|
||||
|
@property (nonatomic, readonly, assign) CGFloat aspectRatio; |
||||
|
|
||||
|
SC_INIT_AND_NEW_UNAVAILABLE |
||||
|
|
||||
|
- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState |
||||
|
toState:(SCCaptureStateMachineStateId)toState |
||||
|
captureSessionId:(NSString *)captureSessionID |
||||
|
aspectRatio:(CGFloat)aspectRatio |
||||
|
completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)block; |
||||
|
|
||||
|
@end |
@ -0,0 +1,27 @@ |
|||||
|
// |
||||
|
// SCCaptureImageStateTransitionPayload.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 1/9/18. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureImageStateTransitionPayload.h" |
||||
|
|
||||
|
@implementation SCCaptureImageStateTransitionPayload |
||||
|
|
||||
|
- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState |
||||
|
toState:(SCCaptureStateMachineStateId)toState |
||||
|
captureSessionId:(NSString *)captureSessionID |
||||
|
aspectRatio:(CGFloat)aspectRatio |
||||
|
completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)block |
||||
|
{ |
||||
|
self = [super initWithFromState:fromState toState:toState]; |
||||
|
if (self) { |
||||
|
_captureSessionID = captureSessionID; |
||||
|
_aspectRatio = aspectRatio; |
||||
|
_block = block; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,22 @@ |
|||||
|
// |
||||
|
// SCCaptureImageWhileRecordingState.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Sun Lei on 22/02/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureBaseState.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@class SCQueuePerformer; |
||||
|
|
||||
|
@interface SCCaptureImageWhileRecordingState : SCCaptureBaseState |
||||
|
|
||||
|
SC_INIT_AND_NEW_UNAVAILABLE |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate; |
||||
|
|
||||
|
@end |
@ -0,0 +1,85 @@ |
|||||
|
// |
||||
|
// SCCaptureImageWhileRecordingState.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Sun Lei on 22/02/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureImageWhileRecordingState.h" |
||||
|
|
||||
|
#import "SCCaptureImageWhileRecordingStateTransitionPayload.h" |
||||
|
#import "SCManagedCapturerV1_Private.h" |
||||
|
|
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
#import <SCFoundation/SCQueuePerformer.h> |
||||
|
|
||||
|
@interface SCCaptureImageWhileRecordingState () { |
||||
|
__weak id<SCCaptureStateDelegate> _delegate; |
||||
|
SCQueuePerformer *_performer; |
||||
|
} |
||||
|
@end |
||||
|
|
||||
|
@implementation SCCaptureImageWhileRecordingState |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate |
||||
|
{ |
||||
|
self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate]; |
||||
|
if (self) { |
||||
|
_delegate = delegate; |
||||
|
_performer = performer; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (SCCaptureStateMachineStateId)stateId |
||||
|
{ |
||||
|
return SCCaptureImageWhileRecordingStateId; |
||||
|
} |
||||
|
|
||||
|
- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(_performer); |
||||
|
SCAssert(payload.fromState == SCCaptureRecordingStateId, @""); |
||||
|
SCAssert(payload.toState == [self stateId], @""); |
||||
|
SCAssert([payload isKindOfClass:[SCCaptureImageWhileRecordingStateTransitionPayload class]], @""); |
||||
|
; |
||||
|
SCCaptureImageWhileRecordingStateTransitionPayload *captureImagePayload = |
||||
|
(SCCaptureImageWhileRecordingStateTransitionPayload *)payload; |
||||
|
|
||||
|
@weakify(self); |
||||
|
sc_managed_capturer_capture_still_image_completion_handler_t block = |
||||
|
^(UIImage *fullScreenImage, NSDictionary *metadata, NSError *error, SCManagedCapturerState *state) { |
||||
|
captureImagePayload.block(fullScreenImage, metadata, error, state); |
||||
|
[_performer perform:^{ |
||||
|
@strongify(self); |
||||
|
[self _cancelRecordingWithContext:context resource:resource]; |
||||
|
}]; |
||||
|
}; |
||||
|
|
||||
|
[SCCaptureWorker |
||||
|
captureStillImageWithCaptureResource:resource |
||||
|
aspectRatio:captureImagePayload.aspectRatio |
||||
|
captureSessionID:captureImagePayload.captureSessionID |
||||
|
shouldCaptureFromVideo:[SCCaptureWorker shouldCaptureImageFromVideoWithResource:resource] |
||||
|
completionHandler:block |
||||
|
context:context]; |
||||
|
|
||||
|
[_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)_cancelRecordingWithContext:(NSString *)context resource:(SCCaptureResource *)resource |
||||
|
{ |
||||
|
SCTraceODPCompatibleStart(2); |
||||
|
SCAssertPerformer(_performer); |
||||
|
|
||||
|
[SCCaptureWorker cancelRecordingWithCaptureResource:resource]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
@end |
@ -0,0 +1,29 @@ |
|||||
|
// |
||||
|
// SCCaptureImageWhileRecordingStateTransitionPayload.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Sun Lei on 22/02/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureCommon.h" |
||||
|
#import "SCStateTransitionPayload.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@interface SCCaptureImageWhileRecordingStateTransitionPayload : SCStateTransitionPayload |
||||
|
|
||||
|
@property (nonatomic, readonly, strong) NSString *captureSessionID; |
||||
|
|
||||
|
@property (nonatomic, readonly, copy) sc_managed_capturer_capture_still_image_completion_handler_t block; |
||||
|
|
||||
|
@property (nonatomic, readonly, assign) CGFloat aspectRatio; |
||||
|
|
||||
|
SC_INIT_AND_NEW_UNAVAILABLE |
||||
|
|
||||
|
- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState |
||||
|
toState:(SCCaptureStateMachineStateId)toState |
||||
|
captureSessionId:(NSString *)captureSessionID |
||||
|
aspectRatio:(CGFloat)aspectRatio |
||||
|
completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)block; |
||||
|
|
||||
|
@end |
@ -0,0 +1,27 @@ |
|||||
|
// |
||||
|
// SCCaptureImageWhileRecordingStateTransitionPayload.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Sun Lei on 22/02/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureImageWhileRecordingStateTransitionPayload.h" |
||||
|
|
||||
|
@implementation SCCaptureImageWhileRecordingStateTransitionPayload |
||||
|
|
||||
|
- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState |
||||
|
toState:(SCCaptureStateMachineStateId)toState |
||||
|
captureSessionId:(NSString *)captureSessionID |
||||
|
aspectRatio:(CGFloat)aspectRatio |
||||
|
completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)block |
||||
|
{ |
||||
|
self = [super initWithFromState:fromState toState:toState]; |
||||
|
if (self) { |
||||
|
_captureSessionID = captureSessionID; |
||||
|
_aspectRatio = aspectRatio; |
||||
|
_block = block; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,22 @@ |
|||||
|
// |
||||
|
// SCCaptureInitializedState.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Jingtian Yang on 20/12/2017. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureBaseState.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@class SCQueuePerformer; |
||||
|
|
||||
|
@interface SCCaptureInitializedState : SCCaptureBaseState |
||||
|
|
||||
|
- (instancetype)init NS_UNAVAILABLE; |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate; |
||||
|
|
||||
|
@end |
@ -0,0 +1,68 @@ |
|||||
|
// |
||||
|
// SCCaptureInitializedState.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Jingtian Yang on 20/12/2017. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureInitializedState.h" |
||||
|
|
||||
|
#import "SCCapturerToken.h" |
||||
|
#import "SCManagedCapturerLogging.h" |
||||
|
#import "SCManagedCapturerV1_Private.h" |
||||
|
|
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
#import <SCFoundation/SCQueuePerformer.h> |
||||
|
|
||||
|
@interface SCCaptureInitializedState () { |
||||
|
__weak id<SCCaptureStateDelegate> _delegate; |
||||
|
SCQueuePerformer *_performer; |
||||
|
} |
||||
|
|
||||
|
@end |
||||
|
|
||||
|
@implementation SCCaptureInitializedState |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate |
||||
|
{ |
||||
|
self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate]; |
||||
|
if (self) { |
||||
|
_delegate = delegate; |
||||
|
_performer = performer; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
// No op. |
||||
|
} |
||||
|
|
||||
|
- (SCCaptureStateMachineStateId)stateId |
||||
|
{ |
||||
|
return SCCaptureInitializedStateId; |
||||
|
} |
||||
|
|
||||
|
- (void)startRunningWithCapturerToken:(SCCapturerToken *)token |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
completionHandler:(dispatch_block_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(_performer); |
||||
|
SCTraceODPCompatibleStart(2); |
||||
|
SCLogCapturerInfo(@"startRunningAsynchronouslyWithCompletionHandler called. token: %@", token); |
||||
|
|
||||
|
[SCCaptureWorker startRunningWithCaptureResource:resource token:token completionHandler:completionHandler]; |
||||
|
|
||||
|
[_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,22 @@ |
|||||
|
// |
||||
|
// SCCaptureRecordingState.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Jingtian Yang on 12/01/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureBaseState.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@class SCQueuePerformer; |
||||
|
|
||||
|
@interface SCCaptureRecordingState : SCCaptureBaseState |
||||
|
|
||||
|
SC_INIT_AND_NEW_UNAVAILABLE |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate; |
||||
|
|
||||
|
@end |
@ -0,0 +1,114 @@ |
|||||
|
// |
||||
|
// SCCaptureRecordingState.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Jingtian Yang on 12/01/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureRecordingState.h" |
||||
|
|
||||
|
#import "SCCaptureImageWhileRecordingStateTransitionPayload.h" |
||||
|
#import "SCCaptureRecordingStateTransitionPayload.h" |
||||
|
#import "SCManagedCapturerV1_Private.h" |
||||
|
#import "SCStateTransitionPayload.h" |
||||
|
|
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
#import <SCFoundation/SCQueuePerformer.h> |
||||
|
|
||||
|
@interface SCCaptureRecordingState () { |
||||
|
__weak id<SCCaptureStateDelegate> _delegate; |
||||
|
SCQueuePerformer *_performer; |
||||
|
} |
||||
|
@end |
||||
|
|
||||
|
@implementation SCCaptureRecordingState |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate |
||||
|
{ |
||||
|
self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate]; |
||||
|
if (self) { |
||||
|
_delegate = delegate; |
||||
|
_performer = performer; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(resource.queuePerformer); |
||||
|
SCAssert(payload.toState == [self stateId], @""); |
||||
|
if (![payload isKindOfClass:[SCCaptureRecordingStateTransitionPayload class]]) { |
||||
|
SCAssertFail(@"wrong payload pass in"); |
||||
|
[_delegate currentState:self requestToTransferToNewState:payload.fromState payload:nil context:context]; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
SCCaptureRecordingStateTransitionPayload *recordingPayload = (SCCaptureRecordingStateTransitionPayload *)payload; |
||||
|
[SCCaptureWorker startRecordingWithCaptureResource:resource |
||||
|
outputSettings:recordingPayload.outputSettings |
||||
|
audioConfiguration:recordingPayload.configuration |
||||
|
maxDuration:recordingPayload.maxDuration |
||||
|
fileURL:recordingPayload.fileURL |
||||
|
captureSessionID:recordingPayload.captureSessionID |
||||
|
completionHandler:recordingPayload.block]; |
||||
|
} |
||||
|
|
||||
|
- (void)stopRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context |
||||
|
{ |
||||
|
SCTraceODPCompatibleStart(2); |
||||
|
SCAssertPerformer(_performer); |
||||
|
|
||||
|
[SCCaptureWorker stopRecordingWithCaptureResource:resource]; |
||||
|
[_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)cancelRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context |
||||
|
{ |
||||
|
SCTraceODPCompatibleStart(2); |
||||
|
SCAssertPerformer(_performer); |
||||
|
|
||||
|
[SCCaptureWorker cancelRecordingWithCaptureResource:resource]; |
||||
|
[_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
- (SCCaptureStateMachineStateId)stateId |
||||
|
{ |
||||
|
return SCCaptureRecordingStateId; |
||||
|
} |
||||
|
|
||||
|
- (void)captureStillImageWithResource:(SCCaptureResource *)resource |
||||
|
aspectRatio:(CGFloat)aspectRatio |
||||
|
captureSessionID:(NSString *)captureSessionID |
||||
|
completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(_performer); |
||||
|
SCCaptureImageWhileRecordingStateTransitionPayload *payload = [ |
||||
|
[SCCaptureImageWhileRecordingStateTransitionPayload alloc] initWithFromState:SCCaptureRecordingStateId |
||||
|
toState:SCCaptureImageWhileRecordingStateId |
||||
|
captureSessionId:captureSessionID |
||||
|
aspectRatio:aspectRatio |
||||
|
completionHandler:completionHandler]; |
||||
|
[_delegate currentState:self |
||||
|
requestToTransferToNewState:SCCaptureImageWhileRecordingStateId |
||||
|
payload:payload |
||||
|
context:context]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,41 @@ |
|||||
|
// |
||||
|
// SCCaptureRecordingStateTransitionPayload.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Jingtian Yang on 12/01/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureCommon.h" |
||||
|
#import "SCManagedVideoCapturerOutputSettings.h" |
||||
|
#import "SCStateTransitionPayload.h" |
||||
|
|
||||
|
#import <SCAudio/SCAudioConfiguration.h> |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@interface SCCaptureRecordingStateTransitionPayload : SCStateTransitionPayload |
||||
|
|
||||
|
@property (nonatomic, readonly, strong) SCManagedVideoCapturerOutputSettings *outputSettings; |
||||
|
|
||||
|
@property (nonatomic, readonly, strong) SCAudioConfiguration *configuration; |
||||
|
|
||||
|
@property (nonatomic, readonly, assign) NSTimeInterval maxDuration; |
||||
|
|
||||
|
@property (nonatomic, readonly, strong) NSURL *fileURL; |
||||
|
|
||||
|
@property (nonatomic, readonly, strong) NSString *captureSessionID; |
||||
|
|
||||
|
@property (nonatomic, readonly, copy) sc_managed_capturer_start_recording_completion_handler_t block; |
||||
|
|
||||
|
SC_INIT_AND_NEW_UNAVAILABLE |
||||
|
|
||||
|
- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState |
||||
|
toState:(SCCaptureStateMachineStateId)toState |
||||
|
outputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings |
||||
|
audioConfiguration:(SCAudioConfiguration *)configuration |
||||
|
maxDuration:(NSTimeInterval)maxDuration |
||||
|
fileURL:(NSURL *)fileURL |
||||
|
captureSessionID:(NSString *)captureSessionID |
||||
|
completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)block; |
||||
|
|
||||
|
@end |
@ -0,0 +1,33 @@ |
|||||
|
// |
||||
|
// SCCaptureRecordingStateTransitionPayload.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Jingtian Yang on 12/01/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureRecordingStateTransitionPayload.h" |
||||
|
|
||||
|
@implementation SCCaptureRecordingStateTransitionPayload |
||||
|
|
||||
|
- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState |
||||
|
toState:(SCCaptureStateMachineStateId)toState |
||||
|
outputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings |
||||
|
audioConfiguration:configuration |
||||
|
maxDuration:(NSTimeInterval)maxDuration |
||||
|
fileURL:(NSURL *)fileURL |
||||
|
captureSessionID:(NSString *)captureSessionID |
||||
|
completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)block |
||||
|
{ |
||||
|
self = [super initWithFromState:fromState toState:toState]; |
||||
|
if (self) { |
||||
|
_outputSettings = outputSettings; |
||||
|
_configuration = configuration; |
||||
|
_maxDuration = maxDuration; |
||||
|
_fileURL = fileURL; |
||||
|
_captureSessionID = captureSessionID; |
||||
|
_block = block; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,22 @@ |
|||||
|
// |
||||
|
// SCCaptureRunningState.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Jingtian Yang on 08/01/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureBaseState.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@class SCQueuePerformer; |
||||
|
|
||||
|
@interface SCCaptureRunningState : SCCaptureBaseState |
||||
|
|
||||
|
- (instancetype)init NS_UNAVAILABLE; |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate; |
||||
|
|
||||
|
@end |
@ -0,0 +1,176 @@ |
|||||
|
// |
||||
|
// SCCaptureRunningState.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Jingtian Yang on 08/01/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureRunningState.h" |
||||
|
|
||||
|
#import "SCCaptureImageStateTransitionPayload.h" |
||||
|
#import "SCCaptureRecordingStateTransitionPayload.h" |
||||
|
#import "SCCaptureWorker.h" |
||||
|
#import "SCManagedCapturerLogging.h" |
||||
|
#import "SCManagedCapturerV1_Private.h" |
||||
|
#import "SCScanConfiguration.h" |
||||
|
|
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
#import <SCFoundation/SCQueuePerformer.h> |
||||
|
#import <SCFoundation/SCTraceODPCompatible.h> |
||||
|
|
||||
|
@interface SCCaptureRunningState () { |
||||
|
__weak id<SCCaptureStateDelegate> _delegate; |
||||
|
SCQueuePerformer *_performer; |
||||
|
} |
||||
|
|
||||
|
@end |
||||
|
|
||||
|
@implementation SCCaptureRunningState |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate |
||||
|
{ |
||||
|
self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate]; |
||||
|
if (self) { |
||||
|
_delegate = delegate; |
||||
|
_performer = performer; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
// No op. |
||||
|
} |
||||
|
|
||||
|
- (void)captureStillImageWithResource:(SCCaptureResource *)resource |
||||
|
aspectRatio:(CGFloat)aspectRatio |
||||
|
captureSessionID:(NSString *)captureSessionID |
||||
|
completionHandler:(sc_managed_capturer_capture_still_image_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(_performer); |
||||
|
SCCaptureImageStateTransitionPayload *payload = |
||||
|
[[SCCaptureImageStateTransitionPayload alloc] initWithFromState:SCCaptureRunningStateId |
||||
|
toState:SCCaptureImageStateId |
||||
|
captureSessionId:captureSessionID |
||||
|
aspectRatio:aspectRatio |
||||
|
completionHandler:completionHandler]; |
||||
|
[_delegate currentState:self requestToTransferToNewState:SCCaptureImageStateId payload:payload context:context]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
- (SCCaptureStateMachineStateId)stateId |
||||
|
{ |
||||
|
return SCCaptureRunningStateId; |
||||
|
} |
||||
|
|
||||
|
- (void)startRunningWithCapturerToken:(SCCapturerToken *)token |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
completionHandler:(dispatch_block_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(_performer); |
||||
|
SCTraceODPCompatibleStart(2); |
||||
|
SCLogCapturerInfo(@"startRunningAsynchronouslyWithCompletionHandler called. token: %@", token); |
||||
|
[SCCaptureWorker startRunningWithCaptureResource:resource token:token completionHandler:completionHandler]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)stopRunningWithCapturerToken:(SCCapturerToken *)token |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
completionHandler:(sc_managed_capturer_stop_running_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCTraceODPCompatibleStart(2); |
||||
|
SCAssertPerformer(_performer); |
||||
|
|
||||
|
SCLogCapturerInfo(@"Stop running asynchronously. token:%@", token); |
||||
|
if ([[SCManagedCapturerV1 sharedInstance] stopRunningWithCaptureToken:token |
||||
|
completionHandler:completionHandler |
||||
|
context:context]) { |
||||
|
[_delegate currentState:self |
||||
|
requestToTransferToNewState:SCCaptureInitializedStateId |
||||
|
payload:nil |
||||
|
context:context]; |
||||
|
} |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)startScanWithScanConfiguration:(SCScanConfiguration *)configuration |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCTraceODPCompatibleStart(2); |
||||
|
SCLogCapturerInfo(@"Start scan on preview asynchronously. configuration:%@", configuration); |
||||
|
SCAssertPerformer(_performer); |
||||
|
[SCCaptureWorker startScanWithScanConfiguration:configuration resource:resource]; |
||||
|
[_delegate currentState:self requestToTransferToNewState:SCCaptureScanningStateId payload:nil context:context]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)prepareForRecordingWithResource:(SCCaptureResource *)resource |
||||
|
audioConfiguration:(SCAudioConfiguration *)configuration |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(_performer); |
||||
|
SCTraceODPCompatibleStart(2); |
||||
|
[SCCaptureWorker prepareForRecordingWithAudioConfiguration:configuration resource:resource]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)startRecordingWithResource:(SCCaptureResource *)resource |
||||
|
audioConfiguration:(SCAudioConfiguration *)configuration |
||||
|
outputSettings:(SCManagedVideoCapturerOutputSettings *)outputSettings |
||||
|
maxDuration:(NSTimeInterval)maxDuration |
||||
|
fileURL:(NSURL *)fileURL |
||||
|
captureSessionID:(NSString *)captureSessionID |
||||
|
completionHandler:(sc_managed_capturer_start_recording_completion_handler_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCTraceODPCompatibleStart(2); |
||||
|
SCAssertPerformer(_performer); |
||||
|
|
||||
|
SCCaptureRecordingStateTransitionPayload *payload = |
||||
|
[[SCCaptureRecordingStateTransitionPayload alloc] initWithFromState:SCCaptureRunningStateId |
||||
|
toState:SCCaptureRecordingStateId |
||||
|
outputSettings:outputSettings |
||||
|
audioConfiguration:configuration |
||||
|
maxDuration:maxDuration |
||||
|
fileURL:fileURL |
||||
|
captureSessionID:captureSessionID |
||||
|
completionHandler:completionHandler]; |
||||
|
[_delegate currentState:self requestToTransferToNewState:SCCaptureRecordingStateId payload:payload context:context]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)cancelRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context |
||||
|
{ |
||||
|
// Intentionally No Op, this will be removed once CCAM-13851 gets resolved. |
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,18 @@ |
|||||
|
// |
||||
|
// SCCaptureScanningState.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Xiaokang Liu on 09/01/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureBaseState.h" |
||||
|
|
||||
|
@class SCQueuePerformer; |
||||
|
|
||||
|
@interface SCCaptureScanningState : SCCaptureBaseState |
||||
|
- (instancetype)init NS_UNAVAILABLE; |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate; |
||||
|
@end |
@ -0,0 +1,75 @@ |
|||||
|
// |
||||
|
// SCCaptureScanningState.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Xiaokang Liu on 09/01/2018. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureScanningState.h" |
||||
|
|
||||
|
#import "SCManagedCapturerLogging.h" |
||||
|
#import "SCManagedCapturerV1_Private.h" |
||||
|
|
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
#import <SCFoundation/SCQueuePerformer.h> |
||||
|
#import <SCFoundation/SCTraceODPCompatible.h> |
||||
|
|
||||
|
@interface SCCaptureScanningState () { |
||||
|
__weak id<SCCaptureStateDelegate> _delegate; |
||||
|
SCQueuePerformer *_performer; |
||||
|
} |
||||
|
|
||||
|
@end |
||||
|
|
||||
|
@implementation SCCaptureScanningState |
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate |
||||
|
{ |
||||
|
self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate]; |
||||
|
if (self) { |
||||
|
SCAssert(delegate, @""); |
||||
|
SCAssert(performer, @""); |
||||
|
SCAssert(bookKeeper, @""); |
||||
|
_delegate = delegate; |
||||
|
_performer = performer; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
// No op. |
||||
|
} |
||||
|
|
||||
|
- (SCCaptureStateMachineStateId)stateId |
||||
|
{ |
||||
|
return SCCaptureScanningStateId; |
||||
|
} |
||||
|
|
||||
|
- (void)stopScanWithCompletionHandler:(dispatch_block_t)completionHandler |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(_performer); |
||||
|
SCTraceODPCompatibleStart(2); |
||||
|
SCLogCapturerInfo(@"stop scan asynchronously."); |
||||
|
[SCCaptureWorker stopScanWithCompletionHandler:completionHandler resource:resource]; |
||||
|
[_delegate currentState:self requestToTransferToNewState:SCCaptureRunningStateId payload:nil context:context]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
- (void)cancelRecordingWithResource:(SCCaptureResource *)resource context:(NSString *)context |
||||
|
{ |
||||
|
// Intentionally No Op, this will be removed once CCAM-13851 gets resolved. |
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,26 @@ |
|||||
|
// |
||||
|
// SCCaptureUninitializedState.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 10/19/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureBaseState.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
/* |
||||
|
State which handles capture initialialization, which should be used only once for every app life span. |
||||
|
*/ |
||||
|
@class SCQueuePerformer; |
||||
|
|
||||
|
@interface SCCaptureUninitializedState : SCCaptureBaseState |
||||
|
|
||||
|
- (instancetype)init NS_UNAVAILABLE; |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate; |
||||
|
|
||||
|
@end |
@ -0,0 +1,70 @@ |
|||||
|
// |
||||
|
// SCCaptureUninitializedState.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 10/19/17. |
||||
|
// |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureUninitializedState.h" |
||||
|
|
||||
|
#import "SCManagedCapturerLogging.h" |
||||
|
#import "SCManagedCapturerV1_Private.h" |
||||
|
|
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
#import <SCFoundation/SCQueuePerformer.h> |
||||
|
#import <SCFoundation/SCTraceODPCompatible.h> |
||||
|
|
||||
|
@interface SCCaptureUninitializedState () { |
||||
|
__weak id<SCCaptureStateDelegate> _delegate; |
||||
|
SCQueuePerformer *_performer; |
||||
|
} |
||||
|
|
||||
|
@end |
||||
|
|
||||
|
@implementation SCCaptureUninitializedState |
||||
|
|
||||
|
- (instancetype)initWithPerformer:(SCQueuePerformer *)performer |
||||
|
bookKeeper:(SCCaptureStateMachineBookKeeper *)bookKeeper |
||||
|
delegate:(id<SCCaptureStateDelegate>)delegate |
||||
|
{ |
||||
|
self = [super initWithPerformer:performer bookKeeper:bookKeeper delegate:delegate]; |
||||
|
if (self) { |
||||
|
_delegate = delegate; |
||||
|
_performer = performer; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (void)didBecomeCurrentState:(SCStateTransitionPayload *)payload |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
// No op. |
||||
|
} |
||||
|
|
||||
|
- (SCCaptureStateMachineStateId)stateId |
||||
|
{ |
||||
|
return SCCaptureUninitializedStateId; |
||||
|
} |
||||
|
|
||||
|
- (void)initializeCaptureWithDevicePosition:(SCManagedCaptureDevicePosition)devicePosition |
||||
|
resource:(SCCaptureResource *)resource |
||||
|
completionHandler:(dispatch_block_t)completionHandler |
||||
|
context:(NSString *)context |
||||
|
{ |
||||
|
SCAssertPerformer(_performer); |
||||
|
SCTraceODPCompatibleStart(2); |
||||
|
SCLogCapturerInfo(@"Setting up with devicePosition:%lu", (unsigned long)devicePosition); |
||||
|
|
||||
|
// TODO: we need to push completionHandler to a payload and let intializedState handle. |
||||
|
[[SCManagedCapturerV1 sharedInstance] setupWithDevicePosition:devicePosition completionHandler:completionHandler]; |
||||
|
|
||||
|
[_delegate currentState:self requestToTransferToNewState:SCCaptureInitializedStateId payload:nil context:context]; |
||||
|
|
||||
|
NSString *apiName = |
||||
|
[NSString sc_stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; |
||||
|
[self.bookKeeper logAPICalled:apiName context:context]; |
||||
|
} |
||||
|
|
||||
|
@end |
@ -0,0 +1,22 @@ |
|||||
|
// |
||||
|
// SCStateTransitionPayload.h |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 1/8/18. |
||||
|
// |
||||
|
|
||||
|
#import "SCCaptureStateUtil.h" |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
@interface SCStateTransitionPayload : NSObject |
||||
|
|
||||
|
@property (nonatomic, readonly, assign) SCCaptureStateMachineStateId fromState; |
||||
|
|
||||
|
@property (nonatomic, readonly, assign) SCCaptureStateMachineStateId toState; |
||||
|
|
||||
|
SC_INIT_AND_NEW_UNAVAILABLE |
||||
|
|
||||
|
- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState toState:(SCCaptureStateMachineStateId)toState; |
||||
|
|
||||
|
@end |
@ -0,0 +1,27 @@ |
|||||
|
// |
||||
|
// SCStateTransitionPayload.m |
||||
|
// Snapchat |
||||
|
// |
||||
|
// Created by Lin Jia on 1/8/18. |
||||
|
// |
||||
|
|
||||
|
#import "SCStateTransitionPayload.h" |
||||
|
|
||||
|
#import <SCFoundation/SCAssertWrapper.h> |
||||
|
|
||||
|
@implementation SCStateTransitionPayload |
||||
|
|
||||
|
- (instancetype)initWithFromState:(SCCaptureStateMachineStateId)fromState toState:(SCCaptureStateMachineStateId)toState |
||||
|
{ |
||||
|
self = [super init]; |
||||
|
if (self) { |
||||
|
SCAssert(fromState != toState, @""); |
||||
|
SCAssert(fromState > SCCaptureBaseStateId && fromState < SCCaptureStateMachineStateIdCount, @""); |
||||
|
SCAssert(toState > SCCaptureBaseStateId && toState < SCCaptureStateMachineStateIdCount, @""); |
||||
|
_fromState = fromState; |
||||
|
_toState = toState; |
||||
|
} |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
@end |
Write
Preview
Loading…
Cancel
Save
Reference in new issue