Source-SCCamera/ManagedCapturer/SCCaptureStateMachineContext.m

302 lines
13 KiB
Objective-C

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