Add files via upload
This commit is contained in:
parent
d959bff84a
commit
bbb5955612
103
ManagedCapturer/SCSingleFrameStreamCapturer.m
Normal file
103
ManagedCapturer/SCSingleFrameStreamCapturer.m
Normal file
@ -0,0 +1,103 @@
|
||||
//
|
||||
// SCSingleFrameStreamCapturer.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Benjamin Hollis on 5/3/16.
|
||||
// Copyright © 2016 Snapchat, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SCSingleFrameStreamCapturer.h"
|
||||
|
||||
#import "SCManagedCapturer.h"
|
||||
|
||||
@implementation SCSingleFrameStreamCapturer {
|
||||
sc_managed_capturer_capture_video_frame_completion_handler_t _callback;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCompletion:(sc_managed_capturer_capture_video_frame_completion_handler_t)completionHandler
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_callback = completionHandler;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - SCManagedVideoDataSourceListener
|
||||
|
||||
- (void)managedVideoDataSource:(id<SCManagedVideoDataSource>)managedVideoDataSource
|
||||
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
||||
devicePosition:(SCManagedCaptureDevicePosition)devicePosition
|
||||
{
|
||||
if (_callback) {
|
||||
UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
|
||||
_callback(image);
|
||||
}
|
||||
_callback = nil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a CMSampleBufferRef to our native camera format (kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
|
||||
* as set in SCManagedVideoStreamer) to a UIImage.
|
||||
*
|
||||
* Code from http://stackoverflow.com/a/31553521/11284
|
||||
*/
|
||||
#define clamp(a) (a > 255 ? 255 : (a < 0 ? 0 : a))
|
||||
// TODO: Use the transform code from SCImageProcessIdentityYUVCommand
|
||||
- (UIImage *)imageFromSampleBuffer:(CMSampleBufferRef)sampleBuffer
|
||||
{
|
||||
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
|
||||
CVPixelBufferLockBaseAddress(imageBuffer, 0);
|
||||
|
||||
size_t width = CVPixelBufferGetWidth(imageBuffer);
|
||||
size_t height = CVPixelBufferGetHeight(imageBuffer);
|
||||
uint8_t *yBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
|
||||
size_t yPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0);
|
||||
uint8_t *cbCrBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 1);
|
||||
size_t cbCrPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 1);
|
||||
|
||||
int bytesPerPixel = 4;
|
||||
uint8_t *rgbBuffer = malloc(width * height * bytesPerPixel);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
uint8_t *rgbBufferLine = &rgbBuffer[y * width * bytesPerPixel];
|
||||
uint8_t *yBufferLine = &yBuffer[y * yPitch];
|
||||
uint8_t *cbCrBufferLine = &cbCrBuffer[(y >> 1) * cbCrPitch];
|
||||
|
||||
for (int x = 0; x < width; x++) {
|
||||
int16_t y = yBufferLine[x];
|
||||
int16_t cb = cbCrBufferLine[x & ~1] - 128;
|
||||
int16_t cr = cbCrBufferLine[x | 1] - 128;
|
||||
|
||||
uint8_t *rgbOutput = &rgbBufferLine[x * bytesPerPixel];
|
||||
|
||||
int16_t r = (int16_t)roundf(y + cr * 1.4);
|
||||
int16_t g = (int16_t)roundf(y + cb * -0.343 + cr * -0.711);
|
||||
int16_t b = (int16_t)roundf(y + cb * 1.765);
|
||||
|
||||
rgbOutput[0] = 0xff;
|
||||
rgbOutput[1] = clamp(b);
|
||||
rgbOutput[2] = clamp(g);
|
||||
rgbOutput[3] = clamp(r);
|
||||
}
|
||||
}
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(rgbBuffer, width, height, 8, width * bytesPerPixel, colorSpace,
|
||||
kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
|
||||
CGImageRef quartzImage = CGBitmapContextCreateImage(context);
|
||||
|
||||
// TODO: Hardcoding UIImageOrientationRight seems cheesy
|
||||
UIImage *image = [UIImage imageWithCGImage:quartzImage scale:1.0 orientation:UIImageOrientationRight];
|
||||
|
||||
CGContextRelease(context);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CGImageRelease(quartzImage);
|
||||
free(rgbBuffer);
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
19
ManagedCapturer/SCStillImageCaptureVideoInputMethod.h
Normal file
19
ManagedCapturer/SCStillImageCaptureVideoInputMethod.h
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// SCStillImageCaptureVideoInputMethod.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Alexander Grytsiuk on 3/16/16.
|
||||
// Copyright © 2016 Snapchat, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SCManagedCapturerState.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@interface SCStillImageCaptureVideoInputMethod : NSObject
|
||||
|
||||
- (void)captureStillImageWithCapturerState:(SCManagedCapturerState *)state
|
||||
successBlock:(void (^)(NSData *imageData, NSDictionary *cameraInfo,
|
||||
NSError *error))successBlock
|
||||
failureBlock:(void (^)(NSError *error))failureBlock;
|
||||
@end
|
140
ManagedCapturer/SCStillImageCaptureVideoInputMethod.m
Normal file
140
ManagedCapturer/SCStillImageCaptureVideoInputMethod.m
Normal file
@ -0,0 +1,140 @@
|
||||
//
|
||||
// SCStillImageCaptureVideoInputMethod.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Alexander Grytsiuk on 3/16/16.
|
||||
// Copyright © 2016 Snapchat, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SCStillImageCaptureVideoInputMethod.h"
|
||||
|
||||
#import "SCManagedCapturer.h"
|
||||
#import "SCManagedVideoFileStreamer.h"
|
||||
|
||||
typedef unsigned char uchar_t;
|
||||
int clamp(int val, int low, int high)
|
||||
{
|
||||
if (val < low)
|
||||
val = low;
|
||||
if (val > high)
|
||||
val = high;
|
||||
return val;
|
||||
}
|
||||
|
||||
void yuv2rgb(uchar_t yValue, uchar_t uValue, uchar_t vValue, uchar_t *r, uchar_t *g, uchar_t *b)
|
||||
{
|
||||
double red = yValue + (1.370705 * (vValue - 128));
|
||||
double green = yValue - (0.698001 * (vValue - 128)) - (0.337633 * (uValue - 128));
|
||||
double blue = yValue + (1.732446 * (uValue - 128));
|
||||
*r = clamp(red, 0, 255);
|
||||
*g = clamp(green, 0, 255);
|
||||
*b = clamp(blue, 0, 255);
|
||||
}
|
||||
|
||||
void convertNV21DataToRGBData(int width, int height, uchar_t *nv21Data, uchar_t *rgbData, int rgbBytesPerPixel,
|
||||
int rgbBytesPerRow)
|
||||
{
|
||||
uchar_t *uvData = nv21Data + height * width;
|
||||
for (int h = 0; h < height; h++) {
|
||||
uchar_t *yRowBegin = nv21Data + h * width;
|
||||
uchar_t *uvRowBegin = uvData + h / 2 * width;
|
||||
uchar_t *rgbRowBegin = rgbData + rgbBytesPerRow * h;
|
||||
for (int w = 0; w < width; w++) {
|
||||
uchar_t *rgbPixelBegin = rgbRowBegin + rgbBytesPerPixel * w;
|
||||
yuv2rgb(yRowBegin[w], uvRowBegin[w / 2 * 2], uvRowBegin[w / 2 * 2 + 1], &(rgbPixelBegin[0]),
|
||||
&(rgbPixelBegin[1]), &(rgbPixelBegin[2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@implementation SCStillImageCaptureVideoInputMethod
|
||||
|
||||
- (void)captureStillImageWithCapturerState:(SCManagedCapturerState *)state
|
||||
successBlock:(void (^)(NSData *imageData, NSDictionary *cameraInfo,
|
||||
NSError *error))successBlock
|
||||
failureBlock:(void (^)(NSError *error))failureBlock
|
||||
{
|
||||
id<SCManagedVideoDataSource> videoDataSource = [[SCManagedCapturer sharedInstance] currentVideoDataSource];
|
||||
if ([videoDataSource isKindOfClass:[SCManagedVideoFileStreamer class]]) {
|
||||
SCManagedVideoFileStreamer *videoFileStreamer = (SCManagedVideoFileStreamer *)videoDataSource;
|
||||
[videoFileStreamer getNextPixelBufferWithCompletion:^(CVPixelBufferRef pixelBuffer) {
|
||||
BOOL shouldFlip = state.devicePosition == SCManagedCaptureDevicePositionFront;
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
UIImage *uiImage = [self imageWithCVPixelBuffer:pixelBuffer];
|
||||
CGImageRef videoImage = uiImage.CGImage;
|
||||
UIImage *capturedImage = [UIImage
|
||||
imageWithCGImage:shouldFlip ? [self flipCGImage:videoImage size:uiImage.size].CGImage : videoImage
|
||||
scale:1.0
|
||||
orientation:UIImageOrientationRight];
|
||||
#else
|
||||
CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
|
||||
CIContext *temporaryContext = [CIContext contextWithOptions:nil];
|
||||
|
||||
CGSize size = CGSizeMake(CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer));
|
||||
CGImageRef videoImage =
|
||||
[temporaryContext createCGImage:ciImage fromRect:CGRectMake(0, 0, size.width, size.height)];
|
||||
|
||||
UIImage *capturedImage =
|
||||
[UIImage imageWithCGImage:shouldFlip ? [self flipCGImage:videoImage size:size].CGImage : videoImage
|
||||
scale:1.0
|
||||
orientation:UIImageOrientationRight];
|
||||
|
||||
CGImageRelease(videoImage);
|
||||
#endif
|
||||
if (successBlock) {
|
||||
successBlock(UIImageJPEGRepresentation(capturedImage, 1.0), nil, nil);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
if (failureBlock) {
|
||||
failureBlock([NSError errorWithDomain:NSStringFromClass(self.class) code:-1 userInfo:nil]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)flipCGImage:(CGImageRef)cgImage size:(CGSize)size
|
||||
{
|
||||
UIGraphicsBeginImageContext(size);
|
||||
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, size.width, size.height), cgImage);
|
||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
return image;
|
||||
}
|
||||
|
||||
- (UIImage *)imageWithCVPixelBuffer:(CVPixelBufferRef)imageBuffer
|
||||
{
|
||||
CVPixelBufferLockBaseAddress(imageBuffer, 0);
|
||||
|
||||
size_t width = CVPixelBufferGetWidth(imageBuffer);
|
||||
size_t height = CVPixelBufferGetHeight(imageBuffer);
|
||||
size_t rgbBytesPerPixel = 4;
|
||||
size_t rgbBytesPerRow = width * rgbBytesPerPixel;
|
||||
|
||||
uchar_t *nv21Data = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);
|
||||
uchar_t *rgbData = malloc(rgbBytesPerRow * height);
|
||||
|
||||
convertNV21DataToRGBData((int)width, (int)height, nv21Data, rgbData, (int)rgbBytesPerPixel, (int)rgbBytesPerRow);
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context =
|
||||
CGBitmapContextCreate(rgbData, width, height, 8, rgbBytesPerRow, colorSpace, kCGImageAlphaNoneSkipLast);
|
||||
CGImageRef cgImage = CGBitmapContextCreateImage(context);
|
||||
|
||||
UIImage *result = [UIImage imageWithCGImage:cgImage];
|
||||
|
||||
CGImageRelease(cgImage);
|
||||
CGContextRelease(context);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
free(rgbData);
|
||||
|
||||
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSString *)methodName
|
||||
{
|
||||
return @"VideoInput";
|
||||
}
|
||||
|
||||
@end
|
28
ManagedCapturer/SCTimedTask.h
Normal file
28
ManagedCapturer/SCTimedTask.h
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// SCTimedTask.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Michel Loenngren on 4/2/17.
|
||||
// Copyright © 2017 Snapchat, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/*
|
||||
Block based timed task
|
||||
*/
|
||||
@interface SCTimedTask : NSObject
|
||||
|
||||
@property (nonatomic, assign) CMTime targetTime;
|
||||
@property (nonatomic, copy) void (^task)(CMTime relativePresentationTime, CGFloat sessionStartTimeDelayInSecond);
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithTargetTime:(CMTime)targetTime
|
||||
task:(void (^)(CMTime relativePresentationTime,
|
||||
CGFloat sessionStartTimeDelayInSecond))task;
|
||||
|
||||
- (NSString *)description;
|
||||
|
||||
@end
|
32
ManagedCapturer/SCTimedTask.m
Normal file
32
ManagedCapturer/SCTimedTask.m
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// SCTimedTask.m
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Michel Loenngren on 4/2/17.
|
||||
// Copyright © 2017 Snapchat, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SCTimedTask.h"
|
||||
|
||||
#import <SCFoundation/NSString+SCFormat.h>
|
||||
|
||||
@implementation SCTimedTask
|
||||
|
||||
- (instancetype)initWithTargetTime:(CMTime)targetTime
|
||||
task:
|
||||
(void (^)(CMTime relativePresentationTime, CGFloat sessionStartTimeDelayInSecond))task
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_targetTime = targetTime;
|
||||
_task = task;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString
|
||||
sc_stringWithFormat:@"<%@: %p, targetTime: %lld>", NSStringFromClass([self class]), self, _targetTime.value];
|
||||
}
|
||||
|
||||
@end
|
83
ManagedCapturer/SCVideoCaptureSessionInfo.h
Normal file
83
ManagedCapturer/SCVideoCaptureSessionInfo.h
Normal file
@ -0,0 +1,83 @@
|
||||
//
|
||||
// SCVideoCaptureSessionInfo.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Michel Loenngren on 3/27/17.
|
||||
// Copyright © 2017 Snapchat, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <SCFoundation/NSString+SCFormat.h>
|
||||
|
||||
#import <CoreMedia/CoreMedia.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, SCManagedVideoCapturerInfoType) {
|
||||
SCManagedVideoCapturerInfoAudioQueueError,
|
||||
SCManagedVideoCapturerInfoAssetWriterError,
|
||||
SCManagedVideoCapturerInfoAudioSessionError,
|
||||
SCManagedVideoCapturerInfoAudioQueueRetrySuccess,
|
||||
SCManagedVideoCapturerInfoAudioQueueRetryDataSourceSuccess_audioQueue,
|
||||
SCManagedVideoCapturerInfoAudioQueueRetryDataSourceSuccess_hardware
|
||||
};
|
||||
|
||||
typedef u_int32_t sc_managed_capturer_recording_session_t;
|
||||
|
||||
/*
|
||||
Container object holding information about the
|
||||
current recording session.
|
||||
*/
|
||||
typedef struct {
|
||||
CMTime startTime;
|
||||
CMTime endTime;
|
||||
CMTime duration;
|
||||
sc_managed_capturer_recording_session_t sessionId;
|
||||
} SCVideoCaptureSessionInfo;
|
||||
|
||||
static inline SCVideoCaptureSessionInfo SCVideoCaptureSessionInfoMake(CMTime startTime, CMTime endTime,
|
||||
sc_managed_capturer_recording_session_t sessionId)
|
||||
{
|
||||
SCVideoCaptureSessionInfo session;
|
||||
session.startTime = startTime;
|
||||
session.endTime = endTime;
|
||||
if (CMTIME_IS_VALID(startTime) && CMTIME_IS_VALID(endTime)) {
|
||||
session.duration = CMTimeSubtract(endTime, startTime);
|
||||
} else {
|
||||
session.duration = kCMTimeInvalid;
|
||||
}
|
||||
session.sessionId = sessionId;
|
||||
return session;
|
||||
}
|
||||
|
||||
static inline NSTimeInterval SCVideoCaptureSessionInfoGetCurrentDuration(SCVideoCaptureSessionInfo sessionInfo)
|
||||
{
|
||||
if (CMTIME_IS_VALID(sessionInfo.startTime)) {
|
||||
if (CMTIME_IS_VALID(sessionInfo.endTime)) {
|
||||
return CMTimeGetSeconds(sessionInfo.duration);
|
||||
}
|
||||
return CACurrentMediaTime() - CMTimeGetSeconds(sessionInfo.startTime);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline NSString *SCVideoCaptureSessionInfoGetDebugString(CMTime time, NSString *label)
|
||||
{
|
||||
if (CMTIME_IS_VALID(time)) {
|
||||
return [NSString sc_stringWithFormat:@"%@: %f", label, CMTimeGetSeconds(time)];
|
||||
} else {
|
||||
return [NSString sc_stringWithFormat:@"%@: Invalid", label];
|
||||
}
|
||||
}
|
||||
|
||||
static inline NSString *SCVideoCaptureSessionInfoGetDebugDescription(SCVideoCaptureSessionInfo sessionInfo)
|
||||
{
|
||||
NSMutableString *description = [NSMutableString new];
|
||||
[description appendString:SCVideoCaptureSessionInfoGetDebugString(sessionInfo.startTime, @"StartTime")];
|
||||
[description appendString:@", "];
|
||||
[description appendString:SCVideoCaptureSessionInfoGetDebugString(sessionInfo.endTime, @"EndTime")];
|
||||
[description appendString:@", "];
|
||||
[description appendString:SCVideoCaptureSessionInfoGetDebugString(sessionInfo.duration, @"Duration")];
|
||||
[description appendString:@", "];
|
||||
[description appendString:[NSString sc_stringWithFormat:@"Id: %u", sessionInfo.sessionId]];
|
||||
|
||||
return [description copy];
|
||||
}
|
103
ManagedCapturer/StateMachine/SCCaptureBaseState.h
Normal file
103
ManagedCapturer/StateMachine/SCCaptureBaseState.h
Normal file
@ -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
|
169
ManagedCapturer/StateMachine/SCCaptureBaseState.m
Normal file
169
ManagedCapturer/StateMachine/SCCaptureBaseState.m
Normal file
@ -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
|
30
ManagedCapturer/StateMachine/SCCaptureStateDelegate.h
Normal file
30
ManagedCapturer/StateMachine/SCCaptureStateDelegate.h
Normal file
@ -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
|
76
ManagedCapturer/StateMachine/SCCaptureStateMachineContext.h
Normal file
76
ManagedCapturer/StateMachine/SCCaptureStateMachineContext.h
Normal file
@ -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
|
301
ManagedCapturer/StateMachine/SCCaptureStateMachineContext.m
Normal file
301
ManagedCapturer/StateMachine/SCCaptureStateMachineContext.m
Normal file
@ -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
|
37
ManagedCapturer/StateMachine/SCCaptureStateUtil.h
Normal file
37
ManagedCapturer/StateMachine/SCCaptureStateUtil.h
Normal file
@ -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
|
38
ManagedCapturer/StateMachine/SCCaptureStateUtil.m
Normal file
38
ManagedCapturer/StateMachine/SCCaptureStateUtil.m
Normal file
@ -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";
|
||||
}
|
12
ManagedCapturer/StateMachine/SCManagedCapturerLogging.h
Normal file
12
ManagedCapturer/StateMachine/SCManagedCapturerLogging.h
Normal file
@ -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__)
|
22
ManagedCapturer/StateMachine/States/SCCaptureImageState.h
Normal file
22
ManagedCapturer/StateMachine/States/SCCaptureImageState.h
Normal file
@ -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
|
65
ManagedCapturer/StateMachine/States/SCCaptureImageState.m
Normal file
65
ManagedCapturer/StateMachine/States/SCCaptureImageState.m
Normal file
@ -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
|
114
ManagedCapturer/StateMachine/States/SCCaptureRecordingState.m
Normal file
114
ManagedCapturer/StateMachine/States/SCCaptureRecordingState.m
Normal file
@ -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
|
22
ManagedCapturer/StateMachine/States/SCCaptureRunningState.h
Normal file
22
ManagedCapturer/StateMachine/States/SCCaptureRunningState.h
Normal file
@ -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
|
176
ManagedCapturer/StateMachine/States/SCCaptureRunningState.m
Normal file
176
ManagedCapturer/StateMachine/States/SCCaptureRunningState.m
Normal file
@ -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
|
18
ManagedCapturer/StateMachine/States/SCCaptureScanningState.h
Normal file
18
ManagedCapturer/StateMachine/States/SCCaptureScanningState.h
Normal file
@ -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
|
75
ManagedCapturer/StateMachine/States/SCCaptureScanningState.m
Normal file
75
ManagedCapturer/StateMachine/States/SCCaptureScanningState.m
Normal file
@ -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
|
13
ManagedCapturer/UIScreen+Debug.h
Normal file
13
ManagedCapturer/UIScreen+Debug.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// UIScreen+Debug.h
|
||||
// Snapchat
|
||||
//
|
||||
// Created by Derek Peirce on 6/1/17.
|
||||
// Copyright © 2017 Snapchat, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIScreen (Debug)
|
||||
|
||||
@end
|
28
ManagedCapturer/UIScreen+Debug.m
Normal file
28
ManagedCapturer/UIScreen+Debug.m
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
#import "UIScreen+Debug.h"
|
||||
|
||||
#import <SCFoundation/SCAppEnvironment.h>
|
||||
#import <SCFoundation/SCLog.h>
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@implementation UIScreen (Debug)
|
||||
+ (void)load
|
||||
{
|
||||
if (SCIsPerformanceLoggingEnabled()) {
|
||||
static dispatch_once_t once_token;
|
||||
dispatch_once(&once_token, ^{
|
||||
SEL setBrightnessSelector = @selector(setBrightness:);
|
||||
SEL setBrightnessLoggerSelector = @selector(logged_setBrightness:);
|
||||
Method originalMethod = class_getInstanceMethod(self, setBrightnessSelector);
|
||||
Method extendedMethod = class_getInstanceMethod(self, setBrightnessLoggerSelector);
|
||||
method_exchangeImplementations(originalMethod, extendedMethod);
|
||||
});
|
||||
}
|
||||
}
|
||||
- (void)logged_setBrightness:(CGFloat)brightness
|
||||
{
|
||||
SCLogGeneralInfo(@"Setting brightness from %f to %f", self.brightness, brightness);
|
||||
[self logged_setBrightness:brightness];
|
||||
}
|
||||
@end
|
Reference in New Issue
Block a user